diff --git a/isaaclab_arena/assets/background_library.py b/isaaclab_arena/assets/background_library.py index 9ced4b37..1b080ab4 100644 --- a/isaaclab_arena/assets/background_library.py +++ b/isaaclab_arena/assets/background_library.py @@ -3,6 +3,8 @@ # # SPDX-License-Identifier: Apache-2.0 +from typing import Any + from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR, ISAACLAB_NUCLEUS_DIR from isaaclab_arena.assets.background import Background @@ -21,6 +23,8 @@ class LibraryBackground(Background): usd_path: str initial_pose: Pose | None = None object_min_z: float + spawn_cfg_addon: dict[str, Any] = {} + asset_cfg_addon: dict[str, Any] = {} def __init__(self, **kwargs): super().__init__( @@ -29,6 +33,8 @@ def __init__(self, **kwargs): usd_path=self.usd_path, initial_pose=self.initial_pose, object_min_z=self.object_min_z, + spawn_cfg_addon=self.spawn_cfg_addon, + asset_cfg_addon=self.asset_cfg_addon, **kwargs, ) diff --git a/isaaclab_arena/assets/object.py b/isaaclab_arena/assets/object.py index 30322177..69b7f6e6 100644 --- a/isaaclab_arena/assets/object.py +++ b/isaaclab_arena/assets/object.py @@ -3,6 +3,8 @@ # # SPDX-License-Identifier: Apache-2.0 +from typing import Any + from isaaclab.assets import ArticulationCfg, AssetBaseCfg, RigidObjectCfg from isaaclab.sim.spawners.from_files.from_files_cfg import UsdFileCfg @@ -27,6 +29,9 @@ def __init__( initial_pose: Pose | None = None, **kwargs, ): + # Pull out addons (and remove them from kwargs before passing to super) + spawn_cfg_addon: dict[str, Any] = kwargs.pop("spawn_cfg_addon", {}) or {} + asset_cfg_addon: dict[str, Any] = kwargs.pop("asset_cfg_addon", {}) or {} if object_type is not ObjectType.SPAWNER: assert usd_path is not None # Detect object type if not provided @@ -36,6 +41,8 @@ def __init__( self.usd_path = usd_path self.scale = scale self.initial_pose = initial_pose + self.spawn_cfg_addon = spawn_cfg_addon + self.asset_cfg_addon = asset_cfg_addon self.object_cfg = self._init_object_cfg() def set_initial_pose(self, pose: Pose) -> None: @@ -56,7 +63,9 @@ def _generate_rigid_cfg(self) -> RigidObjectCfg: usd_path=self.usd_path, scale=self.scale, activate_contact_sensors=True, + **self.spawn_cfg_addon, ), + **self.asset_cfg_addon, ) object_cfg = self._add_initial_pose_to_cfg(object_cfg) return object_cfg @@ -69,7 +78,9 @@ def _generate_articulation_cfg(self) -> ArticulationCfg: usd_path=self.usd_path, scale=self.scale, activate_contact_sensors=True, + **self.spawn_cfg_addon, ), + **self.asset_cfg_addon, actuators={}, ) object_cfg = self._add_initial_pose_to_cfg(object_cfg) @@ -82,7 +93,12 @@ def _generate_base_cfg(self) -> AssetBaseCfg: print("WARNING: Base object has lights, this may cause issues when using with multiple environments.") object_cfg = AssetBaseCfg( prim_path="{ENV_REGEX_NS}/" + self.name, - spawn=UsdFileCfg(usd_path=self.usd_path, scale=self.scale), + spawn=UsdFileCfg( + usd_path=self.usd_path, + scale=self.scale, + **self.spawn_cfg_addon, + ), + **self.asset_cfg_addon, ) object_cfg = self._add_initial_pose_to_cfg(object_cfg) return object_cfg @@ -92,6 +108,7 @@ def _generate_spawner_cfg(self) -> AssetBaseCfg: object_cfg = AssetBaseCfg( prim_path=self.prim_path, spawn=self.spawner_cfg, + **self.asset_cfg_addon, ) object_cfg = self._add_initial_pose_to_cfg(object_cfg) return object_cfg diff --git a/isaaclab_arena/assets/object_library.py b/isaaclab_arena/assets/object_library.py index 8fe360fd..a53295d7 100644 --- a/isaaclab_arena/assets/object_library.py +++ b/isaaclab_arena/assets/object_library.py @@ -3,6 +3,8 @@ # # SPDX-License-Identifier: Apache-2.0 +from typing import Any + import isaaclab.sim as sim_utils from isaaclab.sim.spawners.from_files.from_files_cfg import GroundPlaneCfg from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR, ISAACLAB_NUCLEUS_DIR @@ -26,6 +28,8 @@ class LibraryObject(Object): usd_path: str | None = None object_type: ObjectType = ObjectType.RIGID scale: tuple[float, float, float] = (1.0, 1.0, 1.0) + spawn_cfg_addon: dict[str, Any] = {} + asset_cfg_addon: dict[str, Any] = {} def __init__(self, prim_path: str | None = None, initial_pose: Pose | None = None, **kwargs): super().__init__( @@ -36,6 +40,8 @@ def __init__(self, prim_path: str | None = None, initial_pose: Pose | None = Non object_type=self.object_type, scale=self.scale, initial_pose=initial_pose, + spawn_cfg_addon=self.spawn_cfg_addon, + asset_cfg_addon=self.asset_cfg_addon, **kwargs, ) diff --git a/isaaclab_arena/tests/test_object_with_cfg_addons.py b/isaaclab_arena/tests/test_object_with_cfg_addons.py new file mode 100644 index 00000000..3688d87a --- /dev/null +++ b/isaaclab_arena/tests/test_object_with_cfg_addons.py @@ -0,0 +1,52 @@ +# Copyright (c) 2025, The Isaac Lab Arena Project Developers (https://github.com/isaac-sim/IsaacLab-Arena/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 + + +from isaaclab_arena.tests.utils.subprocess import run_simulation_app_function + +HEADLESS = False + + +def _test_object_with_cfg_addons(simulation_app): + + from isaaclab_arena.assets.object_base import ObjectType + from isaaclab_arena.assets.object_library import LibraryObject + from isaaclab_arena.utils.pose import Pose + + class ConeWithCfgAddons(LibraryObject): + """ + Cone with cfg addons. + """ + + name = "cone_with_cfg_addons" + tags = ["object"] + usd_path = "https://omniverse-content-production.s3-us-west-2.amazonaws.com/Assets/Isaac/4.5/Isaac/Props/Shapes/cone.usd" + default_prim_path = "{ENV_REGEX_NS}/target_cone_with_cfg_addons" + object_type = ObjectType.RIGID + spawn_cfg_addon = {"visible": False} # By default, the object is visible. + asset_cfg_addon = {"debug_vis": True} # By default, the object is not debug visualized. + + def __init__(self, prim_path: str = default_prim_path, initial_pose: Pose | None = None): + super().__init__(prim_path=prim_path, initial_pose=initial_pose) + + cone = ConeWithCfgAddons() + + # Check that the settings have been applied + assert cone.object_cfg.spawn.visible is False + assert cone.object_cfg.debug_vis is True + + return True + + +def test_object_with_cfg_addons(): + result = run_simulation_app_function( + _test_object_with_cfg_addons, + headless=HEADLESS, + ) + assert result, "Test failed" + + +if __name__ == "__main__": + test_object_with_cfg_addons()