Unverified Commit 17fbff54 authored by Mayank Mittal's avatar Mayank Mittal Committed by GitHub

Removes storage of meshes inside the TerrainImporter class (#1987)

# Description

Sometimes users want to import a USD file which doesn't have any
collider meshes. This MR allows having such USD files in the terrain
importer for visualization purposes.

Additionally, the MR removes the storage of warp and tri-mesh based
meshes inside the class. These are stored into the USD and are read
directly from there whenever needed. Their intended use-case for having
them inside the terrain class is not needed.

## Type of change

- Bug fix (non-breaking change which fixes an issue)
- Breaking change

## Checklist

- [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
- [ ] 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

---------
Signed-off-by: 's avatarMayank Mittal <12863862+Mayankm96@users.noreply.github.com>
Co-authored-by: 's avatarKelly Guo <kellyguo123@hotmail.com>
parent 46cbb5d6
[package] [package]
# Note: Semantic Versioning is used: https://semver.org/ # Note: Semantic Versioning is used: https://semver.org/
version = "0.35.0" version = "0.36.0"
# Description # Description
title = "Isaac Lab framework for Robot Learning" title = "Isaac Lab framework for Robot Learning"
......
Changelog Changelog
--------- ---------
0.36.0 (2025-03-07)
~~~~~~~~~~~~~~~~~~~
Removed
^^^^^^^
* Removed the storage of tri-meshes and warp meshes inside the :class:`~isaaclab.terrains.TerrainImporter` class.
Initially these meshes were added for ray-casting purposes. However, since the ray-caster reads the terrains
directly from the USD files, these meshes are no longer needed.
* Deprecated the :attr:`warp_meshes` and :attr:`meshes` attributes from the
:class:`~isaaclab.terrains.TerrainImporter` class. These attributes now return an empty dictionary
with a deprecation warning.
Changed
^^^^^^^
* Changed the prim path of the "plane" terrain inside the :class:`~isaaclab.terrains.TerrainImporter` class.
Earlier, the terrain was imported directly as the importer's prim path. Now, the terrain is imported as
``{importer_prim_path}/{name}``, where ``name`` is the name of the terrain.
0.35.0 (2025-03-07) 0.35.0 (2025-03-07)
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
......
...@@ -11,11 +11,10 @@ There are two main components in this package: ...@@ -11,11 +11,10 @@ There are two main components in this package:
sub-terrain configuration. It creates a ``trimesh`` mesh object and contains the origins of sub-terrain configuration. It creates a ``trimesh`` mesh object and contains the origins of
each generated sub-terrain. each generated sub-terrain.
* :class:`TerrainImporter`: This class mainly deals with importing terrains from different * :class:`TerrainImporter`: This class mainly deals with importing terrains from different
possible sources and adding them to the simulator as a prim object. It also stores the possible sources and adding them to the simulator as a prim object.
terrain mesh into a dictionary called :obj:`TerrainImporter.warp_meshes` that later can be used The following functions are available for importing terrains:
for ray-casting. The following functions are available for importing terrains:
* :meth:`TerrainImporter.import_ground_plane`: spawn a grid plane which is default in isaacsim/isaaclab. * :meth:`TerrainImporter.import_ground_plane`: spawn a grid plane which is default in Isaac Sim.
* :meth:`TerrainImporter.import_mesh`: spawn a prim from a ``trimesh`` object. * :meth:`TerrainImporter.import_mesh`: spawn a prim from a ``trimesh`` object.
* :meth:`TerrainImporter.import_usd`: spawn a prim as reference to input USD file. * :meth:`TerrainImporter.import_usd`: spawn a prim as reference to input USD file.
......
...@@ -14,7 +14,9 @@ simulation_app = AppLauncher(headless=True).app ...@@ -14,7 +14,9 @@ simulation_app = AppLauncher(headless=True).app
import numpy as np import numpy as np
import torch import torch
import trimesh
import unittest import unittest
from typing import Literal
import isaacsim.core.utils.prims as prim_utils import isaacsim.core.utils.prims as prim_utils
import omni.kit import omni.kit
...@@ -24,9 +26,10 @@ from isaacsim.core.api.objects import DynamicSphere ...@@ -24,9 +26,10 @@ from isaacsim.core.api.objects import DynamicSphere
from isaacsim.core.cloner import GridCloner from isaacsim.core.cloner import GridCloner
from isaacsim.core.prims import RigidPrim, SingleGeometryPrim, SingleRigidPrim from isaacsim.core.prims import RigidPrim, SingleGeometryPrim, SingleRigidPrim
from isaacsim.core.utils.extensions import enable_extension from isaacsim.core.utils.extensions import enable_extension
from pxr import UsdGeom
import isaaclab.terrains as terrain_gen import isaaclab.terrains as terrain_gen
from isaaclab.sim import PreviewSurfaceCfg, SimulationContext, build_simulation_context from isaaclab.sim import PreviewSurfaceCfg, SimulationContext, build_simulation_context, get_first_matching_child_prim
from isaaclab.terrains import TerrainImporter, TerrainImporterCfg from isaaclab.terrains import TerrainImporter, TerrainImporterCfg
from isaaclab.terrains.config.rough import ROUGH_TERRAINS_CFG from isaaclab.terrains.config.rough import ROUGH_TERRAINS_CFG
from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR
...@@ -81,8 +84,12 @@ class TestTerrainImporter(unittest.TestCase): ...@@ -81,8 +84,12 @@ class TestTerrainImporter(unittest.TestCase):
) )
terrain_importer = TerrainImporter(terrain_importer_cfg) terrain_importer = TerrainImporter(terrain_importer_cfg)
# check mesh exists # check if mesh prim path exists
mesh = terrain_importer.meshes["terrain"] mesh_prim_path = terrain_importer.cfg.prim_path + "/terrain"
self.assertIn(mesh_prim_path, terrain_importer.terrain_prim_paths)
# obtain underling mesh
mesh = self._obtain_collision_mesh(mesh_prim_path, mesh_type="Mesh")
self.assertIsNotNone(mesh) self.assertIsNotNone(mesh)
# calculate expected size from config # calculate expected size from config
...@@ -105,9 +112,6 @@ class TestTerrainImporter(unittest.TestCase): ...@@ -105,9 +112,6 @@ class TestTerrainImporter(unittest.TestCase):
with build_simulation_context(device=device, auto_add_lighting=True) as sim: with build_simulation_context(device=device, auto_add_lighting=True) as sim:
sim._app_control_on_stop_handle = None sim._app_control_on_stop_handle = None
expectedSizeX = 2.0e6
expectedSizeY = 2.0e6
# create custom material # create custom material
visual_material = PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0)) if use_custom_material else None visual_material = PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0)) if use_custom_material else None
# Handler for terrains importing # Handler for terrains importing
...@@ -120,16 +124,13 @@ class TestTerrainImporter(unittest.TestCase): ...@@ -120,16 +124,13 @@ class TestTerrainImporter(unittest.TestCase):
) )
terrain_importer = TerrainImporter(terrain_importer_cfg) terrain_importer = TerrainImporter(terrain_importer_cfg)
# check mesh exists # check if mesh prim path exists
mesh = terrain_importer.meshes["terrain"] mesh_prim_path = terrain_importer.cfg.prim_path + "/terrain"
self.assertIsNotNone(mesh) self.assertIn(mesh_prim_path, terrain_importer.terrain_prim_paths)
# get size from mesh bounds
bounds = mesh.bounds
actualSize = abs(bounds[1] - bounds[0])
self.assertAlmostEqual(actualSize[0], expectedSizeX) # obtain underling mesh
self.assertAlmostEqual(actualSize[1], expectedSizeY) mesh = self._obtain_collision_mesh(mesh_prim_path, mesh_type="Plane")
self.assertIsNone(mesh)
def test_usd(self) -> None: def test_usd(self) -> None:
"""Imports terrain from a usd and tests that the resulting mesh has the correct size.""" """Imports terrain from a usd and tests that the resulting mesh has the correct size."""
...@@ -146,8 +147,12 @@ class TestTerrainImporter(unittest.TestCase): ...@@ -146,8 +147,12 @@ class TestTerrainImporter(unittest.TestCase):
) )
terrain_importer = TerrainImporter(terrain_importer_cfg) terrain_importer = TerrainImporter(terrain_importer_cfg)
# check mesh exists # check if mesh prim path exists
mesh = terrain_importer.meshes["terrain"] mesh_prim_path = terrain_importer.cfg.prim_path + "/terrain"
self.assertIn(mesh_prim_path, terrain_importer.terrain_prim_paths)
# obtain underling mesh
mesh = self._obtain_collision_mesh(mesh_prim_path, mesh_type="Mesh")
self.assertIsNotNone(mesh) self.assertIsNotNone(mesh)
# expect values from USD file # expect values from USD file
...@@ -226,6 +231,25 @@ class TestTerrainImporter(unittest.TestCase): ...@@ -226,6 +231,25 @@ class TestTerrainImporter(unittest.TestCase):
Helper functions. Helper functions.
""" """
def _obtain_collision_mesh(
self, mesh_prim_path: str, mesh_type: Literal["Mesh", "Plane"]
) -> trimesh.Trimesh | None:
"""Get the collision mesh from the terrain."""
# traverse the prim and get the collision mesh
mesh_prim = get_first_matching_child_prim(mesh_prim_path, lambda prim: prim.GetTypeName() == mesh_type)
# check it is valid
self.assertTrue(mesh_prim.IsValid())
if mesh_prim.GetTypeName() == "Mesh":
# cast into UsdGeomMesh
mesh_prim = UsdGeom.Mesh(mesh_prim)
# store the mesh
vertices = np.asarray(mesh_prim.GetPointsAttr().Get())
faces = np.asarray(mesh_prim.GetFaceVertexIndicesAttr().Get()).reshape(-1, 3)
return trimesh.Trimesh(vertices=vertices, faces=faces)
else:
return None
@staticmethod @staticmethod
def _obtain_grid_cloner_env_origins(num_envs: int, env_spacing: float, device: str) -> torch.Tensor: def _obtain_grid_cloner_env_origins(num_envs: int, env_spacing: float, device: str) -> torch.Tensor:
"""Obtain the env origins generated by IsaacSim GridCloner (grid_cloner.py).""" """Obtain the env origins generated by IsaacSim GridCloner (grid_cloner.py)."""
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment