Skip to content

Commit ec500e8

Browse files
committed
Fix PhysX scene data for joint-labeled bodies
1 parent dc51341 commit ec500e8

6 files changed

Lines changed: 104 additions & 1 deletion

File tree

source/isaaclab/test/sim/test_newton_manager_visualization_state.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,24 @@ def test_update_visualization_state_noop_when_backend_is_newton(monkeypatch):
161161
NewtonManager.update_visualization_state()
162162
assert NewtonManager._model == "live-model"
163163
assert NewtonManager._state_0 == "live-state"
164+
165+
166+
def test_resolve_scene_data_body_paths_uses_joint_body_targets():
167+
"""PhysX visualization sync maps Newton joint labels to the actual body prim path."""
168+
import pytest
169+
170+
pytest.importorskip("pxr")
171+
from isaaclab_newton.physics import NewtonManager
172+
173+
from pxr import Usd, UsdGeom, UsdPhysics
174+
175+
stage = Usd.Stage.CreateInMemory()
176+
body_prim = UsdGeom.Xform.Define(stage, "/World/envs/env_0/Robot/robot0_forearm").GetPrim()
177+
UsdPhysics.RigidBodyAPI.Apply(body_prim)
178+
joint = UsdPhysics.FixedJoint.Define(stage, "/World/envs/env_0/Robot/joints/robot0_forearm")
179+
joint.GetBody1Rel().SetTargets([body_prim.GetPath()])
180+
181+
body_paths = ["/World/envs/env_0/Robot/joints/robot0_forearm"]
182+
resolved_paths = NewtonManager._resolve_scene_data_body_paths(body_paths, stage)
183+
184+
assert resolved_paths == ["/World/envs/env_0/Robot/robot0_forearm"]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fixed
2+
^^^^^
3+
4+
* Fixed Newton visualizers on PhysX simulations when a Newton body label points
5+
at a USD joint prim by resolving the label through the joint's rigid-body target.

source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1846,12 +1846,38 @@ def update_visualization_state(cls, scene_data_provider: SceneDataProvider | Non
18461846
if cls._scene_data is None:
18471847
cls._scene_data = SceneDataFormat.Transform()
18481848
if cls._scene_data_mapping is None:
1849-
body_paths = list(getattr(cls._model, "body_label", None) or [])
1849+
body_paths = cls._resolve_scene_data_body_paths(list(cls._model.body_label), scene_data_provider.usd_stage)
18501850
cls._scene_data_mapping = scene_data_provider.create_mapping(body_paths)
18511851

18521852
cls._scene_data.transforms = cls._state_0.body_q
18531853
scene_data_provider.get_transforms(cls._scene_data, mapping=cls._scene_data_mapping)
18541854

1855+
@staticmethod
1856+
def _resolve_scene_data_body_paths(body_paths: list[str | None], stage) -> list[str | None]:
1857+
"""Map Newton joint labels to their target rigid-body prim paths."""
1858+
if stage is None:
1859+
return body_paths
1860+
1861+
from pxr import UsdPhysics
1862+
1863+
def _joint_body_path(prim):
1864+
joint = UsdPhysics.Joint(prim)
1865+
for rel in (joint.GetBody1Rel(), joint.GetBody0Rel()):
1866+
for target_path in rel.GetTargets():
1867+
target_prim = stage.GetPrimAtPath(target_path)
1868+
if target_prim.IsValid() and target_prim.HasAPI(UsdPhysics.RigidBodyAPI):
1869+
return target_path.pathString
1870+
return None
1871+
1872+
resolved_paths = body_paths.copy()
1873+
for index, body_path in enumerate(body_paths):
1874+
if body_path is None:
1875+
continue
1876+
prim = stage.GetPrimAtPath(body_path)
1877+
if prim.IsValid() and prim.IsA(UsdPhysics.Joint):
1878+
resolved_paths[index] = _joint_body_path(prim) or body_path
1879+
return resolved_paths
1880+
18551881
@classmethod
18561882
def get_state_1(cls) -> State:
18571883
"""Get the next state."""
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fixed
2+
^^^^^
3+
4+
* Fixed PhysX scene-data rigid-body view discovery to ignore USD joint prims
5+
even when an asset authors ``RigidBodyAPI`` on them.

source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ def get_rigid_body_view(self) -> omni.physics.tensors.RigidBodyView | None:
191191
rigid_body_paths: list[str] = []
192192
non_rigid_body_names: set[str] = set()
193193
for prim in stage.Traverse():
194+
if prim.IsA(UsdPhysics.Joint):
195+
continue
194196
prim_path = prim.GetPath().pathString
195197
if prim.HasAPI(UsdPhysics.RigidBodyAPI):
196198
rigid_body_paths.append(prim_path)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
2+
# All rights reserved.
3+
#
4+
# SPDX-License-Identifier: BSD-3-Clause
5+
6+
from types import SimpleNamespace
7+
8+
import pytest
9+
10+
pytest.importorskip("pxr")
11+
pytest.importorskip("omni.physics.tensors")
12+
13+
14+
def test_scene_data_rigid_body_view_skips_joint_prims_with_rigid_body_api(monkeypatch):
15+
"""Joint prims must not be passed to PhysX tensor rigid-body views."""
16+
from isaaclab_physx.physics import physx_manager
17+
from isaaclab_physx.physics.physx_manager import PhysxSceneDataBackend
18+
19+
from pxr import Usd, UsdGeom, UsdPhysics
20+
21+
stage = Usd.Stage.CreateInMemory()
22+
body_prim = UsdGeom.Xform.Define(stage, "/World/envs/env_0/Robot/robot0_forearm").GetPrim()
23+
UsdPhysics.RigidBodyAPI.Apply(body_prim)
24+
joint_prim = UsdPhysics.FixedJoint.Define(stage, "/World/envs/env_0/Robot/joints/robot0_forearm").GetPrim()
25+
UsdPhysics.RigidBodyAPI.Apply(joint_prim)
26+
27+
captured_paths = []
28+
29+
class _SimulationView:
30+
def create_rigid_body_view(self, body_paths):
31+
captured_paths.extend(body_paths)
32+
return SimpleNamespace(prim_paths=body_paths)
33+
34+
monkeypatch.setattr(
35+
physx_manager.omni.usd,
36+
"get_context",
37+
lambda: SimpleNamespace(get_stage=lambda: stage),
38+
)
39+
40+
backend = PhysxSceneDataBackend()
41+
backend.simulation_view = _SimulationView()
42+
backend.get_rigid_body_view()
43+
44+
assert captured_paths == ["/World/envs/env_*/Robot/robot0_forearm"]

0 commit comments

Comments
 (0)