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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Guidelines for modifications:
* CY (Chien-Ying) Chen
* David Yang
* Dhananjay Shendre
* Dongxuan Fan
* Dorsa Rohani
* Emily Sturman
* Fabian Jenelten
Expand Down
2 changes: 1 addition & 1 deletion source/isaaclab/config/extension.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]

# Note: Semantic Versioning is used: https://semver.org/
version = "0.48.6"
version = "0.48.7"

# Description
title = "Isaac Lab framework for Robot Learning"
Expand Down
11 changes: 11 additions & 0 deletions source/isaaclab/docs/CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
Changelog
---------

0.48.7 (2025-11-26)
~~~~~~~~~~~~~~~~~~~

Fixed
^^^^^

* Fixed missing mesh collision approximation attribute when running :class:`~isaaclab.sim.converters.MeshConverter`.
The collision approximation attribute is now properly set on the USD prim when converting meshes with mesh collision
properties.


0.48.6 (2025-11-18)
~~~~~~~~~~~~~~~~~~~

Expand Down
2 changes: 2 additions & 0 deletions source/isaaclab/isaaclab/sim/schemas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"""

from .schemas import (
MESH_APPROXIMATION_TOKENS,
PHYSX_MESH_COLLISION_CFGS,
USD_MESH_COLLISION_CFGS,
activate_contact_sensors,
Expand Down Expand Up @@ -122,4 +123,5 @@
# Constants for configs that use PhysX vs USD API
"PHYSX_MESH_COLLISION_CFGS",
"USD_MESH_COLLISION_CFGS",
"MESH_APPROXIMATION_TOKENS",
]
74 changes: 63 additions & 11 deletions source/isaaclab/isaaclab/sim/schemas/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import logging
import math
from collections.abc import Callable
from typing import Any

import omni.physx.scripts.utils as physx_utils
from omni.physx.scripts import deformableUtils as deformable_utils
Expand All @@ -26,10 +28,26 @@
# import logger
logger = logging.getLogger(__name__)


"""
Articulation root properties.
Constants.
"""

# Mapping from string names to USD/PhysX tokens for mesh collision approximation
# Refer to omniverse documentation
# https://docs.omniverse.nvidia.com/kit/docs/omni_physics/latest/dev_guide/rigid_bodies_articulations/collision.html#mesh-geometry-colliders
# for available tokens.
MESH_APPROXIMATION_TOKENS = {
"boundingCube": UsdPhysics.Tokens.boundingCube,
"boundingSphere": UsdPhysics.Tokens.boundingSphere,
"convexDecomposition": UsdPhysics.Tokens.convexDecomposition,
"convexHull": UsdPhysics.Tokens.convexHull,
"none": UsdPhysics.Tokens.none,
"meshSimplification": UsdPhysics.Tokens.meshSimplification,
"sdf": PhysxSchema.Tokens.sdf,
}


PHYSX_MESH_COLLISION_CFGS = [
schemas_cfg.ConvexDecompositionPropertiesCfg,
schemas_cfg.ConvexHullPropertiesCfg,
Expand All @@ -47,6 +65,11 @@
]


"""
Articulation root properties.
"""


def define_articulation_root_properties(
prim_path: str, cfg: schemas_cfg.ArticulationRootPropertiesCfg, stage: Usd.Stage | None = None
):
Expand Down Expand Up @@ -961,13 +984,26 @@ def modify_deformable_body_properties(
"""


def extract_mesh_collision_api_and_attrs(cfg):
def extract_mesh_collision_api_and_attrs(
cfg: schemas_cfg.MeshCollisionPropertiesCfg,
) -> tuple[Callable, dict[str, Any]]:
"""Extract the mesh collision API function and custom attributes from the configuration.

Args:
cfg: The configuration for the mesh collision properties.

Returns:
A tuple containing the API function to use and a dictionary of custom attributes.

Raises:
ValueError: When neither USD nor PhysX API can be determined to be used.
"""
# We use the number of user set attributes outside of the API function
# to determine which API to use in ambiguous cases, so collect them here
custom_attrs = {
key: value
for key, value in cfg.to_dict().items()
if value is not None and key not in ["usd_func", "physx_func"]
if value is not None and key not in ["usd_func", "physx_func", "mesh_approximation_name"]
}

use_usd_api = False
Expand All @@ -985,20 +1021,17 @@ def extract_mesh_collision_api_and_attrs(cfg):
# Use the PhysX API
use_phsyx_api = True

elif len(custom_attrs > 0) and type(cfg) in USD_MESH_COLLISION_CFGS:
elif len(custom_attrs) > 0 and type(cfg) in USD_MESH_COLLISION_CFGS:
raise ValueError("Args are specified but the USD Mesh API doesn't support them!")

mesh_collision_appx_type = type(cfg).__name__.partition("PropertiesCfg")[0]

if use_usd_api:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is still useful to keep docstrings for the logic on mesh API decision making

# Add approximation to the attributes as this is how USD collision mesh API is configured
# Use USD API for corresponding attributes
# For mesh collision approximation attribute, we set it explicitly in `modify_mesh_collision_properties``
api_func = cfg.usd_func
# Approximation needs to be formatted with camelCase
custom_attrs["Approximation"] = mesh_collision_appx_type[0].lower() + mesh_collision_appx_type[1:]
elif use_phsyx_api:
api_func = cfg.physx_func
else:
raise ValueError("Either USD or PhysX API should be used for mesh collision approximation!")
raise ValueError("Either USD or PhysX API should be used for modifying mesh collision attributes!")

return api_func, custom_attrs

Expand Down Expand Up @@ -1037,7 +1070,7 @@ def define_mesh_collision_properties(
@apply_nested
def modify_mesh_collision_properties(
prim_path: str, cfg: schemas_cfg.MeshCollisionPropertiesCfg, stage: Usd.Stage | None = None
):
) -> bool:
"""Set properties for the mesh collision of a prim.
These properties are based on either the `Phsyx the `UsdPhysics.MeshCollisionAPI` schema.
.. note::
Expand All @@ -1049,13 +1082,32 @@ def modify_mesh_collision_properties(
cfg : The configuration for the mesh collision properties.
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, False otherwise.
Raises:
ValueError: When the mesh approximation name is invalid.
"""
# obtain stage
if stage is None:
stage = get_current_stage()
# get USD prim
prim = stage.GetPrimAtPath(prim_path)

# we need MeshCollisionAPI to set mesh collision approximation attribute
if not UsdPhysics.MeshCollisionAPI(prim):
UsdPhysics.MeshCollisionAPI.Apply(prim)
# convert mesh approximation string to token
approximation_name = cfg.mesh_approximation_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
)

api_func, custom_attrs = extract_mesh_collision_api_and_attrs(cfg=cfg)

# retrieve the mesh collision API
Expand Down
51 changes: 51 additions & 0 deletions source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,8 +442,23 @@ class MeshCollisionPropertiesCfg:
"""

usd_func: callable = MISSING
"""USD API function for modifying mesh collision properties.
Refer to
`original USD Documentation <https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/latest/class_usd_physics_mesh_collision_a_p_i.html>`_
for more information.
"""

physx_func: callable = MISSING
"""PhysX API function for modifying mesh collision properties.
Refer to
`original PhysX Documentation <https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/latest/annotated.html>`_
for more information.
"""

mesh_approximation_name: str = "none"
"""Name of mesh collision approximation method. Default: "none".
Refer to :const:`schemas.MESH_APPROXIMATION_TOKENS` for available options.
"""


@configclass
Expand All @@ -453,6 +468,11 @@ class BoundingCubePropertiesCfg(MeshCollisionPropertiesCfg):
https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/latest/class_usd_physics_mesh_collision_a_p_i.html
"""

mesh_approximation_name: str = "boundingCube"
"""Name of mesh collision approximation method. Default: "boundingCube".
Refer to :const:`schemas.MESH_APPROXIMATION_TOKENS` for available options.
"""


@configclass
class BoundingSpherePropertiesCfg(MeshCollisionPropertiesCfg):
Expand All @@ -461,6 +481,11 @@ class BoundingSpherePropertiesCfg(MeshCollisionPropertiesCfg):
https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/latest/class_usd_physics_mesh_collision_a_p_i.html
"""

mesh_approximation_name: str = "boundingSphere"
"""Name of mesh collision approximation method. Default: "boundingSphere".
Refer to :const:`schemas.MESH_APPROXIMATION_TOKENS` for available options.
"""


@configclass
class ConvexDecompositionPropertiesCfg(MeshCollisionPropertiesCfg):
Expand All @@ -474,6 +499,11 @@ class ConvexDecompositionPropertiesCfg(MeshCollisionPropertiesCfg):
https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/latest/class_physx_schema_physx_convex_decomposition_collision_a_p_i.html
"""

mesh_approximation_name: str = "convexDecomposition"
"""Name of mesh collision approximation method. Default: "convexDecomposition".
Refer to :const:`schemas.MESH_APPROXIMATION_TOKENS` for available options.
"""

hull_vertex_limit: int | None = None
"""Convex hull vertex limit used for convex hull cooking.

Expand Down Expand Up @@ -518,6 +548,11 @@ class ConvexHullPropertiesCfg(MeshCollisionPropertiesCfg):
https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/latest/class_physx_schema_physx_convex_hull_collision_a_p_i.html
"""

mesh_approximation_name: str = "convexHull"
"""Name of mesh collision approximation method. Default: "convexHull".
Refer to :const:`schemas.MESH_APPROXIMATION_TOKENS` for available options.
"""

hull_vertex_limit: int | None = None
"""Convex hull vertex limit used for convex hull cooking.

Expand All @@ -539,6 +574,11 @@ class TriangleMeshPropertiesCfg(MeshCollisionPropertiesCfg):
https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/latest/class_physx_schema_physx_triangle_mesh_collision_a_p_i.html
"""

mesh_approximation_name: str = "none"
"""Name of mesh collision approximation method. Default: "none" (uses triangle mesh).
Refer to :const:`schemas.MESH_APPROXIMATION_TOKENS` for available options.
"""

weld_tolerance: float | None = None
"""Mesh weld tolerance, controls the distance at which vertices are welded.

Expand All @@ -559,6 +599,11 @@ class TriangleMeshSimplificationPropertiesCfg(MeshCollisionPropertiesCfg):
https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/latest/class_physx_schema_physx_triangle_mesh_simplification_collision_a_p_i.html
"""

mesh_approximation_name: str = "meshSimplification"
"""Name of mesh collision approximation method. Default: "meshSimplification".
Refer to :const:`schemas.MESH_APPROXIMATION_TOKENS` for available options.
"""

simplification_metric: float | None = None
"""Mesh simplification accuracy.

Expand All @@ -583,6 +628,12 @@ class SDFMeshPropertiesCfg(MeshCollisionPropertiesCfg):
More details and steps for optimizing SDF results can be found here:
https://nvidia-omniverse.github.io/PhysX/physx/5.2.1/docs/RigidBodyCollision.html#dynamic-triangle-meshes-with-sdfs
"""

mesh_approximation_name: str = "sdf"
"""Name of mesh collision approximation method. Default: "sdf".
Refer to :const:`schemas.MESH_APPROXIMATION_TOKENS` for available options.
"""

sdf_margin: float | None = None
"""Margin to increase the size of the SDF relative to the bounding box diagonal length of the mesh.

Expand Down
57 changes: 52 additions & 5 deletions source/isaaclab/test/sim/test_mesh_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from pxr import UsdGeom, UsdPhysics

from isaaclab.sim.converters import MeshConverter, MeshConverterCfg
from isaaclab.sim.schemas import schemas_cfg
from isaaclab.sim.schemas import MESH_APPROXIMATION_TOKENS, schemas_cfg
from isaaclab.sim.utils import stage as stage_utils
from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR, retrieve_file_path

Expand Down Expand Up @@ -133,12 +133,14 @@ def check_mesh_collider_settings(mesh_converter: MeshConverter):
# -- if collision is enabled, check that collision approximation is correct
if exp_collision_enabled:
if mesh_converter.cfg.mesh_collision_props is not None:
exp_collision_approximation = (
mesh_converter.cfg.mesh_collision_props.usd_func(mesh_prim).GetApproximationAttr().Get()
)
exp_collision_approximation_str = mesh_converter.cfg.mesh_collision_props.mesh_approximation_name
exp_collision_approximation_token = MESH_APPROXIMATION_TOKENS[exp_collision_approximation_str]
mesh_collision_api = UsdPhysics.MeshCollisionAPI(mesh_prim)
collision_approximation = mesh_collision_api.GetApproximationAttr().Get()
assert collision_approximation == exp_collision_approximation, "Collision approximation is not the same!"
# Convert token to string for comparison
assert (
collision_approximation == exp_collision_approximation_token
), "Collision approximation is not the same!"


def test_no_change(assets):
Expand Down Expand Up @@ -255,6 +257,36 @@ def test_collider_convex_hull(assets):
check_mesh_collider_settings(mesh_converter)


def test_collider_convex_decomposition(assets):
"""Convert an OBJ file using convex decomposition approximation"""
collision_props = schemas_cfg.CollisionPropertiesCfg(collision_enabled=True)
mesh_collision_prop = schemas_cfg.ConvexDecompositionPropertiesCfg()
mesh_config = MeshConverterCfg(
asset_path=assets["obj"],
mesh_collision_props=mesh_collision_prop,
collision_props=collision_props,
)
mesh_converter = MeshConverter(mesh_config)

# check that mesh conversion is successful
check_mesh_collider_settings(mesh_converter)


def test_collider_triangle_mesh(assets):
"""Convert an OBJ file using triangle mesh approximation"""
collision_props = schemas_cfg.CollisionPropertiesCfg(collision_enabled=True)
mesh_collision_prop = schemas_cfg.TriangleMeshPropertiesCfg()
mesh_config = MeshConverterCfg(
asset_path=assets["obj"],
mesh_collision_props=mesh_collision_prop,
collision_props=collision_props,
)
mesh_converter = MeshConverter(mesh_config)

# check that mesh conversion is successful
check_mesh_collider_settings(mesh_converter)


def test_collider_mesh_simplification(assets):
"""Convert an OBJ file using mesh simplification approximation"""
collision_props = schemas_cfg.CollisionPropertiesCfg(collision_enabled=True)
Expand Down Expand Up @@ -300,6 +332,21 @@ def test_collider_mesh_bounding_sphere(assets):
check_mesh_collider_settings(mesh_converter)


def test_collider_mesh_sdf(assets):
"""Convert an OBJ file using signed distance field approximation"""
collision_props = schemas_cfg.CollisionPropertiesCfg(collision_enabled=True)
mesh_collision_prop = schemas_cfg.SDFMeshPropertiesCfg()
mesh_config = MeshConverterCfg(
asset_path=assets["obj"],
mesh_collision_props=mesh_collision_prop,
collision_props=collision_props,
)
mesh_converter = MeshConverter(mesh_config)

# check that mesh conversion is successful
check_mesh_collider_settings(mesh_converter)


def test_collider_mesh_no_collision(assets):
"""Convert an OBJ file using bounding sphere with collision disabled"""
collision_props = schemas_cfg.CollisionPropertiesCfg(collision_enabled=False)
Expand Down