@@ -1545,27 +1545,35 @@ def sample_textures(self, fragments):
1545
1545
else :
1546
1546
raise ValueError ("Meshes does not have textures" )
1547
1547
1548
- def centroid (self ):
1548
+ def volume_centroid (self ):
1549
1549
"""
1550
1550
Compute the volumetric centroid of this mesh, which is distinct from the center of mass.
1551
1551
The center of mass (average of all vertices) will be closer to where there are a
1552
1552
higher density of points in a mesh are, but the centroid, which is based on volume,
1553
1553
will be closer to a perceived center of the mesh, as opposed to based on the density
1554
- of vertices.
1554
+ of vertices. This function assumes that the mesh is watertight, and that the faces are
1555
+ all oriented in the same direction.
1555
1556
1556
1557
Returns:
1557
1558
The position of the centroid as a tensor of shape (3).
1558
1559
"""
1559
- v_idxs = self .faces_packed ().split ([1 , 1 , 1 ], dim = - 1 )
1560
- verts = self .verts_packed ()
1561
- v0 , v1 , v2 = [verts [idx ].squeeze (- 2 ) for idx in v_idxs ]
1560
+ v_idxs = self .faces_padded ().split ([1 , 1 , 1 ], dim = - 1 )
1561
+ verts = self .verts_padded ()
1562
+
1563
+ v0 , v1 , v2 = [torch .gather (verts , 1 , idx .expand (- 1 , - 1 , 3 )) for idx in v_idxs ]
1564
+
1562
1565
tetra_center = (v0 + v1 + v2 ) / 4
1563
1566
signed_tetra_vol = (v0 * torch .cross (v1 , v2 , dim = - 1 )).sum (
1564
1567
dim = - 1 , keepdim = True
1565
1568
) / 6
1566
- return (tetra_center * signed_tetra_vol ).sum (dim = - 2 ) / signed_tetra_vol .sum (
1567
- dim = - 2
1568
- ).clamp (min = 1e-5 )
1569
+ denom = signed_tetra_vol .sum (dim = - 2 )
1570
+ # clamp the denominator to prevent instability for degenerate meshes.
1571
+ denom = torch .where (
1572
+ denom < 0 ,
1573
+ denom .clamp (max = - 1e-5 ),
1574
+ denom .clamp (min = 1e-5 )
1575
+ )
1576
+ return (tetra_center * signed_tetra_vol ).sum (dim = - 2 ) / denom
1569
1577
1570
1578
def submeshes (
1571
1579
self ,
0 commit comments