diff --git a/source/isaaclab/isaaclab/sim/simulation_context.py b/source/isaaclab/isaaclab/sim/simulation_context.py index 07ee06819e64..4eb82b8ac4ad 100644 --- a/source/isaaclab/isaaclab/sim/simulation_context.py +++ b/source/isaaclab/isaaclab/sim/simulation_context.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022-2025, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# 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 @@ -30,7 +30,7 @@ from isaacsim.core.simulation_manager import SimulationManager from isaacsim.core.utils.viewports import set_camera_view from isaacsim.core.version import get_version -from pxr import Gf, PhysxSchema, Sdf, Usd, UsdPhysics +from pxr import Gf, PhysxSchema, Sdf, Usd, UsdPhysics, UsdUtils from isaaclab.sim.utils import stage as stage_utils @@ -550,6 +550,11 @@ def step(self, render: bool = True): # meantime if someone stops, break out of the loop if self.is_stopped(): break + if not self.is_stopped(): + # HACK: PhysX Fabric 107.3.21+ skips writing articulation poses on subsequent resumes. + # Remove once the IsaacLab pin includes https://github.com/isaac-sim/IsaacLab/pull/5178 + # or the underlying PhysX issue https://github.com/isaac-sim/IsaacLab/issues/4279 is fixed. + self._re_sync_fabric() # need to do one step to refresh the app # reason: physics has to parse the scene again and inform other extensions like hydra-delegate. # without this the app becomes unresponsive. @@ -855,6 +860,28 @@ def _load_fabric_interface(self): # Needed for backward compatibility with older Isaac Sim versions self._update_fabric = self._fabric_iface.update + def _re_sync_fabric(self) -> None: + """Force Fabric to re-synchronize after a pause/resume transition. + + Detaching and re-attaching the USD stage forces FabricManager to reinitialize and write + articulation transforms for Hydra after PhysX Fabric 107.3.21+ skips that initialization + on subsequent resumes. + """ + if self._fabric_iface is None: + return + + stage = self.stage + if stage is None: + return + + stage_id = UsdUtils.StageCache.Get().GetId(stage).ToLongInt() + if stage_id <= 0: + return + + self._fabric_iface.detach_stage() + self._fabric_iface.attach_stage(stage_id) + self._update_fabric(0.0, 0.0) + def _update_anim_recording(self): """Tracks anim recording timestamps and triggers finish animation recording if the total time has elapsed.""" if self._anim_recording_started_timestamp is None: