Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,24 @@ def test_update_visualization_state_noop_when_backend_is_newton(monkeypatch):
NewtonManager.update_visualization_state()
assert NewtonManager._model == "live-model"
assert NewtonManager._state_0 == "live-state"


def test_resolve_scene_data_body_paths_uses_joint_body_targets():
"""PhysX visualization sync maps Newton joint labels to the actual body prim path."""
import pytest

pytest.importorskip("pxr")
from isaaclab_newton.physics import NewtonManager

from pxr import Usd, UsdGeom, UsdPhysics

stage = Usd.Stage.CreateInMemory()
body_prim = UsdGeom.Xform.Define(stage, "/World/envs/env_0/Robot/robot0_forearm").GetPrim()
UsdPhysics.RigidBodyAPI.Apply(body_prim)
joint = UsdPhysics.FixedJoint.Define(stage, "/World/envs/env_0/Robot/joints/robot0_forearm")
joint.GetBody1Rel().SetTargets([body_prim.GetPath()])

body_paths = ["/World/envs/env_0/Robot/joints/robot0_forearm"]
resolved_paths = NewtonManager._resolve_scene_data_body_paths(body_paths, stage)

assert resolved_paths == ["/World/envs/env_0/Robot/robot0_forearm"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Fixed
^^^^^

* Fixed Newton visualizers on PhysX simulations when a Newton body label points
at a USD joint prim by resolving the label through the joint's rigid-body target.
28 changes: 27 additions & 1 deletion source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1861,12 +1861,38 @@ def update_visualization_state(cls, scene_data_provider: SceneDataProvider | Non
if cls._scene_data is None:
cls._scene_data = SceneDataFormat.Transform()
if cls._scene_data_mapping is None:
body_paths = list(getattr(cls._model, "body_label", None) or [])
body_paths = cls._resolve_scene_data_body_paths(list(cls._model.body_label), scene_data_provider.usd_stage)
cls._scene_data_mapping = scene_data_provider.create_mapping(body_paths)

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

@staticmethod
def _resolve_scene_data_body_paths(body_paths: list[str | None], stage) -> list[str | None]:
"""Map Newton joint labels to their target rigid-body prim paths."""
if stage is None:
return body_paths

from pxr import UsdPhysics

def _joint_body_path(prim):
joint = UsdPhysics.Joint(prim)
for rel in (joint.GetBody1Rel(), joint.GetBody0Rel()):
for target_path in rel.GetTargets():
target_prim = stage.GetPrimAtPath(target_path)
if target_prim.IsValid() and target_prim.HasAPI(UsdPhysics.RigidBodyAPI):
return target_path.pathString
return None

resolved_paths = body_paths.copy()
for index, body_path in enumerate(body_paths):
if body_path is None:
continue
prim = stage.GetPrimAtPath(body_path)
if prim.IsValid() and prim.IsA(UsdPhysics.Joint):
resolved_paths[index] = _joint_body_path(prim) or body_path
return resolved_paths

@classmethod
def get_state_1(cls) -> State:
"""Get the next state."""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Fixed
^^^^^

* Fixed PhysX scene-data rigid-body view discovery to ignore USD joint prims
even when an asset authors ``RigidBodyAPI`` on them.
2 changes: 2 additions & 0 deletions source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ def get_rigid_body_view(self) -> omni.physics.tensors.RigidBodyView | None:
rigid_body_paths: list[str] = []
non_rigid_body_names: set[str] = set()
for prim in stage.Traverse():
if prim.IsA(UsdPhysics.Joint):
continue
prim_path = prim.GetPath().pathString
if prim.HasAPI(UsdPhysics.RigidBodyAPI):
rigid_body_paths.append(prim_path)
Expand Down
44 changes: 44 additions & 0 deletions source/isaaclab_physx/test/sim/test_physx_scene_data_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

from types import SimpleNamespace

import pytest

pytest.importorskip("pxr")
pytest.importorskip("omni.physics.tensors")


def test_scene_data_rigid_body_view_skips_joint_prims_with_rigid_body_api(monkeypatch):
"""Joint prims must not be passed to PhysX tensor rigid-body views."""
from isaaclab_physx.physics import physx_manager
from isaaclab_physx.physics.physx_manager import PhysxSceneDataBackend

from pxr import Usd, UsdGeom, UsdPhysics

stage = Usd.Stage.CreateInMemory()
body_prim = UsdGeom.Xform.Define(stage, "/World/envs/env_0/Robot/robot0_forearm").GetPrim()
UsdPhysics.RigidBodyAPI.Apply(body_prim)
joint_prim = UsdPhysics.FixedJoint.Define(stage, "/World/envs/env_0/Robot/joints/robot0_forearm").GetPrim()
UsdPhysics.RigidBodyAPI.Apply(joint_prim)

captured_paths = []

class _SimulationView:
def create_rigid_body_view(self, body_paths):
captured_paths.extend(body_paths)
return SimpleNamespace(prim_paths=body_paths)

monkeypatch.setattr(
physx_manager.omni.usd,
"get_context",
lambda: SimpleNamespace(get_stage=lambda: stage),
)

backend = PhysxSceneDataBackend()
backend.simulation_view = _SimulationView()
backend.get_rigid_body_view()

assert captured_paths == ["/World/envs/env_*/Robot/robot0_forearm"]
Loading