-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Adding Rigid Body Material USD data classes and writers #6287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
182dcb9
17d7c0a
b8ba64e
bbf9c71
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| Added | ||
| ^^^^^ | ||
|
|
||
| * Added the rigid-body physics-material "fragment" classes | ||
| :class:`~isaaclab.sim.spawners.materials.RigidBodyMaterialFragment` (marker base) and | ||
| :class:`~isaaclab.sim.spawners.materials.UsdPhysicsRigidBodyMaterialCfg` (solver-common | ||
| ``physics:*`` friction/restitution), plus the family writer | ||
| :func:`~isaaclab.sim.spawners.materials.spawn_rigid_body_material_from_fragments` and the slot | ||
| dispatcher :func:`~isaaclab.sim.spawners.materials.spawn_physics_material`. Spawner | ||
| ``physics_material`` slots now accept a list of single-namespace fragments in addition to the | ||
| legacy material cfg. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,10 +12,114 @@ | |||||||||||||||||||||||||
| from isaaclab.sim.schemas.schemas import _apply_namespaced_schemas | ||||||||||||||||||||||||||
| from isaaclab.sim.utils import clone | ||||||||||||||||||||||||||
| from isaaclab.sim.utils.stage import get_current_stage | ||||||||||||||||||||||||||
| from isaaclab.utils.string import string_to_callable | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| from . import physics_materials_cfg | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| def spawn_rigid_body_material_from_fragments( | ||||||||||||||||||||||||||
| prim_path: str, | ||||||||||||||||||||||||||
| fragments: physics_materials_cfg.RigidBodyMaterialFragment | list[physics_materials_cfg.RigidBodyMaterialFragment], | ||||||||||||||||||||||||||
| stage: Usd.Stage | None = None, | ||||||||||||||||||||||||||
| ) -> Usd.Prim: | ||||||||||||||||||||||||||
| """Spawn a rigid-body physics material from a list of single-namespace fragments. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Creates (or reuses) the ``UsdShade.Material`` prim at ``prim_path``, applies the standard | ||||||||||||||||||||||||||
| ``UsdPhysics.MaterialAPI`` anchor, then dispatches each fragment via its | ||||||||||||||||||||||||||
| :attr:`~isaaclab.sim.schemas.SchemaFragment.func` to author its namespace onto the material prim. | ||||||||||||||||||||||||||
| Backend fragments carry backend-specific namespaces (e.g. PhysX ``physxMaterial:*``) without core | ||||||||||||||||||||||||||
| importing a backend. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .. note:: | ||||||||||||||||||||||||||
| Unlike the ``@clone``-decorated :func:`spawn_rigid_body_material`, this writer expects a | ||||||||||||||||||||||||||
| concrete ``prim_path`` and does not resolve regex prim-path patterns. Physics materials are | ||||||||||||||||||||||||||
| spawned at a single derived path by :func:`spawn_physics_material` and the spawner internals, | ||||||||||||||||||||||||||
| so regex resolution does not apply here. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||||
| prim_path: The prim path to spawn the material at. | ||||||||||||||||||||||||||
| fragments: A single :class:`~isaaclab.sim.spawners.materials.RigidBodyMaterialFragment` or a list | ||||||||||||||||||||||||||
| of them. | ||||||||||||||||||||||||||
| stage: The stage to spawn on. Defaults to None, in which case the current stage is used. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Returns: | ||||||||||||||||||||||||||
| The spawned rigid body material prim. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Raises: | ||||||||||||||||||||||||||
| ValueError: When a prim already exists at the path and is not a material. | ||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||
| if stage is None: | ||||||||||||||||||||||||||
| stage = get_current_stage() | ||||||||||||||||||||||||||
| if not isinstance(fragments, (list, tuple)): | ||||||||||||||||||||||||||
| fragments = [fragments] | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # create the material prim if none exists yet | ||||||||||||||||||||||||||
| if not stage.GetPrimAtPath(prim_path).IsValid(): | ||||||||||||||||||||||||||
| UsdShade.Material.Define(stage, prim_path) | ||||||||||||||||||||||||||
| prim = stage.GetPrimAtPath(prim_path) | ||||||||||||||||||||||||||
| if not prim.IsA(UsdShade.Material): | ||||||||||||||||||||||||||
| raise ValueError(f"A prim already exists at path: '{prim_path}' but is not a material.") | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # apply the standard UsdPhysics MaterialAPI anchor (the defining schema for a physics material) | ||||||||||||||||||||||||||
| if not UsdPhysics.MaterialAPI(prim): | ||||||||||||||||||||||||||
| UsdPhysics.MaterialAPI.Apply(prim) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # dispatch each fragment's applier (writes its single namespace onto the material prim) | ||||||||||||||||||||||||||
| for cfg in fragments: | ||||||||||||||||||||||||||
| func = cfg.func if callable(cfg.func) else string_to_callable(cfg.func) | ||||||||||||||||||||||||||
| func(cfg, prim_path, stage) | ||||||||||||||||||||||||||
| return prim | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| def spawn_physics_material( | ||||||||||||||||||||||||||
| prim_path: str, | ||||||||||||||||||||||||||
| material: physics_materials_cfg.PhysicsMaterialCfg | ||||||||||||||||||||||||||
| | physics_materials_cfg.RigidBodyMaterialFragment | ||||||||||||||||||||||||||
| | list[physics_materials_cfg.RigidBodyMaterialFragment], | ||||||||||||||||||||||||||
| stage: Usd.Stage | None = None, | ||||||||||||||||||||||||||
| ) -> Usd.Prim: | ||||||||||||||||||||||||||
|
Comment on lines
+74
to
+80
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! |
||||||||||||||||||||||||||
| """Spawn a physics material from a spawner ``physics_material`` slot value. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Dispatches the two accepted slot forms: a list of (or single) rigid-body fragments is spawned via | ||||||||||||||||||||||||||
| :func:`spawn_rigid_body_material_from_fragments`; otherwise the value is a legacy material cfg and is | ||||||||||||||||||||||||||
| spawned via its own :attr:`func`. Lets spawners accept both the fragment and the legacy interface | ||||||||||||||||||||||||||
| from one call site. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||||
| prim_path: The prim path to spawn the material at. | ||||||||||||||||||||||||||
| material: A :class:`~isaaclab.sim.spawners.materials.RigidBodyMaterialFragment` (or list of | ||||||||||||||||||||||||||
| them), or a legacy material cfg carrying a :attr:`func`. | ||||||||||||||||||||||||||
| stage: The stage to spawn on. Defaults to None, in which case the current stage is used. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Returns: | ||||||||||||||||||||||||||
| The spawned material prim. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Raises: | ||||||||||||||||||||||||||
| ValueError: When ``material`` is an empty list. | ||||||||||||||||||||||||||
| TypeError: When ``material`` is a list containing anything other than | ||||||||||||||||||||||||||
| :class:`~isaaclab.sim.spawners.materials.RigidBodyMaterialFragment` instances. | ||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||
| # a list/tuple slot value is the fragment-list form; validate it up front so a malformed list | ||||||||||||||||||||||||||
| # surfaces a clear error here rather than an opaque ``AttributeError`` on the legacy path below | ||||||||||||||||||||||||||
| if isinstance(material, (list, tuple)): | ||||||||||||||||||||||||||
| if not material: | ||||||||||||||||||||||||||
| raise ValueError(f"Cannot spawn a physics material at '{prim_path}' from an empty fragment list.") | ||||||||||||||||||||||||||
| if not all(isinstance(f, physics_materials_cfg.RigidBodyMaterialFragment) for f in material): | ||||||||||||||||||||||||||
| raise TypeError( | ||||||||||||||||||||||||||
| "A physics-material fragment list must contain only RigidBodyMaterialFragment instances; got" | ||||||||||||||||||||||||||
| f" {[type(f).__name__ for f in material]}." | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
| return spawn_rigid_body_material_from_fragments(prim_path, list(material), stage) | ||||||||||||||||||||||||||
| # a lone fragment routes through the fragment writer too | ||||||||||||||||||||||||||
| if isinstance(material, physics_materials_cfg.RigidBodyMaterialFragment): | ||||||||||||||||||||||||||
| return spawn_rigid_body_material_from_fragments(prim_path, [material], stage) | ||||||||||||||||||||||||||
| # legacy single-cfg path (rigid or deformable material cfg with its own spawner ``func``). | ||||||||||||||||||||||||||
| # NOTE: legacy material funcs take only ``(prim_path, cfg)`` and resolve the stage internally via | ||||||||||||||||||||||||||
| # ``get_current_stage()``; they have no ``stage`` parameter, so ``stage`` is intentionally not | ||||||||||||||||||||||||||
| # forwarded here. This is invisible in single-stage workflows (the only ones materials are used in). | ||||||||||||||||||||||||||
| return material.func(prim_path, material) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| @clone | ||||||||||||||||||||||||||
| def spawn_rigid_body_material(prim_path: str, cfg: physics_materials_cfg.RigidBodyMaterialBaseCfg) -> Usd.Prim: | ||||||||||||||||||||||||||
| """Create material with rigid-body physics properties. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@clonedecoratorspawn_rigid_body_materialandspawn_deformable_body_materialare both decorated with@clone, which resolves regex prim-path patterns (e.g./World/Robot_.*/body) to concrete paths before spawning.spawn_rigid_body_material_from_fragmentsskips the decorator, so callers who pass a regex path directly will silently get a single prim at the literal pattern string rather than one prim per match. The docstring does not note this limitation, which diverges from the documented behaviour of the rest of the spawner family.