Commit 12be8dec authored by Sreekanth Arikatla's avatar Sreekanth Arikatla
Browse files

ENH: Adds mesh data memory layout optimizer

Adds optimizer to the mesh. This algorithm rewires
the node order and triangle connectivity to optimize
for memory layout. Adds tests for the same.

The algorithm starts with a random node and flood-fills
in the triangular mesh space adds nodes and elements
at every iteration. The iteration ends when the surface
is "flooded". The algorithm assumes that the mesh is
one topologically connected entity.

TODO: Further optimization at each iteration. Extend to other mesh types.
parent b4c7b5be
......@@ -98,12 +98,12 @@ Mesh::computeBoundingBox(Vec3d& min, Vec3d& max, const double percent) const
std::numeric_limits<double>::min(),
std::numeric_limits<double>::min());
for (auto it = m_verticesPositions.begin(); it != m_verticesPositions.end(); ++it)
for (auto& pos : m_verticesPositions)
{
for (int i = 0; i < 3; ++i)
{
min[i] = std::min(min[i], (*it)[i]);
max[i] = std::max(max[i], (*it)[i]);
min[i] = std::min(min[i], pos[i]);
max[i] = std::max(max[i], pos[i]);
}
}
......
......@@ -105,8 +105,7 @@ public:
const int getNumVertices() const;
protected:
Mesh(GeometryType type) : Geometry(type, WORLD_ORIGIN, Quatd()) {}
Mesh(GeometryType type) : Geometry(type, WORLD_ORIGIN, Quatd()) {}
// Orientation * Scaling * initialVerticesPositions
// + Position (Initial translation)
// + verticesDisplacements
......
......@@ -31,7 +31,7 @@ const bool computDerivedData)
{
this->clear();
setInitialVerticesPositions(vertices);
setInitialVerticesPositions(vertices);
setVerticesPositions(vertices);
setTrianglesVertices(triangles);
if (texCoords.size() > 0)
......@@ -238,6 +238,12 @@ SurfaceMesh::setTextureCoordinates(const std::vector<Vec2f>& coords)
m_textureCoordinates = coords;
}
const Vec2f&
SurfaceMesh::getVertTextureCoordinate(const int vertNum) const
{
return m_textureCoordinates.at(vertNum);
}
const std::vector<Vec3d>&
SurfaceMesh::getTrianglesNormals() const
{
......@@ -327,4 +333,119 @@ SurfaceMesh::print() const
}
}
void
SurfaceMesh::optimizeForDataLocality()
{
const int numVertices = this->getNumVertices();
const int numTriangles = this->getNumTriangles();
// First find the list of triangles a given vertex is part of
std::vector<std::vector<int>> vertexNeighbors;
vertexNeighbors.resize(this->getNumVertices());
int triId = 0;
for (const auto &tri : this->getTrianglesVertices())
{
vertexNeighbors[tri[0]].push_back(triId);
vertexNeighbors[tri[1]].push_back(triId);
vertexNeighbors[tri[2]].push_back(triId);
triId++;
}
std::vector<TriangleArray> optimizedConnectivity;
std::vector<int> optimallyOrderedNodes;
std::list<int> triUnderConsideration;
std::vector<bool> isNodeAdded(numVertices, false);
std::vector<bool> isTriangleAdded(numTriangles, false);
std::list<int> newlyAddedNodes;
// A. Initialize
optimallyOrderedNodes.push_back(0);
isNodeAdded.at(0) = true;
for (const auto &neighTriId : vertexNeighbors[0])
{
triUnderConsideration.push_back(neighTriId);
}
// B. Iterate till all the nodes are added to optimized mesh
int vertId[3];
auto connectivity = this->getTrianglesVertices();
while (triUnderConsideration.size() != 0)
{
// B.1 Add new nodes and triangles
for (const auto &triId : triUnderConsideration)
{
for (int i = 0; i < 3; ++i)
{
if (!isNodeAdded.at(connectivity.at(triId)[i]))
{
optimallyOrderedNodes.push_back(connectivity.at(triId)[i]);
isNodeAdded.at(connectivity.at(triId)[i]) = true;
newlyAddedNodes.push_back(connectivity.at(triId)[i]);
}
vertId[i] = *std::find(optimallyOrderedNodes.begin(),
optimallyOrderedNodes.end(),
connectivity.at(triId)[i]);
}
TriangleArray tmpTri = { { vertId[0], vertId[1], vertId[2] } };
optimizedConnectivity.push_back(tmpTri);
isTriangleAdded.at(triId) = true;
}
// B.2 Setup triangles to be considered for next iteration
triUnderConsideration.clear();
std::vector<bool> triangleAdded;
for (const auto &newNodes : newlyAddedNodes)
{
for (const auto &neighTriId : vertexNeighbors[newNodes])
{
if (!isTriangleAdded[neighTriId])
{
triUnderConsideration.push_back(neighTriId);
}
}
}
triUnderConsideration.sort();
triUnderConsideration.unique();
newlyAddedNodes.clear();
}
// C. Initialize this mesh with the newly computed ones
std::vector<Vec3d> optimallyOrderedNodalPos;
std::vector<TriangleArray> optConnectivityRenumbered;
std::vector<Vec2f> newTexCoordinates;
bool texCoorExist = (this->m_textureCoordinates.size()!=0) ? true : false;
// C.1 Get the positions
for (const auto &nodalId : optimallyOrderedNodes)
{
optimallyOrderedNodalPos.push_back(this->getInitialVertexPosition(nodalId));
if (texCoorExist)
{
newTexCoordinates.push_back(this->getVertTextureCoordinate(nodalId));
}
}
// C.2 Get the renumbered connectivity
for (size_t i = 0; i < numTriangles; ++i)
{
for (int j = 0; j < 3; ++j)
{
vertId[j] = (std::find(optimallyOrderedNodes.begin(),
optimallyOrderedNodes.end(),
optimizedConnectivity.at(i)[j]) -
optimallyOrderedNodes.begin()) ;
}
TriangleArray tmpTriArray = { { vertId[0], vertId[1], vertId[2] } };
optConnectivityRenumbered.push_back(tmpTriArray);
}
// D. Assign the rewired mesh data to the mesh
this->initialize(optimallyOrderedNodalPos, optConnectivityRenumbered, newTexCoordinates);
}
}
......@@ -23,6 +23,7 @@
#define imstkSurfaceMesh_h
#include <array>
#include <list>
#include <set>
#include "imstkMesh.h"
......@@ -89,6 +90,13 @@ public:
///
void clear();
///
/// \brief Rewire the node order and triangle connectivity to optimize for memory layout
/// The intended use is for large meshes that doesn't fit into CPU/GPU memory.
/// TODO: Further optimization to find a 1-d uninterrupted sub-graph at each iteration.
///
void optimizeForDataLocality();
///
/// \brief Print the surface mesh
///
......@@ -108,6 +116,11 @@ public:
void setTextureCoordinates(const std::vector<Vec2f>& coords);
const std::vector<Vec2f>& getTextureCoordinates() const;
///
/// \brief Returns the texture coordinates for a given vertex
///
const Vec2f& getVertTextureCoordinate(const int vertNum) const;
///
/// \brief Get vector of normals of all the triangles
///
......
......@@ -273,7 +273,7 @@ TetrahedralMesh::extractSurfaceMesh(std::shared_ptr<SurfaceMesh> surfaceMesh)
// Renumber the vertices
std::list<int> uniqueVertIdList;
for (auto &face : surfaceTri)
for (const auto &face : surfaceTri)
{
uniqueVertIdList.push_back(face[0]);
uniqueVertIdList.push_back(face[1]);
......
......@@ -33,6 +33,7 @@ void testIsometricMap();
void testTetraTriangleMap();
void testOneToOneNodalMap();
void testExtractSurfaceMesh();
void testSurfaceMeshOptimizer();
int main()
{
......@@ -46,8 +47,9 @@ int main()
//testScenesManagement();
//testIsometricMap();
//testTetraTriangleMap();
//testExtractSurfaceMesh();
testOneToOneNodalMap();
testExtractSurfaceMesh();
//testOneToOneNodalMap();
//testSurfaceMeshOptimizer();
return 0;
}
......@@ -397,5 +399,61 @@ void testOneToOneNodalMap()
oneToOneNodalMap->print();
}
getchar();
}
void testSurfaceMeshOptimizer()
{
auto sdk = std::make_shared<imstk::SimulationManager>();
// a. Construct a sample triangular mesh
// b. Add nodal data
auto surfMesh = std::make_shared<imstk::SurfaceMesh>();
std::vector<imstk::Vec3d> vertList;
vertList.push_back(imstk::Vec3d(0, 0, 0));
vertList.push_back(imstk::Vec3d(0.5, 0.5, 0));
vertList.push_back(imstk::Vec3d(1, 1, 0));
vertList.push_back(imstk::Vec3d(1, 0, 0));
vertList.push_back(imstk::Vec3d(0, 1, 0));
vertList.push_back(imstk::Vec3d(0.5, 1, 0));
vertList.push_back(imstk::Vec3d(0, 0.5, 0));
vertList.push_back(imstk::Vec3d(1, 0.5, 0));
vertList.push_back(imstk::Vec3d(0.5, 0, 0));
surfMesh->setInitialVerticesPositions(vertList);
surfMesh->setVerticesPositions(vertList);
// c. Add connectivity data
std::vector<imstk::SurfaceMesh::TriangleArray> triangles;
imstk::SurfaceMesh::TriangleArray tri[8];
tri[0] = { { 0, 8, 6 } };
tri[1] = { { 7, 2, 5 } };
tri[2] = { { 1, 5, 4 } };
tri[3] = { { 3, 7, 1 } };
tri[4] = { { 8, 1, 6 } };
tri[5] = { { 1, 4, 6 } };
tri[6] = { { 1, 7, 5 } };
tri[7] = { { 3, 1, 8 } };
for (int i = 0; i < 8; i++)
{
triangles.push_back(tri[i]);
}
surfMesh->setTrianglesVertices(triangles);
// d. Print the mesh
surfMesh->print();
// e. Rewire the mesh position and connectivity
surfMesh->optimizeForDataLocality();
// f. Print the resulting mesh
surfMesh->print();
// Cross-check
// Connectivity: 0:(0, 1, 2), 1:(1, 3, 2), 2:(3, 4, 2), 3:(5, 3, 1), 4:(3, 6, 4), 5:(5, 7, 3), 6:(3, 7, 6), 7:(7, 8, 6)
// Nodal data: 0:(0, 0, 0), 1:(0.5, 0, 0), 2:(0, 0.5, 0), 3:(0.5, 0.5, 0), 4:(0, 1, 0), 5:(1, 0, 0), 6:(0.5, 1, 0), 7:(1, 0.5, 0), 8:(1, 1, 0)
getchar();
}
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment