From afea0c890286360b5469a05bb0fc695d6991a881 Mon Sep 17 00:00:00 2001 From: Anoop Gadhrri Date: Thu, 19 Mar 2026 12:00:45 -0400 Subject: [PATCH] Fix randomize_rigid_body_material crash with Newton backend The `randomize_rigid_body_material` event term crashes when using the Newton physics backend because it unconditionally accesses PhysX-specific APIs (`root_view.link_paths`, `_physics_sim_view.create_rigid_body_view`, `root_view.max_shapes`) that do not exist on Newton's `ArticulationView`. Newton's underlying engine (MuJoCo-Warp) does not currently expose runtime material property APIs (`get_material_properties`, `set_material_properties`), so full material randomization is not yet possible on the Newton backend. This change: - Detects backend support by checking for `root_view.max_shapes` - Gracefully skips with a warning when the backend lacks support - Also uses `asset.num_shapes_per_body` when available (Newton provides this property), avoiding the PhysX-specific `link_paths` workaround - Preserves existing PhysX behavior unchanged --- source/isaaclab/isaaclab/envs/mdp/events.py | 27 +++++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/source/isaaclab/isaaclab/envs/mdp/events.py b/source/isaaclab/isaaclab/envs/mdp/events.py index 18c9b5eff63..d47f82d3e5f 100644 --- a/source/isaaclab/isaaclab/envs/mdp/events.py +++ b/source/isaaclab/isaaclab/envs/mdp/events.py @@ -206,11 +206,24 @@ def __init__(self, cfg: EventTermCfg, env: ManagerBasedEnv): # obtain number of shapes per body (needed for indexing the material properties correctly) # note: this is a workaround since the Articulation does not provide a direct way to obtain the number of shapes # per body. We use the physics simulation view to obtain the number of shapes per body. - if isinstance(self.asset, BaseArticulation) and self.asset_cfg.body_ids != slice(None): - self.num_shapes_per_body = [] - for link_path in self.asset.root_view.link_paths[0]: - link_physx_view = self.asset._physics_sim_view.create_rigid_body_view(link_path) # type: ignore - self.num_shapes_per_body.append(link_physx_view.max_shapes) + # Check if the backend supports material property APIs + self._supports_material_randomization = hasattr(self.asset.root_view, "max_shapes") + if not self._supports_material_randomization: + logger.warning( + "randomize_rigid_body_material: the current physics backend does not support" + " material property randomization (missing root_view.max_shapes). Skipping." + ) + self.num_shapes_per_body = None + elif isinstance(self.asset, BaseArticulation) and self.asset_cfg.body_ids != slice(None): + if hasattr(self.asset, "num_shapes_per_body"): + # Backend (e.g. Newton) provides this directly + self.num_shapes_per_body = self.asset.num_shapes_per_body + else: + # PhysX backend: compute from physics simulation view + self.num_shapes_per_body = [] + for link_path in self.asset.root_view.link_paths[0]: + link_physx_view = self.asset._physics_sim_view.create_rigid_body_view(link_path) # type: ignore + self.num_shapes_per_body.append(link_physx_view.max_shapes) # ensure the parsing is correct num_shapes = sum(self.num_shapes_per_body) expected_shapes = self.asset.root_view.max_shapes @@ -252,6 +265,10 @@ def __call__( asset_cfg: SceneEntityCfg, make_consistent: bool = False, ): + # skip if backend does not support material randomization + if not self._supports_material_randomization: + return + # resolve environment ids if env_ids is None: env_ids = torch.arange(env.scene.num_envs, device="cpu", dtype=torch.int32)