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]
# Note: Semantic Versioning is used: https://semver.org/
version = "0.35.0"
version = "0.36.0"
# Description
title = "Isaac Lab framework for Robot Learning"
......
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)
~~~~~~~~~~~~~~~~~~~
......
......@@ -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
each generated sub-terrain.
* :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
terrain mesh into a dictionary called :obj:`TerrainImporter.warp_meshes` that later can be used
for ray-casting. The following functions are available for importing terrains:
possible sources and adding them to the simulator as a prim object.
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_usd`: spawn a prim as reference to input USD file.
......
......@@ -14,7 +14,9 @@ simulation_app = AppLauncher(headless=True).app
import numpy as np
import torch
import trimesh
import unittest
from typing import Literal
import isaacsim.core.utils.prims as prim_utils
import omni.kit
......@@ -24,9 +26,10 @@ from isaacsim.core.api.objects import DynamicSphere
from isaacsim.core.cloner import GridCloner
from isaacsim.core.prims import RigidPrim, SingleGeometryPrim, SingleRigidPrim
from isaacsim.core.utils.extensions import enable_extension
from pxr import UsdGeom
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.config.rough import ROUGH_TERRAINS_CFG
from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR
......@@ -81,8 +84,12 @@ class TestTerrainImporter(unittest.TestCase):
)
terrain_importer = TerrainImporter(terrain_importer_cfg)
# check mesh exists
mesh = terrain_importer.meshes["terrain"]
# check if mesh prim path exists
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)
# calculate expected size from config
......@@ -105,9 +112,6 @@ class TestTerrainImporter(unittest.TestCase):
with build_simulation_context(device=device, auto_add_lighting=True) as sim:
sim._app_control_on_stop_handle = None
expectedSizeX = 2.0e6
expectedSizeY = 2.0e6
# create custom material
visual_material = PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0)) if use_custom_material else None
# Handler for terrains importing
......@@ -120,16 +124,13 @@ class TestTerrainImporter(unittest.TestCase):
)
terrain_importer = TerrainImporter(terrain_importer_cfg)
# check mesh exists
mesh = terrain_importer.meshes["terrain"]
self.assertIsNotNone(mesh)
# get size from mesh bounds
bounds = mesh.bounds
actualSize = abs(bounds[1] - bounds[0])
# check if mesh prim path exists
mesh_prim_path = terrain_importer.cfg.prim_path + "/terrain"
self.assertIn(mesh_prim_path, terrain_importer.terrain_prim_paths)
self.assertAlmostEqual(actualSize[0], expectedSizeX)
self.assertAlmostEqual(actualSize[1], expectedSizeY)
# obtain underling mesh
mesh = self._obtain_collision_mesh(mesh_prim_path, mesh_type="Plane")
self.assertIsNone(mesh)
def test_usd(self) -> None:
"""Imports terrain from a usd and tests that the resulting mesh has the correct size."""
......@@ -146,8 +147,12 @@ class TestTerrainImporter(unittest.TestCase):
)
terrain_importer = TerrainImporter(terrain_importer_cfg)
# check mesh exists
mesh = terrain_importer.meshes["terrain"]
# check if mesh prim path exists
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)
# expect values from USD file
......@@ -226,6 +231,25 @@ class TestTerrainImporter(unittest.TestCase):
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
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)."""
......
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