-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Adds Raycaster with tracking for Dynamic Meshes #3298
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
Open
renezurbruegg
wants to merge
131
commits into
isaac-sim:main
Choose a base branch
from
renezurbruegg:feature/multi-mesh-ray-caster
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+3,691
−85
Open
Changes from 86 commits
Commits
Show all changes
131 commits
Select commit
Hold shift + click to select a range
c9bcd3b
Adds multi-mesh raycaster from Orbit (#4)
pascal-roth b7c172f
Fixes the number of meshes for dynamic raycasting with an regex expre…
pascal-roth e1a9124
Separates our multi-mesh raycaster as its own file (#63)
pascal-roth 4ca82f6
Removes ClassVar `meshes` and `mesh_views` from MultiMeshRayCatser an…
pascal-roth d13331c
forgotten merge
pascal-roth 3f5d008
revert classvar changes and apply formatter
pascal-roth 5decd09
revert device change in ray caster
pascal-roth f585a31
update tests from unittest to pytest
pascal-roth 7242ea2
add initial benchmark scripts and formatter
pascal-roth 2c6c607
Fix check script
renezurbruegg 86418d3
Manually merging pascal-roth changes from static vars branch
renezurbruegg fa6b498
remove wrongfully pushed wheels
renezurbruegg 8beb0d2
Merge changes from #90 - dropping support for cache_combined_meshes …
renezurbruegg 5ca6e6e
add memory benchmark
renezurbruegg eefcb3a
fix garbage collection issue
renezurbruegg 6eda9c8
Add initial offset if mesh transform track is set to false
renezurbruegg 0712ee5
avoid visualization when ray hits is not instantiated yet
renezurbruegg 004184e
Add resolve world pose
renezurbruegg 9287efe
Change benchmark to sphere. Add different amount of faces and multi i…
renezurbruegg f80e384
fix local offsets
renezurbruegg bcd747a
add example allegro hand
renezurbruegg 4f6c772
Fix local offsets
renezurbruegg f98150e
Added regex lookups
renezurbruegg 5940b98
Add num assets flag.
renezurbruegg 500931c
Add anymal demo
renezurbruegg 586858c
Add scale randomizations. Drop support for instancer meshes
renezurbruegg 4d167ec
add shapes
renezurbruegg 5b8563d
update benchmark scripts for different use cases - WIP
renezurbruegg 17b5819
cleanup tests
renezurbruegg b3747c4
Add is shared functionality back
renezurbruegg ad7cbfc
change default
renezurbruegg e2e7f9e
cleanup
renezurbruegg 8a37d61
Cleanup benchmark. Add flag to specify if meshse should be referenced
renezurbruegg 3a6a242
remove old scripts
renezurbruegg 16cd7c2
lint
renezurbruegg 8efd58d
Merge branch 'main' into feature/multi-mesh-ray-caster
pascal-roth 7509d45
Update source/isaaclab/isaaclab/sensors/ray_caster/ray_caster.py
renezurbruegg 4e40239
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg 88da283
Move move merge_prim_meshes to RaycastTargetCfg
renezurbruegg 13ba4e2
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg abbc8ed
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg df9f660
Update source/isaaclab/isaaclab/sensors/utils.py
renezurbruegg f2894a0
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg 3eaecb5
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg 0a47268
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg b27caa5
Move track_mesh_transforms to target config
renezurbruegg 39fa91b
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg 82f72ce
Add specification about string targets
renezurbruegg 332b8be
improve docstrings
renezurbruegg f5e91cf
lint
renezurbruegg 7c98e27
remove absolute paths
renezurbruegg a048c16
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg 123cd01
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg bfeaf48
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg 532b1f6
Update docstrings
renezurbruegg cc7dfee
minor fixes for track mesh transforms
renezurbruegg 2ec9bd7
Improve docstring
renezurbruegg 2743d03
lint
renezurbruegg b44eaf2
Migrating to pytest
renezurbruegg 7ce6d6d
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg 6066474
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg 5984c82
Update source/isaaclab/isaaclab/sensors/utils.py
renezurbruegg 09f17f5
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg 3a2de0d
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg 7b39736
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg e9fb9a1
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg 43da984
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg 12de1fa
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg 1635968
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg 115ce9f
Merge branch 'feature/multi-mesh-ray-caster' of github.com:renezurbru…
renezurbruegg 636406b
lint
renezurbruegg 8ac9ad6
move track_mesh_transforms to target cfg
renezurbruegg 404bc69
migrate to pytest
renezurbruegg eef0399
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg d9a5f3a
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg d196bbd
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg f094b27
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg c764f0c
Update source/isaaclab/isaaclab/sensors/ray_caster/multi_mesh_ray_cas…
renezurbruegg bccb267
removes ununsed files
Mayankm96 3d7c8f7
adds test for instanced proxies
Mayankm96 ab7eac2
runs formatter
Mayankm96 8ae7743
adds resolve prim funcs
Mayankm96 e6cbead
Apply suggestion from @Copilot
Mayankm96 796c1a4
updates changelog
Mayankm96 9c34e95
Merge branch 'feature/prim-prop-funcs' of github.com:Mayankm96/IsaacL…
Mayankm96 41c5f7a
Merge branch 'main' into feature/multi-mesh-ray-caster
kellyguo11 371785d
Move utils to sim_utils
renezurbruegg 90ef912
rename to obtain_world_pose_from_view
renezurbruegg 616d677
lint
renezurbruegg 2d1c3c2
fix tests
renezurbruegg a289d1c
lint
renezurbruegg 20772f4
Remove is global configuration
renezurbruegg 04a7185
lint
renezurbruegg 5c927da
Merge branch 'main' into feature/prim-prop-funcs
kellyguo11 7864b21
Merge branch 'main' into fix/instanced-proxies
kellyguo11 dd61d3e
revert changes to merge prs
renezurbruegg 82819d8
Merge branch 'sim_utils' into feature/multi-mesh-ray-caster
renezurbruegg f5af366
Merge branch 'prim_utils' into feature/multi-mesh-ray-caster
renezurbruegg 50fc172
fix typo
renezurbruegg c891112
Update trimesh functions
renezurbruegg 85fbe94
Merge branch 'main' into feature/multi-mesh-ray-caster
jtigue-bdai c251913
Also lookup physics prim for sensor origin
renezurbruegg 22040a7
fix merge issue
renezurbruegg 0c2955b
update benchmarking
renezurbruegg 41924f2
formatter and fix multi mesh raycaster
pascal-roth a194b3b
image based benchmark
pascal-roth 9a0f5ab
fixes
pascal-roth 80908ed
fix md
pascal-roth 30ceb59
fix data type name
pascal-roth 4439744
fix benchmark ray caster
pascal-roth 0a832b5
plot scripts
pascal-roth e1a5780
update plot script
pascal-roth c09b9d0
update
pascal-roth b2103cb
Create agg. plot script
renezurbruegg 335dc71
Merge branch 'feature/multi-mesh-ray-caster' of github.com:renezurbru…
renezurbruegg 3c5bd81
updated plotting scripts
renezurbruegg 0d3f5be
Change output dir, disable non cached tests
renezurbruegg ee12bb3
Merge branch 'feature/multi-mesh-ray-caster' of github.com:renezurbru…
pascal-roth bc6886f
allow plot single nav environment
pascal-roth 8d84fb1
refactor camera benchmark
pascal-roth 8796999
add bash scripts
renezurbruegg 9b2871b
Merge branch 'feature/multi-mesh-ray-caster' of github.com:renezurbru…
renezurbruegg 64a9a61
add fps plot
renezurbruegg 04cd1c5
bugfix
pascal-roth ee857ce
Merge branch 'main' into feature/multi-mesh-ray-caster
pascal-roth dfc9283
Add more mesh types
renezurbruegg 8b2de9b
Add generic fan triangulation
renezurbruegg c97b616
Merge remote-tracking branch 'public/main' into upstream/feature/mult…
renezurbruegg 0dbb2da
Remove unusued benchmarks
renezurbruegg ddcc3d8
lint
renezurbruegg dfd08df
remove benchmarks
renezurbruegg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ | |
timer | ||
types | ||
warp | ||
mesh | ||
|
||
.. Rubric:: Functions | ||
|
||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,258 @@ | ||
# Copyright (c) 2022-2025, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). | ||
# All rights reserved. | ||
# | ||
# SPDX-License-Identifier: BSD-3-Clause | ||
|
||
"""Benchmark throughput of different camera implementations. | ||
|
||
This script benchmarks per-step time while varying: | ||
- camera implementation: standard camera, tiled camera, warp ray-caster camera | ||
- image resolutions (height x width) | ||
- number of environments | ||
|
||
Sensors are added to the scene config before `InteractiveScene` is constructed. | ||
Each benchmark run initializes a fresh simulation and scene and tears it down. | ||
|
||
Examples: | ||
|
||
- Benchmark all camera types across resolutions: | ||
./isaaclab.sh -p scripts/benchmarks/benchmark_camera_throughput.py \\ | ||
--num_envs 256 512 --impls standard,tiled,ray_caster \\ | ||
--resolutions 240x320,480x640 --steps 200 --warmup 20 --headless | ||
|
||
- Only standard camera at 720p: | ||
./isaaclab.sh -p scripts/benchmarks/benchmark_camera_throughput.py \\ | ||
--num_envs 256 --impls standard --resolutions 720x1280 --steps 200 --warmup 20 --headless | ||
""" | ||
|
||
"""Launch Isaac Sim Simulator first.""" | ||
|
||
import argparse | ||
import csv | ||
import os | ||
import time | ||
|
||
from isaaclab.app import AppLauncher | ||
|
||
parser = argparse.ArgumentParser(description="Benchmark throughput of different camera implementations.") | ||
parser.add_argument( | ||
"--num_envs", | ||
type=int, | ||
nargs="+", | ||
default=[256, 512, 1024], | ||
help="List of environment counts to benchmark (e.g., 256 512 1024).", | ||
) | ||
parser.add_argument( | ||
"--impls", | ||
type=str, | ||
default="standard,tiled,ray_caster", | ||
help="Comma-separated list of implementations: standard,tiled,ray_caster", | ||
) | ||
parser.add_argument( | ||
"--resolutions", | ||
type=str, | ||
default="240x320,480x640", | ||
help="Comma-separated list of HxW resolutions, e.g., 240x320,480x640", | ||
) | ||
parser.add_argument("--steps", type=int, default=500, help="Steps per run to time.") | ||
parser.add_argument("--warmup", type=int, default=50, help="Warmup steps per run before timing.") | ||
|
||
# Append AppLauncher CLI args and parse | ||
AppLauncher.add_app_launcher_args(parser) | ||
args_cli, _ = parser.parse_known_args() | ||
args_cli.enable_cameras = True | ||
|
||
# launch omniverse app | ||
app_launcher = AppLauncher(args_cli) | ||
simulation_app = app_launcher.app | ||
|
||
"""Rest everything follows.""" | ||
|
||
import isaaclab.sim as sim_utils | ||
from isaaclab.assets import ArticulationCfg, AssetBaseCfg, RigidObjectCfg | ||
from isaaclab.scene import InteractiveScene, InteractiveSceneCfg | ||
from isaaclab.sensors import CameraCfg, RayCasterCameraCfg, TiledCameraCfg, patterns | ||
from isaaclab.sim import SimulationContext | ||
from isaaclab.utils import configclass | ||
from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR | ||
|
||
# Robot config to attach sensors under a valid prim | ||
from isaaclab_assets.robots.anymal import ANYMAL_D_CFG # isort: skip | ||
|
||
|
||
def _parse_resolutions(res_str: str) -> list[tuple[int, int]]: | ||
resolutions: list[tuple[int, int]] = [] | ||
for token in [s for s in res_str.split(",") if s]: | ||
h, w = token.lower().split("x") | ||
resolutions.append((int(h), int(w))) | ||
return resolutions | ||
|
||
|
||
@configclass | ||
class CameraBenchmarkSceneCfg(InteractiveSceneCfg): | ||
"""Scene config with ground, light, robot, and one camera sensor per env.""" | ||
|
||
ground = AssetBaseCfg( | ||
prim_path="/World/ground", | ||
spawn=sim_utils.UsdFileCfg(usd_path=f"{ISAAC_NUCLEUS_DIR}/Environments/Terrains/rough_plane.usd"), | ||
) | ||
light = AssetBaseCfg( | ||
prim_path="/World/Light", | ||
spawn=sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75)), | ||
) | ||
robot: ArticulationCfg = ANYMAL_D_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot") # type: ignore[attr-defined] | ||
|
||
# one cube per environment (optional target for ray-caster camera) | ||
cube: RigidObjectCfg = RigidObjectCfg( | ||
prim_path="{ENV_REGEX_NS}/cube", | ||
spawn=sim_utils.CuboidCfg( | ||
size=(0.2, 0.2, 0.2), | ||
rigid_props=sim_utils.RigidBodyPropertiesCfg(max_depenetration_velocity=1.0, disable_gravity=True), | ||
mass_props=sim_utils.MassPropertiesCfg(mass=1.0), | ||
visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.5, 0.0, 0.0)), | ||
), | ||
init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 1.0)), | ||
) | ||
|
||
standard_camera: CameraCfg | None = None | ||
tiled_camera: TiledCameraCfg | None = None | ||
ray_caster_camera: RayCasterCameraCfg | None = None | ||
|
||
|
||
def _make_scene_cfg_standard(num_envs: int, height: int, width: int, debug_vis: bool) -> CameraBenchmarkSceneCfg: | ||
scene_cfg = CameraBenchmarkSceneCfg(num_envs=num_envs, env_spacing=2.0) | ||
scene_cfg.standard_camera = CameraCfg( | ||
prim_path="{ENV_REGEX_NS}/Camera", | ||
height=height, | ||
width=width, | ||
data_types=["rgb"], | ||
spawn=sim_utils.PinholeCameraCfg( | ||
focal_length=24.0, focus_distance=400.0, horizontal_aperture=20.955, clipping_range=(0.1, 1e4) | ||
), | ||
debug_vis=debug_vis, | ||
) | ||
return scene_cfg | ||
|
||
|
||
def _make_scene_cfg_tiled(num_envs: int, height: int, width: int, debug_vis: bool) -> CameraBenchmarkSceneCfg: | ||
scene_cfg = CameraBenchmarkSceneCfg(num_envs=num_envs, env_spacing=2.0) | ||
scene_cfg.tiled_camera = TiledCameraCfg( | ||
prim_path="{ENV_REGEX_NS}/TiledCamera", | ||
height=height, | ||
width=width, | ||
data_types=["rgb", "depth"], | ||
spawn=sim_utils.PinholeCameraCfg( | ||
focal_length=24.0, focus_distance=400.0, horizontal_aperture=20.955, clipping_range=(0.1, 1e4) | ||
), | ||
debug_vis=debug_vis, | ||
) | ||
return scene_cfg | ||
|
||
|
||
def _make_scene_cfg_ray_caster(num_envs: int, height: int, width: int, debug_vis: bool) -> CameraBenchmarkSceneCfg: | ||
scene_cfg = CameraBenchmarkSceneCfg(num_envs=num_envs, env_spacing=2.0) | ||
scene_cfg.ray_caster_camera = RayCasterCameraCfg( | ||
prim_path="{ENV_REGEX_NS}/Robot/base", # attach to existing prim | ||
mesh_prim_paths=["/World/ground", "/World/envs/env_.*/cube"], | ||
pattern_cfg=patterns.PinholeCameraPatternCfg( | ||
focal_length=24.0, horizontal_aperture=20.955, height=height, width=width | ||
), | ||
data_types=["distance_to_image_plane"], | ||
debug_vis=debug_vis, | ||
) | ||
return scene_cfg | ||
|
||
|
||
import isaacsim.core.utils.stage as stage_utils | ||
|
||
|
||
def _setup_scene(scene_cfg: CameraBenchmarkSceneCfg) -> tuple[SimulationContext, InteractiveScene, float]: | ||
# Create a new stage to avoid residue across runs | ||
stage_utils.create_new_stage() | ||
sim_cfg = sim_utils.SimulationCfg(device=args_cli.device) | ||
sim = SimulationContext(sim_cfg) | ||
sim.set_camera_view((2.5, 0.0, 4.0), (0.0, 0.0, 2.0)) | ||
setup_time_begin = time.perf_counter_ns() | ||
scene = InteractiveScene(scene_cfg) | ||
setup_time_end = time.perf_counter_ns() | ||
print(f"[INFO]: Scene creation time: {(setup_time_end - setup_time_begin) / 1e6:.2f} ms") | ||
reset_time_begin = time.perf_counter_ns() | ||
sim.reset() | ||
reset_time_end = time.perf_counter_ns() | ||
print(f"[INFO]: Sim start time: {(reset_time_end - reset_time_begin) / 1e6:.2f} ms") | ||
return sim, scene, sim.get_physics_dt() | ||
|
||
|
||
def main(): | ||
impls = [s.strip() for s in args_cli.impls.split(",") if s] | ||
resolutions = _parse_resolutions(args_cli.resolutions) | ||
results: list[dict[str, object]] = [] | ||
|
||
def _bench(num_envs: int, impl: str, height: int, width: int): | ||
if impl == "standard": | ||
scene_cfg = _make_scene_cfg_standard(num_envs, height, width, debug_vis=not args_cli.headless) | ||
sim, scene, sim_dt = _setup_scene(scene_cfg) | ||
camera_obj = scene["standard_camera"] | ||
label = "StandardCamera" | ||
elif impl == "tiled": | ||
scene_cfg = _make_scene_cfg_tiled(num_envs, height, width, debug_vis=not args_cli.headless) | ||
sim, scene, sim_dt = _setup_scene(scene_cfg) | ||
camera_obj = scene["tiled_camera"] | ||
label = "TiledCamera" | ||
elif impl == "ray_caster": | ||
scene_cfg = _make_scene_cfg_ray_caster(num_envs, height, width, debug_vis=not args_cli.headless) | ||
sim, scene, sim_dt = _setup_scene(scene_cfg) | ||
camera_obj = scene["ray_caster_camera"] | ||
label = "RayCasterCamera" | ||
else: | ||
raise ValueError(f"Unknown impl: {impl}") | ||
|
||
# Warmup | ||
for _ in range(args_cli.warmup): | ||
sim.step() | ||
camera_obj.update(dt=sim_dt) | ||
# Timing | ||
t0 = time.perf_counter_ns() | ||
for _ in range(args_cli.steps): | ||
sim.step() | ||
camera_obj.update(dt=sim_dt) | ||
t1 = time.perf_counter_ns() | ||
per_step_ms = (t1 - t0) / args_cli.steps / 1e6 | ||
print(f"[INFO]: {label}: {num_envs} envs, res={height}x{width}, per-step={per_step_ms:.3f} ms") | ||
results.append({ | ||
"impl": impl, | ||
"num_envs": num_envs, | ||
"height": height, | ||
"width": width, | ||
"per_step_ms": float(per_step_ms), | ||
}) | ||
# Teardown | ||
sim.clear_instance() | ||
|
||
for num_envs in args_cli.num_envs: | ||
for impl in impls: | ||
print(f"\n[INFO]: Benchmarking {impl} cameras with {num_envs} envs") | ||
for h, w in resolutions: | ||
_bench(num_envs, impl, h, w) | ||
|
||
# Save results | ||
os.makedirs("outputs/benchmarks", exist_ok=True) | ||
csv_path = os.path.join("outputs/benchmarks", "camera_throughput.csv") | ||
md_path = os.path.join("outputs/benchmarks", "camera_throughput.md") | ||
|
||
fieldnames = ["impl", "num_envs", "height", "width", "per_step_ms"] | ||
with open(csv_path, "w", newline="") as f: | ||
writer = csv.DictWriter(f, fieldnames=fieldnames) | ||
writer.writeheader() | ||
writer.writerows(results) | ||
with open(md_path, "w") as f: | ||
f.write("| impl | num_envs | height | width | per_step_ms |\n") | ||
f.write("|---|---:|---:|---:|---:|\n") | ||
for r in results: | ||
f.write(f"| {r['impl']} | {r['num_envs']} | {r['height']} | {r['width']} | {r['per_step_ms']:.3f} |\n") | ||
print(f"[INFO]: Saved benchmark results to {csv_path} and {md_path}") | ||
|
||
|
||
if __name__ == "__main__": | ||
main() | ||
simulation_app.close() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
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.
Nice. I think we will need to consolidate the terrain mesh utils also in this to just have one place with bunch of stuff. Right now things have become spreaded out...
Future us task for sure.