UsdGeomTetMesh faces computation: changing the map so that the key does not carry...
authorunhyperbolic <unhyperbolic@users.noreply.github.com>
Sat, 3 Feb 2024 04:04:30 +0000 (20:04 -0800)
committerpixar-oss <pixar-oss@users.noreply.github.com>
Mon, 5 Feb 2024 20:21:06 +0000 (12:21 -0800)
(Internal change: 2314459)

pxr/usd/usdGeom/tetMesh.cpp
pxr/usd/usdGeom/tetMesh.h

index 3c07c7b2c7b667c84c41ffc9a50473aaa52c19fb..ab9203f4a40539b346dc1a7264a2d2e7c2a53de5 100644 (file)
@@ -180,65 +180,139 @@ PXR_NAMESPACE_CLOSE_SCOPE
 // --(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;
 }
 
index 11079f835b37126dfafbb8dce57e6c78d6eb54ee..6f3dc56acf9f3211d696dfd6fa3e1a3ad4b32922 100644 (file)
@@ -241,106 +241,6 @@ public:
     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