Skip to content
54 changes: 54 additions & 0 deletions embodichain/lab/sim/objects/articulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,9 @@ def __init__(
# set default collision filter
self._set_default_collision_filter()

# flag for collision visible node existence
self._has_collision_visible_node = False

def __str__(self) -> str:
parent_str = super().__str__()
return parent_str + f" | dof: {self.dof} | num_links: {self.num_links}"
Expand Down Expand Up @@ -1535,6 +1538,57 @@ def get_visual_material_inst(
result.append(mat_dict)
return result

def set_collision_render_visibility(
self,
collision_visible: bool = True,
render_visible: bool = True,
link_names: Optional[List[str]] = None,
collision_visible_rgba: Optional[Sequence[float]] = None,
):
"""set collision

Args:
collision_visible (bool, optional): is collision body visible. Defaults to True.
render_visible (bool, optional): is render body visible. Defaults to True.
link_names (Optional[List[str]], optional): links to set visibility. Defaults to None.
collision_visible_rgba (Optional[Sequence[float]], optional): collision body visible rgba. Defaults to None.
"""
collision_visible_rgba = (
collision_visible_rgba
if collision_visible_rgba is not None
else (0.8, 0.2, 0.2, 0.7)
)
if len(collision_visible_rgba) != 4:
logger.log_error(
f"Invalid collision_visible_rgba {collision_visible_rgba}, should be a sequence of 4 floats."
)
collision_visible_rgba = np.array(
[
collision_visible_rgba[0],
collision_visible_rgba[1],
collision_visible_rgba[2],
collision_visible_rgba[3],
]
)
link_names = self.link_names if link_names is None else link_names

# create collision visible node if not exist
if collision_visible:
if not self._has_collision_visible_node:
for i, env_idx in enumerate(self._all_indices):
for link_name in link_names:
self._entities[env_idx].create_physical_visible_node(
collision_visible_rgba, link_name
)
self._has_collision_visible_node = True

# set visibility
for i, env_idx in enumerate(self._all_indices):
for link_name in link_names:
self._entities[env_idx].set_physical_visible(
collision_visible, render_visible, link_name
)

def destroy(self) -> None:
env = self._world.get_env()
arenas = env.get_all_arenas()
Expand Down
48 changes: 48 additions & 0 deletions embodichain/lab/sim/objects/rigid_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ def __init__(
# set default collision filter
self._set_default_collision_filter()

# reserve flag for collision visible node existence
self._has_collision_visible_node = False

def __str__(self) -> str:
parent_str = super().__str__()
return (
Expand Down Expand Up @@ -631,6 +634,51 @@ def clear_dynamics(self, env_ids: Optional[Sequence[int]] = None) -> None:
data_type=RigidBodyGPUAPIWriteType.TORQUE,
)

def set_collision_render_visibility(
self,
collision_visible: bool = True,
render_visible: bool = True,
collision_visible_rgba: Optional[Sequence[float]] = None,
):
"""set collion render visibility

Args:
collision_visible (bool, optional): is collision body visible. Defaults to True.
render_visible (bool, optional): is render body visible. Defaults to True.
collision_visible_rgba (Optional[Sequence[float]], optional): collision body visible rgba. Defaults to None.
"""
collision_visible_rgba = (
collision_visible_rgba
if collision_visible_rgba is not None
else (0.8, 0.2, 0.2, 0.7)
)
if len(collision_visible_rgba) != 4:
logger.log_error(
f"Invalid collision_visible_rgba {collision_visible_rgba}, should be a sequence of 4 floats."
)

# create collision visible node if not exist
if collision_visible:
if not self._has_collision_visible_node:
for i, env_idx in enumerate(self._all_indices):
self._entities[env_idx].create_physical_visible_node(
np.array(
[
collision_visible_rgba[0],
collision_visible_rgba[1],
collision_visible_rgba[2],
collision_visible_rgba[3],
]
)
)
self._has_collision_visible_node = True

# create collision visible node if not exist
for i, env_idx in enumerate(self._all_indices):
self._entities[env_idx].set_physical_visible(
collision_visible, render_visible
)

def reset(self, env_ids: Optional[Sequence[int]] = None) -> None:
local_env_ids = self._all_indices if env_ids is None else env_ids
num_instances = len(local_env_ids)
Expand Down
8 changes: 7 additions & 1 deletion scripts/tutorials/sim/create_robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def main():
description="Create and simulate a robot in SimulationManager"
)
parser.add_argument(
"--num_envs", type=int, default=1, help="Number of environments to simulate"
"--num_envs", type=int, default=4, help="Number of environments to simulate"
)
parser.add_argument(
"--device",
Expand Down Expand Up @@ -87,6 +87,12 @@ def main():
if not args.headless:
sim.open_window()

robot.set_collision_render_visibility(
True, False, collision_visible_rgba=(0.1, 0.1, 0.9, 0.4)
)
time.sleep(1.0)
robot.set_collision_render_visibility(False, True)

# Run simulation loop
run_simulation(sim, robot)

Expand Down
41 changes: 40 additions & 1 deletion scripts/tutorials/sim/create_scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@

from embodichain.lab.sim import SimulationManager, SimulationManagerCfg
from embodichain.lab.sim.cfg import RigidBodyAttributesCfg
from embodichain.lab.sim.shapes import CubeCfg
from embodichain.lab.sim.shapes import CubeCfg, MeshCfg
from embodichain.lab.sim.objects import RigidObject, RigidObjectCfg
from dexsim.utility.path import get_resources_data_path


def main():
Expand Down Expand Up @@ -88,10 +89,48 @@ def main():
)
)

cow: RigidObject = sim.add_rigid_object(
cfg=RigidObjectCfg(
uid="cow",
shape=MeshCfg(
fpath=get_resources_data_path("Model", "cow", "cow.obj"),
),
body_type="dynamic",
attrs=RigidBodyAttributesCfg(
mass=1.0,
dynamic_friction=0.5,
static_friction=0.5,
restitution=0.1,
),
init_pos=[1.5, 0.0, 1.0],
)
)

cow1: RigidObject = sim.add_rigid_object(
cfg=RigidObjectCfg(
uid="cow1",
shape=MeshCfg(
fpath=get_resources_data_path("Model", "cow", "cow.obj"),
),
body_type="dynamic",
attrs=RigidBodyAttributesCfg(
mass=1.0,
dynamic_friction=0.5,
static_friction=0.5,
restitution=0.1,
),
init_pos=[1.5, 1.5, 1.0],
)
)

print("[INFO]: Scene setup complete!")
print(f"[INFO]: Running simulation with {args.num_envs} environment(s)")
print("[INFO]: Press Ctrl+C to stop the simulation")

cow.set_collision_render_visibility(
True, False, collision_visible_rgba=(0.1, 0.1, 0.9, 0.4)
)

# Open window when the scene has been set up
if not args.headless:
sim.open_window()
Expand Down
36 changes: 36 additions & 0 deletions tests/sim/objects/test_articulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,42 @@ def test_remove_articulation(self):
self.art.uid not in self.sim.asset_uids
), "FAIL: Articulation UID still present after removal"

def test_set_collision_render_visibility(self):
"""Test setting collision and render visibility of the duck object."""

# Set collision with color
self.art.set_collision_render_visibility(
collision_visible=True,
render_visible=True,
collision_visible_rgba=(0.1, 0.1, 0.9, 0.4),
)

# Set collision visible, render invisible
self.art.set_collision_render_visibility(
collision_visible=True, render_visible=False
)

# Set collision invisible, render visible
self.art.set_collision_render_visibility(
collision_visible=False, render_visible=True
)

# Set both visible
self.art.set_collision_render_visibility(
collision_visible=True, render_visible=True
)

# Set both invisible
self.art.set_collision_render_visibility(
collision_visible=False, render_visible=False
)

# set visible with link names
all_link_names = self.art.link_names
self.art.set_collision_render_visibility(
collision_visible=True, render_visible=False, link_names=all_link_names[:3]
)

def teardown_method(self):
"""Clean up resources after each test method."""
self.sim.destroy()
Expand Down
31 changes: 31 additions & 0 deletions tests/sim/objects/test_rigid_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def setup_simulation(self, sim_device):
uid="table", shape=MeshCfg(fpath=table_path), body_type="static"
),
)

self.chair: RigidObject = self.sim.add_rigid_object(
cfg=RigidObjectCfg(
uid="chair", shape=MeshCfg(fpath=chair_path), body_type="kinematic"
Expand Down Expand Up @@ -253,6 +254,36 @@ def test_remove(self):
self.duck.uid not in self.sim.asset_uids
), "Duck UID still present after removal"

def test_set_collision_render_visibility(self):
"""Test setting collision and render visibility of the duck object."""

# Set collision with color
self.table.set_collision_render_visibility(
collision_visible=True,
render_visible=True,
collision_visible_rgba=(0.1, 0.1, 0.9, 0.4),
)

# Set collision visible, render invisible
self.table.set_collision_render_visibility(
collision_visible=True, render_visible=False
)

# Set collision invisible, render visible
self.table.set_collision_render_visibility(
collision_visible=False, render_visible=True
)

# Set both visible
self.table.set_collision_render_visibility(
collision_visible=True, render_visible=True
)

# Set both invisible
self.table.set_collision_render_visibility(
collision_visible=False, render_visible=False
)

def teardown_method(self):
"""Clean up resources after each test method."""
self.sim.destroy()
Expand Down
Loading