Fix: USD: Correct the exported extents for point instancers

Adds and corrects the extent attributes of USD PointInstancer prims.
Extents are now computed for PointInstancers just before the USD stage
is saved and during the export finalization step. The unit test has been
updated accordingly.

This PR also marks all point instancers' prototypes as over after the
extent calculation is done, including the prototypes used by nested
point instancers. This follows the official USD recommendation to place
prototypes under a point instancer marked as over:
https://openusd.org/docs/api/class_usd_geom_point_instancer.html#:~:text=place%20them%20under%20a%20prim%20that%20is%20just%20an%20%22over%22

Authored by Apple: Zili Zhou (Liz)

Co-authored-by: Zili (Liz) Zhou <zili_zhou@apple.com>
Pull Request: https://projects.blender.org/blender/blender/pulls/141299
This commit is contained in:
Michael B Johnson
2025-07-08 06:19:32 +02:00
committed by Jesse Yurkovich
parent 4431d7a369
commit a5f915d3d3
3 changed files with 74 additions and 19 deletions

View File

@@ -1736,28 +1736,42 @@ class USDExportTest(AbstractUSDTest):
'mesh_count': 3,
'instancer_count': 1,
'total_instances': 16,
'total_prototypes': 1},
'total_prototypes': 1,
'extent': {
"/root/Plane/Mesh": [Gf.Vec3f(-1.0999999, -1.0999999, -0.1),
Gf.Vec3f(1.1, 1.1, 0.1)]}},
# collection reference from single point instancer
{'input_file': str(self.testdir / "usd_point_instancer_collection_ref.blend"),
'output_file': self.tempdir / "usd_export_point_instancer_collection_ref.usda",
'mesh_count': 5,
'instancer_count': 1,
'total_instances': 32,
'total_prototypes': 2},
'total_prototypes': 2,
'extent': {
"/root/Plane/Mesh": [Gf.Vec3f(-1.1758227, -1.1, -0.1),
Gf.Vec3f(1.1, 1.1526861, 0.14081651)]}},
# collection references in nested point instancer
{'input_file': str(self.testdir / "usd_point_instancer_nested.blend"),
'output_file': self.tempdir / "usd_export_point_instancer_nested.usda",
'mesh_count': 9,
'instancer_count': 3,
'total_instances': 14,
'total_prototypes': 4},
'total_prototypes': 4,
'extent': {
"/root/Triangle/Triangle": [Gf.Vec3f(-0.976631, -1.2236981, -0.7395363),
Gf.Vec3f(1.8081428, 3.371673, 1.2604637)],
"/root/Plane/Plane": [Gf.Vec3f(-1.164238, -3.5953712, -0.2883494),
Gf.Vec3f(-0.68365526, -3.1147888, -0.18980181)]}},
# object reference coming from a collection with separate children
{'input_file': str(self.testdir / "../render/shader/texture_coordinate_camera.blend"),
'output_file': self.tempdir / "usd_export_point_instancer_separate_children.usda",
'mesh_count': 9,
'instancer_count': 1,
'total_instances': 4,
'total_prototypes': 2}
'total_prototypes': 2,
'extent': {
"/root/Rotated_and_Scaled_Instances/Cube_003": [Gf.Vec3f(-8.488519, -6.1219244, -6.964829),
Gf.Vec3f(3.2331002, 5.4789553, 7.095813)]}}
]
for scenario in point_instance_test_scenarios:
@@ -1777,6 +1791,20 @@ class USDExportTest(AbstractUSDTest):
self.assertEqual(scenario['instancer_count'], instancer_count, "Unexpected number of point instancers")
self.assertEqual(scenario['total_instances'], instance_count, "Unexpected number of total instances")
self.assertEqual(scenario['total_prototypes'], proto_count, "Unexpected number of total prototypes")
if 'extent' in scenario:
for prim_path, (expected_min, expected_max) in scenario['extent'].items():
prim = stage.GetPrimAtPath(prim_path)
self.assertTrue(prim.IsValid(), f"Prim {prim_path} not found on stage")
boundable = UsdGeom.Boundable(prim)
extent_attr = boundable.GetExtentAttr()
self.assertTrue(extent_attr.HasAuthoredValue(), f"Prim {prim_path} has no authored extent")
extent = extent_attr.Get()
self.assertIsNotNone(extent, f"Extent on {prim_path} could not be retrieved")
self.compareVec3d(Gf.Vec3d(extent[0]), expected_min)
self.compareVec3d(Gf.Vec3d(extent[1]), expected_max)
class USDHookBase: