// --(BEGIN CUSTOM CODE)--
PXR_NAMESPACE_OPEN_SCOPE
-bool UsdGeomTetMesh::ComputeSurfaceFaces(const UsdGeomTetMesh& tetMesh,
- VtVec3iArray* surfaceFaceIndices,
- const UsdTimeCode timeCode)
+namespace {
+
+GfVec3i
+_Sorted(GfVec3i v)
{
-
- if (surfaceFaceIndices == nullptr)
+ if (v[0] > v[1]) {
+ std::swap(v[0], v[1]);
+ }
+ if (v[0] > v[2]) {
+ std::swap(v[0], v[2]);
+ }
+ if (v[1] > v[2]) {
+ std::swap(v[1], v[2]);
+ }
+ return v;
+}
+
+struct _Vec3iHash
+{
+ size_t operator()(const GfVec3i& v) const
{
- return false;
+ return
+ static_cast<size_t>(v[0]) << 42 ^
+ static_cast<size_t>(v[1]) << 21 ^
+ static_cast<size_t>(v[2]);
}
-
- // The four triangles of the tetrahedron
- static int tetFaceIndices[4][3] = {
- {1,2,3},
- {0,3,2},
- {0,1,3},
- {0,2,1}
- };
+};
- const UsdAttribute& tetVertexIndicesAttr = tetMesh.GetTetVertexIndicesAttr();
- VtVec4iArray tetVertexIndices;
- tetVertexIndicesAttr.Get(&tetVertexIndices, timeCode);
+struct _Vec3iCmp
+{
+ // Comparator function
+ bool operator()(const GfVec3i& f1, const GfVec3i &f2)
+ {
+ if (f1[0] == f2[0])
+ {
+ if (f1[1] == f2[1])
+ {
+ return f1[2] < f2[2];
+ }
+
+ return f1[1] < f2[1];
+ }
+
+ return f1[0] < f2[0];
+ }
+};
+
+VtVec3iArray
+_ComputeSurfaceFaces(const VtVec4iArray &tetVertexIndices)
+{
// The surface faces are made of triangles that are not shared between
- // tetrahedra and only occur once. We create a hashmap from face
- // triangles to occurence counts and then run through all the triangles
- // in the tetmesh incrementing the count. When we are done, we sweep
- // the hashmap and gather the faces with one occurence.
- TfHashMap<_IndexTri, size_t,
- _IndexTriHash, _IndexTriEquals> triangleCounts;
+ // tetrahedra and only occur once. We use a hashmap from triangles
+ // to counting information to see whether a triangle occurs in the
+ // hashmap just and thus is not shared. We then sweep the hashmap
+ // to find all triangles.
+ //
+ // Recall that a triangle is a triple of indices. But two triangles are
+ // shared if these two triples are related by a permutation. Thus, the
+ // key into the hashmap is the sorted triple which we call the signature.
+ //
+ // The value of the hashmap is a pair of (count, triple). The triple
+ // is stored next to the count so that we do not loose the orientation
+ // information that was lost when sorting the triple.
+ //
+ using SigToCountAndTriangle =
+ TfHashMap<GfVec3i, std::pair<size_t, GfVec3i>, _Vec3iHash>;
+
+ SigToCountAndTriangle sigToCountAndTriangle;
for (size_t t = 0; t < tetVertexIndices.size(); t++) {
const GfVec4i& tet = tetVertexIndices[t];
+
+ // The four triangles of a tetrahedron
+ static int tetFaceIndices[4][3] = {
+ {1,2,3},
+ {0,3,2},
+ {0,1,3},
+ {0,2,1}
+ };
for (int tFace = 0; tFace < 4; tFace++) {
-
- const _IndexTri faceId(tet[tetFaceIndices[tFace][0]],
- tet[tetFaceIndices[tFace][1]],
- tet[tetFaceIndices[tFace][2]]);
- triangleCounts[faceId]++;
+
+ // A triangle of this tetrahedron.
+ const GfVec3i triangle(
+ tet[tetFaceIndices[tFace][0]],
+ tet[tetFaceIndices[tFace][1]],
+ tet[tetFaceIndices[tFace][2]]);
+
+ std::pair<size_t, GfVec3i> &item =
+ sigToCountAndTriangle[_Sorted(triangle)];
+ item.first++;
+ item.second = triangle;
}
}
-
- // Take a guess and generously reserve one surface face
- // per tetrahedron.
- surfaceFaceIndices->reserve(tetVertexIndices.size());
-
- for(auto&& [first, second] : triangleCounts) {
- if (second == 1) {
- const _IndexTri& tri = first;
- surfaceFaceIndices->push_back(tri.GetUnsortedIndices());
+
+ VtVec3iArray result;
+ // Reserve one surface face per tetrahedron.
+ // A tetrahedron can contribute up to 4 faces, but typically,
+ // most faces of tet mesh are shared. So this is really just
+ // a guess.
+ result.reserve(tetVertexIndices.size());
+
+ for(auto && [sig, countAndTriangle] : sigToCountAndTriangle) {
+ if (countAndTriangle.first == 1) {
+ result.push_back(countAndTriangle.second);
}
}
// Need to sort results for deterministic behavior across different
// compiler/OS versions
- FaceVertexIndicesCompare comparator;
- std::sort(surfaceFaceIndices->begin(),
- surfaceFaceIndices->end(), comparator);
+ std::sort(result.begin(), result.end(), _Vec3iCmp());
+
+ return result;
+}
+}
+
+bool UsdGeomTetMesh::ComputeSurfaceFaces(const UsdGeomTetMesh& tetMesh,
+ VtVec3iArray* surfaceFaceIndices,
+ const UsdTimeCode timeCode)
+{
+
+ if (surfaceFaceIndices == nullptr)
+ {
+ return false;
+ }
+
+ const UsdAttribute& tetVertexIndicesAttr = tetMesh.GetTetVertexIndicesAttr();
+ VtVec4iArray tetVertexIndices;
+ tetVertexIndicesAttr.Get(&tetVertexIndices, timeCode);
+
+ *surfaceFaceIndices = _ComputeSurfaceFaces(tetVertexIndices);
return true;
}
static bool ComputeSurfaceFaces(const UsdGeomTetMesh& tetMesh,
VtVec3iArray* surfaceFaceIndices,
const UsdTimeCode timeCode = UsdTimeCode::Default());
-
-private:
- // IndexTri is a helper class used by ComputeSurfaceFaces
- class _IndexTri
- {
- public:
- _IndexTri(int fv0, int fv1, int fv2):_unsortedIndices(fv0, fv1, fv2),
- _sortedIdx0(fv0),
- _sortedIdx1(fv1),
- _sortedIdx2(fv2)
- {
- // _sortedIdx0, _sortedIdx1 and _sortedIdx2 are
- // sorted indices because order is important when determining
- // face equality.
- _SortFVIndices(_sortedIdx0,_sortedIdx1,_sortedIdx2);
- // .. and this is a perfect hash for a face, provided
- // there are fewer than 2097152 (2**21) points. But we
- // keep the sorted indices around to resolve collisions
- //
- _hash = _sortedIdx0 << 42 ^ _sortedIdx1 << 21 ^ _sortedIdx2;
- }
-
- size_t GetHash() const {
- return _hash;
- }
-
- bool operator == (const _IndexTri& other) const {
- // A face is the same as another if the
- // three sorted indices are identical.
- // In practice, this function will only
- // get called for extremely large meshes.
- return (_sortedIdx0 == other._sortedIdx0) &&
- (_sortedIdx1 == other._sortedIdx1) &&
- (_sortedIdx2 == other._sortedIdx2);
- }
-
- const GfVec3i& GetUnsortedIndices() const {
- return _unsortedIndices;
- }
-
- private:
-
- static void _SortFVIndices(size_t& a, size_t& b, size_t& c) {
- if (a > b) {
- std::swap(a, b);
- }
-
- if (a > c) {
- std::swap(a, c);
- }
-
- if (b > c) {
- std::swap(b, c);
- }
- }
-
- // Original unsorted indices
- const GfVec3i _unsortedIndices;
-
- // Sorted Indices
- size_t _sortedIdx0;
- size_t _sortedIdx1;
- size_t _sortedIdx2;
-
- // Precomputed hash
- size_t _hash;
- };
-
- struct _IndexTriHash {
- size_t operator()(const _IndexTri& tri) const
- {
- return tri.GetHash();
- }
- };
-
- struct _IndexTriEquals {
- bool operator()(const _IndexTri& a, const _IndexTri& b) const
- {
- return a == b;
- }
- };
-
- class FaceVertexIndicesCompare {
- public:
- // Comparator function
- inline bool operator()(const GfVec3i& f1, const GfVec3i f2)
- {
- if (f1[0] == f2[0])
- {
- if (f1[1] == f2[1])
- {
- return f1[2] < f2[2];
- }
-
- return f1[1] < f2[1];
- }
-
- return f1[0] < f2[0];
- }
- };
};
PXR_NAMESPACE_CLOSE_SCOPE