From 40954172964a61d802b307168ff3b499be36f5ef Mon Sep 17 00:00:00 2001 From: HuiDong Chen Date: Wed, 24 Jun 2026 09:18:36 +0800 Subject: [PATCH 1/9] Bump newton version pin to ``79e95bf5571d70a0a46c8eaedc80644531d27368`` (#6242) # Description Fixed the `newton[sim]` dependency pin to use Newton commit `79e95bf5571d70a0a46c8eaedc80644531d27368`, including the `RenderContext` triangle-mesh construction fix from [newton-physics/newton#3199](https://github.com/newton-physics/newton/pull/3199). ## Type of change - Bug fix (non-breaking change which fixes an issue) ## Checklist - [x] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --- source/isaaclab/changelog.d/bump-newton-pin.rst | 7 +++++++ source/isaaclab_newton/changelog.d/bump-newton-pin.rst | 7 +++++++ source/isaaclab_newton/pyproject.toml | 2 +- source/isaaclab_physx/changelog.d/bump-newton-pin.rst | 7 +++++++ source/isaaclab_physx/pyproject.toml | 2 +- .../isaaclab_visualizers/changelog.d/bump-newton-pin.rst | 7 +++++++ source/isaaclab_visualizers/pyproject.toml | 8 ++++---- tools/wheel_builder/res/python_packages.toml | 2 +- 8 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 source/isaaclab/changelog.d/bump-newton-pin.rst create mode 100644 source/isaaclab_newton/changelog.d/bump-newton-pin.rst create mode 100644 source/isaaclab_physx/changelog.d/bump-newton-pin.rst create mode 100644 source/isaaclab_visualizers/changelog.d/bump-newton-pin.rst diff --git a/source/isaaclab/changelog.d/bump-newton-pin.rst b/source/isaaclab/changelog.d/bump-newton-pin.rst new file mode 100644 index 000000000000..90a5b53e3bc7 --- /dev/null +++ b/source/isaaclab/changelog.d/bump-newton-pin.rst @@ -0,0 +1,7 @@ +Fixed +^^^^^ + +* Fixed the wheel-builder ``newton[sim]`` dependency pin to use Newton commit + ``79e95bf5571d70a0a46c8eaedc80644531d27368``, including the + RenderContext triangle-mesh construction fix from `newton-physics/newton#3199 + `_. diff --git a/source/isaaclab_newton/changelog.d/bump-newton-pin.rst b/source/isaaclab_newton/changelog.d/bump-newton-pin.rst new file mode 100644 index 000000000000..e38454e51070 --- /dev/null +++ b/source/isaaclab_newton/changelog.d/bump-newton-pin.rst @@ -0,0 +1,7 @@ +Fixed +^^^^^ + +* Fixed the ``newton[sim]`` dependency pin to use Newton commit + ``79e95bf5571d70a0a46c8eaedc80644531d27368``, including the + RenderContext triangle-mesh construction fix from `newton-physics/newton#3199 + `_. diff --git a/source/isaaclab_newton/pyproject.toml b/source/isaaclab_newton/pyproject.toml index 880c175855d5..a59ec5015409 100644 --- a/source/isaaclab_newton/pyproject.toml +++ b/source/isaaclab_newton/pyproject.toml @@ -27,7 +27,7 @@ all = [ "prettytable>=3.3.0", "PyOpenGL-accelerate>=3.1.0", "pyglet>=2.1.6,<3", - "newton[sim] @ git+https://github.com/newton-physics/newton.git@811968bfb7cc7ff4e37b9260a2ba56930a3e605e", + "newton[sim] @ git+https://github.com/newton-physics/newton.git@79e95bf5571d70a0a46c8eaedc80644531d27368", ] [tool.setuptools] diff --git a/source/isaaclab_physx/changelog.d/bump-newton-pin.rst b/source/isaaclab_physx/changelog.d/bump-newton-pin.rst new file mode 100644 index 000000000000..676868e37a8e --- /dev/null +++ b/source/isaaclab_physx/changelog.d/bump-newton-pin.rst @@ -0,0 +1,7 @@ +Fixed +^^^^^ + +* Fixed the optional ``newton[sim]`` dependency pin to use Newton commit + ``79e95bf5571d70a0a46c8eaedc80644531d27368``, including the + RenderContext triangle-mesh construction fix from `newton-physics/newton#3199 + `_. diff --git a/source/isaaclab_physx/pyproject.toml b/source/isaaclab_physx/pyproject.toml index f63c83809f95..ffc37e686cff 100644 --- a/source/isaaclab_physx/pyproject.toml +++ b/source/isaaclab_physx/pyproject.toml @@ -24,7 +24,7 @@ Repository = "https://github.com/isaac-sim/IsaacLab" [project.optional-dependencies] newton = [ - "newton[sim] @ git+https://github.com/newton-physics/newton.git@811968bfb7cc7ff4e37b9260a2ba56930a3e605e", + "newton[sim] @ git+https://github.com/newton-physics/newton.git@79e95bf5571d70a0a46c8eaedc80644531d27368", ] [tool.setuptools] diff --git a/source/isaaclab_visualizers/changelog.d/bump-newton-pin.rst b/source/isaaclab_visualizers/changelog.d/bump-newton-pin.rst new file mode 100644 index 000000000000..d8cb1021d482 --- /dev/null +++ b/source/isaaclab_visualizers/changelog.d/bump-newton-pin.rst @@ -0,0 +1,7 @@ +Fixed +^^^^^ + +* Fixed the visualizer extras' ``newton[sim]`` dependency pin to use Newton + commit ``79e95bf5571d70a0a46c8eaedc80644531d27368``, including the + RenderContext triangle-mesh construction fix from `newton-physics/newton#3199 + `_. diff --git a/source/isaaclab_visualizers/pyproject.toml b/source/isaaclab_visualizers/pyproject.toml index 9b0aeefcf3e7..1dda46c96e2a 100644 --- a/source/isaaclab_visualizers/pyproject.toml +++ b/source/isaaclab_visualizers/pyproject.toml @@ -29,24 +29,24 @@ Repository = "https://github.com/isaac-sim/IsaacLab" kit = [] newton = [ "warp-lang", - "newton[sim] @ git+https://github.com/newton-physics/newton.git@811968bfb7cc7ff4e37b9260a2ba56930a3e605e", + "newton[sim] @ git+https://github.com/newton-physics/newton.git@79e95bf5571d70a0a46c8eaedc80644531d27368", "PyOpenGL-accelerate", "pyglet>=2.1.6,<3", "imgui-bundle>=1.92.5", "typing-extensions>=4.15.0", ] rerun = [ - "newton[sim] @ git+https://github.com/newton-physics/newton.git@811968bfb7cc7ff4e37b9260a2ba56930a3e605e", + "newton[sim] @ git+https://github.com/newton-physics/newton.git@79e95bf5571d70a0a46c8eaedc80644531d27368", "rerun-sdk>=0.29.0", "pyarrow==22.0.0", ] viser = [ - "newton[sim] @ git+https://github.com/newton-physics/newton.git@811968bfb7cc7ff4e37b9260a2ba56930a3e605e", + "newton[sim] @ git+https://github.com/newton-physics/newton.git@79e95bf5571d70a0a46c8eaedc80644531d27368", "viser>=1.0.16", ] all = [ "imgui-bundle>=1.92.5", - "newton[sim] @ git+https://github.com/newton-physics/newton.git@811968bfb7cc7ff4e37b9260a2ba56930a3e605e", + "newton[sim] @ git+https://github.com/newton-physics/newton.git@79e95bf5571d70a0a46c8eaedc80644531d27368", "PyOpenGL-accelerate", "pyglet>=2.1.6,<3", # Match rerun-sdk's supported Arrow stack and avoid resolver drift across environments. diff --git a/tools/wheel_builder/res/python_packages.toml b/tools/wheel_builder/res/python_packages.toml index db104ce02f0c..72926c0a93d3 100644 --- a/tools/wheel_builder/res/python_packages.toml +++ b/tools/wheel_builder/res/python_packages.toml @@ -88,7 +88,7 @@ pyproject.optional-dependencies.all = [ # ================================================================================ { "newton" = [ "warp-lang==1.14.0", - "newton[sim] @ git+https://github.com/newton-physics/newton.git@811968bfb7cc7ff4e37b9260a2ba56930a3e605e", + "newton[sim] @ git+https://github.com/newton-physics/newton.git@79e95bf5571d70a0a46c8eaedc80644531d27368", "PyOpenGL-accelerate==3.1.10" ] }, # ================================================================================ From 1013ef9e4418e98d55968a1e3b93704ee5dd9238 Mon Sep 17 00:00:00 2001 From: "isaaclab-bot[bot]" <282401363+isaaclab-bot[bot]@users.noreply.github.com> Date: Wed, 24 Jun 2026 06:24:00 +0000 Subject: [PATCH 2/9] [CI][Auto Version Bump] Compile changelog fragments (schedule) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumped packages: - isaaclab: 7.0.5 → 7.1.0 - isaaclab_newton: 1.0.4 → 1.1.0 - isaaclab_ov: 0.4.6 → 0.5.0 - isaaclab_physx: 2.0.2 → 2.1.0 - isaaclab_tasks: 8.0.4 → 8.0.5 - isaaclab_visualizers: 1.0.1 → 1.0.2 --- .../isaaclab/changelog.d/bump-newton-pin.rst | 7 ----- .../changelog.d/jmart-cartpole-rtx.minor.rst | 12 ------- .../isaaclab/changelog.d/jmart-dr-reset.rst | 9 ------ source/isaaclab/config/extension.toml | 2 +- source/isaaclab/docs/CHANGELOG.rst | 31 +++++++++++++++++++ source/isaaclab/pyproject.toml | 2 +- .../changelog.d/bump-newton-pin.rst | 7 ----- .../changelog.d/jmart-cartpole-rtx.minor.rst | 7 ----- source/isaaclab_newton/config/extension.toml | 2 +- source/isaaclab_newton/docs/CHANGELOG.rst | 20 ++++++++++++ source/isaaclab_newton/pyproject.toml | 2 +- .../changelog.d/jmart-cartpole-rtx.minor.rst | 6 ---- source/isaaclab_ov/config/extension.toml | 2 +- source/isaaclab_ov/docs/CHANGELOG.rst | 11 +++++++ source/isaaclab_ov/pyproject.toml | 2 +- .../changelog.d/bump-newton-pin.rst | 7 ----- .../changelog.d/jmart-cartpole-rtx.minor.rst | 6 ---- source/isaaclab_physx/config/extension.toml | 2 +- source/isaaclab_physx/docs/CHANGELOG.rst | 19 ++++++++++++ source/isaaclab_physx/pyproject.toml | 2 +- .../changelog.d/jmart-cartpole-rtx.rst | 13 -------- source/isaaclab_tasks/config/extension.toml | 2 +- source/isaaclab_tasks/docs/CHANGELOG.rst | 18 +++++++++++ source/isaaclab_tasks/pyproject.toml | 2 +- .../changelog.d/bump-newton-pin.rst | 7 ----- .../config/extension.toml | 2 +- .../isaaclab_visualizers/docs/CHANGELOG.rst | 12 +++++++ source/isaaclab_visualizers/pyproject.toml | 2 +- 28 files changed, 123 insertions(+), 93 deletions(-) delete mode 100644 source/isaaclab/changelog.d/bump-newton-pin.rst delete mode 100644 source/isaaclab/changelog.d/jmart-cartpole-rtx.minor.rst delete mode 100644 source/isaaclab/changelog.d/jmart-dr-reset.rst delete mode 100644 source/isaaclab_newton/changelog.d/bump-newton-pin.rst delete mode 100644 source/isaaclab_newton/changelog.d/jmart-cartpole-rtx.minor.rst delete mode 100644 source/isaaclab_ov/changelog.d/jmart-cartpole-rtx.minor.rst delete mode 100644 source/isaaclab_physx/changelog.d/bump-newton-pin.rst delete mode 100644 source/isaaclab_physx/changelog.d/jmart-cartpole-rtx.minor.rst delete mode 100644 source/isaaclab_tasks/changelog.d/jmart-cartpole-rtx.rst delete mode 100644 source/isaaclab_visualizers/changelog.d/bump-newton-pin.rst diff --git a/source/isaaclab/changelog.d/bump-newton-pin.rst b/source/isaaclab/changelog.d/bump-newton-pin.rst deleted file mode 100644 index 90a5b53e3bc7..000000000000 --- a/source/isaaclab/changelog.d/bump-newton-pin.rst +++ /dev/null @@ -1,7 +0,0 @@ -Fixed -^^^^^ - -* Fixed the wheel-builder ``newton[sim]`` dependency pin to use Newton commit - ``79e95bf5571d70a0a46c8eaedc80644531d27368``, including the - RenderContext triangle-mesh construction fix from `newton-physics/newton#3199 - `_. diff --git a/source/isaaclab/changelog.d/jmart-cartpole-rtx.minor.rst b/source/isaaclab/changelog.d/jmart-cartpole-rtx.minor.rst deleted file mode 100644 index c7b1de77a767..000000000000 --- a/source/isaaclab/changelog.d/jmart-cartpole-rtx.minor.rst +++ /dev/null @@ -1,12 +0,0 @@ -Added -^^^^^ - -* Added the :meth:`~isaaclab.physics.physics_manager.PhysicsManager.provides_implicit_damping` and - :meth:`~isaaclab.renderers.base_renderer.BaseRenderer.provides_temporal_camera_data` capability - classmethods on the runtime backend bases, so physics and renderer backends declare whether a - camera observation carries the temporal information a policy needs to infer velocity (used to - decide frame stacking). Base defaults: physics has implicit damping (``True``); a renderer - provides no temporal data (``False``). -* Added :meth:`~isaaclab.renderers.Renderer.resolve_class` to resolve a renderer's implementation - class from its configuration without instantiating it (so tasks can query the above classmethod - before a simulation exists). diff --git a/source/isaaclab/changelog.d/jmart-dr-reset.rst b/source/isaaclab/changelog.d/jmart-dr-reset.rst deleted file mode 100644 index 9088b9800e13..000000000000 --- a/source/isaaclab/changelog.d/jmart-dr-reset.rst +++ /dev/null @@ -1,9 +0,0 @@ -Fixed -^^^^^ - -* Fixed a crash where :class:`~isaaclab.envs.mdp.randomize_visual_color` used in ``reset`` mode - raised ``AttributeError: 'NoneType' object has no attribute 'link_count'`` during environment - startup on the PhysX backend. The randomizer authored USD (``SetInstanceable`` and material - binding) on the articulation root prim, which invalidated the PhysX articulation view so that - the subsequent at-play body-name resolution dereferenced a ``None`` metatype. It now scopes to - descendant visual prims, mirroring :class:`~isaaclab.envs.mdp.randomize_visual_texture_material`. diff --git a/source/isaaclab/config/extension.toml b/source/isaaclab/config/extension.toml index 6fd0197bebd3..bb4ba3ffbdf4 100644 --- a/source/isaaclab/config/extension.toml +++ b/source/isaaclab/config/extension.toml @@ -1,7 +1,7 @@ [package] # Note: Semantic Versioning is used: https://semver.org/ -version = "7.0.5" +version = "7.1.0" # Description title = "Isaac Lab framework for Robot Learning" diff --git a/source/isaaclab/docs/CHANGELOG.rst b/source/isaaclab/docs/CHANGELOG.rst index ba7727cba561..b08a87ffdead 100644 --- a/source/isaaclab/docs/CHANGELOG.rst +++ b/source/isaaclab/docs/CHANGELOG.rst @@ -1,6 +1,37 @@ Changelog --------- +7.1.0 (2026-06-24) +~~~~~~~~~~~~~~~~~~ + +Added +^^^^^ + +* Added the :meth:`~isaaclab.physics.physics_manager.PhysicsManager.provides_implicit_damping` and + :meth:`~isaaclab.renderers.base_renderer.BaseRenderer.provides_temporal_camera_data` capability + classmethods on the runtime backend bases, so physics and renderer backends declare whether a + camera observation carries the temporal information a policy needs to infer velocity (used to + decide frame stacking). Base defaults: physics has implicit damping (``True``); a renderer + provides no temporal data (``False``). +* Added :meth:`~isaaclab.renderers.Renderer.resolve_class` to resolve a renderer's implementation + class from its configuration without instantiating it (so tasks can query the above classmethod + before a simulation exists). + +Fixed +^^^^^ + +* Fixed a crash where :class:`~isaaclab.envs.mdp.randomize_visual_color` used in ``reset`` mode + raised ``AttributeError: 'NoneType' object has no attribute 'link_count'`` during environment + startup on the PhysX backend. The randomizer authored USD (``SetInstanceable`` and material + binding) on the articulation root prim, which invalidated the PhysX articulation view so that + the subsequent at-play body-name resolution dereferenced a ``None`` metatype. It now scopes to + descendant visual prims, mirroring :class:`~isaaclab.envs.mdp.randomize_visual_texture_material`. +* Fixed the wheel-builder ``newton[sim]`` dependency pin to use Newton commit + ``79e95bf5571d70a0a46c8eaedc80644531d27368``, including the + RenderContext triangle-mesh construction fix from `newton-physics/newton#3199 + `_. + + 7.0.5 (2026-06-23) ~~~~~~~~~~~~~~~~~~ diff --git a/source/isaaclab/pyproject.toml b/source/isaaclab/pyproject.toml index 03511cf9f6db..e3169ab0dc33 100644 --- a/source/isaaclab/pyproject.toml +++ b/source/isaaclab/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta" [project] name = "isaaclab" -version = "7.0.5" +version = "7.1.0" description = "Extension providing main framework interfaces and abstractions for robot learning." license = {text = "BSD-3-Clause"} authors = [{name = "Isaac Lab Project Developers"}] diff --git a/source/isaaclab_newton/changelog.d/bump-newton-pin.rst b/source/isaaclab_newton/changelog.d/bump-newton-pin.rst deleted file mode 100644 index e38454e51070..000000000000 --- a/source/isaaclab_newton/changelog.d/bump-newton-pin.rst +++ /dev/null @@ -1,7 +0,0 @@ -Fixed -^^^^^ - -* Fixed the ``newton[sim]`` dependency pin to use Newton commit - ``79e95bf5571d70a0a46c8eaedc80644531d27368``, including the - RenderContext triangle-mesh construction fix from `newton-physics/newton#3199 - `_. diff --git a/source/isaaclab_newton/changelog.d/jmart-cartpole-rtx.minor.rst b/source/isaaclab_newton/changelog.d/jmart-cartpole-rtx.minor.rst deleted file mode 100644 index fc7a261e769f..000000000000 --- a/source/isaaclab_newton/changelog.d/jmart-cartpole-rtx.minor.rst +++ /dev/null @@ -1,7 +0,0 @@ -Added -^^^^^ - -* Overrode :meth:`provides_implicit_damping` on :class:`NewtonManager` to return ``False`` (its - symplectic integrator has no implicit damping) and :meth:`provides_temporal_camera_data` on - :class:`NewtonWarpRenderer` to return ``False`` (the rasterizer accumulates no temporal data), - so camera tasks can auto-enable frame stacking for the Newton combos that need it. diff --git a/source/isaaclab_newton/config/extension.toml b/source/isaaclab_newton/config/extension.toml index e634a251cfc0..d2e4ba3d446a 100644 --- a/source/isaaclab_newton/config/extension.toml +++ b/source/isaaclab_newton/config/extension.toml @@ -1,7 +1,7 @@ [package] # Note: Semantic Versioning is used: https://semver.org/ -version = "1.0.4" +version = "1.1.0" # Description title = "Newton simulation interfaces for IsaacLab core package" diff --git a/source/isaaclab_newton/docs/CHANGELOG.rst b/source/isaaclab_newton/docs/CHANGELOG.rst index 1e92f80043e8..45dfdd21f71a 100644 --- a/source/isaaclab_newton/docs/CHANGELOG.rst +++ b/source/isaaclab_newton/docs/CHANGELOG.rst @@ -1,6 +1,26 @@ Changelog --------- +1.1.0 (2026-06-24) +~~~~~~~~~~~~~~~~~~ + +Added +^^^^^ + +* Overrode :meth:`provides_implicit_damping` on :class:`NewtonManager` to return ``False`` (its + symplectic integrator has no implicit damping) and :meth:`provides_temporal_camera_data` on + :class:`NewtonWarpRenderer` to return ``False`` (the rasterizer accumulates no temporal data), + so camera tasks can auto-enable frame stacking for the Newton combos that need it. + +Fixed +^^^^^ + +* Fixed the ``newton[sim]`` dependency pin to use Newton commit + ``79e95bf5571d70a0a46c8eaedc80644531d27368``, including the + RenderContext triangle-mesh construction fix from `newton-physics/newton#3199 + `_. + + 1.0.4 (2026-06-23) ~~~~~~~~~~~~~~~~~~ diff --git a/source/isaaclab_newton/pyproject.toml b/source/isaaclab_newton/pyproject.toml index a59ec5015409..68c9fbae4ebc 100644 --- a/source/isaaclab_newton/pyproject.toml +++ b/source/isaaclab_newton/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta" [project] name = "isaaclab_newton" -version = "1.0.4" +version = "1.1.0" description = "Extension providing IsaacLab with Newton specific abstractions." license = {text = "BSD-3-Clause"} authors = [{name = "Isaac Lab Project Developers"}] diff --git a/source/isaaclab_ov/changelog.d/jmart-cartpole-rtx.minor.rst b/source/isaaclab_ov/changelog.d/jmart-cartpole-rtx.minor.rst deleted file mode 100644 index 86cfb672bf0b..000000000000 --- a/source/isaaclab_ov/changelog.d/jmart-cartpole-rtx.minor.rst +++ /dev/null @@ -1,6 +0,0 @@ -Added -^^^^^ - -* Overrode :meth:`provides_temporal_camera_data` on :class:`OVRTXRenderer` to return ``True`` - only for the ``rgb``/``rgba`` beauty buffer (temporally accumulated by DLSS), matching Isaac RTX; - other AOVs return ``False``. diff --git a/source/isaaclab_ov/config/extension.toml b/source/isaaclab_ov/config/extension.toml index e4ec845231d9..5c240c1cf43a 100644 --- a/source/isaaclab_ov/config/extension.toml +++ b/source/isaaclab_ov/config/extension.toml @@ -1,5 +1,5 @@ [package] -version = "0.4.6" +version = "0.5.0" title = "Omniverse renderers for IsaacLab" description = "Extension providing Omniverse renderers (OVRTX, ovphysx, etc.) for tiled camera rendering." readme = "docs/README.md" diff --git a/source/isaaclab_ov/docs/CHANGELOG.rst b/source/isaaclab_ov/docs/CHANGELOG.rst index 4307aa7707a4..b6bc15626d66 100644 --- a/source/isaaclab_ov/docs/CHANGELOG.rst +++ b/source/isaaclab_ov/docs/CHANGELOG.rst @@ -1,6 +1,17 @@ Changelog --------- +0.5.0 (2026-06-24) +~~~~~~~~~~~~~~~~~~ + +Added +^^^^^ + +* Overrode :meth:`provides_temporal_camera_data` on :class:`OVRTXRenderer` to return ``True`` + only for the ``rgb``/``rgba`` beauty buffer (temporally accumulated by DLSS), matching Isaac RTX; + other AOVs return ``False``. + + 0.4.6 (2026-06-16) ~~~~~~~~~~~~~~~~~~ diff --git a/source/isaaclab_ov/pyproject.toml b/source/isaaclab_ov/pyproject.toml index e24cd26ee96f..6a8b4704efdb 100644 --- a/source/isaaclab_ov/pyproject.toml +++ b/source/isaaclab_ov/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta" [project] name = "isaaclab_ov" -version = "0.4.6" +version = "0.5.0" description = "Extension providing Omniverse renderers (OVRTX, ovphysx, etc.) for tiled camera rendering." license = {text = "BSD-3-Clause"} authors = [{name = "Isaac Lab Project Developers"}] diff --git a/source/isaaclab_physx/changelog.d/bump-newton-pin.rst b/source/isaaclab_physx/changelog.d/bump-newton-pin.rst deleted file mode 100644 index 676868e37a8e..000000000000 --- a/source/isaaclab_physx/changelog.d/bump-newton-pin.rst +++ /dev/null @@ -1,7 +0,0 @@ -Fixed -^^^^^ - -* Fixed the optional ``newton[sim]`` dependency pin to use Newton commit - ``79e95bf5571d70a0a46c8eaedc80644531d27368``, including the - RenderContext triangle-mesh construction fix from `newton-physics/newton#3199 - `_. diff --git a/source/isaaclab_physx/changelog.d/jmart-cartpole-rtx.minor.rst b/source/isaaclab_physx/changelog.d/jmart-cartpole-rtx.minor.rst deleted file mode 100644 index 1f2a1c956e42..000000000000 --- a/source/isaaclab_physx/changelog.d/jmart-cartpole-rtx.minor.rst +++ /dev/null @@ -1,6 +0,0 @@ -Added -^^^^^ - -* Overrode :meth:`provides_temporal_camera_data` on :class:`IsaacRtxRenderer` to return ``True`` - only for the ``rgb``/``rgba`` beauty buffer (temporally accumulated by DLSS); the depth, albedo, - simple_shading, and segmentation AOVs return ``False`` as they bypass DLSS. diff --git a/source/isaaclab_physx/config/extension.toml b/source/isaaclab_physx/config/extension.toml index 519859d53fc1..1640fb94cafe 100644 --- a/source/isaaclab_physx/config/extension.toml +++ b/source/isaaclab_physx/config/extension.toml @@ -1,7 +1,7 @@ [package] # Note: Semantic Versioning is used: https://semver.org/ -version = "2.0.2" +version = "2.1.0" # Description title = "PhysX simulation interfaces for IsaacLab core package" diff --git a/source/isaaclab_physx/docs/CHANGELOG.rst b/source/isaaclab_physx/docs/CHANGELOG.rst index 6f7c55b3b0ba..a503a8c4ca41 100644 --- a/source/isaaclab_physx/docs/CHANGELOG.rst +++ b/source/isaaclab_physx/docs/CHANGELOG.rst @@ -1,6 +1,25 @@ Changelog --------- +2.1.0 (2026-06-24) +~~~~~~~~~~~~~~~~~~ + +Added +^^^^^ + +* Overrode :meth:`provides_temporal_camera_data` on :class:`IsaacRtxRenderer` to return ``True`` + only for the ``rgb``/``rgba`` beauty buffer (temporally accumulated by DLSS); the depth, albedo, + simple_shading, and segmentation AOVs return ``False`` as they bypass DLSS. + +Fixed +^^^^^ + +* Fixed the optional ``newton[sim]`` dependency pin to use Newton commit + ``79e95bf5571d70a0a46c8eaedc80644531d27368``, including the + RenderContext triangle-mesh construction fix from `newton-physics/newton#3199 + `_. + + 2.0.2 (2026-06-17) ~~~~~~~~~~~~~~~~~~ diff --git a/source/isaaclab_physx/pyproject.toml b/source/isaaclab_physx/pyproject.toml index ffc37e686cff..305c6d17d1b4 100644 --- a/source/isaaclab_physx/pyproject.toml +++ b/source/isaaclab_physx/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta" [project] name = "isaaclab_physx" -version = "2.0.2" +version = "2.1.0" description = "Extension providing IsaacLab with PhysX specific abstractions." license = {text = "BSD-3-Clause"} authors = [{name = "Isaac Lab Project Developers"}] diff --git a/source/isaaclab_tasks/changelog.d/jmart-cartpole-rtx.rst b/source/isaaclab_tasks/changelog.d/jmart-cartpole-rtx.rst deleted file mode 100644 index c25a345722f7..000000000000 --- a/source/isaaclab_tasks/changelog.d/jmart-cartpole-rtx.rst +++ /dev/null @@ -1,13 +0,0 @@ -Fixed -^^^^^ - -* Fixed the camera-based Cartpole task failing to converge under Newton physics with the RTX - ``depth``, ``albedo``, and ``simple_shading`` AOV observations. These AOVs bypass DLSS temporal - accumulation, so the observation carried no temporal cue for the policy to infer velocity from - (Newton's symplectic integrator has no implicit damping). The ``frame_stack`` default resolver - now enables 2-frame stacking for these Newton + RTX AOVs, matching the existing Newton + Warp - behavior; Newton + RTX ``rgb`` keeps single-frame observations as DLSS already supplies the cue. - The resolver reads backend capability classmethods - (:meth:`~isaaclab.physics.physics_manager.PhysicsManager.provides_implicit_damping`, - :meth:`~isaaclab.renderers.base_renderer.BaseRenderer.provides_temporal_camera_data`) resolved - from the configs, instead of hard-coding backend types in the task. diff --git a/source/isaaclab_tasks/config/extension.toml b/source/isaaclab_tasks/config/extension.toml index 781b51f6cca7..e05d772e49a6 100644 --- a/source/isaaclab_tasks/config/extension.toml +++ b/source/isaaclab_tasks/config/extension.toml @@ -1,7 +1,7 @@ [package] # Note: Semantic Versioning is used: https://semver.org/ -version = "8.0.4" +version = "8.0.5" # Description title = "Isaac Lab Environments" diff --git a/source/isaaclab_tasks/docs/CHANGELOG.rst b/source/isaaclab_tasks/docs/CHANGELOG.rst index 540363429b2c..aff3e0f19094 100644 --- a/source/isaaclab_tasks/docs/CHANGELOG.rst +++ b/source/isaaclab_tasks/docs/CHANGELOG.rst @@ -1,6 +1,24 @@ Changelog --------- +8.0.5 (2026-06-24) +~~~~~~~~~~~~~~~~~~ + +Fixed +^^^^^ + +* Fixed the camera-based Cartpole task failing to converge under Newton physics with the RTX + ``depth``, ``albedo``, and ``simple_shading`` AOV observations. These AOVs bypass DLSS temporal + accumulation, so the observation carried no temporal cue for the policy to infer velocity from + (Newton's symplectic integrator has no implicit damping). The ``frame_stack`` default resolver + now enables 2-frame stacking for these Newton + RTX AOVs, matching the existing Newton + Warp + behavior; Newton + RTX ``rgb`` keeps single-frame observations as DLSS already supplies the cue. + The resolver reads backend capability classmethods + (:meth:`~isaaclab.physics.physics_manager.PhysicsManager.provides_implicit_damping`, + :meth:`~isaaclab.renderers.base_renderer.BaseRenderer.provides_temporal_camera_data`) resolved + from the configs, instead of hard-coding backend types in the task. + + 8.0.4 (2026-06-23) ~~~~~~~~~~~~~~~~~~ diff --git a/source/isaaclab_tasks/pyproject.toml b/source/isaaclab_tasks/pyproject.toml index 988ef4966925..00bf52e4cf6c 100644 --- a/source/isaaclab_tasks/pyproject.toml +++ b/source/isaaclab_tasks/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta" [project] name = "isaaclab_tasks" -version = "8.0.4" +version = "8.0.5" description = "Extension containing suite of environments for robot learning." license = {text = "BSD-3-Clause"} authors = [{name = "Isaac Lab Project Developers"}] diff --git a/source/isaaclab_visualizers/changelog.d/bump-newton-pin.rst b/source/isaaclab_visualizers/changelog.d/bump-newton-pin.rst deleted file mode 100644 index d8cb1021d482..000000000000 --- a/source/isaaclab_visualizers/changelog.d/bump-newton-pin.rst +++ /dev/null @@ -1,7 +0,0 @@ -Fixed -^^^^^ - -* Fixed the visualizer extras' ``newton[sim]`` dependency pin to use Newton - commit ``79e95bf5571d70a0a46c8eaedc80644531d27368``, including the - RenderContext triangle-mesh construction fix from `newton-physics/newton#3199 - `_. diff --git a/source/isaaclab_visualizers/config/extension.toml b/source/isaaclab_visualizers/config/extension.toml index e02c29a67cad..4fe46a909a09 100644 --- a/source/isaaclab_visualizers/config/extension.toml +++ b/source/isaaclab_visualizers/config/extension.toml @@ -1,7 +1,7 @@ [package] # Semantic Versioning is used: https://semver.org/ -version = "1.0.1" +version = "1.0.2" # Description category = "isaaclab" diff --git a/source/isaaclab_visualizers/docs/CHANGELOG.rst b/source/isaaclab_visualizers/docs/CHANGELOG.rst index cd4c8d8a6410..a68605282274 100644 --- a/source/isaaclab_visualizers/docs/CHANGELOG.rst +++ b/source/isaaclab_visualizers/docs/CHANGELOG.rst @@ -1,6 +1,18 @@ Changelog --------- +1.0.2 (2026-06-24) +~~~~~~~~~~~~~~~~~~ + +Fixed +^^^^^ + +* Fixed the visualizer extras' ``newton[sim]`` dependency pin to use Newton + commit ``79e95bf5571d70a0a46c8eaedc80644531d27368``, including the + RenderContext triangle-mesh construction fix from `newton-physics/newton#3199 + `_. + + 1.0.1 (2026-06-14) ~~~~~~~~~~~~~~~~~~ diff --git a/source/isaaclab_visualizers/pyproject.toml b/source/isaaclab_visualizers/pyproject.toml index 1dda46c96e2a..970de1d79423 100644 --- a/source/isaaclab_visualizers/pyproject.toml +++ b/source/isaaclab_visualizers/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta" [project] name = "isaaclab-visualizers" -version = "1.0.1" +version = "1.0.2" description = "Visualizer backends for Isaac Lab (Kit, Newton, Rerun, Viser)." license = {text = "BSD-3-Clause"} authors = [{name = "Isaac Lab Project Developers"}] From e2d279904e9d26a8845d2d622d411853031daa66 Mon Sep 17 00:00:00 2001 From: rilei-nvidia Date: Wed, 24 Jun 2026 21:03:34 +1200 Subject: [PATCH 3/9] Add distance_to_camera and distance_to_image_plane camera presets to cartpole, dexsuite_kuka, and shadow_hand with test coverage (#6101) # Description Add distance_to_camera and distance_to_image_plane camera presets to cartpole, dexsuite_kuka, and shadow_hand with test coverage. ## Checklist - [x] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --- .../changelog.d/extend-test-distance-aovs.rst | 6 ++ .../isaaclab_ov/renderers/ovrtx_renderer.py | 2 +- .../isaaclab_ov/renderers/ovrtx_usd.py | 6 +- .../changelog.d/extend-test-distance-aovs.rst | 7 ++ .../shadow_hand/shadow_hand_camera_env_cfg.py | 5 +- ...aacsim_rtx_renderer-distance_to_camera.png | 3 + ...m_rtx_renderer-distance_to_image_plane.png | 3 + ...wton-ovrtx_renderer-distance_to_camera.png | 3 + ...ovrtx_renderer-distance_to_image_plane.png | 3 + ...hysx-ovrtx_renderer-distance_to_camera.png | 3 + ...ovrtx_renderer-distance_to_image_plane.png | 3 + ...aacsim_rtx_renderer-distance_to_camera.png | 3 + ...m_rtx_renderer-distance_to_image_plane.png | 3 + ...aacsim_rtx_renderer-distance_to_camera.png | 3 + ...m_rtx_renderer-distance_to_image_plane.png | 3 + ...wton-ovrtx_renderer-distance_to_camera.png | 3 + ...ovrtx_renderer-distance_to_image_plane.png | 3 + ...aacsim_rtx_renderer-distance_to_camera.png | 3 + ...m_rtx_renderer-distance_to_image_plane.png | 3 + ...aacsim_rtx_renderer-distance_to_camera.png | 3 + ...m_rtx_renderer-distance_to_image_plane.png | 3 + ...wton-ovrtx_renderer-distance_to_camera.png | 3 + ...ovrtx_renderer-distance_to_image_plane.png | 3 + ...aacsim_rtx_renderer-distance_to_camera.png | 3 + ...m_rtx_renderer-distance_to_image_plane.png | 3 + ...aacsim_rtx_renderer-distance_to_camera.png | 3 + ...m_rtx_renderer-distance_to_image_plane.png | 3 + ...wton-ovrtx_renderer-distance_to_camera.png | 3 + ...ovrtx_renderer-distance_to_image_plane.png | 3 + ...aacsim_rtx_renderer-distance_to_camera.png | 3 + ...m_rtx_renderer-distance_to_image_plane.png | 3 + .../test/rendering_test_utils.py | 84 +++++++++++++++++-- 32 files changed, 179 insertions(+), 9 deletions(-) create mode 100644 source/isaaclab_ov/changelog.d/extend-test-distance-aovs.rst create mode 100644 source/isaaclab_tasks/changelog.d/extend-test-distance-aovs.rst create mode 100644 source/isaaclab_tasks/test/golden_images/cartpole/newton-isaacsim_rtx_renderer-distance_to_camera.png create mode 100644 source/isaaclab_tasks/test/golden_images/cartpole/newton-isaacsim_rtx_renderer-distance_to_image_plane.png create mode 100644 source/isaaclab_tasks/test/golden_images/cartpole/newton-ovrtx_renderer-distance_to_camera.png create mode 100644 source/isaaclab_tasks/test/golden_images/cartpole/newton-ovrtx_renderer-distance_to_image_plane.png create mode 100644 source/isaaclab_tasks/test/golden_images/cartpole/ovphysx-ovrtx_renderer-distance_to_camera.png create mode 100644 source/isaaclab_tasks/test/golden_images/cartpole/ovphysx-ovrtx_renderer-distance_to_image_plane.png create mode 100644 source/isaaclab_tasks/test/golden_images/cartpole/physx-isaacsim_rtx_renderer-distance_to_camera.png create mode 100644 source/isaaclab_tasks/test/golden_images/cartpole/physx-isaacsim_rtx_renderer-distance_to_image_plane.png create mode 100644 source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/newton-isaacsim_rtx_renderer-distance_to_camera.png create mode 100644 source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/newton-isaacsim_rtx_renderer-distance_to_image_plane.png create mode 100644 source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/newton-ovrtx_renderer-distance_to_camera.png create mode 100644 source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/newton-ovrtx_renderer-distance_to_image_plane.png create mode 100644 source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/physx-isaacsim_rtx_renderer-distance_to_camera.png create mode 100644 source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/physx-isaacsim_rtx_renderer-distance_to_image_plane.png create mode 100644 source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/newton-isaacsim_rtx_renderer-distance_to_camera.png create mode 100644 source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/newton-isaacsim_rtx_renderer-distance_to_image_plane.png create mode 100644 source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/newton-ovrtx_renderer-distance_to_camera.png create mode 100644 source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/newton-ovrtx_renderer-distance_to_image_plane.png create mode 100644 source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/physx-isaacsim_rtx_renderer-distance_to_camera.png create mode 100644 source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/physx-isaacsim_rtx_renderer-distance_to_image_plane.png create mode 100644 source/isaaclab_tasks/test/golden_images/shadow_hand/newton-isaacsim_rtx_renderer-distance_to_camera.png create mode 100644 source/isaaclab_tasks/test/golden_images/shadow_hand/newton-isaacsim_rtx_renderer-distance_to_image_plane.png create mode 100644 source/isaaclab_tasks/test/golden_images/shadow_hand/newton-ovrtx_renderer-distance_to_camera.png create mode 100644 source/isaaclab_tasks/test/golden_images/shadow_hand/newton-ovrtx_renderer-distance_to_image_plane.png create mode 100644 source/isaaclab_tasks/test/golden_images/shadow_hand/physx-isaacsim_rtx_renderer-distance_to_camera.png create mode 100644 source/isaaclab_tasks/test/golden_images/shadow_hand/physx-isaacsim_rtx_renderer-distance_to_image_plane.png diff --git a/source/isaaclab_ov/changelog.d/extend-test-distance-aovs.rst b/source/isaaclab_ov/changelog.d/extend-test-distance-aovs.rst new file mode 100644 index 000000000000..e2a7880fc16c --- /dev/null +++ b/source/isaaclab_ov/changelog.d/extend-test-distance-aovs.rst @@ -0,0 +1,6 @@ +Fixed +^^^^^ + +* Fixed ``distance_to_camera`` incorrectly mapping to ``DistanceToImagePlaneSD`` in the OVRTX + renderer backend. It now correctly uses ``DistanceToCameraSD``, matching the intended semantics + of eye-space ray length versus perpendicular image-plane distance. diff --git a/source/isaaclab_ov/isaaclab_ov/renderers/ovrtx_renderer.py b/source/isaaclab_ov/isaaclab_ov/renderers/ovrtx_renderer.py index 08d5855f33cf..359e1c4bd365 100644 --- a/source/isaaclab_ov/isaaclab_ov/renderers/ovrtx_renderer.py +++ b/source/isaaclab_ov/isaaclab_ov/renderers/ovrtx_renderer.py @@ -757,7 +757,7 @@ def _process_render_frame(self, render_data: OVRTXRenderData, frame, output_buff tiled_data = wp.from_dlpack(mapping.tensor) self._extract_rgba_tiles(render_data, tiled_data, output_buffers, buffer_key) - for depth_var in ["DistanceToImagePlaneSD", "DepthSD"]: + for depth_var in ["DistanceToCameraSD", "DistanceToImagePlaneSD", "DepthSD"]: if depth_var not in frame.render_vars: continue with frame.render_vars[depth_var].map(device=Device.CUDA) as mapping: diff --git a/source/isaaclab_ov/isaaclab_ov/renderers/ovrtx_usd.py b/source/isaaclab_ov/isaaclab_ov/renderers/ovrtx_usd.py index e774703dc68f..be91d46f14e4 100644 --- a/source/isaaclab_ov/isaaclab_ov/renderers/ovrtx_usd.py +++ b/source/isaaclab_ov/isaaclab_ov/renderers/ovrtx_usd.py @@ -18,13 +18,17 @@ def get_render_var_config(data_types: list[str]) -> tuple[str, str, str]: """Return (render_var_path, render_var_name, source_name) from data_types.""" use_depth = any(dt in ["depth", "distance_to_image_plane", "distance_to_camera"] for dt in data_types) + use_distance_to_camera = "distance_to_camera" in data_types and not any( + dt in ["depth", "distance_to_image_plane"] for dt in data_types + ) use_albedo = "albedo" in data_types use_semantic = "semantic_segmentation" in data_types use_rgb = any(dt in ["rgb", "rgba"] for dt in data_types) use_hdr = "rgb_hdr" in data_types if use_depth and not (use_rgb or use_albedo or use_semantic): - return "/Render/Vars/depth", "depth", "DistanceToImagePlaneSD" + source = "DistanceToCameraSD" if use_distance_to_camera else "DistanceToImagePlaneSD" + return "/Render/Vars/depth", "depth", source if use_albedo and not (use_rgb or use_semantic): return "/Render/Vars/albedo", "albedo", "DiffuseAlbedoSD" if use_semantic and not (use_rgb or use_albedo): diff --git a/source/isaaclab_tasks/changelog.d/extend-test-distance-aovs.rst b/source/isaaclab_tasks/changelog.d/extend-test-distance-aovs.rst new file mode 100644 index 000000000000..735bf4d24276 --- /dev/null +++ b/source/isaaclab_tasks/changelog.d/extend-test-distance-aovs.rst @@ -0,0 +1,7 @@ +Added +^^^^^ + +* Added golden-image rendering tests for ``distance_to_camera`` and ``distance_to_image_plane`` + AOV types across the Cartpole, DexSuite Kuka, and Shadow Hand camera environments. + Test-local subclasses of the relevant env and camera configs are used so the production + task API remains unchanged. diff --git a/source/isaaclab_tasks/isaaclab_tasks/core/reorient/config/shadow_hand/shadow_hand_camera_env_cfg.py b/source/isaaclab_tasks/isaaclab_tasks/core/reorient/config/shadow_hand/shadow_hand_camera_env_cfg.py index 9811bfa8526e..55066df4e8d6 100644 --- a/source/isaaclab_tasks/isaaclab_tasks/core/reorient/config/shadow_hand/shadow_hand_camera_env_cfg.py +++ b/source/isaaclab_tasks/isaaclab_tasks/core/reorient/config/shadow_hand/shadow_hand_camera_env_cfg.py @@ -134,7 +134,10 @@ def validate_config(self): "Choose a compatible preset, e.g. presets=newton_renderer,rgb." ) - if set(self.tiled_camera.data_types) == {"depth"} and self.feature_extractor.enabled: + non_depth_data_types = set(self.tiled_camera.data_types).difference( + {"depth", "distance_to_image_plane", "distance_to_camera"} + ) + if self.tiled_camera.data_types and not non_depth_data_types and self.feature_extractor.enabled: raise ValueError( "Depth-only camera data type is intended for benchmarking only. " "The keypoint-regression CNN cannot be meaningfully trained from depth alone. " diff --git a/source/isaaclab_tasks/test/golden_images/cartpole/newton-isaacsim_rtx_renderer-distance_to_camera.png b/source/isaaclab_tasks/test/golden_images/cartpole/newton-isaacsim_rtx_renderer-distance_to_camera.png new file mode 100644 index 000000000000..0a26486d7f85 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/cartpole/newton-isaacsim_rtx_renderer-distance_to_camera.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8929af0b155ac73e4dd74dd70dbc502fb78bd962248e5c46f88972a0a7e3e80e +size 538 diff --git a/source/isaaclab_tasks/test/golden_images/cartpole/newton-isaacsim_rtx_renderer-distance_to_image_plane.png b/source/isaaclab_tasks/test/golden_images/cartpole/newton-isaacsim_rtx_renderer-distance_to_image_plane.png new file mode 100644 index 000000000000..0387686a7a78 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/cartpole/newton-isaacsim_rtx_renderer-distance_to_image_plane.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea4e6ba2251666e4df0c937fbd123489f2a06939bda9e674d5f84eb5c8831f7d +size 422 diff --git a/source/isaaclab_tasks/test/golden_images/cartpole/newton-ovrtx_renderer-distance_to_camera.png b/source/isaaclab_tasks/test/golden_images/cartpole/newton-ovrtx_renderer-distance_to_camera.png new file mode 100644 index 000000000000..0a26486d7f85 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/cartpole/newton-ovrtx_renderer-distance_to_camera.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8929af0b155ac73e4dd74dd70dbc502fb78bd962248e5c46f88972a0a7e3e80e +size 538 diff --git a/source/isaaclab_tasks/test/golden_images/cartpole/newton-ovrtx_renderer-distance_to_image_plane.png b/source/isaaclab_tasks/test/golden_images/cartpole/newton-ovrtx_renderer-distance_to_image_plane.png new file mode 100644 index 000000000000..0387686a7a78 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/cartpole/newton-ovrtx_renderer-distance_to_image_plane.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea4e6ba2251666e4df0c937fbd123489f2a06939bda9e674d5f84eb5c8831f7d +size 422 diff --git a/source/isaaclab_tasks/test/golden_images/cartpole/ovphysx-ovrtx_renderer-distance_to_camera.png b/source/isaaclab_tasks/test/golden_images/cartpole/ovphysx-ovrtx_renderer-distance_to_camera.png new file mode 100644 index 000000000000..0a26486d7f85 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/cartpole/ovphysx-ovrtx_renderer-distance_to_camera.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8929af0b155ac73e4dd74dd70dbc502fb78bd962248e5c46f88972a0a7e3e80e +size 538 diff --git a/source/isaaclab_tasks/test/golden_images/cartpole/ovphysx-ovrtx_renderer-distance_to_image_plane.png b/source/isaaclab_tasks/test/golden_images/cartpole/ovphysx-ovrtx_renderer-distance_to_image_plane.png new file mode 100644 index 000000000000..0387686a7a78 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/cartpole/ovphysx-ovrtx_renderer-distance_to_image_plane.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea4e6ba2251666e4df0c937fbd123489f2a06939bda9e674d5f84eb5c8831f7d +size 422 diff --git a/source/isaaclab_tasks/test/golden_images/cartpole/physx-isaacsim_rtx_renderer-distance_to_camera.png b/source/isaaclab_tasks/test/golden_images/cartpole/physx-isaacsim_rtx_renderer-distance_to_camera.png new file mode 100644 index 000000000000..0a26486d7f85 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/cartpole/physx-isaacsim_rtx_renderer-distance_to_camera.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8929af0b155ac73e4dd74dd70dbc502fb78bd962248e5c46f88972a0a7e3e80e +size 538 diff --git a/source/isaaclab_tasks/test/golden_images/cartpole/physx-isaacsim_rtx_renderer-distance_to_image_plane.png b/source/isaaclab_tasks/test/golden_images/cartpole/physx-isaacsim_rtx_renderer-distance_to_image_plane.png new file mode 100644 index 000000000000..0387686a7a78 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/cartpole/physx-isaacsim_rtx_renderer-distance_to_image_plane.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea4e6ba2251666e4df0c937fbd123489f2a06939bda9e674d5f84eb5c8831f7d +size 422 diff --git a/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/newton-isaacsim_rtx_renderer-distance_to_camera.png b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/newton-isaacsim_rtx_renderer-distance_to_camera.png new file mode 100644 index 000000000000..a4626ba44f33 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/newton-isaacsim_rtx_renderer-distance_to_camera.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:41ecbb1dbb608dbc9aa4b172fcbff57333a741e36b85ea937930b593eba774c1 +size 588 diff --git a/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/newton-isaacsim_rtx_renderer-distance_to_image_plane.png b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/newton-isaacsim_rtx_renderer-distance_to_image_plane.png new file mode 100644 index 000000000000..d823d5d96998 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/newton-isaacsim_rtx_renderer-distance_to_image_plane.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f401da10698c1025957c57b8ca5ee359dab0449a78613941e3d9841df595794 +size 568 diff --git a/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/newton-ovrtx_renderer-distance_to_camera.png b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/newton-ovrtx_renderer-distance_to_camera.png new file mode 100644 index 000000000000..d823d5d96998 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/newton-ovrtx_renderer-distance_to_camera.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f401da10698c1025957c57b8ca5ee359dab0449a78613941e3d9841df595794 +size 568 diff --git a/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/newton-ovrtx_renderer-distance_to_image_plane.png b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/newton-ovrtx_renderer-distance_to_image_plane.png new file mode 100644 index 000000000000..d823d5d96998 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/newton-ovrtx_renderer-distance_to_image_plane.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f401da10698c1025957c57b8ca5ee359dab0449a78613941e3d9841df595794 +size 568 diff --git a/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/physx-isaacsim_rtx_renderer-distance_to_camera.png b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/physx-isaacsim_rtx_renderer-distance_to_camera.png new file mode 100644 index 000000000000..a4626ba44f33 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/physx-isaacsim_rtx_renderer-distance_to_camera.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:41ecbb1dbb608dbc9aa4b172fcbff57333a741e36b85ea937930b593eba774c1 +size 588 diff --git a/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/physx-isaacsim_rtx_renderer-distance_to_image_plane.png b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/physx-isaacsim_rtx_renderer-distance_to_image_plane.png new file mode 100644 index 000000000000..d823d5d96998 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_hetero/physx-isaacsim_rtx_renderer-distance_to_image_plane.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f401da10698c1025957c57b8ca5ee359dab0449a78613941e3d9841df595794 +size 568 diff --git a/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/newton-isaacsim_rtx_renderer-distance_to_camera.png b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/newton-isaacsim_rtx_renderer-distance_to_camera.png new file mode 100644 index 000000000000..d87fea10d447 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/newton-isaacsim_rtx_renderer-distance_to_camera.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:084f049a7b700ea75716f77e0c80e307c38f414b5da68be34ebad3c1ebe453a9 +size 551 diff --git a/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/newton-isaacsim_rtx_renderer-distance_to_image_plane.png b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/newton-isaacsim_rtx_renderer-distance_to_image_plane.png new file mode 100644 index 000000000000..bf4b0e0290dd --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/newton-isaacsim_rtx_renderer-distance_to_image_plane.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c003b10810539f538464992860c74ee3bf531b8b4e9b6e0ebe84041d42dba643 +size 532 diff --git a/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/newton-ovrtx_renderer-distance_to_camera.png b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/newton-ovrtx_renderer-distance_to_camera.png new file mode 100644 index 000000000000..bf4b0e0290dd --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/newton-ovrtx_renderer-distance_to_camera.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c003b10810539f538464992860c74ee3bf531b8b4e9b6e0ebe84041d42dba643 +size 532 diff --git a/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/newton-ovrtx_renderer-distance_to_image_plane.png b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/newton-ovrtx_renderer-distance_to_image_plane.png new file mode 100644 index 000000000000..bf4b0e0290dd --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/newton-ovrtx_renderer-distance_to_image_plane.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c003b10810539f538464992860c74ee3bf531b8b4e9b6e0ebe84041d42dba643 +size 532 diff --git a/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/physx-isaacsim_rtx_renderer-distance_to_camera.png b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/physx-isaacsim_rtx_renderer-distance_to_camera.png new file mode 100644 index 000000000000..d87fea10d447 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/physx-isaacsim_rtx_renderer-distance_to_camera.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:084f049a7b700ea75716f77e0c80e307c38f414b5da68be34ebad3c1ebe453a9 +size 551 diff --git a/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/physx-isaacsim_rtx_renderer-distance_to_image_plane.png b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/physx-isaacsim_rtx_renderer-distance_to_image_plane.png new file mode 100644 index 000000000000..bf4b0e0290dd --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/dexsuite_kuka_homo/physx-isaacsim_rtx_renderer-distance_to_image_plane.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c003b10810539f538464992860c74ee3bf531b8b4e9b6e0ebe84041d42dba643 +size 532 diff --git a/source/isaaclab_tasks/test/golden_images/shadow_hand/newton-isaacsim_rtx_renderer-distance_to_camera.png b/source/isaaclab_tasks/test/golden_images/shadow_hand/newton-isaacsim_rtx_renderer-distance_to_camera.png new file mode 100644 index 000000000000..2b472cbaac9e --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/shadow_hand/newton-isaacsim_rtx_renderer-distance_to_camera.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b991b49bc8061c5cb0fa33f4f940547c532919179c7c7fa384b8c6c796dfa949 +size 4039 diff --git a/source/isaaclab_tasks/test/golden_images/shadow_hand/newton-isaacsim_rtx_renderer-distance_to_image_plane.png b/source/isaaclab_tasks/test/golden_images/shadow_hand/newton-isaacsim_rtx_renderer-distance_to_image_plane.png new file mode 100644 index 000000000000..3893ecf7729d --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/shadow_hand/newton-isaacsim_rtx_renderer-distance_to_image_plane.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d249747e56b41ada8e507c138979791ef6f558043eb23c9f65d6826220eac5f1 +size 3752 diff --git a/source/isaaclab_tasks/test/golden_images/shadow_hand/newton-ovrtx_renderer-distance_to_camera.png b/source/isaaclab_tasks/test/golden_images/shadow_hand/newton-ovrtx_renderer-distance_to_camera.png new file mode 100644 index 000000000000..2b472cbaac9e --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/shadow_hand/newton-ovrtx_renderer-distance_to_camera.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b991b49bc8061c5cb0fa33f4f940547c532919179c7c7fa384b8c6c796dfa949 +size 4039 diff --git a/source/isaaclab_tasks/test/golden_images/shadow_hand/newton-ovrtx_renderer-distance_to_image_plane.png b/source/isaaclab_tasks/test/golden_images/shadow_hand/newton-ovrtx_renderer-distance_to_image_plane.png new file mode 100644 index 000000000000..3893ecf7729d --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/shadow_hand/newton-ovrtx_renderer-distance_to_image_plane.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d249747e56b41ada8e507c138979791ef6f558043eb23c9f65d6826220eac5f1 +size 3752 diff --git a/source/isaaclab_tasks/test/golden_images/shadow_hand/physx-isaacsim_rtx_renderer-distance_to_camera.png b/source/isaaclab_tasks/test/golden_images/shadow_hand/physx-isaacsim_rtx_renderer-distance_to_camera.png new file mode 100644 index 000000000000..0987d3f567ea --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/shadow_hand/physx-isaacsim_rtx_renderer-distance_to_camera.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ace1d37f1fa54bd692808ead80ef19470aa3da2efd4c43d7ef1f3ed1b3155bf7 +size 4025 diff --git a/source/isaaclab_tasks/test/golden_images/shadow_hand/physx-isaacsim_rtx_renderer-distance_to_image_plane.png b/source/isaaclab_tasks/test/golden_images/shadow_hand/physx-isaacsim_rtx_renderer-distance_to_image_plane.png new file mode 100644 index 000000000000..a38beb8f053f --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/shadow_hand/physx-isaacsim_rtx_renderer-distance_to_image_plane.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ddd7a52ad1de3f636d29d53bb156d04ac222d1318fc9574617712b53d796508 +size 3681 diff --git a/source/isaaclab_tasks/test/rendering_test_utils.py b/source/isaaclab_tasks/test/rendering_test_utils.py index 2e35bc1f0d54..6fd98dbbd9c9 100644 --- a/source/isaaclab_tasks/test/rendering_test_utils.py +++ b/source/isaaclab_tasks/test/rendering_test_utils.py @@ -83,6 +83,8 @@ "simple_shading_full_mdl", "semantic_segmentation", "depth", + "distance_to_camera", + "distance_to_image_plane", ) @@ -621,12 +623,27 @@ def rendering_test_shadow_hand( if physics_backend == "ovphysx": pytest.skip("ovphysx is not supported yet.") + from isaaclab.utils.configclass import configclass + from isaaclab_tasks.core.reorient.config.shadow_hand.shadow_hand_camera_env import ShadowHandCameraEnv - from isaaclab_tasks.core.reorient.config.shadow_hand.shadow_hand_camera_env_cfg import ShadowHandCameraEnvCfg + from isaaclab_tasks.core.reorient.config.shadow_hand.shadow_hand_camera_env_cfg import ( + ShadowHandCameraEnvCfg, + ShadowHandTiledCameraCfg, + _ShadowHandBaseTiledCameraCfg, + ) + + @configclass + class _ShadowHandTiledCameraTestCfg(ShadowHandTiledCameraCfg): + distance_to_camera = _ShadowHandBaseTiledCameraCfg(data_types=["distance_to_camera"]) + distance_to_image_plane = _ShadowHandBaseTiledCameraCfg(data_types=["distance_to_image_plane"]) + + @configclass + class _ShadowHandCameraTestEnvCfg(ShadowHandCameraEnvCfg): + tiled_camera = _ShadowHandTiledCameraTestCfg() override_args = [f"presets={_physics_preset_name(physics_backend)},{renderer},{data_type}"] - env_cfg = ShadowHandCameraEnvCfg() + env_cfg = _ShadowHandCameraTestEnvCfg() env_cfg = _apply_overrides_to_env_cfg(env_cfg, override_args) env_cfg.scene.num_envs = 4 @@ -634,7 +651,7 @@ def rendering_test_shadow_hand( if renderer == "ovrtx_renderer": _redirect_ovrtx_renderer_log_to_stdout(env_cfg) - if data_type == "depth": + if data_type in {"depth", "distance_to_camera", "distance_to_image_plane"}: # Disable CNN forward pass as it cannot be meaningfully trained from depth alone and will raise a ValueError. env_cfg.feature_extractor.enabled = False @@ -667,10 +684,28 @@ def rendering_test_cartpole( data_type: str, comparison_scores: list[dict], ) -> None: + from isaaclab.utils.configclass import configclass + from isaaclab_tasks.core.cartpole.cartpole_direct_camera_env import CartpoleCameraEnv - from isaaclab_tasks.core.cartpole.cartpole_direct_camera_env_cfg import CartpoleCameraEnvCfg + from isaaclab_tasks.core.cartpole.cartpole_direct_camera_env_cfg import CartpoleCameraEnvCfg, CartpoleTiledCameraCfg + + @configclass + class _CartpoleTiledCameraTestCfg(CartpoleTiledCameraCfg): + distance_to_camera = CartpoleTiledCameraCfg.BaseCartpoleTiledCameraCfg(data_types=["distance_to_camera"]) + distance_to_image_plane = CartpoleTiledCameraCfg.BaseCartpoleTiledCameraCfg( + data_types=["distance_to_image_plane"] + ) + + @configclass + class _CartpoleCameraTestEnvCfg(CartpoleCameraEnvCfg): + distance_to_camera = CartpoleCameraEnvCfg.BaseCartpoleCameraEnvCfg( + observation_space=[1, 100, 100], tiled_camera=_CartpoleTiledCameraTestCfg() + ) + distance_to_image_plane = CartpoleCameraEnvCfg.BaseCartpoleCameraEnvCfg( + observation_space=[1, 100, 100], tiled_camera=_CartpoleTiledCameraTestCfg() + ) - env_cfg = CartpoleCameraEnvCfg() + env_cfg = _CartpoleCameraTestEnvCfg() env_cfg = _apply_overrides_to_env_cfg( env_cfg, [f"presets={_physics_preset_name(physics_backend)},{renderer},{data_type}"] ) @@ -713,11 +748,48 @@ def rendering_test_dexsuite_kuka( pytest.skip("ovphysx is not supported yet.") from isaaclab.envs import ManagerBasedRLEnv + from isaaclab.sensors import CameraCfg + from isaaclab.utils.configclass import configclass + from isaaclab_tasks.core.dexsuite.config.kuka_allegro.camera_cfg import ( + BASE_CAMERA_CFG, + BaseTiledCameraCfg, + SingleCameraObservationsCfg, + ) from isaaclab_tasks.core.dexsuite.config.kuka_allegro.dexsuite_kuka_allegro_camera_env_cfg import ( + _SCENE_KWARGS, DexsuiteKukaAllegroLiftCameraEnvCfg, + SingleCameraSceneCfg, + ) + from isaaclab_tasks.core.dexsuite.config.kuka_allegro.dexsuite_kuka_allegro_env_cfg import ( + DexsuiteKukaAllegroLiftEnvCfg, ) + @configclass + class _DexsuiteBaseTiledCameraTestCfg(BaseTiledCameraCfg): + distance_to_camera64 = BASE_CAMERA_CFG.replace(data_types=["distance_to_camera"], width=64, height=64) + distance_to_camera128 = BASE_CAMERA_CFG.replace(data_types=["distance_to_camera"], width=128, height=128) + distance_to_camera256 = BASE_CAMERA_CFG.replace(data_types=["distance_to_camera"], width=256, height=256) + distance_to_image_plane64 = BASE_CAMERA_CFG.replace(data_types=["distance_to_image_plane"], width=64, height=64) + distance_to_image_plane128 = BASE_CAMERA_CFG.replace( + data_types=["distance_to_image_plane"], width=128, height=128 + ) + distance_to_image_plane256 = BASE_CAMERA_CFG.replace( + data_types=["distance_to_image_plane"], width=256, height=256 + ) + + @configclass + class _DexsuiteSingleCameraTestSceneCfg(SingleCameraSceneCfg): + base_camera: CameraCfg = _DexsuiteBaseTiledCameraTestCfg() + + @configclass + class _DexsuiteKukaAllegroLiftCameraTestEnvCfg(DexsuiteKukaAllegroLiftCameraEnvCfg): + single_camera = DexsuiteKukaAllegroLiftEnvCfg( + scene=_DexsuiteSingleCameraTestSceneCfg(**_SCENE_KWARGS), + observations=SingleCameraObservationsCfg(), + ) + default = single_camera + override_arg = f"presets={_physics_preset_name(physics_backend)},{renderer},{data_type}64,single_camera" # The default setup uses heterogeneous environments with multiple asset spawner to place random objects. @@ -725,7 +797,7 @@ def rendering_test_dexsuite_kuka( if setup_homogeneous_envs: override_arg += ",cube" - env_cfg = DexsuiteKukaAllegroLiftCameraEnvCfg() + env_cfg = _DexsuiteKukaAllegroLiftCameraTestEnvCfg() env_cfg = _apply_overrides_to_env_cfg(env_cfg, [override_arg]) env_cfg.scene.num_envs = 4 From baf3ae6ee67670190f3d6bf3c2ad02b7fa6d8515 Mon Sep 17 00:00:00 2001 From: rilei-nvidia Date: Thu, 25 Jun 2026 01:02:39 +1200 Subject: [PATCH 4/9] Gate Isaac RTX per-env scene partitioning behind environment variable (#6238) # Description Add ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION environment variable to opt in to authoring primvars:omni:scenePartition and omni:scenePartition on the USD stage. Previously this was always on; now it is off by default. This is to address an OOM in one of our IsaacSim RTX benchmark suites related to primvar inheritance of the scene partition attributes while the root cause/fix is under investigation. ## Testing We have a separate issue which only visualizes env_0 in kit when scene partitioning is enabled... so I tested with: ```bash ./isaaclab.sh train --rl_library rsl_rl \ --task Isaac-Lift-KukaAllegro-Camera \ presets=newton_mjwarp,isaacsim_rtx_renderer,rgb64,single_camera \ --seed 42 \ --num_envs 4 \ --max_iterations 1000 \ --visualizer kit ``` ... and it shows all the envs - checked all the env Xforms and camera and they don't have the scenePartition attribute/primvars: image Conversely, tested with the environment variable enabled: ```bash ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION=1 ./isaaclab.sh train --rl_library rsl_rl \ --task Isaac-Lift-KukaAllegro-Camera \ presets=newton_mjwarp,isaacsim_rtx_renderer,rgb64,single_camera \ --seed 42 \ --num_envs 4 \ --max_iterations 1000 \ --visualizer kit ``` And we get the old behavior of only env_0 showing. @fatimaanes keen to hear if this helps mitigate the benchmark OOM you are seeing. ## Checklist - [x] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --- ...ac-rtx-per-env-scene-partition-env-var.rst | 7 +++ source/isaaclab/isaaclab/utils/renderers.py | 18 ++++++++ ...ac-rtx-per-env-scene-partition-env-var.rst | 7 +++ .../renderers/isaac_rtx_renderer.py | 19 +++++++- ...t_isaac_rtx_renderer_scene_partitioning.py | 44 ++++++++++++++++++- ...ac-rtx-per-env-scene-partition-env-var.rst | 6 +++ source/isaaclab_tasks/test/conftest.py | 8 ++++ .../test/core/test_rendering_cartpole.py | 2 +- .../core/test_rendering_registered_tasks.py | 2 +- ...ac-rtx-per-env-scene-partition-env-var.rst | 7 +++ .../kit/kit_visualizer.py | 17 +++++++ 11 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 source/isaaclab/changelog.d/isaac-rtx-per-env-scene-partition-env-var.rst create mode 100644 source/isaaclab/isaaclab/utils/renderers.py create mode 100644 source/isaaclab_physx/changelog.d/isaac-rtx-per-env-scene-partition-env-var.rst create mode 100644 source/isaaclab_tasks/changelog.d/isaac-rtx-per-env-scene-partition-env-var.rst create mode 100644 source/isaaclab_visualizers/changelog.d/isaac-rtx-per-env-scene-partition-env-var.rst diff --git a/source/isaaclab/changelog.d/isaac-rtx-per-env-scene-partition-env-var.rst b/source/isaaclab/changelog.d/isaac-rtx-per-env-scene-partition-env-var.rst new file mode 100644 index 000000000000..d868abb3fc1e --- /dev/null +++ b/source/isaaclab/changelog.d/isaac-rtx-per-env-scene-partition-env-var.rst @@ -0,0 +1,7 @@ +Added +^^^^^ + +* Added :func:`~isaaclab.utils.renderers.isaac_rtx_per_env_scene_partition_enabled` to + query whether per-environment Isaac RTX scene partitioning is enabled. Set + ``ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION=1`` to enable authoring of + ``primvars:omni:scenePartition`` and ``omni:scenePartition`` on the USD stage. diff --git a/source/isaaclab/isaaclab/utils/renderers.py b/source/isaaclab/isaaclab/utils/renderers.py new file mode 100644 index 000000000000..171204e95014 --- /dev/null +++ b/source/isaaclab/isaaclab/utils/renderers.py @@ -0,0 +1,18 @@ +# 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 + +"""Renderer-related utility helpers.""" + +import os + + +def isaac_rtx_per_env_scene_partition_enabled() -> bool: + """Return whether per-environment RTX scene partitioning is enabled. + + Partitioning is opt-in: set ``ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION=1`` + to enable authoring of ``primvars:omni:scenePartition`` and ``omni:scenePartition`` + on the USD stage. + """ + return os.environ.get("ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION", "0") == "1" diff --git a/source/isaaclab_physx/changelog.d/isaac-rtx-per-env-scene-partition-env-var.rst b/source/isaaclab_physx/changelog.d/isaac-rtx-per-env-scene-partition-env-var.rst new file mode 100644 index 000000000000..826584fd2dba --- /dev/null +++ b/source/isaaclab_physx/changelog.d/isaac-rtx-per-env-scene-partition-env-var.rst @@ -0,0 +1,7 @@ +Changed +^^^^^^^ + +* Changed :meth:`~isaaclab_physx.renderers.IsaacRtxRenderer.prepare_stage` to skip authoring + ``primvars:omni:scenePartition`` and ``omni:scenePartition`` by default. Set the environment + variable ``ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION=1`` to re-enable + per-environment scene partitioning for Isaac RTX rendering. diff --git a/source/isaaclab_physx/isaaclab_physx/renderers/isaac_rtx_renderer.py b/source/isaaclab_physx/isaaclab_physx/renderers/isaac_rtx_renderer.py index 6fa530c52514..5eecd87242b9 100644 --- a/source/isaaclab_physx/isaaclab_physx/renderers/isaac_rtx_renderer.py +++ b/source/isaaclab_physx/isaaclab_physx/renderers/isaac_rtx_renderer.py @@ -22,6 +22,7 @@ from isaaclab.app.settings_manager import get_settings_manager from isaaclab.renderers import BaseRenderer, RenderBufferKind, RenderBufferSpec from isaaclab.renderers.camera_render_spec import CameraRenderSpec +from isaaclab.utils.renderers import isaac_rtx_per_env_scene_partition_enabled from isaaclab.utils.version import get_isaac_sim_version from isaaclab.utils.warp.kernels import reshape_tiled_image from isaaclab.utils.warp.warp_math import clamp_depth_to_inf_wp, replace_inf_depth_wp @@ -187,12 +188,28 @@ def supported_output_types(self) -> dict[RenderBufferKind, RenderBufferSpec]: def prepare_stage(self, stage: Usd.Stage, num_envs: int) -> None: """Author per-env ``omni:scenePartition`` attributes for RTX cull-by-env rendering. - For each ``/World/envs/env_{i}`` root, writes the inheriting primvar + Authoring is only performed when + ``ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION=1`` is set. + When the variable is absent the method is a no-op and no ``primvars:omni:scenePartition`` + or ``omni:scenePartition`` attributes are written to the stage. + + When enabled, for each ``/World/envs/env_{i}`` root, writes the inheriting primvar ``primvars:omni:scenePartition`` (token ``env_{i}``) on the root and the matching non-primvar ``omni:scenePartition`` token on every :class:`UsdGeom.Camera` descendant. RTX honors primvar inheritance, so the env-root primvar propagates to all descendant geometry and isolates each env's render tile. See :meth:`~isaaclab.renderers.base_renderer.BaseRenderer.prepare_stage`.""" + + if not isaac_rtx_per_env_scene_partition_enabled(): + return + + logger.debug( + "Per-environment RTX scene partitioning is enabled" + " (ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION=1)." + " Authoring primvars:omni:scenePartition on %d env(s).", + num_envs, + ) + root_layer = stage.GetRootLayer() token_type = Sdf.ValueTypeNames.Token with Sdf.ChangeBlock(): diff --git a/source/isaaclab_physx/test/renderers/test_isaac_rtx_renderer_scene_partitioning.py b/source/isaaclab_physx/test/renderers/test_isaac_rtx_renderer_scene_partitioning.py index c54bf9ee85ef..22c3ae59a75f 100644 --- a/source/isaaclab_physx/test/renderers/test_isaac_rtx_renderer_scene_partitioning.py +++ b/source/isaaclab_physx/test/renderers/test_isaac_rtx_renderer_scene_partitioning.py @@ -17,6 +17,12 @@ ``isaacsim_ci`` test file in its own app, so keeping these tests isolated here gives them a clean renderer and exercises the real single-scene use case. +Per-env scene partitioning is gated behind the +``ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION`` environment variable and is off +by default. Tests that verify partitioning is *active* use the ``enable_scene_partition`` +fixture, which sets the variable for the duration of the test and restores the previous +state afterwards. + Launch Isaac Sim Simulator first. """ @@ -32,6 +38,7 @@ import pytest import torch import warp as wp +from isaaclab_physx.renderers.isaac_rtx_renderer import IsaacRtxRenderer, IsaacRtxRendererCfg import isaaclab.sim as sim_utils from isaaclab.assets import ArticulationCfg, AssetBaseCfg, RigidObjectCfg @@ -42,9 +49,42 @@ from isaaclab_assets.robots.kuka_allegro import KUKA_ALLEGRO_CFG +_ENV_VAR = "ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION" + + +@pytest.fixture() +def enable_scene_partition(monkeypatch): + """Set ``ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION=1`` for the duration of one test.""" + monkeypatch.setenv(_ENV_VAR, "1") + + +@pytest.mark.isaacsim_ci +def test_partitioning_disabled_by_default(monkeypatch): + """``primvars:omni:scenePartition`` must NOT be authored when the env var is absent. + + The feature is off by default; this test confirms that :meth:`IsaacRtxRenderer.prepare_stage` + is a no-op without ``ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION=1``. + """ + from pxr import Usd + + monkeypatch.delenv(_ENV_VAR, raising=False) + + stage = Usd.Stage.CreateInMemory() + world = stage.DefinePrim("/World", "Xform") # noqa: F841 + env0 = stage.DefinePrim("/World/envs/env_0", "Xform") # noqa: F841 + + renderer = object.__new__(IsaacRtxRenderer) + renderer.cfg = IsaacRtxRendererCfg() + renderer.prepare_stage(stage, num_envs=1) + + prim = stage.GetPrimAtPath("/World/envs/env_0") + assert not prim.HasAttribute("primvars:omni:scenePartition"), ( + "primvars:omni:scenePartition must not be authored when partitioning is disabled." + ) + @pytest.mark.isaacsim_ci -def test_partitioning_isolates_rigid_object(): +def test_partitioning_isolates_rigid_object(enable_scene_partition): """Per-env :class:`~isaaclab.assets.RigidObject` instances at unique world positions render as visibly different per-env tiles when RTX honors ``primvars:omni:scenePartition``.""" @@ -111,7 +151,7 @@ class _Scene(InteractiveSceneCfg): @pytest.mark.isaacsim_ci -def test_partitioning_isolates_articulation(): +def test_partitioning_isolates_articulation(enable_scene_partition): """Per-env :class:`~isaaclab.assets.Articulation` instances driven to wildly different joint poses render as visibly different per-env tiles when RTX honors top-level scene partitions.""" diff --git a/source/isaaclab_tasks/changelog.d/isaac-rtx-per-env-scene-partition-env-var.rst b/source/isaaclab_tasks/changelog.d/isaac-rtx-per-env-scene-partition-env-var.rst new file mode 100644 index 000000000000..0731b9c88cb1 --- /dev/null +++ b/source/isaaclab_tasks/changelog.d/isaac-rtx-per-env-scene-partition-env-var.rst @@ -0,0 +1,6 @@ +Added +^^^^^ + +* Added ``enable_scene_partition`` pytest fixture and enabled Isaac RTX per-environment scene + partitioning in rendering correctness tests for cartpole and registered camera tasks as a + temporary workaround. diff --git a/source/isaaclab_tasks/test/conftest.py b/source/isaaclab_tasks/test/conftest.py index 4cc0cca45509..b37173affaa8 100644 --- a/source/isaaclab_tasks/test/conftest.py +++ b/source/isaaclab_tasks/test/conftest.py @@ -13,4 +13,12 @@ import os import sys +import pytest + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + + +@pytest.fixture() +def enable_scene_partition(monkeypatch): + """Set ``ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION=1`` for the duration of one test.""" + monkeypatch.setenv("ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION", "1") diff --git a/source/isaaclab_tasks/test/core/test_rendering_cartpole.py b/source/isaaclab_tasks/test/core/test_rendering_cartpole.py index 6f10a80a1d4b..5ac49d76df33 100644 --- a/source/isaaclab_tasks/test/core/test_rendering_cartpole.py +++ b/source/isaaclab_tasks/test/core/test_rendering_cartpole.py @@ -32,6 +32,6 @@ @pytest.mark.parametrize("physics_backend,renderer,data_type", PHYSICS_RENDERER_AOV_COMBINATIONS) -def test_rendering_cartpole(physics_backend, renderer, data_type): +def test_rendering_cartpole(physics_backend, renderer, data_type, enable_scene_partition): """Test cartpole environment rendering correctness.""" rendering_test_cartpole(physics_backend, renderer, data_type, _COMPARISON_SCORES) diff --git a/source/isaaclab_tasks/test/core/test_rendering_registered_tasks.py b/source/isaaclab_tasks/test/core/test_rendering_registered_tasks.py index e66538c46c1c..fd2f7535a7da 100644 --- a/source/isaaclab_tasks/test/core/test_rendering_registered_tasks.py +++ b/source/isaaclab_tasks/test/core/test_rendering_registered_tasks.py @@ -86,7 +86,7 @@ def _collect_camera_outputs(env: object) -> dict[str, dict[str, torch.Tensor]]: @pytest.mark.parametrize("task_id, presets, env_name", _RENDER_CORRECTNESS_TASK_IDS) -def test_rendering_registered_tasks(task_id: str, presets: str | None, env_name: str): +def test_rendering_registered_tasks(task_id: str, presets: str | None, env_name: str, enable_scene_partition): """Test registered tasks rendering correctness.""" env = None diff --git a/source/isaaclab_visualizers/changelog.d/isaac-rtx-per-env-scene-partition-env-var.rst b/source/isaaclab_visualizers/changelog.d/isaac-rtx-per-env-scene-partition-env-var.rst new file mode 100644 index 000000000000..664c13b13b67 --- /dev/null +++ b/source/isaaclab_visualizers/changelog.d/isaac-rtx-per-env-scene-partition-env-var.rst @@ -0,0 +1,7 @@ +Changed +^^^^^^^ + +* Changed :class:`~isaaclab_visualizers.kit.KitVisualizer` to skip authoring the + ``omni:scenePartition`` attribute on the viewport camera by default. Set + ``ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION=1`` to re-enable per-environment + scene partitioning for the Kit viewport camera. diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py index 1f8bec44fbc4..4aa0d40892d0 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py @@ -30,6 +30,7 @@ resolve_tiled_env_indices, ) from isaaclab.utils.math import create_rotation_matrix_from_view, quat_from_matrix +from isaaclab.utils.renderers import isaac_rtx_per_env_scene_partition_enabled from isaaclab.visualizers.base_visualizer import BaseVisualizer from isaaclab_visualizers.newton_adapter import resolve_visible_env_indices @@ -513,9 +514,25 @@ def _apply_viewport_camera_scene_partition(self, usd_stage: Usd.Stage, num_envs: ``omni:scenePartition`` token. Interactive viewport cameras live outside ``/World/envs`` and are created by Kit, so they do not inherit the env-root primvar authored by :class:`~isaaclab.scene.InteractiveScene`. + + This method is a no-op unless ``ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION=1``, + matching the opt-in behaviour of + :meth:`~isaaclab_physx.renderers.IsaacRtxRenderer.prepare_stage`. """ + + if not isaac_rtx_per_env_scene_partition_enabled(): + return + if num_envs <= 0 or self._controlled_camera_path is None: return + + logger.debug( + "[KitVisualizer] Per-environment Isaac RTX scene partitioning is enabled" + " (ISAAC_LAB_ENABLE_ISAAC_RTX_PER_ENV_SCENE_PARTITION=1)." + " Authoring omni:scenePartition attribute onto viewport camera '%s'.", + self._controlled_camera_path, + ) + env_id = self._resolved_visible_env_ids[0] if self._resolved_visible_env_ids else 0 camera_prim = usd_stage.GetPrimAtPath(self._controlled_camera_path) if not camera_prim.IsValid() or not camera_prim.IsA(UsdGeom.Camera): From 08b44c71a142e11a0f68ea024827e76bdb5f853a Mon Sep 17 00:00:00 2001 From: vidurv-nvidia Date: Wed, 24 Jun 2026 16:14:52 -0500 Subject: [PATCH 5/9] Adding RigidBody USD data classes and writers (#5976) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description ## Summary (PR 1 of 2 — additive, no behavior change) Introduces single-namespace schema **fragments** so a prim can carry rigid-body properties from multiple USD namespaces at once (`physics:`, `physxRigidBody:`, `mjc:`), with core naming no backend. **Purely additive** — existing cfgs and the deprecated `RigidBodyPropertiesCfg` class are untouched. - **core:** `SchemaFragment`, `RigidBodyFragment`, `UsdPhysicsRigidBodyCfg`; `apply_namespaced` (generic per-fragment writer) and `apply_rigid_body_properties` (applies a fragment list with `UsdPhysics.RigidBodyAPI` as the implicit anchor). - **spawner:** `rigid_props` accepts a fragment list; legacy single cfgs keep working via a transition bridge (shapes/meshes/from_files/mesh_converter). - **backends:** `PhysxRigidBodyCfg` (physx), `MujocoRigidBodyCfg` (newton). - new `test_schema_fragments.py` + exports. **PR 2** (stacked) flips `RigidBodyPropertiesCfg` to a deprecation factory and migrates all call sites: vidurv-nvidia/IsaacLab#4 (review after this lands). ## Test Plan - [x] `test_schema_fragments.py` — 7 passed - [x] pre-commit clean · `./isaaclab.sh -d` docs build clean - [ ] CI First slice of the staged physics schema-cfg refactor (collision / mass / joint-drive / … follow later). Draft for review of the fragment API shape. ## Type of change - New feature (non-breaking change which adds functionality) ## Screenshots N/A — no user-facing visual changes. ## Checklist - [x] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [x] I have run the pre-commit checks (`ruff` + `ruff-format`) on the changed files - [ ] I have made corresponding changes to the documentation (the public API reference is auto-generated via `./isaaclab.sh -d`; not separately authored) - [x] My changes generate no new warnings - [x] I have added tests that prove my feature works - [x] I have added a changelog fragment under `source//changelog.d/` for every touched package - [ ] I have added my name to `CONTRIBUTORS.md` --------- Co-authored-by: ooctipus --- CONTRIBUTORS.md | 1 + .../vidurv-schema-fragments.minor.rst | 19 ++ source/isaaclab/isaaclab/sim/__init__.pyi | 232 +++++++++-------- .../isaaclab/sim/converters/mesh_converter.py | 8 +- .../isaaclab/sim/schemas/__init__.pyi | 10 + .../isaaclab/isaaclab/sim/schemas/schemas.py | 84 ++++++- .../isaaclab/sim/schemas/schemas_cfg.py | 70 ++++++ .../sim/spawners/from_files/from_files.py | 21 +- .../isaaclab/sim/spawners/meshes/meshes.py | 8 +- .../isaaclab/sim/spawners/shapes/shapes.py | 7 +- .../isaaclab/sim/spawners/spawner_cfg.py | 11 +- .../test/sim/test_schema_fragments.py | 237 ++++++++++++++++++ .../vidurv-schema-fragments.minor.rst | 7 + .../isaaclab_newton/sim/schemas/__init__.pyi | 2 + .../sim/schemas/schemas_cfg.py | 26 ++ .../vidurv-schema-fragments.minor.rst | 8 + .../isaaclab_physx/sim/schemas/__init__.pyi | 2 + .../isaaclab_physx/sim/schemas/schemas_cfg.py | 59 +++++ 18 files changed, 691 insertions(+), 121 deletions(-) create mode 100644 source/isaaclab/changelog.d/vidurv-schema-fragments.minor.rst create mode 100644 source/isaaclab/test/sim/test_schema_fragments.py create mode 100644 source/isaaclab_newton/changelog.d/vidurv-schema-fragments.minor.rst create mode 100644 source/isaaclab_physx/changelog.d/vidurv-schema-fragments.minor.rst diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 7c63d42f56ec..ee6967f09fc6 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -184,6 +184,7 @@ Guidelines for modifications: * Tsz Ki GAO * Tyler Lum * Victor Khaustov +* Vidur Vij * Virgilio Gómez Lambo * Vladimir Fokow * Wei Yang diff --git a/source/isaaclab/changelog.d/vidurv-schema-fragments.minor.rst b/source/isaaclab/changelog.d/vidurv-schema-fragments.minor.rst new file mode 100644 index 000000000000..49cea07c0068 --- /dev/null +++ b/source/isaaclab/changelog.d/vidurv-schema-fragments.minor.rst @@ -0,0 +1,19 @@ +Added +^^^^^ + +* Added the single-namespace schema-fragment API: :class:`~isaaclab.sim.schemas.SchemaFragment`, + the :class:`~isaaclab.sim.schemas.RigidBodyFragment` marker, and + :class:`~isaaclab.sim.schemas.UsdPhysicsRigidBodyCfg`. Each fragment carries + ``_usd_namespace`` / ``_usd_applied_schema`` metadata and a ``func`` applier so a prim can + carry rigid-body properties from multiple USD namespaces at once. +* Added :func:`~isaaclab.sim.schemas.apply_namespaced` (generic fragment writer) and + :func:`~isaaclab.sim.schemas.apply_rigid_body_properties` (applies a list of rigid-body + fragments with ``UsdPhysics.RigidBodyAPI`` as the implicit anchor). + +Changed +^^^^^^^ + +* Changed the spawner ``rigid_props`` slot + (:attr:`~isaaclab.sim.spawners.RigidObjectSpawnerCfg.rigid_props`) to also accept a list of + :class:`~isaaclab.sim.schemas.RigidBodyFragment` fragments. Legacy single cfgs continue to + work through a transition bridge in the spawn writers. diff --git a/source/isaaclab/isaaclab/sim/__init__.pyi b/source/isaaclab/isaaclab/sim/__init__.pyi index 05b76be4270c..e0365d3e759c 100644 --- a/source/isaaclab/isaaclab/sim/__init__.pyi +++ b/source/isaaclab/isaaclab/sim/__init__.pyi @@ -59,6 +59,11 @@ __all__ = [ "PhysxJointDrivePropertiesCfg", "PhysxRigidBodyPropertiesCfg", "RigidBodyBaseCfg", + "RigidBodyFragment", + "SchemaFragment", + "UsdPhysicsRigidBodyCfg", + "apply_namespaced", + "apply_rigid_body_properties", "SDFMeshPropertiesCfg", "SpatialTendonPropertiesCfg", "TriangleMeshPropertiesCfg", @@ -192,8 +197,6 @@ __all__ = [ "XformPrimView", ] -from .simulation_cfg import RenderCfg, SimulationCfg -from .simulation_context import SimulationContext, build_simulation_context from .converters import ( AssetConverterBase, AssetConverterBaseCfg, @@ -208,22 +211,6 @@ from .schemas import ( MESH_APPROXIMATION_TOKENS, PHYSX_MESH_COLLISION_CFGS, USD_MESH_COLLISION_CFGS, - activate_contact_sensors, - define_articulation_root_properties, - define_collision_properties, - define_deformable_body_properties, - define_mass_properties, - define_mesh_collision_properties, - define_rigid_body_properties, - modify_articulation_root_properties, - modify_collision_properties, - modify_deformable_body_properties, - modify_fixed_tendon_properties, - modify_joint_drive_properties, - modify_mass_properties, - modify_mesh_collision_properties, - modify_rigid_body_properties, - modify_spatial_tendon_properties, ArticulationRootPropertiesCfg, BoundingCubePropertiesCfg, BoundingSpherePropertiesCfg, @@ -239,11 +226,34 @@ from .schemas import ( PhysxJointDrivePropertiesCfg, PhysxRigidBodyPropertiesCfg, RigidBodyBaseCfg, + RigidBodyFragment, + SchemaFragment, SDFMeshPropertiesCfg, SpatialTendonPropertiesCfg, TriangleMeshPropertiesCfg, TriangleMeshSimplificationPropertiesCfg, + UsdPhysicsRigidBodyCfg, + activate_contact_sensors, + apply_namespaced, + apply_rigid_body_properties, + define_articulation_root_properties, + define_collision_properties, + define_deformable_body_properties, + define_mass_properties, + define_mesh_collision_properties, + define_rigid_body_properties, + modify_articulation_root_properties, + modify_collision_properties, + modify_deformable_body_properties, + modify_fixed_tendon_properties, + modify_joint_drive_properties, + modify_mass_properties, + modify_mesh_collision_properties, + modify_rigid_body_properties, + modify_spatial_tendon_properties, ) +from .simulation_cfg import RenderCfg, SimulationCfg +from .simulation_context import SimulationContext, build_simulation_context # Forwarded to isaaclab_newton.sim.schemas via __getattr__ shim MujocoJointDrivePropertiesCfg = ... @@ -256,46 +266,22 @@ NewtonMeshCollisionPropertiesCfg = ... NewtonRigidBodyPropertiesCfg = ... NewtonSDFCollisionPropertiesCfg = ... from .spawners import ( - SpawnerCfg, - RigidObjectSpawnerCfg, - DeformableObjectSpawnerCfg, - spawn_from_mjcf, - spawn_from_urdf, - spawn_from_usd, - spawn_from_usd_with_compliant_contact_material, - spawn_ground_plane, - GroundPlaneCfg, - MjcfFileCfg, - UrdfFileCfg, - UsdFileCfg, - UsdFileWithCompliantContactCfg, - spawn_light, + CapsuleCfg, + ConeCfg, + CuboidCfg, + CylinderCfg, CylinderLightCfg, + DeformableBodyMaterialBaseCfg, + DeformableBodyMaterialCfg, + DeformableObjectSpawnerCfg, DiskLightCfg, DistantLightCfg, DomeLightCfg, - LightCfg, - SphereLightCfg, - spawn_rigid_body_material, - spawn_deformable_body_material, - PhysicsMaterialCfg, - RigidBodyMaterialCfg, - DeformableBodyMaterialBaseCfg, - DeformableBodyMaterialCfg, - SurfaceDeformableBodyMaterialBaseCfg, - SurfaceDeformableBodyMaterialCfg, - spawn_from_mdl_file, - spawn_preview_surface, + FisheyeCameraCfg, GlassMdlCfg, + GroundPlaneCfg, + LightCfg, MdlFileCfg, - PreviewSurfaceCfg, - VisualMaterialCfg, - spawn_mesh_capsule, - spawn_mesh_cone, - spawn_mesh_cuboid, - spawn_mesh_cylinder, - spawn_mesh_rectangle, - spawn_mesh_sphere, MeshCapsuleCfg, MeshCfg, MeshConeCfg, @@ -303,83 +289,111 @@ from .spawners import ( MeshCylinderCfg, MeshRectangleCfg, MeshSphereCfg, - spawn_camera, - spawn_sensor_frame, - FisheyeCameraCfg, + MjcfFileCfg, + MultiAssetSpawnerCfg, + MultiUsdFileCfg, + PhysicsMaterialCfg, PinholeCameraCfg, + PreviewSurfaceCfg, + RigidBodyMaterialCfg, + RigidObjectSpawnerCfg, SensorFrameCfg, + ShapeCfg, + SpawnerCfg, + SphereCfg, + SphereLightCfg, + SurfaceDeformableBodyMaterialBaseCfg, + SurfaceDeformableBodyMaterialCfg, + UrdfFileCfg, + UsdFileCfg, + UsdFileWithCompliantContactCfg, + VisualMaterialCfg, + spawn_camera, spawn_capsule, spawn_cone, spawn_cuboid, spawn_cylinder, - spawn_sphere, - CapsuleCfg, - ConeCfg, - CuboidCfg, - CylinderCfg, - ShapeCfg, - SphereCfg, + spawn_deformable_body_material, + spawn_from_mdl_file, + spawn_from_mjcf, + spawn_from_urdf, + spawn_from_usd, + spawn_from_usd_with_compliant_contact_material, + spawn_ground_plane, + spawn_light, + spawn_mesh_capsule, + spawn_mesh_cone, + spawn_mesh_cuboid, + spawn_mesh_cylinder, + spawn_mesh_rectangle, + spawn_mesh_sphere, spawn_multi_asset, spawn_multi_usd_file, - MultiAssetSpawnerCfg, - MultiUsdFileCfg, + spawn_preview_surface, + spawn_rigid_body_material, + spawn_sensor_frame, + spawn_sphere, ) from .utils import ( + add_labels, add_reference_to_stage, - get_stage_up_axis, - traverse_stage, - get_prim_at_path, - get_prim_path, - is_prim_path_valid, - define_prim, - get_prim_type_name, - get_next_free_path, + add_usd_reference, + apply_nested, + bind_physics_material, + bind_visual_material, + change_prim_property, + check_missing_labels, + clear_stage, + clone, + close_stage, + convert_world_pose_to_local, + count_total_labels, + create_new_stage, create_prim, + define_prim, delete_prim, - make_uninstanceable, - set_prim_visibility, - safe_set_attribute_on_usd_schema, - safe_set_attribute_on_usd_prim, - change_prim_property, export_prim_to_file, - apply_nested, - clone, - bind_visual_material, - bind_physics_material, - add_usd_reference, - get_usd_references, - select_usd_variants, - get_next_free_prim_path, - get_first_matching_ancestor_prim, - get_first_matching_child_prim, - get_all_matching_child_prims, find_first_matching_prim, - find_matching_prims, - matches_path_expr_prefix, - resolve_matching_prims_from_source, - find_matching_prim_paths, find_global_fixed_joint_prim, - add_labels, + find_matching_prim_paths, + find_matching_prims, + get_all_matching_child_prims, + get_current_stage, + get_current_stage_id, + get_first_matching_ancestor_prim, + get_first_matching_child_prim, get_labels, - remove_labels, - check_missing_labels, - count_total_labels, - resolve_paths, - create_new_stage, + get_next_free_path, + get_next_free_prim_path, + get_prim_at_path, + get_prim_path, + get_prim_type_name, + get_stage_up_axis, + get_usd_references, is_current_stage_in_memory, + is_prim_path_valid, + make_uninstanceable, + matches_path_expr_prefix, open_stage, - use_stage, - update_stage, + remove_labels, + resolve_matching_prims_from_source, + resolve_paths, + resolve_prim_pose, + resolve_prim_scale, + safe_set_attribute_on_usd_prim, + safe_set_attribute_on_usd_schema, save_stage, - close_stage, - clear_stage, - get_current_stage, - get_current_stage_id, + select_usd_variants, + set_prim_visibility, standardize_xform_ops, + traverse_stage, + update_stage, + use_stage, validate_standard_xform_ops, - resolve_prim_pose, - resolve_prim_scale, - convert_world_pose_to_local, ) -from .views import BaseFrameView, UsdFrameView, FrameView -from .views import XformPrimView # deprecated alias +from .views import ( + BaseFrameView, + FrameView, + UsdFrameView, + XformPrimView, # deprecated alias +) diff --git a/source/isaaclab/isaaclab/sim/converters/mesh_converter.py b/source/isaaclab/isaaclab/sim/converters/mesh_converter.py index 74ba8b470c3a..f814e4fdb877 100644 --- a/source/isaaclab/isaaclab/sim/converters/mesh_converter.py +++ b/source/isaaclab/isaaclab/sim/converters/mesh_converter.py @@ -185,9 +185,13 @@ def _convert_asset(self, cfg: MeshConverterCfg): # apply mass properties if cfg.mass_props is not None: schemas.define_mass_properties(prim_path=xform_prim.GetPath(), cfg=cfg.mass_props, stage=stage) - # apply rigid body properties + # apply rigid body properties (transition shim, remove later: fragment list -> apply_*; legacy cfg -> define_*) if cfg.rigid_props is not None: - schemas.define_rigid_body_properties(prim_path=xform_prim.GetPath(), cfg=cfg.rigid_props, stage=stage) + rigid_frags = cfg.rigid_props if isinstance(cfg.rigid_props, (list, tuple)) else [cfg.rigid_props] + if rigid_frags and all(isinstance(f, schemas.SchemaFragment) for f in rigid_frags): + schemas.apply_rigid_body_properties(str(xform_prim.GetPath()), rigid_frags, stage=stage) + else: + schemas.define_rigid_body_properties(prim_path=xform_prim.GetPath(), cfg=cfg.rigid_props, stage=stage) # Save changes to USD stage stage.Save() diff --git a/source/isaaclab/isaaclab/sim/schemas/__init__.pyi b/source/isaaclab/isaaclab/sim/schemas/__init__.pyi index 49eff741c8c8..af153a60fc63 100644 --- a/source/isaaclab/isaaclab/sim/schemas/__init__.pyi +++ b/source/isaaclab/isaaclab/sim/schemas/__init__.pyi @@ -8,6 +8,8 @@ __all__ = [ "PHYSX_MESH_COLLISION_CFGS", "USD_MESH_COLLISION_CFGS", "activate_contact_sensors", + "apply_namespaced", + "apply_rigid_body_properties", "define_actuator_properties", "define_articulation_root_properties", "define_collision_properties", @@ -33,6 +35,9 @@ __all__ = [ "JointDriveBaseCfg", "MassPropertiesCfg", "MeshCollisionBaseCfg", + "RigidBodyFragment", + "SchemaFragment", + "UsdPhysicsRigidBodyCfg", "MujocoJointDrivePropertiesCfg", "MujocoRigidBodyPropertiesCfg", "NewtonArticulationRootPropertiesCfg", @@ -50,6 +55,8 @@ from .schemas import ( PHYSX_MESH_COLLISION_CFGS, USD_MESH_COLLISION_CFGS, activate_contact_sensors, + apply_namespaced, + apply_rigid_body_properties, define_articulation_root_properties, define_collision_properties, define_deformable_body_properties, @@ -80,6 +87,9 @@ from .schemas_cfg import ( MassPropertiesCfg, MeshCollisionBaseCfg, RigidBodyBaseCfg, + RigidBodyFragment, + SchemaFragment, + UsdPhysicsRigidBodyCfg, ) # Forwarded to isaaclab_newton.sim.schemas via __getattr__ shim diff --git a/source/isaaclab/isaaclab/sim/schemas/schemas.py b/source/isaaclab/isaaclab/sim/schemas/schemas.py index 23f53b105cb3..6617543cad61 100644 --- a/source/isaaclab/isaaclab/sim/schemas/schemas.py +++ b/source/isaaclab/isaaclab/sim/schemas/schemas.py @@ -9,6 +9,7 @@ import dataclasses import logging import math +from collections.abc import Iterable import numpy as np import warp as wp @@ -16,7 +17,7 @@ from pxr import Sdf, Usd, UsdGeom, UsdPhysics from isaaclab.sim.utils.stage import get_current_stage -from isaaclab.utils.string import to_camel_case +from isaaclab.utils.string import string_to_callable, to_camel_case from ..utils import ( apply_nested, @@ -214,6 +215,53 @@ class that declares it (walking the MRO). Each group writes under that class's safe_set_attribute_on_usd_prim(prim, f"{namespace}:{usd_attr}", value, camel_case=False) +def apply_namespaced(cfg: schemas_cfg.SchemaFragment, prim_path: str, stage: Usd.Stage | None = None) -> bool: + """Default fragment applier: apply the fragment's schema and write its namespaced attrs. + + Reads :attr:`~isaaclab.sim.schemas.SchemaFragment._usd_namespace` / + :attr:`~isaaclab.sim.schemas.SchemaFragment._usd_applied_schema` from the cfg's class. If the + fragment owns an applied schema, it is applied (once). Each non-``None`` dataclass field is + written as ``:``; the ``func`` field is skipped. ``None`` fields + are left unchanged on the prim (partial update). + + Args: + cfg: The fragment instance carrying ``_usd_namespace`` / ``_usd_applied_schema`` metadata. + prim_path: The prim path to author on. + stage: The stage where to find the prim. Defaults to None, in which case the current + stage is used. + + Returns: + True if the properties were successfully set. + """ + if stage is None: + stage = get_current_stage() + prim = stage.GetPrimAtPath(prim_path) + # fail loudly on an invalid path (matches the legacy define_/modify_ writers) + if not prim.IsValid(): + raise ValueError(f"Prim path '{prim_path}' is not valid.") + namespace = type(cfg)._usd_namespace + applied = type(cfg)._usd_applied_schema + # every fragment field is a namespaced USD attribute, so a namespace is required + if namespace is None: + raise ValueError( + f"Fragment '{type(cfg).__name__}' has no '_usd_namespace' set. Every fragment field is" + " authored as ':', so a USD namespace is required; non-USD state must" + " live on the spawner cfg or be passed as a writer keyword argument, not as a fragment" + " field." + ) + if applied and applied not in prim.GetAppliedSchemas(): + prim.AddAppliedSchema(applied) + for f in dataclasses.fields(cfg): + # ``func`` is the only non-USD field; non-scalar values raise in the setter + if f.name == "func": + continue + value = getattr(cfg, f.name) + if value is None: + continue + safe_set_attribute_on_usd_prim(prim, f"{namespace}:{to_camel_case(f.name, 'cC')}", value, camel_case=False) + return True + + """ Articulation root properties. """ @@ -386,6 +434,40 @@ def modify_articulation_root_properties( """ +def apply_rigid_body_properties( + prim_path: str, fragments: Iterable[schemas_cfg.RigidBodyFragment], stage: Usd.Stage | None = None +) -> bool: + """Apply a list of rigid-body fragments to a prim. + + Applies ``UsdPhysics.RigidBodyAPI`` as the implicit anchor (the defining schema for a rigid + body), then dispatches each fragment via its :attr:`~isaaclab.sim.schemas.SchemaFragment.func`. + Backend fragments carry backend-specific funcs, so core never imports a backend. + + Args: + prim_path: The prim path to apply the rigid-body schemas on. + fragments: An iterable of :class:`~isaaclab.sim.schemas.RigidBodyFragment` instances. + stage: The stage where to find the prim. Defaults to None, in which case the current + stage is used. + + Returns: + True if the properties were successfully set. + """ + if stage is None: + stage = get_current_stage() + prim = stage.GetPrimAtPath(prim_path) + # fail loudly on an invalid path (matches the legacy define_rigid_body_properties writer) + if not prim.IsValid(): + raise ValueError(f"Prim path '{prim_path}' is not valid.") + if not UsdPhysics.RigidBodyAPI(prim): + UsdPhysics.RigidBodyAPI.Apply(prim) + # aggregate per-fragment results so a reported failure is not masked by the always-applied anchor + success = True + for cfg in fragments: + func = cfg.func if callable(cfg.func) else string_to_callable(cfg.func) + success = bool(func(cfg, prim_path, stage)) and success + return success + + def define_rigid_body_properties(prim_path: str, cfg: schemas_cfg.RigidBodyBaseCfg, stage: Usd.Stage | None = None): """Apply the rigid body schema on the input prim and set its properties. diff --git a/source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py b/source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py index 4eaec2004e91..ac5d8b22c020 100644 --- a/source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py +++ b/source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py @@ -6,6 +6,7 @@ from __future__ import annotations import warnings +from collections.abc import Callable from typing import ClassVar, Literal from isaaclab.utils.configclass import configclass @@ -106,6 +107,75 @@ def _deprecate_field_alias(cfg, alias: str, canonical: str) -> None: setattr(cfg, alias, None) +@configclass +class SchemaFragment: + """Base for a single-namespace USD-schema config fragment. + + Each subclass mirrors exactly one USD applied schema. The fragment carries class-level + metadata describing which USD namespace its fields write to (:attr:`_usd_namespace`) and + which applied schema, if any, it owns (:attr:`_usd_applied_schema`). The :attr:`func` + field names the callable that applies the fragment to a prim; the default generic applier + (:func:`~isaaclab.sim.schemas.apply_namespaced`) reads the metadata and writes each + non-``None`` field as ``:``. Irregular APIs override + :attr:`func` with a custom applier. + + .. note:: + A fragment present in a spawner slot means its schema is applied. ``None`` fields are + left unchanged on the prim (partial update). + + .. important:: + Every dataclass field other than :attr:`func` is authored as a USD attribute + ``<_usd_namespace>:``. A fragment must not carry non-USD/bookkeeping + fields -- such state belongs on the spawner cfg or as a writer keyword argument (this is + why ``fix_root_link`` / ``ensure_drives_exist`` are not fragment fields). The generic + applier (:func:`~isaaclab.sim.schemas.apply_namespaced`) enforces the invariant: it raises + when a fragment has no ``_usd_namespace``, and unsupported (non-scalar) value types raise + when written. + """ + + # -- Class metadata (not dataclass fields) -- + _usd_namespace: ClassVar[str | None] = None + _usd_applied_schema: ClassVar[str | None] = None + + func: Callable | str = "isaaclab.sim.schemas:apply_namespaced" + """Callable (or its ``module:attr`` import string) that applies this fragment to a prim. + + Resolved via :func:`~isaaclab.utils.string.string_to_callable` when a string. The callable + signature is ``func(cfg, prim_path, stage)``. + """ + + +@configclass +class RigidBodyFragment(SchemaFragment): + """Marker base for rigid-body fragments; types the ``rigid_props`` slot.""" + + pass + + +@configclass +class UsdPhysicsRigidBodyCfg(RigidBodyFragment): + """``physics:*`` rigid-body attributes from `UsdPhysics.RigidBodyAPI`_. + + The ``UsdPhysics.RigidBodyAPI`` schema is applied as the implicit anchor by the rigid-body + family writer, so this fragment owns no applied schema of its own. + + .. _UsdPhysics.RigidBodyAPI: https://openusd.org/dev/api/class_usd_physics_rigid_body_a_p_i.html + """ + + _usd_namespace: ClassVar[str | None] = "physics" + _usd_applied_schema: ClassVar[str | None] = None # RigidBodyAPI applied by the family anchor + + rigid_body_enabled: bool | None = None + """Whether to enable or disable the rigid body.""" + + kinematic_enabled: bool | None = None + """Determines whether the body is kinematic or not. + + A kinematic body is moved through animated or user-defined poses; the simulation still + derives velocities for it based on the external motion. + """ + + @configclass class ArticulationRootBaseCfg: """Solver-common properties to apply to the root of an articulation. diff --git a/source/isaaclab/isaaclab/sim/spawners/from_files/from_files.py b/source/isaaclab/isaaclab/sim/spawners/from_files/from_files.py index 3a6fa939a63e..4545eb23e115 100644 --- a/source/isaaclab/isaaclab/sim/spawners/from_files/from_files.py +++ b/source/isaaclab/isaaclab/sim/spawners/from_files/from_files.py @@ -344,7 +344,12 @@ def _spawn_from_usd_file( # modify rigid body properties if cfg.rigid_props is not None: - schemas.modify_rigid_body_properties(prim_path, cfg.rigid_props) + # transition shim, remove later: new fragment list -> apply_*; legacy single cfg -> modify_* + rigid_frags = cfg.rigid_props if isinstance(cfg.rigid_props, (list, tuple)) else [cfg.rigid_props] + if rigid_frags and all(isinstance(f, schemas.SchemaFragment) for f in rigid_frags): + schemas.apply_rigid_body_properties(prim_path, rigid_frags) + else: + schemas.modify_rigid_body_properties(prim_path, cfg.rigid_props) # modify collision properties if cfg.collision_props is not None: schemas.modify_collision_properties(prim_path, cfg.collision_props) @@ -368,10 +373,18 @@ def _spawn_from_usd_file( # without it — actuatorgravcomp has no effect since there are no forces to route. # Only auto-populates when the user did not already set ``gravcomp`` themselves; # an explicit ``MujocoRigidBodyPropertiesCfg(gravcomp=0.5)`` is preserved as-is. - from isaaclab_newton.sim.schemas.schemas_cfg import MujocoJointDrivePropertiesCfg, MujocoRigidBodyPropertiesCfg + from isaaclab_newton.sim.schemas.schemas_cfg import ( + MujocoJointDrivePropertiesCfg, + MujocoRigidBodyCfg, + MujocoRigidBodyPropertiesCfg, + ) - body_gravcomp_unset = ( - not isinstance(cfg.rigid_props, MujocoRigidBodyPropertiesCfg) or cfg.rigid_props.gravcomp is None + # gravcomp may be authored either via the legacy MujocoRigidBodyPropertiesCfg or via a + # MujocoRigidBodyCfg fragment in a rigid_props list. Treat either as "already set". + rigid_props_list = cfg.rigid_props if isinstance(cfg.rigid_props, (list, tuple)) else [cfg.rigid_props] + body_gravcomp_unset = not any( + isinstance(f, (MujocoRigidBodyPropertiesCfg, MujocoRigidBodyCfg)) and f.gravcomp is not None + for f in rigid_props_list ) if ( isinstance(cfg.joint_drive_props, MujocoJointDrivePropertiesCfg) diff --git a/source/isaaclab/isaaclab/sim/spawners/meshes/meshes.py b/source/isaaclab/isaaclab/sim/spawners/meshes/meshes.py index cfc7f51b9ff2..27f815343c0a 100644 --- a/source/isaaclab/isaaclab/sim/spawners/meshes/meshes.py +++ b/source/isaaclab/isaaclab/sim/spawners/meshes/meshes.py @@ -444,5 +444,9 @@ def _spawn_mesh_geom_from_mesh( # apply mass properties if cfg.mass_props is not None: schemas.define_mass_properties(prim_path, cfg.mass_props, stage=stage) - # apply rigid properties - schemas.define_rigid_body_properties(prim_path, cfg.rigid_props, stage=stage) + # apply rigid properties (transition shim, remove later: fragment list -> apply_*; legacy cfg -> define_*) + rigid_frags = cfg.rigid_props if isinstance(cfg.rigid_props, (list, tuple)) else [cfg.rigid_props] + if rigid_frags and all(isinstance(f, schemas.SchemaFragment) for f in rigid_frags): + schemas.apply_rigid_body_properties(prim_path, rigid_frags, stage=stage) + else: + schemas.define_rigid_body_properties(prim_path, cfg.rigid_props, stage=stage) diff --git a/source/isaaclab/isaaclab/sim/spawners/shapes/shapes.py b/source/isaaclab/isaaclab/sim/spawners/shapes/shapes.py index 9e8eafc1c578..0601cbed5411 100644 --- a/source/isaaclab/isaaclab/sim/spawners/shapes/shapes.py +++ b/source/isaaclab/isaaclab/sim/spawners/shapes/shapes.py @@ -322,4 +322,9 @@ def _spawn_geom_from_prim_type( schemas.define_mass_properties(prim_path, cfg.mass_props, stage=stage) # apply rigid body properties if cfg.rigid_props is not None: - schemas.define_rigid_body_properties(prim_path, cfg.rigid_props, stage=stage) + # transition shim, remove later: new fragment list -> apply_*; legacy single cfg -> define_* + rigid_frags = cfg.rigid_props if isinstance(cfg.rigid_props, (list, tuple)) else [cfg.rigid_props] + if rigid_frags and all(isinstance(f, schemas.SchemaFragment) for f in rigid_frags): + schemas.apply_rigid_body_properties(prim_path, rigid_frags, stage=stage) + else: + schemas.define_rigid_body_properties(prim_path, cfg.rigid_props, stage=stage) diff --git a/source/isaaclab/isaaclab/sim/spawners/spawner_cfg.py b/source/isaaclab/isaaclab/sim/spawners/spawner_cfg.py index 3f1eef72a2fa..1d52d451ba1b 100644 --- a/source/isaaclab/isaaclab/sim/spawners/spawner_cfg.py +++ b/source/isaaclab/isaaclab/sim/spawners/spawner_cfg.py @@ -85,11 +85,18 @@ class RigidObjectSpawnerCfg(SpawnerCfg): mass_props: schemas.MassPropertiesCfg | None = None """Mass properties.""" - rigid_props: schemas.RigidBodyBaseCfg | None = None + rigid_props: schemas.RigidBodyBaseCfg | schemas.RigidBodyFragment | list[schemas.RigidBodyFragment] | None = None """Rigid body properties. + Accepts either a single legacy cfg (e.g. :class:`~isaaclab.sim.schemas.RigidBodyBaseCfg`) or a + list of :class:`~isaaclab.sim.schemas.RigidBodyFragment` fragments + (e.g. ``[UsdPhysicsRigidBodyCfg(...), PhysxRigidBodyCfg(...)]``). When a fragment list is given, + ``UsdPhysics.RigidBodyAPI`` is applied as the implicit anchor and each fragment writes its own + namespace. + For making a rigid object static, set the :attr:`schemas.RigidBodyBaseCfg.kinematic_enabled` - as True. This will make the object static and will not be affected by gravity or other forces. + (or :attr:`~isaaclab.sim.schemas.UsdPhysicsRigidBodyCfg.kinematic_enabled`) as True. This will + make the object static and will not be affected by gravity or other forces. """ collision_props: schemas.CollisionPropertiesCfg | None = None diff --git a/source/isaaclab/test/sim/test_schema_fragments.py b/source/isaaclab/test/sim/test_schema_fragments.py new file mode 100644 index 000000000000..b76239963da2 --- /dev/null +++ b/source/isaaclab/test/sim/test_schema_fragments.py @@ -0,0 +1,237 @@ +# 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 + +"""Launch Isaac Sim Simulator first.""" + +from isaaclab.app import AppLauncher + +# launch omniverse app +simulation_app = AppLauncher(headless=True).app + +"""Rest everything follows.""" + +import pytest + +from pxr import UsdGeom, UsdPhysics + +import isaaclab.sim as sim_utils +from isaaclab.sim import SimulationCfg, SimulationContext + + +def _make_xform(stage, path="/World/Body"): + UsdGeom.Xform.Define(stage, path) + return stage.GetPrimAtPath(path) + + +# ------------------------------------------------------------------------------------- +# SchemaFragment base, RigidBodyFragment marker, UsdPhysicsRigidBodyCfg +# ------------------------------------------------------------------------------------- + + +def test_fragment_metadata_defaults(): + from isaaclab.sim.schemas import RigidBodyFragment, SchemaFragment, UsdPhysicsRigidBodyCfg + + cfg = UsdPhysicsRigidBodyCfg(rigid_body_enabled=True) + assert isinstance(cfg, RigidBodyFragment) and isinstance(cfg, SchemaFragment) + assert type(cfg)._usd_namespace == "physics" + assert type(cfg)._usd_applied_schema is None # anchor applies RigidBodyAPI, not the fragment + assert cfg.func == "isaaclab.sim.schemas:apply_namespaced" + assert cfg.rigid_body_enabled is True and cfg.kinematic_enabled is None + + +# ------------------------------------------------------------------------------------- +# apply_namespaced generic applier +# ------------------------------------------------------------------------------------- + + +def test_apply_namespaced_writes_only_set_fields(): + from isaaclab.sim.schemas import UsdPhysicsRigidBodyCfg, apply_namespaced + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + prim = _make_xform(stage) + UsdPhysics.RigidBodyAPI.Apply(prim) + apply_namespaced(UsdPhysicsRigidBodyCfg(rigid_body_enabled=True), "/World/Body", stage) + assert prim.GetAttribute("physics:rigidBodyEnabled").Get() is True + # ``kinematicEnabled`` is a RigidBodyAPI fallback attr (so HasAttribute is True), but the + # None field must not be authored by apply_namespaced. + assert not prim.GetAttribute("physics:kinematicEnabled").HasAuthoredValue() + + +# ------------------------------------------------------------------------------------- +# PhysxRigidBodyCfg (isaaclab_physx) +# ------------------------------------------------------------------------------------- + + +def test_physx_rigid_body_fragment_writes_physx_namespace(): + from isaaclab_physx.sim.schemas import PhysxRigidBodyCfg + + from isaaclab.sim.schemas import apply_namespaced + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + prim = _make_xform(stage, "/World/B2") + UsdPhysics.RigidBodyAPI.Apply(prim) + apply_namespaced(PhysxRigidBodyCfg(linear_damping=0.1, disable_gravity=True), "/World/B2", stage) + assert abs(prim.GetAttribute("physxRigidBody:linearDamping").Get() - 0.1) < 1e-6 + assert prim.GetAttribute("physxRigidBody:disableGravity").Get() is True + + +# ------------------------------------------------------------------------------------- +# MujocoRigidBodyCfg (isaaclab_newton) +# ------------------------------------------------------------------------------------- + + +def test_mujoco_rigid_body_fragment_writes_mjc_namespace(): + from isaaclab_newton.sim.schemas import MujocoRigidBodyCfg + + from isaaclab.sim.schemas import apply_namespaced + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + prim = _make_xform(stage, "/World/B3") + UsdPhysics.RigidBodyAPI.Apply(prim) + apply_namespaced(MujocoRigidBodyCfg(gravcomp=1.0), "/World/B3", stage) + assert abs(prim.GetAttribute("mjc:gravcomp").Get() - 1.0) < 1e-6 + + +# ------------------------------------------------------------------------------------- +# apply_rigid_body_properties dispatch (implicit anchor + multi-namespace) +# ------------------------------------------------------------------------------------- + + +def test_apply_rigid_body_properties_composes_namespaces(): + from isaaclab_newton.sim.schemas import MujocoRigidBodyCfg + from isaaclab_physx.sim.schemas import PhysxRigidBodyCfg + + from isaaclab.sim.schemas import UsdPhysicsRigidBodyCfg, apply_rigid_body_properties + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + _make_xform(stage, "/World/B4") + apply_rigid_body_properties( + "/World/B4", + [ + UsdPhysicsRigidBodyCfg(rigid_body_enabled=True), + PhysxRigidBodyCfg(linear_damping=0.2), + MujocoRigidBodyCfg(gravcomp=1.0), + ], + stage, + ) + prim = stage.GetPrimAtPath("/World/B4") + assert bool(UsdPhysics.RigidBodyAPI(prim)) # implicit anchor applied + assert prim.GetAttribute("physics:rigidBodyEnabled").Get() is True + assert abs(prim.GetAttribute("physxRigidBody:linearDamping").Get() - 0.2) < 1e-6 + assert abs(prim.GetAttribute("mjc:gravcomp").Get() - 1.0) < 1e-6 + + +# ------------------------------------------------------------------------------------- +# spawner slot accepts a fragment list + transition routing +# ------------------------------------------------------------------------------------- + + +def test_spawn_shape_with_rigid_fragment_list(): + from isaaclab_physx.sim.schemas import PhysxRigidBodyCfg + + from isaaclab.sim.schemas import UsdPhysicsRigidBodyCfg + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + cfg = sim_utils.CuboidCfg( + size=(1, 1, 1), + rigid_props=[UsdPhysicsRigidBodyCfg(rigid_body_enabled=True), PhysxRigidBodyCfg(linear_damping=0.3)], + ) + cfg.func("/World/Cube", cfg) + prim = sim_utils.get_current_stage().GetPrimAtPath("/World/Cube") + assert bool(UsdPhysics.RigidBodyAPI(prim)) + assert abs(prim.GetAttribute("physxRigidBody:linearDamping").Get() - 0.3) < 1e-6 + + +# ------------------------------------------------------------------------------------- +# public imports +# ------------------------------------------------------------------------------------- + + +def test_public_imports(): + from isaaclab_newton.sim.schemas import MujocoRigidBodyCfg # noqa: F401 + from isaaclab_physx.sim.schemas import PhysxRigidBodyCfg # noqa: F401 + + from isaaclab.sim.schemas import ( # noqa: F401 + RigidBodyFragment, + SchemaFragment, + UsdPhysicsRigidBodyCfg, + apply_namespaced, + apply_rigid_body_properties, + ) + + +# ------------------------------------------------------------------------------------- +# Review follow-ups -- prim-validity guard, aggregated return, namespace invariant guard +# ------------------------------------------------------------------------------------- + + +def test_apply_namespaced_raises_on_invalid_prim(): + from isaaclab.sim.schemas import UsdPhysicsRigidBodyCfg, apply_namespaced + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + # no prim authored at this path -> GetPrimAtPath returns an invalid prim + with pytest.raises(ValueError): + apply_namespaced(UsdPhysicsRigidBodyCfg(rigid_body_enabled=True), "/World/DoesNotExist", stage) + + +def test_apply_rigid_body_properties_raises_on_invalid_prim(): + from isaaclab.sim.schemas import UsdPhysicsRigidBodyCfg, apply_rigid_body_properties + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + with pytest.raises(ValueError): + apply_rigid_body_properties("/World/DoesNotExist", [UsdPhysicsRigidBodyCfg(rigid_body_enabled=True)], stage) + + +def test_apply_rigid_body_properties_aggregates_fragment_results(): + from isaaclab.sim.schemas import UsdPhysicsRigidBodyCfg, apply_rigid_body_properties + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + _make_xform(stage, "/World/Agg") + + # a fragment whose applier reports failure must make the aggregate return False + failing = UsdPhysicsRigidBodyCfg(rigid_body_enabled=True) + failing.func = lambda cfg, prim_path, stage=None: False + assert apply_rigid_body_properties("/World/Agg", [failing], stage) is False + + # all-succeeding fragments return True + ok = UsdPhysicsRigidBodyCfg(rigid_body_enabled=True) + assert apply_rigid_body_properties("/World/Agg", [ok], stage) is True + + +def test_apply_namespaced_raises_without_namespace(): + from typing import ClassVar + + from isaaclab.sim.schemas import RigidBodyFragment, apply_namespaced + from isaaclab.utils import configclass + + @configclass + class _NoNamespaceFragment(RigidBodyFragment): + # deliberately leaves ``_usd_namespace`` as None, violating the fragment invariant that + # every field is authored as a namespaced USD attribute + _usd_namespace: ClassVar[str | None] = None + rigid_body_enabled: bool | None = None + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + prim = _make_xform(stage, "/World/NoNs") + UsdPhysics.RigidBodyAPI.Apply(prim) + with pytest.raises(ValueError): + apply_namespaced(_NoNamespaceFragment(rigid_body_enabled=True), "/World/NoNs", stage) diff --git a/source/isaaclab_newton/changelog.d/vidurv-schema-fragments.minor.rst b/source/isaaclab_newton/changelog.d/vidurv-schema-fragments.minor.rst new file mode 100644 index 000000000000..a4cb8ef1dddf --- /dev/null +++ b/source/isaaclab_newton/changelog.d/vidurv-schema-fragments.minor.rst @@ -0,0 +1,7 @@ +Added +^^^^^ + +* Added :class:`~isaaclab_newton.sim.schemas.MujocoRigidBodyCfg`, the ``mjc:*`` single-namespace + rigid-body fragment (``mjc:gravcomp``) for Newton's MuJoCo solver. It composes with + :class:`~isaaclab.sim.schemas.UsdPhysicsRigidBodyCfg` and + :class:`~isaaclab_physx.sim.schemas.PhysxRigidBodyCfg` in a ``rigid_props`` fragment list. diff --git a/source/isaaclab_newton/isaaclab_newton/sim/schemas/__init__.pyi b/source/isaaclab_newton/isaaclab_newton/sim/schemas/__init__.pyi index e546498ee4aa..89ca2d068afb 100644 --- a/source/isaaclab_newton/isaaclab_newton/sim/schemas/__init__.pyi +++ b/source/isaaclab_newton/isaaclab_newton/sim/schemas/__init__.pyi @@ -5,6 +5,7 @@ __all__ = [ "MujocoJointDrivePropertiesCfg", + "MujocoRigidBodyCfg", "MujocoRigidBodyPropertiesCfg", "NewtonArticulationRootPropertiesCfg", "NewtonCollisionPropertiesCfg", @@ -18,6 +19,7 @@ __all__ = [ from .schemas_cfg import ( MujocoJointDrivePropertiesCfg, + MujocoRigidBodyCfg, MujocoRigidBodyPropertiesCfg, NewtonArticulationRootPropertiesCfg, NewtonCollisionPropertiesCfg, diff --git a/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py b/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py index a7b0d0082577..4c99379e1ad8 100644 --- a/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py +++ b/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py @@ -14,6 +14,7 @@ JointDriveBaseCfg, MeshCollisionBaseCfg, RigidBodyBaseCfg, + RigidBodyFragment, ) from isaaclab.sim.spawners.materials.physics_materials_cfg import RigidBodyMaterialBaseCfg from isaaclab.utils.configclass import configclass @@ -84,6 +85,31 @@ class MujocoRigidBodyPropertiesCfg(NewtonRigidBodyPropertiesCfg): """ +@configclass +class MujocoRigidBodyCfg(RigidBodyFragment): + """``mjc:*`` rigid-body attributes for Newton's MuJoCo solver. + + A single-namespace fragment (see :class:`~isaaclab.sim.schemas.SchemaFragment`) carrying + body-level gravity compensation. The ``mjc`` namespace has no applied schema; the + ``UsdPhysics.RigidBodyAPI`` anchor is applied by + :func:`~isaaclab.sim.schemas.apply_rigid_body_properties`. + + .. note:: + A ``newton:*`` rigid-body fragment is reserved but currently empty (Newton has no native + ``newton:`` rigid-body attributes today). + """ + + _usd_namespace: ClassVar[str | None] = "mjc" + _usd_applied_schema: ClassVar[str | None] = None + + gravcomp: float | None = None + """Gravity compensation scale for the body [dimensionless]. + + ``0.0`` = no compensation; ``1.0`` = full compensation. Written to ``mjc:gravcomp``. Body-level + gravcomp must be set for joint-level ``actuatorgravcomp`` to have any effect. + """ + + @configclass class NewtonJointDrivePropertiesCfg(JointDriveBaseCfg): """Newton-targeted joint drive properties. diff --git a/source/isaaclab_physx/changelog.d/vidurv-schema-fragments.minor.rst b/source/isaaclab_physx/changelog.d/vidurv-schema-fragments.minor.rst new file mode 100644 index 000000000000..6bd8bcac8b86 --- /dev/null +++ b/source/isaaclab_physx/changelog.d/vidurv-schema-fragments.minor.rst @@ -0,0 +1,8 @@ +Added +^^^^^ + +* Added :class:`~isaaclab_physx.sim.schemas.PhysxRigidBodyCfg`, the ``physxRigidBody:*`` + single-namespace rigid-body fragment (PhysX ``PhysxRigidBodyAPI``). It carries the PhysX + damping / velocity-limit / solver-iteration / sleep fields plus ``disable_gravity``, and + composes with :class:`~isaaclab.sim.schemas.UsdPhysicsRigidBodyCfg` in a ``rigid_props`` + fragment list. diff --git a/source/isaaclab_physx/isaaclab_physx/sim/schemas/__init__.pyi b/source/isaaclab_physx/isaaclab_physx/sim/schemas/__init__.pyi index b542edf9f454..10d2502ddf2b 100644 --- a/source/isaaclab_physx/isaaclab_physx/sim/schemas/__init__.pyi +++ b/source/isaaclab_physx/isaaclab_physx/sim/schemas/__init__.pyi @@ -23,6 +23,7 @@ __all__ = [ "PhysxDeformableCollisionPropertiesCfg", "PhysxFixedTendonPropertiesCfg", "PhysxJointDrivePropertiesCfg", + "PhysxRigidBodyCfg", "PhysxRigidBodyPropertiesCfg", "PhysxSDFMeshPropertiesCfg", "PhysxSpatialTendonPropertiesCfg", @@ -57,6 +58,7 @@ from .schemas_cfg import ( PhysxDeformableCollisionPropertiesCfg, PhysxFixedTendonPropertiesCfg, PhysxJointDrivePropertiesCfg, + PhysxRigidBodyCfg, PhysxRigidBodyPropertiesCfg, PhysxSDFMeshPropertiesCfg, PhysxSpatialTendonPropertiesCfg, diff --git a/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas_cfg.py b/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas_cfg.py index 2eacd969e949..18a5de023f90 100644 --- a/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas_cfg.py +++ b/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas_cfg.py @@ -15,6 +15,7 @@ JointDriveBaseCfg, MeshCollisionBaseCfg, RigidBodyBaseCfg, + RigidBodyFragment, ) from isaaclab.utils.configclass import configclass @@ -261,6 +262,64 @@ class PhysxRigidBodyPropertiesCfg(RigidBodyBaseCfg): """The mass-normalized kinetic energy threshold below which an actor may participate in stabilization.""" +@configclass +class PhysxRigidBodyCfg(RigidBodyFragment): + """``physxRigidBody:*`` rigid-body attributes from `PhysxRigidBodyAPI`_. + + A single-namespace fragment (see :class:`~isaaclab.sim.schemas.SchemaFragment`) for the + PhysX rigid-body add-on schema. Applied alongside :class:`~isaaclab.sim.schemas.UsdPhysicsRigidBodyCfg` + via :func:`~isaaclab.sim.schemas.apply_rigid_body_properties`. + + .. _PhysxRigidBodyAPI: https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/104.2/class_physx_schema_physx_rigid_body_a_p_i.html + """ + + _usd_namespace: ClassVar[str | None] = "physxRigidBody" + _usd_applied_schema: ClassVar[str | None] = "PhysxRigidBodyAPI" + + linear_damping: float | None = None + """Linear damping coefficient for the body [1/s].""" + + angular_damping: float | None = None + """Angular damping coefficient for the body [1/s].""" + + max_linear_velocity: float | None = None + """Maximum linear velocity for the body [m/s].""" + + max_angular_velocity: float | None = None + """Maximum angular velocity for the body [deg/s].""" + + max_depenetration_velocity: float | None = None + """Maximum depenetration velocity permitted to be introduced by the solver [m/s].""" + + max_contact_impulse: float | None = None + """The limit on the impulse that may be applied at a contact [N·s].""" + + enable_gyroscopic_forces: bool | None = None + """Enables computation of gyroscopic forces on the rigid body.""" + + retain_accelerations: bool | None = None + """Carries over forces/accelerations over sub-steps.""" + + solver_position_iteration_count: int | None = None + """Solver position iteration counts for the body.""" + + solver_velocity_iteration_count: int | None = None + """Solver velocity iteration counts for the body.""" + + sleep_threshold: float | None = None + """Mass-normalized kinetic energy threshold below which an actor may go to sleep [m²/s²].""" + + stabilization_threshold: float | None = None + """Mass-normalized kinetic energy threshold below which an actor may participate in stabilization [m²/s²].""" + + disable_gravity: bool | None = None + """Disable gravity for the body. + + PhysX honors this per-body via ``physxRigidBody:disableGravity``: setting True excludes the + body from world gravity integration. + """ + + @configclass class RigidBodyPropertiesCfg(PhysxRigidBodyPropertiesCfg): """Deprecated: use :class:`PhysxRigidBodyPropertiesCfg` or :class:`~isaaclab.sim.schemas.RigidBodyBaseCfg`. From 46a6ebacaff5591b1fc2ac9c45d955479e93c5cd Mon Sep 17 00:00:00 2001 From: Vidur Vij Date: Thu, 4 Jun 2026 18:23:20 -0700 Subject: [PATCH 6/9] feat(schemas): add mesh-collision schema-fragment API Add the additive mesh-collision schema-fragment family, mirroring the rigid-body pilot. Core gains the MeshCollisionFragment marker, the UsdPhysicsMeshCollisionCfg fragment carrying the physics:approximation token, and the apply_mesh_collision_properties family writer (applies the MeshCollisionAPI anchor, resolves and validates the approximation token from whichever cooking fragment is present, then dispatches each fragment via its func). PhysX cooking fragments (PhysxConvexHullCfg, PhysxConvexDecompositionCfg, PhysxTriangleMeshCfg, PhysxTriangleMeshSimplificationCfg, PhysxSDFMeshCfg) and Newton cooking fragments (NewtonMeshCollisionCfg, NewtonSDFCollisionCfg) each own a single namespace and applied schema, dispatched through the generic apply_namespaced applier. Widen the MeshConverterCfg.mesh_collision_props slot to accept a fragment list and add the transition bridge in mesh_converter.py without breaking the legacy single-cfg path. Legacy cfgs and modify_mesh_collision_properties stay intact. --- ...vidurv-schema-frag-meshcollision.minor.rst | 20 ++ source/isaaclab/isaaclab/sim/__init__.pyi | 6 + .../isaaclab/sim/converters/mesh_converter.py | 18 +- .../sim/converters/mesh_converter_cfg.py | 16 +- .../isaaclab/sim/schemas/__init__.pyi | 6 + .../isaaclab/isaaclab/sim/schemas/schemas.py | 72 +++++ .../isaaclab/sim/schemas/schemas_cfg.py | 46 ++++ .../test/sim/test_mesh_collision_fragments.py | 247 ++++++++++++++++++ ...vidurv-schema-frag-meshcollision.minor.rst | 9 + .../isaaclab_newton/sim/schemas/__init__.pyi | 4 + .../sim/schemas/schemas_cfg.py | 105 ++++++++ ...vidurv-schema-frag-meshcollision.minor.rst | 11 + .../isaaclab_physx/sim/schemas/__init__.pyi | 10 + .../isaaclab_physx/sim/schemas/schemas_cfg.py | 168 ++++++++++++ 14 files changed, 735 insertions(+), 3 deletions(-) create mode 100644 source/isaaclab/changelog.d/vidurv-schema-frag-meshcollision.minor.rst create mode 100644 source/isaaclab/test/sim/test_mesh_collision_fragments.py create mode 100644 source/isaaclab_newton/changelog.d/vidurv-schema-frag-meshcollision.minor.rst create mode 100644 source/isaaclab_physx/changelog.d/vidurv-schema-frag-meshcollision.minor.rst diff --git a/source/isaaclab/changelog.d/vidurv-schema-frag-meshcollision.minor.rst b/source/isaaclab/changelog.d/vidurv-schema-frag-meshcollision.minor.rst new file mode 100644 index 000000000000..3532b8f68ffe --- /dev/null +++ b/source/isaaclab/changelog.d/vidurv-schema-frag-meshcollision.minor.rst @@ -0,0 +1,20 @@ +Added +^^^^^ + +* Added the mesh-collision schema-fragment API: the + :class:`~isaaclab.sim.schemas.MeshCollisionFragment` marker and + :class:`~isaaclab.sim.schemas.UsdPhysicsMeshCollisionCfg` (carrying the standard + ``physics:approximation`` token via ``UsdPhysics.MeshCollisionAPI``). +* Added :func:`~isaaclab.sim.schemas.apply_mesh_collision_properties`, which applies + ``UsdPhysics.MeshCollisionAPI`` as the implicit anchor, resolves the + ``physics:approximation`` token from whichever cooking fragment is present (validated against + :const:`~isaaclab.sim.schemas.MESH_APPROXIMATION_TOKENS`), and dispatches each fragment via its + ``func``. + +Changed +^^^^^^^ + +* Changed the mesh-converter ``mesh_collision_props`` slot + (:attr:`~isaaclab.sim.converters.MeshConverterCfg.mesh_collision_props`) to also accept a list of + :class:`~isaaclab.sim.schemas.MeshCollisionFragment` fragments. Legacy single cfgs continue to + work through a transition bridge in the converter. diff --git a/source/isaaclab/isaaclab/sim/__init__.pyi b/source/isaaclab/isaaclab/sim/__init__.pyi index e0365d3e759c..6988381491d1 100644 --- a/source/isaaclab/isaaclab/sim/__init__.pyi +++ b/source/isaaclab/isaaclab/sim/__init__.pyi @@ -56,12 +56,15 @@ __all__ = [ "NewtonMeshCollisionPropertiesCfg", "NewtonRigidBodyPropertiesCfg", "NewtonSDFCollisionPropertiesCfg", + "MeshCollisionFragment", "PhysxJointDrivePropertiesCfg", "PhysxRigidBodyPropertiesCfg", "RigidBodyBaseCfg", "RigidBodyFragment", "SchemaFragment", + "UsdPhysicsMeshCollisionCfg", "UsdPhysicsRigidBodyCfg", + "apply_mesh_collision_properties", "apply_namespaced", "apply_rigid_body_properties", "SDFMeshPropertiesCfg", @@ -222,6 +225,7 @@ from .schemas import ( FixedTendonPropertiesCfg, JointDriveBaseCfg, MassPropertiesCfg, + MeshCollisionFragment, MeshCollisionPropertiesCfg, PhysxJointDrivePropertiesCfg, PhysxRigidBodyPropertiesCfg, @@ -232,8 +236,10 @@ from .schemas import ( SpatialTendonPropertiesCfg, TriangleMeshPropertiesCfg, TriangleMeshSimplificationPropertiesCfg, + UsdPhysicsMeshCollisionCfg, UsdPhysicsRigidBodyCfg, activate_contact_sensors, + apply_mesh_collision_properties, apply_namespaced, apply_rigid_body_properties, define_articulation_root_properties, diff --git a/source/isaaclab/isaaclab/sim/converters/mesh_converter.py b/source/isaaclab/isaaclab/sim/converters/mesh_converter.py index f814e4fdb877..aa64e6671ae3 100644 --- a/source/isaaclab/isaaclab/sim/converters/mesh_converter.py +++ b/source/isaaclab/isaaclab/sim/converters/mesh_converter.py @@ -15,6 +15,7 @@ from isaaclab.sim.converters.asset_converter_base import AssetConverterBase from isaaclab.sim.converters.mesh_converter_cfg import MeshConverterCfg from isaaclab.sim.schemas import schemas +from isaaclab.sim.schemas.schemas_cfg import SchemaFragment from isaaclab.sim.utils import delete_prim, export_prim_to_file # import logger @@ -131,9 +132,22 @@ def _convert_asset(self, cfg: MeshConverterCfg): ) # Add collision mesh if cfg.mesh_collision_props is not None: - schemas.define_mesh_collision_properties( - prim_path=child_mesh_prim.GetPath(), cfg=cfg.mesh_collision_props, stage=stage + # Transition bridge: route a fragment (or list of fragments) through the new + # ``apply_mesh_collision_properties`` family writer; otherwise fall back to the + # legacy single-cfg ``define_mesh_collision_properties`` path. + mesh_collision_frags = ( + cfg.mesh_collision_props + if isinstance(cfg.mesh_collision_props, (list, tuple)) + else [cfg.mesh_collision_props] ) + if all(isinstance(f, SchemaFragment) for f in mesh_collision_frags): + schemas.apply_mesh_collision_properties( + prim_path=child_mesh_prim.GetPath(), fragments=mesh_collision_frags, stage=stage + ) + else: + schemas.define_mesh_collision_properties( + prim_path=child_mesh_prim.GetPath(), cfg=cfg.mesh_collision_props, stage=stage + ) # Delete the old Xform and make the new Xform the default prim stage.SetDefaultPrim(xform_prim) # Apply default Xform rotation to mesh -> enable to set rotation and scale diff --git a/source/isaaclab/isaaclab/sim/converters/mesh_converter_cfg.py b/source/isaaclab/isaaclab/sim/converters/mesh_converter_cfg.py index 73ec37e777b6..057c9ecdc1eb 100644 --- a/source/isaaclab/isaaclab/sim/converters/mesh_converter_cfg.py +++ b/source/isaaclab/isaaclab/sim/converters/mesh_converter_cfg.py @@ -32,8 +32,22 @@ class MeshConverterCfg(AssetConverterBaseCfg): Note: If None, then no collision properties will be added. """ - mesh_collision_props: schemas_cfg.MeshCollisionBaseCfg = None + mesh_collision_props: ( + schemas_cfg.MeshCollisionBaseCfg + | schemas_cfg.MeshCollisionFragment + | list[schemas_cfg.MeshCollisionFragment] + | None + ) = None """Mesh approximation properties to apply to all collision meshes in the USD. + + Accepts either a single legacy cfg (e.g. :class:`~isaaclab.sim.schemas.MeshCollisionBaseCfg` or + a ``Physx*PropertiesCfg`` cooking cfg) or a list of + :class:`~isaaclab.sim.schemas.MeshCollisionFragment` fragments (e.g. + ``[UsdPhysicsMeshCollisionCfg(...), PhysxConvexHullCfg(...)]``). When a fragment list is given, + ``UsdPhysics.MeshCollisionAPI`` is applied as the implicit anchor, the ``physics:approximation`` + token is resolved from whichever cooking fragment is present, and each fragment writes its own + namespace. + Note: If None, then no mesh approximation properties will be added. """ diff --git a/source/isaaclab/isaaclab/sim/schemas/__init__.pyi b/source/isaaclab/isaaclab/sim/schemas/__init__.pyi index af153a60fc63..ff630fc881e0 100644 --- a/source/isaaclab/isaaclab/sim/schemas/__init__.pyi +++ b/source/isaaclab/isaaclab/sim/schemas/__init__.pyi @@ -8,6 +8,7 @@ __all__ = [ "PHYSX_MESH_COLLISION_CFGS", "USD_MESH_COLLISION_CFGS", "activate_contact_sensors", + "apply_mesh_collision_properties", "apply_namespaced", "apply_rigid_body_properties", "define_actuator_properties", @@ -35,8 +36,10 @@ __all__ = [ "JointDriveBaseCfg", "MassPropertiesCfg", "MeshCollisionBaseCfg", + "MeshCollisionFragment", "RigidBodyFragment", "SchemaFragment", + "UsdPhysicsMeshCollisionCfg", "UsdPhysicsRigidBodyCfg", "MujocoJointDrivePropertiesCfg", "MujocoRigidBodyPropertiesCfg", @@ -55,6 +58,7 @@ from .schemas import ( PHYSX_MESH_COLLISION_CFGS, USD_MESH_COLLISION_CFGS, activate_contact_sensors, + apply_mesh_collision_properties, apply_namespaced, apply_rigid_body_properties, define_articulation_root_properties, @@ -86,9 +90,11 @@ from .schemas_cfg import ( JointDriveBaseCfg, MassPropertiesCfg, MeshCollisionBaseCfg, + MeshCollisionFragment, RigidBodyBaseCfg, RigidBodyFragment, SchemaFragment, + UsdPhysicsMeshCollisionCfg, UsdPhysicsRigidBodyCfg, ) diff --git a/source/isaaclab/isaaclab/sim/schemas/schemas.py b/source/isaaclab/isaaclab/sim/schemas/schemas.py index 6617543cad61..f09bf18efc17 100644 --- a/source/isaaclab/isaaclab/sim/schemas/schemas.py +++ b/source/isaaclab/isaaclab/sim/schemas/schemas.py @@ -255,6 +255,13 @@ def apply_namespaced(cfg: schemas_cfg.SchemaFragment, prim_path: str, stage: Usd # ``func`` is the only non-USD field; non-scalar values raise in the setter if f.name == "func": continue + # ``mesh_approximation_name`` is not a namespaced attribute: it is the standard + # ``physics:approximation`` token, written by ``apply_mesh_collision_properties`` (the + # family writer) which validates it against ``MESH_APPROXIMATION_TOKENS``. Skip it here + # so a mesh-collision cooking fragment dispatched through this generic applier does not + # author a spurious ``:meshApproximationName`` attribute. + if f.name == "mesh_approximation_name": + continue value = getattr(cfg, f.name) if value is None: continue @@ -468,6 +475,71 @@ def apply_rigid_body_properties( return success +def apply_mesh_collision_properties(prim_path: str, fragments, stage: Usd.Stage | None = None) -> bool: + """Apply a list of mesh-collision fragments to a prim. + + Applies ``UsdPhysics.MeshCollisionAPI`` as the implicit anchor (the carrier of the + ``physics:approximation`` token), resolves and writes that token, then dispatches each + fragment via its :attr:`~isaaclab.sim.schemas.SchemaFragment.func`. Backend cooking fragments + carry backend-specific funcs (the generic :func:`apply_namespaced` applier), so core never + imports a backend. + + .. attention:: + **Approximation-token coupling.** The ``physics:approximation`` token is *not* a plain + namespaced attribute: it is shared state set by whichever cooking fragment is present. + Each fragment carries a :attr:`mesh_approximation_name` whose default encodes the token its + cooking schema implies (e.g. ``"convexHull"`` for :class:`PhysxConvexHullCfg`, ``"sdf"`` + for :class:`PhysxSDFMeshCfg`). This writer scans the fragment list and uses the last + fragment whose :attr:`mesh_approximation_name` is set to a non-``"none"`` value (mirroring + the legacy single-cfg behavior), falling back to ``"none"`` when none is set. The token is + validated against :const:`MESH_APPROXIMATION_TOKENS`; an unknown name raises ``ValueError``. + The :attr:`mesh_approximation_name` field is therefore handled here and explicitly skipped + by :func:`apply_namespaced` so it is never authored as a namespaced attribute. + + Args: + prim_path: The prim path to apply the mesh-collision schemas on. This prim should be a Mesh. + fragments: An iterable of :class:`~isaaclab.sim.schemas.MeshCollisionFragment` instances. + stage: The stage where to find the prim. Defaults to None, in which case the current + stage is used. + + Returns: + True if the properties were successfully set. + + Raises: + ValueError: When the resolved mesh approximation name is not in + :const:`MESH_APPROXIMATION_TOKENS`. + """ + if stage is None: + stage = get_current_stage() + prim = stage.GetPrimAtPath(prim_path) + # apply the standard MeshCollisionAPI anchor (carrier of ``physics:approximation``) + if not UsdPhysics.MeshCollisionAPI(prim): + UsdPhysics.MeshCollisionAPI.Apply(prim) + + # resolve the approximation token shared across the fragment list: the last fragment whose + # ``mesh_approximation_name`` is set to a non-"none" value wins; otherwise "none". + approximation_name = "none" + for cfg in fragments: + name = getattr(cfg, "mesh_approximation_name", None) + if name is not None and name != "none": + approximation_name = name + if approximation_name not in MESH_APPROXIMATION_TOKENS: + raise ValueError( + f"Invalid mesh approximation name: '{approximation_name}'. " + f"Valid options are: {list(MESH_APPROXIMATION_TOKENS.keys())}" + ) + approximation_token = MESH_APPROXIMATION_TOKENS[approximation_name] + safe_set_attribute_on_usd_schema( + UsdPhysics.MeshCollisionAPI(prim), "Approximation", approximation_token, camel_case=False + ) + + # dispatch each fragment via its ``func`` (cooking-schema application + namespaced tuning attrs) + for cfg in fragments: + func = cfg.func if callable(cfg.func) else string_to_callable(cfg.func) + func(cfg, prim_path, stage) + return True + + def define_rigid_body_properties(prim_path: str, cfg: schemas_cfg.RigidBodyBaseCfg, stage: Usd.Stage | None = None): """Apply the rigid body schema on the input prim and set its properties. diff --git a/source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py b/source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py index ac5d8b22c020..2831cadbc3e8 100644 --- a/source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py +++ b/source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py @@ -176,6 +176,52 @@ class UsdPhysicsRigidBodyCfg(RigidBodyFragment): """ +@configclass +class MeshCollisionFragment(SchemaFragment): + """Marker base for mesh-collision fragments; types the ``mesh_collision_props`` slot. + + A mesh-collision concept is split across one *core* fragment carrying the standard + ``physics:approximation`` token (:class:`UsdPhysicsMeshCollisionCfg`) and one cooking + fragment per backend cooking schema (PhysX convex hull / decomposition / triangle mesh / + SDF, Newton mesh / SDF). Whichever cooking fragment is present implies the approximation + token written to ``physics:approximation`` -- see + :func:`~isaaclab.sim.schemas.apply_mesh_collision_properties`. + """ + + pass + + +@configclass +class UsdPhysicsMeshCollisionCfg(MeshCollisionFragment): + """``physics:approximation`` mesh-collision token from `UsdPhysics.MeshCollisionAPI`_. + + Carries the standard mesh-collision approximation token (:attr:`mesh_approximation_name` + written to ``physics:approximation``). The ``UsdPhysics.MeshCollisionAPI`` schema is applied + as the implicit anchor by the mesh-collision family writer + (:func:`~isaaclab.sim.schemas.apply_mesh_collision_properties`), so this fragment owns no + applied schema of its own. + + .. note:: + The ``physics:approximation`` attribute is a ``TfToken`` validated against + :const:`~isaaclab.sim.schemas.MESH_APPROXIMATION_TOKENS`; the family writer (not the generic + :func:`~isaaclab.sim.schemas.apply_namespaced` applier) handles the token write, so this + fragment overrides nothing but the namespace metadata. When a PhysX/Newton cooking fragment + is present alongside this one, its default :attr:`mesh_approximation_name` sets the token. + + .. _UsdPhysics.MeshCollisionAPI: https://openusd.org/release/api/class_usd_physics_mesh_collision_a_p_i.html + """ + + _usd_namespace: ClassVar[str | None] = "physics" + _usd_applied_schema: ClassVar[str | None] = None # MeshCollisionAPI applied by the family anchor + + mesh_approximation_name: str = "none" + """Name of mesh collision approximation method. Default: "none". + + Writes the ``physics:approximation`` token via :class:`UsdPhysics.MeshCollisionAPI`. + Refer to :const:`~isaaclab.sim.schemas.MESH_APPROXIMATION_TOKENS` for available options. + """ + + @configclass class ArticulationRootBaseCfg: """Solver-common properties to apply to the root of an articulation. diff --git a/source/isaaclab/test/sim/test_mesh_collision_fragments.py b/source/isaaclab/test/sim/test_mesh_collision_fragments.py new file mode 100644 index 000000000000..47b25d119f30 --- /dev/null +++ b/source/isaaclab/test/sim/test_mesh_collision_fragments.py @@ -0,0 +1,247 @@ +# 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 + +"""Launch Isaac Sim Simulator first.""" + +from isaaclab.app import AppLauncher + +# launch omniverse app +simulation_app = AppLauncher(headless=True).app + +"""Rest everything follows.""" + +from pxr import UsdGeom, UsdPhysics + +import isaaclab.sim as sim_utils +from isaaclab.sim import SimulationCfg, SimulationContext + + +def _make_xform(stage, path="/World/Mesh"): + UsdGeom.Xform.Define(stage, path) + return stage.GetPrimAtPath(path) + + +# ------------------------------------------------------------------------------------- +# Fragment metadata + marker hierarchy +# ------------------------------------------------------------------------------------- + + +def test_mesh_collision_fragment_metadata_defaults(): + from isaaclab.sim.schemas import MeshCollisionFragment, SchemaFragment, UsdPhysicsMeshCollisionCfg + + cfg = UsdPhysicsMeshCollisionCfg(mesh_approximation_name="convexHull") + assert isinstance(cfg, MeshCollisionFragment) and isinstance(cfg, SchemaFragment) + assert type(cfg)._usd_namespace == "physics" + assert type(cfg)._usd_applied_schema is None # anchor applies MeshCollisionAPI, not the fragment + assert cfg.func == "isaaclab.sim.schemas:apply_namespaced" + assert cfg.mesh_approximation_name == "convexHull" + + +# ------------------------------------------------------------------------------------- +# Core USD fragment: physics:approximation token via apply_mesh_collision_properties +# ------------------------------------------------------------------------------------- + + +def test_usd_mesh_collision_fragment_writes_approximation_token(): + from isaaclab.sim.schemas import UsdPhysicsMeshCollisionCfg, apply_mesh_collision_properties + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + _make_xform(stage, "/World/M0") + apply_mesh_collision_properties( + "/World/M0", [UsdPhysicsMeshCollisionCfg(mesh_approximation_name="boundingCube")], stage + ) + prim = stage.GetPrimAtPath("/World/M0") + assert bool(UsdPhysics.MeshCollisionAPI(prim)) + assert prim.GetAttribute("physics:approximation").Get() == "boundingCube" + + +# ------------------------------------------------------------------------------------- +# PhysX cooking fragments (isaaclab_physx): each writes its own physx*Collision namespace +# ------------------------------------------------------------------------------------- + + +def test_physx_convex_hull_fragment_writes_namespace(): + from isaaclab_physx.sim.schemas import PhysxConvexHullCfg + + from isaaclab.sim.schemas import apply_namespaced + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + prim = _make_xform(stage, "/World/M1") + UsdPhysics.MeshCollisionAPI.Apply(prim) + apply_namespaced(PhysxConvexHullCfg(hull_vertex_limit=32, min_thickness=0.002), "/World/M1", stage) + assert prim.GetAttribute("physxConvexHullCollision:hullVertexLimit").Get() == 32 + assert abs(prim.GetAttribute("physxConvexHullCollision:minThickness").Get() - 0.002) < 1e-6 + # ``mesh_approximation_name`` must NOT be authored as a namespaced attr by the generic applier. + assert not prim.HasAttribute("physxConvexHullCollision:meshApproximationName") + + +def test_physx_convex_decomposition_fragment_writes_namespace(): + from isaaclab_physx.sim.schemas import PhysxConvexDecompositionCfg + + from isaaclab.sim.schemas import apply_namespaced + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + prim = _make_xform(stage, "/World/M2") + UsdPhysics.MeshCollisionAPI.Apply(prim) + apply_namespaced(PhysxConvexDecompositionCfg(max_convex_hulls=8, shrink_wrap=True), "/World/M2", stage) + assert prim.GetAttribute("physxConvexDecompositionCollision:maxConvexHulls").Get() == 8 + assert prim.GetAttribute("physxConvexDecompositionCollision:shrinkWrap").Get() is True + + +def test_physx_triangle_mesh_fragment_writes_namespace(): + from isaaclab_physx.sim.schemas import PhysxTriangleMeshCfg + + from isaaclab.sim.schemas import apply_namespaced + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + prim = _make_xform(stage, "/World/M3") + UsdPhysics.MeshCollisionAPI.Apply(prim) + apply_namespaced(PhysxTriangleMeshCfg(weld_tolerance=0.01), "/World/M3", stage) + assert abs(prim.GetAttribute("physxTriangleMeshCollision:weldTolerance").Get() - 0.01) < 1e-6 + + +def test_physx_triangle_mesh_simplification_fragment_writes_namespace(): + from isaaclab_physx.sim.schemas import PhysxTriangleMeshSimplificationCfg + + from isaaclab.sim.schemas import apply_namespaced + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + prim = _make_xform(stage, "/World/M4") + UsdPhysics.MeshCollisionAPI.Apply(prim) + apply_namespaced(PhysxTriangleMeshSimplificationCfg(simplification_metric=0.7), "/World/M4", stage) + ns = "physxTriangleMeshSimplificationCollision" + assert abs(prim.GetAttribute(f"{ns}:simplificationMetric").Get() - 0.7) < 1e-6 + + +def test_physx_sdf_mesh_fragment_writes_namespace(): + from isaaclab_physx.sim.schemas import PhysxSDFMeshCfg + + from isaaclab.sim.schemas import apply_namespaced + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + prim = _make_xform(stage, "/World/M5") + UsdPhysics.MeshCollisionAPI.Apply(prim) + apply_namespaced(PhysxSDFMeshCfg(sdf_resolution=128, sdf_margin=0.02), "/World/M5", stage) + assert prim.GetAttribute("physxSDFMeshCollision:sdfResolution").Get() == 128 + assert abs(prim.GetAttribute("physxSDFMeshCollision:sdfMargin").Get() - 0.02) < 1e-6 + + +# ------------------------------------------------------------------------------------- +# Newton cooking fragments (isaaclab_newton): newton namespace + applied schema +# ------------------------------------------------------------------------------------- + + +def test_newton_mesh_collision_fragment_writes_namespace(): + from isaaclab_newton.sim.schemas import NewtonMeshCollisionCfg + + from isaaclab.sim.schemas import apply_namespaced + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + prim = _make_xform(stage, "/World/M6") + UsdPhysics.MeshCollisionAPI.Apply(prim) + apply_namespaced(NewtonMeshCollisionCfg(max_hull_vertices=24), "/World/M6", stage) + assert prim.GetAttribute("newton:maxHullVertices").Get() == 24 + assert "NewtonMeshCollisionAPI" in prim.GetAppliedSchemas() + + +def test_newton_sdf_collision_fragment_writes_namespace(): + from isaaclab_newton.sim.schemas import NewtonSDFCollisionCfg + + from isaaclab.sim.schemas import apply_namespaced + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + prim = _make_xform(stage, "/World/M7") + UsdPhysics.MeshCollisionAPI.Apply(prim) + apply_namespaced(NewtonSDFCollisionCfg(sdf_max_resolution=64, hydroelastic_enabled=True), "/World/M7", stage) + assert prim.GetAttribute("newton:sdfMaxResolution").Get() == 64 + assert prim.GetAttribute("newton:hydroelasticEnabled").Get() is True + assert "NewtonSDFCollisionAPI" in prim.GetAppliedSchemas() + + +# ------------------------------------------------------------------------------------- +# Composition through apply_mesh_collision_properties: token coupling + multi-namespace +# ------------------------------------------------------------------------------------- + + +def test_apply_mesh_collision_properties_composes_namespaces(): + from isaaclab_newton.sim.schemas import NewtonMeshCollisionCfg + from isaaclab_physx.sim.schemas import PhysxConvexHullCfg + + from isaaclab.sim.schemas import UsdPhysicsMeshCollisionCfg, apply_mesh_collision_properties + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + _make_xform(stage, "/World/M8") + apply_mesh_collision_properties( + "/World/M8", + [ + UsdPhysicsMeshCollisionCfg(), + PhysxConvexHullCfg(hull_vertex_limit=48), + NewtonMeshCollisionCfg(max_hull_vertices=48), + ], + stage, + ) + prim = stage.GetPrimAtPath("/World/M8") + assert bool(UsdPhysics.MeshCollisionAPI(prim)) # implicit anchor applied + # token coupling: the convex-hull cooking fragment sets ``physics:approximation`` + assert prim.GetAttribute("physics:approximation").Get() == "convexHull" + assert prim.GetAttribute("physxConvexHullCollision:hullVertexLimit").Get() == 48 + assert prim.GetAttribute("newton:maxHullVertices").Get() == 48 + + +def test_apply_mesh_collision_properties_rejects_invalid_token(): + import pytest + + from isaaclab.sim.schemas import UsdPhysicsMeshCollisionCfg, apply_mesh_collision_properties + + sim_utils.create_new_stage() + SimulationContext(SimulationCfg(dt=0.01)) + stage = sim_utils.get_current_stage() + _make_xform(stage, "/World/M9") + with pytest.raises(ValueError): + apply_mesh_collision_properties( + "/World/M9", [UsdPhysicsMeshCollisionCfg(mesh_approximation_name="notAToken")], stage + ) + + +# ------------------------------------------------------------------------------------- +# Public imports +# ------------------------------------------------------------------------------------- + + +def test_public_imports(): + from isaaclab_newton.sim.schemas import NewtonMeshCollisionCfg, NewtonSDFCollisionCfg # noqa: F401 + from isaaclab_physx.sim.schemas import ( # noqa: F401 + PhysxConvexDecompositionCfg, + PhysxConvexHullCfg, + PhysxSDFMeshCfg, + PhysxTriangleMeshCfg, + PhysxTriangleMeshSimplificationCfg, + ) + + from isaaclab.sim.schemas import ( # noqa: F401 + MeshCollisionFragment, + SchemaFragment, + UsdPhysicsMeshCollisionCfg, + apply_mesh_collision_properties, + apply_namespaced, + ) diff --git a/source/isaaclab_newton/changelog.d/vidurv-schema-frag-meshcollision.minor.rst b/source/isaaclab_newton/changelog.d/vidurv-schema-frag-meshcollision.minor.rst new file mode 100644 index 000000000000..4246cfd9a1f5 --- /dev/null +++ b/source/isaaclab_newton/changelog.d/vidurv-schema-frag-meshcollision.minor.rst @@ -0,0 +1,9 @@ +Added +^^^^^ + +* Added the Newton mesh-collision cooking fragments: + :class:`~isaaclab_newton.sim.schemas.NewtonMeshCollisionCfg` (``newton:maxHullVertices`` via + ``NewtonMeshCollisionAPI``) and :class:`~isaaclab_newton.sim.schemas.NewtonSDFCollisionCfg` + (Newton SDF generation and hydroelastic-contact attributes via ``NewtonSDFCollisionAPI``). Each is + a single-namespace :class:`~isaaclab.sim.schemas.MeshCollisionFragment` dispatched via + :func:`~isaaclab.sim.schemas.apply_mesh_collision_properties`. diff --git a/source/isaaclab_newton/isaaclab_newton/sim/schemas/__init__.pyi b/source/isaaclab_newton/isaaclab_newton/sim/schemas/__init__.pyi index 89ca2d068afb..c82fcf052648 100644 --- a/source/isaaclab_newton/isaaclab_newton/sim/schemas/__init__.pyi +++ b/source/isaaclab_newton/isaaclab_newton/sim/schemas/__init__.pyi @@ -12,8 +12,10 @@ __all__ = [ "NewtonDeformableBodyPropertiesCfg", "NewtonJointDrivePropertiesCfg", "NewtonMaterialPropertiesCfg", + "NewtonMeshCollisionCfg", "NewtonMeshCollisionPropertiesCfg", "NewtonRigidBodyPropertiesCfg", + "NewtonSDFCollisionCfg", "NewtonSDFCollisionPropertiesCfg", ] @@ -26,7 +28,9 @@ from .schemas_cfg import ( NewtonDeformableBodyPropertiesCfg, NewtonJointDrivePropertiesCfg, NewtonMaterialPropertiesCfg, + NewtonMeshCollisionCfg, NewtonMeshCollisionPropertiesCfg, NewtonRigidBodyPropertiesCfg, + NewtonSDFCollisionCfg, NewtonSDFCollisionPropertiesCfg, ) diff --git a/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py b/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py index 4c99379e1ad8..82f675558802 100644 --- a/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py +++ b/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py @@ -13,6 +13,7 @@ DeformableBodyPropertiesBaseCfg, JointDriveBaseCfg, MeshCollisionBaseCfg, + MeshCollisionFragment, RigidBodyBaseCfg, RigidBodyFragment, ) @@ -289,6 +290,110 @@ class NewtonSDFCollisionPropertiesCfg(NewtonCollisionPropertiesCfg): """ +# ------------------------------------------------------------------------------------- +# Mesh-collision cooking fragments (single-namespace; Newton cooking add-on schemas). +# +# Each fragment owns the ``newton`` namespace + its applied schema and is dispatched through the +# generic ``apply_namespaced`` applier by the family writer +# ``isaaclab.sim.schemas.apply_mesh_collision_properties``. They author no ``mesh_approximation_name`` +# of their own (the ``physics:approximation`` token is set by the PhysX/USD fragment present in the +# same list), so they only tune Newton-native cooking attributes. +# ------------------------------------------------------------------------------------- + + +@configclass +class NewtonMeshCollisionCfg(MeshCollisionFragment): + """``newton:maxHullVertices`` mesh-cooking attribute from ``NewtonMeshCollisionAPI``. + + A single-namespace fragment (see :class:`~isaaclab.sim.schemas.SchemaFragment`) carrying + Newton's convex-hull vertex limit. Dispatched alongside the USD/PhysX mesh-collision fragments + via :func:`~isaaclab.sim.schemas.apply_mesh_collision_properties`. + + .. note:: + If the values are None, they are not modified. + """ + + _usd_namespace: ClassVar[str | None] = "newton" + _usd_applied_schema: ClassVar[str | None] = "NewtonMeshCollisionAPI" + + max_hull_vertices: int | None = None + """Maximum vertices in the convex hull approximation [dimensionless]. + + Only relevant when ``physics:approximation = "convexHull"``. + Written to ``newton:maxHullVertices`` via ``NewtonMeshCollisionAPI``. + Set to ``-1`` to use as many vertices as needed for a perfect hull. + """ + + +@configclass +class NewtonSDFCollisionCfg(MeshCollisionFragment): + """``newton:*`` SDF and hydroelastic mesh-cooking attributes from ``NewtonSDFCollisionAPI``. + + A single-namespace fragment carrying Newton SDF generation and hydroelastic-contact attributes + consumed by Newton's USD importer. Mirrors the legacy + :class:`NewtonSDFCollisionPropertiesCfg`. Dispatched alongside the USD/PhysX mesh-collision + fragments via :func:`~isaaclab.sim.schemas.apply_mesh_collision_properties`. + + .. note:: + If the values are None, they are not modified. + """ + + _usd_namespace: ClassVar[str | None] = "newton" + _usd_applied_schema: ClassVar[str | None] = "NewtonSDFCollisionAPI" + + sdf_max_resolution: int | None = None + """Maximum SDF grid dimension [dimensionless]. + + Newton requires this value to be divisible by 8. If :attr:`sdf_target_voxel_size` is also + authored, Newton uses the target voxel size and ignores this resolution. + Written to ``newton:sdfMaxResolution`` via ``NewtonSDFCollisionAPI``. + """ + + sdf_narrow_band_inner: float | None = None + """Inner narrow-band distance for SDF generation [m]. + + Written to ``newton:sdfNarrowBandInner`` via ``NewtonSDFCollisionAPI``. + """ + + sdf_narrow_band_outer: float | None = None + """Outer narrow-band distance for SDF generation [m]. + + Written to ``newton:sdfNarrowBandOuter`` via ``NewtonSDFCollisionAPI``. + """ + + sdf_target_voxel_size: float | None = None + """Target SDF voxel size [m]. + + Takes precedence over :attr:`sdf_max_resolution` in Newton's USD importer. + Written to ``newton:sdfTargetVoxelSize`` via ``NewtonSDFCollisionAPI``. + """ + + sdf_texture_format: Literal["uint8", "uint16", "float32"] | None = None + """Subgrid texture storage format for generated SDFs. + + Written to ``newton:sdfTextureFormat`` via ``NewtonSDFCollisionAPI``. + """ + + sdf_padding: float | None = None + """SDF AABB padding [m]. + + Written to ``newton:sdfPadding`` via ``NewtonSDFCollisionAPI``. + """ + + hydroelastic_enabled: bool | None = None + """Whether Newton should use SDF-based hydroelastic contacts for this shape. + + Both participating collision shapes must enable hydroelastic contacts for Newton to use this + path. Written to ``newton:hydroelasticEnabled`` via ``NewtonSDFCollisionAPI``. + """ + + hydroelastic_stiffness: float | None = None + """Hydroelastic contact stiffness. + + Written to ``newton:hydroelasticStiffness`` via ``NewtonSDFCollisionAPI``. + """ + + @configclass class NewtonMaterialPropertiesCfg(RigidBodyMaterialBaseCfg): """Newton-specific rigid body material properties. diff --git a/source/isaaclab_physx/changelog.d/vidurv-schema-frag-meshcollision.minor.rst b/source/isaaclab_physx/changelog.d/vidurv-schema-frag-meshcollision.minor.rst new file mode 100644 index 000000000000..8ced9b2e5d2a --- /dev/null +++ b/source/isaaclab_physx/changelog.d/vidurv-schema-frag-meshcollision.minor.rst @@ -0,0 +1,11 @@ +Added +^^^^^ + +* Added the PhysX mesh-collision cooking fragments: + :class:`~isaaclab_physx.sim.schemas.PhysxConvexHullCfg`, + :class:`~isaaclab_physx.sim.schemas.PhysxConvexDecompositionCfg`, + :class:`~isaaclab_physx.sim.schemas.PhysxTriangleMeshCfg`, + :class:`~isaaclab_physx.sim.schemas.PhysxTriangleMeshSimplificationCfg`, and + :class:`~isaaclab_physx.sim.schemas.PhysxSDFMeshCfg`. Each is a single-namespace + :class:`~isaaclab.sim.schemas.MeshCollisionFragment` owning one ``physx*Collision:*`` namespace and + applied schema, dispatched via :func:`~isaaclab.sim.schemas.apply_mesh_collision_properties`. diff --git a/source/isaaclab_physx/isaaclab_physx/sim/schemas/__init__.pyi b/source/isaaclab_physx/isaaclab_physx/sim/schemas/__init__.pyi index 10d2502ddf2b..e2d139e919b2 100644 --- a/source/isaaclab_physx/isaaclab_physx/sim/schemas/__init__.pyi +++ b/source/isaaclab_physx/isaaclab_physx/sim/schemas/__init__.pyi @@ -17,7 +17,9 @@ __all__ = [ "OmniPhysicsDeformableBodyPropertiesCfg", "PhysxArticulationRootPropertiesCfg", "PhysxCollisionPropertiesCfg", + "PhysxConvexDecompositionCfg", "PhysxConvexDecompositionPropertiesCfg", + "PhysxConvexHullCfg", "PhysxConvexHullPropertiesCfg", "PhysxDeformableBodyPropertiesCfg", "PhysxDeformableCollisionPropertiesCfg", @@ -25,9 +27,12 @@ __all__ = [ "PhysxJointDrivePropertiesCfg", "PhysxRigidBodyCfg", "PhysxRigidBodyPropertiesCfg", + "PhysxSDFMeshCfg", "PhysxSDFMeshPropertiesCfg", "PhysxSpatialTendonPropertiesCfg", + "PhysxTriangleMeshCfg", "PhysxTriangleMeshPropertiesCfg", + "PhysxTriangleMeshSimplificationCfg", "PhysxTriangleMeshSimplificationPropertiesCfg", "RigidBodyPropertiesCfg", "SDFMeshPropertiesCfg", @@ -52,7 +57,9 @@ from .schemas_cfg import ( OmniPhysicsDeformableBodyPropertiesCfg, PhysxArticulationRootPropertiesCfg, PhysxCollisionPropertiesCfg, + PhysxConvexDecompositionCfg, PhysxConvexDecompositionPropertiesCfg, + PhysxConvexHullCfg, PhysxConvexHullPropertiesCfg, PhysxDeformableBodyPropertiesCfg, PhysxDeformableCollisionPropertiesCfg, @@ -60,9 +67,12 @@ from .schemas_cfg import ( PhysxJointDrivePropertiesCfg, PhysxRigidBodyCfg, PhysxRigidBodyPropertiesCfg, + PhysxSDFMeshCfg, PhysxSDFMeshPropertiesCfg, PhysxSpatialTendonPropertiesCfg, + PhysxTriangleMeshCfg, PhysxTriangleMeshPropertiesCfg, + PhysxTriangleMeshSimplificationCfg, PhysxTriangleMeshSimplificationPropertiesCfg, RigidBodyPropertiesCfg, SDFMeshPropertiesCfg, diff --git a/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas_cfg.py b/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas_cfg.py index 18a5de023f90..b07b4a67a0dd 100644 --- a/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas_cfg.py +++ b/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas_cfg.py @@ -14,6 +14,7 @@ DeformableBodyPropertiesBaseCfg, JointDriveBaseCfg, MeshCollisionBaseCfg, + MeshCollisionFragment, RigidBodyBaseCfg, RigidBodyFragment, ) @@ -535,6 +536,173 @@ def __post_init__(self): super().__post_init__() +# ------------------------------------------------------------------------------------- +# Mesh-collision cooking fragments (single-namespace; PhysX cooking add-on schemas). +# +# Each fragment owns one ``physx*Collision:*`` namespace + applied schema, and carries a +# ``mesh_approximation_name`` whose default encodes the ``physics:approximation`` token its +# cooking schema implies. The token is written by the family writer +# ``isaaclab.sim.schemas.apply_mesh_collision_properties`` (which scans the fragment list and +# validates the name against ``MESH_APPROXIMATION_TOKENS``); the tuning attributes are written by +# the generic ``apply_namespaced`` applier. +# ------------------------------------------------------------------------------------- + + +@configclass +class PhysxConvexHullCfg(MeshCollisionFragment): + """``physxConvexHullCollision:*`` mesh-cooking attributes from `PhysxConvexHullCollisionAPI`_. + + A single-namespace fragment (see :class:`~isaaclab.sim.schemas.SchemaFragment`) for the PhysX + convex-hull cooking schema. The ``convexHull`` token is written to ``physics:approximation`` by + :func:`~isaaclab.sim.schemas.apply_mesh_collision_properties`. + + .. _PhysxConvexHullCollisionAPI: https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/latest/class_physx_schema_physx_convex_hull_collision_a_p_i.html + """ + + _usd_namespace: ClassVar[str | None] = "physxConvexHullCollision" + _usd_applied_schema: ClassVar[str | None] = "PhysxConvexHullCollisionAPI" + + mesh_approximation_name: str = "convexHull" + """Name of mesh collision approximation method. Default: "convexHull".""" + + hull_vertex_limit: int | None = None + """Convex hull vertex limit used for convex hull cooking [dimensionless]. Defaults to 64.""" + + min_thickness: float | None = None + """Convex hull min thickness [m]. Range: [0, inf). Default value is 0.001.""" + + +@configclass +class PhysxConvexDecompositionCfg(MeshCollisionFragment): + """``physxConvexDecompositionCollision:*`` mesh-cooking attributes from `PhysxConvexDecompositionCollisionAPI`_. + + A single-namespace fragment for the PhysX convex-decomposition cooking schema. The + ``convexDecomposition`` token is written to ``physics:approximation`` by + :func:`~isaaclab.sim.schemas.apply_mesh_collision_properties`. + + .. _PhysxConvexDecompositionCollisionAPI: https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/latest/class_physx_schema_physx_convex_decomposition_collision_a_p_i.html + """ + + _usd_namespace: ClassVar[str | None] = "physxConvexDecompositionCollision" + _usd_applied_schema: ClassVar[str | None] = "PhysxConvexDecompositionCollisionAPI" + + mesh_approximation_name: str = "convexDecomposition" + """Name of mesh collision approximation method. Default: "convexDecomposition".""" + + hull_vertex_limit: int | None = None + """Convex hull vertex limit used for convex hull cooking [dimensionless]. Defaults to 64.""" + + max_convex_hulls: int | None = None + """Maximum of convex hulls created during convex decomposition [dimensionless]. Default value is 32.""" + + min_thickness: float | None = None + """Convex hull min thickness [m]. Range: [0, inf). Default value is 0.001.""" + + voxel_resolution: int | None = None + """Voxel resolution used for convex decomposition [dimensionless]. Defaults to 500,000 voxels.""" + + error_percentage: float | None = None + """Convex decomposition error percentage parameter [%]. Defaults to 10 percent.""" + + shrink_wrap: bool | None = None + """Attempts to adjust the convex hull points so that they are projected onto the surface of the + original graphics mesh. Defaults to False. + """ + + +@configclass +class PhysxTriangleMeshCfg(MeshCollisionFragment): + """``physxTriangleMeshCollision:*`` mesh-cooking attributes from `PhysxTriangleMeshCollisionAPI`_. + + A single-namespace fragment for the PhysX triangle-mesh cooking schema (PhysX-only colliders). + + .. _PhysxTriangleMeshCollisionAPI: https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/latest/class_physx_schema_physx_triangle_mesh_collision_a_p_i.html + """ + + _usd_namespace: ClassVar[str | None] = "physxTriangleMeshCollision" + _usd_applied_schema: ClassVar[str | None] = "PhysxTriangleMeshCollisionAPI" + + mesh_approximation_name: str = "none" + """Name of mesh collision approximation method. Default: "none" (uses triangle mesh).""" + + weld_tolerance: float | None = None + """Mesh weld tolerance controlling the distance at which vertices are welded [m]. + + Default ``-inf`` autocomputes the welding tolerance from the mesh size; ``0`` disables welding. + Range: [0, inf). + """ + + +@configclass +class PhysxTriangleMeshSimplificationCfg(MeshCollisionFragment): + """``physxTriangleMeshSimplificationCollision:*`` attributes from `PhysxTriangleMeshSimplificationCollisionAPI`_. + + A single-namespace fragment for the PhysX triangle-mesh-simplification cooking schema. The + ``meshSimplification`` token is written to ``physics:approximation`` by + :func:`~isaaclab.sim.schemas.apply_mesh_collision_properties`. + + .. _PhysxTriangleMeshSimplificationCollisionAPI: https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/latest/class_physx_schema_physx_triangle_mesh_simplification_collision_a_p_i.html + """ + + _usd_namespace: ClassVar[str | None] = "physxTriangleMeshSimplificationCollision" + _usd_applied_schema: ClassVar[str | None] = "PhysxTriangleMeshSimplificationCollisionAPI" + + mesh_approximation_name: str = "meshSimplification" + """Name of mesh collision approximation method. Default: "meshSimplification".""" + + simplification_metric: float | None = None + """Mesh simplification accuracy [dimensionless]. Defaults to 0.55.""" + + weld_tolerance: float | None = None + """Mesh weld tolerance controlling the distance at which vertices are welded [m]. + + Default ``-inf`` autocomputes the welding tolerance from the mesh size; ``0`` disables welding. + Range: [0, inf). + """ + + +@configclass +class PhysxSDFMeshCfg(MeshCollisionFragment): + """``physxSDFMeshCollision:*`` mesh-cooking attributes from `PhysxSDFMeshCollisionAPI`_. + + A single-namespace fragment for the PhysX signed-distance-field cooking schema (PhysX-only + colliders). The ``sdf`` token is written to ``physics:approximation`` by + :func:`~isaaclab.sim.schemas.apply_mesh_collision_properties`. + + .. _PhysxSDFMeshCollisionAPI: https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/latest/class_physx_schema_physx_s_d_f_mesh_collision_a_p_i.html + """ + + _usd_namespace: ClassVar[str | None] = "physxSDFMeshCollision" + _usd_applied_schema: ClassVar[str | None] = "PhysxSDFMeshCollisionAPI" + + mesh_approximation_name: str = "sdf" + """Name of mesh collision approximation method. Default: "sdf".""" + + sdf_margin: float | None = None + """Margin to increase the size of the SDF relative to the mesh bounding-box diagonal [dimensionless]. + + Scale-independent (fraction of the bounding-box diagonal). Default value is 0.01. Range: [0, inf). + """ + + sdf_narrow_band_thickness: float | None = None + """Size of the narrow band around the mesh surface with high-resolution SDF samples [dimensionless]. + + Scale-independent (fraction of the bounding-box diagonal). Default value is 0.01. Range: [0, 1]. + """ + + sdf_resolution: int | None = None + """Uniform SDF sampling resolution (largest AABB extent divided by this value) [dimensionless]. + + Default value is 256. Range: (1, inf). + """ + + sdf_subgrid_resolution: int | None = None + """Subgrid resolution enabling SDF sparsity; ``0`` selects a dense SDF [dimensionless]. + + Default value is 6. Range: [0, inf). + """ + + @configclass class PhysxConvexHullPropertiesCfg(MeshCollisionBaseCfg): """PhysX convex-hull cooking properties for a mesh collider. From ffdde563dded88d888a8ce9fc22dc0c099827891 Mon Sep 17 00:00:00 2001 From: Vidur Vij Date: Thu, 4 Jun 2026 20:14:08 -0700 Subject: [PATCH 7/9] fix(schemas): NewtonSDFCollisionCfg applies no schema (NewtonSDFCollisionAPI unregistered) --- source/isaaclab/test/sim/test_mesh_collision_fragments.py | 3 ++- .../isaaclab_newton/sim/schemas/schemas_cfg.py | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/source/isaaclab/test/sim/test_mesh_collision_fragments.py b/source/isaaclab/test/sim/test_mesh_collision_fragments.py index 47b25d119f30..3586dfd1ee3b 100644 --- a/source/isaaclab/test/sim/test_mesh_collision_fragments.py +++ b/source/isaaclab/test/sim/test_mesh_collision_fragments.py @@ -173,7 +173,8 @@ def test_newton_sdf_collision_fragment_writes_namespace(): apply_namespaced(NewtonSDFCollisionCfg(sdf_max_resolution=64, hydroelastic_enabled=True), "/World/M7", stage) assert prim.GetAttribute("newton:sdfMaxResolution").Get() == 64 assert prim.GetAttribute("newton:hydroelasticEnabled").Get() is True - assert "NewtonSDFCollisionAPI" in prim.GetAppliedSchemas() + # ``NewtonSDFCollisionAPI`` is not a registered applied API schema in the current Newton + # build, so the fragment writes the ``newton:*`` attributes without applying a schema. # ------------------------------------------------------------------------------------- diff --git a/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py b/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py index 82f675558802..dad7fd6652cd 100644 --- a/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py +++ b/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py @@ -339,7 +339,11 @@ class NewtonSDFCollisionCfg(MeshCollisionFragment): """ _usd_namespace: ClassVar[str | None] = "newton" - _usd_applied_schema: ClassVar[str | None] = "NewtonSDFCollisionAPI" + # ``NewtonSDFCollisionAPI`` is not a registered applied API schema in the current Newton + # build (verified via ``Usd.SchemaRegistry``), so no schema is applied; the ``newton:*`` + # attributes are authored directly and consumed by Newton's USD importer. Switch this to + # ``"NewtonSDFCollisionAPI"`` once that schema ships. + _usd_applied_schema: ClassVar[str | None] = None sdf_max_resolution: int | None = None """Maximum SDF grid dimension [dimensionless]. From 76ecbd558ede9ddc616327d9749f347b033ed6c1 Mon Sep 17 00:00:00 2001 From: Vidur Vij Date: Thu, 4 Jun 2026 20:32:54 -0700 Subject: [PATCH 8/9] fix(schemas): keep NewtonSDFCollisionAPI authored; assert via apiSchemas listOp --- .../test/sim/test_mesh_collision_fragments.py | 23 +++++++++++++++++-- .../sim/schemas/schemas_cfg.py | 10 ++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/source/isaaclab/test/sim/test_mesh_collision_fragments.py b/source/isaaclab/test/sim/test_mesh_collision_fragments.py index 3586dfd1ee3b..2359c8d28143 100644 --- a/source/isaaclab/test/sim/test_mesh_collision_fragments.py +++ b/source/isaaclab/test/sim/test_mesh_collision_fragments.py @@ -23,6 +23,23 @@ def _make_xform(stage, path="/World/Mesh"): return stage.GetPrimAtPath(path) +def _has_authored_api_schema(prim, schema_name: str) -> bool: + """Return whether a schema name is applied or authored in ``apiSchemas`` metadata. + + A schema that is authored via ``AddAppliedSchema`` but not registered in the current build + appears in the ``apiSchemas`` listOp yet not in the composed ``GetAppliedSchemas()``. + """ + if schema_name in prim.GetAppliedSchemas(): + return True + api_schemas = prim.GetMetadata("apiSchemas") + if api_schemas is None: + return False + return any( + schema_name in getattr(api_schemas, item_list) + for item_list in ("explicitItems", "prependedItems", "appendedItems", "addedItems") + ) + + # ------------------------------------------------------------------------------------- # Fragment metadata + marker hierarchy # ------------------------------------------------------------------------------------- @@ -173,8 +190,10 @@ def test_newton_sdf_collision_fragment_writes_namespace(): apply_namespaced(NewtonSDFCollisionCfg(sdf_max_resolution=64, hydroelastic_enabled=True), "/World/M7", stage) assert prim.GetAttribute("newton:sdfMaxResolution").Get() == 64 assert prim.GetAttribute("newton:hydroelasticEnabled").Get() is True - # ``NewtonSDFCollisionAPI`` is not a registered applied API schema in the current Newton - # build, so the fragment writes the ``newton:*`` attributes without applying a schema. + # ``NewtonSDFCollisionAPI`` is authored into the ``apiSchemas`` listOp (like the legacy cfg) but + # is not a registered schema in this Newton build, so it is absent from the composed + # ``GetAppliedSchemas()``. Assert the authored token, matching the legacy Newton test. + assert _has_authored_api_schema(prim, "NewtonSDFCollisionAPI") # ------------------------------------------------------------------------------------- diff --git a/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py b/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py index dad7fd6652cd..19339b58997e 100644 --- a/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py +++ b/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py @@ -339,11 +339,11 @@ class NewtonSDFCollisionCfg(MeshCollisionFragment): """ _usd_namespace: ClassVar[str | None] = "newton" - # ``NewtonSDFCollisionAPI`` is not a registered applied API schema in the current Newton - # build (verified via ``Usd.SchemaRegistry``), so no schema is applied; the ``newton:*`` - # attributes are authored directly and consumed by Newton's USD importer. Switch this to - # ``"NewtonSDFCollisionAPI"`` once that schema ships. - _usd_applied_schema: ClassVar[str | None] = None + # ``NewtonSDFCollisionAPI`` is authored into the prim's ``apiSchemas`` listOp (matching the + # legacy ``NewtonSDFCollisionPropertiesCfg``). It is not a *registered* applied API schema in + # the current Newton build, so it does not appear in the composed ``GetAppliedSchemas()`` until + # the schema ships -- but it is authored, and Newton's importer reads the ``newton:*`` attrs. + _usd_applied_schema: ClassVar[str | None] = "NewtonSDFCollisionAPI" sdf_max_resolution: int | None = None """Maximum SDF grid dimension [dimensionless]. From 1d637476a3e6d671926805266b98ccedb55b3478 Mon Sep 17 00:00:00 2001 From: Vidur Vij Date: Mon, 8 Jun 2026 20:08:38 -0700 Subject: [PATCH 9/9] style(schemas): trim verbose comments in mesh-collision fragment code No behavior change; collapse over-explained inline comments to terse intent. --- source/isaaclab/isaaclab/sim/schemas/schemas.py | 3 +-- source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py | 5 +---- .../isaaclab_newton/sim/schemas/schemas_cfg.py | 8 +++----- .../isaaclab_physx/sim/schemas/schemas_cfg.py | 10 ++++------ 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/source/isaaclab/isaaclab/sim/schemas/schemas.py b/source/isaaclab/isaaclab/sim/schemas/schemas.py index f09bf18efc17..027ec51483aa 100644 --- a/source/isaaclab/isaaclab/sim/schemas/schemas.py +++ b/source/isaaclab/isaaclab/sim/schemas/schemas.py @@ -516,8 +516,7 @@ def apply_mesh_collision_properties(prim_path: str, fragments, stage: Usd.Stage if not UsdPhysics.MeshCollisionAPI(prim): UsdPhysics.MeshCollisionAPI.Apply(prim) - # resolve the approximation token shared across the fragment list: the last fragment whose - # ``mesh_approximation_name`` is set to a non-"none" value wins; otherwise "none". + # resolve the shared approximation token: last fragment with a non-"none" name wins approximation_name = "none" for cfg in fragments: name = getattr(cfg, "mesh_approximation_name", None) diff --git a/source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py b/source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py index 2831cadbc3e8..167135484318 100644 --- a/source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py +++ b/source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py @@ -584,10 +584,7 @@ class MeshCollisionBaseCfg: """ # -- Class metadata (not dataclass fields) -- - # The standard ``UsdPhysics.MeshCollisionAPI`` is always applied by the writer when a - # mesh-collision cfg is supplied; ``_usd_applied_schema`` here records the standard - # API name so subclasses that author no PhysX namespace can rely on the writer's - # standard-vs-PhysX gating logic. PhysX-cooking subclasses override this. + # Records the standard API name for the writer's standard-vs-PhysX gating; cooking subclasses override. _usd_applied_schema: ClassVar[str | None] = "MeshCollisionAPI" # Base class authors no PhysX-namespaced fields, so no namespace is defined. _usd_namespace: ClassVar[str | None] = None diff --git a/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py b/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py index 19339b58997e..0ac6f9d582a3 100644 --- a/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py +++ b/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py @@ -293,11 +293,9 @@ class NewtonSDFCollisionPropertiesCfg(NewtonCollisionPropertiesCfg): # ------------------------------------------------------------------------------------- # Mesh-collision cooking fragments (single-namespace; Newton cooking add-on schemas). # -# Each fragment owns the ``newton`` namespace + its applied schema and is dispatched through the -# generic ``apply_namespaced`` applier by the family writer -# ``isaaclab.sim.schemas.apply_mesh_collision_properties``. They author no ``mesh_approximation_name`` -# of their own (the ``physics:approximation`` token is set by the PhysX/USD fragment present in the -# same list), so they only tune Newton-native cooking attributes. +# Each fragment owns the ``newton`` namespace + its applied schema, dispatched via ``apply_namespaced``. +# They author no ``mesh_approximation_name`` (the token is set by the PhysX/USD fragment in the same +# list), so they only tune Newton-native cooking attributes. # ------------------------------------------------------------------------------------- diff --git a/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas_cfg.py b/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas_cfg.py index b07b4a67a0dd..a4303c0c715f 100644 --- a/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas_cfg.py +++ b/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas_cfg.py @@ -539,12 +539,10 @@ def __post_init__(self): # ------------------------------------------------------------------------------------- # Mesh-collision cooking fragments (single-namespace; PhysX cooking add-on schemas). # -# Each fragment owns one ``physx*Collision:*`` namespace + applied schema, and carries a -# ``mesh_approximation_name`` whose default encodes the ``physics:approximation`` token its -# cooking schema implies. The token is written by the family writer -# ``isaaclab.sim.schemas.apply_mesh_collision_properties`` (which scans the fragment list and -# validates the name against ``MESH_APPROXIMATION_TOKENS``); the tuning attributes are written by -# the generic ``apply_namespaced`` applier. +# Each fragment owns one ``physx*Collision:*`` namespace + applied schema; its +# ``mesh_approximation_name`` default encodes the ``physics:approximation`` token its cooking +# schema implies. The token is written by the family writer +# ``isaaclab.sim.schemas.apply_mesh_collision_properties``; tuning attrs go via ``apply_namespaced``. # -------------------------------------------------------------------------------------