|
28 | 28 | import omni.physx |
29 | 29 | import omni.timeline |
30 | 30 | import omni.usd |
31 | | -from pxr import Sdf |
| 31 | +from pxr import Sdf, UsdUtils |
32 | 32 |
|
33 | 33 | import isaaclab.sim as sim_utils |
34 | 34 | from isaaclab.physics import CallbackHandle, PhysicsEvent, PhysicsManager |
@@ -290,6 +290,30 @@ def stop(cls) -> None: |
290 | 290 | # Pump events so timeline callbacks fire synchronously |
291 | 291 | omni.kit.app.get_app().update() |
292 | 292 |
|
| 293 | + @classmethod |
| 294 | + def wait_for_playing(cls) -> None: |
| 295 | + """Block until the timeline is playing, keeping the GUI responsive. |
| 296 | +
|
| 297 | + After resume, forces a fabric re-sync so articulation meshes unfreeze. |
| 298 | + See: https://github.com/isaac-sim/IsaacLab/issues/4279 |
| 299 | + """ |
| 300 | + if cls._timeline.is_playing(): |
| 301 | + return |
| 302 | + app = omni.kit.app.get_app() |
| 303 | + while not cls._timeline.is_playing(): |
| 304 | + app.update() |
| 305 | + if cls._timeline.is_stopped(): |
| 306 | + break |
| 307 | + # Force fabric to re-sync articulation transforms after resume. |
| 308 | + # detach/attach resets the FabricManager, then we immediately push |
| 309 | + # current poses so the first render after resume shows correct state. |
| 310 | + if not cls._timeline.is_stopped(): |
| 311 | + cls._re_sync_fabric() |
| 312 | + if cls._view is not None: |
| 313 | + cls._view.update_articulations_kinematic() |
| 314 | + if cls._update_fabric is not None: |
| 315 | + cls._update_fabric(0.0, 0.0) |
| 316 | + |
293 | 317 | @classmethod |
294 | 318 | def close(cls) -> None: |
295 | 319 | """Clean up physics resources.""" |
@@ -585,6 +609,42 @@ def _load_fabric(cls) -> None: |
585 | 609 | sim.set_setting("/isaaclab/fabric_enabled", use_fabric) # type: ignore[union-attr] |
586 | 610 | sim.set_setting("/physics/visualizationDisplaySimulationOutput", False) # type: ignore[union-attr] |
587 | 611 |
|
| 612 | + @classmethod |
| 613 | + def _re_sync_fabric(cls) -> None: |
| 614 | + """Force the PhysX fabric extension to re-synchronize after a pause/resume transition. |
| 615 | +
|
| 616 | + Starting with PhysX fabric 107.3.21 (Isaac Sim 5.1), the FabricManager skips writing |
| 617 | + initial articulation poses to fabric on subsequent resumes, causing articulation meshes |
| 618 | + to freeze visually while physics continues to run. |
| 619 | +
|
| 620 | + The workaround detaches and re-attaches the USD stage on the fabric interface, forcing |
| 621 | + the FabricManager to fully reinitialize and write transforms into fabric so that Hydra |
| 622 | + picks them up. |
| 623 | + """ |
| 624 | + if cls._fabric is None: |
| 625 | + return |
| 626 | + sim = PhysicsManager._sim |
| 627 | + if sim is None: |
| 628 | + return |
| 629 | + stage = sim.stage |
| 630 | + if stage is None: |
| 631 | + return |
| 632 | + stage_id = UsdUtils.StageCache.Get().GetId(stage).ToLongInt() |
| 633 | + if stage_id <= 0: |
| 634 | + return |
| 635 | + try: |
| 636 | + cls._fabric.detach_stage() |
| 637 | + except Exception: |
| 638 | + logger.warning("Failed to detach fabric stage during re-sync. Articulation visuals may be stale.") |
| 639 | + return |
| 640 | + try: |
| 641 | + cls._fabric.attach_stage(stage_id) |
| 642 | + except Exception: |
| 643 | + logger.error( |
| 644 | + "Could not re-attach fabric stage. Articulation visuals will be broken until next reset.", |
| 645 | + exc_info=True, |
| 646 | + ) |
| 647 | + |
588 | 648 | @classmethod |
589 | 649 | def _warmup_and_create_views(cls) -> None: |
590 | 650 | """Warm-start physics and create simulation views.""" |
|
0 commit comments