From 631dc61c43027e08ca763eae8b1e8a9bb201b9aa Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Fri, 8 May 2026 21:01:17 +0000 Subject: [PATCH 01/31] wip --- docs/source/features/visualization.rst | 2 + .../isaaclab/isaaclab/envs/direct_marl_env.py | 1 + .../isaaclab/isaaclab/envs/direct_rl_env.py | 1 + .../isaaclab/envs/manager_based_env.py | 1 + .../isaaclab/envs/utils/camera_view.py | 202 ++++++++++++++++++ .../physics/base_scene_data_provider.py | 19 ++ .../isaaclab/sim/simulation_context.py | 9 + .../isaaclab/visualizers/visualizer_cfg.py | 23 +- .../renderers/newton_warp_renderer.py | 6 +- .../kit/kit_visualizer.py | 129 ++++++++++- .../newton/newton_visualizer.py | 151 +++++++++++-- 11 files changed, 520 insertions(+), 24 deletions(-) create mode 100644 source/isaaclab/isaaclab/envs/utils/camera_view.py diff --git a/docs/source/features/visualization.rst b/docs/source/features/visualization.rst index c68816c0da00..95225170f737 100644 --- a/docs/source/features/visualization.rst +++ b/docs/source/features/visualization.rst @@ -287,6 +287,7 @@ Omniverse Visualizer - Live plots for monitoring training metrics - Full Isaac Sim rendering capabilities and tooling - Visualization markers for debugging (arrows, frames, object targets, etc.) +- Tiled RGB camera views from Isaac Lab ``Camera`` sensors in a dockable image panel **Core Configuration:** @@ -320,6 +321,7 @@ Newton Visualizer - Adjustable update frequency for performance tuning - Some customizable rendering options (shadows, sky, wireframe) - Visualization markers (joints, contacts, springs, COM, debug markers) +- Tiled RGB camera views via Newton ``Viewer.log_image`` from Isaac Lab ``Camera`` sensors **Interactive Controls:** diff --git a/source/isaaclab/isaaclab/envs/direct_marl_env.py b/source/isaaclab/isaaclab/envs/direct_marl_env.py index b8fda8cf0986..f512ad355eb5 100644 --- a/source/isaaclab/isaaclab/envs/direct_marl_env.py +++ b/source/isaaclab/isaaclab/envs/direct_marl_env.py @@ -145,6 +145,7 @@ def _init_sim(self, render_mode: str | None = None, **kwargs): with use_stage(self.sim.stage): self.scene = InteractiveScene(self.cfg.scene) self._setup_scene() + self.sim.register_interactive_scene(self.scene) print("[INFO]: Scene manager: ", self.scene) # set up camera viewport controller diff --git a/source/isaaclab/isaaclab/envs/direct_rl_env.py b/source/isaaclab/isaaclab/envs/direct_rl_env.py index 9251eb0fe817..62c8e5604459 100644 --- a/source/isaaclab/isaaclab/envs/direct_rl_env.py +++ b/source/isaaclab/isaaclab/envs/direct_rl_env.py @@ -150,6 +150,7 @@ def _init_sim(self, render_mode: str | None = None, **kwargs): with use_stage(self.sim.stage): self.scene = InteractiveScene(self.cfg.scene) self._setup_scene() + self.sim.register_interactive_scene(self.scene) print("[INFO]: Scene manager: ", self.scene) # set up camera viewport controller diff --git a/source/isaaclab/isaaclab/envs/manager_based_env.py b/source/isaaclab/isaaclab/envs/manager_based_env.py index 92db9ad117b5..807a37cba9cd 100644 --- a/source/isaaclab/isaaclab/envs/manager_based_env.py +++ b/source/isaaclab/isaaclab/envs/manager_based_env.py @@ -169,6 +169,7 @@ def _init_sim(self): # set the stage context for scene creation steps which use the stage with use_stage(self.sim.stage): self.scene = InteractiveScene(self.cfg.scene) + self.sim.register_interactive_scene(self.scene) print("[INFO]: Scene manager: ", self.scene) # set up camera viewport controller diff --git a/source/isaaclab/isaaclab/envs/utils/camera_view.py b/source/isaaclab/isaaclab/envs/utils/camera_view.py new file mode 100644 index 000000000000..ead00f530906 --- /dev/null +++ b/source/isaaclab/isaaclab/envs/utils/camera_view.py @@ -0,0 +1,202 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Helpers for visualizer and recorder camera image views.""" + +from __future__ import annotations + +import math +import random +from dataclasses import dataclass +from typing import Any + +import numpy as np +import torch + +import isaaclab.sim as sim_utils +from isaaclab.sensors.camera import Camera, CameraCfg + +_GENERATED_CAMERA_NAME = "VisualizerCamera" + + +@dataclass +class ResolvedCameraView: + """Camera sensor plus selected sensor indices.""" + + camera: Camera + env_indices: list[int] + owns_camera: bool = False + generated_prim_paths: list[str] | None = None + + +def resolve_tiled_env_indices(num_envs: int, tiled_cam_num: int, env_indices: list[int] | None) -> list[int]: + """Resolve env ids for tiled camera view once at visualizer initialization.""" + if num_envs <= 0: + return [] + max_count = min(max(1, int(tiled_cam_num)), num_envs) + if env_indices is not None: + return [idx for idx in env_indices if 0 <= int(idx) < num_envs][:max_count] + return sorted(random.sample(range(num_envs), max_count)) + + +def resolve_mono_env_index(num_envs: int) -> list[int]: + """Return env_0 for mono sensor camera views.""" + return [0] if num_envs > 0 else [] + + +def env_path_from_template(path_template: str, env_id: int) -> str: + """Resolve common env wildcard/template spellings to a concrete env path.""" + path = path_template + if "%d" in path: + return path % env_id + if "{}" in path: + return path.format(env_id) + path = path.replace("/World/envs/*", f"/World/envs/env_{env_id}") + path = path.replace("/World/envs/env_.*", f"/World/envs/env_{env_id}") + path = path.replace("/World/envs/env_.*/", f"/World/envs/env_{env_id}/") + return path + + +def _camera_concrete_paths(camera: Camera) -> list[str]: + view = getattr(camera, "_view", None) + prims = getattr(view, "prims", None) + if not prims: + return [] + return [prim.GetPath().pathString for prim in prims] + + +def find_camera_by_prim_path(camera_sensors: dict[str, Camera], cam_prim_path: str, env_indices: list[int]) -> Camera: + """Find a scene-owned Camera by config template or concrete camera prim paths.""" + wanted = {env_path_from_template(cam_prim_path, env_id) for env_id in env_indices} + for camera in camera_sensors.values(): + if getattr(camera.cfg, "prim_path", None) == cam_prim_path: + return camera + concrete = set(_camera_concrete_paths(camera)) + if wanted and wanted.issubset(concrete): + return camera + stage = sim_utils.get_current_stage() + stage_matches = [path for path in wanted if stage.GetPrimAtPath(path).IsValid()] if stage is not None else [] + if stage_matches: + raise RuntimeError( + f"cam_prim_path={cam_prim_path!r} matched USD camera prims, but no Isaac Lab Camera sensor owns them. " + "Add the camera to scene.sensors or use cam_source='cfg'." + ) + raise RuntimeError(f"No Isaac Lab Camera sensor matched cam_prim_path={cam_prim_path!r}.") + + +def ensure_camera_initialized(camera: Camera) -> None: + """Initialize a visualizer-owned Camera created after the normal physics-ready callback.""" + if not camera.is_initialized: + camera._initialize_callback(None) + + +def create_visualizer_camera( + *, + num_envs: int, + camera_name: str = _GENERATED_CAMERA_NAME, + width: int, + height: int, + renderer_cfg: Any, +) -> tuple[Camera, list[str]]: + """Create an internal RGB Camera sensor for visualizer image views.""" + spawn = sim_utils.PinholeCameraCfg( + focal_length=24.0, + focus_distance=400.0, + horizontal_aperture=20.955, + clipping_range=(0.1, 1.0e5), + ) + generated_paths = [f"/World/envs/env_{env_id}/{camera_name}" for env_id in range(int(num_envs))] + for path in generated_paths: + if len(sim_utils.find_matching_prims(path)) == 0: + spawn.func(path, spawn, translation=(0.0, 0.0, 0.0), orientation=(0.0, 0.0, 0.0, 1.0)) + cfg = CameraCfg( + prim_path=f"/World/envs/env_.*/{camera_name}", + update_period=0.0, + height=int(height), + width=int(width), + data_types=["rgb"], + spawn=None, + renderer_cfg=renderer_cfg, + ) + camera = Camera(cfg) + ensure_camera_initialized(camera) + return camera, generated_paths + + +def remove_generated_prims(prim_paths: list[str] | None) -> None: + """Remove visualizer-owned camera prims from the current stage.""" + if not prim_paths: + return + stage = sim_utils.get_current_stage() + if stage is None: + return + for path in prim_paths: + prim = stage.GetPrimAtPath(path) + if prim.IsValid(): + stage.RemovePrim(path) + + +def camera_rgb_batch(camera: Camera, env_indices: list[int]) -> torch.Tensor: + """Return RGB output for selected env indices.""" + rgb = camera.data.output["rgb"] + if env_indices: + index = torch.tensor(env_indices, dtype=torch.long, device=rgb.device) + return rgb.index_select(0, index) + return rgb + + +def compose_rgb_grid(rgb_batch: torch.Tensor) -> np.ndarray: + """Compose an RGB batch into a near-square uint8 image grid for Kit UI display.""" + rgb_np = rgb_batch.detach().contiguous().cpu().numpy() + if rgb_np.ndim == 3: + return rgb_np[..., :3] + n, h, w, _ = rgb_np.shape + cols = max(1, math.ceil(math.sqrt(n))) + rows = math.ceil(n / cols) + pad = rows * cols - n + if pad > 0: + rgb_np = np.concatenate([rgb_np[..., :3], np.zeros((pad, h, w, 3), dtype=rgb_np.dtype)], axis=0) + else: + rgb_np = rgb_np[..., :3] + return rgb_np.reshape(rows, cols, h, w, 3).transpose(0, 2, 1, 3, 4).reshape(rows * h, cols * w, 3) + + +def compute_tile_resolution(window_width: int, window_height: int, num_tiles: int) -> tuple[int, int]: + """Derive a conservative per-tile resolution from the visualizer window.""" + cols = max(1, math.ceil(math.sqrt(max(1, num_tiles)))) + rows = math.ceil(max(1, num_tiles) / cols) + return max(1, int(window_width) // cols), max(1, int(window_height) // rows) + + +def prim_world_positions(stage: Any, prim_path_template: str, env_indices: list[int]) -> torch.Tensor: + """Return world-space translations for concrete prim paths resolved from env ids.""" + from pxr import UsdGeom + + xform_cache = UsdGeom.XformCache() + positions = [] + for env_id in env_indices: + prim_path = env_path_from_template(prim_path_template, env_id) + prim = stage.GetPrimAtPath(prim_path) + if not prim.IsValid(): + raise RuntimeError(f"cam_follow_prim_path resolved to missing prim: {prim_path!r}.") + transform = xform_cache.GetLocalToWorldTransform(prim) + translation = transform.ExtractTranslation() + positions.append((float(translation[0]), float(translation[1]), float(translation[2]))) + return torch.tensor(positions, dtype=torch.float32) + + +def apply_camera_view_from_origins( + camera: Camera, + origins: torch.Tensor, + eye: tuple[float, float, float], + lookat: tuple[float, float, float], + env_ids: list[int] | None = None, +) -> None: + """Set camera poses from origins plus relative eye/lookat offsets.""" + device = camera.device + origins = origins.to(device=device) + eye_offset = torch.tensor(eye, dtype=torch.float32, device=device).unsqueeze(0) + lookat_offset = torch.tensor(lookat, dtype=torch.float32, device=device).unsqueeze(0) + camera.set_world_poses_from_view(origins + eye_offset, origins + lookat_offset, env_ids=env_ids) diff --git a/source/isaaclab/isaaclab/physics/base_scene_data_provider.py b/source/isaaclab/isaaclab/physics/base_scene_data_provider.py index 9760a71d25ba..32337cca17b9 100644 --- a/source/isaaclab/isaaclab/physics/base_scene_data_provider.py +++ b/source/isaaclab/isaaclab/physics/base_scene_data_provider.py @@ -14,6 +14,25 @@ class BaseSceneDataProvider(ABC): """Backend-agnostic scene data provider interface.""" + def set_interactive_scene(self, scene: Any) -> None: + """Attach the interactive scene so visualizers can discover scene-owned sensors.""" + self._interactive_scene = scene + + def get_interactive_scene(self) -> Any | None: + """Return the registered interactive scene, if available.""" + return getattr(self, "_interactive_scene", None) + + def get_camera_sensors(self) -> dict[str, Any]: + """Return Isaac Lab camera sensors keyed by scene sensor name.""" + scene = getattr(self, "_interactive_scene", None) + if scene is None: + return {} + try: + from isaaclab.sensors.camera import Camera + except ImportError: + return {} + return {name: sensor for name, sensor in scene.sensors.items() if isinstance(sensor, Camera)} + @abstractmethod def update(self) -> None: """Refresh any cached scene data (full model/state).""" diff --git a/source/isaaclab/isaaclab/sim/simulation_context.py b/source/isaaclab/isaaclab/sim/simulation_context.py index d5ef6f64e9c5..16c29154fbd2 100644 --- a/source/isaaclab/isaaclab/sim/simulation_context.py +++ b/source/isaaclab/isaaclab/sim/simulation_context.py @@ -181,6 +181,7 @@ def __init__(self, cfg: SimulationCfg | None = None): # their own backend args. Empty dict until :meth:`InteractiveScene.clone_environments` # runs. self._clone_plans: dict[str, ClonePlan] = {} + self._interactive_scene = None self._visualizer_step_counter = 0 # Default visualization dt used before/without visualizer initialization. physics_dt = getattr(self.cfg.physics, "dt", None) @@ -625,8 +626,16 @@ def initialize_visualizers(self) -> None: def initialize_scene_data_provider(self) -> BaseSceneDataProvider: if self._scene_data_provider is None: self._scene_data_provider = SceneDataProvider(self.stage, self) + if self._interactive_scene is not None: + self._scene_data_provider.set_interactive_scene(self._interactive_scene) return self._scene_data_provider + def register_interactive_scene(self, scene) -> None: + """Register the active scene so scene data providers can expose scene-owned sensors.""" + self._interactive_scene = scene + if self._scene_data_provider is not None: + self._scene_data_provider.set_interactive_scene(scene) + def get_scene_data_requirements(self) -> SceneDataRequirement: """Return scene-data requirements resolved from visualizers/renderers.""" return self._scene_data_requirements diff --git a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py index 1ee4cde038b5..af0815a02aee 100644 --- a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py +++ b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py @@ -41,10 +41,27 @@ class VisualizerCfg: """Initial camera look-at point (x, y, z) in world coordinates.""" cam_source: Literal["cfg", "prim_path"] = "cfg" - """Camera source mode: 'cfg' uses eye/lookat, 'prim_path' follows a camera prim.""" + """Camera source mode: 'cfg' uses eye/lookat, 'prim_path' uses Isaac Lab camera sensor output.""" - cam_prim_path: str = "/World/envs/env_0/Camera" - """Absolute USD path to a camera prim when cam_source='prim_path'.""" + cam_prim_path: str = "/World/envs/*/Camera" + """Camera prim path or env wildcard path when cam_source='prim_path'.""" + + cam_follow_prim_path: str | None = None + """Optional prim path whose world pose anchors generated camera views. + + When set, camera controls are disabled and eye/lookat are interpreted relative + to the matched follow prim. Wildcards resolve to env_0 for mono view and to + selected envs for tiled view. + """ + + tiled_cam_view: bool = False + """Enable a non-interactive tiled camera image view.""" + + tiled_cam_num: int = 24 + """Number of camera tiles to show when tiled_cam_env_indices is None.""" + + tiled_cam_env_indices: list[int] | None = None + """Env ids to show in tiled camera view; if None, sample tiled_cam_num envs once at initialization.""" max_visible_envs: int | None = None """Upper bound on how many envs are shown. diff --git a/source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py b/source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py index a02d820f2951..dbe86f7499ef 100644 --- a/source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py +++ b/source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py @@ -220,8 +220,12 @@ def update_camera( def render(self, render_data: RenderData): """Render and write to output buffers. See :meth:`~isaaclab.renderers.base_renderer.BaseRenderer.render`.""" + state = self.get_scene_data_provider().get_newton_state() + # Newton's raytraced camera kernels require fresh BVHs before shape/particle rendering. + newton.geometry.build_bvh_shape(self.newton_sensor.model, state) + newton.geometry.build_bvh_particle(self.newton_sensor.model, state) self.newton_sensor.update( - self.get_scene_data_provider().get_newton_state(), + state, render_data.camera_transforms, render_data.camera_rays, color_image=render_data.outputs.color_image, diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py index 701a21c79984..a84317eb1cdd 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py @@ -13,6 +13,18 @@ from pxr import Gf, Usd, UsdGeom, Vt +from isaaclab.envs.utils.camera_view import ( + apply_camera_view_from_origins, + camera_rgb_batch, + compose_rgb_grid, + compute_tile_resolution, + create_visualizer_camera, + find_camera_by_prim_path, + prim_world_positions, + remove_generated_prims, + resolve_mono_env_index, + resolve_tiled_env_indices, +) from isaaclab.app.settings_manager import get_settings_manager from isaaclab.visualizers.base_visualizer import BaseVisualizer @@ -52,6 +64,13 @@ def __init__(self, cfg: KitVisualizerCfg): self._runtime_headless = bool(cfg.headless) # USD path for the viewport's active camera, refreshed after setup (used by CI/tests). self._controlled_camera_path: str | None = None + self._camera_sensor = None + self._camera_sensor_indices: list[int] = [] + self._camera_env_indices: list[int] = [] + self._camera_is_owned = False + self._generated_camera_prim_paths: list[str] = [] + self._camera_image_provider = None + self._camera_image_window = None # ---- Lifecycle ------------------------------------------------------------------------ @@ -102,6 +121,7 @@ def initialize(self, scene_data_provider: BaseSceneDataProvider) -> None: ("headless", self._runtime_headless), ], ) + self._setup_camera_sensor_view(num_envs_meta) self._is_initialized = True @@ -128,6 +148,7 @@ def step(self, dt: float) -> None: settings.set_bool("/app/player/playSimulations", True) except (ImportError, AttributeError) as exc: logger.debug("[KitVisualizer] App update skipped: %s", exc) + self._update_camera_image_panel() # Markers (VisualizationMarkers) are often created or resized to num_envs only after the first # simulation / debug-vis step; re-apply PointInstancer invisibleIds each step when partial viz is on. self._refresh_partial_viz_point_instancers_if_needed() @@ -137,6 +158,14 @@ def close(self) -> None: if not self._is_initialized: return self._restore_env_visibility() + if self._camera_sensor is not None and self._camera_is_owned: + cleanup = getattr(self._camera_sensor, "__del__", None) + if callable(cleanup): + cleanup() + remove_generated_prims(self._generated_camera_prim_paths) + self._camera_sensor = None + self._camera_image_provider = None + self._camera_image_window = None self._simulation_app = None self._viewport_window = None self._viewport_api = None @@ -289,7 +318,10 @@ def _setup_viewport(self) -> None: self._refresh_controlled_camera_path() return self._viewport_api = self._viewport_window.viewport_api - if self.cfg.cam_source == "prim_path": + if self._uses_camera_sensor_view(): + # Camera sensor image views are shown in a non-interactive image panel. + pass + elif self.cfg.cam_source == "prim_path": if not self._set_active_camera_path(self.cfg.cam_prim_path): raise RuntimeError( "[KitVisualizer] cam_source='prim_path' requires a valid cam_prim_path. " @@ -299,6 +331,101 @@ def _setup_viewport(self) -> None: self._apply_cfg_camera_pose_if_configured() self._refresh_controlled_camera_path() + def _uses_camera_sensor_view(self) -> bool: + """Return whether Kit should display a camera sensor image instead of an interactive viewport camera.""" + return bool(self.cfg.tiled_cam_view or self.cfg.cam_source == "prim_path" or self.cfg.cam_follow_prim_path) + + def _setup_camera_sensor_view(self, num_envs: int) -> None: + """Resolve or create the Camera sensor backing non-interactive image views.""" + if not self._uses_camera_sensor_view() or self._runtime_headless: + return + env_ids = ( + resolve_tiled_env_indices(num_envs, self.cfg.tiled_cam_num, self.cfg.tiled_cam_env_indices) + if self.cfg.tiled_cam_view + else resolve_mono_env_index(num_envs) + ) + self._camera_env_indices = env_ids + if self.cfg.cam_source == "prim_path": + cameras = self._scene_data_provider.get_camera_sensors() + self._camera_sensor = find_camera_by_prim_path(cameras, self.cfg.cam_prim_path, env_ids) + self._camera_sensor_indices = env_ids + else: + from isaaclab_physx.renderers import IsaacRtxRendererCfg + + count = max(1, len(env_ids)) + tile_w, tile_h = compute_tile_resolution(self.cfg.window_width, self.cfg.window_height, count) + self._camera_sensor, self._generated_camera_prim_paths = create_visualizer_camera( + num_envs=num_envs, + width=tile_w, + height=tile_h, + renderer_cfg=IsaacRtxRendererCfg(), + ) + self._camera_sensor_indices = env_ids + self._camera_is_owned = True + self._update_owned_camera_poses() + self._setup_camera_image_window() + + def _setup_camera_image_window(self) -> None: + """Create a dockable Kit UI image panel for camera sensor RGB output.""" + import omni.ui + + title = self.cfg.viewport_name or "Visualizer Camera" + self._camera_image_provider = omni.ui.ByteImageProvider() + self._camera_image_window = omni.ui.Window(title, width=self.cfg.window_width, height=self.cfg.window_height) + with self._camera_image_window.frame: + omni.ui.ImageWithProvider(self._camera_image_provider) + + dock_position_name = self.cfg.dock_position.upper() + dock_position_map = { + "LEFT": omni.ui.DockPosition.LEFT, + "RIGHT": omni.ui.DockPosition.RIGHT, + "BOTTOM": omni.ui.DockPosition.BOTTOM, + "SAME": omni.ui.DockPosition.SAME, + } + asyncio.ensure_future( + self._dock_image_window_async(title, dock_position_map.get(dock_position_name, omni.ui.DockPosition.SAME)) + ) + + async def _dock_image_window_async(self, window_name: str, dock_position) -> None: + """Dock the camera image panel next to the main viewport.""" + import omni.kit.app + import omni.ui + + image_window = None + for _ in range(10): + image_window = omni.ui.Workspace.get_window(window_name) + if image_window: + break + await omni.kit.app.get_app().next_update_async() + main_viewport = omni.ui.Workspace.get_window("Viewport") + if image_window is not None and main_viewport is not None and image_window != main_viewport: + image_window.dock_in(main_viewport, dock_position, 0.5) + + def _update_owned_camera_poses(self) -> None: + """Update generated camera poses from env origins or follow prims.""" + if self._camera_sensor is None or not self._camera_is_owned: + return + if self.cfg.cam_follow_prim_path: + origins = prim_world_positions( + self._scene_data_provider.get_usd_stage(), self.cfg.cam_follow_prim_path, self._camera_env_indices + ) + else: + env_origins = self._scene_data_provider.get_interactive_scene().env_origins + origins = env_origins[self._camera_env_indices].detach().cpu() + apply_camera_view_from_origins( + self._camera_sensor, origins, self.cfg.eye, self.cfg.lookat, self._camera_env_indices + ) + + def _update_camera_image_panel(self) -> None: + """Refresh the non-interactive Kit image panel from camera RGB output.""" + if self._camera_sensor is None or self._camera_image_provider is None: + return + if self._camera_is_owned and self.cfg.cam_follow_prim_path: + self._update_owned_camera_poses() + rgb = camera_rgb_batch(self._camera_sensor, self._camera_sensor_indices) + image = compose_rgb_grid(rgb) if self.cfg.tiled_cam_view else rgb[0].detach().contiguous().cpu().numpy() + self._camera_image_provider.set_data_array(image.astype("uint8", copy=False), [image.shape[1], image.shape[0]]) + def _refresh_controlled_camera_path(self) -> None: """Cache :attr:`_controlled_camera_path` from the active viewport (or default persp).""" if self._viewport_api is not None: diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py index b548a3e5f4f3..410684668df6 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py @@ -11,9 +11,21 @@ from typing import TYPE_CHECKING import numpy as np +import torch import warp as wp from newton.viewer import ViewerGL +from isaaclab.envs.utils.camera_view import ( + apply_camera_view_from_origins, + camera_rgb_batch, + compute_tile_resolution, + create_visualizer_camera, + find_camera_by_prim_path, + prim_world_positions, + remove_generated_prims, + resolve_mono_env_index, + resolve_tiled_env_indices, +) from isaaclab.visualizers.base_visualizer import BaseVisualizer from isaaclab_visualizers.newton_adapter import apply_viewer_visible_worlds, resolve_visible_env_indices @@ -226,6 +238,11 @@ def _render_left_panel(self): except Exception as exc: logger.debug("[NewtonVisualizer] Rendering color controls failed: %s", exc) + # Newton's ImageLogger owns camera-output image windows. Since Isaac Lab overrides + # ViewerGL's left panel, explicitly keep the logged-image selector and draw path. + if getattr(self, "_image_logger", None) is not None: + self._image_logger.draw_controls() + imgui.set_next_item_open(True, imgui.Cond_.appearing) if imgui.collapsing_header("Camera"): imgui.separator() @@ -246,6 +263,8 @@ def _render_left_panel(self): imgui.text("ESC - Exit") imgui.end() + if getattr(self, "_image_logger", None) is not None: + self._image_logger.draw() return @@ -270,6 +289,12 @@ def __init__(self, cfg: NewtonVisualizerCfg): self._last_camera_pose: tuple[tuple[float, float, float], tuple[float, float, float]] | None = None self._headless_no_viewer = False self._resolved_visible_env_ids: list[int] | None = None + self._camera_sensor = None + self._camera_sensor_indices: list[int] = [] + self._camera_env_indices: list[int] = [] + self._camera_is_owned = False + self._generated_camera_prim_paths: list[str] = [] + self._logged_camera_stats = False def initialize(self, scene_data_provider: BaseSceneDataProvider) -> None: """Initialize viewer resources and bind scene data provider. @@ -314,7 +339,12 @@ def initialize(self, scene_data_provider: BaseSceneDataProvider) -> None: num_envs=num_envs, ) self._viewer.set_world_offsets((0.0, 0.0, 0.0)) - initial_pose = self._resolve_initial_camera_pose() + # Sensor-backed camera views are non-interactive image panels; keep the GL camera at cfg pose. + initial_pose = ( + self._resolve_cfg_camera_pose("NewtonVisualizer") + if self._uses_camera_sensor_view() + else self._resolve_initial_camera_pose() + ) self._apply_camera_pose(initial_pose) self._viewer.up_axis = 2 # Z-up @@ -338,6 +368,7 @@ def initialize(self, scene_data_provider: BaseSceneDataProvider) -> None: self._viewer.renderer._light_color = self._viewer._coerce_color3(self.cfg.light_color) self._resolved_visible_env_ids = resolve_visible_env_indices(self._env_ids, self.cfg.max_visible_envs, num_envs) + self._setup_camera_sensor_view(num_envs) num_visualized_envs = ( len(self._resolved_visible_env_ids) if self._resolved_visible_env_ids is not None else num_envs ) @@ -374,7 +405,7 @@ def step(self, dt: float) -> None: self._state = self._scene_data_provider.get_newton_state() return - if self.cfg.cam_source == "prim_path": + if self.cfg.cam_source == "prim_path" and not self._uses_camera_sensor_view(): self._update_camera_from_usd_path() self._state = self._scene_data_provider.get_newton_state() @@ -395,26 +426,31 @@ def step(self, dt: float) -> None: try: if not self._viewer.is_paused(): self._viewer.begin_frame(self._sim_time) - if self._state is not None: - body_q = getattr(self._state, "body_q", None) - if hasattr(body_q, "shape") and body_q.shape[0] == 0: - self._viewer.end_frame() - return - self._viewer.log_state(self._state) - if contacts is not None and hasattr(self._viewer, "log_contacts"): + try: + if self._state is not None: + body_q = getattr(self._state, "body_q", None) + if hasattr(body_q, "shape") and body_q.shape[0] == 0: + return + self._viewer.log_state(self._state) + if contacts is not None and hasattr(self._viewer, "log_contacts"): + try: + self._viewer.log_contacts(contacts, self._state) + except RuntimeError as exc: + logger.debug(f"[NewtonVisualizer] Failed to log contacts: {exc}") + if self.cfg.enable_markers: + render_newton_visualization_markers( + self._viewer, self._resolved_visible_env_ids, num_envs=num_envs + ) try: - self._viewer.log_contacts(contacts, self._state) - except RuntimeError as exc: - logger.debug(f"[NewtonVisualizer] Failed to log contacts: {exc}") - if self.cfg.enable_markers: - render_newton_visualization_markers( - self._viewer, self._resolved_visible_env_ids, num_envs=num_envs - ) - self._viewer.end_frame() + self._log_camera_sensor_image() + except Exception as exc: + logger.warning("[NewtonVisualizer] Tiled camera image update failed: %s", exc) + finally: + self._viewer.end_frame() else: self._viewer._update() - except Exception as exc: - logger.debug("[NewtonVisualizer] Viewer update failed: %s", exc) + except Exception: + logger.exception("[NewtonVisualizer] Viewer update failed.") def close(self) -> None: """Release viewer resources.""" @@ -422,6 +458,12 @@ def close(self) -> None: return if self._viewer is not None: self._viewer = None + if self._camera_sensor is not None and self._camera_is_owned: + cleanup = getattr(self._camera_sensor, "__del__", None) + if callable(cleanup): + cleanup() + remove_generated_prims(self._generated_camera_prim_paths) + self._camera_sensor = None self._is_closed = True def is_running(self) -> bool: @@ -454,6 +496,77 @@ def _resolve_initial_camera_pose(self) -> tuple[tuple[float, float, float], tupl ) return self._resolve_cfg_camera_pose("NewtonVisualizer") + def _uses_camera_sensor_view(self) -> bool: + """Return whether the visualizer displays camera sensor images instead of interactive camera controls.""" + return bool(self.cfg.tiled_cam_view or self.cfg.cam_source == "prim_path" or self.cfg.cam_follow_prim_path) + + def _setup_camera_sensor_view(self, num_envs: int) -> None: + """Resolve or create the camera sensor used by non-interactive image views.""" + if not self._uses_camera_sensor_view(): + return + env_ids = ( + resolve_tiled_env_indices(num_envs, self.cfg.tiled_cam_num, self.cfg.tiled_cam_env_indices) + if self.cfg.tiled_cam_view + else resolve_mono_env_index(num_envs) + ) + self._camera_env_indices = env_ids + if self.cfg.cam_source == "prim_path": + cameras = self._scene_data_provider.get_camera_sensors() + self._camera_sensor = find_camera_by_prim_path(cameras, self.cfg.cam_prim_path, env_ids) + self._camera_sensor_indices = env_ids + return + + from isaaclab_newton.renderers import NewtonWarpRendererCfg + + count = max(1, len(env_ids)) + tile_w, tile_h = compute_tile_resolution(self.cfg.window_width, self.cfg.window_height, count) + self._camera_sensor, self._generated_camera_prim_paths = create_visualizer_camera( + num_envs=num_envs, + width=tile_w, + height=tile_h, + renderer_cfg=NewtonWarpRendererCfg(), + ) + self._camera_sensor_indices = env_ids + self._camera_is_owned = True + self._update_owned_camera_poses() + + def _update_owned_camera_poses(self) -> None: + """Update generated camera poses from env origins or follow prims.""" + if self._camera_sensor is None or not self._camera_is_owned: + return + if self.cfg.cam_follow_prim_path: + origins = prim_world_positions( + self._scene_data_provider.get_usd_stage(), self.cfg.cam_follow_prim_path, self._camera_env_indices + ) + else: + env_origins = self._scene_data_provider.get_interactive_scene().env_origins + origins = env_origins[self._camera_env_indices].detach().cpu() + apply_camera_view_from_origins( + self._camera_sensor, origins, self.cfg.eye, self.cfg.lookat, self._camera_env_indices + ) + + def _log_camera_sensor_image(self) -> None: + """Log the selected camera sensor RGB output into Newton's image panel.""" + if self._viewer is None or self._camera_sensor is None: + return + if self._camera_is_owned and self.cfg.cam_follow_prim_path: + self._update_owned_camera_poses() + rgb = camera_rgb_batch(self._camera_sensor, self._camera_sensor_indices).contiguous() + if not self._logged_camera_stats: + rgb_min = int(rgb.min().item()) + rgb_max = int(rgb.max().item()) + rgb_nonzero = int(torch.count_nonzero(rgb).item()) + logger.warning( + "[NewtonVisualizer] Camera image stats: shape=%s dtype=%s min=%s max=%s nonzero=%s", + tuple(rgb.shape), + rgb.dtype, + rgb_min, + rgb_max, + rgb_nonzero, + ) + self._logged_camera_stats = True + self._viewer.log_image("IsaacLab Camera", wp.from_torch(rgb)) + def _apply_camera_pose(self, pose: tuple[tuple[float, float, float], tuple[float, float, float]]) -> None: """Apply camera eye/target pose to the Newton viewer. From 823c41cecc9839dbaa54682075e77e43d19ffa14 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Sat, 9 May 2026 03:23:55 +0000 Subject: [PATCH 02/31] debug rebase --- .../sensors/contact_sensor/contact_sensor.py | 51 +++++++++++++++++++ .../test/sensors/test_contact_sensor.py | 7 +++ .../test_visualizer_cartpole_integration.py | 38 +++++++------- 3 files changed, 78 insertions(+), 18 deletions(-) diff --git a/source/isaaclab_newton/isaaclab_newton/sensors/contact_sensor/contact_sensor.py b/source/isaaclab_newton/isaaclab_newton/sensors/contact_sensor/contact_sensor.py index 02850e9cc903..5eeb2e3ae500 100644 --- a/source/isaaclab_newton/isaaclab_newton/sensors/contact_sensor/contact_sensor.py +++ b/source/isaaclab_newton/isaaclab_newton/sensors/contact_sensor/contact_sensor.py @@ -40,6 +40,30 @@ logger = logging.getLogger(__name__) +def _flatten_metadata(values) -> list: + """Return Newton contact sensor metadata as a flat Python list.""" + if values is None: + return [] + if isinstance(values, wp.array): + values = values.numpy() + flat_values = np.asarray(values, dtype=object).reshape(-1).tolist() + if flat_values and isinstance(flat_values[0], list | tuple | np.ndarray): + return [ + value + for nested_values in flat_values + for value in np.asarray(nested_values, dtype=object).reshape(-1).tolist() + ] + return flat_values + + +def _broadcast_metadata_kind(kind_values, count: int) -> list: + """Broadcast scalar Newton metadata kinds to match per-object indices.""" + flat_values = _flatten_metadata(kind_values) + if len(flat_values) == 1 and count > 1: + return flat_values * count + return flat_values + + class ContactSensor(BaseContactSensor): """A contact reporting sensor. @@ -349,6 +373,7 @@ def _create_buffers(self): def get_name(idx, kind): kind_name = getattr(kind, "name", None) kind_value = getattr(kind, "value", kind) +<<<<<<< Updated upstream if kind_name == "BODY" or kind_value == 2: return body_labels[idx].split("/")[-1] if kind_name == "SHAPE" or kind_value == 1: @@ -360,6 +385,32 @@ def get_name(idx, kind): # Assumes the environments are processed in order. self._sensor_names = self._sensor_names[: self._num_sensors] flat_counterparts = [obj for world_objs in self.contact_view.counterparts for obj in world_objs] +======= + kind_value_str = kind_value.lower() if isinstance(kind_value, str) else None + if kind_name == "BODY" or kind_value == 2 or kind_value_str == "body": + return body_labels[int(idx)].split("/")[-1] + if kind_name == "SHAPE" or kind_value == 1 or kind_value_str == "shape": + return shape_labels[int(idx)].split("/")[-1] + return "MATCH_ANY" + + sensing_indices = _flatten_metadata(self.contact_view.sensing_obj_idx) + flat_sensing = list( + zip( + sensing_indices, + _broadcast_metadata_kind(self.contact_view.sensing_obj_type, len(sensing_indices)), + ) + ) + self._sensor_names = [get_name(idx, kind) for idx, kind in flat_sensing] + # Assumes the environments are processed in order. + self._sensor_names = self._sensor_names[: self._num_sensors] + counterpart_indices = _flatten_metadata(self.contact_view.counterpart_indices) + flat_counterparts = list( + zip( + counterpart_indices, + _broadcast_metadata_kind(self.contact_view.counterpart_type, len(counterpart_indices)), + ) + ) +>>>>>>> Stashed changes self._filter_object_names = [get_name(idx, kind) for idx, kind in flat_counterparts] # Number of filter objects (counterparts minus the total column) diff --git a/source/isaaclab_newton/test/sensors/test_contact_sensor.py b/source/isaaclab_newton/test/sensors/test_contact_sensor.py index 066803ee884b..e8c57c6ae8d9 100644 --- a/source/isaaclab_newton/test/sensors/test_contact_sensor.py +++ b/source/isaaclab_newton/test/sensors/test_contact_sensor.py @@ -47,6 +47,7 @@ from isaaclab.utils import configclass from isaaclab_assets.robots.allegro import ALLEGRO_HAND_CFG +from isaaclab_newton.sensors.contact_sensor.contact_sensor import _broadcast_metadata_kind, _flatten_metadata ## # Scene Configuration @@ -68,6 +69,12 @@ class ContactSensorTestSceneCfg(InteractiveSceneCfg): SIM_DT = 1.0 / 120.0 +def test_contact_sensor_metadata_helpers_broadcast_scalar_kind(): + """Newton exposes object kind as a scalar while indices are per object.""" + assert _flatten_metadata([[0, 1], [2]]) == [0, 1, 2] + assert _broadcast_metadata_kind("body", 3) == ["body", "body", "body"] + + # =================================================================== # Priority 1: Contact Detection Accuracy # =================================================================== diff --git a/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py b/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py index 42d1368dcebf..fe4a5f3f3c0a 100644 --- a/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py +++ b/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py @@ -456,24 +456,26 @@ def _make_cartpole_camera_env(visualizer_kind: str, backend_kind: str) -> Cartpo @pytest.mark.parametrize( "backend_kind", [ - # xfail: Kit visualizer + PhysX only (Newton backend uses skip below — separate CUDA issue). - pytest.param( - "physx", - marks=pytest.mark.xfail( - reason=("Kit visualizer + PhysX: TODO remove xfail when stale Fabric transforms bug in Kit is fixed"), - strict=False, - ), - ), - pytest.param( - "newton", - marks=pytest.mark.skip( - reason=( - "TODO: Kit visualizer + Newton physics + Isaac RTX tiled camera can hit CUDA illegal access " - "or bad GPU state. Repro: rl_games train Isaac-Cartpole-Camera-Presets-Direct-v0 " - "--enable_cameras presets=newton_mjwarp --viz kit. Re-enable when fixed." - ) - ), - ), + # # xfail: Kit visualizer + PhysX only (Newton backend uses skip below — separate CUDA issue). + # pytest.param( + # "physx", + # marks=pytest.mark.xfail( + # reason=("Kit visualizer + PhysX: TODO remove xfail when stale Fabric transforms bug in Kit is fixed"), + # strict=False, + # ), + # ), + # pytest.param( + # "newton", + # marks=pytest.mark.skip( + # reason=( + # "TODO: Kit visualizer + Newton physics + Isaac RTX tiled camera can hit CUDA illegal access " + # "or bad GPU state. Repro: rl_games train Isaac-Cartpole-Camera-Presets-Direct-v0 " + # "--enable_cameras presets=newton_mjwarp --viz kit. Re-enable when fixed." + # ) + # ), + # ), + "physx", + "newton", ], ) def test_cartpole_kit_visualizer_replicator_viewport_rgb_motion( From 8cfd3a039effd338b0f8a87f5babe85cb0d2ad4f Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Mon, 11 May 2026 16:36:40 +0000 Subject: [PATCH 03/31] initial tiled panel in newton viz --- .../isaaclab/envs/utils/camera_view.py | 39 +++++- .../isaaclab/visualizers/visualizer_cfg.py | 2 +- .../kit/kit_visualizer.py | 122 +++++++++++++++++- .../newton/newton_visualizer.py | 28 +++- 4 files changed, 182 insertions(+), 9 deletions(-) diff --git a/source/isaaclab/isaaclab/envs/utils/camera_view.py b/source/isaaclab/isaaclab/envs/utils/camera_view.py index ead00f530906..5ad5e23b0593 100644 --- a/source/isaaclab/isaaclab/envs/utils/camera_view.py +++ b/source/isaaclab/isaaclab/envs/utils/camera_view.py @@ -199,4 +199,41 @@ def apply_camera_view_from_origins( origins = origins.to(device=device) eye_offset = torch.tensor(eye, dtype=torch.float32, device=device).unsqueeze(0) lookat_offset = torch.tensor(lookat, dtype=torch.float32, device=device).unsqueeze(0) - camera.set_world_poses_from_view(origins + eye_offset, origins + lookat_offset, env_ids=env_ids) + # Temporary validation mode: force every generated camera to the same pose + # as tile 0. If renderer batching is correct, all tiles should match. + origin0 = origins[:1] + eyes = (origin0 + eye_offset).repeat(origins.shape[0], 1) + targets = (origin0 + lookat_offset).repeat(origins.shape[0], 1) + camera.set_world_poses_from_view(eyes, targets) + camera._update_poses(None) + + +def apply_debug_top_down_grid_camera_poses(camera: Camera, env_indices: list[int], spacing: float = 20.0) -> None: + """Temporary debug pose: place cameras on a grid at z=10 looking straight down.""" + device = camera.device + count = len(env_indices) + cols = max(1, math.ceil(math.sqrt(max(1, count)))) + eyes = [] + targets = [] + for i, _env_id in enumerate(env_indices): + row, col = divmod(i, cols) + x = float(col) * spacing + y = float(row) * spacing + eyes.append((x, y, 10.0)) + targets.append((x, y, 0.0)) + eye_tensor = torch.tensor(eyes, dtype=torch.float32, device=device) + target_tensor = torch.tensor(targets, dtype=torch.float32, device=device) + camera.set_world_poses_from_view(eye_tensor, target_tensor) + camera._update_poses(None) + + +def apply_debug_oblique_env_camera_poses(camera: Camera, origins: torch.Tensor, env_indices: list[int]) -> None: + """Temporary debug pose: place cameras near each env origin with an oblique view.""" + device = camera.device + origins = origins.to(device=device) + # Use a high, nearly top-down view for rough terrain. The small horizontal + # offset avoids the Z-up singularity from a perfectly vertical look vector. + eye_offset = torch.tensor((2.0, -2.0, 3.0), dtype=torch.float32, device=device).unsqueeze(0) + lookat_offset = torch.tensor((0.0, 0.0, 0.0), dtype=torch.float32, device=device).unsqueeze(0) + camera.set_world_poses_from_view(origins + eye_offset, origins + lookat_offset) + camera._update_poses(None) diff --git a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py index af0815a02aee..f8bbaded0bdf 100644 --- a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py +++ b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py @@ -54,7 +54,7 @@ class VisualizerCfg: selected envs for tiled view. """ - tiled_cam_view: bool = False + tiled_cam_view: bool = True """Enable a non-interactive tiled camera image view.""" tiled_cam_num: int = 24 diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py index a84317eb1cdd..2f49dce638b9 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py @@ -9,11 +9,15 @@ import asyncio import logging +import os from typing import TYPE_CHECKING +import numpy as np +import torch from pxr import Gf, Usd, UsdGeom, Vt from isaaclab.envs.utils.camera_view import ( + apply_debug_oblique_env_camera_poses, apply_camera_view_from_origins, camera_rgb_batch, compose_rgb_grid, @@ -71,6 +75,9 @@ def __init__(self, cfg: KitVisualizerCfg): self._generated_camera_prim_paths: list[str] = [] self._camera_image_provider = None self._camera_image_window = None + self._camera_debug_frame = 0 + self._camera_update_count = 0 + self._camera_last_checksum: int | None = None # ---- Lifecycle ------------------------------------------------------------------------ @@ -148,7 +155,7 @@ def step(self, dt: float) -> None: settings.set_bool("/app/player/playSimulations", True) except (ImportError, AttributeError) as exc: logger.debug("[KitVisualizer] App update skipped: %s", exc) - self._update_camera_image_panel() + self._update_camera_image_panel(dt) # Markers (VisualizationMarkers) are often created or resized to num_envs only after the first # simulation / debug-vis step; re-apply PointInstancer invisibleIds each step when partial viz is on. self._refresh_partial_viz_point_instancers_if_needed() @@ -339,6 +346,12 @@ def _setup_camera_sensor_view(self, num_envs: int) -> None: """Resolve or create the Camera sensor backing non-interactive image views.""" if not self._uses_camera_sensor_view() or self._runtime_headless: return + logger.warning( + "[KitVisualizer] Setting up camera image view: tiled=%s source=%s num_envs=%s", + self.cfg.tiled_cam_view, + self.cfg.cam_source, + num_envs, + ) env_ids = ( resolve_tiled_env_indices(num_envs, self.cfg.tiled_cam_num, self.cfg.tiled_cam_env_indices) if self.cfg.tiled_cam_view @@ -354,16 +367,35 @@ def _setup_camera_sensor_view(self, num_envs: int) -> None: count = max(1, len(env_ids)) tile_w, tile_h = compute_tile_resolution(self.cfg.window_width, self.cfg.window_height, count) + logger.warning( + "[KitVisualizer] Creating generated camera sensor: env_ids=%s tile=%sx%s", + env_ids, + tile_w, + tile_h, + ) self._camera_sensor, self._generated_camera_prim_paths = create_visualizer_camera( num_envs=num_envs, width=tile_w, height=tile_h, renderer_cfg=IsaacRtxRendererCfg(), ) - self._camera_sensor_indices = env_ids + logger.warning("[KitVisualizer] Generated camera sensor initialized.") + self._camera_sensor_indices = [] self._camera_is_owned = True - self._update_owned_camera_poses() + env_origins = self._scene_data_provider.get_interactive_scene().env_origins + apply_debug_oblique_env_camera_poses( + self._camera_sensor, env_origins[self._camera_env_indices].detach().cpu(), self._camera_env_indices + ) + pos = self._camera_sensor.data.pos_w[self._camera_env_indices] + logger.warning( + "[KitVisualizer] Generated camera pose stats: first_pos=%s min_z=%.2f max_z=%.2f", + tuple(float(x) for x in pos[0].detach().cpu()), + float(pos[:, 2].min().item()), + float(pos[:, 2].max().item()), + ) + logger.warning("[KitVisualizer] Generated camera poses initialized.") self._setup_camera_image_window() + logger.warning("[KitVisualizer] Camera image window initialized.") def _setup_camera_image_window(self) -> None: """Create a dockable Kit UI image panel for camera sensor RGB output.""" @@ -405,6 +437,12 @@ def _update_owned_camera_poses(self) -> None: """Update generated camera poses from env origins or follow prims.""" if self._camera_sensor is None or not self._camera_is_owned: return + if self.cfg.tiled_cam_view and self.cfg.cam_follow_prim_path is None: + env_origins = self._scene_data_provider.get_interactive_scene().env_origins + apply_debug_oblique_env_camera_poses( + self._camera_sensor, env_origins[self._camera_env_indices].detach().cpu(), self._camera_env_indices + ) + return if self.cfg.cam_follow_prim_path: origins = prim_world_positions( self._scene_data_provider.get_usd_stage(), self.cfg.cam_follow_prim_path, self._camera_env_indices @@ -416,15 +454,89 @@ def _update_owned_camera_poses(self) -> None: self._camera_sensor, origins, self.cfg.eye, self.cfg.lookat, self._camera_env_indices ) - def _update_camera_image_panel(self) -> None: + def _update_camera_image_panel(self, dt: float) -> None: """Refresh the non-interactive Kit image panel from camera RGB output.""" if self._camera_sensor is None or self._camera_image_provider is None: return + if not hasattr(self, "_logged_camera_panel_first_update"): + logger.warning("[KitVisualizer] Updating camera image panel for the first time.") + self._logged_camera_panel_first_update = True + self._camera_update_count += 1 if self._camera_is_owned and self.cfg.cam_follow_prim_path: self._update_owned_camera_poses() + if not hasattr(self, "_logged_camera_panel_before_data"): + logger.warning("[KitVisualizer] Fetching camera RGB tensor.") + self._logged_camera_panel_before_data = True + if self._camera_is_owned: + self._camera_sensor.update(dt=dt, force_recompute=True) rgb = camera_rgb_batch(self._camera_sensor, self._camera_sensor_indices) + if self._camera_update_count <= 5 or self._camera_update_count % 30 == 0: + checksum = int(rgb.to(dtype=torch.int64).sum().item()) + logger.warning( + "[KitVisualizer] Camera update #%s checksum=%s changed=%s", + self._camera_update_count, + checksum, + self._camera_last_checksum is None or checksum != self._camera_last_checksum, + ) + self._camera_last_checksum = checksum + if not hasattr(self, "_logged_camera_panel_after_data"): + tile_means = rgb[..., :3].float().mean(dim=(1, 2)).detach().cpu().numpy() + logger.warning( + "[KitVisualizer] Camera RGB tensor ready: shape=%s dtype=%s min=%s max=%s", + tuple(rgb.shape), + rgb.dtype, + int(rgb.min().item()), + int(rgb.max().item()), + ) + logger.warning("[KitVisualizer] First tile RGB means: %s", tile_means[: min(4, len(tile_means))].tolist()) + try: + from PIL import Image + + debug_dir = os.path.join(os.getcwd(), "mtrepte") + for tile_idx in range(min(4, rgb.shape[0])): + tile = rgb[tile_idx].detach().contiguous().cpu().numpy()[..., :3] + Image.fromarray(tile).save(os.path.join(debug_dir, f"kit_tiled_camera_tile_{tile_idx}.png")) + logger.warning("[KitVisualizer] Saved first camera debug tiles to %s", debug_dir) + except Exception as exc: + logger.warning("[KitVisualizer] Failed to save individual camera debug tiles: %s", exc) + self._logged_camera_panel_after_data = True image = compose_rgb_grid(rgb) if self.cfg.tiled_cam_view else rgb[0].detach().contiguous().cpu().numpy() - self._camera_image_provider.set_data_array(image.astype("uint8", copy=False), [image.shape[1], image.shape[0]]) + self._upload_camera_image_to_panel(image) + + def _upload_camera_image_to_panel(self, image: np.ndarray) -> None: + """Upload an RGB/RGBA image to the Kit image provider.""" + if not hasattr(self, "_logged_camera_panel_image_stats"): + channel_mean = image[..., :3].reshape(-1, 3).mean(axis=0) + logger.warning( + "[KitVisualizer] Composed camera image stats: shape=%s min=%s max=%s mean_rgb=(%.2f, %.2f, %.2f)", + image.shape, + int(image.min()), + int(image.max()), + float(channel_mean[0]), + float(channel_mean[1]), + float(channel_mean[2]), + ) + try: + from PIL import Image + + debug_path = os.path.join(os.getcwd(), "mtrepte", "kit_tiled_camera_debug.png") + Image.fromarray(image[..., :3]).save(debug_path) + logger.warning("[KitVisualizer] Saved composed camera debug image to %s", debug_path) + except Exception as exc: + logger.warning("[KitVisualizer] Failed to save composed camera debug image: %s", exc) + self._logged_camera_panel_image_stats = True + if not hasattr(self, "_logged_camera_panel_before_upload"): + logger.warning("[KitVisualizer] Uploading camera image to UI provider: shape=%s", image.shape) + self._logged_camera_panel_before_upload = True + image = image.astype("uint8", copy=False) + if image.ndim == 3 and image.shape[2] == 3: + alpha = np.full((*image.shape[:2], 1), 255, dtype=np.uint8) + image = np.concatenate((image, alpha), axis=2) + image = np.ascontiguousarray(image) + self._camera_image_provider.set_bytes_data(image.flatten().data, [image.shape[1], image.shape[0]]) + if not hasattr(self, "_logged_camera_panel_after_upload"): + logger.warning("[KitVisualizer] Camera image upload complete.") + self._logged_camera_panel_after_upload = True def _refresh_controlled_camera_path(self) -> None: """Cache :attr:`_controlled_camera_path` from the active viewport (or default persp).""" diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py index eff63a022c54..220724a5a4c1 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py @@ -17,6 +17,7 @@ from pyglet.math import Vec3 as PygletVec3 from isaaclab.envs.utils.camera_view import ( + apply_debug_oblique_env_camera_poses, apply_camera_view_from_origins, camera_rgb_batch, compute_tile_resolution, @@ -296,6 +297,8 @@ def __init__(self, cfg: NewtonVisualizerCfg): self._camera_is_owned = False self._generated_camera_prim_paths: list[str] = [] self._logged_camera_stats = False + self._camera_update_count = 0 + self._camera_last_checksum: int | None = None def initialize(self, scene_data_provider: BaseSceneDataProvider) -> None: """Initialize viewer resources and bind scene data provider. @@ -527,14 +530,23 @@ def _setup_camera_sensor_view(self, num_envs: int) -> None: height=tile_h, renderer_cfg=NewtonWarpRendererCfg(), ) - self._camera_sensor_indices = env_ids + self._camera_sensor_indices = [] self._camera_is_owned = True - self._update_owned_camera_poses() + env_origins = self._scene_data_provider.get_interactive_scene().env_origins + apply_debug_oblique_env_camera_poses( + self._camera_sensor, env_origins[self._camera_env_indices].detach().cpu(), self._camera_env_indices + ) def _update_owned_camera_poses(self) -> None: """Update generated camera poses from env origins or follow prims.""" if self._camera_sensor is None or not self._camera_is_owned: return + if self.cfg.tiled_cam_view and self.cfg.cam_follow_prim_path is None: + env_origins = self._scene_data_provider.get_interactive_scene().env_origins + apply_debug_oblique_env_camera_poses( + self._camera_sensor, env_origins[self._camera_env_indices].detach().cpu(), self._camera_env_indices + ) + return if self.cfg.cam_follow_prim_path: origins = prim_world_positions( self._scene_data_provider.get_usd_stage(), self.cfg.cam_follow_prim_path, self._camera_env_indices @@ -552,7 +564,19 @@ def _log_camera_sensor_image(self) -> None: return if self._camera_is_owned and self.cfg.cam_follow_prim_path: self._update_owned_camera_poses() + if self._camera_is_owned: + self._camera_sensor.update(dt=0.0, force_recompute=True) rgb = camera_rgb_batch(self._camera_sensor, self._camera_sensor_indices).contiguous() + self._camera_update_count += 1 + if self._camera_update_count <= 5 or self._camera_update_count % 30 == 0: + checksum = int(rgb.to(dtype=torch.int64).sum().item()) + logger.warning( + "[NewtonVisualizer] Camera update #%s checksum=%s changed=%s", + self._camera_update_count, + checksum, + self._camera_last_checksum is None or checksum != self._camera_last_checksum, + ) + self._camera_last_checksum = checksum if not self._logged_camera_stats: rgb_min = int(rgb.min().item()) rgb_max = int(rgb.max().item()) From a7f387238784e52df1e9f906ec889d17dee7b3de Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Wed, 13 May 2026 22:19:45 +0000 Subject: [PATCH 04/31] shorten viz test steps, disable auto browser launch for rerun/viser, clean --- source/isaaclab/isaaclab/app/app_launcher.py | 1 - .../isaaclab/envs/manager_based_env.py | 1 - .../isaaclab/envs/manager_based_rl_env.py | 1 - .../isaaclab/markers/visualization_markers.py | 6 +- .../isaaclab/isaaclab/sim/simulation_cfg.py | 2 +- .../isaaclab/visualizers/base_visualizer.py | 23 +++ .../markers/test_visualization_markers.py | 103 ++++++++++++ .../test_simulation_context_visualizers.py | 110 +++++++++++++ .../isaaclab_newton/physics/newton_manager.py | 56 +++++++ .../sensors/contact_sensor/contact_sensor.py | 24 ++- .../isaaclab_physx/physics/physx_manager.py | 2 - .../isaaclab_rl/rsl_rl/vecenv_wrapper.py | 7 +- .../isaaclab_tasks/utils/sim_launcher.py | 1 - .../kit/kit_visualizer.py | 12 +- .../rerun/rerun_visualizer.py | 2 +- .../rerun/rerun_visualizer_cfg.py | 9 +- .../viser/viser_visualizer.py | 2 +- .../viser/viser_visualizer_cfg.py | 7 +- .../test_visualizer_cartpole_integration.py | 151 +++++++++++++++--- 19 files changed, 473 insertions(+), 47 deletions(-) diff --git a/source/isaaclab/isaaclab/app/app_launcher.py b/source/isaaclab/isaaclab/app/app_launcher.py index 513757e0808b..8b8c2be02537 100644 --- a/source/isaaclab/isaaclab/app/app_launcher.py +++ b/source/isaaclab/isaaclab/app/app_launcher.py @@ -38,7 +38,6 @@ logging.getLogger("matplotlib").setLevel(logging.WARNING) logging.getLogger("h5py").setLevel(logging.WARNING) - class ExplicitAction(argparse.Action): """Custom action to track if an argument was explicitly passed by the user.""" diff --git a/source/isaaclab/isaaclab/envs/manager_based_env.py b/source/isaaclab/isaaclab/envs/manager_based_env.py index 92db9ad117b5..3f7f1301d4c1 100644 --- a/source/isaaclab/isaaclab/envs/manager_based_env.py +++ b/source/isaaclab/isaaclab/envs/manager_based_env.py @@ -32,7 +32,6 @@ # import logger logger = logging.getLogger(__name__) - class ManagerBasedEnv: """The base environment encapsulates the simulation scene and the environment managers for the manager-based workflow. diff --git a/source/isaaclab/isaaclab/envs/manager_based_rl_env.py b/source/isaaclab/isaaclab/envs/manager_based_rl_env.py index 3e48dcd19f88..c69f8572b0c1 100644 --- a/source/isaaclab/isaaclab/envs/manager_based_rl_env.py +++ b/source/isaaclab/isaaclab/envs/manager_based_rl_env.py @@ -21,7 +21,6 @@ from .manager_based_env import ManagerBasedEnv from .manager_based_rl_env_cfg import ManagerBasedRLEnvCfg - class ManagerBasedRLEnv(ManagerBasedEnv, gym.Env): """The superclass for the manager-based workflow reinforcement learning-based environments. diff --git a/source/isaaclab/isaaclab/markers/visualization_markers.py b/source/isaaclab/isaaclab/markers/visualization_markers.py index 2e418bbecade..447d439ff034 100644 --- a/source/isaaclab/isaaclab/markers/visualization_markers.py +++ b/source/isaaclab/isaaclab/markers/visualization_markers.py @@ -250,7 +250,11 @@ def _ensure_backends_initialized(self) -> None: self._ensure_kit_backend() return - if any(viz.supports_markers() and viz.pumps_app_update() and viz.cfg.enable_markers for viz in sim.visualizers): + needs_kit_backend = ( + bool(getattr(sim, "is_rendering", False)) + or any(viz.supports_markers() and viz.pumps_app_update() and viz.cfg.enable_markers for viz in sim.visualizers) + ) + if needs_kit_backend: self._ensure_kit_backend() if any( viz.supports_markers() and not viz.pumps_app_update() and viz.cfg.enable_markers for viz in sim.visualizers diff --git a/source/isaaclab/isaaclab/sim/simulation_cfg.py b/source/isaaclab/isaaclab/sim/simulation_cfg.py index 6c2e97bb7e82..1881badeb9bf 100644 --- a/source/isaaclab/isaaclab/sim/simulation_cfg.py +++ b/source/isaaclab/isaaclab/sim/simulation_cfg.py @@ -203,7 +203,7 @@ class SimulationCfg: """ use_fabric: bool = True - """Enable/disable reading of physics buffers directly. Default is True. + """Enable/disable reading of physics buffers directly. Default is False. When running the simulation, updates in the states in the scene is normally synchronized with USD. This leads to an overhead in reading the data and does not scale well with massive parallelization. diff --git a/source/isaaclab/isaaclab/visualizers/base_visualizer.py b/source/isaaclab/isaaclab/visualizers/base_visualizer.py index b0fc5a81088f..93831d3e43fc 100644 --- a/source/isaaclab/isaaclab/visualizers/base_visualizer.py +++ b/source/isaaclab/isaaclab/visualizers/base_visualizer.py @@ -310,6 +310,29 @@ def _log_initialization_table(self, logger: logging.Logger, title: str, rows: li table.add_row([key, value]) logger.info("Visualizer initialization:\n%s", table.get_string()) + @staticmethod + def _log_viewer_url(logger: logging.Logger, visualizer_name: str, viewer_url: str) -> None: + """Log a visible browser URL block for web-based visualizers. + + Args: + logger: Logger used to emit the URL block. + visualizer_name: Name of the visualizer exposing the URL. + viewer_url: Browser URL for the visualizer. + """ + border = "=" * 80 + logger.warning( + "\n%s\n" + "[%s] Open the browser viewer manually:\n" + " %s\n" + "Ctrl-click the URL in supported terminals/IDEs.\n" + "Set `open_browser=True` in the visualizer config to auto-launch it.\n" + "%s\n", + border, + visualizer_name, + viewer_url, + border, + ) + def play(self) -> None: """Handle simulation play/start. No-op by default.""" pass diff --git a/source/isaaclab/test/markers/test_visualization_markers.py b/source/isaaclab/test/markers/test_visualization_markers.py index 906c14ccb7d5..39c59c72fc17 100644 --- a/source/isaaclab/test/markers/test_visualization_markers.py +++ b/source/isaaclab/test/markers/test_visualization_markers.py @@ -54,6 +54,109 @@ def test_instantiation(sim): assert test_marker.num_prototypes == 1 +def test_rendering_without_visualizers_initializes_kit_backend(monkeypatch): + """Rendering observations should still author USD markers when no visualizer is launched.""" + + class _FakeSim: + is_rendering = True + visualizers = [] + + marker = object.__new__(VisualizationMarkers) + marker._backends = [] + + monkeypatch.setattr(sim_utils.SimulationContext, "instance", staticmethod(lambda: _FakeSim())) + monkeypatch.setattr(VisualizationMarkers, "_ensure_kit_backend", lambda self: self._backends.append("kit")) + monkeypatch.setattr(VisualizationMarkers, "_ensure_newton_backend", lambda self: self._backends.append("newton")) + + marker._ensure_backends_initialized() + + assert marker._backends == ["kit"] + + +def test_non_rendering_without_visualizers_defers_backend_initialization(monkeypatch): + """Avoid creating render backends when neither rendering nor visualizers are active.""" + + class _FakeSim: + is_rendering = False + visualizers = [] + + marker = object.__new__(VisualizationMarkers) + marker._backends = [] + + monkeypatch.setattr(sim_utils.SimulationContext, "instance", staticmethod(lambda: _FakeSim())) + monkeypatch.setattr(VisualizationMarkers, "_ensure_kit_backend", lambda self: self._backends.append("kit")) + monkeypatch.setattr(VisualizationMarkers, "_ensure_newton_backend", lambda self: self._backends.append("newton")) + + marker._ensure_backends_initialized() + + assert marker._backends == [] + + +def test_kit_visualizer_initializes_kit_backend_when_rendering_flag_is_false(monkeypatch): + """Keep the explicit Kit visualizer path independent of SimulationContext.is_rendering.""" + + class _FakeKitVisualizer: + cfg = type("Cfg", (), {"enable_markers": True})() + + def supports_markers(self): + return True + + def pumps_app_update(self): + return True + + class _FakeSim: + is_rendering = False + visualizers = [_FakeKitVisualizer()] + + marker = object.__new__(VisualizationMarkers) + marker._backends = [] + + monkeypatch.setattr(sim_utils.SimulationContext, "instance", staticmethod(lambda: _FakeSim())) + monkeypatch.setattr(VisualizationMarkers, "_ensure_kit_backend", lambda self: self._backends.append("kit")) + monkeypatch.setattr(VisualizationMarkers, "_ensure_newton_backend", lambda self: self._backends.append("newton")) + + marker._ensure_backends_initialized() + + assert marker._backends == ["kit"] + + +def test_rendering_context_authors_visible_usd_point_instancer(sim): + """Rendering-active contexts should create visible USD marker prims.""" + from pxr import UsdGeom + + sim._has_offscreen_render = True + config = VisualizationMarkersCfg( + prim_path="/World/Visuals/rendered_marker", + markers={ + "failure": sim_utils.CuboidCfg( + size=(0.1, 0.1, 0.1), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.25, 0.15, 0.15)), + visible=True, + ), + "success": sim_utils.CuboidCfg( + size=(0.1, 0.1, 0.1), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.15, 0.25, 0.15)), + visible=True, + ), + }, + ) + test_marker = VisualizationMarkers(config) + test_marker.visualize( + translations=torch.tensor([[0.0, 0.0, 0.0], [0.2, 0.0, 0.0]], device=sim.device), + marker_indices=torch.tensor([0, 1], device=sim.device), + ) + + stage = sim_utils.get_current_stage() + instancer_prim = stage.GetPrimAtPath(test_marker.prim_path) + instancer = UsdGeom.PointInstancer(instancer_prim) + + assert instancer_prim.IsValid() + assert instancer + assert UsdGeom.Imageable(instancer_prim).GetVisibilityAttr().Get() != UsdGeom.Tokens.invisible + assert len(instancer.GetPositionsAttr().Get()) == 2 + assert list(instancer.GetProtoIndicesAttr().Get()) == [0, 1] + + def test_usd_marker(sim): """Test with marker from a USD.""" # create a marker diff --git a/source/isaaclab/test/sim/test_simulation_context_visualizers.py b/source/isaaclab/test/sim/test_simulation_context_visualizers.py index 1c40e21cb548..f211ad6ffbb0 100644 --- a/source/isaaclab/test/sim/test_simulation_context_visualizers.py +++ b/source/isaaclab/test/sim/test_simulation_context_visualizers.py @@ -25,6 +25,11 @@ from isaaclab.sim.simulation_context import SimulationContext +def test_web_visualizer_cfgs_do_not_open_browser_by_default(): + assert RerunVisualizerCfg().open_browser is False + assert ViserVisualizerCfg().open_browser is False + + class _FakePhysicsManager: def __init__(self): self.forward_calls = 0 @@ -534,6 +539,49 @@ def set_world_offsets(self, spacing) -> None: assert captured["set_world_offsets"] == (0.0, 0.0, 0.0) +def test_viser_visualizer_open_browser_is_opt_in(monkeypatch: pytest.MonkeyPatch): + opened_ports = [] + + class _FakeNewtonViewerViser: + def __init__( + self, + *, + port: int, + label: str | None, + verbose: bool, + share: bool, + record_to_viser: str | None, + metadata: dict | None = None, + ): + pass + + def set_model(self, model: Any) -> None: + pass + + def set_visible_worlds(self, worlds) -> None: + pass + + def set_world_offsets(self, spacing) -> None: + pass + + monkeypatch.setattr(viser_visualizer, "NewtonViewerViser", _FakeNewtonViewerViser) + monkeypatch.setattr(viser_visualizer, "_open_viser_web_viewer", lambda port: opened_ports.append(port)) + monkeypatch.setattr( + viser_visualizer.ViserVisualizer, + "_resolve_initial_camera_pose", + lambda self: ((1.0, 2.0, 3.0), (0.0, 0.0, 0.0)), + ) + monkeypatch.setattr(viser_visualizer.ViserVisualizer, "_set_viser_camera_view", lambda self, pose: None) + + cfg = ViserVisualizerCfg(open_browser=True) + visualizer = viser_visualizer.ViserVisualizer(cfg) + visualizer._model = "dummy-model" + visualizer._env_ids = None + visualizer._create_viewer(record_to_viser=None, metadata={"num_envs": 1}) + + assert opened_ports == [cfg.port] + + @pytest.mark.parametrize( ("cfg_max_visible_envs", "expected_visible"), [ @@ -623,6 +671,68 @@ def get_camera_transforms(self): assert captured["set_world_offsets"] == (0.0, 0.0, 0.0) +def test_rerun_visualizer_open_browser_is_opt_in(monkeypatch: pytest.MonkeyPatch): + opened = [] + + class _FakeNewtonViewerRerun: + def __init__( + self, + *, + app_id: str, + address: str | None, + serve_web_viewer: bool, + web_port: int, + grpc_port: int, + keep_historical_data: bool, + keep_scalar_history: bool, + record_to_rrd: str | None, + ): + pass + + def set_model(self, model: Any) -> None: + pass + + def set_visible_worlds(self, worlds) -> None: + pass + + def set_world_offsets(self, spacing) -> None: + pass + + def close(self) -> None: + pass + + class _DummyRerunSceneDataProvider: + def get_metadata(self) -> dict: + return {"num_envs": 1} + + def get_newton_model(self): + return "dummy-model" + + def get_newton_state(self): + return {"ok": True} + + def get_camera_transforms(self): + return {} + + monkeypatch.setattr(rerun_visualizer, "NewtonViewerRerun", _FakeNewtonViewerRerun) + monkeypatch.setattr( + rerun_visualizer, "_ensure_rerun_server", lambda **kwargs: ("rerun+http://127.0.0.1:9876/proxy", False) + ) + monkeypatch.setattr(rerun_visualizer, "_open_rerun_web_viewer", lambda *args: opened.append(args)) + monkeypatch.setattr( + rerun_visualizer.RerunVisualizer, + "_resolve_initial_camera_pose", + lambda self: ((1.0, 2.0, 3.0), (0.0, 0.0, 0.0)), + ) + monkeypatch.setattr(rerun_visualizer.RerunVisualizer, "_apply_camera_pose", lambda self, pose: None) + + cfg = RerunVisualizerCfg(open_browser=True) + visualizer = rerun_visualizer.RerunVisualizer(cfg) + visualizer.initialize(cast(Any, _DummyRerunSceneDataProvider())) + + assert opened == [("127.0.0.1", cfg.web_port, "rerun+http://127.0.0.1:9876/proxy")] + + def test_rerun_visualizer_marker_failure_still_ends_frame(monkeypatch: pytest.MonkeyPatch): class _FakeRerunViewer: def __init__(self): diff --git a/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py b/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py index 97535fb8a328..cd41f76bb9c6 100644 --- a/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py +++ b/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py @@ -232,6 +232,49 @@ def reset(cls, soft: bool = False) -> None: cls.start_simulation() cls.initialize_solver() + @classmethod + def play(cls) -> None: + """Start or resume the Kit timeline when Kit is driving visualization.""" + timeline, app = cls._get_kit_timeline_and_app() + if timeline is None: + return + timeline.play() + if app is not None: + app.update() + + @classmethod + def pause(cls) -> None: + """Pause the Kit timeline when Kit is driving visualization.""" + timeline, app = cls._get_kit_timeline_and_app() + if timeline is None: + return + timeline.pause() + if app is not None: + app.update() + + @classmethod + def stop(cls) -> None: + """Stop the Kit timeline when Kit is driving visualization.""" + timeline, app = cls._get_kit_timeline_and_app() + if timeline is None: + return + timeline.stop() + if app is not None: + app.update() + + @classmethod + def wait_for_playing(cls) -> None: + """Block while the Kit timeline is paused, keeping the UI responsive.""" + timeline, app = cls._get_kit_timeline_and_app() + if timeline is None or app is None: + return + if timeline.is_playing(): + return + while not timeline.is_playing(): + app.update() + if timeline.is_stopped(): + break + @classmethod def forward(cls) -> None: """Update articulation kinematics without stepping physics. @@ -426,6 +469,19 @@ def register_callback( """Register a callback. Passes event to parent class.""" return PhysicsManager.register_callback(callback, event, order, name, wrap_weak_ref) + @classmethod + def _get_kit_timeline_and_app(cls): + """Return Kit timeline/app interfaces if Kit is available.""" + try: + import omni.kit.app + import omni.timeline + except (ImportError, AttributeError): + return None, None + app = omni.kit.app.get_app() + if app is None: + return None, None + return omni.timeline.get_timeline_interface(), app + @classmethod def get_physics_sim_view(cls) -> list: """Get the list of registered views. diff --git a/source/isaaclab_newton/isaaclab_newton/sensors/contact_sensor/contact_sensor.py b/source/isaaclab_newton/isaaclab_newton/sensors/contact_sensor/contact_sensor.py index 43e878fecbfb..e2ed00aae98b 100644 --- a/source/isaaclab_newton/isaaclab_newton/sensors/contact_sensor/contact_sensor.py +++ b/source/isaaclab_newton/isaaclab_newton/sensors/contact_sensor/contact_sensor.py @@ -349,9 +349,9 @@ def _create_buffers(self): def get_name(idx, kind): kind_name = getattr(kind, "name", None) kind_value = getattr(kind, "value", kind) - if kind_name == "BODY" or kind_value == 2: + if kind_name == "BODY" or kind_value == 2 or kind_value == "body": return body_labels[int(idx)].split("/")[-1] - if kind_name == "SHAPE" or kind_value == 1: + if kind_name == "SHAPE" or kind_value == 1 or kind_value == "shape": return shape_labels[int(idx)].split("/")[-1] return "MATCH_ANY" @@ -367,19 +367,31 @@ def flatten_metadata(values): ] return flat_values + def expand_metadata_type(values, count): + # Newton may expose object types as scalar strings ("body"/"shape") instead of + # per-object enum metadata. Expand scalars so they align with object indices. + if isinstance(values, str) or values is None: + return [values] * count + flat_values = flatten_metadata(values) + if len(flat_values) == 1 and count != 1: + return flat_values * count + return flat_values + + flat_sensing_indices = flatten_metadata(self.contact_view.sensing_obj_idx) flat_sensing = list( zip( - flatten_metadata(self.contact_view.sensing_obj_idx), - flatten_metadata(self.contact_view.sensing_obj_type), + flat_sensing_indices, + expand_metadata_type(self.contact_view.sensing_obj_type, len(flat_sensing_indices)), ) ) self._sensor_names = [get_name(idx, kind) for idx, kind in flat_sensing] # Assumes the environments are processed in order. self._sensor_names = self._sensor_names[: self._num_sensors] + flat_counterpart_indices = flatten_metadata(self.contact_view.counterpart_indices) flat_counterparts = list( zip( - flatten_metadata(self.contact_view.counterpart_indices), - flatten_metadata(self.contact_view.counterpart_type), + flat_counterpart_indices, + expand_metadata_type(self.contact_view.counterpart_type, len(flat_counterpart_indices)), ) ) self._filter_object_names = [get_name(idx, kind) for idx, kind in flat_counterparts] diff --git a/source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py b/source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py index 4fb55064f072..5da2f3cd34e7 100644 --- a/source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py +++ b/source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py @@ -43,7 +43,6 @@ logger = logging.getLogger(__name__) - class IsaacEvents(Enum): """Events dispatched during simulation lifecycle. @@ -262,7 +261,6 @@ def step(cls) -> None: cls._physx_sim.simulate(sim.cfg.dt, 0.0) cls._physx_sim.fetch_results() - device = PhysicsManager._device if "cuda" in device: torch.cuda.set_device(device) diff --git a/source/isaaclab_rl/isaaclab_rl/rsl_rl/vecenv_wrapper.py b/source/isaaclab_rl/isaaclab_rl/rsl_rl/vecenv_wrapper.py index 5a3fdcd47164..e63d3c9f16db 100644 --- a/source/isaaclab_rl/isaaclab_rl/rsl_rl/vecenv_wrapper.py +++ b/source/isaaclab_rl/isaaclab_rl/rsl_rl/vecenv_wrapper.py @@ -21,7 +21,6 @@ with contextlib.suppress(ImportError): from isaaclab_experimental.envs import DirectRLEnvWarp, ManagerBasedRLEnvWarp - class RslRlVecEnvWrapper(VecEnv): """Wraps around Isaac Lab environment for the RSL-RL library @@ -167,7 +166,8 @@ def seed(self, seed: int = -1) -> int: # noqa: D102 def reset(self) -> tuple[TensorDict, dict]: # noqa: D102 # reset the environment obs_dict, extras = self.env.reset() - return TensorDict(obs_dict, batch_size=[self.num_envs]), extras + result = TensorDict(obs_dict, batch_size=[self.num_envs]), extras + return result def get_observations(self) -> TensorDict: """Returns the current observations of the environment.""" @@ -175,7 +175,8 @@ def get_observations(self) -> TensorDict: obs_dict = self.unwrapped.observation_manager.compute() else: obs_dict = self.unwrapped._get_observations() - return TensorDict(obs_dict, batch_size=[self.num_envs]) + result = TensorDict(obs_dict, batch_size=[self.num_envs]) + return result def step(self, actions: torch.Tensor) -> tuple[TensorDict, torch.Tensor, torch.Tensor, dict]: # clip actions diff --git a/source/isaaclab_tasks/isaaclab_tasks/utils/sim_launcher.py b/source/isaaclab_tasks/isaaclab_tasks/utils/sim_launcher.py index 3030bda1c4b7..76a8fd96c594 100644 --- a/source/isaaclab_tasks/isaaclab_tasks/utils/sim_launcher.py +++ b/source/isaaclab_tasks/isaaclab_tasks/utils/sim_launcher.py @@ -19,7 +19,6 @@ logger = logging.getLogger(__name__) - def add_launcher_args(parser: argparse.ArgumentParser) -> None: """Add simulation-launcher CLI arguments (``--headless``, ``--device``, etc.) to *parser*. diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py index 701a21c79984..5f325c3a3c7f 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py @@ -52,6 +52,7 @@ def __init__(self, cfg: KitVisualizerCfg): self._runtime_headless = bool(cfg.headless) # USD path for the viewport's active camera, refreshed after setup (used by CI/tests). self._controlled_camera_path: str | None = None + self._physics_backend: str | None = None # ---- Lifecycle ------------------------------------------------------------------------ @@ -72,6 +73,7 @@ def initialize(self, scene_data_provider: BaseSceneDataProvider) -> None: if usd_stage is None: raise RuntimeError("[KitVisualizer] USD stage not available from scene_data_provider.") metadata = scene_data_provider.get_metadata() + self._physics_backend = str(metadata.get("physics_backend", "")).lower() self._ensure_simulation_app() self._setup_viewport() @@ -122,10 +124,16 @@ def step(self, dt: float) -> None: if app is not None and app.is_running(): # Keep app pumping for viewport/UI updates only; physics is owned by SimulationContext. # Disable playSimulations around app.update() so Kit does not advance its own physics here. + # Newton is not driven by Kit's player loop, so avoid toggling the setting; the Kit + # play/pause UI needs to control the timeline without this visualizer overwriting it. settings = get_settings_manager() - settings.set_bool("/app/player/playSimulations", False) + guard_play_simulations = self._physics_backend != "newton" + play_before = settings.get("/app/player/playSimulations") + if guard_play_simulations: + settings.set_bool("/app/player/playSimulations", False) app.update() - settings.set_bool("/app/player/playSimulations", True) + if guard_play_simulations: + settings.set_bool("/app/player/playSimulations", True if play_before is None else bool(play_before)) except (ImportError, AttributeError) as exc: logger.debug("[KitVisualizer] App update skipped: %s", exc) # Markers (VisualizationMarkers) are often created or resized to num_envs only after the first diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py index 5600ad2ee9c4..e5eeead9e206 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py @@ -181,6 +181,7 @@ def initialize(self, scene_data_provider: BaseSceneDataProvider) -> None: rerun_address = getattr(self._viewer, "_grpc_server_uri", rerun_address) viewer_host = _normalize_host(bind_address) viewer_url = _rerun_web_viewer_url(viewer_host, web_port, rerun_address) + self._log_viewer_url(logger, "RerunVisualizer", viewer_url) if self.cfg.open_browser and not start_server_in_viewer: _open_rerun_web_viewer(viewer_host, web_port, rerun_address) self._viewer.set_model(self._model) @@ -211,7 +212,6 @@ def initialize(self, scene_data_provider: BaseSceneDataProvider) -> None: ("cam_source", self.cfg.cam_source), ("num_visualized_envs", num_visualized_envs), ("endpoint", f"http://{viewer_host}:{web_port}"), - ("viewer_url", viewer_url), ("bind_address", bind_address), ("grpc_port", grpc_port), ("web_port", web_port), diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer_cfg.py b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer_cfg.py index 780b346f802b..68f944477745 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer_cfg.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer_cfg.py @@ -22,7 +22,7 @@ class RerunVisualizerCfg(VisualizerCfg): """Application identifier shown in viewer title.""" web_port: int = 9090 - """Port of the local rerun web viewer which is launched in the browser.""" + """Port of the local rerun web viewer whose URL is logged during initialization.""" grpc_port: int = 9876 """Port of the rerun gRPC server (used when serving web viewer externally).""" @@ -36,8 +36,11 @@ class RerunVisualizerCfg(VisualizerCfg): - Local browser links normalize common loopback/wildcard hosts to ``127.0.0.1``. """ - open_browser: bool = True - """Whether to attempt opening the rerun web viewer URL in a browser.""" + open_browser: bool = False + """Whether to attempt opening the rerun web viewer URL in a browser. + + The viewer URL is always logged during initialization. Set this to ``True`` to auto-launch it. + """ keep_historical_data: bool = False """Keep transform history for time scrubbing (False = constant memory for training).""" diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py index 44a0e92c1d70..0db927999c8d 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py @@ -161,6 +161,7 @@ def initialize(self, scene_data_provider: BaseSceneDataProvider) -> None: len(self._resolved_visible_env_ids) if self._resolved_visible_env_ids is not None else num_envs_meta ) viewer_url = _viser_web_viewer_url(self.cfg.port) + self._log_viewer_url(logger, "ViserVisualizer", viewer_url) self._log_initialization_table( logger=logger, title="ViserVisualizer Configuration", @@ -170,7 +171,6 @@ def initialize(self, scene_data_provider: BaseSceneDataProvider) -> None: ("cam_source", self.cfg.cam_source), ("num_visualized_envs", num_visualized_envs), ("port", self.cfg.port), - ("viewer_url", viewer_url), ("record_to_viser", self.cfg.record_to_viser or ""), ], ) diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer_cfg.py b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer_cfg.py index f3f2aa39b0c2..c8b3034b9664 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer_cfg.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer_cfg.py @@ -21,8 +21,11 @@ class ViserVisualizerCfg(VisualizerCfg): port: int = 8080 """Port of the local viser web server.""" - open_browser: bool = True - """Whether to attempt opening the viser web viewer URL in a browser.""" + open_browser: bool = False + """Whether to attempt opening the viser web viewer URL in a browser. + + The viewer URL is always logged during initialization. Set this to ``True`` to auto-launch it. + """ label: str | None = "Isaac Lab Simulation" """Optional label shown in the viewer page title.""" diff --git a/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py b/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py index 42d1368dcebf..1473113ae6cb 100644 --- a/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py +++ b/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py @@ -35,14 +35,13 @@ import logging import socket +import omni.timeline import numpy as np import pytest import torch import warp as wp from isaaclab_visualizers.kit import KitVisualizer, KitVisualizerCfg from isaaclab_visualizers.newton import NewtonVisualizer, NewtonVisualizerCfg -from isaaclab_visualizers.rerun import RerunVisualizer, RerunVisualizerCfg -from isaaclab_visualizers.viser import ViserVisualizer, ViserVisualizerCfg import isaaclab.sim as sim_utils from isaaclab.sim import SimulationContext @@ -76,9 +75,12 @@ _CARTPOLE_TILED_CAMERA_INTEGRATION_WH: tuple[int, int] = (600, 600) """Tiled camera per-env tile width/height (preset default is 100×100); keeps ``observation_space`` consistent.""" -_VIS_FRAME_TEST_STEPS = 60 +_VIS_FRAME_TEST_STEPS = 10 """Steps for Kit / Newton frame capture: no early exit.""" +_PAUSED_FRAME_TEST_STEPS = 5 +"""Visualizer frames to pump while simulation is paused.""" + # Motion check compares the 2nd vs last captured frame (e.g. 2nd vs 60th when *_STEPS* is 60). _MOTION_FRAME_EARLY_IDX = 1 """0-based index of the *early* frame (2nd capture).""" @@ -178,6 +180,8 @@ def _get_visualizer_cfg(visualizer_kind: str): if visualizer_kind == "viser": __import__("newton") __import__("viser") + from isaaclab_visualizers.viser import ViserVisualizer, ViserVisualizerCfg + port = _find_free_tcp_port(host="127.0.0.1") return ( ViserVisualizerCfg(open_browser=False, port=port, randomly_sample_visible_envs=False, **cam), @@ -186,6 +190,8 @@ def _get_visualizer_cfg(visualizer_kind: str): if visualizer_kind == "rerun": __import__("newton") __import__("rerun") + from isaaclab_visualizers.rerun import RerunVisualizer, RerunVisualizerCfg + web_port, grpc_port = _allocate_rerun_test_ports(host="127.0.0.1") return ( RerunVisualizerCfg( @@ -303,6 +309,14 @@ def _count_significantly_differing_pixels( return int(np.count_nonzero(per_pixel_max >= channel_diff_threshold)) +def _assert_frames_remain_stable(frame_a, frame_b, *, max_differing_pixels: int = 10) -> None: + """Assert two viewport frames are effectively unchanged while simulation is paused.""" + n_diff = _count_significantly_differing_pixels(frame_a, frame_b) + assert n_diff <= max_differing_pixels, ( + f"Paused viewport frames changed unexpectedly ({n_diff} > {max_differing_pixels} differing pixels)." + ) + + def _assert_early_and_late_motion_frames_differ( frames: list, *, @@ -333,6 +347,12 @@ def _assert_early_and_late_motion_frames_differ( ) +def _assert_frame_sequence_has_motion(frames: list) -> None: + """Assert a captured frame sequence is visible and changes over time.""" + _assert_non_black_frame_array(frames[-1]) + _assert_early_and_late_motion_frames_differ(frames) + + def _step_until_non_black_camera(env, actions: torch.Tensor, *, max_steps: int = _MAX_NON_BLACK_STEPS) -> None: """Step env until the env's tiled camera RGB tensor is non-black, bounded by *max_steps*.""" last_rgb = None @@ -350,20 +370,88 @@ def _step_until_non_black_camera(env, actions: torch.Tensor, *, max_steps: int = _assert_non_black_tensor(last_rgb) +def _select_newton_training_control_button(viewer, target_label: str) -> None: + """Trigger one Newton visualizer training-control button by label.""" + + class _FakeImgui: + def separator(self): + pass + + def text(self, _text): + pass + + def button(self, label): + return label == target_label + + def slider_int(self, _label, value, _min_value, _max_value, _format): + return False, value + + def is_item_hovered(self): + return False + + def set_tooltip(self, _text): + pass + + viewer._render_training_controls(_FakeImgui()) + + +def _select_newton_pause_simulation_button(viewer) -> None: + """Trigger the Newton visualizer's Pause/Resume Simulation UI button.""" + label = "Resume Simulation" if viewer.is_training_paused() else "Pause Simulation" + _select_newton_training_control_button(viewer, label) + + +def _select_newton_pause_rendering_button(viewer) -> None: + """Trigger the Newton visualizer's Pause/Resume Rendering UI button.""" + label = "Resume Rendering" if viewer.is_rendering_paused() else "Pause Rendering" + _select_newton_training_control_button(viewer, label) + + def _run_newton_viewer_frame_motion_test( viewer, *, + visualizer: NewtonVisualizer, step_hook, + get_physics_step_count, physics_kind: str, viz_kind: str = "newton", ) -> None: - """Exactly ``_VIS_FRAME_TEST_STEPS`` sim steps; last frame non-black; early vs late motion check.""" + """Check Newton viewer simulation pause, rendering pause, then resumed motion.""" frames: list = [] for _ in range(_VIS_FRAME_TEST_STEPS): step_hook() frames.append(viewer.get_frame()) - _assert_non_black_frame_array(frames[-1]) - _assert_early_and_late_motion_frames_differ(frames) + _assert_frame_sequence_has_motion(frames) + + _select_newton_pause_simulation_button(viewer) + paused_frame = viewer.get_frame() + for _ in range(_PAUSED_FRAME_TEST_STEPS): + visualizer.step(0.0) + _assert_frames_remain_stable(paused_frame, viewer.get_frame()) + + _select_newton_pause_simulation_button(viewer) + resumed_frames: list = [] + for _ in range(_VIS_FRAME_TEST_STEPS): + step_hook() + resumed_frames.append(viewer.get_frame()) + _assert_frame_sequence_has_motion(resumed_frames) + + _select_newton_pause_rendering_button(viewer) + rendering_paused_frame = viewer.get_frame() + physics_step_before_render_pause = get_physics_step_count() + for _ in range(_PAUSED_FRAME_TEST_STEPS): + step_hook() + _assert_frames_remain_stable(rendering_paused_frame, viewer.get_frame()) + assert get_physics_step_count() > physics_step_before_render_pause, ( + "Physics did not advance while Newton visualizer rendering was paused." + ) + + _select_newton_pause_rendering_button(viewer) + render_resumed_frames: list = [] + for _ in range(_VIS_FRAME_TEST_STEPS): + step_hook() + render_resumed_frames.append(viewer.get_frame()) + _assert_frame_sequence_has_motion(render_resumed_frames) def _step_env_without_frame_check(env, actions: torch.Tensor, *, max_steps: int = _MAX_NON_BLACK_STEPS) -> None: @@ -403,7 +491,7 @@ def _run_kit_viewport_frame_motion_test( physics_kind: str, viz_kind: str = "kit", ) -> None: - """Exactly ``_VIS_FRAME_TEST_STEPS`` env steps; last Replicator frame non-black; early vs late motion check.""" + """Check Kit viewport motion, timeline pause freeze, then resumed motion.""" camera_path = getattr(kit_visualizer, "_controlled_camera_path", None) assert camera_path, "Kit visualizer does not expose a controlled viewport camera path." @@ -417,14 +505,35 @@ def _run_kit_viewport_frame_motion_test( env.step(action=actions) rgb_data = annotator.get_data() frames.append(_annotator_rgb_to_numpy(rgb_data)) - _assert_non_black_frame_array(frames[-1]) - _assert_early_and_late_motion_frames_differ(frames) + _assert_frame_sequence_has_motion(frames) + + timeline = omni.timeline.get_timeline_interface() + timeline.pause() + simulation_app.update() + paused_frame = _capture_kit_viewport_rgb(annotator) + try: + for _ in range(_PAUSED_FRAME_TEST_STEPS): + env.sim.render() + _assert_frames_remain_stable(paused_frame, _capture_kit_viewport_rgb(annotator)) + finally: + timeline.play() + simulation_app.update() + + resumed_frames: list = [] + for _ in range(_VIS_FRAME_TEST_STEPS): + env.step(action=actions) + resumed_frames.append(_capture_kit_viewport_rgb(annotator)) + _assert_frame_sequence_has_motion(resumed_frames) finally: if annotator is not None and render_product is not None: with contextlib.suppress(Exception): annotator.detach([render_product]) +def _capture_kit_viewport_rgb(annotator) -> np.ndarray: + return _annotator_rgb_to_numpy(annotator.get_data()) + + def _make_cartpole_camera_env(visualizer_kind: str, backend_kind: str) -> CartpoleCameraEnv: """Create cartpole camera env configured with selected visualizer and physics backend.""" env_cfg_root = CartpoleCameraPresetsEnvCfg() @@ -456,7 +565,6 @@ def _make_cartpole_camera_env(visualizer_kind: str, backend_kind: str) -> Cartpo @pytest.mark.parametrize( "backend_kind", [ - # xfail: Kit visualizer + PhysX only (Newton backend uses skip below — separate CUDA issue). pytest.param( "physx", marks=pytest.mark.xfail( @@ -464,16 +572,7 @@ def _make_cartpole_camera_env(visualizer_kind: str, backend_kind: str) -> Cartpo strict=False, ), ), - pytest.param( - "newton", - marks=pytest.mark.skip( - reason=( - "TODO: Kit visualizer + Newton physics + Isaac RTX tiled camera can hit CUDA illegal access " - "or bad GPU state. Repro: rl_games train Isaac-Cartpole-Camera-Presets-Direct-v0 " - "--enable_cameras presets=newton_mjwarp --viz kit. Re-enable when fixed." - ) - ), - ), + "newton", ], ) def test_cartpole_kit_visualizer_replicator_viewport_rgb_motion( @@ -541,7 +640,13 @@ def test_cartpole_newton_visualizer_viewergl_rgb_motion(backend_kind: str, caplo def _step_env() -> None: env.step(action=actions) - _run_newton_viewer_frame_motion_test(viewer, step_hook=_step_env, physics_kind=backend_kind) + _run_newton_viewer_frame_motion_test( + viewer, + visualizer=newton_visualizers[0], + step_hook=_step_env, + get_physics_step_count=lambda: env.sim._physics_step_count, + physics_kind=backend_kind, + ) _assert_no_visualizer_log_issues(caplog) finally: if env is not None: @@ -565,6 +670,8 @@ def test_cartpole_rerun_visualizer_smoke_steps_and_logs(backend_kind: str, caplo with caplog.at_level(logging.WARNING): env.reset() actions = torch.zeros((env.num_envs, env.action_space.shape[-1]), device=env.device) + from isaaclab_visualizers.rerun import RerunVisualizer + rerun_visualizers = [viz for viz in env.sim.visualizers if isinstance(viz, RerunVisualizer)] assert rerun_visualizers, "Expected an initialized Rerun visualizer." assert getattr(rerun_visualizers[0], "_viewer", None) is not None, "Rerun viewer was not created." @@ -592,6 +699,8 @@ def test_cartpole_viser_visualizer_smoke_steps_and_logs(backend_kind: str, caplo with caplog.at_level(logging.WARNING): env.reset() actions = torch.zeros((env.num_envs, env.action_space.shape[-1]), device=env.device) + from isaaclab_visualizers.viser import ViserVisualizer + viser_visualizers = [viz for viz in env.sim.visualizers if isinstance(viz, ViserVisualizer)] assert viser_visualizers, "Expected an initialized Viser visualizer." assert getattr(viser_visualizers[0], "_viewer", None) is not None, "Viser viewer was not created." From cc6f606fa424514c5663168fa205d01a8c21dc8b Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Wed, 13 May 2026 22:19:58 +0000 Subject: [PATCH 05/31] docs/scripts --- docs/source/features/visualization.rst | 25 ++++++- scripts/ci/run_failing_tests_serial.sh | 66 +++++++++++++++++++ .../reinforcement_learning/rsl_rl/train.py | 29 ++++++++ 3 files changed, 118 insertions(+), 2 deletions(-) create mode 100755 scripts/ci/run_failing_tests_serial.sh diff --git a/docs/source/features/visualization.rst b/docs/source/features/visualization.rst index c68816c0da00..a8577783ec8a 100644 --- a/docs/source/features/visualization.rst +++ b/docs/source/features/visualization.rst @@ -83,6 +83,13 @@ To run in headless mode, omit the ``--viz`` argument: The ``--headless`` argument is deprecated. For compatibility, ``--headless`` still takes precedence and disables all visualizers. +.. important:: + + Rerun and Viser do not open a browser tab by default. Their browser URLs are printed in the logs when the + visualizer initializes, before the simulation or training workflow starts. In supported terminals and IDEs, + Ctrl-click the printed ``http://...`` URL to open the viewer manually. To restore automatic browser launch, + set ``open_browser=True`` on the visualizer config. + .. _visualization-configuration: @@ -400,8 +407,9 @@ Rerun Visualizer # Server settings app_id="isaaclab-simulation", # Application identifier for viewer grpc_port=9876, # gRPC endpoint for logging SDK connection - web_port=9090, # Port for local web viewer (launched in browser) + web_port=9090, # Port for local web viewer URL printed in logs bind_address="0.0.0.0", # Endpoint host formatting/reuse checks + open_browser=False, # Set True to auto-launch the browser # Camera settings eye=(8.0, 8.0, 3.0), # Initial camera position (x, y, z) @@ -419,6 +427,12 @@ Rerun startup uses the Python SDK through ``newton.viewer.ViewerRerun`` (no exte management). If ``grpc_port`` is already active, Isaac Lab reuses that server. If ``web_port`` is occupied while starting a new server, initialization fails with a clear port-conflict error. +.. important:: + + The Rerun browser URL is logged as a highlighted block during visualizer initialization, before the main + simulation or training loop begins. Ctrl-click the printed URL in supported terminals/IDEs to open it. + Set ``open_browser=True`` to automatically open the browser tab instead. + Viser Visualizer ~~~~~~~~~~~~~~~~ @@ -458,12 +472,19 @@ server, allowing you to view and interact with the scene from any browser. **Configuration options:** - ``port`` (int, default ``8080``): Port of the local Viser web server. -- ``open_browser`` (bool, default ``True``): Automatically open the viewer URL in a browser. +- ``open_browser`` (bool, default ``False``): Automatically open the viewer URL in a browser. The URL is logged even + when this is ``False``. - ``label`` (str or None, default ``"Isaac Lab Simulation"``): Page title shown in the viewer. - ``share`` (bool, default ``False``): Request a public share URL from Viser for remote viewing. - ``record_to_viser`` (str or None, default ``None``): Path to save a ``.viser`` recording file. - ``verbose`` (bool, default ``True``): Print viewer server startup information. +.. important:: + + The Viser browser URL is logged as a highlighted block during visualizer initialization, before the main + simulation or training loop begins. Ctrl-click the printed URL in supported terminals/IDEs to open it. + Set ``open_browser=True`` to automatically open the browser tab instead. + .. note:: The Viser visualizer does not currently support live plots. diff --git a/scripts/ci/run_failing_tests_serial.sh b/scripts/ci/run_failing_tests_serial.sh new file mode 100755 index 000000000000..e82dadc5b332 --- /dev/null +++ b/scripts/ci/run_failing_tests_serial.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# Run CI-failing test modules one at a time; continue on failure; per-test log files. +# +# Usage (from repo root): +# ./scripts/ci/run_failing_tests_serial.sh +# ./scripts/ci/run_failing_tests_serial.sh -v --tb=short # extra pytest args +# +# Detached run after SSH disconnect (pick one): +# nohup ./scripts/ci/run_failing_tests_serial.sh > run_serial_master.log 2>&1 & disown +# # or: screen -S ci / tmux new -s ci — then run the script inside the session. +# +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" +cd "${REPO_ROOT}" + +LOG_DIR="${REPO_ROOT}/logs/ci_serial_$(date +%Y%m%d_%H%M%S)" +mkdir -p "${LOG_DIR}" + +# Paths relative to repo root (match isaaclab CI layout under workspace/isaaclab). +TESTS=( + "source/isaaclab/test/utils/test_configclass.py" + "source/isaaclab_mimic/test/test_generate_dataset_gr1t2_pickplace.py" + "source/isaaclab_physx/test/assets/test_deformable_object.py" +) + +SUMMARY="${LOG_DIR}/SUMMARY.txt" +{ + echo "REPO_ROOT=${REPO_ROOT}" + echo "Started: $(date -Is)" + echo "Extra pytest args: $*" + echo "----------" +} | tee "${SUMMARY}" + +failed=0 +passed=0 +for rel in "${TESTS[@]}"; do + slug="$(basename "${rel}" .py)" + log="${LOG_DIR}/${slug}.log" + echo "" | tee -a "${SUMMARY}" + echo "=== $(date -Is) :: pytest ${rel} ===" | tee -a "${SUMMARY}" + # Continue on failure. Avoid `pytest | tee` for exit code — preceding pipelines can + # leave PIPESTATUS stale in some bash builds; write to file then cat. + set +e + pytest "${rel}" "$@" >"${log}" 2>&1 + ec=$? + set -e + cat "${log}" + if [[ "${ec}" -eq 0 ]]; then + echo "RESULT: PASS (exit ${ec})" | tee -a "${SUMMARY}" + passed=$((passed + 1)) + else + echo "RESULT: FAIL (exit ${ec})" | tee -a "${SUMMARY}" + failed=$((failed + 1)) + fi +done + +{ + echo "----------" + echo "Finished: $(date -Is)" + echo "Passed modules: ${passed} Failed modules: ${failed}" + echo "Logs: ${LOG_DIR}" +} | tee -a "${SUMMARY}" + +exit $(( failed > 0 ? 1 : 0 )) diff --git a/scripts/reinforcement_learning/rsl_rl/train.py b/scripts/reinforcement_learning/rsl_rl/train.py index eefc13a8aa2c..fbfe0b1ec4f7 100644 --- a/scripts/reinforcement_learning/rsl_rl/train.py +++ b/scripts/reinforcement_learning/rsl_rl/train.py @@ -36,6 +36,11 @@ logger = logging.getLogger(__name__) + +def _hang_debug(message: str) -> None: + """Print unbuffered breadcrumbs for diagnosing training startup hangs.""" + print(f"[ISAACLAB-HANG-DEBUG][{time.monotonic():.6f}][rsl_rl.train] {message}", flush=True) + # PLACEHOLDER: Extension template (do not remove this comment) with contextlib.suppress(ImportError): import isaaclab_tasks_experimental # noqa: F401 @@ -171,13 +176,17 @@ def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agen env_cfg.log_dir = log_dir # create isaac environment + _hang_debug("before gym.make") env = gym.make(args_cli.task, cfg=env_cfg, render_mode="rgb_array" if args_cli.video else None) + _hang_debug(f"after gym.make env_type={type(env).__name__} unwrapped={type(env.unwrapped).__name__}") # convert to single-agent instance if required by the RL algorithm if isinstance(env.unwrapped.cfg, DirectMARLEnvCfg): from isaaclab.envs import multi_agent_to_single_agent + _hang_debug("before multi_agent_to_single_agent") env = multi_agent_to_single_agent(env) + _hang_debug(f"after multi_agent_to_single_agent env_type={type(env).__name__}") # save resume path before creating a new log_dir if agent_cfg.resume or agent_cfg.algorithm.class_name == "Distillation": @@ -193,39 +202,59 @@ def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agen } print("[INFO] Recording videos during training.") print_dict(video_kwargs, nesting=4) + _hang_debug("before gym.wrappers.RecordVideo") env = gym.wrappers.RecordVideo(env, **video_kwargs) + _hang_debug(f"after gym.wrappers.RecordVideo env_type={type(env).__name__}") start_time = time.time() # wrap around environment for rsl-rl + _hang_debug("before RslRlVecEnvWrapper") env = RslRlVecEnvWrapper(env, clip_actions=agent_cfg.clip_actions) + _hang_debug(f"after RslRlVecEnvWrapper env_type={type(env).__name__}") # create runner from rsl-rl if agent_cfg.class_name == "OnPolicyRunner": + _hang_debug("before OnPolicyRunner construction") runner = OnPolicyRunner(env, agent_cfg.to_dict(), log_dir=log_dir, device=agent_cfg.device) + _hang_debug("after OnPolicyRunner construction") elif agent_cfg.class_name == "DistillationRunner": + _hang_debug("before DistillationRunner construction") runner = DistillationRunner(env, agent_cfg.to_dict(), log_dir=log_dir, device=agent_cfg.device) + _hang_debug("after DistillationRunner construction") else: raise ValueError(f"Unsupported runner class: {agent_cfg.class_name}") # write git state to logs + _hang_debug("before runner.add_git_repo_to_log") runner.add_git_repo_to_log(__file__) + _hang_debug("after runner.add_git_repo_to_log") # load the checkpoint if agent_cfg.resume or agent_cfg.algorithm.class_name == "Distillation": print(f"[INFO]: Loading model checkpoint from: {resume_path}") # load previously trained model + _hang_debug("before runner.load") runner.load(resume_path) + _hang_debug("after runner.load") # dump the configuration into log-directory + _hang_debug("before dump env.yaml") dump_yaml(os.path.join(log_dir, "params", "env.yaml"), env_cfg) + _hang_debug("after dump env.yaml; before dump agent.yaml") dump_yaml(os.path.join(log_dir, "params", "agent.yaml"), agent_cfg) + _hang_debug("after dump agent.yaml") # run training try: + _hang_debug(f"before runner.learn max_iterations={agent_cfg.max_iterations}") runner.learn(num_learning_iterations=agent_cfg.max_iterations, init_at_random_ep_len=True) + _hang_debug("after runner.learn") print(f"Training time: {round(time.time() - start_time, 2)} seconds") # close the simulator + _hang_debug("before env.close") env.close() + _hang_debug("after env.close") except KeyboardInterrupt: + _hang_debug("KeyboardInterrupt caught") pass From 94a3a81a32d0e2870cd1940805ae7d82ca6cefdb Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Wed, 13 May 2026 23:05:01 +0000 Subject: [PATCH 06/31] disable browser launch default behavior in rerun/viser --- docs/source/features/visualization.rst | 33 ++++++++++----- .../reinforcement_learning/rsl_rl/train.py | 29 -------------- .../isaaclab/visualizers/base_visualizer.py | 29 +++++++++----- .../test_simulation_context_visualizers.py | 4 ++ .../rerun/rerun_visualizer.py | 25 ++++++++++-- .../viser/viser_visualizer.py | 40 +++++-------------- 6 files changed, 75 insertions(+), 85 deletions(-) diff --git a/docs/source/features/visualization.rst b/docs/source/features/visualization.rst index a8577783ec8a..5cbfe1bc78a7 100644 --- a/docs/source/features/visualization.rst +++ b/docs/source/features/visualization.rst @@ -85,10 +85,21 @@ To run in headless mode, omit the ``--viz`` argument: .. important:: - Rerun and Viser do not open a browser tab by default. Their browser URLs are printed in the logs when the - visualizer initializes, before the simulation or training workflow starts. In supported terminals and IDEs, - Ctrl-click the printed ``http://...`` URL to open the viewer manually. To restore automatic browser launch, - set ``open_browser=True`` on the visualizer config. + Rerun and Viser do not open a browser tab by default. A highlighted browser URL is printed in the logs + before the simulation or training starts. In supported terminals and IDEs, Ctrl-click the printed + ``http://...`` URL to open the viewer manually. To set automatic browser launch, set ``open_browser=True`` + on the visualizer config. + + Example: + + .. code-block:: text + + ╭────── viser (listening *:8080) ───────╮ + │ ╷ │ + │ HTTP │ http://localhost:8080 │ + │ Websocket │ ws://localhost:8080 │ + │ ╵ │ + ╰───────────────────────────────────────╯ .. _visualization-configuration: @@ -429,9 +440,9 @@ starting a new server, initialization fails with a clear port-conflict error. .. important:: - The Rerun browser URL is logged as a highlighted block during visualizer initialization, before the main - simulation or training loop begins. Ctrl-click the printed URL in supported terminals/IDEs to open it. - Set ``open_browser=True`` to automatically open the browser tab instead. + A highlighted Rerun browser URL is printed in the logs before the main simulation or training loop begins. + Ctrl-click the printed URL in supported terminals/IDEs to open it. Set ``open_browser=True`` to automatically + open the browser tab instead. Viser Visualizer @@ -463,7 +474,7 @@ server, allowing you to view and interact with the scene from any browser. visualizer_cfg = ViserVisualizerCfg( port=8080, - open_browser=True, + open_browser=False, label="Isaac Lab Simulation", share=False, max_visible_envs=16, @@ -481,9 +492,9 @@ server, allowing you to view and interact with the scene from any browser. .. important:: - The Viser browser URL is logged as a highlighted block during visualizer initialization, before the main - simulation or training loop begins. Ctrl-click the printed URL in supported terminals/IDEs to open it. - Set ``open_browser=True`` to automatically open the browser tab instead. + A highlighted Viser browser URL is printed in the logs before the main simulation or training loop begins. + Ctrl-click the printed URL in supported terminals/IDEs to open it. Set ``open_browser=True`` to automatically + open the browser tab instead. .. note:: diff --git a/scripts/reinforcement_learning/rsl_rl/train.py b/scripts/reinforcement_learning/rsl_rl/train.py index cff6f2f328c3..b5f4fcaf0db5 100644 --- a/scripts/reinforcement_learning/rsl_rl/train.py +++ b/scripts/reinforcement_learning/rsl_rl/train.py @@ -36,11 +36,6 @@ logger = logging.getLogger(__name__) - -def _hang_debug(message: str) -> None: - """Print unbuffered breadcrumbs for diagnosing training startup hangs.""" - print(f"[ISAACLAB-HANG-DEBUG][{time.monotonic():.6f}][rsl_rl.train] {message}", flush=True) - # PLACEHOLDER: Extension template (do not remove this comment) with contextlib.suppress(ImportError): import isaaclab_tasks_experimental # noqa: F401 @@ -176,17 +171,13 @@ def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agen env_cfg.log_dir = log_dir # create isaac environment - _hang_debug("before gym.make") env = gym.make(args_cli.task, cfg=env_cfg, render_mode="rgb_array" if args_cli.video else None) - _hang_debug(f"after gym.make env_type={type(env).__name__} unwrapped={type(env.unwrapped).__name__}") # convert to single-agent instance if required by the RL algorithm if isinstance(env.unwrapped.cfg, DirectMARLEnvCfg): from isaaclab.envs import multi_agent_to_single_agent - _hang_debug("before multi_agent_to_single_agent") env = multi_agent_to_single_agent(env) - _hang_debug(f"after multi_agent_to_single_agent env_type={type(env).__name__}") # save resume path before creating a new log_dir if agent_cfg.resume or agent_cfg.algorithm.class_name == "Distillation": @@ -202,59 +193,39 @@ def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agen } print("[INFO] Recording videos during training.") print_dict(video_kwargs, nesting=4) - _hang_debug("before gym.wrappers.RecordVideo") env = gym.wrappers.RecordVideo(env, **video_kwargs) - _hang_debug(f"after gym.wrappers.RecordVideo env_type={type(env).__name__}") start_time = time.time() # wrap around environment for rsl-rl - _hang_debug("before RslRlVecEnvWrapper") env = RslRlVecEnvWrapper(env, clip_actions=agent_cfg.clip_actions) - _hang_debug(f"after RslRlVecEnvWrapper env_type={type(env).__name__}") # create runner from rsl-rl if agent_cfg.class_name == "OnPolicyRunner": - _hang_debug("before OnPolicyRunner construction") runner = OnPolicyRunner(env, agent_cfg.to_dict(), log_dir=log_dir, device=agent_cfg.device) - _hang_debug("after OnPolicyRunner construction") elif agent_cfg.class_name == "DistillationRunner": - _hang_debug("before DistillationRunner construction") runner = DistillationRunner(env, agent_cfg.to_dict(), log_dir=log_dir, device=agent_cfg.device) - _hang_debug("after DistillationRunner construction") else: raise ValueError(f"Unsupported runner class: {agent_cfg.class_name}") # write git state to logs - _hang_debug("before runner.add_git_repo_to_log") runner.add_git_repo_to_log(__file__) - _hang_debug("after runner.add_git_repo_to_log") # load the checkpoint if agent_cfg.resume or agent_cfg.algorithm.class_name == "Distillation": print(f"[INFO]: Loading model checkpoint from: {resume_path}") # load previously trained model - _hang_debug("before runner.load") runner.load(resume_path) - _hang_debug("after runner.load") # dump the configuration into log-directory - _hang_debug("before dump env.yaml") dump_yaml(os.path.join(log_dir, "params", "env.yaml"), env_cfg) - _hang_debug("after dump env.yaml; before dump agent.yaml") dump_yaml(os.path.join(log_dir, "params", "agent.yaml"), agent_cfg) - _hang_debug("after dump agent.yaml") # run training try: - _hang_debug(f"before runner.learn max_iterations={agent_cfg.max_iterations}") runner.learn(num_learning_iterations=agent_cfg.max_iterations, init_at_random_ep_len=True) - _hang_debug("after runner.learn") print(f"Training time: {round(time.time() - start_time, 2)} seconds") # close the simulator - _hang_debug("before env.close") env.close() - _hang_debug("after env.close") except KeyboardInterrupt: - _hang_debug("KeyboardInterrupt caught") pass diff --git a/source/isaaclab/isaaclab/visualizers/base_visualizer.py b/source/isaaclab/isaaclab/visualizers/base_visualizer.py index 93831d3e43fc..3352042cdcc6 100644 --- a/source/isaaclab/isaaclab/visualizers/base_visualizer.py +++ b/source/isaaclab/isaaclab/visualizers/base_visualizer.py @@ -12,6 +12,7 @@ import re from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Any +from urllib.parse import urlparse if TYPE_CHECKING: from isaaclab.physics import SceneDataProvider @@ -308,7 +309,7 @@ def _log_initialization_table(self, logger: logging.Logger, title: str, rows: li table.align["Value"] = "l" for key, value in rows: table.add_row([key, value]) - logger.info("Visualizer initialization:\n%s", table.get_string()) + logger.debug("Visualizer initialization:\n%s", table.get_string()) @staticmethod def _log_viewer_url(logger: logging.Logger, visualizer_name: str, viewer_url: str) -> None: @@ -319,18 +320,24 @@ def _log_viewer_url(logger: logging.Logger, visualizer_name: str, viewer_url: st visualizer_name: Name of the visualizer exposing the URL. viewer_url: Browser URL for the visualizer. """ - border = "=" * 80 + parsed_url = urlparse(viewer_url) + visualizer_label = visualizer_name.removesuffix("Visualizer").lower() + title = f" {visualizer_label} (listening *:{parsed_url.port}) " if parsed_url.port else f" {visualizer_label} " + label_width = 9 + value_width = max(len(viewer_url), len(title) + 2, 21) + inner_width = label_width + value_width + 9 + left_rule_width = max((inner_width - len(title)) // 2, 1) + right_rule_width = max(inner_width - len(title) - left_rule_width, 1) + logger.warning( - "\n%s\n" - "[%s] Open the browser viewer manually:\n" - " %s\n" - "Ctrl-click the URL in supported terminals/IDEs.\n" - "Set `open_browser=True` in the visualizer config to auto-launch it.\n" - "%s\n", - border, - visualizer_name, + "\n%s\n%s\n%s\n%s\n%s\n%s server running at: %s\n", + f"╭{'─' * left_rule_width}{title}{'─' * right_rule_width}╮", + f"│{' ' * (label_width + 4)}╷{' ' * (value_width + 4)}│", + f"│ {'HTTP':<{label_width}} │ {viewer_url:<{value_width}} │", + f"│{' ' * (label_width + 4)}╵{' ' * (value_width + 4)}│", + f"╰{'─' * inner_width}╯", + visualizer_label.capitalize(), viewer_url, - border, ) def play(self) -> None: diff --git a/source/isaaclab/test/sim/test_simulation_context_visualizers.py b/source/isaaclab/test/sim/test_simulation_context_visualizers.py index f211ad6ffbb0..f89d4c3ddbdb 100644 --- a/source/isaaclab/test/sim/test_simulation_context_visualizers.py +++ b/source/isaaclab/test/sim/test_simulation_context_visualizers.py @@ -535,6 +535,7 @@ def set_world_offsets(self, spacing) -> None: visualizer._create_viewer(record_to_viser="record.viser", metadata={"num_envs": 8}) assert captured["set_model"] == "dummy-model" + assert captured["init"]["open_browser"] is False assert captured["visible_worlds"] == expected_visible assert captured["set_world_offsets"] == (0.0, 0.0, 0.0) @@ -609,6 +610,7 @@ def __init__( keep_historical_data: bool, keep_scalar_history: bool, record_to_rrd: str | None, + open_browser: bool, ): captured["init"] = { "app_id": app_id, @@ -619,6 +621,7 @@ def __init__( "keep_historical_data": keep_historical_data, "keep_scalar_history": keep_scalar_history, "record_to_rrd": record_to_rrd, + "open_browser": open_browser, } def set_model(self, model: Any) -> None: @@ -686,6 +689,7 @@ def __init__( keep_historical_data: bool, keep_scalar_history: bool, record_to_rrd: str | None, + open_browser: bool, ): pass diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py index e5eeead9e206..882b1e06a856 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py @@ -8,6 +8,8 @@ from __future__ import annotations import atexit +import contextlib +import inspect import logging import socket import webbrowser @@ -85,15 +87,30 @@ def _open_rerun_web_viewer(host: str, web_port: int, connect_to: str) -> None: def _rerun_web_viewer_url(host: str, web_port: int, connect_to: str) -> str: """Return rerun web UI URL with prefilled endpoint.""" - return f"http://{host}:{int(web_port)}/?url={quote(connect_to, safe='')}" + return f"http://{host}:{int(web_port)}/?url={quote(connect_to, safe=':/')}" class NewtonViewerRerun(ViewerRerun): """Wrapper around Newton's ViewerRerun with rendering pause controls.""" - def __init__(self, *args, **kwargs): + def __init__(self, *args, open_browser: bool = False, **kwargs): """Initialize viewer wrapper and Isaac Lab pause state.""" - super().__init__(*args, **kwargs) + if open_browser: + super().__init__(*args, **kwargs) + else: + original_serve_web_viewer = rr.serve_web_viewer + + def _serve_web_viewer_without_browser(*serve_args, **serve_kwargs): + with contextlib.suppress(TypeError, ValueError): + supports_open_browser = "open_browser" in inspect.signature(original_serve_web_viewer).parameters + if supports_open_browser: + serve_kwargs.setdefault("open_browser", False) + return original_serve_web_viewer(*serve_args, **serve_kwargs) + + with contextlib.ExitStack() as stack: + rr.serve_web_viewer = _serve_web_viewer_without_browser + stack.callback(setattr, rr, "serve_web_viewer", original_serve_web_viewer) + super().__init__(*args, **kwargs) self._paused_rendering = False def is_rendering_paused(self) -> bool: @@ -176,11 +193,13 @@ def initialize(self, scene_data_provider: BaseSceneDataProvider) -> None: keep_historical_data=self.cfg.keep_historical_data, keep_scalar_history=self.cfg.keep_scalar_history, record_to_rrd=self.cfg.record_to_rrd, + open_browser=self.cfg.open_browser, ) if start_server_in_viewer: rerun_address = getattr(self._viewer, "_grpc_server_uri", rerun_address) viewer_host = _normalize_host(bind_address) viewer_url = _rerun_web_viewer_url(viewer_host, web_port, rerun_address) + print() self._log_viewer_url(logger, "RerunVisualizer", viewer_url) if self.cfg.open_browser and not start_server_in_viewer: _open_rerun_web_viewer(viewer_host, web_port, rerun_address) diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py index 0db927999c8d..da057c1c116d 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py @@ -7,8 +7,6 @@ from __future__ import annotations -import contextlib -import io import logging import os import webbrowser @@ -45,24 +43,6 @@ def _disable_viser_runtime_client_rebuild_if_bundled() -> None: client_autobuild.ensure_client_is_built = lambda: None - -@contextlib.contextmanager -def _suppress_viser_startup_logs(enabled: bool): - """Temporarily quiet noisy viser/websockets startup output.""" - if not enabled: - yield - return - - websockets_logger = logging.getLogger("websockets.server") - previous_level = websockets_logger.level - websockets_logger.setLevel(logging.WARNING) - try: - with contextlib.redirect_stdout(io.StringIO()), contextlib.redirect_stderr(io.StringIO()): - yield - finally: - websockets_logger.setLevel(previous_level) - - def _open_viser_web_viewer(port: int) -> None: """Open the local viser web UI in a browser.""" url = _viser_web_viewer_url(port) @@ -160,8 +140,6 @@ def initialize(self, scene_data_provider: BaseSceneDataProvider) -> None: num_visualized_envs = ( len(self._resolved_visible_env_ids) if self._resolved_visible_env_ids is not None else num_envs_meta ) - viewer_url = _viser_web_viewer_url(self.cfg.port) - self._log_viewer_url(logger, "ViserVisualizer", viewer_url) self._log_initialization_table( logger=logger, title="ViserVisualizer Configuration", @@ -263,15 +241,15 @@ def _create_viewer(self, record_to_viser: str | None, metadata: dict | None = No if self._model is None: raise RuntimeError("Viser visualizer requires a Newton model.") - with _suppress_viser_startup_logs(enabled=not self.cfg.verbose): - self._viewer = NewtonViewerViser( - port=self.cfg.port, - label=self.cfg.label, - verbose=self.cfg.verbose, - share=self.cfg.share, - record_to_viser=record_to_viser, - metadata=metadata or {}, - ) + print() + self._viewer = NewtonViewerViser( + port=self.cfg.port, + label=self.cfg.label, + verbose=self.cfg.verbose, + share=self.cfg.share, + record_to_viser=record_to_viser, + metadata=metadata or {}, + ) num_envs = int((metadata or {}).get("num_envs", 0)) self._viewer.set_model(self._model) apply_viewer_visible_worlds( From c5fa1a782baf86616a346dc0c156cbdfb7338a97 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Wed, 13 May 2026 23:14:40 +0000 Subject: [PATCH 07/31] lint/changelog frags/clean --- scripts/ci/run_failing_tests_serial.sh | 66 ------------------- .../changelog.d/mtrepte-update-debug-viz.rst | 9 +++ source/isaaclab/isaaclab/app/app_launcher.py | 1 + .../isaaclab/envs/manager_based_env.py | 1 + .../isaaclab/envs/manager_based_rl_env.py | 1 + .../isaaclab/markers/visualization_markers.py | 5 +- .../changelog.d/mtrepte-update-debug-viz.rst | 4 ++ .../changelog.d/mtrepte-update-debug-viz.skip | 1 + .../isaaclab_physx/physics/physx_manager.py | 1 + .../changelog.d/mtrepte-update-debug-viz.skip | 1 + .../isaaclab_rl/rsl_rl/vecenv_wrapper.py | 1 + .../changelog.d/mtrepte-update-debug-viz.skip | 1 + .../isaaclab_tasks/utils/sim_launcher.py | 1 + .../changelog.d/mtrepte-update-debug-viz.rst | 5 ++ .../viser/viser_visualizer.py | 1 + .../test_visualizer_cartpole_integration.py | 3 +- 16 files changed, 32 insertions(+), 70 deletions(-) delete mode 100755 scripts/ci/run_failing_tests_serial.sh create mode 100644 source/isaaclab/changelog.d/mtrepte-update-debug-viz.rst create mode 100644 source/isaaclab_newton/changelog.d/mtrepte-update-debug-viz.rst create mode 100644 source/isaaclab_physx/changelog.d/mtrepte-update-debug-viz.skip create mode 100644 source/isaaclab_rl/changelog.d/mtrepte-update-debug-viz.skip create mode 100644 source/isaaclab_tasks/changelog.d/mtrepte-update-debug-viz.skip create mode 100644 source/isaaclab_visualizers/changelog.d/mtrepte-update-debug-viz.rst diff --git a/scripts/ci/run_failing_tests_serial.sh b/scripts/ci/run_failing_tests_serial.sh deleted file mode 100755 index e82dadc5b332..000000000000 --- a/scripts/ci/run_failing_tests_serial.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env bash -# Run CI-failing test modules one at a time; continue on failure; per-test log files. -# -# Usage (from repo root): -# ./scripts/ci/run_failing_tests_serial.sh -# ./scripts/ci/run_failing_tests_serial.sh -v --tb=short # extra pytest args -# -# Detached run after SSH disconnect (pick one): -# nohup ./scripts/ci/run_failing_tests_serial.sh > run_serial_master.log 2>&1 & disown -# # or: screen -S ci / tmux new -s ci — then run the script inside the session. -# -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" -cd "${REPO_ROOT}" - -LOG_DIR="${REPO_ROOT}/logs/ci_serial_$(date +%Y%m%d_%H%M%S)" -mkdir -p "${LOG_DIR}" - -# Paths relative to repo root (match isaaclab CI layout under workspace/isaaclab). -TESTS=( - "source/isaaclab/test/utils/test_configclass.py" - "source/isaaclab_mimic/test/test_generate_dataset_gr1t2_pickplace.py" - "source/isaaclab_physx/test/assets/test_deformable_object.py" -) - -SUMMARY="${LOG_DIR}/SUMMARY.txt" -{ - echo "REPO_ROOT=${REPO_ROOT}" - echo "Started: $(date -Is)" - echo "Extra pytest args: $*" - echo "----------" -} | tee "${SUMMARY}" - -failed=0 -passed=0 -for rel in "${TESTS[@]}"; do - slug="$(basename "${rel}" .py)" - log="${LOG_DIR}/${slug}.log" - echo "" | tee -a "${SUMMARY}" - echo "=== $(date -Is) :: pytest ${rel} ===" | tee -a "${SUMMARY}" - # Continue on failure. Avoid `pytest | tee` for exit code — preceding pipelines can - # leave PIPESTATUS stale in some bash builds; write to file then cat. - set +e - pytest "${rel}" "$@" >"${log}" 2>&1 - ec=$? - set -e - cat "${log}" - if [[ "${ec}" -eq 0 ]]; then - echo "RESULT: PASS (exit ${ec})" | tee -a "${SUMMARY}" - passed=$((passed + 1)) - else - echo "RESULT: FAIL (exit ${ec})" | tee -a "${SUMMARY}" - failed=$((failed + 1)) - fi -done - -{ - echo "----------" - echo "Finished: $(date -Is)" - echo "Passed modules: ${passed} Failed modules: ${failed}" - echo "Logs: ${LOG_DIR}" -} | tee -a "${SUMMARY}" - -exit $(( failed > 0 ? 1 : 0 )) diff --git a/source/isaaclab/changelog.d/mtrepte-update-debug-viz.rst b/source/isaaclab/changelog.d/mtrepte-update-debug-viz.rst new file mode 100644 index 000000000000..b7aa84db0dfc --- /dev/null +++ b/source/isaaclab/changelog.d/mtrepte-update-debug-viz.rst @@ -0,0 +1,9 @@ +Fixed +^^^^^ + +* Fixed visualization marker backend initialization so USD markers remain available during rendering even when standalone visualizers are not launched. + +Changed +^^^^^^^ + +* Removed temporary startup and runtime debug breadcrumbs from core simulation and environment setup logs. diff --git a/source/isaaclab/isaaclab/app/app_launcher.py b/source/isaaclab/isaaclab/app/app_launcher.py index 8b8c2be02537..513757e0808b 100644 --- a/source/isaaclab/isaaclab/app/app_launcher.py +++ b/source/isaaclab/isaaclab/app/app_launcher.py @@ -38,6 +38,7 @@ logging.getLogger("matplotlib").setLevel(logging.WARNING) logging.getLogger("h5py").setLevel(logging.WARNING) + class ExplicitAction(argparse.Action): """Custom action to track if an argument was explicitly passed by the user.""" diff --git a/source/isaaclab/isaaclab/envs/manager_based_env.py b/source/isaaclab/isaaclab/envs/manager_based_env.py index 9deb0b768d5d..620cf2895718 100644 --- a/source/isaaclab/isaaclab/envs/manager_based_env.py +++ b/source/isaaclab/isaaclab/envs/manager_based_env.py @@ -32,6 +32,7 @@ # import logger logger = logging.getLogger(__name__) + class ManagerBasedEnv: """The base environment encapsulates the simulation scene and the environment managers for the manager-based workflow. diff --git a/source/isaaclab/isaaclab/envs/manager_based_rl_env.py b/source/isaaclab/isaaclab/envs/manager_based_rl_env.py index c69f8572b0c1..3e48dcd19f88 100644 --- a/source/isaaclab/isaaclab/envs/manager_based_rl_env.py +++ b/source/isaaclab/isaaclab/envs/manager_based_rl_env.py @@ -21,6 +21,7 @@ from .manager_based_env import ManagerBasedEnv from .manager_based_rl_env_cfg import ManagerBasedRLEnvCfg + class ManagerBasedRLEnv(ManagerBasedEnv, gym.Env): """The superclass for the manager-based workflow reinforcement learning-based environments. diff --git a/source/isaaclab/isaaclab/markers/visualization_markers.py b/source/isaaclab/isaaclab/markers/visualization_markers.py index 447d439ff034..b8ddf891010f 100644 --- a/source/isaaclab/isaaclab/markers/visualization_markers.py +++ b/source/isaaclab/isaaclab/markers/visualization_markers.py @@ -250,9 +250,8 @@ def _ensure_backends_initialized(self) -> None: self._ensure_kit_backend() return - needs_kit_backend = ( - bool(getattr(sim, "is_rendering", False)) - or any(viz.supports_markers() and viz.pumps_app_update() and viz.cfg.enable_markers for viz in sim.visualizers) + needs_kit_backend = bool(getattr(sim, "is_rendering", False)) or any( + viz.supports_markers() and viz.pumps_app_update() and viz.cfg.enable_markers for viz in sim.visualizers ) if needs_kit_backend: self._ensure_kit_backend() diff --git a/source/isaaclab_newton/changelog.d/mtrepte-update-debug-viz.rst b/source/isaaclab_newton/changelog.d/mtrepte-update-debug-viz.rst new file mode 100644 index 000000000000..3295f14e5270 --- /dev/null +++ b/source/isaaclab_newton/changelog.d/mtrepte-update-debug-viz.rst @@ -0,0 +1,4 @@ +Fixed +^^^^^ + +* Fixed Newton physics timeline controls so Kit-driven visualizer pause and resume controls keep the UI responsive. diff --git a/source/isaaclab_physx/changelog.d/mtrepte-update-debug-viz.skip b/source/isaaclab_physx/changelog.d/mtrepte-update-debug-viz.skip new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/source/isaaclab_physx/changelog.d/mtrepte-update-debug-viz.skip @@ -0,0 +1 @@ + diff --git a/source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py b/source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py index 5da2f3cd34e7..dba8377c7d1b 100644 --- a/source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py +++ b/source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py @@ -43,6 +43,7 @@ logger = logging.getLogger(__name__) + class IsaacEvents(Enum): """Events dispatched during simulation lifecycle. diff --git a/source/isaaclab_rl/changelog.d/mtrepte-update-debug-viz.skip b/source/isaaclab_rl/changelog.d/mtrepte-update-debug-viz.skip new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/source/isaaclab_rl/changelog.d/mtrepte-update-debug-viz.skip @@ -0,0 +1 @@ + diff --git a/source/isaaclab_rl/isaaclab_rl/rsl_rl/vecenv_wrapper.py b/source/isaaclab_rl/isaaclab_rl/rsl_rl/vecenv_wrapper.py index e63d3c9f16db..562fca1c24cc 100644 --- a/source/isaaclab_rl/isaaclab_rl/rsl_rl/vecenv_wrapper.py +++ b/source/isaaclab_rl/isaaclab_rl/rsl_rl/vecenv_wrapper.py @@ -21,6 +21,7 @@ with contextlib.suppress(ImportError): from isaaclab_experimental.envs import DirectRLEnvWarp, ManagerBasedRLEnvWarp + class RslRlVecEnvWrapper(VecEnv): """Wraps around Isaac Lab environment for the RSL-RL library diff --git a/source/isaaclab_tasks/changelog.d/mtrepte-update-debug-viz.skip b/source/isaaclab_tasks/changelog.d/mtrepte-update-debug-viz.skip new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/source/isaaclab_tasks/changelog.d/mtrepte-update-debug-viz.skip @@ -0,0 +1 @@ + diff --git a/source/isaaclab_tasks/isaaclab_tasks/utils/sim_launcher.py b/source/isaaclab_tasks/isaaclab_tasks/utils/sim_launcher.py index 76a8fd96c594..3030bda1c4b7 100644 --- a/source/isaaclab_tasks/isaaclab_tasks/utils/sim_launcher.py +++ b/source/isaaclab_tasks/isaaclab_tasks/utils/sim_launcher.py @@ -19,6 +19,7 @@ logger = logging.getLogger(__name__) + def add_launcher_args(parser: argparse.ArgumentParser) -> None: """Add simulation-launcher CLI arguments (``--headless``, ``--device``, etc.) to *parser*. diff --git a/source/isaaclab_visualizers/changelog.d/mtrepte-update-debug-viz.rst b/source/isaaclab_visualizers/changelog.d/mtrepte-update-debug-viz.rst new file mode 100644 index 000000000000..c26216180202 --- /dev/null +++ b/source/isaaclab_visualizers/changelog.d/mtrepte-update-debug-viz.rst @@ -0,0 +1,5 @@ +Changed +^^^^^^^ + +* Changed Rerun and Viser visualizers to avoid opening browser tabs by default and to show browser URLs in the startup logs instead. +* Changed visualizer initialization tables to debug-level logging to reduce default startup log noise. diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py index da057c1c116d..4446fa6f92a6 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py @@ -43,6 +43,7 @@ def _disable_viser_runtime_client_rebuild_if_bundled() -> None: client_autobuild.ensure_client_is_built = lambda: None + def _open_viser_web_viewer(port: int) -> None: """Open the local viser web UI in a browser.""" url = _viser_web_viewer_url(port) diff --git a/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py b/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py index 1473113ae6cb..ce6ab295af52 100644 --- a/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py +++ b/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py @@ -35,7 +35,6 @@ import logging import socket -import omni.timeline import numpy as np import pytest import torch @@ -43,6 +42,8 @@ from isaaclab_visualizers.kit import KitVisualizer, KitVisualizerCfg from isaaclab_visualizers.newton import NewtonVisualizer, NewtonVisualizerCfg +import omni.timeline + import isaaclab.sim as sim_utils from isaaclab.sim import SimulationContext From 81ea123be65a44eadfa6775cf9532eb9e9c0113a Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Wed, 13 May 2026 23:19:41 +0000 Subject: [PATCH 08/31] rm test skips --- .../test/test_visualizer_cartpole_integration.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py b/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py index ce6ab295af52..8df7056ee553 100644 --- a/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py +++ b/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py @@ -565,16 +565,7 @@ def _make_cartpole_camera_env(visualizer_kind: str, backend_kind: str) -> Cartpo @pytest.mark.isaacsim_ci @pytest.mark.parametrize( "backend_kind", - [ - pytest.param( - "physx", - marks=pytest.mark.xfail( - reason=("Kit visualizer + PhysX: TODO remove xfail when stale Fabric transforms bug in Kit is fixed"), - strict=False, - ), - ), - "newton", - ], + ["physx", "newton"], ) def test_cartpole_kit_visualizer_replicator_viewport_rgb_motion( backend_kind: str, caplog: pytest.LogCaptureFixture From 9f532811830a311258bd2f8cf53bf824c283fcec Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Wed, 13 May 2026 23:26:49 +0000 Subject: [PATCH 09/31] docs --- docs/source/features/visualization.rst | 94 +++++++++---------- .../changelog.d/mtrepte-update-debug-viz.skip | 1 - .../changelog.d/mtrepte-update-debug-viz.skip | 1 - .../changelog.d/mtrepte-update-debug-viz.skip | 1 - 4 files changed, 46 insertions(+), 51 deletions(-) diff --git a/docs/source/features/visualization.rst b/docs/source/features/visualization.rst index 5cbfe1bc78a7..b70f3496f4eb 100644 --- a/docs/source/features/visualization.rst +++ b/docs/source/features/visualization.rst @@ -83,24 +83,6 @@ To run in headless mode, omit the ``--viz`` argument: The ``--headless`` argument is deprecated. For compatibility, ``--headless`` still takes precedence and disables all visualizers. -.. important:: - - Rerun and Viser do not open a browser tab by default. A highlighted browser URL is printed in the logs - before the simulation or training starts. In supported terminals and IDEs, Ctrl-click the printed - ``http://...`` URL to open the viewer manually. To set automatic browser launch, set ``open_browser=True`` - on the visualizer config. - - Example: - - .. code-block:: text - - ╭────── viser (listening *:8080) ───────╮ - │ ╷ │ - │ HTTP │ http://localhost:8080 │ - │ Websocket │ ws://localhost:8080 │ - │ ╵ │ - ╰───────────────────────────────────────╯ - .. _visualization-configuration: @@ -408,6 +390,22 @@ Rerun Visualizer - Timeline scrubbing and playback controls of recordings - Visualization debug markers +.. important:: + + A highlighted Rerun browser URL is printed in the logs before the main simulation or training loop begins. + Ctrl-click the printed URL in supported terminals/IDEs to open it. Set ``open_browser=True`` to automatically + open the browser tab instead. + + Example: + + .. code-block:: text + + ╭─────────────────────────── rerun (listening *:9090) ───────────────────────────╮ + │ ╷ │ + │ HTTP │ http://127.0.0.1:9090/?url=rerun%2Bhttp://127.0.0.1:9876/proxy │ + │ ╵ │ + ╰────────────────────────────────────────────────────────────────────────────────╯ + **Core Configuration:** .. code-block:: python @@ -438,12 +436,6 @@ Rerun startup uses the Python SDK through ``newton.viewer.ViewerRerun`` (no exte management). If ``grpc_port`` is already active, Isaac Lab reuses that server. If ``web_port`` is occupied while starting a new server, initialization fails with a clear port-conflict error. -.. important:: - - A highlighted Rerun browser URL is printed in the logs before the main simulation or training loop begins. - Ctrl-click the printed URL in supported terminals/IDEs to open it. Set ``open_browser=True`` to automatically - open the browser tab instead. - Viser Visualizer ~~~~~~~~~~~~~~~~ @@ -452,7 +444,7 @@ The `Viser `_ visualizer provides a **web-based** 3D view simulations powered by the Newton Warp renderer. It streams the simulation state to a local web server, allowing you to view and interact with the scene from any browser. -**Key features:** +**Main Features:** - Browser-based visualization accessible at ``http://localhost:8080`` by default - Optional public share URL for remote viewing @@ -460,41 +452,47 @@ server, allowing you to view and interact with the scene from any browser. - Environment filtering to control which environments are rendered - Visualization debug markers -**Launch with Viser:** +.. important:: -.. code-block:: bash + A highlighted Viser browser URL is printed in the logs before the main simulation or training loop begins. + Ctrl-click the printed URL in supported terminals/IDEs to open it. Set ``open_browser=True`` to automatically + open the browser tab instead. + + Example: - ./isaaclab.sh -p source/isaaclab_tasks/isaaclab_tasks/direct/cartpole/cartpole_env.py --viz viser + .. code-block:: text -**Configuration example:** + ╭────── viser (listening *:8080) ───────╮ + │ ╷ │ + │ HTTP │ http://localhost:8080 │ + │ Websocket │ ws://localhost:8080 │ + │ ╵ │ + ╰───────────────────────────────────────╯ + +**Core Configuration:** .. code-block:: python from isaaclab_visualizers.viser import ViserVisualizerCfg visualizer_cfg = ViserVisualizerCfg( - port=8080, - open_browser=False, - label="Isaac Lab Simulation", - share=False, - max_visible_envs=16, - ) - -**Configuration options:** + # Server settings + port=8080, # Port for local Viser web server + open_browser=False, # Set True to auto-launch the browser + label="Isaac Lab Simulation", # Page title shown in the viewer + share=False, # Request a public share URL for remote viewing + verbose=True, # Print viewer server startup information -- ``port`` (int, default ``8080``): Port of the local Viser web server. -- ``open_browser`` (bool, default ``False``): Automatically open the viewer URL in a browser. The URL is logged even - when this is ``False``. -- ``label`` (str or None, default ``"Isaac Lab Simulation"``): Page title shown in the viewer. -- ``share`` (bool, default ``False``): Request a public share URL from Viser for remote viewing. -- ``record_to_viser`` (str or None, default ``None``): Path to save a ``.viser`` recording file. -- ``verbose`` (bool, default ``True``): Print viewer server startup information. + # Camera settings + eye=(8.0, 8.0, 3.0), # Initial camera position (x, y, z) + lookat=(0.0, 0.0, 0.0), # Camera look-at target -.. important:: + # Environment filtering + max_visible_envs=16, # Maximum number of environments to visualize - A highlighted Viser browser URL is printed in the logs before the main simulation or training loop begins. - Ctrl-click the printed URL in supported terminals/IDEs to open it. Set ``open_browser=True`` to automatically - open the browser tab instead. + # Recording + record_to_viser="recording.viser", # Path to save .viser file (None = no recording) + ) .. note:: diff --git a/source/isaaclab_physx/changelog.d/mtrepte-update-debug-viz.skip b/source/isaaclab_physx/changelog.d/mtrepte-update-debug-viz.skip index 8b137891791f..e69de29bb2d1 100644 --- a/source/isaaclab_physx/changelog.d/mtrepte-update-debug-viz.skip +++ b/source/isaaclab_physx/changelog.d/mtrepte-update-debug-viz.skip @@ -1 +0,0 @@ - diff --git a/source/isaaclab_rl/changelog.d/mtrepte-update-debug-viz.skip b/source/isaaclab_rl/changelog.d/mtrepte-update-debug-viz.skip index 8b137891791f..e69de29bb2d1 100644 --- a/source/isaaclab_rl/changelog.d/mtrepte-update-debug-viz.skip +++ b/source/isaaclab_rl/changelog.d/mtrepte-update-debug-viz.skip @@ -1 +0,0 @@ - diff --git a/source/isaaclab_tasks/changelog.d/mtrepte-update-debug-viz.skip b/source/isaaclab_tasks/changelog.d/mtrepte-update-debug-viz.skip index 8b137891791f..e69de29bb2d1 100644 --- a/source/isaaclab_tasks/changelog.d/mtrepte-update-debug-viz.skip +++ b/source/isaaclab_tasks/changelog.d/mtrepte-update-debug-viz.skip @@ -1 +0,0 @@ - From b196cf6d1a3540eb8dfa2784ce27f7883a642b00 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Wed, 13 May 2026 23:37:06 +0000 Subject: [PATCH 10/31] fix tests --- .../test/test_visualizer_cartpole_integration.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py b/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py index 8df7056ee553..fb79138f1014 100644 --- a/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py +++ b/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py @@ -21,10 +21,18 @@ # Pyglet must use HeadlessWindow (EGL) before ``pyglet.window`` is imported so Newton # ViewerGL can construct without an X11 display (matches ``headless=True`` on NewtonVisualizerCfg). +import types + import pyglet +import warp as wp pyglet.options["headless"] = True +# TODO(mtrepte): check if this is still needed on TOT Lab/Sim before merge +# Replicator 1.13 expects older Warp API aliases during Kit startup. +wp.context = types.SimpleNamespace(Kernel=wp.Kernel) +wp.types.array = wp.array + from isaaclab.app import AppLauncher # launch Kit app @@ -38,7 +46,6 @@ import numpy as np import pytest import torch -import warp as wp from isaaclab_visualizers.kit import KitVisualizer, KitVisualizerCfg from isaaclab_visualizers.newton import NewtonVisualizer, NewtonVisualizerCfg From 44fb716793d7b6d600824ce826a53a54522fe144 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Thu, 14 May 2026 20:44:43 +0000 Subject: [PATCH 11/31] wip --- .../isaaclab/envs/utils/camera_view.py | 97 ++++++++++++++--- .../isaaclab/visualizers/visualizer_cfg.py | 20 ++-- .../renderers/newton_warp_renderer.py | 101 +++++++++++++----- .../kit/kit_visualizer.py | 57 +++++++++- .../newton/newton_visualizer.py | 42 ++++++-- 5 files changed, 258 insertions(+), 59 deletions(-) diff --git a/source/isaaclab/isaaclab/envs/utils/camera_view.py b/source/isaaclab/isaaclab/envs/utils/camera_view.py index 5ad5e23b0593..12296228f7d6 100644 --- a/source/isaaclab/isaaclab/envs/utils/camera_view.py +++ b/source/isaaclab/isaaclab/envs/utils/camera_view.py @@ -163,6 +163,20 @@ def compose_rgb_grid(rgb_batch: torch.Tensor) -> np.ndarray: return rgb_np.reshape(rows, cols, h, w, 3).transpose(0, 2, 1, 3, 4).reshape(rows * h, cols * w, 3) +def compose_rgb_grid_tensor(rgb_batch: torch.Tensor) -> torch.Tensor: + """Compose an RGB batch into a near-square uint8 image grid without leaving its device.""" + if rgb_batch.ndim == 3: + return rgb_batch[..., :3].contiguous() + n, h, w, _ = rgb_batch.shape + cols = max(1, math.ceil(math.sqrt(n))) + rows = math.ceil(n / cols) + rgb = rgb_batch[..., :3] + pad = rows * cols - n + if pad > 0: + rgb = torch.cat([rgb, torch.zeros((pad, h, w, 3), dtype=rgb.dtype, device=rgb.device)], dim=0) + return rgb.reshape(rows, cols, h, w, 3).permute(0, 2, 1, 3, 4).reshape(rows * h, cols * w, 3).contiguous() + + def compute_tile_resolution(window_width: int, window_height: int, num_tiles: int) -> tuple[int, int]: """Derive a conservative per-tile resolution from the visualizer window.""" cols = max(1, math.ceil(math.sqrt(max(1, num_tiles)))) @@ -170,20 +184,66 @@ def compute_tile_resolution(window_width: int, window_height: int, num_tiles: in return max(1, int(window_width) // cols), max(1, int(window_height) // rows) -def prim_world_positions(stage: Any, prim_path_template: str, env_indices: list[int]) -> torch.Tensor: - """Return world-space translations for concrete prim paths resolved from env ids.""" +def _normalize_env0_path(path_template: str) -> str: + """Resolve env template spellings to env_0 for path comparison.""" + return env_path_from_template(path_template, 0) + + +def _scene_articulation_positions(scene: Any, prim_path_template: str, env_indices: list[int]) -> torch.Tensor | None: + """Resolve follow positions from scene articulation state when the path targets an asset/body.""" + follow_env0 = _normalize_env0_path(prim_path_template) + for asset in getattr(scene, "articulations", {}).values(): + asset_path = _normalize_env0_path(getattr(asset.cfg, "prim_path", "")) + if not asset_path: + continue + if follow_env0 == asset_path: + return asset.data.root_pos_w.torch[env_indices].detach().cpu() + prefix = asset_path + "/" + if not follow_env0.startswith(prefix): + continue + body_name = follow_env0.removeprefix(prefix).split("/")[-1] + if body_name not in asset.body_names: + continue + body_ids, _ = asset.find_bodies(body_name) + if not body_ids: + continue + return asset.data.body_pos_w.torch[env_indices, int(body_ids[0])].detach().cpu() + return None + + +def prim_world_positions(stage: Any, prim_path_template: str, env_indices: list[int], scene: Any | None = None) -> torch.Tensor: + """Return world-space translations for concrete prim paths resolved from env ids. + + Uses ``FrameView`` first so PhysX/Fabric-backed transforms are current; falls + back to USD only if the backend view cannot be constructed. + """ from pxr import UsdGeom + from isaaclab.sim.views import FrameView + + if scene is not None: + positions = _scene_articulation_positions(scene, prim_path_template, env_indices) + if positions is not None: + return positions + xform_cache = UsdGeom.XformCache() positions = [] for env_id in env_indices: prim_path = env_path_from_template(prim_path_template, env_id) - prim = stage.GetPrimAtPath(prim_path) - if not prim.IsValid(): - raise RuntimeError(f"cam_follow_prim_path resolved to missing prim: {prim_path!r}.") - transform = xform_cache.GetLocalToWorldTransform(prim) - translation = transform.ExtractTranslation() - positions.append((float(translation[0]), float(translation[1]), float(translation[2]))) + try: + view = FrameView(prim_path, device="cpu", stage=stage) + if view.count != 1: + raise RuntimeError(f"expected one prim, got {view.count}") + pos_w, _ = view.get_world_poses() + pos = pos_w.torch[0].detach().cpu() + positions.append((float(pos[0]), float(pos[1]), float(pos[2]))) + except Exception: + prim = stage.GetPrimAtPath(prim_path) + if not prim.IsValid(): + raise RuntimeError(f"cam_follow_prim_path resolved to missing prim: {prim_path!r}.") + transform = xform_cache.GetLocalToWorldTransform(prim) + translation = transform.ExtractTranslation() + positions.append((float(translation[0]), float(translation[1]), float(translation[2]))) return torch.tensor(positions, dtype=torch.float32) @@ -199,13 +259,24 @@ def apply_camera_view_from_origins( origins = origins.to(device=device) eye_offset = torch.tensor(eye, dtype=torch.float32, device=device).unsqueeze(0) lookat_offset = torch.tensor(lookat, dtype=torch.float32, device=device).unsqueeze(0) - # Temporary validation mode: force every generated camera to the same pose - # as tile 0. If renderer batching is correct, all tiles should match. - origin0 = origins[:1] - eyes = (origin0 + eye_offset).repeat(origins.shape[0], 1) - targets = (origin0 + lookat_offset).repeat(origins.shape[0], 1) + camera.set_world_poses_from_view(origins + eye_offset, origins + lookat_offset) + camera._update_poses(None) + + +def apply_camera_follow_positions( + camera: Camera, + follow_positions: torch.Tensor, + eye: tuple[float, float, float], +) -> tuple[torch.Tensor, torch.Tensor]: + """Set generated camera poses to look at followed prim positions.""" + device = camera.device + follow_positions = follow_positions.to(device=device) + eye_offset = torch.tensor(eye, dtype=torch.float32, device=device).unsqueeze(0) + eyes = follow_positions + eye_offset + targets = follow_positions camera.set_world_poses_from_view(eyes, targets) camera._update_poses(None) + return eyes.detach().cpu(), targets.detach().cpu() def apply_debug_top_down_grid_camera_poses(camera: Camera, env_indices: list[int], spacing: float = 20.0) -> None: diff --git a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py index f8bbaded0bdf..87ca934aa021 100644 --- a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py +++ b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py @@ -34,11 +34,15 @@ class VisualizerCfg: enable_live_plots: bool = True """Enable live plotting of data.""" - eye: tuple[float, float, float] = (7.5, 7.5, 7.5) + eye: tuple[float, float, float] = (4.0, -4.0, 3.0) """Initial camera eye position (x, y, z) in world coordinates.""" lookat: tuple[float, float, float] = (0.0, 0.0, 0.0) - """Initial camera look-at point (x, y, z) in world coordinates.""" + """Initial camera look-at point (x, y, z) in world coordinates. + + Ignored for generated camera views when ``cam_follow_prim_path`` is set; those cameras + always look at the followed prim position. + """ cam_source: Literal["cfg", "prim_path"] = "cfg" """Camera source mode: 'cfg' uses eye/lookat, 'prim_path' uses Isaac Lab camera sensor output.""" @@ -46,18 +50,18 @@ class VisualizerCfg: cam_prim_path: str = "/World/envs/*/Camera" """Camera prim path or env wildcard path when cam_source='prim_path'.""" - cam_follow_prim_path: str | None = None + cam_follow_prim_path: str | None = "/World/envs/*/Robot/base" """Optional prim path whose world pose anchors generated camera views. - When set, camera controls are disabled and eye/lookat are interpreted relative - to the matched follow prim. Wildcards resolve to env_0 for mono view and to - selected envs for tiled view. + When set, camera controls are disabled. ``eye`` is interpreted as a world-axis + offset from each matched follow prim, and ``lookat`` is ignored. Wildcards + resolve to env_0 for mono view and to selected envs for tiled view. """ - tiled_cam_view: bool = True + tiled_cam_view: bool = False """Enable a non-interactive tiled camera image view.""" - tiled_cam_num: int = 24 + tiled_cam_num: int = 100 """Number of camera tiles to show when tiled_cam_env_indices is None.""" tiled_cam_env_indices: list[int] | None = None diff --git a/source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py b/source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py index 0ba3558c3504..08de615719d6 100644 --- a/source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py +++ b/source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py @@ -7,6 +7,7 @@ from __future__ import annotations +import inspect import logging from dataclasses import dataclass from typing import TYPE_CHECKING, Any @@ -101,9 +102,13 @@ def update(self, positions: torch.Tensor, orientations: torch.Tensor, intrinsics first_focal_length = intrinsics[:, 1, 1][0:1] fov_radians_all = 2.0 * torch.atan(self.height / (2.0 * first_focal_length)) - self.camera_rays = self.newton_sensor.utils.compute_pinhole_camera_rays( - self.width, self.height, wp.from_torch(fov_radians_all, dtype=wp.float32) - ) + fov_warp = wp.from_torch(fov_radians_all, dtype=wp.float32) + if hasattr(self.newton_sensor, "utils"): + self.camera_rays = self.newton_sensor.utils.compute_pinhole_camera_rays( + self.width, self.height, fov_warp + ) + else: + self.camera_rays = self.newton_sensor.compute_pinhole_camera_rays(self.width, self.height, fov_warp) def _from_torch(self, tensor: torch.Tensor, dtype) -> wp.array: proxy_array = wp.from_torch(tensor) @@ -166,27 +171,68 @@ def initialize(self) -> None: "Check the log for earlier Newton model build errors." ) - self.newton_sensor = newton.sensors.SensorTiledCamera( - newton_model, - config=newton.sensors.SensorTiledCamera.RenderConfig( - enable_textures=self.cfg.enable_textures, - enable_shadows=self.cfg.enable_shadows, - enable_ambient_lighting=self.cfg.enable_ambient_lighting, - enable_backface_culling=self.cfg.enable_backface_culling, - max_distance=self.cfg.max_distance, - ), - ) + sensor_cls = newton.sensors.SensorTiledCamera + sensor_config = self._create_sensor_config(sensor_cls) + self.newton_sensor = sensor_cls(newton_model, config=sensor_config) + self._apply_render_context_config() # Newton ``v1.2.0rc2`` made shape-BVH construction explicit; ``SensorTiledCamera.update`` # no longer auto-builds when a non-``None`` state is passed, and the underlying # ``RenderContext.render`` raises if ``build_bvh_shape`` was never called for the model. # Build it once per model — idempotent across multiple sensors that share ``newton_model`` # because subsequent calls overwrite the same model-level BVH attributes. - if newton_model.shape_count > 0 and newton_model.bvh_shapes is None: + if ( + newton_model.shape_count > 0 + and hasattr(newton_model, "bvh_shapes") + and newton_model.bvh_shapes is None + and hasattr(newton.geometry, "build_bvh_shape") + ): newton.geometry.build_bvh_shape(newton_model, newton_model.state()) if self.cfg.create_default_light: - self.newton_sensor.utils.create_default_light(enable_shadows=self.cfg.enable_shadows) + if hasattr(self.newton_sensor, "utils"): + self.newton_sensor.utils.create_default_light(enable_shadows=self.cfg.enable_shadows) + elif hasattr(self.newton_sensor, "create_default_light"): + self.newton_sensor.create_default_light(enable_shadows=self.cfg.enable_shadows) + + def _create_sensor_config(self, sensor_cls): + """Create a tiled-camera config compatible with the loaded Newton package.""" + if hasattr(sensor_cls, "RenderConfig"): + config_cls = sensor_cls.RenderConfig + requested = { + "enable_textures": self.cfg.enable_textures, + "enable_shadows": self.cfg.enable_shadows, + "enable_ambient_lighting": self.cfg.enable_ambient_lighting, + "enable_backface_culling": self.cfg.enable_backface_culling, + "max_distance": self.cfg.max_distance, + } + else: + config_cls = sensor_cls.Config + requested = { + "default_light": self.cfg.create_default_light, + "default_light_shadows": self.cfg.enable_shadows, + "enable_ambient_lighting": self.cfg.enable_ambient_lighting, + "enable_textures": self.cfg.enable_textures, + "backface_culling": self.cfg.enable_backface_culling, + } + params = inspect.signature(config_cls).parameters + return config_cls(**{name: value for name, value in requested.items() if name in params}) + + def _apply_render_context_config(self) -> None: + """Fill renderer options that older Newton config objects do not accept.""" + render_config = getattr(getattr(self.newton_sensor, "render_context", None), "config", None) + if render_config is None: + return + optional_values = { + "enable_textures": self.cfg.enable_textures, + "enable_shadows": self.cfg.enable_shadows, + "enable_ambient_lighting": self.cfg.enable_ambient_lighting, + "enable_backface_culling": self.cfg.enable_backface_culling, + "max_distance": self.cfg.max_distance, + } + for name, value in optional_values.items(): + if hasattr(render_config, name): + setattr(render_config, name, value) def supported_output_types(self) -> dict[RenderBufferKind, RenderBufferSpec]: """Publish the per-output layout this Newton Warp backend writes. @@ -236,20 +282,19 @@ def render(self, render_data: RenderData): newton_state = self.get_scene_data_provider().get_newton_state() # Refit the shape BVH against the current state since env body poses move every frame. # ``build_bvh_shape`` ran once in ``__init__``; ``refit_bvh_shape`` reuses that topology. - if self.newton_sensor.model.shape_count > 0: + if self.newton_sensor.model.shape_count > 0 and hasattr(newton.geometry, "refit_bvh_shape"): newton.geometry.refit_bvh_shape(self.newton_sensor.model, newton_state) - self.newton_sensor.update( - newton_state, - render_data.camera_transforms, - render_data.camera_rays, - color_image=render_data.outputs.color_image, - albedo_image=render_data.outputs.albedo_image, - depth_image=render_data.outputs.depth_image, - normal_image=render_data.outputs.normals_image, - shape_index_image=render_data.outputs.instance_segmentation_image, - # ARGB 93% gray to improve visibility of dark objects and align with RTX renderer background - clear_data=newton.sensors.SensorTiledCamera.ClearData(clear_color=0xFFEEEEEE), - ) + update_kwargs = { + "color_image": render_data.outputs.color_image, + "albedo_image": render_data.outputs.albedo_image, + "depth_image": render_data.outputs.depth_image, + "normal_image": render_data.outputs.normals_image, + "shape_index_image": render_data.outputs.instance_segmentation_image, + } + if hasattr(newton.sensors.SensorTiledCamera, "ClearData"): + # ARGB 93% gray to improve visibility of dark objects and align with RTX renderer background. + update_kwargs["clear_data"] = newton.sensors.SensorTiledCamera.ClearData(clear_color=0xFFEEEEEE) + self.newton_sensor.update(newton_state, render_data.camera_transforms, render_data.camera_rays, **update_kwargs) def read_output(self, render_data: RenderData, camera_data: CameraData) -> None: """Copy rendered outputs to the camera data buffers. diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py index 92904b8c1b6c..3771f89d9cfb 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py @@ -18,9 +18,11 @@ from isaaclab.envs.utils.camera_view import ( apply_debug_oblique_env_camera_poses, + apply_camera_follow_positions, apply_camera_view_from_origins, camera_rgb_batch, compose_rgb_grid, + compose_rgb_grid_tensor, compute_tile_resolution, create_visualizer_camera, find_camera_by_prim_path, @@ -78,6 +80,10 @@ def __init__(self, cfg: KitVisualizerCfg): self._camera_debug_frame = 0 self._camera_update_count = 0 self._camera_last_checksum: int | None = None + self._camera_pose_update_count = 0 + self._camera_last_pose_debug: tuple[float, ...] | None = None + self._camera_gpu_upload_tensor = None + self._warned_gpu_upload_failure = False self._physics_backend: str | None = None # ---- Lifecycle ------------------------------------------------------------------------ @@ -452,9 +458,15 @@ def _update_owned_camera_poses(self) -> None: ) return if self.cfg.cam_follow_prim_path: - origins = prim_world_positions( - self._scene_data_provider.get_usd_stage(), self.cfg.cam_follow_prim_path, self._camera_env_indices + follow_positions = prim_world_positions( + self._scene_data_provider.get_usd_stage(), + self.cfg.cam_follow_prim_path, + self._camera_env_indices, + scene=self._scene_data_provider.get_interactive_scene(), ) + eyes, targets = apply_camera_follow_positions(self._camera_sensor, follow_positions, self.cfg.eye) + self._log_camera_pose_debug(eyes, targets) + return else: env_origins = self._scene_data_provider.get_interactive_scene().env_origins origins = env_origins[self._camera_env_indices].detach().cpu() @@ -462,6 +474,23 @@ def _update_owned_camera_poses(self) -> None: self._camera_sensor, origins, self.cfg.eye, self.cfg.lookat, self._camera_env_indices ) + def _log_camera_pose_debug(self, eyes, targets) -> None: + """Log generated camera pose changes for follow-mode debugging.""" + self._camera_pose_update_count += 1 + first_eye = tuple(float(v) for v in eyes[0]) + first_target = tuple(float(v) for v in targets[0]) + pose_key = (*first_eye, *first_target) + changed = self._camera_last_pose_debug is None or pose_key != self._camera_last_pose_debug + if self._camera_pose_update_count <= 5 or self._camera_pose_update_count % 30 == 0: + logger.warning( + "[KitVisualizer] Camera pose update #%s first_eye=%s first_target=%s changed=%s", + self._camera_pose_update_count, + first_eye, + first_target, + changed, + ) + self._camera_last_pose_debug = pose_key + def _update_camera_image_panel(self, dt: float) -> None: """Refresh the non-interactive Kit image panel from camera RGB output.""" if self._camera_sensor is None or self._camera_image_provider is None: @@ -508,11 +537,31 @@ def _update_camera_image_panel(self, dt: float) -> None: except Exception as exc: logger.warning("[KitVisualizer] Failed to save individual camera debug tiles: %s", exc) self._logged_camera_panel_after_data = True - image = compose_rgb_grid(rgb) if self.cfg.tiled_cam_view else rgb[0].detach().contiguous().cpu().numpy() + image = compose_rgb_grid_tensor(rgb) if self.cfg.tiled_cam_view else rgb[0].contiguous() self._upload_camera_image_to_panel(image) - def _upload_camera_image_to_panel(self, image: np.ndarray) -> None: + def _upload_camera_image_to_panel(self, image: np.ndarray | torch.Tensor) -> None: """Upload an RGB/RGBA image to the Kit image provider.""" + if isinstance(image, torch.Tensor): + if image.is_cuda: + try: + import omni.gpu_foundation_factory as gf + + if image.ndim == 3 and image.shape[2] == 3: + alpha = torch.full((*image.shape[:2], 1), 255, dtype=torch.uint8, device=image.device) + image = torch.cat((image, alpha), dim=2) + image = image.to(dtype=torch.uint8).contiguous() + self._camera_gpu_upload_tensor = image + self._camera_image_provider.set_bytes_data_from_gpu( + int(image.data_ptr()), [int(image.shape[1]), int(image.shape[0])], gf.TextureFormat.RGBA8_UNORM + ) + return + except Exception as exc: + if not self._warned_gpu_upload_failure: + logger.warning("[KitVisualizer] GPU image upload failed; falling back to CPU upload: %s", exc) + self._warned_gpu_upload_failure = True + image = image.detach().contiguous().cpu().numpy() + if not hasattr(self, "_logged_camera_panel_image_stats"): channel_mean = image[..., :3].reshape(-1, 3).mean(axis=0) logger.warning( diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py index 220724a5a4c1..5226439d5d00 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py @@ -8,6 +8,7 @@ from __future__ import annotations import logging +import os from typing import TYPE_CHECKING import numpy as np @@ -18,6 +19,7 @@ from isaaclab.envs.utils.camera_view import ( apply_debug_oblique_env_camera_poses, + apply_camera_follow_positions, apply_camera_view_from_origins, camera_rgb_batch, compute_tile_resolution, @@ -299,6 +301,8 @@ def __init__(self, cfg: NewtonVisualizerCfg): self._logged_camera_stats = False self._camera_update_count = 0 self._camera_last_checksum: int | None = None + self._camera_pose_update_count = 0 + self._camera_last_pose_debug: tuple[float, ...] | None = None def initialize(self, scene_data_provider: BaseSceneDataProvider) -> None: """Initialize viewer resources and bind scene data provider. @@ -319,9 +323,12 @@ def initialize(self, scene_data_provider: BaseSceneDataProvider) -> None: self._model = scene_data_provider.get_newton_model() self._state = scene_data_provider.get_newton_state() - # Use pyglet's EGL headless backend when requested. Must run before the first - # ``pyglet.window`` import so ``Window`` resolves to :class:`~pyglet.window.headless.HeadlessWindow`. - if self.cfg.headless: + runtime_headless = self.cfg.headless or not os.environ.get("DISPLAY") + + # Use pyglet's EGL headless backend when requested or when no X display is available. + # This must run before the first ``pyglet.window`` import so ``Window`` resolves to + # :class:`~pyglet.window.headless.HeadlessWindow`. + if runtime_headless: import pyglet pyglet.options["headless"] = True @@ -329,7 +336,7 @@ def initialize(self, scene_data_provider: BaseSceneDataProvider) -> None: self._viewer = NewtonViewerGL( width=self.cfg.window_width, height=self.cfg.window_height, - headless=self.cfg.headless, + headless=runtime_headless, metadata=metadata, update_frequency=self.cfg.update_frequency, ) @@ -548,9 +555,15 @@ def _update_owned_camera_poses(self) -> None: ) return if self.cfg.cam_follow_prim_path: - origins = prim_world_positions( - self._scene_data_provider.get_usd_stage(), self.cfg.cam_follow_prim_path, self._camera_env_indices + follow_positions = prim_world_positions( + self._scene_data_provider.get_usd_stage(), + self.cfg.cam_follow_prim_path, + self._camera_env_indices, + scene=self._scene_data_provider.get_interactive_scene(), ) + eyes, targets = apply_camera_follow_positions(self._camera_sensor, follow_positions, self.cfg.eye) + self._log_camera_pose_debug(eyes, targets) + return else: env_origins = self._scene_data_provider.get_interactive_scene().env_origins origins = env_origins[self._camera_env_indices].detach().cpu() @@ -558,6 +571,23 @@ def _update_owned_camera_poses(self) -> None: self._camera_sensor, origins, self.cfg.eye, self.cfg.lookat, self._camera_env_indices ) + def _log_camera_pose_debug(self, eyes, targets) -> None: + """Log generated camera pose changes for follow-mode debugging.""" + self._camera_pose_update_count += 1 + first_eye = tuple(float(v) for v in eyes[0]) + first_target = tuple(float(v) for v in targets[0]) + pose_key = (*first_eye, *first_target) + changed = self._camera_last_pose_debug is None or pose_key != self._camera_last_pose_debug + if self._camera_pose_update_count <= 5 or self._camera_pose_update_count % 30 == 0: + logger.warning( + "[NewtonVisualizer] Camera pose update #%s first_eye=%s first_target=%s changed=%s", + self._camera_pose_update_count, + first_eye, + first_target, + changed, + ) + self._camera_last_pose_debug = pose_key + def _log_camera_sensor_image(self) -> None: """Log the selected camera sensor RGB output into Newton's image panel.""" if self._viewer is None or self._camera_sensor is None: From d09aa0b92ea71a0cbc835b02a26861eb09cd1c94 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Fri, 15 May 2026 02:17:37 +0000 Subject: [PATCH 12/31] prepping for review --- .../isaaclab/isaaclab/sim/simulation_cfg.py | 2 +- .../isaaclab/sim/simulation_context.py | 5 ++ .../isaaclab/visualizers/base_visualizer.py | 33 +++++++---- .../test_simulation_context_visualizers.py | 15 +++-- .../manipulation/dexsuite/mdp/observations.py | 5 ++ .../kit/kit_visualizer.py | 15 ++--- .../rerun/rerun_visualizer.py | 1 + .../viser/viser_visualizer.py | 57 +++++++++++++------ .../viser/viser_visualizer_cfg.py | 12 ++++ 9 files changed, 101 insertions(+), 44 deletions(-) diff --git a/source/isaaclab/isaaclab/sim/simulation_cfg.py b/source/isaaclab/isaaclab/sim/simulation_cfg.py index 52b8d2c189f7..eb0f9821592c 100644 --- a/source/isaaclab/isaaclab/sim/simulation_cfg.py +++ b/source/isaaclab/isaaclab/sim/simulation_cfg.py @@ -260,7 +260,7 @@ class SimulationCfg: """ use_fabric: bool = True - """Enable/disable reading of physics buffers directly. Default is False. + """Enable/disable reading of physics buffers directly. Default is True. When running the simulation, updates in the states in the scene is normally synchronized with USD. This leads to an overhead in reading the data and does not scale well with massive parallelization. diff --git a/source/isaaclab/isaaclab/sim/simulation_context.py b/source/isaaclab/isaaclab/sim/simulation_context.py index 175961fcd383..9cb57a6d9ebc 100644 --- a/source/isaaclab/isaaclab/sim/simulation_context.py +++ b/source/isaaclab/isaaclab/sim/simulation_context.py @@ -761,6 +761,11 @@ def update_visualizers(self, dt: float, skip_app_pumping: bool = False) -> None: if not self._visualizers: return + for viz in self._visualizers: + flush_startup_messages = getattr(viz, "flush_startup_messages", None) + if callable(flush_startup_messages): + flush_startup_messages() + if self._should_forward_before_visualizer_update(): self.physics_manager.forward() diff --git a/source/isaaclab/isaaclab/visualizers/base_visualizer.py b/source/isaaclab/isaaclab/visualizers/base_visualizer.py index 73aa0a6f6fd6..10283c3e84f3 100644 --- a/source/isaaclab/isaaclab/visualizers/base_visualizer.py +++ b/source/isaaclab/isaaclab/visualizers/base_visualizer.py @@ -39,6 +39,7 @@ def __init__(self, cfg: VisualizerCfg): self._scene_data_provider = None self._is_initialized = False self._is_closed = False + self._deferred_startup_messages: list[str] = [] @abstractmethod def initialize(self, scene_data_provider: SceneDataProvider) -> None: @@ -311,34 +312,44 @@ def _log_initialization_table(self, logger: logging.Logger, title: str, rows: li table.add_row([key, value]) logger.debug("Visualizer initialization:\n%s", table.get_string()) - @staticmethod - def _log_viewer_url(logger: logging.Logger, visualizer_name: str, viewer_url: str) -> None: - """Log a visible browser URL block for web-based visualizers. + def _log_viewer_url( + self, + logger: logging.Logger, + visualizer_name: str, + viewer_url: str, + ) -> None: + """Queue a visible browser URL block for web-based visualizers. Args: - logger: Logger used to emit the URL block. + logger: Unused; kept for call-site compatibility. visualizer_name: Name of the visualizer exposing the URL. viewer_url: Browser URL for the visualizer. """ + del logger parsed_url = urlparse(viewer_url) visualizer_label = visualizer_name.removesuffix("Visualizer").lower() title = f" {visualizer_label} (listening *:{parsed_url.port}) " if parsed_url.port else f" {visualizer_label} " - label_width = 9 + label = "URL" + label_width = max(9, len(label)) value_width = max(len(viewer_url), len(title) + 2, 21) inner_width = label_width + value_width + 9 left_rule_width = max((inner_width - len(title)) // 2, 1) right_rule_width = max(inner_width - len(title) - left_rule_width, 1) - logger.warning( - "\n%s\n%s\n%s\n%s\n%s\n%s server running at: %s\n", + lines = [ f"╭{'─' * left_rule_width}{title}{'─' * right_rule_width}╮", f"│{' ' * (label_width + 4)}╷{' ' * (value_width + 4)}│", - f"│ {'HTTP':<{label_width}} │ {viewer_url:<{value_width}} │", + f"│ {label:<{label_width}} │ {viewer_url:<{value_width}} │", f"│{' ' * (label_width + 4)}╵{' ' * (value_width + 4)}│", f"╰{'─' * inner_width}╯", - visualizer_label.capitalize(), - viewer_url, - ) + ] + self._deferred_startup_messages.append("\n".join(lines)) + + def flush_startup_messages(self) -> None: + """Print deferred startup messages immediately before the workflow update loop starts.""" + for message in self._deferred_startup_messages: + print(message, flush=True) + self._deferred_startup_messages.clear() def play(self) -> None: """Handle simulation play/start. No-op by default.""" diff --git a/source/isaaclab/test/sim/test_simulation_context_visualizers.py b/source/isaaclab/test/sim/test_simulation_context_visualizers.py index 93377d13a5bc..ae0b865e9062 100644 --- a/source/isaaclab/test/sim/test_simulation_context_visualizers.py +++ b/source/isaaclab/test/sim/test_simulation_context_visualizers.py @@ -536,6 +536,7 @@ def __init__( self, *, port: int, + bind_address: str, label: str | None, verbose: bool, share: bool, @@ -544,6 +545,7 @@ def __init__( ): captured["init"] = { "port": port, + "bind_address": bind_address, "label": label, "verbose": verbose, "share": share, @@ -579,19 +581,20 @@ def set_world_offsets(self, spacing) -> None: visualizer._create_viewer(record_to_viser="record.viser", metadata={"num_envs": 8}) assert captured["set_model"] == "dummy-model" - assert captured["init"]["open_browser"] is False + assert captured["init"]["bind_address"] == cfg.bind_address assert captured["visible_worlds"] == expected_visible assert captured["set_world_offsets"] == (0.0, 0.0, 0.0) def test_viser_visualizer_open_browser_is_opt_in(monkeypatch: pytest.MonkeyPatch): - opened_ports = [] + opened_urls = [] class _FakeNewtonViewerViser: def __init__( self, *, port: int, + bind_address: str, label: str | None, verbose: bool, share: bool, @@ -610,7 +613,7 @@ def set_world_offsets(self, spacing) -> None: pass monkeypatch.setattr(viser_visualizer, "NewtonViewerViser", _FakeNewtonViewerViser) - monkeypatch.setattr(viser_visualizer, "_open_viser_web_viewer", lambda port: opened_ports.append(port)) + monkeypatch.setattr(viser_visualizer, "_open_viser_web_viewer", lambda url: opened_urls.append(url)) monkeypatch.setattr( viser_visualizer.ViserVisualizer, "_resolve_initial_camera_pose", @@ -618,13 +621,13 @@ def set_world_offsets(self, spacing) -> None: ) monkeypatch.setattr(viser_visualizer.ViserVisualizer, "_set_viser_camera_view", lambda self, pose: None) - cfg = ViserVisualizerCfg(open_browser=True) + cfg = ViserVisualizerCfg(open_browser=True, display_address="robot.example.com") visualizer = viser_visualizer.ViserVisualizer(cfg) visualizer._model = "dummy-model" visualizer._env_ids = None visualizer._create_viewer(record_to_viser=None, metadata={"num_envs": 1}) - assert opened_ports == [cfg.port] + assert opened_urls == [f"http://robot.example.com:{cfg.port}"] @pytest.mark.parametrize( @@ -766,6 +769,8 @@ def close(self) -> None: pass class _DummyRerunSceneDataProvider: + num_envs = 1 + def get_metadata(self) -> dict: return {"num_envs": 1} diff --git a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/mdp/observations.py b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/mdp/observations.py index fe666a7ecffe..1a8a87c0835f 100644 --- a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/mdp/observations.py +++ b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/mdp/observations.py @@ -217,6 +217,11 @@ def __call__( ) -> torch.Tensor: # obtain the input image images = self.sensor.data.output[self.sensor_type] torch.nan_to_num_(images, nan=1e6) + # TEMP DEBUG: remove after verifying the Dexsuite table marker and robot motion in raw RTX observations. + debug_count = getattr(self, "_debug_collage_count", 0) + if debug_count < 8: + self.show_collage(images, f"observation_{debug_count:03d}.png") + self._debug_collage_count = debug_count + 1 if normalize: images = self.norm_fn(images) images = images.permute(0, 3, 1, 2).contiguous() diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py index 5e5420adcaf9..b006ad16d534 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py @@ -11,6 +11,7 @@ import logging from typing import TYPE_CHECKING +import carb from pxr import Gf, Usd, UsdGeom, Vt from isaaclab.app.settings_manager import get_settings_manager @@ -72,8 +73,6 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: usd_stage = scene_data_provider.usd_stage if usd_stage is None: raise RuntimeError("[KitVisualizer] USD stage not available from scene_data_provider.") - metadata = scene_data_provider.get_metadata() - self._physics_backend = str(metadata.get("physics_backend", "")).lower() num_envs = scene_data_provider.num_envs self._ensure_simulation_app() @@ -122,16 +121,10 @@ def step(self, dt: float) -> None: if app is not None and app.is_running(): # Keep app pumping for viewport/UI updates only; physics is owned by SimulationContext. # Disable playSimulations around app.update() so Kit does not advance its own physics here. - # Newton is not driven by Kit's player loop, so avoid toggling the setting; the Kit - # play/pause UI needs to control the timeline without this visualizer overwriting it. - settings = get_settings_manager() - guard_play_simulations = self._physics_backend != "newton" - play_before = settings.get("/app/player/playSimulations") - if guard_play_simulations: - settings.set_bool("/app/player/playSimulations", False) + settings = carb.settings.get_settings() + settings.set_bool("/app/player/playSimulations", False) app.update() - if guard_play_simulations: - settings.set_bool("/app/player/playSimulations", True if play_before is None else bool(play_before)) + settings.set_bool("/app/player/playSimulations", True) except (ImportError, AttributeError) as exc: logger.debug("[KitVisualizer] App update skipped: %s", exc) # Markers (VisualizationMarkers) are often created or resized to num_envs only after the first diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py index 89f46ed18748..5508462e784f 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py @@ -87,6 +87,7 @@ def _open_rerun_web_viewer(host: str, web_port: int, connect_to: str) -> None: def _rerun_web_viewer_url(host: str, web_port: int, connect_to: str) -> str: """Return rerun web UI URL with prefilled endpoint.""" + # Keep the nested URL readable while still encoding '+' in the rerun+http scheme. return f"http://{host}:{int(web_port)}/?url={quote(connect_to, safe=':/')}" diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py index abbb272c2b5d..cc420e245b73 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py @@ -7,6 +7,7 @@ from __future__ import annotations +import contextlib import logging import os import webbrowser @@ -44,9 +45,8 @@ def _disable_viser_runtime_client_rebuild_if_bundled() -> None: client_autobuild.ensure_client_is_built = lambda: None -def _open_viser_web_viewer(port: int) -> None: - """Open the local viser web UI in a browser.""" - url = _viser_web_viewer_url(port) +def _open_viser_web_viewer(url: str) -> None: + """Open the Viser web UI in a browser.""" try: if not webbrowser.open_new_tab(url): logger.info("[ViserVisualizer] Could not auto-open browser tab. Open manually: %s", url) @@ -54,9 +54,9 @@ def _open_viser_web_viewer(port: int) -> None: logger.info("[ViserVisualizer] Could not auto-open browser tab. Open manually: %s", url) -def _viser_web_viewer_url(port: int) -> str: - """Return local viser web UI URL.""" - return f"http://localhost:{int(port)}" +def _viser_web_viewer_url(port: int, display_address: str) -> str: + """Return Viser web UI URL for display to users.""" + return f"http://{display_address}:{int(port)}" class NewtonViewerViser(ViewerViser): @@ -65,6 +65,7 @@ class NewtonViewerViser(ViewerViser): def __init__( self, port: int = 8080, + bind_address: str = "0.0.0.0", label: str | None = None, verbose: bool = True, share: bool = False, @@ -75,6 +76,7 @@ def __init__( Args: port: HTTP port for viser server. + bind_address: Host/interface for the Viser server to bind. label: Optional viewer label. verbose: Whether to keep verbose startup output enabled. share: Whether to enable sharing/tunneling. @@ -82,13 +84,24 @@ def __init__( metadata: Optional metadata attached to the viewer. """ _disable_viser_runtime_client_rebuild_if_bundled() - super().__init__( - port=port, - label=label, - verbose=verbose, - share=share, - record_to_viser=record_to_viser, - ) + viser = self._get_viser() + original_viser_server = viser.ViserServer + + def _viser_server_with_bind_address(*args, **kwargs): + kwargs["host"] = bind_address + kwargs.setdefault("verbose", verbose) + return original_viser_server(*args, **kwargs) + + with contextlib.ExitStack() as stack: + viser.ViserServer = _viser_server_with_bind_address + stack.callback(setattr, viser, "ViserServer", original_viser_server) + super().__init__( + port=port, + label=label, + verbose=verbose, + share=share, + record_to_viser=record_to_viser, + ) self._metadata = metadata or {} @@ -149,6 +162,8 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: ("lookat", self.cfg.lookat), ("cam_source", self.cfg.cam_source), ("num_visualized_envs", num_visualized_envs), + ("bind_address", self.cfg.bind_address), + ("display_address", self.cfg.display_address), ("port", self.cfg.port), ("record_to_viser", self.cfg.record_to_viser or ""), ], @@ -245,15 +260,25 @@ def _create_viewer(self, record_to_viser: str | None, metadata: dict | None = No if self._model is None: raise RuntimeError("Viser visualizer requires a Newton model.") - print() self._viewer = NewtonViewerViser( port=self.cfg.port, + bind_address=self.cfg.bind_address, label=self.cfg.label, - verbose=self.cfg.verbose, + verbose=False, share=self.cfg.share, record_to_viser=record_to_viser, metadata=metadata or {}, ) + viewer_url = getattr(self._viewer, "_share_url", None) or _viser_web_viewer_url( + self.cfg.port, self.cfg.display_address + ) + if self.cfg.verbose: + print() + self._log_viewer_url( + logger, + "ViserVisualizer", + viewer_url, + ) num_envs = int((metadata or {}).get("num_envs", 0)) self._viewer.set_model(self._model) apply_viewer_visible_worlds( @@ -265,7 +290,7 @@ def _create_viewer(self, record_to_viser: str | None, metadata: dict | None = No # Preserve simulation world positions (env_spacing) rather than adding viewer-side offsets. self._viewer.set_world_offsets((0.0, 0.0, 0.0)) if self.cfg.open_browser: - _open_viser_web_viewer(self.cfg.port) + _open_viser_web_viewer(viewer_url) initial_pose = self._resolve_initial_camera_pose() self._set_viser_camera_view(initial_pose) self._sim_time = 0.0 diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer_cfg.py b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer_cfg.py index c8b3034b9664..73282175589d 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer_cfg.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer_cfg.py @@ -21,6 +21,18 @@ class ViserVisualizerCfg(VisualizerCfg): port: int = 8080 """Port of the local viser web server.""" + bind_address: str = "0.0.0.0" + """Host/interface for the Viser server to bind. + + Use ``"0.0.0.0"`` to listen on all interfaces for remote access. + """ + + display_address: str = "localhost" + """Host name or IP address shown in the printed browser URL. + + For remote access, set this to the hostname/IP reachable from your browser. + """ + open_browser: bool = False """Whether to attempt opening the viser web viewer URL in a browser. From 2d9dfb53c3e7f6b93733f42e946ffde48831edf7 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Fri, 15 May 2026 02:18:48 +0000 Subject: [PATCH 13/31] docs, rm test snippet --- docs/source/features/visualization.rst | 18 ++++++++++++++---- .../overview/core-concepts/renderers.rst | 6 ++++++ .../manipulation/dexsuite/mdp/observations.py | 5 ----- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/docs/source/features/visualization.rst b/docs/source/features/visualization.rst index 280630c6ded4..9c111ac6d3ce 100644 --- a/docs/source/features/visualization.rst +++ b/docs/source/features/visualization.rst @@ -121,6 +121,8 @@ You can also configure custom visualizers in the code by defining ``VisualizerCf ), ViserVisualizerCfg( port=8080, + bind_address="0.0.0.0", + display_address="localhost", share=False, ), ] @@ -402,7 +404,7 @@ Rerun Visualizer ╭─────────────────────────── rerun (listening *:9090) ───────────────────────────╮ │ ╷ │ - │ HTTP │ http://127.0.0.1:9090/?url=rerun%2Bhttp://127.0.0.1:9876/proxy │ + │ URL │ http://127.0.0.1:9090/?url=rerun%2Bhttp://127.0.0.1:9876/proxy │ │ ╵ │ ╰────────────────────────────────────────────────────────────────────────────────╯ @@ -456,7 +458,8 @@ server, allowing you to view and interact with the scene from any browser. A highlighted Viser browser URL is printed in the logs before the main simulation or training loop begins. Ctrl-click the printed URL in supported terminals/IDEs to open it. Set ``open_browser=True`` to automatically - open the browser tab instead. + open the browser tab instead. For remote access, keep ``bind_address="0.0.0.0"`` and set + ``display_address`` to the hostname or IP address reachable from your browser. Example: @@ -464,8 +467,7 @@ server, allowing you to view and interact with the scene from any browser. ╭────── viser (listening *:8080) ───────╮ │ ╷ │ - │ HTTP │ http://localhost:8080 │ - │ Websocket │ ws://localhost:8080 │ + │ URL │ http://localhost:8080 │ │ ╵ │ ╰───────────────────────────────────────╯ @@ -478,6 +480,8 @@ server, allowing you to view and interact with the scene from any browser. visualizer_cfg = ViserVisualizerCfg( # Server settings port=8080, # Port for local Viser web server + bind_address="0.0.0.0", # Interface to listen on; use 0.0.0.0 for remote access + display_address="localhost", # Host/IP shown in the printed browser URL open_browser=False, # Set True to auto-launch the browser label="Isaac Lab Simulation", # Page title shown in the viewer share=False, # Request a public share URL for remote viewing @@ -494,6 +498,12 @@ server, allowing you to view and interact with the scene from any browser. record_to_viser="recording.viser", # Path to save .viser file (None = no recording) ) +Viser uses an in-process ``viser.ViserServer`` through ``newton.viewer.ViewerViser``. ``bind_address`` +controls the network interface that the server listens on, while ``display_address`` controls only the +URL printed by Isaac Lab. On a remote machine, set ``display_address`` to the machine hostname/IP and +ensure the configured ``port`` is reachable from your browser. Set ``share=True`` to request Viser's +public share/tunnel URL when that service is available. + .. note:: The Viser visualizer does not currently support live plots. diff --git a/docs/source/overview/core-concepts/renderers.rst b/docs/source/overview/core-concepts/renderers.rst index 2ae8bae28af3..a95267fcf9e7 100644 --- a/docs/source/overview/core-concepts/renderers.rst +++ b/docs/source/overview/core-concepts/renderers.rst @@ -30,6 +30,12 @@ Choosing a renderer backend | Newton Warp | No (kit-less) | Newton backend, fast training | +---------------------+-------------------------------+---------------------------------+ +.. note:: + + Visualization markers are not yet supported by Newton-based renderer backends, + including the Newton Warp renderer. Use an RTX-based renderer, such as the + Isaac RTX renderer or OVRTX renderer, when marker visualization is needed. + Architecture Overview --------------------- diff --git a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/mdp/observations.py b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/mdp/observations.py index 1a8a87c0835f..fe666a7ecffe 100644 --- a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/mdp/observations.py +++ b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/mdp/observations.py @@ -217,11 +217,6 @@ def __call__( ) -> torch.Tensor: # obtain the input image images = self.sensor.data.output[self.sensor_type] torch.nan_to_num_(images, nan=1e6) - # TEMP DEBUG: remove after verifying the Dexsuite table marker and robot motion in raw RTX observations. - debug_count = getattr(self, "_debug_collage_count", 0) - if debug_count < 8: - self.show_collage(images, f"observation_{debug_count:03d}.png") - self._debug_collage_count = debug_count + 1 if normalize: images = self.norm_fn(images) images = images.permute(0, 3, 1, 2).contiguous() From fbd9a5fd2289252b19c863906fea7f8241845f1c Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Fri, 15 May 2026 02:31:51 +0000 Subject: [PATCH 14/31] newlines --- source/isaaclab/isaaclab/visualizers/base_visualizer.py | 4 ++-- .../isaaclab_visualizers/viser/viser_visualizer.py | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/source/isaaclab/isaaclab/visualizers/base_visualizer.py b/source/isaaclab/isaaclab/visualizers/base_visualizer.py index 10283c3e84f3..e9e55f4f2fae 100644 --- a/source/isaaclab/isaaclab/visualizers/base_visualizer.py +++ b/source/isaaclab/isaaclab/visualizers/base_visualizer.py @@ -330,7 +330,7 @@ def _log_viewer_url( visualizer_label = visualizer_name.removesuffix("Visualizer").lower() title = f" {visualizer_label} (listening *:{parsed_url.port}) " if parsed_url.port else f" {visualizer_label} " label = "URL" - label_width = max(9, len(label)) + label_width = len(label) value_width = max(len(viewer_url), len(title) + 2, 21) inner_width = label_width + value_width + 9 left_rule_width = max((inner_width - len(title)) // 2, 1) @@ -343,7 +343,7 @@ def _log_viewer_url( f"│{' ' * (label_width + 4)}╵{' ' * (value_width + 4)}│", f"╰{'─' * inner_width}╯", ] - self._deferred_startup_messages.append("\n".join(lines)) + self._deferred_startup_messages.append("\n" + "\n".join(lines) + "\n") def flush_startup_messages(self) -> None: """Print deferred startup messages immediately before the workflow update loop starts.""" diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py index cc420e245b73..e70f8ec99ab5 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py @@ -8,6 +8,7 @@ from __future__ import annotations import contextlib +import io import logging import os import webbrowser @@ -89,12 +90,15 @@ def __init__( def _viser_server_with_bind_address(*args, **kwargs): kwargs["host"] = bind_address - kwargs.setdefault("verbose", verbose) + kwargs["verbose"] = verbose return original_viser_server(*args, **kwargs) with contextlib.ExitStack() as stack: viser.ViserServer = _viser_server_with_bind_address stack.callback(setattr, viser, "ViserServer", original_viser_server) + if not verbose: + stack.enter_context(contextlib.redirect_stdout(io.StringIO())) + stack.enter_context(contextlib.redirect_stderr(io.StringIO())) super().__init__( port=port, label=label, From f5c987ac33084deec54785e42121c0cac45abac1 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Fri, 15 May 2026 02:55:20 +0000 Subject: [PATCH 15/31] remove unneeded changes --- .../isaaclab/markers/visualization_markers.py | 2 +- .../isaaclab/sim/simulation_context.py | 4 +- .../isaaclab/visualizers/base_visualizer.py | 3 - .../test_simulation_context_visualizers.py | 11 ++++ .../isaaclab_newton/physics/newton_manager.py | 56 ------------------- .../isaaclab_rl/rsl_rl/vecenv_wrapper.py | 6 +- .../kit/kit_visualizer.py | 4 +- .../rerun/rerun_visualizer.py | 2 +- .../viser/viser_visualizer.py | 10 ++-- .../test_visualizer_cartpole_integration.py | 11 +--- 10 files changed, 25 insertions(+), 84 deletions(-) diff --git a/source/isaaclab/isaaclab/markers/visualization_markers.py b/source/isaaclab/isaaclab/markers/visualization_markers.py index b8ddf891010f..667f9b4ddf7d 100644 --- a/source/isaaclab/isaaclab/markers/visualization_markers.py +++ b/source/isaaclab/isaaclab/markers/visualization_markers.py @@ -250,7 +250,7 @@ def _ensure_backends_initialized(self) -> None: self._ensure_kit_backend() return - needs_kit_backend = bool(getattr(sim, "is_rendering", False)) or any( + needs_kit_backend = sim.is_rendering or any( viz.supports_markers() and viz.pumps_app_update() and viz.cfg.enable_markers for viz in sim.visualizers ) if needs_kit_backend: diff --git a/source/isaaclab/isaaclab/sim/simulation_context.py b/source/isaaclab/isaaclab/sim/simulation_context.py index 9cb57a6d9ebc..9975cf13100d 100644 --- a/source/isaaclab/isaaclab/sim/simulation_context.py +++ b/source/isaaclab/isaaclab/sim/simulation_context.py @@ -762,9 +762,7 @@ def update_visualizers(self, dt: float, skip_app_pumping: bool = False) -> None: return for viz in self._visualizers: - flush_startup_messages = getattr(viz, "flush_startup_messages", None) - if callable(flush_startup_messages): - flush_startup_messages() + viz.flush_startup_messages() if self._should_forward_before_visualizer_update(): self.physics_manager.forward() diff --git a/source/isaaclab/isaaclab/visualizers/base_visualizer.py b/source/isaaclab/isaaclab/visualizers/base_visualizer.py index e9e55f4f2fae..08dbd968c020 100644 --- a/source/isaaclab/isaaclab/visualizers/base_visualizer.py +++ b/source/isaaclab/isaaclab/visualizers/base_visualizer.py @@ -314,18 +314,15 @@ def _log_initialization_table(self, logger: logging.Logger, title: str, rows: li def _log_viewer_url( self, - logger: logging.Logger, visualizer_name: str, viewer_url: str, ) -> None: """Queue a visible browser URL block for web-based visualizers. Args: - logger: Unused; kept for call-site compatibility. visualizer_name: Name of the visualizer exposing the URL. viewer_url: Browser URL for the visualizer. """ - del logger parsed_url = urlparse(viewer_url) visualizer_label = visualizer_name.removesuffix("Visualizer").lower() title = f" {visualizer_label} (listening *:{parsed_url.port}) " if parsed_url.port else f" {visualizer_label} " diff --git a/source/isaaclab/test/sim/test_simulation_context_visualizers.py b/source/isaaclab/test/sim/test_simulation_context_visualizers.py index ae0b865e9062..24b22d5e18b4 100644 --- a/source/isaaclab/test/sim/test_simulation_context_visualizers.py +++ b/source/isaaclab/test/sim/test_simulation_context_visualizers.py @@ -119,6 +119,9 @@ def pumps_app_update(self): def supports_markers(self): return False + def flush_startup_messages(self): + pass + def _make_context(visualizers, provider=None): ctx = object.__new__(SimulationContext) @@ -562,6 +565,10 @@ def set_visible_worlds(self, worlds) -> None: def set_world_offsets(self, spacing) -> None: captured["set_world_offsets"] = tuple(spacing) + @property + def share_url(self) -> str | None: + return None + monkeypatch.setattr(viser_visualizer, "NewtonViewerViser", _FakeNewtonViewerViser) monkeypatch.setattr( viser_visualizer.ViserVisualizer, @@ -612,6 +619,10 @@ def set_visible_worlds(self, worlds) -> None: def set_world_offsets(self, spacing) -> None: pass + @property + def share_url(self) -> str | None: + return None + monkeypatch.setattr(viser_visualizer, "NewtonViewerViser", _FakeNewtonViewerViser) monkeypatch.setattr(viser_visualizer, "_open_viser_web_viewer", lambda url: opened_urls.append(url)) monkeypatch.setattr( diff --git a/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py b/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py index abc7ac96c8c8..7f112af4bacf 100644 --- a/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py +++ b/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py @@ -281,49 +281,6 @@ def reset(cls, soft: bool = False) -> None: cls.start_simulation() cls.initialize_solver() - @classmethod - def play(cls) -> None: - """Start or resume the Kit timeline when Kit is driving visualization.""" - timeline, app = cls._get_kit_timeline_and_app() - if timeline is None: - return - timeline.play() - if app is not None: - app.update() - - @classmethod - def pause(cls) -> None: - """Pause the Kit timeline when Kit is driving visualization.""" - timeline, app = cls._get_kit_timeline_and_app() - if timeline is None: - return - timeline.pause() - if app is not None: - app.update() - - @classmethod - def stop(cls) -> None: - """Stop the Kit timeline when Kit is driving visualization.""" - timeline, app = cls._get_kit_timeline_and_app() - if timeline is None: - return - timeline.stop() - if app is not None: - app.update() - - @classmethod - def wait_for_playing(cls) -> None: - """Block while the Kit timeline is paused, keeping the UI responsive.""" - timeline, app = cls._get_kit_timeline_and_app() - if timeline is None or app is None: - return - if timeline.is_playing(): - return - while not timeline.is_playing(): - app.update() - if timeline.is_stopped(): - break - @classmethod def forward(cls) -> None: """Update articulation kinematics without stepping physics. @@ -523,19 +480,6 @@ def register_callback( """Register a callback. Passes event to parent class.""" return PhysicsManager.register_callback(callback, event, order, name, wrap_weak_ref) - @classmethod - def _get_kit_timeline_and_app(cls): - """Return Kit timeline/app interfaces if Kit is available.""" - try: - import omni.kit.app - import omni.timeline - except (ImportError, AttributeError): - return None, None - app = omni.kit.app.get_app() - if app is None: - return None, None - return omni.timeline.get_timeline_interface(), app - @classmethod def get_physics_sim_view(cls) -> list: """Get the list of registered views. diff --git a/source/isaaclab_rl/isaaclab_rl/rsl_rl/vecenv_wrapper.py b/source/isaaclab_rl/isaaclab_rl/rsl_rl/vecenv_wrapper.py index 562fca1c24cc..5a3fdcd47164 100644 --- a/source/isaaclab_rl/isaaclab_rl/rsl_rl/vecenv_wrapper.py +++ b/source/isaaclab_rl/isaaclab_rl/rsl_rl/vecenv_wrapper.py @@ -167,8 +167,7 @@ def seed(self, seed: int = -1) -> int: # noqa: D102 def reset(self) -> tuple[TensorDict, dict]: # noqa: D102 # reset the environment obs_dict, extras = self.env.reset() - result = TensorDict(obs_dict, batch_size=[self.num_envs]), extras - return result + return TensorDict(obs_dict, batch_size=[self.num_envs]), extras def get_observations(self) -> TensorDict: """Returns the current observations of the environment.""" @@ -176,8 +175,7 @@ def get_observations(self) -> TensorDict: obs_dict = self.unwrapped.observation_manager.compute() else: obs_dict = self.unwrapped._get_observations() - result = TensorDict(obs_dict, batch_size=[self.num_envs]) - return result + return TensorDict(obs_dict, batch_size=[self.num_envs]) def step(self, actions: torch.Tensor) -> tuple[TensorDict, torch.Tensor, torch.Tensor, dict]: # clip actions diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py index b006ad16d534..4766fbe7f68f 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py @@ -11,7 +11,6 @@ import logging from typing import TYPE_CHECKING -import carb from pxr import Gf, Usd, UsdGeom, Vt from isaaclab.app.settings_manager import get_settings_manager @@ -53,7 +52,6 @@ def __init__(self, cfg: KitVisualizerCfg): self._runtime_headless = bool(cfg.headless) # USD path for the viewport's active camera, refreshed after setup (used by CI/tests). self._controlled_camera_path: str | None = None - self._physics_backend: str | None = None # ---- Lifecycle ------------------------------------------------------------------------ @@ -121,7 +119,7 @@ def step(self, dt: float) -> None: if app is not None and app.is_running(): # Keep app pumping for viewport/UI updates only; physics is owned by SimulationContext. # Disable playSimulations around app.update() so Kit does not advance its own physics here. - settings = carb.settings.get_settings() + settings = get_settings_manager() settings.set_bool("/app/player/playSimulations", False) app.update() settings.set_bool("/app/player/playSimulations", True) diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py index 5508462e784f..55fac9e06615 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py @@ -202,7 +202,7 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: viewer_host = _normalize_host(bind_address) viewer_url = _rerun_web_viewer_url(viewer_host, web_port, rerun_address) print() - self._log_viewer_url(logger, "RerunVisualizer", viewer_url) + self._log_viewer_url("RerunVisualizer", viewer_url) if self.cfg.open_browser and not start_server_in_viewer: _open_rerun_web_viewer(viewer_host, web_port, rerun_address) self._viewer.set_model(self._model) diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py index e70f8ec99ab5..c5d4162655f8 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py @@ -108,6 +108,11 @@ def _viser_server_with_bind_address(*args, **kwargs): ) self._metadata = metadata or {} + @property + def share_url(self) -> str | None: + """Return the public share URL created by Viser, if any.""" + return self._share_url + class ViserVisualizer(BaseVisualizer): """Viser web-based visualizer backed by Newton's ViewerViser.""" @@ -273,13 +278,10 @@ def _create_viewer(self, record_to_viser: str | None, metadata: dict | None = No record_to_viser=record_to_viser, metadata=metadata or {}, ) - viewer_url = getattr(self._viewer, "_share_url", None) or _viser_web_viewer_url( - self.cfg.port, self.cfg.display_address - ) + viewer_url = self._viewer.share_url or _viser_web_viewer_url(self.cfg.port, self.cfg.display_address) if self.cfg.verbose: print() self._log_viewer_url( - logger, "ViserVisualizer", viewer_url, ) diff --git a/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py b/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py index fb79138f1014..9b08a75ae7a5 100644 --- a/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py +++ b/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py @@ -19,20 +19,13 @@ from __future__ import annotations -# Pyglet must use HeadlessWindow (EGL) before ``pyglet.window`` is imported so Newton -# ViewerGL can construct without an X11 display (matches ``headless=True`` on NewtonVisualizerCfg). -import types - import pyglet import warp as wp +# Pyglet must use HeadlessWindow (EGL) before ``pyglet.window`` is imported so Newton +# ViewerGL can construct without an X11 display (matches ``headless=True`` on NewtonVisualizerCfg). pyglet.options["headless"] = True -# TODO(mtrepte): check if this is still needed on TOT Lab/Sim before merge -# Replicator 1.13 expects older Warp API aliases during Kit startup. -wp.context = types.SimpleNamespace(Kernel=wp.Kernel) -wp.types.array = wp.array - from isaaclab.app import AppLauncher # launch Kit app From 80abc927ecacf2b21465b71fe6ef5969f7853d67 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Fri, 15 May 2026 02:59:43 +0000 Subject: [PATCH 16/31] drop item --- .../isaaclab_newton/changelog.d/mtrepte-update-debug-viz.rst | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 source/isaaclab_newton/changelog.d/mtrepte-update-debug-viz.rst diff --git a/source/isaaclab_newton/changelog.d/mtrepte-update-debug-viz.rst b/source/isaaclab_newton/changelog.d/mtrepte-update-debug-viz.rst deleted file mode 100644 index 3295f14e5270..000000000000 --- a/source/isaaclab_newton/changelog.d/mtrepte-update-debug-viz.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fixed -^^^^^ - -* Fixed Newton physics timeline controls so Kit-driven visualizer pause and resume controls keep the UI responsive. From c06acb281c7520bbd2bbe194431e8e9f615fe3f7 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Fri, 15 May 2026 04:44:11 +0000 Subject: [PATCH 17/31] add capture mode --- .../test_visualizer_cartpole_integration.py | 396 +++++++++++++----- 1 file changed, 300 insertions(+), 96 deletions(-) diff --git a/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py b/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py index 9b08a75ae7a5..df18cab767d5 100644 --- a/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py +++ b/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py @@ -35,6 +35,7 @@ import copy import logging import socket +from pathlib import Path import numpy as np import pytest @@ -54,12 +55,15 @@ # When True, tests also fail on WARNING-level records from visualizer-related loggers. ASSERT_VISUALIZER_WARNINGS = False -_MAX_NON_BLACK_STEPS = 8 -"""Steps for tiled camera / Rerun / Viser smoke tests (early exit ok when non-black).""" +_MAX_FRAME_CHECK_STEPS = 8 +"""Steps for tiled camera / Rerun / Viser smoke tests (early exit ok when frame is non-flat).""" _CARTPOLE_INTEGRATION_NUM_ENVS = 1 """Vectorized env count for cartpole + visualizer integration tests.""" +# TODO: Compare these direct-env visualizer checks with RSL-RL train runs; +# manager-based training can show different frozen-visualizer behavior. + _CARTPOLE_INTEGRATION_VISUALIZER_EYE: tuple[float, float, float] = (3.0, 3.0, 3.0) """Passed to :class:`~isaaclab.visualizers.visualizer_cfg.VisualizerCfg` subclasses (``eye``).""" @@ -67,27 +71,20 @@ """Passed to visualizer cfgs (``lookat``); also applied to :class:`~isaaclab.envs.common.ViewerCfg` for the env.""" # Resolution overrides for this test module (cartpole preset defaults: tiled camera 100×100; Kit helper was 320×240). -_CARTPOLE_KIT_INTEGRATION_RENDER_RESOLUTION: tuple[int, int] = (600, 600) +_CARTPOLE_KIT_INTEGRATION_RENDER_RESOLUTION: tuple[int, int] = (500, 500) """Kit: Replicator ``render_product`` (width, height) for viewport RGB in the motion check.""" -_CARTPOLE_NEWTON_INTEGRATION_WINDOW_SIZE: tuple[int, int] = (600, 600) +_CARTPOLE_NEWTON_INTEGRATION_WINDOW_SIZE: tuple[int, int] = (500, 500) """Newton: ``NewtonVisualizerCfg`` framebuffer (window_width × window_height) for ``get_frame()``.""" -_CARTPOLE_TILED_CAMERA_INTEGRATION_WH: tuple[int, int] = (600, 600) +_CARTPOLE_TILED_CAMERA_INTEGRATION_WH: tuple[int, int] = (500, 500) """Tiled camera per-env tile width/height (preset default is 100×100); keeps ``observation_space`` consistent.""" -_VIS_FRAME_TEST_STEPS = 10 -"""Steps for Kit / Newton frame capture: no early exit.""" - -_PAUSED_FRAME_TEST_STEPS = 5 -"""Visualizer frames to pump while simulation is paused.""" +_START_BUFFER_STEPS = 5 +"""Warmup steps before capturing the first motion-test frame.""" -# Motion check compares the 2nd vs last captured frame (e.g. 2nd vs 60th when *_STEPS* is 60). -_MOTION_FRAME_EARLY_IDX = 1 -"""0-based index of the *early* frame (2nd capture).""" - -_MOTION_FRAME_LATE_IDX = _VIS_FRAME_TEST_STEPS - 1 -"""0-based index of the *late* frame (e.g. 60th capture when :data:`_VIS_FRAME_TEST_STEPS` is 60).""" +_CYCLE_FRAME_STEPS = 10 +"""Steps to run for each motion, pause, and play segment.""" # Early vs late frame motion: void background stays similar; only count *strongly* differing pixels. _FRAME_MOTION_CHANNEL_DIFF_THRESHOLD = 50 @@ -96,12 +93,21 @@ _FRAME_MOTION_MIN_DIFFERING_PIXELS = 100 """Minimum number of such pixels between early and late frames (stale/frozen viz should be near zero).""" +_FRAME_MIN_CHANNEL_RANGE = 10 +"""Minimum per-frame channel range to reject all-one-color images.""" + _VIS_LOGGER_PREFIXES = ( "isaaclab.visualizers", "isaaclab_visualizers", "isaaclab.sim.simulation_context", ) +_WRITE_VIS_DEBUG_FRAMES = False +"""Whether to emit visualizer debug PNGs during integration tests.""" + +_VIS_DEBUG_IMAGE_DIR = Path("logs/visualizer_debug") +"""Directory for opt-in visualizer debug images emitted by integration tests.""" + def _logger_name_matches_visualizer_scope(logger_name: str) -> bool: """Return True if *logger_name* is a visualizer / SimulationContext visualizer path.""" @@ -243,16 +249,20 @@ def _get_physics_cfg(backend_kind: str): raise ValueError(f"Unknown backend: {backend_kind!r}") -def _assert_non_black_tensor(image_tensor: torch.Tensor, *, min_nonzero_pixels: int = 1) -> None: - """Assert camera-like tensor contains non-black pixels.""" +def _assert_non_flat_tensor(image_tensor: torch.Tensor) -> None: + """Assert every camera-like tensor image has non-flat content.""" assert isinstance(image_tensor, torch.Tensor), f"Expected torch.Tensor, got {type(image_tensor)!r}" assert image_tensor.numel() > 0, "Image tensor is empty." finite_tensor = torch.where(torch.isfinite(image_tensor), image_tensor, torch.zeros_like(image_tensor)) - if finite_tensor.dtype.is_floating_point: - nonzero = torch.count_nonzero(torch.abs(finite_tensor) > 1e-6).item() - else: - nonzero = torch.count_nonzero(finite_tensor > 0).item() - assert nonzero >= min_nonzero_pixels, "Rendered frame appears black (no non-zero pixels)." + finite_tensor = finite_tensor.float() + if finite_tensor.numel() > 0 and float(finite_tensor.max().item()) <= 1.0 + 1e-6: + finite_tensor = finite_tensor * 255.0 + flat = finite_tensor.reshape(finite_tensor.shape[0], -1) + channel_ranges = flat.max(dim=1).values - flat.min(dim=1).values + min_range = float(channel_ranges.min().item()) + assert min_range >= _FRAME_MIN_CHANNEL_RANGE, ( + f"Rendered frame appears flat / single-color (min channel range {min_range:.3f} < {_FRAME_MIN_CHANNEL_RANGE})." + ) def _frame_to_numpy(frame) -> np.ndarray: @@ -269,17 +279,17 @@ def _frame_to_numpy(frame) -> np.ndarray: return np.asarray(frame) -def _assert_non_black_frame_array(frame) -> None: - """Assert viewer-captured frame has visible, non-black content.""" +def _assert_non_flat_frame_array(frame) -> None: + """Assert viewer-captured frame has non-flat content.""" frame_arr = _frame_to_numpy(frame) assert frame_arr.size > 0, "Viewer returned an empty frame." - if frame_arr.ndim == 2: - color = frame_arr - else: + if frame_arr.ndim != 2: assert frame_arr.shape[-1] >= 3, f"Expected at least 3 channels, got shape {frame_arr.shape}." - color = frame_arr[..., :3] - finite = np.where(np.isfinite(color), color, 0) - assert np.count_nonzero(finite) > 0, "Viewer frame appears fully black." + rgb = _frame_rgb_255_space(frame) + channel_range = float(np.max(rgb) - np.min(rgb)) + assert channel_range >= _FRAME_MIN_CHANNEL_RANGE, ( + f"Viewer frame appears flat / single-color (channel range {channel_range:.3f} < {_FRAME_MIN_CHANNEL_RANGE})." + ) def _frame_rgb_255_space(frame) -> np.ndarray: @@ -296,6 +306,43 @@ def _frame_rgb_255_space(frame) -> np.ndarray: return rgb +def _save_visualizer_debug_frame(frame, *, viz_kind: str, physics_kind: str, phase: str, frame_idx: int) -> None: + """Save a visualizer frame to a clearly named PNG for pause/motion debugging.""" + if not _WRITE_VIS_DEBUG_FRAMES: + return + from PIL import Image + + rgb = np.clip(_frame_rgb_255_space(frame), 0, 255).astype(np.uint8) + _VIS_DEBUG_IMAGE_DIR.mkdir(parents=True, exist_ok=True) + file_name = f"{viz_kind}_viz_{physics_kind}_physics_{phase}_frame_{frame_idx:03d}.png" + Image.fromarray(rgb).save(_VIS_DEBUG_IMAGE_DIR / file_name) + + +def _save_visualizer_debug_delta( + frame_a, frame_b, *, viz_kind: str, physics_kind: str, phase: str, frame_idx: int +) -> None: + """Save an amplified absolute-difference image for a start/end frame pair.""" + if not _WRITE_VIS_DEBUG_FRAMES: + return + from PIL import Image + + a = _frame_rgb_255_space(frame_a) + b = _frame_rgb_255_space(frame_b) + assert a.shape == b.shape, f"Frame shape mismatch for delta image: {a.shape} vs {b.shape}." + delta = np.clip(np.abs(a - b) * 4.0, 0, 255).astype(np.uint8) + _VIS_DEBUG_IMAGE_DIR.mkdir(parents=True, exist_ok=True) + file_name = f"{viz_kind}_viz_{physics_kind}_physics_{phase}_delta_frame_{frame_idx:03d}.png" + Image.fromarray(delta).save(_VIS_DEBUG_IMAGE_DIR / file_name) + + +def _clear_visualizer_debug_frames(viz_kind: str, physics_kind: str) -> None: + if not _WRITE_VIS_DEBUG_FRAMES: + return + _VIS_DEBUG_IMAGE_DIR.mkdir(parents=True, exist_ok=True) + for path in _VIS_DEBUG_IMAGE_DIR.glob(f"{viz_kind}_viz_{physics_kind}_physics_*.png"): + path.unlink() + + def _count_significantly_differing_pixels( frame_a, frame_b, @@ -318,31 +365,17 @@ def _assert_frames_remain_stable(frame_a, frame_b, *, max_differing_pixels: int ) -def _assert_early_and_late_motion_frames_differ( - frames: list, +def _assert_frames_differ( + frame_a, + frame_b, *, channel_diff_threshold: float = _FRAME_MOTION_CHANNEL_DIFF_THRESHOLD, min_differing_pixels: int = _FRAME_MOTION_MIN_DIFFERING_PIXELS, ) -> None: - """Fail if early vs late frames lack enough strongly differing pixels (stale/frozen bodies). - - Compares :data:`_MOTION_FRAME_EARLY_IDX` vs :data:`_MOTION_FRAME_LATE_IDX` (e.g. 2nd vs 60th capture). - - Voids/background stay near-identical; we only count pixels that change by at least - *channel_diff_threshold* on some channel (0–255). - """ - assert len(frames) >= _VIS_FRAME_TEST_STEPS, ( - f"Need at least {_VIS_FRAME_TEST_STEPS} frames for motion check, got {len(frames)}." - ) - i_early = _MOTION_FRAME_EARLY_IDX - i_late = _MOTION_FRAME_LATE_IDX - early_1 = i_early + 1 - late_1 = i_late + 1 - n_diff = _count_significantly_differing_pixels( - frames[i_early], frames[i_late], channel_diff_threshold=channel_diff_threshold - ) + """Fail if two frames lack enough strongly differing pixels (stale/frozen bodies).""" + n_diff = _count_significantly_differing_pixels(frame_a, frame_b, channel_diff_threshold=channel_diff_threshold) assert n_diff >= min_differing_pixels, ( - f"Viewport captures #{early_1} and #{late_1} have too few strongly differing pixels " + "Viewport frame pair has too few strongly differing pixels " f"({n_diff} < {min_differing_pixels}; threshold per channel={channel_diff_threshold} in 0–255 space). " "Possible frozen or stale robot visualization." ) @@ -350,12 +383,12 @@ def _assert_early_and_late_motion_frames_differ( def _assert_frame_sequence_has_motion(frames: list) -> None: """Assert a captured frame sequence is visible and changes over time.""" - _assert_non_black_frame_array(frames[-1]) - _assert_early_and_late_motion_frames_differ(frames) + _assert_non_flat_frame_array(frames[-1]) + _assert_frames_differ(frames[0], frames[-1]) -def _step_until_non_black_camera(env, actions: torch.Tensor, *, max_steps: int = _MAX_NON_BLACK_STEPS) -> None: - """Step env until the env's tiled camera RGB tensor is non-black, bounded by *max_steps*.""" +def _step_until_non_flat_camera(env, actions: torch.Tensor, *, max_steps: int = _MAX_FRAME_CHECK_STEPS) -> None: + """Step env until the env's tiled camera RGB tensor is non-flat, bounded by *max_steps*.""" last_rgb = None for _ in range(max_steps): env.step(action=actions) @@ -364,11 +397,11 @@ def _step_until_non_black_camera(env, actions: torch.Tensor, *, max_steps: int = rgb = env._tiled_camera.data.output[env.cfg.tiled_camera.data_types[0]] last_rgb = rgb try: - _assert_non_black_tensor(rgb) + _assert_non_flat_tensor(rgb) return except AssertionError: continue - _assert_non_black_tensor(last_rgb) + _assert_non_flat_tensor(last_rgb) def _select_newton_training_control_button(viewer, target_label: str) -> None: @@ -418,44 +451,150 @@ def _run_newton_viewer_frame_motion_test( viz_kind: str = "newton", ) -> None: """Check Newton viewer simulation pause, rendering pause, then resumed motion.""" - frames: list = [] - for _ in range(_VIS_FRAME_TEST_STEPS): + _clear_visualizer_debug_frames(viz_kind, physics_kind) + for _ in range(_START_BUFFER_STEPS): step_hook() - frames.append(viewer.get_frame()) - _assert_frame_sequence_has_motion(frames) + motion_start_frame = viewer.get_frame() + _save_visualizer_debug_frame( + motion_start_frame, viz_kind=viz_kind, physics_kind=physics_kind, phase="motion_start", frame_idx=0 + ) + for _ in range(_CYCLE_FRAME_STEPS): + step_hook() + motion_end_frame = viewer.get_frame() + _save_visualizer_debug_frame( + motion_end_frame, viz_kind=viz_kind, physics_kind=physics_kind, phase="motion_end", frame_idx=_CYCLE_FRAME_STEPS + ) + _save_visualizer_debug_delta( + motion_start_frame, + motion_end_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="motion", + frame_idx=_CYCLE_FRAME_STEPS, + ) + _assert_non_flat_frame_array(motion_end_frame) + _assert_frames_differ(motion_start_frame, motion_end_frame) _select_newton_pause_simulation_button(viewer) - paused_frame = viewer.get_frame() - for _ in range(_PAUSED_FRAME_TEST_STEPS): + paused_start_frame = viewer.get_frame() + _save_visualizer_debug_frame( + paused_start_frame, viz_kind=viz_kind, physics_kind=physics_kind, phase="simulation_pause_start", frame_idx=0 + ) + for _ in range(_CYCLE_FRAME_STEPS): visualizer.step(0.0) - _assert_frames_remain_stable(paused_frame, viewer.get_frame()) + paused_end_frame = viewer.get_frame() + _save_visualizer_debug_frame( + paused_end_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="simulation_pause_end", + frame_idx=_CYCLE_FRAME_STEPS, + ) + _save_visualizer_debug_delta( + paused_start_frame, + paused_end_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="simulation_pause", + frame_idx=_CYCLE_FRAME_STEPS, + ) + _assert_frames_remain_stable(paused_start_frame, paused_end_frame) _select_newton_pause_simulation_button(viewer) - resumed_frames: list = [] - for _ in range(_VIS_FRAME_TEST_STEPS): + simulation_play_start_frame = viewer.get_frame() + _save_visualizer_debug_frame( + simulation_play_start_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="simulation_play_start", + frame_idx=0, + ) + for _ in range(_CYCLE_FRAME_STEPS): step_hook() - resumed_frames.append(viewer.get_frame()) - _assert_frame_sequence_has_motion(resumed_frames) + simulation_play_end_frame = viewer.get_frame() + _save_visualizer_debug_frame( + simulation_play_end_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="simulation_play_end", + frame_idx=_CYCLE_FRAME_STEPS, + ) + _save_visualizer_debug_delta( + simulation_play_start_frame, + simulation_play_end_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="simulation_play", + frame_idx=_CYCLE_FRAME_STEPS, + ) + _assert_non_flat_frame_array(simulation_play_end_frame) + _assert_frames_differ(simulation_play_start_frame, simulation_play_end_frame) _select_newton_pause_rendering_button(viewer) - rendering_paused_frame = viewer.get_frame() + rendering_paused_start_frame = viewer.get_frame() + _save_visualizer_debug_frame( + rendering_paused_start_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="rendering_pause_start", + frame_idx=0, + ) physics_step_before_render_pause = get_physics_step_count() - for _ in range(_PAUSED_FRAME_TEST_STEPS): + for _ in range(_CYCLE_FRAME_STEPS): step_hook() - _assert_frames_remain_stable(rendering_paused_frame, viewer.get_frame()) + rendering_paused_end_frame = viewer.get_frame() + _save_visualizer_debug_frame( + rendering_paused_end_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="rendering_pause_end", + frame_idx=_CYCLE_FRAME_STEPS, + ) + _save_visualizer_debug_delta( + rendering_paused_start_frame, + rendering_paused_end_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="rendering_pause", + frame_idx=_CYCLE_FRAME_STEPS, + ) + _assert_frames_remain_stable(rendering_paused_start_frame, rendering_paused_end_frame) assert get_physics_step_count() > physics_step_before_render_pause, ( "Physics did not advance while Newton visualizer rendering was paused." ) _select_newton_pause_rendering_button(viewer) - render_resumed_frames: list = [] - for _ in range(_VIS_FRAME_TEST_STEPS): + rendering_play_start_frame = viewer.get_frame() + _save_visualizer_debug_frame( + rendering_play_start_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="rendering_play_start", + frame_idx=0, + ) + for _ in range(_CYCLE_FRAME_STEPS): step_hook() - render_resumed_frames.append(viewer.get_frame()) - _assert_frame_sequence_has_motion(render_resumed_frames) + rendering_play_end_frame = viewer.get_frame() + _save_visualizer_debug_frame( + rendering_play_end_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="rendering_play_end", + frame_idx=_CYCLE_FRAME_STEPS, + ) + _save_visualizer_debug_delta( + rendering_play_start_frame, + rendering_play_end_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="rendering_play", + frame_idx=_CYCLE_FRAME_STEPS, + ) + _assert_non_flat_frame_array(rendering_play_end_frame) + _assert_frames_differ(rendering_play_start_frame, rendering_play_end_frame) -def _step_env_without_frame_check(env, actions: torch.Tensor, *, max_steps: int = _MAX_NON_BLACK_STEPS) -> None: +def _step_env_without_frame_check(env, actions: torch.Tensor, *, max_steps: int = _MAX_FRAME_CHECK_STEPS) -> None: """Step the env to exercise visualizers that do not implement ``get_frame`` (e.g. Rerun, Viser).""" for _ in range(max_steps): env.step(action=actions) @@ -493,6 +632,7 @@ def _run_kit_viewport_frame_motion_test( viz_kind: str = "kit", ) -> None: """Check Kit viewport motion, timeline pause freeze, then resumed motion.""" + _clear_visualizer_debug_frames(viz_kind, physics_kind) camera_path = getattr(kit_visualizer, "_controlled_camera_path", None) assert camera_path, "Kit visualizer does not expose a controlled viewport camera path." @@ -501,30 +641,88 @@ def _run_kit_viewport_frame_motion_test( try: annotator, render_product = _build_rgb_annotator_for_camera(camera_path) actions = torch.zeros((env.num_envs, env.action_space.shape[-1]), device=env.device) - frames: list = [] - for _ in range(_VIS_FRAME_TEST_STEPS): + for _ in range(_START_BUFFER_STEPS): + env.step(action=actions) + motion_start_frame = _capture_kit_viewport_rgb(annotator) + _save_visualizer_debug_frame( + motion_start_frame, viz_kind=viz_kind, physics_kind=physics_kind, phase="motion_start", frame_idx=0 + ) + for _ in range(_CYCLE_FRAME_STEPS): env.step(action=actions) - rgb_data = annotator.get_data() - frames.append(_annotator_rgb_to_numpy(rgb_data)) - _assert_frame_sequence_has_motion(frames) + motion_end_frame = _capture_kit_viewport_rgb(annotator) + _save_visualizer_debug_frame( + motion_end_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="motion_end", + frame_idx=_CYCLE_FRAME_STEPS, + ) + _save_visualizer_debug_delta( + motion_start_frame, + motion_end_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="motion", + frame_idx=_CYCLE_FRAME_STEPS, + ) + _assert_non_flat_frame_array(motion_end_frame) + _assert_frames_differ(motion_start_frame, motion_end_frame) timeline = omni.timeline.get_timeline_interface() timeline.pause() simulation_app.update() - paused_frame = _capture_kit_viewport_rgb(annotator) + paused_start_frame = _capture_kit_viewport_rgb(annotator) + _save_visualizer_debug_frame( + paused_start_frame, viz_kind=viz_kind, physics_kind=physics_kind, phase="timeline_pause_start", frame_idx=0 + ) try: - for _ in range(_PAUSED_FRAME_TEST_STEPS): + for _ in range(_CYCLE_FRAME_STEPS): env.sim.render() - _assert_frames_remain_stable(paused_frame, _capture_kit_viewport_rgb(annotator)) + paused_end_frame = _capture_kit_viewport_rgb(annotator) + _save_visualizer_debug_frame( + paused_end_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="timeline_pause_end", + frame_idx=_CYCLE_FRAME_STEPS, + ) + _save_visualizer_debug_delta( + paused_start_frame, + paused_end_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="timeline_pause", + frame_idx=_CYCLE_FRAME_STEPS, + ) + _assert_frames_remain_stable(paused_start_frame, paused_end_frame) finally: timeline.play() simulation_app.update() - resumed_frames: list = [] - for _ in range(_VIS_FRAME_TEST_STEPS): + play_start_frame = _capture_kit_viewport_rgb(annotator) + _save_visualizer_debug_frame( + play_start_frame, viz_kind=viz_kind, physics_kind=physics_kind, phase="timeline_play_start", frame_idx=0 + ) + for _ in range(_CYCLE_FRAME_STEPS): env.step(action=actions) - resumed_frames.append(_capture_kit_viewport_rgb(annotator)) - _assert_frame_sequence_has_motion(resumed_frames) + play_end_frame = _capture_kit_viewport_rgb(annotator) + _save_visualizer_debug_frame( + play_end_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="timeline_play_end", + frame_idx=_CYCLE_FRAME_STEPS, + ) + _save_visualizer_debug_delta( + play_start_frame, + play_end_frame, + viz_kind=viz_kind, + physics_kind=physics_kind, + phase="timeline_play", + frame_idx=_CYCLE_FRAME_STEPS, + ) + _assert_non_flat_frame_array(play_end_frame) + _assert_frames_differ(play_start_frame, play_end_frame) finally: if annotator is not None and render_product is not None: with contextlib.suppress(Exception): @@ -532,7 +730,13 @@ def _run_kit_viewport_frame_motion_test( def _capture_kit_viewport_rgb(annotator) -> np.ndarray: - return _annotator_rgb_to_numpy(annotator.get_data()) + frame = _annotator_rgb_to_numpy(annotator.get_data()) + for _ in range(5): + if frame.shape[:2] != (1, 1) or np.count_nonzero(frame) > 0: + return frame + simulation_app.update() + frame = _annotator_rgb_to_numpy(annotator.get_data()) + return frame def _make_cartpole_camera_env(visualizer_kind: str, backend_kind: str) -> CartpoleCameraEnv: @@ -570,7 +774,7 @@ def _make_cartpole_camera_env(visualizer_kind: str, backend_kind: str) -> Cartpo def test_cartpole_kit_visualizer_replicator_viewport_rgb_motion( backend_kind: str, caplog: pytest.LogCaptureFixture ) -> None: - """Kit + cartpole: Replicator RGB on viewport camera; last frame non-black; early vs late frame differ; logs.""" + """Kit + cartpole: Replicator RGB on viewport camera; frames are non-flat and motion changes pixels; logs.""" env = None try: sim_utils.create_new_stage() @@ -591,10 +795,10 @@ def test_cartpole_kit_visualizer_replicator_viewport_rgb_motion( @pytest.mark.isaacsim_ci @pytest.mark.parametrize("backend_kind", ["physx", "newton"]) -def test_cartpole_newton_visualizer_tiled_camera_rgb_non_black( +def test_cartpole_newton_visualizer_tiled_camera_rgb_non_flat( backend_kind: str, caplog: pytest.LogCaptureFixture ) -> None: - """Newton visualizer + cartpole: env tiled-camera RGB becomes non-black within a few steps; clean logs.""" + """Newton visualizer + cartpole: env tiled-camera RGB becomes non-flat within a few steps; clean logs.""" env = None try: sim_utils.create_new_stage() @@ -603,7 +807,7 @@ def test_cartpole_newton_visualizer_tiled_camera_rgb_non_black( with caplog.at_level(logging.WARNING): env.reset() actions = torch.zeros((env.num_envs, env.action_space.shape[-1]), device=env.device) - _step_until_non_black_camera(env, actions, max_steps=_MAX_NON_BLACK_STEPS) + _step_until_non_flat_camera(env, actions, max_steps=_MAX_FRAME_CHECK_STEPS) _assert_no_visualizer_log_issues(caplog) finally: if env is not None: @@ -615,7 +819,7 @@ def test_cartpole_newton_visualizer_tiled_camera_rgb_non_black( @pytest.mark.isaacsim_ci @pytest.mark.parametrize("backend_kind", ["physx", "newton"]) def test_cartpole_newton_visualizer_viewergl_rgb_motion(backend_kind: str, caplog: pytest.LogCaptureFixture) -> None: - """Newton GL (``ViewerGL.get_frame``): full motion steps, last frame non-black; early vs late differ; logs.""" + """Newton GL (``ViewerGL.get_frame``): frames are non-flat and motion changes pixels; logs.""" env = None try: sim_utils.create_new_stage() @@ -667,7 +871,7 @@ def test_cartpole_rerun_visualizer_smoke_steps_and_logs(backend_kind: str, caplo rerun_visualizers = [viz for viz in env.sim.visualizers if isinstance(viz, RerunVisualizer)] assert rerun_visualizers, "Expected an initialized Rerun visualizer." assert getattr(rerun_visualizers[0], "_viewer", None) is not None, "Rerun viewer was not created." - _step_env_without_frame_check(env, actions, max_steps=_MAX_NON_BLACK_STEPS) + _step_env_without_frame_check(env, actions, max_steps=_MAX_FRAME_CHECK_STEPS) _assert_no_visualizer_log_issues(caplog) finally: if env is not None: @@ -696,7 +900,7 @@ def test_cartpole_viser_visualizer_smoke_steps_and_logs(backend_kind: str, caplo viser_visualizers = [viz for viz in env.sim.visualizers if isinstance(viz, ViserVisualizer)] assert viser_visualizers, "Expected an initialized Viser visualizer." assert getattr(viser_visualizers[0], "_viewer", None) is not None, "Viser viewer was not created." - _step_env_without_frame_check(env, actions, max_steps=_MAX_NON_BLACK_STEPS) + _step_env_without_frame_check(env, actions, max_steps=_MAX_FRAME_CHECK_STEPS) _assert_no_visualizer_log_issues(caplog) finally: if env is not None: From ac4d8229aee9987f3328f01d74d9393b09608748 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Fri, 15 May 2026 20:59:16 +0000 Subject: [PATCH 18/31] tot fixes --- .../isaaclab/isaaclab/envs/direct_marl_env.py | 2 +- .../isaaclab/isaaclab/envs/direct_rl_env.py | 2 +- .../isaaclab/envs/leapp_deployment_env.py | 2 +- .../isaaclab/envs/manager_based_env.py | 2 +- .../markers/visualization_markers_cfg.py | 2 +- .../isaaclab/physics/physics_manager_cfg.py | 2 +- .../isaaclab/scene/interactive_scene_cfg.py | 2 +- .../isaaclab/scene/scene_data_provider.py | 27 +++++++++++++++++++ .../isaaclab/sim/simulation_context.py | 8 ------ .../curobo/curobo_planner_cfg.py | 2 +- .../physics/newton_collision_cfg.py | 2 +- .../physics/newton_manager_cfg.py | 2 +- 12 files changed, 37 insertions(+), 18 deletions(-) diff --git a/source/isaaclab/isaaclab/envs/direct_marl_env.py b/source/isaaclab/isaaclab/envs/direct_marl_env.py index 35272932e4ab..fde6e93beb81 100644 --- a/source/isaaclab/isaaclab/envs/direct_marl_env.py +++ b/source/isaaclab/isaaclab/envs/direct_marl_env.py @@ -27,7 +27,7 @@ from isaaclab.scene import InteractiveScene from isaaclab.sim import SimulationContext from isaaclab.sim.utils.stage import use_stage -from isaaclab.utils.configclass import resolve_cfg_presets +from isaaclab.utils import resolve_cfg_presets from isaaclab.utils.noise import NoiseModel from isaaclab.utils.seed import configure_seed from isaaclab.utils.timer import Timer diff --git a/source/isaaclab/isaaclab/envs/direct_rl_env.py b/source/isaaclab/isaaclab/envs/direct_rl_env.py index 976e3a804c04..b8d18409d6db 100644 --- a/source/isaaclab/isaaclab/envs/direct_rl_env.py +++ b/source/isaaclab/isaaclab/envs/direct_rl_env.py @@ -23,7 +23,7 @@ from isaaclab.scene import InteractiveScene from isaaclab.sim import SimulationContext from isaaclab.sim.utils.stage import use_stage -from isaaclab.utils.configclass import resolve_cfg_presets +from isaaclab.utils import resolve_cfg_presets from isaaclab.utils.noise import NoiseModel from isaaclab.utils.seed import configure_seed from isaaclab.utils.timer import Timer diff --git a/source/isaaclab/isaaclab/envs/leapp_deployment_env.py b/source/isaaclab/isaaclab/envs/leapp_deployment_env.py index a2ad5b897647..6fdf231da86c 100644 --- a/source/isaaclab/isaaclab/envs/leapp_deployment_env.py +++ b/source/isaaclab/isaaclab/envs/leapp_deployment_env.py @@ -31,7 +31,7 @@ from isaaclab.scene import InteractiveScene from isaaclab.sim import SimulationContext from isaaclab.sim.utils.stage import use_stage -from isaaclab.utils.configclass import resolve_cfg_presets +from isaaclab.utils import resolve_cfg_presets from .ui import ViewportCameraController diff --git a/source/isaaclab/isaaclab/envs/manager_based_env.py b/source/isaaclab/isaaclab/envs/manager_based_env.py index 3bab2e841a9a..968208b8ad13 100644 --- a/source/isaaclab/isaaclab/envs/manager_based_env.py +++ b/source/isaaclab/isaaclab/envs/manager_based_env.py @@ -18,7 +18,7 @@ from isaaclab.sim import SimulationContext from isaaclab.sim.utils.stage import use_stage from isaaclab.ui.widgets import ManagerLiveVisualizer -from isaaclab.utils.configclass import resolve_cfg_presets +from isaaclab.utils import resolve_cfg_presets from isaaclab.utils.seed import configure_seed from isaaclab.utils.timer import Timer from isaaclab.utils.version import has_kit diff --git a/source/isaaclab/isaaclab/markers/visualization_markers_cfg.py b/source/isaaclab/isaaclab/markers/visualization_markers_cfg.py index b7cc2002ccf0..933be2d3b53f 100644 --- a/source/isaaclab/isaaclab/markers/visualization_markers_cfg.py +++ b/source/isaaclab/isaaclab/markers/visualization_markers_cfg.py @@ -10,7 +10,7 @@ from dataclasses import MISSING from isaaclab.sim.spawners import SpawnerCfg -from isaaclab.utils.configclass import configclass +from isaaclab.utils import configclass @configclass diff --git a/source/isaaclab/isaaclab/physics/physics_manager_cfg.py b/source/isaaclab/isaaclab/physics/physics_manager_cfg.py index d8e68f0b41a2..0ff2348b591a 100644 --- a/source/isaaclab/isaaclab/physics/physics_manager_cfg.py +++ b/source/isaaclab/isaaclab/physics/physics_manager_cfg.py @@ -10,7 +10,7 @@ from dataclasses import MISSING from typing import TYPE_CHECKING, Any -from isaaclab.utils.configclass import configclass +from isaaclab.utils import configclass if TYPE_CHECKING: from .physics_manager import PhysicsManager diff --git a/source/isaaclab/isaaclab/scene/interactive_scene_cfg.py b/source/isaaclab/isaaclab/scene/interactive_scene_cfg.py index f4328324152c..d88a64c38adb 100644 --- a/source/isaaclab/isaaclab/scene/interactive_scene_cfg.py +++ b/source/isaaclab/isaaclab/scene/interactive_scene_cfg.py @@ -5,7 +5,7 @@ from dataclasses import MISSING -from isaaclab.utils.configclass import configclass +from isaaclab.utils import configclass @configclass diff --git a/source/isaaclab/isaaclab/scene/scene_data_provider.py b/source/isaaclab/isaaclab/scene/scene_data_provider.py index a44700dd575c..5f3f78bf94eb 100644 --- a/source/isaaclab/isaaclab/scene/scene_data_provider.py +++ b/source/isaaclab/isaaclab/scene/scene_data_provider.py @@ -28,6 +28,29 @@ def __init__(self, backend: SceneDataBackend): """ self.backend = backend self._num_envs_cache: int | None = None + self._interactive_scene: Any | None = None + + def set_interactive_scene(self, scene: Any) -> None: + """Attach the active interactive scene for scene-owned sensor discovery.""" + self._interactive_scene = scene + + def get_interactive_scene(self) -> Any | None: + """Return the registered interactive scene, if available.""" + return self._interactive_scene + + def get_camera_sensors(self) -> dict[str, Any]: + """Return Isaac Lab camera sensors keyed by scene sensor name.""" + if self._interactive_scene is None: + return {} + try: + from isaaclab.sensors.camera import Camera + except ImportError: + return {} + return { + name: sensor + for name, sensor in getattr(self._interactive_scene, "sensors", {}).items() + if isinstance(sensor, Camera) + } @property def transform_count(self) -> int: @@ -56,6 +79,10 @@ def usd_stage(self) -> Usd.Stage | None: except Exception: return None + def get_usd_stage(self) -> Usd.Stage | None: + """Return the USD stage for callers using the older method-style API.""" + return self.usd_stage + @property def num_envs(self) -> int: """Number of environments discovered from ``/World/envs/env_`` prims. diff --git a/source/isaaclab/isaaclab/sim/simulation_context.py b/source/isaaclab/isaaclab/sim/simulation_context.py index af0a258dc814..4fdd11daaefe 100644 --- a/source/isaaclab/isaaclab/sim/simulation_context.py +++ b/source/isaaclab/isaaclab/sim/simulation_context.py @@ -634,21 +634,13 @@ def initialize_visualizers(self) -> None: viz.set_camera_view(eye, target) self._pending_camera_view = None -<<<<<<< HEAD if not self._visualizers and self._scene_data_provider is not None: close_provider = getattr(self._scene_data_provider, "close", None) if callable(close_provider): close_provider() self._scene_data_provider = None - def initialize_scene_data_provider(self) -> BaseSceneDataProvider: - if self._scene_data_provider is None: - self._scene_data_provider = SceneDataProvider(self.stage, self) - if self._interactive_scene is not None: - self._scene_data_provider.set_interactive_scene(self._interactive_scene) -======= def get_scene_data_provider(self) -> SceneDataProvider: ->>>>>>> develop return self._scene_data_provider def register_interactive_scene(self, scene) -> None: diff --git a/source/isaaclab_mimic/isaaclab_mimic/motion_planners/curobo/curobo_planner_cfg.py b/source/isaaclab_mimic/isaaclab_mimic/motion_planners/curobo/curobo_planner_cfg.py index b3b3f7cce827..0150052440db 100644 --- a/source/isaaclab_mimic/isaaclab_mimic/motion_planners/curobo/curobo_planner_cfg.py +++ b/source/isaaclab_mimic/isaaclab_mimic/motion_planners/curobo/curobo_planner_cfg.py @@ -13,7 +13,7 @@ from curobo.util_file import get_robot_configs_path, get_world_configs_path, join_path, load_yaml from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR, retrieve_file_path -from isaaclab.utils.configclass import configclass +from isaaclab.utils import configclass @configclass diff --git a/source/isaaclab_newton/isaaclab_newton/physics/newton_collision_cfg.py b/source/isaaclab_newton/isaaclab_newton/physics/newton_collision_cfg.py index 86014d13a885..c8b0db0b3f4a 100644 --- a/source/isaaclab_newton/isaaclab_newton/physics/newton_collision_cfg.py +++ b/source/isaaclab_newton/isaaclab_newton/physics/newton_collision_cfg.py @@ -9,7 +9,7 @@ from typing import Any, Literal -from isaaclab.utils.configclass import configclass +from isaaclab.utils import configclass @configclass diff --git a/source/isaaclab_newton/isaaclab_newton/physics/newton_manager_cfg.py b/source/isaaclab_newton/isaaclab_newton/physics/newton_manager_cfg.py index 6ff646aff57b..85e4061ab911 100644 --- a/source/isaaclab_newton/isaaclab_newton/physics/newton_manager_cfg.py +++ b/source/isaaclab_newton/isaaclab_newton/physics/newton_manager_cfg.py @@ -10,7 +10,7 @@ from typing import TYPE_CHECKING from isaaclab.physics import PhysicsCfg -from isaaclab.utils.configclass import configclass +from isaaclab.utils import configclass from .newton_collision_cfg import NewtonCollisionPipelineCfg From 6d8f029d29ffd8ceb209c4504021262653f190a2 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Tue, 19 May 2026 23:26:24 +0000 Subject: [PATCH 19/31] wip --- docs/source/features/visualization.rst | 53 ++++-------- .../isaaclab/envs/utils/camera_view.py | 36 +++----- .../isaaclab/sensors/camera/camera.py | 8 +- .../isaaclab/visualizers/base_visualizer.py | 19 ----- .../isaaclab/visualizers/visualizer_cfg.py | 61 +++++--------- .../test_simulation_context_visualizers.py | 2 +- .../kit/kit_visualizer.py | 58 ++++--------- .../newton/newton_visualizer.py | 82 ++++--------------- .../rerun/rerun_visualizer.py | 23 +----- .../viser/viser_visualizer.py | 22 +---- 10 files changed, 89 insertions(+), 275 deletions(-) diff --git a/docs/source/features/visualization.rst b/docs/source/features/visualization.rst index 8bfd328a5655..cabd236b7def 100644 --- a/docs/source/features/visualization.rst +++ b/docs/source/features/visualization.rst @@ -190,40 +190,27 @@ Also, there is a CLI arg ``--max_visible_envs`` that overrides ``VisualizerCfg.m Camera Pose Behavior ~~~~~~~~~~~~~~~~~~~~ -The default visualizer camera mode is interactive: ``cam_source="cfg"``, ``tiled_cam_view=False``, -``eye_reference_frame="world"``, ``lookat`` set to a coordinate, and ``lookat_prim_path=None``. -Kit and Newton can also run non-interactive camera image views, including tiled views. If -``tiled_cam_view=True`` is set, that visualizer config uses a camera image view rather than the +The default visualizer camera mode is interactive: ``tiled_cam_view=False`` with ``eye`` and ``lookat`` +controlling the viewer camera. Kit and Newton can also run non-interactive tiled camera image views. +If ``tiled_cam_view=True`` is set, that visualizer config uses a camera image view rather than the default interactive camera; launch a second visualizer config if you want both. -.. list-table:: Camera pose modes +.. list-table:: Camera configuration modes :header-rows: 1 - :widths: 24 22 22 32 + :widths: 24 30 46 * - Mode - Key fields - - ``eye`` meaning - Behavior * - **Default interactive** - - ``lookat=(0, 0, 0)``, ``lookat_prim_path=None``, ``eye_reference_frame="world"`` - - Absolute world position + - ``tiled_cam_view=False``, ``eye=(4, -4, 3)``, ``lookat=(0, 0, 0)`` - Interactive visualizer camera starts at ``eye`` and looks at the fixed ``lookat`` coordinate. - * - Fixed eye, tracked target - - ``lookat=None``, ``lookat_prim_path="/World/envs/*/Robot/base"``, ``eye_reference_frame="world"`` - - Absolute world position - - Camera stays at ``eye`` and rotates toward the matched prim. - * - Follow target - - ``lookat=None``, ``lookat_prim_path="/World/envs/*/Robot/base"``, ``eye_reference_frame="lookat_target"`` - - Offset from target - - Camera moves with the matched prim and looks back at it. - * - Offset from fixed target - - ``lookat=(0, 0, 0)``, ``lookat_prim_path=None``, ``eye_reference_frame="lookat_target"`` - - Offset from ``lookat`` - - Camera position is ``lookat + eye`` and target is the fixed ``lookat`` coordinate. - -When ``cam_source="prim_path"``, the visualizer uses an existing Isaac Lab ``Camera`` sensor selected by -``cam_prim_path``. In that mode, ``eye``, ``eye_reference_frame``, ``lookat``, and ``lookat_prim_path`` do -not drive the sensor pose. + * - Generated tiled camera + - ``tiled_cam_view=True``, ``tiled_cam_prim_path=None``, ``tiled_cam_target_prim_path="/World/envs/*/Robot/base"`` + - The visualizer creates per-env cameras. Each camera looks at the matched target prim, with ``tiled_cam_eye`` as an offset from that target. + * - Existing tiled camera sensors + - ``tiled_cam_view=True``, ``tiled_cam_prim_path="/World/envs/*/Camera"`` + - The visualizer displays existing Isaac Lab ``Camera`` sensor output. Generated-camera fields such as ``tiled_cam_eye`` and ``tiled_cam_target_prim_path`` are ignored. Video Recording --------------- @@ -345,9 +332,7 @@ Omniverse Visualizer window_height=720, eye=(8.0, 8.0, 3.0), - eye_reference_frame="world", lookat=(0.0, 0.0, 0.0), - lookat_prim_path=None, enable_markers=True, enable_live_plots=True, @@ -364,10 +349,8 @@ viewport camera. viewport_name="Tiled Cameras", tiled_cam_view=True, tiled_cam_num=16, - lookat=None, - lookat_prim_path="/World/envs/*/Robot/base", - eye_reference_frame="lookat_target", - eye=(4.0, -4.0, 3.0), + tiled_cam_eye=(4.0, -4.0, 3.0), + tiled_cam_target_prim_path="/World/envs/*/Robot/base", ) @@ -419,9 +402,7 @@ Newton Visualizer # Camera settings eye=(8.0, 8.0, 3.0), # Initial camera position (x, y, z) - eye_reference_frame="world", # Interpret eye as a world-space position lookat=(0.0, 0.0, 0.0), # Camera look-at target - lookat_prim_path=None, # Optional dynamic target prim path # Performance tuning update_frequency=1, # Update every N frames (1=every frame) @@ -454,10 +435,8 @@ GL camera. window_height=720, tiled_cam_view=True, tiled_cam_env_indices=[0, 1, 2, 3], - lookat=None, - lookat_prim_path="/World/envs/*/Robot/base", - eye_reference_frame="lookat_target", - eye=(4.0, -4.0, 3.0), + tiled_cam_eye=(4.0, -4.0, 3.0), + tiled_cam_target_prim_path="/World/envs/*/Robot/base", ) diff --git a/source/isaaclab/isaaclab/envs/utils/camera_view.py b/source/isaaclab/isaaclab/envs/utils/camera_view.py index 3bfb23fc5249..3ee3d89f252d 100644 --- a/source/isaaclab/isaaclab/envs/utils/camera_view.py +++ b/source/isaaclab/isaaclab/envs/utils/camera_view.py @@ -14,6 +14,7 @@ import numpy as np import torch +import warp as wp import isaaclab.sim as sim_utils from isaaclab.sensors.camera import Camera, CameraCfg @@ -81,7 +82,7 @@ def find_camera_by_prim_path(camera_sensors: dict[str, Camera], cam_prim_path: s if stage_matches: raise RuntimeError( f"cam_prim_path={cam_prim_path!r} matched USD camera prims, but no Isaac Lab Camera sensor owns them. " - "Add the camera to scene.sensors or use cam_source='cfg'." + "Add the camera to scene.sensors or leave tiled_cam_prim_path unset to use generated tiled cameras." ) raise RuntimeError(f"No Isaac Lab Camera sensor matched cam_prim_path={cam_prim_path!r}.") @@ -141,6 +142,10 @@ def remove_generated_prims(prim_paths: list[str] | None) -> None: def camera_rgb_batch(camera: Camera, env_indices: list[int]) -> torch.Tensor: """Return RGB output for selected env indices.""" rgb = camera.data.output["rgb"] + if isinstance(rgb, wp.array): + rgb = wp.to_torch(rgb) + elif hasattr(rgb, "torch"): + rgb = rgb.torch if env_indices: index = torch.tensor(env_indices, dtype=torch.long, device=rgb.device) return rgb.index_select(0, index) @@ -240,7 +245,7 @@ def prim_world_positions(stage: Any, prim_path_template: str, env_indices: list[ except Exception: prim = stage.GetPrimAtPath(prim_path) if not prim.IsValid(): - raise RuntimeError(f"lookat_prim_path resolved to missing prim: {prim_path!r}.") + raise RuntimeError(f"tiled_cam_target_prim_path resolved to missing prim: {prim_path!r}.") transform = xform_cache.GetLocalToWorldTransform(prim) translation = transform.ExtractTranslation() positions.append((float(translation[0]), float(translation[1]), float(translation[2]))) @@ -263,38 +268,19 @@ def apply_camera_view_from_origins( camera._update_poses(None) -def resolve_fixed_camera_targets( - lookat: tuple[float, float, float], - count: int, - *, - device: str | torch.device = "cpu", -) -> torch.Tensor: - """Return a repeated fixed look-at target tensor.""" - target = torch.tensor(lookat, dtype=torch.float32, device=device).reshape(1, 3) - return target.repeat(max(0, int(count)), 1) - - def apply_camera_target_positions( camera: Camera, target_positions: torch.Tensor, eye: tuple[float, float, float], - eye_reference_frame: str, + env_ids: list[int] | None = None, ) -> tuple[torch.Tensor, torch.Tensor]: - """Set generated camera poses from target positions and eye reference mode.""" + """Set generated tiled camera poses as target-relative eye offsets.""" device = camera.device target_positions = target_positions.to(device=device) eye_offset = torch.tensor(eye, dtype=torch.float32, device=device).unsqueeze(0) - if eye_reference_frame == "world": - eyes = eye_offset.repeat(target_positions.shape[0], 1) - elif eye_reference_frame == "lookat_target": - eyes = target_positions + eye_offset - else: - raise ValueError( - "VisualizerCfg.eye_reference_frame must be either 'world' or 'lookat_target', " - f"got {eye_reference_frame!r}." - ) + eyes = target_positions + eye_offset targets = target_positions - camera.set_world_poses_from_view(eyes, targets) + camera.set_world_poses_from_view(eyes, targets, env_ids=env_ids) camera._update_poses(None) return eyes.detach().cpu(), targets.detach().cpu() diff --git a/source/isaaclab/isaaclab/sensors/camera/camera.py b/source/isaaclab/isaaclab/sensors/camera/camera.py index 588d4cb000f4..33c5e3c3fc26 100644 --- a/source/isaaclab/isaaclab/sensors/camera/camera.py +++ b/source/isaaclab/isaaclab/sensors/camera/camera.py @@ -330,7 +330,7 @@ def set_world_poses( elif not isinstance(positions, torch.Tensor): positions = torch.tensor(positions, device=self._device) positions = positions.to(device=self._device, dtype=torch.float32).reshape(-1, 3) - pos_wp = wp.from_torch(positions.contiguous()) + pos_wp = wp.from_torch(positions.contiguous(), dtype=wp.vec3f) ori_wp = None if orientations is not None: if isinstance(orientations, np.ndarray): @@ -339,7 +339,7 @@ def set_world_poses( orientations = torch.tensor(orientations, device=self._device) orientations = orientations.to(device=self._device, dtype=torch.float32).reshape(-1, 4) orientations = convert_camera_frame_orientation_convention(orientations, origin=convention, target="opengl") - ori_wp = wp.from_torch(orientations.contiguous()) + ori_wp = wp.from_torch(orientations.contiguous(), dtype=wp.vec4f) idx_wp = self._resolve_env_ids_wp(env_ids) self._view.set_world_poses(pos_wp, ori_wp, idx_wp) @@ -400,8 +400,8 @@ def set_world_poses_from_view( orientations = quat_from_matrix(rotation_matrix) idx_wp = wp.from_torch(env_ids_torch.contiguous(), dtype=wp.int32) self._view.set_world_poses( - wp.from_torch(eyes.contiguous()), - wp.from_torch(orientations.contiguous()), + wp.from_torch(eyes.contiguous(), dtype=wp.vec3f), + wp.from_torch(orientations.contiguous(), dtype=wp.vec4f), idx_wp, ) diff --git a/source/isaaclab/isaaclab/visualizers/base_visualizer.py b/source/isaaclab/isaaclab/visualizers/base_visualizer.py index de429930cfda..9e41ddce3402 100644 --- a/source/isaaclab/isaaclab/visualizers/base_visualizer.py +++ b/source/isaaclab/isaaclab/visualizers/base_visualizer.py @@ -189,29 +189,10 @@ def _resolve_cfg_camera_pose( self, _visualizer_name: str ) -> tuple[tuple[float, float, float], tuple[float, float, float]]: """Resolve camera pose from cfg eye/lookat fields.""" - self._validate_cfg_camera_target(_visualizer_name) - if self.cfg.lookat_prim_path is not None: - raise RuntimeError( - f"[{_visualizer_name}] lookat_prim_path is not supported by this cfg camera pose path." - ) eye = tuple(float(v) for v in self.cfg.eye) lookat = tuple(float(v) for v in self.cfg.lookat) return eye, lookat - def _validate_cfg_camera_target(self, _visualizer_name: str) -> None: - """Validate mutually exclusive cfg camera target fields.""" - if self.cfg.cam_source == "prim_path": - return - if self.cfg.lookat is not None and self.cfg.lookat_prim_path is not None: - raise RuntimeError( - f"[{_visualizer_name}] lookat and lookat_prim_path are mutually exclusive. " - "Set lookat=None when using lookat_prim_path." - ) - if self.cfg.lookat is None and self.cfg.lookat_prim_path is None: - raise RuntimeError( - f"[{_visualizer_name}] either lookat or lookat_prim_path must be set for cfg camera mode." - ) - def _focal_length_to_vertical_fov_degrees(self) -> float: """Convert cfg focal length to vertical FOV using USD's default aperture.""" focal_length = float(self.cfg.focal_length) diff --git a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py index 5621279f5a20..e39ae00b90de 100644 --- a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py +++ b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py @@ -7,7 +7,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Literal +from typing import TYPE_CHECKING from isaaclab.utils.configclass import configclass @@ -35,58 +35,35 @@ class VisualizerCfg: """Enable live plotting of data.""" eye: tuple[float, float, float] = (4.0, -4.0, 3.0) - """Camera eye position. + """Interactive visualizer camera eye position in world coordinates.""" - Interpreted according to :attr:`eye_reference_frame`. - """ - - eye_reference_frame: Literal["world", "lookat_target"] = "world" - """Reference frame for :attr:`eye`. - - - ``"world"``: ``eye`` is an absolute world-space camera position. - - ``"lookat_target"``: ``eye`` is an offset from the active look-at target. - """ - - lookat: tuple[float, float, float] | None = (0.0, 0.0, 0.0) - """Fixed camera look-at target in world coordinates. - - Mutually exclusive with :attr:`lookat_prim_path`. - """ + lookat: tuple[float, float, float] = (0.0, 0.0, 0.0) + """Interactive visualizer camera look-at target in world coordinates.""" focal_length: float = 15.0 - """Camera focal length in millimeters for visualizer camera views. + """Camera focal length in millimeters for visualizer camera views.""" - Kit applies this directly to USD cameras. Newton-style backends convert it - to a vertical field-of-view using the USD default vertical aperture. - """ - - cam_source: Literal["cfg", "prim_path"] = "cfg" - """Camera source mode. + tiled_cam_view: bool = True + """Enable a non-interactive tiled camera image view.""" - - ``"cfg"`` uses the configured camera pose fields. - - ``"prim_path"`` uses existing Isaac Lab camera sensor output and ignores - :attr:`eye`, :attr:`eye_reference_frame`, :attr:`lookat`, and :attr:`lookat_prim_path`. - """ + tiled_cam_num: int = 24 + """Number of camera tiles to show when tiled_cam_env_indices is None.""" - cam_prim_path: str = "/World/envs/*/Camera" - """Camera prim path or env wildcard path when cam_source='prim_path'.""" + tiled_cam_env_indices: list[int] | None = None + """Env ids to show in tiled camera view; if None, sample tiled_cam_num envs once at initialization.""" - lookat_prim_path: str | None = None - """Prim path used as a dynamic look-at target. + tiled_cam_prim_path: str | None = None + """Existing Isaac Lab Camera sensor prim path to display. - When set, the camera orients toward the prim's world position. Supports env - wildcard paths such as ``"/World/envs/*/Robot/base"``. Mutually exclusive - with :attr:`lookat`. + If ``None``, the visualizer creates generated tiled cameras. If set, it should + point to an existing camera sensor, for example ``"/World/envs/*/Camera"``. """ - tiled_cam_view: bool = False - """Enable a non-interactive tiled camera image view.""" + tiled_cam_eye: tuple[float, float, float] = (4.0, -4.0, 3.0) + """Eye offset from tiled_cam_target_prim_path for generated tiled cameras.""" - tiled_cam_num: int = 100 - """Number of camera tiles to show when tiled_cam_env_indices is None.""" - - tiled_cam_env_indices: list[int] | None = None - """Env ids to show in tiled camera view; if None, sample tiled_cam_num envs once at initialization.""" + tiled_cam_target_prim_path: str = "/World/envs/*/Robot/base" + """Prim path that generated tiled cameras follow and look at.""" max_visible_envs: int | None = None """Upper bound on how many envs are shown. diff --git a/source/isaaclab/test/sim/test_simulation_context_visualizers.py b/source/isaaclab/test/sim/test_simulation_context_visualizers.py index 330086e3517c..d2f1a8029a48 100644 --- a/source/isaaclab/test/sim/test_simulation_context_visualizers.py +++ b/source/isaaclab/test/sim/test_simulation_context_visualizers.py @@ -679,7 +679,7 @@ def get_usd_stage(self): visualizer._setup_viewport() - assert cfg.cam_source == "cfg" + assert not cfg.tiled_cam_view assert applied_camera_poses == [(cfg.eye, cfg.lookat)] assert viewport_window.viewport_api.set_active_camera_calls == [] assert visualizer._controlled_camera_path == "/OmniverseKit_Persp" diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py index f5943ed9474b..bb6d2c39675c 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py @@ -24,8 +24,6 @@ find_camera_by_prim_path, prim_world_positions, remove_generated_prims, - resolve_fixed_camera_targets, - resolve_mono_env_index, resolve_tiled_env_indices, ) from isaaclab.app.settings_manager import get_settings_manager @@ -123,10 +121,9 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: title="KitVisualizer Configuration", rows=[ ("eye", self.cfg.eye), - ("eye_reference_frame", self.cfg.eye_reference_frame), ("lookat", self.cfg.lookat), - ("lookat_prim_path", self.cfg.lookat_prim_path), - ("cam_source", self.cfg.cam_source), + ("tiled_cam_view", self.cfg.tiled_cam_view), + ("tiled_cam_num", self.cfg.tiled_cam_num), ("max_visible_envs", self.cfg.max_visible_envs), ("num_visualized_envs", num_visualized_envs), ("create_viewport", self.cfg.create_viewport), @@ -324,19 +321,13 @@ def _setup_viewport(self) -> None: if self._uses_camera_sensor_view(): # Camera sensor image views are shown in a non-interactive image panel. pass - elif self.cfg.cam_source == "prim_path": - if not self._set_active_camera_path(self.cfg.cam_prim_path): - raise RuntimeError( - "[KitVisualizer] cam_source='prim_path' requires a valid cam_prim_path. " - f"Camera prim not found: '{self.cfg.cam_prim_path}'." - ) else: self._apply_cfg_camera_pose_if_configured() self._refresh_controlled_camera_path() def _uses_camera_sensor_view(self) -> bool: """Return whether Kit should display a camera sensor image instead of an interactive viewport camera.""" - return bool(self.cfg.tiled_cam_view or self.cfg.cam_source == "prim_path" or self.cfg.lookat_prim_path) + return bool(self.cfg.tiled_cam_view) def _setup_camera_sensor_view(self, num_envs: int) -> None: """Resolve or create the Camera sensor backing non-interactive image views.""" @@ -345,22 +336,18 @@ def _setup_camera_sensor_view(self, num_envs: int) -> None: logger.debug( "[KitVisualizer] Setting up camera image view: tiled=%s source=%s num_envs=%s", self.cfg.tiled_cam_view, - self.cfg.cam_source, + "prim_path" if self.cfg.tiled_cam_prim_path is not None else "generated", num_envs, ) - env_ids = ( - resolve_tiled_env_indices(num_envs, self.cfg.tiled_cam_num, self.cfg.tiled_cam_env_indices) - if self.cfg.tiled_cam_view - else resolve_mono_env_index(num_envs) - ) + env_ids = resolve_tiled_env_indices(num_envs, self.cfg.tiled_cam_num, self.cfg.tiled_cam_env_indices) self._camera_env_indices = env_ids - if self.cfg.cam_source == "prim_path": + if self.cfg.tiled_cam_prim_path is not None: logger.debug( - "[KitVisualizer] cam_source='prim_path' uses existing camera sensor output; " - "cfg camera pose fields are ignored." + "[KitVisualizer] tiled_cam_prim_path uses existing camera sensor output; " + "generated tiled camera pose fields are ignored." ) cameras = self._scene_data_provider.get_camera_sensors() - self._camera_sensor = find_camera_by_prim_path(cameras, self.cfg.cam_prim_path, env_ids) + self._camera_sensor = find_camera_by_prim_path(cameras, self.cfg.tiled_cam_prim_path, env_ids) self._camera_sensor_indices = env_ids else: from isaaclab_physx.renderers import IsaacRtxRendererCfg @@ -380,7 +367,7 @@ def _setup_camera_sensor_view(self, num_envs: int) -> None: renderer_cfg=IsaacRtxRendererCfg(), ) logger.debug("[KitVisualizer] Generated camera sensor initialized.") - self._camera_sensor_indices = [] + self._camera_sensor_indices = env_ids self._camera_is_owned = True self._update_owned_camera_poses() logger.debug("[KitVisualizer] Generated camera poses initialized.") @@ -427,21 +414,14 @@ def _update_owned_camera_poses(self) -> None: """Update generated camera poses from env origins or follow prims.""" if self._camera_sensor is None or not self._camera_is_owned: return - if self.cfg.lookat_prim_path: - target_positions = prim_world_positions( - self._scene_data_provider.get_usd_stage(), - self.cfg.lookat_prim_path, - self._camera_env_indices, - scene=self._scene_data_provider.get_interactive_scene(), - ) - elif self.cfg.lookat is not None: - target_positions = resolve_fixed_camera_targets( - self.cfg.lookat, len(self._camera_env_indices), device=self._camera_sensor.device - ) - else: - raise RuntimeError("[KitVisualizer] lookat or lookat_prim_path must be set for generated camera views.") + target_positions = prim_world_positions( + self._scene_data_provider.get_usd_stage(), + self.cfg.tiled_cam_target_prim_path, + self._camera_env_indices, + scene=self._scene_data_provider.get_interactive_scene(), + ) eyes, targets = apply_camera_target_positions( - self._camera_sensor, target_positions, self.cfg.eye, self.cfg.eye_reference_frame + self._camera_sensor, target_positions, self.cfg.tiled_cam_eye, self._camera_env_indices ) self._log_camera_pose_debug(eyes, targets) @@ -470,7 +450,7 @@ def _update_camera_image_panel(self, dt: float) -> None: logger.debug("[KitVisualizer] Updating camera image panel for the first time.") self._logged_camera_panel_first_update = True self._camera_update_count += 1 - if self._camera_is_owned and self.cfg.lookat_prim_path: + if self._camera_is_owned: self._update_owned_camera_poses() if not hasattr(self, "_logged_camera_panel_before_data"): logger.debug("[KitVisualizer] Fetching camera RGB tensor.") @@ -608,8 +588,6 @@ def _set_viewport_camera(self, position: tuple[float, float, float], target: tup def _apply_cfg_camera_pose_if_configured(self) -> None: """Apply configured camera pose from eye/lookat.""" - if self.cfg.lookat is None: - raise RuntimeError("[KitVisualizer] lookat must be set when using the interactive cfg camera.") self._set_viewport_camera(self.cfg.eye, self.cfg.lookat) def _set_active_camera_path(self, camera_path: str) -> bool: diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py index 1eb737fca2e2..80a40bb603ad 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py @@ -24,8 +24,6 @@ find_camera_by_prim_path, prim_world_positions, remove_generated_prims, - resolve_fixed_camera_targets, - resolve_mono_env_index, resolve_tiled_env_indices, ) from isaaclab.visualizers.base_visualizer import BaseVisualizer @@ -317,7 +315,6 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: raise RuntimeError("Newton visualizer requires a scene_data_provider.") self._scene_data_provider = scene_data_provider - self._validate_cfg_camera_target("NewtonVisualizer") num_envs = scene_data_provider.num_envs metadata = {"num_envs": num_envs} self._env_ids = self._compute_visualized_env_ids() @@ -352,12 +349,7 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: ) self._viewer.set_world_offsets((0.0, 0.0, 0.0)) self._apply_camera_focal_length() - # Sensor-backed camera views are non-interactive image panels; keep the GL camera at cfg pose. - initial_pose = ( - self._resolve_static_viewer_camera_pose() - if self._uses_camera_sensor_view() - else self._resolve_initial_camera_pose() - ) + initial_pose = self._resolve_initial_camera_pose() self._apply_camera_pose(initial_pose) self._viewer.up_axis = 2 # Z-up @@ -393,11 +385,10 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: "eye", tuple(float(x) for x in self._viewer.camera.pos) if self._viewer is not None else self.cfg.eye, ), - ("eye_reference_frame", self.cfg.eye_reference_frame), ("lookat", self._last_camera_pose[1] if self._last_camera_pose else self.cfg.lookat), - ("lookat_prim_path", self.cfg.lookat_prim_path), ("focal_length", self.cfg.focal_length), - ("cam_source", self.cfg.cam_source), + ("tiled_cam_view", self.cfg.tiled_cam_view), + ("tiled_cam_num", self.cfg.tiled_cam_num), ("num_visualized_envs", num_visualized_envs), ("headless", self.cfg.headless), ], @@ -422,9 +413,6 @@ def step(self, dt: float) -> None: self._state = NewtonManager.get_state(self._scene_data_provider) return - if self.cfg.cam_source == "prim_path" and not self._uses_camera_sensor_view(): - self._update_camera_from_usd_path() - self._state = NewtonManager.get_state(self._scene_data_provider) update_frequency = self._viewer._update_frequency if self._viewer else self._update_frequency @@ -488,43 +476,25 @@ def _resolve_initial_camera_pose(self) -> tuple[tuple[float, float, float], tupl Returns: Camera eye and target tuples. """ - if self.cfg.cam_source == "prim_path": - pose = self._resolve_camera_pose_from_usd_path(self.cfg.cam_prim_path) - if pose is not None: - return pose - raise RuntimeError( - "[NewtonVisualizer] cam_source='prim_path' requires a resolvable camera prim path, " - f"but no camera pose was found for '{self.cfg.cam_prim_path}'." - ) return self._resolve_cfg_camera_pose("NewtonVisualizer") - def _resolve_static_viewer_camera_pose(self) -> tuple[tuple[float, float, float], tuple[float, float, float]]: - """Resolve a non-interactive fallback pose for the GL camera.""" - if self.cfg.lookat is not None: - return self._resolve_cfg_camera_pose("NewtonVisualizer") - return tuple(float(v) for v in self.cfg.eye), (0.0, 0.0, 0.0) - def _uses_camera_sensor_view(self) -> bool: """Return whether the visualizer displays camera sensor images instead of interactive camera controls.""" - return bool(self.cfg.tiled_cam_view or self.cfg.cam_source == "prim_path" or self.cfg.lookat_prim_path) + return bool(self.cfg.tiled_cam_view) def _setup_camera_sensor_view(self, num_envs: int) -> None: """Resolve or create the camera sensor used by non-interactive image views.""" if not self._uses_camera_sensor_view(): return - env_ids = ( - resolve_tiled_env_indices(num_envs, self.cfg.tiled_cam_num, self.cfg.tiled_cam_env_indices) - if self.cfg.tiled_cam_view - else resolve_mono_env_index(num_envs) - ) + env_ids = resolve_tiled_env_indices(num_envs, self.cfg.tiled_cam_num, self.cfg.tiled_cam_env_indices) self._camera_env_indices = env_ids - if self.cfg.cam_source == "prim_path": + if self.cfg.tiled_cam_prim_path is not None: logger.debug( - "[NewtonVisualizer] cam_source='prim_path' uses existing camera sensor output; " - "cfg camera pose fields are ignored." + "[NewtonVisualizer] tiled_cam_prim_path uses existing camera sensor output; " + "generated tiled camera pose fields are ignored." ) cameras = self._scene_data_provider.get_camera_sensors() - self._camera_sensor = find_camera_by_prim_path(cameras, self.cfg.cam_prim_path, env_ids) + self._camera_sensor = find_camera_by_prim_path(cameras, self.cfg.tiled_cam_prim_path, env_ids) self._camera_sensor_indices = env_ids return @@ -538,7 +508,7 @@ def _setup_camera_sensor_view(self, num_envs: int) -> None: height=tile_h, renderer_cfg=NewtonWarpRendererCfg(), ) - self._camera_sensor_indices = [] + self._camera_sensor_indices = env_ids self._camera_is_owned = True self._update_owned_camera_poses() @@ -546,21 +516,14 @@ def _update_owned_camera_poses(self) -> None: """Update generated camera poses from env origins or follow prims.""" if self._camera_sensor is None or not self._camera_is_owned: return - if self.cfg.lookat_prim_path: - target_positions = prim_world_positions( - self._scene_data_provider.get_usd_stage(), - self.cfg.lookat_prim_path, - self._camera_env_indices, - scene=self._scene_data_provider.get_interactive_scene(), - ) - elif self.cfg.lookat is not None: - target_positions = resolve_fixed_camera_targets( - self.cfg.lookat, len(self._camera_env_indices), device=self._camera_sensor.device - ) - else: - raise RuntimeError("[NewtonVisualizer] lookat or lookat_prim_path must be set for generated camera views.") + target_positions = prim_world_positions( + self._scene_data_provider.get_usd_stage(), + self.cfg.tiled_cam_target_prim_path, + self._camera_env_indices, + scene=self._scene_data_provider.get_interactive_scene(), + ) eyes, targets = apply_camera_target_positions( - self._camera_sensor, target_positions, self.cfg.eye, self.cfg.eye_reference_frame + self._camera_sensor, target_positions, self.cfg.tiled_cam_eye, self._camera_env_indices ) self._log_camera_pose_debug(eyes, targets) @@ -585,7 +548,7 @@ def _log_camera_sensor_image(self) -> None: """Log the selected camera sensor RGB output into Newton's image panel.""" if self._viewer is None or self._camera_sensor is None: return - if self._camera_is_owned and self.cfg.lookat_prim_path: + if self._camera_is_owned: self._update_owned_camera_poses() if self._camera_is_owned: self._camera_sensor.update(dt=0.0, force_recompute=True) @@ -635,15 +598,6 @@ def _apply_camera_focal_length(self) -> None: return self._viewer.camera.fov = self._focal_length_to_vertical_fov_degrees() - def _update_camera_from_usd_path(self) -> None: - """Refresh camera pose from configured USD camera path when it changes.""" - pose = self._resolve_camera_pose_from_usd_path(self.cfg.cam_prim_path) - if pose is None: - return - if self._last_camera_pose == pose: - return - self._apply_camera_pose(pose) - def supports_markers(self) -> bool: """Newton OpenGL viewer supports Isaac Lab markers through viewer-side meshes and lines.""" return bool(self.cfg.enable_markers) diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py index cc55dcef7fa5..751b14205698 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py @@ -232,7 +232,6 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: ("eye", self.cfg.eye), ("lookat", self.cfg.lookat), ("focal_length", f"{self.cfg.focal_length} (not applied: Rerun EyeControls3D has no FOV field)"), - ("cam_source", self.cfg.cam_source), ("num_visualized_envs", num_visualized_envs), ("endpoint", f"http://{viewer_host}:{web_port}"), ("bind_address", bind_address), @@ -260,9 +259,6 @@ def step(self, dt: float) -> None: self._sim_time += dt self._step_counter += 1 - if self.cfg.cam_source == "prim_path": - self._update_camera_from_usd_path() - self._state = NewtonManager.get_state(self._scene_data_provider) num_envs = NewtonManager.get_num_envs() @@ -313,15 +309,7 @@ def is_running(self) -> bool: return self._viewer.is_running() def _resolve_initial_camera_pose(self) -> tuple[tuple[float, float, float], tuple[float, float, float]]: - """Resolve initial camera pose from config or USD camera path.""" - if self.cfg.cam_source == "prim_path": - pose = self._resolve_camera_pose_from_usd_path(self.cfg.cam_prim_path) - if pose is not None: - return pose - raise RuntimeError( - "[RerunVisualizer] cam_source='prim_path' requires a resolvable camera prim path, " - f"but no camera pose was found for '{self.cfg.cam_prim_path}'." - ) + """Resolve initial camera pose from config.""" return self._resolve_cfg_camera_pose("RerunVisualizer") def _apply_camera_pose(self, pose: tuple[tuple[float, float, float], tuple[float, float, float]]) -> None: @@ -348,15 +336,6 @@ def _apply_camera_pose(self, pose: tuple[tuple[float, float, float], tuple[float ) self._last_camera_pose = (cam_pos, cam_target) - def _update_camera_from_usd_path(self) -> None: - """Refresh camera pose from configured USD camera path when it changes.""" - pose = self._resolve_camera_pose_from_usd_path(self.cfg.cam_prim_path) - if pose is None: - return - if self._last_camera_pose == pose: - return - self._apply_camera_pose(pose) - def supports_markers(self) -> bool: """Rerun backend supports Isaac Lab markers through Newton viewer primitives.""" return bool(self.cfg.enable_markers) diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py index 5848690263f1..57282b3152db 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py @@ -171,7 +171,6 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: ("eye", self.cfg.eye), ("lookat", self.cfg.lookat), ("focal_length", self.cfg.focal_length), - ("cam_source", self.cfg.cam_source), ("num_visualized_envs", num_visualized_envs), ("bind_address", self.cfg.bind_address), ("display_address", self.cfg.display_address), @@ -192,8 +191,6 @@ def step(self, dt: float) -> None: if not self._is_initialized or self._viewer is None or self._scene_data_provider is None: return - if self.cfg.cam_source == "prim_path": - self._update_camera_from_usd_path() self._apply_pending_camera_pose() self._state = NewtonManager.get_state(self._scene_data_provider) @@ -317,15 +314,7 @@ def _close_viewer(self, finalize_viser: bool = False) -> None: self._viewer = None def _resolve_initial_camera_pose(self) -> tuple[tuple[float, float, float], tuple[float, float, float]]: - """Resolve initial camera pose from config or USD camera path.""" - if self.cfg.cam_source == "prim_path": - pose = self._resolve_camera_pose_from_usd_path(self.cfg.cam_prim_path) - if pose is not None: - return pose - raise RuntimeError( - "[ViserVisualizer] cam_source='prim_path' requires a resolvable camera prim path, " - f"but no camera pose was found for '{self.cfg.cam_prim_path}'." - ) + """Resolve initial camera pose from config.""" return self._resolve_cfg_camera_pose("ViserVisualizer") def _try_apply_viser_camera_view(self, pose: tuple[tuple[float, float, float], tuple[float, float, float]]) -> bool: @@ -383,12 +372,3 @@ def _apply_pending_camera_pose(self) -> None: if self._try_apply_viser_camera_view(self._pending_camera_pose): self._last_camera_pose = self._pending_camera_pose self._pending_camera_pose = None - - def _update_camera_from_usd_path(self) -> None: - """Refresh camera pose from configured USD camera path when it changes.""" - pose = self._resolve_camera_pose_from_usd_path(self.cfg.cam_prim_path) - if pose is None: - return - if self._last_camera_pose == pose or self._pending_camera_pose == pose: - return - self._set_viser_camera_view(pose) From 862e394562d9907a8cd944b39d232af425815b66 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Wed, 20 May 2026 00:39:46 +0000 Subject: [PATCH 20/31] working! --- docs/source/features/visualization.rst | 2 + .../isaaclab/envs/utils/camera_view.py | 21 ++++- .../isaaclab/visualizers/visualizer_cfg.py | 9 ++- .../kit/kit_visualizer.py | 81 ++++++++++++++++++- .../newton/newton_visualizer.py | 53 +++++++++++- 5 files changed, 155 insertions(+), 11 deletions(-) diff --git a/docs/source/features/visualization.rst b/docs/source/features/visualization.rst index cabd236b7def..cc7f7ab3b327 100644 --- a/docs/source/features/visualization.rst +++ b/docs/source/features/visualization.rst @@ -194,6 +194,8 @@ The default visualizer camera mode is interactive: ``tiled_cam_view=False`` with controlling the viewer camera. Kit and Newton can also run non-interactive tiled camera image views. If ``tiled_cam_view=True`` is set, that visualizer config uses a camera image view rather than the default interactive camera; launch a second visualizer config if you want both. +Kit and Newton cap tiled camera views at 100 tiles. +Kit tiled camera views require launching with ``--enable_cameras``. .. list-table:: Camera configuration modes :header-rows: 1 diff --git a/source/isaaclab/isaaclab/envs/utils/camera_view.py b/source/isaaclab/isaaclab/envs/utils/camera_view.py index 3ee3d89f252d..21a68f3f4d14 100644 --- a/source/isaaclab/isaaclab/envs/utils/camera_view.py +++ b/source/isaaclab/isaaclab/envs/utils/camera_view.py @@ -20,6 +20,7 @@ from isaaclab.sensors.camera import Camera, CameraCfg _GENERATED_CAMERA_NAME = "VisualizerCamera" +VISUALIZER_TILED_CAMERA_MAX_TILES = 100 @dataclass @@ -32,14 +33,28 @@ class ResolvedCameraView: generated_prim_paths: list[str] | None = None -def resolve_tiled_env_indices(num_envs: int, tiled_cam_num: int, env_indices: list[int] | None) -> list[int]: +def resolve_tiled_env_indices( + num_envs: int, + tiled_cam_num: int, + env_indices: list[int] | None, + max_tiles: int | None = None, + sample_from: list[int] | None = None, +) -> list[int]: """Resolve env ids for tiled camera view once at visualizer initialization.""" if num_envs <= 0: return [] - max_count = min(max(1, int(tiled_cam_num)), num_envs) if env_indices is not None: + max_count = min(max(1, int(tiled_cam_num)), num_envs) + if max_tiles is not None: + max_count = min(max_count, max(1, int(max_tiles))) return [idx for idx in env_indices if 0 <= int(idx) < num_envs][:max_count] - return sorted(random.sample(range(num_envs), max_count)) + candidates = [idx for idx in (sample_from if sample_from is not None else range(num_envs)) if 0 <= int(idx) < num_envs] + if not candidates: + return [] + max_count = min(max(1, int(tiled_cam_num)), len(candidates)) + if max_tiles is not None: + max_count = min(max_count, max(1, int(max_tiles))) + return sorted(random.sample(candidates, max_count)) def resolve_mono_env_index(num_envs: int) -> list[int]: diff --git a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py index e39ae00b90de..b1dcd50ddda1 100644 --- a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py +++ b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py @@ -46,11 +46,14 @@ class VisualizerCfg: tiled_cam_view: bool = True """Enable a non-interactive tiled camera image view.""" - tiled_cam_num: int = 24 - """Number of camera tiles to show when tiled_cam_env_indices is None.""" + tiled_cam_num: int = 57 + """Number of camera tiles to show when tiled_cam_env_indices is None, capped at 100.""" tiled_cam_env_indices: list[int] | None = None - """Env ids to show in tiled camera view; if None, sample tiled_cam_num envs once at initialization.""" + """Env ids to show in tiled camera view; capped at 100 entries. + + If ``None``, envs are randomly sampled from all visible envs. + """ tiled_cam_prim_path: str | None = None """Existing Isaac Lab Camera sensor prim path to display. diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py index bb6d2c39675c..080836a019cf 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py @@ -16,6 +16,7 @@ from pxr import Gf, Usd, UsdGeom, Vt from isaaclab.envs.utils.camera_view import ( + VISUALIZER_TILED_CAMERA_MAX_TILES, apply_camera_target_positions, camera_rgb_batch, compose_rgb_grid_tensor, @@ -27,6 +28,7 @@ resolve_tiled_env_indices, ) from isaaclab.app.settings_manager import get_settings_manager +from isaaclab.utils.math import create_rotation_matrix_from_view, quat_from_matrix from isaaclab.visualizers.base_visualizer import BaseVisualizer from isaaclab_visualizers.newton_adapter import resolve_visible_env_indices @@ -102,7 +104,6 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: raise RuntimeError("[KitVisualizer] USD stage not available from scene_data_provider.") num_envs = scene_data_provider.num_envs - self._validate_cfg_camera_target("KitVisualizer") self._ensure_simulation_app() self._setup_viewport() @@ -331,7 +332,14 @@ def _uses_camera_sensor_view(self) -> bool: def _setup_camera_sensor_view(self, num_envs: int) -> None: """Resolve or create the Camera sensor backing non-interactive image views.""" - if not self._uses_camera_sensor_view() or self._runtime_headless: + if not self._uses_camera_sensor_view(): + return + if not get_settings_manager().get("/isaaclab/cameras_enabled", False): + raise RuntimeError( + "[KitVisualizer] tiled_cam_view=True requires camera rendering support. " + "Rerun with --enable_cameras, or disable tiled_cam_view for this visualizer config." + ) + if self._runtime_headless: return logger.debug( "[KitVisualizer] Setting up camera image view: tiled=%s source=%s num_envs=%s", @@ -339,7 +347,13 @@ def _setup_camera_sensor_view(self, num_envs: int) -> None: "prim_path" if self.cfg.tiled_cam_prim_path is not None else "generated", num_envs, ) - env_ids = resolve_tiled_env_indices(num_envs, self.cfg.tiled_cam_num, self.cfg.tiled_cam_env_indices) + env_ids = resolve_tiled_env_indices( + num_envs, + self.cfg.tiled_cam_num, + self.cfg.tiled_cam_env_indices, + max_tiles=VISUALIZER_TILED_CAMERA_MAX_TILES, + sample_from=self._resolved_visible_env_ids, + ) self._camera_env_indices = env_ids if self.cfg.tiled_cam_prim_path is not None: logger.debug( @@ -423,6 +437,7 @@ def _update_owned_camera_poses(self) -> None: eyes, targets = apply_camera_target_positions( self._camera_sensor, target_positions, self.cfg.tiled_cam_eye, self._camera_env_indices ) + self._set_generated_usd_camera_poses(eyes, targets) self._log_camera_pose_debug(eyes, targets) def _log_camera_pose_debug(self, eyes, targets) -> None: @@ -452,6 +467,7 @@ def _update_camera_image_panel(self, dt: float) -> None: self._camera_update_count += 1 if self._camera_is_owned: self._update_owned_camera_poses() + self._sync_camera_pose_updates_to_kit() if not hasattr(self, "_logged_camera_panel_before_data"): logger.debug("[KitVisualizer] Fetching camera RGB tensor.") self._logged_camera_panel_before_data = True @@ -528,6 +544,23 @@ def _upload_camera_image_to_panel(self, image: np.ndarray | torch.Tensor) -> Non logger.debug("[KitVisualizer] Camera image upload complete.") self._logged_camera_panel_after_upload = True + def _sync_camera_pose_updates_to_kit(self) -> None: + """Flush generated camera pose writes before camera RGB is sampled.""" + try: + import omni.kit.app + + app = omni.kit.app.get_app() + if app is None or not app.is_running(): + return + settings = get_settings_manager() + play_flag = settings.get("/app/player/playSimulations") + settings.set_bool("/app/player/playSimulations", False) + app.update() + if play_flag is not None: + settings.set_bool("/app/player/playSimulations", bool(play_flag)) + except Exception as exc: + logger.debug("[KitVisualizer] Camera pose Kit sync skipped: %s", exc) + def _refresh_controlled_camera_path(self) -> None: """Cache :attr:`_controlled_camera_path` from the active viewport (or default persp).""" if self._viewport_api is not None: @@ -586,6 +619,48 @@ def _set_viewport_camera(self, position: tuple[float, float, float], target: tup camera_state.set_position_world(Gf.Vec3d(float(position[0]), float(position[1]), float(position[2])), True) camera_state.set_target_world(Gf.Vec3d(float(target[0]), float(target[1]), float(target[2])), True) + def _set_generated_usd_camera_poses(self, eyes: torch.Tensor, targets: torch.Tensor) -> None: + """Author generated camera poses directly on USD camera prims for Kit/Fabric visibility.""" + # TODO: Remove this USD-side pose path once Fabric-backed camera transforms propagate reliably to Kit. + for local_idx, env_id in enumerate(self._camera_env_indices): + if local_idx >= eyes.shape[0]: + break + camera_path = ( + self._generated_camera_prim_paths[env_id] + if 0 <= env_id < len(self._generated_camera_prim_paths) + else f"/World/envs/env_{env_id}/VisualizerCamera" + ) + self._set_usd_camera_pose(camera_path, eyes[local_idx], targets[local_idx]) + + def _set_usd_camera_pose(self, camera_path: str, position, target) -> None: + """Apply eye/target camera pose directly to a USD camera prim.""" + # TODO: Remove this USD-side pose path once Fabric-backed camera transforms propagate reliably to Kit. + usd_stage = self._scene_data_provider.usd_stage if self._scene_data_provider else None + if usd_stage is None: + return + + camera = UsdGeom.Camera.Define(usd_stage, camera_path) + camera_xform = UsdGeom.Xformable(camera.GetPrim()) + camera_xform.ClearXformOpOrder() + # Generated visualizer cameras live under env prims, but eyes/targets are world-space. + # Reset the xform stack so Kit/Fabric sees the authored pose as a world pose. + camera_xform.SetResetXformStack(True) + + eye = torch.as_tensor(position, dtype=torch.float32, device="cpu").reshape(1, 3) + lookat = torch.as_tensor(target, dtype=torch.float32, device="cpu").reshape(1, 3) + up_axis = UsdGeom.GetStageUpAxis(usd_stage) + rotation_matrix = create_rotation_matrix_from_view(eye, lookat, up_axis=up_axis, device="cpu") + if torch.isnan(rotation_matrix).any(): + raise ValueError("[KitVisualizer] Cannot set camera pose because eye and lookat are degenerate.") + quat_xyzw = quat_from_matrix(rotation_matrix)[0] + quat_gf = Gf.Quatd( + float(quat_xyzw[3]), + Gf.Vec3d(float(quat_xyzw[0]), float(quat_xyzw[1]), float(quat_xyzw[2])), + ) + + camera_xform.AddTranslateOp().Set(Gf.Vec3d(float(eye[0, 0]), float(eye[0, 1]), float(eye[0, 2]))) + camera_xform.AddOrientOp(UsdGeom.XformOp.PrecisionDouble).Set(quat_gf) + def _apply_cfg_camera_pose_if_configured(self) -> None: """Apply configured camera pose from eye/lookat.""" self._set_viewport_camera(self.cfg.eye, self.cfg.lookat) diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py index 80a40bb603ad..06116ab5970c 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py @@ -8,6 +8,7 @@ from __future__ import annotations import logging +import math import os from typing import TYPE_CHECKING @@ -17,6 +18,7 @@ from pyglet.math import Vec3 as PygletVec3 from isaaclab.envs.utils.camera_view import ( + VISUALIZER_TILED_CAMERA_MAX_TILES, apply_camera_target_positions, camera_rgb_batch, compute_tile_resolution, @@ -264,9 +266,50 @@ def _render_left_panel(self): imgui.end() if getattr(self, "_image_logger", None) is not None: + self._prime_image_logger_window_layout() self._image_logger.draw() return + def _prime_image_logger_window_layout(self) -> None: + """Make first-open image windows use the available viewer space. + + Newton's ImageLogger initializes windows from a fixed per-tile size, so + small tiled batches otherwise open as small windows. Prime the next + ImGui window size once, then let users move/resize it normally. + """ + image_logger = getattr(self, "_image_logger", None) + if image_logger is None: + return + selected = getattr(image_logger, "_selected", None) + if selected is None: + return + entry = getattr(image_logger, "_images", {}).get(selected) + if entry is None or getattr(entry, "window_initialized", False): + return + + imgui = self.ui.imgui + viewport = imgui.get_main_viewport() + sidebar_width = float(getattr(image_logger, "_sidebar_width_px", 300.0)) + margin = 20.0 + available_w = max(320.0, viewport.work_size.x - sidebar_width - 2.0 * margin) + available_h = max(240.0, viewport.work_size.y - 2.0 * margin) + + n_tiles = max(1, int(getattr(entry, "n", 1))) + tile_aspect = float(getattr(entry, "tile_aspect", 1.0)) + cols = max(1, math.ceil(math.sqrt(n_tiles))) + rows = math.ceil(n_tiles / cols) + grid_aspect = (rows * tile_aspect) / cols + title_and_padding_h = 40.0 + + window_w = available_w + window_h = min(available_h, max(240.0, window_w * grid_aspect + title_and_padding_h)) + pos_x = sidebar_width + margin + pos_y = margin + + imgui.set_next_window_pos(imgui.ImVec2(float(pos_x), float(pos_y)), imgui.Cond_.once) + imgui.set_next_window_size(imgui.ImVec2(float(window_w), float(window_h)), imgui.Cond_.once) + entry.window_initialized = True + class NewtonVisualizer(BaseVisualizer): """Newton OpenGL visualizer for Isaac Lab.""" @@ -486,7 +529,13 @@ def _setup_camera_sensor_view(self, num_envs: int) -> None: """Resolve or create the camera sensor used by non-interactive image views.""" if not self._uses_camera_sensor_view(): return - env_ids = resolve_tiled_env_indices(num_envs, self.cfg.tiled_cam_num, self.cfg.tiled_cam_env_indices) + env_ids = resolve_tiled_env_indices( + num_envs, + self.cfg.tiled_cam_num, + self.cfg.tiled_cam_env_indices, + max_tiles=VISUALIZER_TILED_CAMERA_MAX_TILES, + sample_from=self._resolved_visible_env_ids, + ) self._camera_env_indices = env_ids if self.cfg.tiled_cam_prim_path is not None: logger.debug( @@ -576,7 +625,7 @@ def _log_camera_sensor_image(self) -> None: rgb_nonzero, ) self._logged_camera_stats = True - self._viewer.log_image("IsaacLab Camera", wp.from_torch(rgb)) + self._viewer.log_image("Visualizer Tiled Camera", wp.from_torch(rgb)) def _apply_camera_pose(self, pose: tuple[tuple[float, float, float], tuple[float, float, float]]) -> None: """Apply camera eye/target pose to the Newton viewer. From 3ea2d7453ae8bb5b449fcd9edd44bb1468b78c40 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Wed, 20 May 2026 00:43:51 +0000 Subject: [PATCH 21/31] lint --- source/isaaclab/isaaclab/envs/utils/camera_view.py | 8 ++++++-- source/isaaclab/isaaclab/visualizers/visualizer_cfg.py | 2 +- .../motion_planners/curobo/curobo_planner_cfg.py | 2 +- .../isaaclab_newton/test/sensors/test_contact_sensor.py | 2 +- .../changelog.d/mtrepte-update-debug-viz.rst | 2 ++ .../isaaclab_visualizers/kit/kit_visualizer.py | 3 ++- 6 files changed, 13 insertions(+), 6 deletions(-) diff --git a/source/isaaclab/isaaclab/envs/utils/camera_view.py b/source/isaaclab/isaaclab/envs/utils/camera_view.py index 21a68f3f4d14..40c3a86a35c5 100644 --- a/source/isaaclab/isaaclab/envs/utils/camera_view.py +++ b/source/isaaclab/isaaclab/envs/utils/camera_view.py @@ -48,7 +48,9 @@ def resolve_tiled_env_indices( if max_tiles is not None: max_count = min(max_count, max(1, int(max_tiles))) return [idx for idx in env_indices if 0 <= int(idx) < num_envs][:max_count] - candidates = [idx for idx in (sample_from if sample_from is not None else range(num_envs)) if 0 <= int(idx) < num_envs] + candidates = [ + idx for idx in (sample_from if sample_from is not None else range(num_envs)) if 0 <= int(idx) < num_envs + ] if not candidates: return [] max_count = min(max(1, int(tiled_cam_num)), len(candidates)) @@ -231,7 +233,9 @@ def _scene_articulation_positions(scene: Any, prim_path_template: str, env_indic return None -def prim_world_positions(stage: Any, prim_path_template: str, env_indices: list[int], scene: Any | None = None) -> torch.Tensor: +def prim_world_positions( + stage: Any, prim_path_template: str, env_indices: list[int], scene: Any | None = None +) -> torch.Tensor: """Return world-space translations for concrete prim paths resolved from env ids. Uses ``FrameView`` first so PhysX/Fabric-backed transforms are current; falls diff --git a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py index b1dcd50ddda1..659c97c6b248 100644 --- a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py +++ b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py @@ -51,7 +51,7 @@ class VisualizerCfg: tiled_cam_env_indices: list[int] | None = None """Env ids to show in tiled camera view; capped at 100 entries. - + If ``None``, envs are randomly sampled from all visible envs. """ diff --git a/source/isaaclab_mimic/isaaclab_mimic/motion_planners/curobo/curobo_planner_cfg.py b/source/isaaclab_mimic/isaaclab_mimic/motion_planners/curobo/curobo_planner_cfg.py index 0150052440db..caa29d45da04 100644 --- a/source/isaaclab_mimic/isaaclab_mimic/motion_planners/curobo/curobo_planner_cfg.py +++ b/source/isaaclab_mimic/isaaclab_mimic/motion_planners/curobo/curobo_planner_cfg.py @@ -12,8 +12,8 @@ from curobo.geom.types import WorldConfig from curobo.util_file import get_robot_configs_path, get_world_configs_path, join_path, load_yaml -from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR, retrieve_file_path from isaaclab.utils import configclass +from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR, retrieve_file_path @configclass diff --git a/source/isaaclab_newton/test/sensors/test_contact_sensor.py b/source/isaaclab_newton/test/sensors/test_contact_sensor.py index a84cf2c4ba2a..9149769da87b 100644 --- a/source/isaaclab_newton/test/sensors/test_contact_sensor.py +++ b/source/isaaclab_newton/test/sensors/test_contact_sensor.py @@ -27,6 +27,7 @@ import pytest import torch from isaaclab_newton.sensors.contact_sensor import ContactSensorCfg as NewtonContactSensorCfg +from isaaclab_newton.sensors.contact_sensor.contact_sensor import _broadcast_metadata_kind, _flatten_metadata from physics.physics_test_utils import ( COLLISION_PIPELINES, STABLE_SHAPES, @@ -48,7 +49,6 @@ from isaaclab.utils.configclass import configclass from isaaclab_assets.robots.allegro import ALLEGRO_HAND_CFG -from isaaclab_newton.sensors.contact_sensor.contact_sensor import _broadcast_metadata_kind, _flatten_metadata ## # Scene Configuration diff --git a/source/isaaclab_visualizers/changelog.d/mtrepte-update-debug-viz.rst b/source/isaaclab_visualizers/changelog.d/mtrepte-update-debug-viz.rst index c26216180202..f2e45fe04edd 100644 --- a/source/isaaclab_visualizers/changelog.d/mtrepte-update-debug-viz.rst +++ b/source/isaaclab_visualizers/changelog.d/mtrepte-update-debug-viz.rst @@ -3,3 +3,5 @@ Changed * Changed Rerun and Viser visualizers to avoid opening browser tabs by default and to show browser URLs in the startup logs instead. * Changed visualizer initialization tables to debug-level logging to reduce default startup log noise. +* Added non-interactive tiled camera image views for Kit and Newton visualizers, with generated per-env cameras or existing Camera sensor support. +* Added clearer Kit visualizer errors when tiled camera views are enabled without camera rendering support. diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py index 080836a019cf..6abc478bea6e 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py @@ -13,8 +13,10 @@ import numpy as np import torch + from pxr import Gf, Usd, UsdGeom, Vt +from isaaclab.app.settings_manager import get_settings_manager from isaaclab.envs.utils.camera_view import ( VISUALIZER_TILED_CAMERA_MAX_TILES, apply_camera_target_positions, @@ -27,7 +29,6 @@ remove_generated_prims, resolve_tiled_env_indices, ) -from isaaclab.app.settings_manager import get_settings_manager from isaaclab.utils.math import create_rotation_matrix_from_view, quat_from_matrix from isaaclab.visualizers.base_visualizer import BaseVisualizer From eda4ab0ff200f3478d4aa5b0c48cc80422281c05 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Wed, 20 May 2026 01:10:58 +0000 Subject: [PATCH 22/31] prep --- .../isaaclab/isaaclab/envs/direct_marl_env.py | 2 +- .../isaaclab/isaaclab/envs/direct_rl_env.py | 2 +- .../isaaclab/envs/leapp_deployment_env.py | 2 +- .../isaaclab/envs/manager_based_env.py | 2 +- .../isaaclab/envs/utils/camera_view.py | 59 --------- .../markers/visualization_markers_cfg.py | 2 +- .../isaaclab/physics/physics_manager_cfg.py | 2 +- .../isaaclab/scene/interactive_scene_cfg.py | 2 +- .../isaaclab/sim/simulation_context.py | 2 - .../isaaclab/visualizers/visualizer_cfg.py | 4 +- .../test_simulation_context_visualizers.py | 115 +----------------- .../curobo/curobo_planner_cfg.py | 2 +- .../physics/newton_collision_cfg.py | 2 +- .../physics/newton_manager_cfg.py | 2 +- .../kit/kit_visualizer.py | 67 ---------- .../newton/newton_visualizer.py | 47 ------- 16 files changed, 13 insertions(+), 301 deletions(-) diff --git a/source/isaaclab/isaaclab/envs/direct_marl_env.py b/source/isaaclab/isaaclab/envs/direct_marl_env.py index 4c57a1b7c357..2119fbe4d632 100644 --- a/source/isaaclab/isaaclab/envs/direct_marl_env.py +++ b/source/isaaclab/isaaclab/envs/direct_marl_env.py @@ -27,7 +27,7 @@ from isaaclab.scene import InteractiveScene from isaaclab.sim import SimulationContext from isaaclab.sim.utils.stage import use_stage -from isaaclab.utils import resolve_cfg_presets +from isaaclab.utils.configclass import resolve_cfg_presets from isaaclab.utils.noise import NoiseModel from isaaclab.utils.seed import configure_seed from isaaclab.utils.timer import Timer diff --git a/source/isaaclab/isaaclab/envs/direct_rl_env.py b/source/isaaclab/isaaclab/envs/direct_rl_env.py index 22ab4cfff232..7e931c8eabf9 100644 --- a/source/isaaclab/isaaclab/envs/direct_rl_env.py +++ b/source/isaaclab/isaaclab/envs/direct_rl_env.py @@ -23,7 +23,7 @@ from isaaclab.scene import InteractiveScene from isaaclab.sim import SimulationContext from isaaclab.sim.utils.stage import use_stage -from isaaclab.utils import resolve_cfg_presets +from isaaclab.utils.configclass import resolve_cfg_presets from isaaclab.utils.noise import NoiseModel from isaaclab.utils.seed import configure_seed from isaaclab.utils.timer import Timer diff --git a/source/isaaclab/isaaclab/envs/leapp_deployment_env.py b/source/isaaclab/isaaclab/envs/leapp_deployment_env.py index 6fdf231da86c..a2ad5b897647 100644 --- a/source/isaaclab/isaaclab/envs/leapp_deployment_env.py +++ b/source/isaaclab/isaaclab/envs/leapp_deployment_env.py @@ -31,7 +31,7 @@ from isaaclab.scene import InteractiveScene from isaaclab.sim import SimulationContext from isaaclab.sim.utils.stage import use_stage -from isaaclab.utils import resolve_cfg_presets +from isaaclab.utils.configclass import resolve_cfg_presets from .ui import ViewportCameraController diff --git a/source/isaaclab/isaaclab/envs/manager_based_env.py b/source/isaaclab/isaaclab/envs/manager_based_env.py index 74877a53e82b..95011738aed8 100644 --- a/source/isaaclab/isaaclab/envs/manager_based_env.py +++ b/source/isaaclab/isaaclab/envs/manager_based_env.py @@ -18,7 +18,7 @@ from isaaclab.sim import SimulationContext from isaaclab.sim.utils.stage import use_stage from isaaclab.ui.widgets import ManagerLiveVisualizer -from isaaclab.utils import resolve_cfg_presets +from isaaclab.utils.configclass import resolve_cfg_presets from isaaclab.utils.seed import configure_seed from isaaclab.utils.timer import Timer from isaaclab.utils.version import has_kit diff --git a/source/isaaclab/isaaclab/envs/utils/camera_view.py b/source/isaaclab/isaaclab/envs/utils/camera_view.py index 40c3a86a35c5..bb57f0c8c4c3 100644 --- a/source/isaaclab/isaaclab/envs/utils/camera_view.py +++ b/source/isaaclab/isaaclab/envs/utils/camera_view.py @@ -9,10 +9,8 @@ import math import random -from dataclasses import dataclass from typing import Any -import numpy as np import torch import warp as wp @@ -23,16 +21,6 @@ VISUALIZER_TILED_CAMERA_MAX_TILES = 100 -@dataclass -class ResolvedCameraView: - """Camera sensor plus selected sensor indices.""" - - camera: Camera - env_indices: list[int] - owns_camera: bool = False - generated_prim_paths: list[str] | None = None - - def resolve_tiled_env_indices( num_envs: int, tiled_cam_num: int, @@ -169,22 +157,6 @@ def camera_rgb_batch(camera: Camera, env_indices: list[int]) -> torch.Tensor: return rgb -def compose_rgb_grid(rgb_batch: torch.Tensor) -> np.ndarray: - """Compose an RGB batch into a near-square uint8 image grid for Kit UI display.""" - rgb_np = rgb_batch.detach().contiguous().cpu().numpy() - if rgb_np.ndim == 3: - return rgb_np[..., :3] - n, h, w, _ = rgb_np.shape - cols = max(1, math.ceil(math.sqrt(n))) - rows = math.ceil(n / cols) - pad = rows * cols - n - if pad > 0: - rgb_np = np.concatenate([rgb_np[..., :3], np.zeros((pad, h, w, 3), dtype=rgb_np.dtype)], axis=0) - else: - rgb_np = rgb_np[..., :3] - return rgb_np.reshape(rows, cols, h, w, 3).transpose(0, 2, 1, 3, 4).reshape(rows * h, cols * w, 3) - - def compose_rgb_grid_tensor(rgb_batch: torch.Tensor) -> torch.Tensor: """Compose an RGB batch into a near-square uint8 image grid without leaving its device.""" if rgb_batch.ndim == 3: @@ -302,34 +274,3 @@ def apply_camera_target_positions( camera.set_world_poses_from_view(eyes, targets, env_ids=env_ids) camera._update_poses(None) return eyes.detach().cpu(), targets.detach().cpu() - - -def apply_debug_top_down_grid_camera_poses(camera: Camera, env_indices: list[int], spacing: float = 20.0) -> None: - """Temporary debug pose: place cameras on a grid at z=10 looking straight down.""" - device = camera.device - count = len(env_indices) - cols = max(1, math.ceil(math.sqrt(max(1, count)))) - eyes = [] - targets = [] - for i, _env_id in enumerate(env_indices): - row, col = divmod(i, cols) - x = float(col) * spacing - y = float(row) * spacing - eyes.append((x, y, 10.0)) - targets.append((x, y, 0.0)) - eye_tensor = torch.tensor(eyes, dtype=torch.float32, device=device) - target_tensor = torch.tensor(targets, dtype=torch.float32, device=device) - camera.set_world_poses_from_view(eye_tensor, target_tensor) - camera._update_poses(None) - - -def apply_debug_oblique_env_camera_poses(camera: Camera, origins: torch.Tensor, env_indices: list[int]) -> None: - """Temporary debug pose: place cameras near each env origin with an oblique view.""" - device = camera.device - origins = origins.to(device=device) - # Use a high, nearly top-down view for rough terrain. The small horizontal - # offset avoids the Z-up singularity from a perfectly vertical look vector. - eye_offset = torch.tensor((2.0, -2.0, 3.0), dtype=torch.float32, device=device).unsqueeze(0) - lookat_offset = torch.tensor((0.0, 0.0, 0.0), dtype=torch.float32, device=device).unsqueeze(0) - camera.set_world_poses_from_view(origins + eye_offset, origins + lookat_offset) - camera._update_poses(None) diff --git a/source/isaaclab/isaaclab/markers/visualization_markers_cfg.py b/source/isaaclab/isaaclab/markers/visualization_markers_cfg.py index 933be2d3b53f..b7cc2002ccf0 100644 --- a/source/isaaclab/isaaclab/markers/visualization_markers_cfg.py +++ b/source/isaaclab/isaaclab/markers/visualization_markers_cfg.py @@ -10,7 +10,7 @@ from dataclasses import MISSING from isaaclab.sim.spawners import SpawnerCfg -from isaaclab.utils import configclass +from isaaclab.utils.configclass import configclass @configclass diff --git a/source/isaaclab/isaaclab/physics/physics_manager_cfg.py b/source/isaaclab/isaaclab/physics/physics_manager_cfg.py index 0ff2348b591a..d8e68f0b41a2 100644 --- a/source/isaaclab/isaaclab/physics/physics_manager_cfg.py +++ b/source/isaaclab/isaaclab/physics/physics_manager_cfg.py @@ -10,7 +10,7 @@ from dataclasses import MISSING from typing import TYPE_CHECKING, Any -from isaaclab.utils import configclass +from isaaclab.utils.configclass import configclass if TYPE_CHECKING: from .physics_manager import PhysicsManager diff --git a/source/isaaclab/isaaclab/scene/interactive_scene_cfg.py b/source/isaaclab/isaaclab/scene/interactive_scene_cfg.py index 7a38edb17c99..034307017219 100644 --- a/source/isaaclab/isaaclab/scene/interactive_scene_cfg.py +++ b/source/isaaclab/isaaclab/scene/interactive_scene_cfg.py @@ -5,7 +5,7 @@ from dataclasses import MISSING -from isaaclab.utils import configclass +from isaaclab.utils.configclass import configclass @configclass diff --git a/source/isaaclab/isaaclab/sim/simulation_context.py b/source/isaaclab/isaaclab/sim/simulation_context.py index 4fdd11daaefe..e035d3f9e94a 100644 --- a/source/isaaclab/isaaclab/sim/simulation_context.py +++ b/source/isaaclab/isaaclab/sim/simulation_context.py @@ -177,8 +177,6 @@ def __init__(self, cfg: SimulationCfg | None = None): self._scene_data_provider = SceneDataProvider(self.physics_manager.get_scene_data_backend()) self._visualizers: list[BaseVisualizer] = [] self._scene_data_requirements = SceneDataRequirement() - # TODO: move to SDP? - self._interactive_scene = None # Clone plan published by InteractiveScene after cloning. Providers (e.g. the # Newton visualizer model rebuilder on a PhysX backend) consume this to derive # their own backend args. None until :meth:`InteractiveScene.clone_environments` runs. diff --git a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py index 659c97c6b248..a54cd1ca6edf 100644 --- a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py +++ b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py @@ -40,10 +40,10 @@ class VisualizerCfg: lookat: tuple[float, float, float] = (0.0, 0.0, 0.0) """Interactive visualizer camera look-at target in world coordinates.""" - focal_length: float = 15.0 + focal_length: float = 12.0 """Camera focal length in millimeters for visualizer camera views.""" - tiled_cam_view: bool = True + tiled_cam_view: bool = False """Enable a non-interactive tiled camera image view.""" tiled_cam_num: int = 57 diff --git a/source/isaaclab/test/sim/test_simulation_context_visualizers.py b/source/isaaclab/test/sim/test_simulation_context_visualizers.py index d2f1a8029a48..7c2b0a7913da 100644 --- a/source/isaaclab/test/sim/test_simulation_context_visualizers.py +++ b/source/isaaclab/test/sim/test_simulation_context_visualizers.py @@ -341,54 +341,6 @@ def share_url(self) -> str | None: assert captured["set_world_offsets"] == (0.0, 0.0, 0.0) -def test_viser_visualizer_open_browser_is_opt_in(monkeypatch: pytest.MonkeyPatch): - opened_urls = [] - - class _FakeNewtonViewerViser: - def __init__( - self, - *, - port: int, - bind_address: str, - label: str | None, - verbose: bool, - share: bool, - record_to_viser: str | None, - metadata: dict | None = None, - ): - pass - - def set_model(self, model: Any) -> None: - pass - - def set_visible_worlds(self, worlds) -> None: - pass - - def set_world_offsets(self, spacing) -> None: - pass - - @property - def share_url(self) -> str | None: - return None - - monkeypatch.setattr(viser_visualizer, "NewtonViewerViser", _FakeNewtonViewerViser) - monkeypatch.setattr(viser_visualizer, "_open_viser_web_viewer", lambda url: opened_urls.append(url)) - monkeypatch.setattr( - viser_visualizer.ViserVisualizer, - "_resolve_initial_camera_pose", - lambda self: ((1.0, 2.0, 3.0), (0.0, 0.0, 0.0)), - ) - monkeypatch.setattr(viser_visualizer.ViserVisualizer, "_set_viser_camera_view", lambda self, pose: None) - - cfg = ViserVisualizerCfg(open_browser=True, display_address="robot.example.com") - visualizer = viser_visualizer.ViserVisualizer(cfg) - visualizer._model = "dummy-model" - visualizer._env_ids = None - visualizer._create_viewer(record_to_viser=None, metadata={"num_envs": 1}) - - assert opened_urls == [f"http://robot.example.com:{cfg.port}"] - - @pytest.mark.parametrize( ("cfg_max_visible_envs", "expected_visible"), [ @@ -497,71 +449,6 @@ def get_num_envs() -> int: assert captured["set_world_offsets"] == (0.0, 0.0, 0.0) -def test_rerun_visualizer_open_browser_is_opt_in(monkeypatch: pytest.MonkeyPatch): - opened = [] - - class _FakeNewtonViewerRerun: - def __init__( - self, - *, - app_id: str, - address: str | None, - serve_web_viewer: bool, - web_port: int, - grpc_port: int, - keep_historical_data: bool, - keep_scalar_history: bool, - record_to_rrd: str | None, - open_browser: bool, - ): - pass - - def set_model(self, model: Any) -> None: - pass - - def set_visible_worlds(self, worlds) -> None: - pass - - def set_world_offsets(self, spacing) -> None: - pass - - def close(self) -> None: - pass - - class _DummyRerunSceneDataProvider: - num_envs = 1 - - def get_metadata(self) -> dict: - return {"num_envs": 1} - - def get_newton_model(self): - return "dummy-model" - - def get_newton_state(self): - return {"ok": True} - - def get_camera_transforms(self): - return {} - - monkeypatch.setattr(rerun_visualizer, "NewtonViewerRerun", _FakeNewtonViewerRerun) - monkeypatch.setattr( - rerun_visualizer, "_ensure_rerun_server", lambda **kwargs: ("rerun+http://127.0.0.1:9876/proxy", False) - ) - monkeypatch.setattr(rerun_visualizer, "_open_rerun_web_viewer", lambda *args: opened.append(args)) - monkeypatch.setattr( - rerun_visualizer.RerunVisualizer, - "_resolve_initial_camera_pose", - lambda self: ((1.0, 2.0, 3.0), (0.0, 0.0, 0.0)), - ) - monkeypatch.setattr(rerun_visualizer.RerunVisualizer, "_apply_camera_pose", lambda self, pose: None) - - cfg = RerunVisualizerCfg(open_browser=True) - visualizer = rerun_visualizer.RerunVisualizer(cfg) - visualizer.initialize(cast(Any, _DummyRerunSceneDataProvider())) - - assert opened == [("127.0.0.1", cfg.web_port, "rerun+http://127.0.0.1:9876/proxy")] - - def test_rerun_visualizer_marker_failure_still_ends_frame(monkeypatch: pytest.MonkeyPatch): class _FakeRerunViewer: def __init__(self): @@ -600,7 +487,7 @@ def get_model(): return "dummy-model" @staticmethod - def get_state(): + def get_state(scene_data_provider=None): return {"ok": True} @staticmethod diff --git a/source/isaaclab_mimic/isaaclab_mimic/motion_planners/curobo/curobo_planner_cfg.py b/source/isaaclab_mimic/isaaclab_mimic/motion_planners/curobo/curobo_planner_cfg.py index caa29d45da04..b3b3f7cce827 100644 --- a/source/isaaclab_mimic/isaaclab_mimic/motion_planners/curobo/curobo_planner_cfg.py +++ b/source/isaaclab_mimic/isaaclab_mimic/motion_planners/curobo/curobo_planner_cfg.py @@ -12,8 +12,8 @@ from curobo.geom.types import WorldConfig from curobo.util_file import get_robot_configs_path, get_world_configs_path, join_path, load_yaml -from isaaclab.utils import configclass from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR, retrieve_file_path +from isaaclab.utils.configclass import configclass @configclass diff --git a/source/isaaclab_newton/isaaclab_newton/physics/newton_collision_cfg.py b/source/isaaclab_newton/isaaclab_newton/physics/newton_collision_cfg.py index c8b0db0b3f4a..86014d13a885 100644 --- a/source/isaaclab_newton/isaaclab_newton/physics/newton_collision_cfg.py +++ b/source/isaaclab_newton/isaaclab_newton/physics/newton_collision_cfg.py @@ -9,7 +9,7 @@ from typing import Any, Literal -from isaaclab.utils import configclass +from isaaclab.utils.configclass import configclass @configclass diff --git a/source/isaaclab_newton/isaaclab_newton/physics/newton_manager_cfg.py b/source/isaaclab_newton/isaaclab_newton/physics/newton_manager_cfg.py index 85e4061ab911..6ff646aff57b 100644 --- a/source/isaaclab_newton/isaaclab_newton/physics/newton_manager_cfg.py +++ b/source/isaaclab_newton/isaaclab_newton/physics/newton_manager_cfg.py @@ -10,7 +10,7 @@ from typing import TYPE_CHECKING from isaaclab.physics import PhysicsCfg -from isaaclab.utils import configclass +from isaaclab.utils.configclass import configclass from .newton_collision_cfg import NewtonCollisionPipelineCfg diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py index 6abc478bea6e..143e34f6c085 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py @@ -79,10 +79,6 @@ def __init__(self, cfg: KitVisualizerCfg): self._camera_image_provider = None self._camera_image_window = None self._camera_gpu_upload_tensor = None - self._camera_update_count = 0 - self._camera_last_checksum: int | None = None - self._camera_pose_update_count = 0 - self._camera_last_pose_debug: tuple[float, ...] | None = None self._warned_gpu_upload_failure = False # ---- Lifecycle ------------------------------------------------------------------------ @@ -439,62 +435,17 @@ def _update_owned_camera_poses(self) -> None: self._camera_sensor, target_positions, self.cfg.tiled_cam_eye, self._camera_env_indices ) self._set_generated_usd_camera_poses(eyes, targets) - self._log_camera_pose_debug(eyes, targets) - - def _log_camera_pose_debug(self, eyes, targets) -> None: - """Log generated camera pose changes for follow-mode debugging.""" - self._camera_pose_update_count += 1 - first_eye = tuple(float(v) for v in eyes[0]) - first_target = tuple(float(v) for v in targets[0]) - pose_key = (*first_eye, *first_target) - changed = self._camera_last_pose_debug is None or pose_key != self._camera_last_pose_debug - if self._camera_pose_update_count <= 5 or self._camera_pose_update_count % 30 == 0: - logger.debug( - "[KitVisualizer] Camera pose update #%s first_eye=%s first_target=%s changed=%s", - self._camera_pose_update_count, - first_eye, - first_target, - changed, - ) - self._camera_last_pose_debug = pose_key def _update_camera_image_panel(self, dt: float) -> None: """Refresh the non-interactive Kit image panel from camera RGB output.""" if self._camera_sensor is None or self._camera_image_provider is None: return - if not hasattr(self, "_logged_camera_panel_first_update"): - logger.debug("[KitVisualizer] Updating camera image panel for the first time.") - self._logged_camera_panel_first_update = True - self._camera_update_count += 1 if self._camera_is_owned: self._update_owned_camera_poses() self._sync_camera_pose_updates_to_kit() - if not hasattr(self, "_logged_camera_panel_before_data"): - logger.debug("[KitVisualizer] Fetching camera RGB tensor.") - self._logged_camera_panel_before_data = True if self._camera_is_owned: self._camera_sensor.update(dt=dt, force_recompute=True) rgb = camera_rgb_batch(self._camera_sensor, self._camera_sensor_indices) - if self._camera_update_count <= 5 or self._camera_update_count % 30 == 0: - checksum = int(rgb.to(dtype=torch.int64).sum().item()) - logger.debug( - "[KitVisualizer] Camera update #%s checksum=%s changed=%s", - self._camera_update_count, - checksum, - self._camera_last_checksum is None or checksum != self._camera_last_checksum, - ) - self._camera_last_checksum = checksum - if not hasattr(self, "_logged_camera_panel_after_data"): - tile_means = rgb[..., :3].float().mean(dim=(1, 2)).detach().cpu().numpy() - logger.debug( - "[KitVisualizer] Camera RGB tensor ready: shape=%s dtype=%s min=%s max=%s", - tuple(rgb.shape), - rgb.dtype, - int(rgb.min().item()), - int(rgb.max().item()), - ) - logger.debug("[KitVisualizer] First tile RGB means: %s", tile_means[: min(4, len(tile_means))].tolist()) - self._logged_camera_panel_after_data = True image = compose_rgb_grid_tensor(rgb) if self.cfg.tiled_cam_view else rgb[0].contiguous() self._upload_camera_image_to_panel(image) @@ -520,30 +471,12 @@ def _upload_camera_image_to_panel(self, image: np.ndarray | torch.Tensor) -> Non self._warned_gpu_upload_failure = True image = image.detach().contiguous().cpu().numpy() - if not hasattr(self, "_logged_camera_panel_image_stats"): - channel_mean = image[..., :3].reshape(-1, 3).mean(axis=0) - logger.debug( - "[KitVisualizer] Composed camera image stats: shape=%s min=%s max=%s mean_rgb=(%.2f, %.2f, %.2f)", - image.shape, - int(image.min()), - int(image.max()), - float(channel_mean[0]), - float(channel_mean[1]), - float(channel_mean[2]), - ) - self._logged_camera_panel_image_stats = True - if not hasattr(self, "_logged_camera_panel_before_upload"): - logger.debug("[KitVisualizer] Uploading camera image to UI provider: shape=%s", image.shape) - self._logged_camera_panel_before_upload = True image = image.astype("uint8", copy=False) if image.ndim == 3 and image.shape[2] == 3: alpha = np.full((*image.shape[:2], 1), 255, dtype=np.uint8) image = np.concatenate((image, alpha), axis=2) image = np.ascontiguousarray(image) self._camera_image_provider.set_bytes_data(image.flatten().data, [image.shape[1], image.shape[0]]) - if not hasattr(self, "_logged_camera_panel_after_upload"): - logger.debug("[KitVisualizer] Camera image upload complete.") - self._logged_camera_panel_after_upload = True def _sync_camera_pose_updates_to_kit(self) -> None: """Flush generated camera pose writes before camera RGB is sampled.""" diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py index 06116ab5970c..d4af3cd1f991 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py @@ -12,7 +12,6 @@ import os from typing import TYPE_CHECKING -import torch import warp as wp from newton.viewer import ViewerGL from pyglet.math import Vec3 as PygletVec3 @@ -337,11 +336,6 @@ def __init__(self, cfg: NewtonVisualizerCfg): self._camera_env_indices: list[int] = [] self._camera_is_owned = False self._generated_camera_prim_paths: list[str] = [] - self._logged_camera_stats = False - self._camera_update_count = 0 - self._camera_last_checksum: int | None = None - self._camera_pose_update_count = 0 - self._camera_last_pose_debug: tuple[float, ...] | None = None def initialize(self, scene_data_provider: SceneDataProvider) -> None: """Initialize viewer resources and bind scene data provider. @@ -574,24 +568,6 @@ def _update_owned_camera_poses(self) -> None: eyes, targets = apply_camera_target_positions( self._camera_sensor, target_positions, self.cfg.tiled_cam_eye, self._camera_env_indices ) - self._log_camera_pose_debug(eyes, targets) - - def _log_camera_pose_debug(self, eyes, targets) -> None: - """Log generated camera pose changes for follow-mode debugging.""" - self._camera_pose_update_count += 1 - first_eye = tuple(float(v) for v in eyes[0]) - first_target = tuple(float(v) for v in targets[0]) - pose_key = (*first_eye, *first_target) - changed = self._camera_last_pose_debug is None or pose_key != self._camera_last_pose_debug - if self._camera_pose_update_count <= 5 or self._camera_pose_update_count % 30 == 0: - logger.debug( - "[NewtonVisualizer] Camera pose update #%s first_eye=%s first_target=%s changed=%s", - self._camera_pose_update_count, - first_eye, - first_target, - changed, - ) - self._camera_last_pose_debug = pose_key def _log_camera_sensor_image(self) -> None: """Log the selected camera sensor RGB output into Newton's image panel.""" @@ -602,29 +578,6 @@ def _log_camera_sensor_image(self) -> None: if self._camera_is_owned: self._camera_sensor.update(dt=0.0, force_recompute=True) rgb = camera_rgb_batch(self._camera_sensor, self._camera_sensor_indices).contiguous() - self._camera_update_count += 1 - if self._camera_update_count <= 5 or self._camera_update_count % 30 == 0: - checksum = int(rgb.to(dtype=torch.int64).sum().item()) - logger.debug( - "[NewtonVisualizer] Camera update #%s checksum=%s changed=%s", - self._camera_update_count, - checksum, - self._camera_last_checksum is None or checksum != self._camera_last_checksum, - ) - self._camera_last_checksum = checksum - if not self._logged_camera_stats: - rgb_min = int(rgb.min().item()) - rgb_max = int(rgb.max().item()) - rgb_nonzero = int(torch.count_nonzero(rgb).item()) - logger.debug( - "[NewtonVisualizer] Camera image stats: shape=%s dtype=%s min=%s max=%s nonzero=%s", - tuple(rgb.shape), - rgb.dtype, - rgb_min, - rgb_max, - rgb_nonzero, - ) - self._logged_camera_stats = True self._viewer.log_image("Visualizer Tiled Camera", wp.from_torch(rgb)) def _apply_camera_pose(self, pose: tuple[tuple[float, float, float], tuple[float, float, float]]) -> None: From a15471631f55afb17f798976e199adbf6a5ccd88 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Wed, 20 May 2026 01:13:23 +0000 Subject: [PATCH 23/31] docs --- docs/source/features/visualization.rst | 62 +++++++++----------------- 1 file changed, 20 insertions(+), 42 deletions(-) diff --git a/docs/source/features/visualization.rst b/docs/source/features/visualization.rst index cc7f7ab3b327..238d52a6eff8 100644 --- a/docs/source/features/visualization.rst +++ b/docs/source/features/visualization.rst @@ -22,10 +22,10 @@ Isaac Lab supports four visualizer backends, each optimized for different use ca - Key Features * - **Omniverse** - High-fidelity, Isaac Sim integration - - Interactive viewport, USD, visualization markers, live plots, tiled camera image view + - USD, visualization markers, live plots, tiled camera image view * - **Newton** - Fast iteration - - Low overhead, visualization markers, interactive GL camera, tiled camera image view + - Low overhead, visualization markers, tiled camera image view * - **Rerun** - Remote viewing, replay - Webviewer, time scrubbing, recording export, visualization markers @@ -190,12 +190,14 @@ Also, there is a CLI arg ``--max_visible_envs`` that overrides ``VisualizerCfg.m Camera Pose Behavior ~~~~~~~~~~~~~~~~~~~~ -The default visualizer camera mode is interactive: ``tiled_cam_view=False`` with ``eye`` and ``lookat`` -controlling the viewer camera. Kit and Newton can also run non-interactive tiled camera image views. -If ``tiled_cam_view=True`` is set, that visualizer config uses a camera image view rather than the -default interactive camera; launch a second visualizer config if you want both. +The default visualizer camera mode is interactive, with ``eye`` and ``lookat`` specifying the initial pose. +Kit and Newton visualizers can also run non-interactive tiled camera image views. +If ``tiled_cam_view=True`` is set, an additional window is launched in the visualizer which shows +a non-interactive tiled camera image view. + Kit and Newton cap tiled camera views at 100 tiles. -Kit tiled camera views require launching with ``--enable_cameras``. + +Note, Kit tiled camera views require launching with ``--enable_cameras``. .. list-table:: Camera configuration modes :header-rows: 1 @@ -313,11 +315,10 @@ Omniverse Visualizer **Main Features:** - Native USD stage integration -- Interactive viewport camera by default - Live plots for monitoring training metrics - Full Isaac Sim rendering capabilities and tooling - Visualization markers for debugging (arrows, frames, object targets, etc.) -- Non-interactive tiled RGB camera views from Isaac Lab ``Camera`` sensors in a dockable image panel +- Tiled camera views which can track multiple robots **Core Configuration:** @@ -340,22 +341,6 @@ Omniverse Visualizer enable_live_plots=True, ) -Set ``tiled_cam_view=True`` on a Kit visualizer config to show camera sensor RGB in a dockable image -panel. This config is non-interactive; use another ``KitVisualizerCfg`` if you also want an interactive -viewport camera. - -.. code-block:: python - - tiled_kit_cfg = KitVisualizerCfg( - create_viewport=True, - viewport_name="Tiled Cameras", - tiled_cam_view=True, - tiled_cam_num=16, - tiled_cam_eye=(4.0, -4.0, 3.0), - tiled_cam_target_prim_path="/World/envs/*/Robot/base", - ) - - Newton Visualizer ~~~~~~~~~~~~~~~~~ @@ -366,8 +351,7 @@ Newton Visualizer - Adjustable update frequency for performance tuning - Some customizable rendering options (shadows, sky, wireframe) - Visualization markers (joints, contacts, springs, COM, debug markers) -- Interactive GL camera by default -- Non-interactive tiled RGB camera views via Newton ``Viewer.log_image`` from Isaac Lab ``Camera`` sensors +- Tiled camera views which can track multiple robots **Interactive Controls:** @@ -405,6 +389,15 @@ Newton Visualizer # Camera settings eye=(8.0, 8.0, 3.0), # Initial camera position (x, y, z) lookat=(0.0, 0.0, 0.0), # Camera look-at target + focal_length=12.0, # Camera focal length in millimeters + + # Tiled camera view settings + tiled_cam_view=True, # Enable non-interactive tiled camera image view + tiled_cam_num=57, # Number of generated camera tiles to display + tiled_cam_env_indices=None, # Optional explicit env ids to show in the tiled view + tiled_cam_prim_path=None, # Existing Camera sensor prim path, e.g. "/World/envs/*/Camera" + tiled_cam_eye=(4.0, -4.0, 3.0), # Eye offset for generated tiled cameras + tiled_cam_target_prim_path="/World/envs/*/Robot/base", # Prim that generated cameras follow/look at # Performance tuning update_frequency=1, # Update every N frames (1=every frame) @@ -426,21 +419,6 @@ Newton Visualizer light_color=(1.0, 1.0, 1.0), # Directional light color (RGB [0,1]) ) -Set ``tiled_cam_view=True`` on a Newton visualizer config to show camera sensor RGB in Newton's image -view. This config is non-interactive; use another ``NewtonVisualizerCfg`` if you also want an interactive -GL camera. - -.. code-block:: python - - tiled_newton_cfg = NewtonVisualizerCfg( - window_width=1280, - window_height=720, - tiled_cam_view=True, - tiled_cam_env_indices=[0, 1, 2, 3], - tiled_cam_eye=(4.0, -4.0, 3.0), - tiled_cam_target_prim_path="/World/envs/*/Robot/base", - ) - Rerun Visualizer ~~~~~~~~~~~~~~~~ From d98d4b6da7f4259310baea49f3ec6784d19d7897 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Wed, 20 May 2026 01:19:51 +0000 Subject: [PATCH 24/31] clean --- docs/source/features/visualization.rst | 8 +- .../physics/base_scene_data_provider.py | 79 ------------------- .../isaaclab/visualizers/visualizer_cfg.py | 2 +- .../test_simulation_context_visualizers.py | 63 --------------- .../sensors/contact_sensor/contact_sensor.py | 24 ------ .../test/sensors/test_contact_sensor.py | 7 -- .../newton/newton_visualizer.py | 18 ++--- 7 files changed, 14 insertions(+), 187 deletions(-) delete mode 100644 source/isaaclab/isaaclab/physics/base_scene_data_provider.py diff --git a/docs/source/features/visualization.rst b/docs/source/features/visualization.rst index 238d52a6eff8..7e1a807188f7 100644 --- a/docs/source/features/visualization.rst +++ b/docs/source/features/visualization.rst @@ -22,10 +22,10 @@ Isaac Lab supports four visualizer backends, each optimized for different use ca - Key Features * - **Omniverse** - High-fidelity, Isaac Sim integration - - USD, visualization markers, live plots, tiled camera image view + - USD, visualization markers, live plots, tiled camera panel * - **Newton** - Fast iteration - - Low overhead, visualization markers, tiled camera image view + - Low overhead, visualization markers, tiled camera panel * - **Rerun** - Remote viewing, replay - Webviewer, time scrubbing, recording export, visualization markers @@ -191,8 +191,8 @@ Camera Pose Behavior ~~~~~~~~~~~~~~~~~~~~ The default visualizer camera mode is interactive, with ``eye`` and ``lookat`` specifying the initial pose. -Kit and Newton visualizers can also run non-interactive tiled camera image views. -If ``tiled_cam_view=True`` is set, an additional window is launched in the visualizer which shows +Kit and Newton visualizers can also run additional tiled camera image panels. +If ``tiled_cam_view=True`` is set, another window is launched in the visualizer which shows a non-interactive tiled camera image view. Kit and Newton cap tiled camera views at 100 tiles. diff --git a/source/isaaclab/isaaclab/physics/base_scene_data_provider.py b/source/isaaclab/isaaclab/physics/base_scene_data_provider.py deleted file mode 100644 index 32337cca17b9..000000000000 --- a/source/isaaclab/isaaclab/physics/base_scene_data_provider.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). -# All rights reserved. -# -# SPDX-License-Identifier: BSD-3-Clause - -"""Scene data provider interface for visualizers and renderers.""" - -from __future__ import annotations - -from abc import ABC, abstractmethod -from typing import Any - - -class BaseSceneDataProvider(ABC): - """Backend-agnostic scene data provider interface.""" - - def set_interactive_scene(self, scene: Any) -> None: - """Attach the interactive scene so visualizers can discover scene-owned sensors.""" - self._interactive_scene = scene - - def get_interactive_scene(self) -> Any | None: - """Return the registered interactive scene, if available.""" - return getattr(self, "_interactive_scene", None) - - def get_camera_sensors(self) -> dict[str, Any]: - """Return Isaac Lab camera sensors keyed by scene sensor name.""" - scene = getattr(self, "_interactive_scene", None) - if scene is None: - return {} - try: - from isaaclab.sensors.camera import Camera - except ImportError: - return {} - return {name: sensor for name, sensor in scene.sensors.items() if isinstance(sensor, Camera)} - - @abstractmethod - def update(self) -> None: - """Refresh any cached scene data (full model/state).""" - raise NotImplementedError - - @abstractmethod - def get_newton_model(self) -> Any | None: - """Return Newton model handle when available.""" - raise NotImplementedError - - @abstractmethod - def get_newton_state(self) -> Any | None: - """Return Newton state handle when available (full state).""" - raise NotImplementedError - - @abstractmethod - def get_usd_stage(self) -> Any | None: - """Return USD stage handle when available.""" - raise NotImplementedError - - @abstractmethod - def get_metadata(self) -> dict[str, Any]: - """Return backend metadata (num_envs, gravity, etc.).""" - raise NotImplementedError - - @abstractmethod - def get_transforms(self) -> dict[str, Any] | None: - """Return body transforms, if supported.""" - raise NotImplementedError - - @abstractmethod - def get_velocities(self) -> dict[str, Any] | None: - """Return body velocities, if supported.""" - raise NotImplementedError - - @abstractmethod - def get_contacts(self) -> dict[str, Any] | None: - """Return contacts, if supported.""" - raise NotImplementedError - - @abstractmethod - def get_camera_transforms(self) -> dict[str, Any] | None: - """Return per-camera, per-env transforms, if supported.""" - raise NotImplementedError diff --git a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py index a54cd1ca6edf..9d09ae3228de 100644 --- a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py +++ b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py @@ -40,7 +40,7 @@ class VisualizerCfg: lookat: tuple[float, float, float] = (0.0, 0.0, 0.0) """Interactive visualizer camera look-at target in world coordinates.""" - focal_length: float = 12.0 + focal_length: float = 15.0 """Camera focal length in millimeters for visualizer camera views.""" tiled_cam_view: bool = False diff --git a/source/isaaclab/test/sim/test_simulation_context_visualizers.py b/source/isaaclab/test/sim/test_simulation_context_visualizers.py index 7c2b0a7913da..d7645ec6be2a 100644 --- a/source/isaaclab/test/sim/test_simulation_context_visualizers.py +++ b/source/isaaclab/test/sim/test_simulation_context_visualizers.py @@ -449,69 +449,6 @@ def get_num_envs() -> int: assert captured["set_world_offsets"] == (0.0, 0.0, 0.0) -def test_rerun_visualizer_marker_failure_still_ends_frame(monkeypatch: pytest.MonkeyPatch): - class _FakeRerunViewer: - def __init__(self): - self.calls = [] - - def is_paused(self): - return False - - def begin_frame(self, sim_time): - self.calls.append(("begin_frame", sim_time)) - - def log_state(self, state): - self.calls.append(("log_state", state)) - - def end_frame(self): - self.calls.append(("end_frame",)) - - class _DummyRerunSceneDataProvider: - def get_metadata(self) -> dict: - return {"num_envs": 4} - - def get_newton_state(self): - return {"ok": True} - - def get_camera_transforms(self): - return {} - - def _raise_marker_render(*args, **kwargs): - raise RuntimeError("marker render failed") - - monkeypatch.setattr(rerun_visualizer, "render_newton_visualization_markers", _raise_marker_render) - - class _FakeNewtonManager: - @staticmethod - def get_model(): - return "dummy-model" - - @staticmethod - def get_state(scene_data_provider=None): - return {"ok": True} - - @staticmethod - def get_num_envs() -> int: - return 4 - - import isaaclab_newton.physics as _np_mod - - monkeypatch.setattr(_np_mod, "NewtonManager", _FakeNewtonManager) - - visualizer = rerun_visualizer.RerunVisualizer(RerunVisualizerCfg()) - viewer = _FakeRerunViewer() - visualizer._is_initialized = True - visualizer._is_closed = False - visualizer._viewer = viewer - visualizer._scene_data_provider = _DummyRerunSceneDataProvider() - visualizer._resolved_visible_env_ids = None - - with pytest.raises(RuntimeError, match="marker render failed"): - visualizer.step(0.25) - - assert [call[0] for call in viewer.calls] == ["begin_frame", "log_state", "end_frame"] - - def test_kit_visualizer_default_camera_source_does_not_require_camera_prim(monkeypatch: pytest.MonkeyPatch): """Default ``--viz kit`` should work for envs without a camera prim.""" diff --git a/source/isaaclab_newton/isaaclab_newton/sensors/contact_sensor/contact_sensor.py b/source/isaaclab_newton/isaaclab_newton/sensors/contact_sensor/contact_sensor.py index b7c3fbc21a09..65d15de98750 100644 --- a/source/isaaclab_newton/isaaclab_newton/sensors/contact_sensor/contact_sensor.py +++ b/source/isaaclab_newton/isaaclab_newton/sensors/contact_sensor/contact_sensor.py @@ -40,30 +40,6 @@ logger = logging.getLogger(__name__) -def _flatten_metadata(values) -> list: - """Return Newton contact sensor metadata as a flat Python list.""" - if values is None: - return [] - if isinstance(values, wp.array): - values = values.numpy() - flat_values = np.asarray(values, dtype=object).reshape(-1).tolist() - if flat_values and isinstance(flat_values[0], list | tuple | np.ndarray): - return [ - value - for nested_values in flat_values - for value in np.asarray(nested_values, dtype=object).reshape(-1).tolist() - ] - return flat_values - - -def _broadcast_metadata_kind(kind_values, count: int) -> list: - """Broadcast scalar Newton metadata kinds to match per-object indices.""" - flat_values = _flatten_metadata(kind_values) - if len(flat_values) == 1 and count > 1: - return flat_values * count - return flat_values - - class ContactSensor(BaseContactSensor): """A contact reporting sensor. diff --git a/source/isaaclab_newton/test/sensors/test_contact_sensor.py b/source/isaaclab_newton/test/sensors/test_contact_sensor.py index 9149769da87b..230214e9588b 100644 --- a/source/isaaclab_newton/test/sensors/test_contact_sensor.py +++ b/source/isaaclab_newton/test/sensors/test_contact_sensor.py @@ -27,7 +27,6 @@ import pytest import torch from isaaclab_newton.sensors.contact_sensor import ContactSensorCfg as NewtonContactSensorCfg -from isaaclab_newton.sensors.contact_sensor.contact_sensor import _broadcast_metadata_kind, _flatten_metadata from physics.physics_test_utils import ( COLLISION_PIPELINES, STABLE_SHAPES, @@ -70,12 +69,6 @@ class ContactSensorTestSceneCfg(InteractiveSceneCfg): SIM_DT = 1.0 / 120.0 -def test_contact_sensor_metadata_helpers_broadcast_scalar_kind(): - """Newton exposes object kind as a scalar while indices are per object.""" - assert _flatten_metadata([[0, 1], [2]]) == [0, 1, 2] - assert _broadcast_metadata_kind("body", 3) == ["body", "body", "body"] - - # =================================================================== # Priority 1: Contact Detection Accuracy # =================================================================== diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py index d4af3cd1f991..68e2e63858ca 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py @@ -241,7 +241,7 @@ def _render_left_panel(self): # Newton's ImageLogger owns camera-output image windows. Since Isaac Lab overrides # ViewerGL's left panel, explicitly keep the logged-image selector and draw path. - if getattr(self, "_image_logger", None) is not None: + if self._image_logger is not None: self._image_logger.draw_controls() imgui.set_next_item_open(True, imgui.Cond_.appearing) @@ -264,7 +264,7 @@ def _render_left_panel(self): imgui.text("ESC - Exit") imgui.end() - if getattr(self, "_image_logger", None) is not None: + if self._image_logger is not None: self._prime_image_logger_window_layout() self._image_logger.draw() return @@ -276,25 +276,25 @@ def _prime_image_logger_window_layout(self) -> None: small tiled batches otherwise open as small windows. Prime the next ImGui window size once, then let users move/resize it normally. """ - image_logger = getattr(self, "_image_logger", None) + image_logger = self._image_logger if image_logger is None: return - selected = getattr(image_logger, "_selected", None) + selected = image_logger._selected if selected is None: return - entry = getattr(image_logger, "_images", {}).get(selected) - if entry is None or getattr(entry, "window_initialized", False): + entry = image_logger._images.get(selected) + if entry is None or entry.window_initialized: return imgui = self.ui.imgui viewport = imgui.get_main_viewport() - sidebar_width = float(getattr(image_logger, "_sidebar_width_px", 300.0)) + sidebar_width = float(image_logger._sidebar_width_px) margin = 20.0 available_w = max(320.0, viewport.work_size.x - sidebar_width - 2.0 * margin) available_h = max(240.0, viewport.work_size.y - 2.0 * margin) - n_tiles = max(1, int(getattr(entry, "n", 1))) - tile_aspect = float(getattr(entry, "tile_aspect", 1.0)) + n_tiles = max(1, int(entry.n)) + tile_aspect = float(entry.tile_aspect) cols = max(1, math.ceil(math.sqrt(n_tiles))) rows = math.ceil(n_tiles / cols) grid_aspect = (rows * tile_aspect) / cols From cd1ba9134df3a349a0fc9ef30db8da57689e7ac6 Mon Sep 17 00:00:00 2001 From: matthewtrepte Date: Tue, 19 May 2026 18:44:07 -0700 Subject: [PATCH 25/31] Minor Updates & Patches to Visualizers (#5610) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Contains a few minor updates and patches to visualizers. - Disable default browser launch to Rerun & Viser visualizers - Add explicit logging urls to Rerun & Viser which appear before simulation loop - Reduce visualizer num sim steps in unit tests - Unskip all visualizer cartpole integration tests - Add focal length to visualizer cfg, for flexibility, align default visualizer cams, and add more consistency to tests - Expand pausing checks for Kit & Rerun tests - Fix frozen robot bodies in newton visualizers with physx - Add a save visualizer test captures mode to visualizer cartpole unit test (disabled by default) - Re-enable visualization markers when no visualizer is launched - Fix Newton Visualizer visualization marker regression - Expose remote desktop config for Viser Visualizer - Align Viser Visualizer docs with other visualizers - Update docs to reflect the changes ## Type of change - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) - Documentation update ## Screenshots Please attach before and after screenshots of the change if applicable. ## Checklist - [ ] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [ ] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [ ] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --- docs/source/features/visualization.rst | 78 ++- .../overview/core-concepts/renderers.rst | 5 + .../changelog.d/mtrepte-update-debug-viz.rst | 9 + .../isaaclab/isaaclab/cli/commands/install.py | 2 + .../isaaclab/markers/visualization_markers.py | 5 +- .../isaaclab/sim/simulation_context.py | 3 + .../isaaclab/visualizers/base_visualizer.py | 50 +- .../isaaclab/visualizers/visualizer_cfg.py | 7 + .../markers/test_visualization_markers.py | 563 +++++++++++++++--- ...test_newton_manager_visualization_state.py | 16 +- .../test_simulation_context_visualizers.py | 362 ++--------- .../changelog.d/mtrepte-update-debug-viz.rst | 4 + .../isaaclab_newton/physics/newton_manager.py | 21 +- .../changelog.d/mtrepte-update-debug-viz.skip | 0 .../isaaclab_physx/physics/physx_manager.py | 25 +- .../changelog.d/mtrepte-update-debug-viz.skip | 0 .../changelog.d/mtrepte-update-debug-viz.skip | 0 .../changelog.d/mtrepte-update-debug-viz.rst | 5 + .../kit/kit_visualizer.py | 7 - .../newton/newton_visualizer.py | 46 +- .../rerun/rerun_visualizer.py | 34 +- .../rerun/rerun_visualizer_cfg.py | 9 +- .../viser/viser_visualizer.py | 102 ++-- .../viser/viser_visualizer_cfg.py | 19 +- .../test_visualizer_cartpole_integration.py | 1 + 25 files changed, 846 insertions(+), 527 deletions(-) create mode 100644 source/isaaclab/changelog.d/mtrepte-update-debug-viz.rst create mode 100644 source/isaaclab_newton/changelog.d/mtrepte-update-debug-viz.rst create mode 100644 source/isaaclab_physx/changelog.d/mtrepte-update-debug-viz.skip create mode 100644 source/isaaclab_rl/changelog.d/mtrepte-update-debug-viz.skip create mode 100644 source/isaaclab_tasks/changelog.d/mtrepte-update-debug-viz.skip create mode 100644 source/isaaclab_visualizers/changelog.d/mtrepte-update-debug-viz.rst diff --git a/docs/source/features/visualization.rst b/docs/source/features/visualization.rst index c37fb6d9be96..c23d9d4b5fd2 100644 --- a/docs/source/features/visualization.rst +++ b/docs/source/features/visualization.rst @@ -121,6 +121,8 @@ You can also configure custom visualizers in the code by defining ``VisualizerCf ), ViserVisualizerCfg( port=8080, + bind_address="0.0.0.0", + display_address="localhost", share=False, ), ] @@ -390,6 +392,22 @@ Rerun Visualizer - Timeline scrubbing and playback controls of recordings - Visualization debug markers +.. important:: + + A highlighted Rerun browser URL is printed in the logs before the main simulation or training loop begins. + Ctrl-click the printed URL in supported terminals/IDEs to open it. Set ``open_browser=True`` to automatically + open the browser tab instead. + + Example: + + .. code-block:: text + + ╭─────────────────────────── rerun (listening *:9090) ───────────────────────────╮ + │ ╷ │ + │ URL │ http://127.0.0.1:9090/?url=rerun%2Bhttp://127.0.0.1:9876/proxy │ + │ ╵ │ + ╰────────────────────────────────────────────────────────────────────────────────╯ + **Core Configuration:** .. code-block:: python @@ -400,8 +418,9 @@ Rerun Visualizer # Server settings app_id="isaaclab-simulation", # Application identifier for viewer grpc_port=9876, # gRPC endpoint for logging SDK connection - web_port=9090, # Port for local web viewer (launched in browser) + web_port=9090, # Port for local web viewer URL printed in logs bind_address="0.0.0.0", # Endpoint host formatting/reuse checks + open_browser=False, # Set True to auto-launch the browser # Camera settings eye=(8.0, 8.0, 3.0), # Initial camera position (x, y, z) @@ -427,7 +446,7 @@ The `Viser `_ visualizer provides a **web-based** 3D view simulations powered by the Newton Warp renderer. It streams the simulation state to a local web server, allowing you to view and interact with the scene from any browser. -**Key features:** +**Main Features:** - Browser-based visualization accessible at ``http://localhost:8080`` by default - Optional public share URL for remote viewing @@ -435,34 +454,55 @@ server, allowing you to view and interact with the scene from any browser. - Environment filtering to control which environments are rendered - Visualization debug markers -**Launch with Viser:** +.. important:: -.. code-block:: bash + A highlighted Viser browser URL is printed in the logs before the main simulation or training loop begins. + Ctrl-click the printed URL in supported terminals/IDEs to open it. Set ``open_browser=True`` to automatically + open the browser tab instead. For remote access, keep ``bind_address="0.0.0.0"`` and set + ``display_address`` to the hostname or IP address reachable from your browser. + + Example: - ./isaaclab.sh -p source/isaaclab_tasks/isaaclab_tasks/direct/cartpole/cartpole_env.py --viz viser + .. code-block:: text -**Configuration example:** + ╭────── viser (listening *:8080) ───────╮ + │ ╷ │ + │ URL │ http://localhost:8080 │ + │ ╵ │ + ╰───────────────────────────────────────╯ + +**Core Configuration:** .. code-block:: python from isaaclab_visualizers.viser import ViserVisualizerCfg visualizer_cfg = ViserVisualizerCfg( - port=8080, - open_browser=True, - label="Isaac Lab Simulation", - share=False, - max_visible_envs=16, - ) + # Server settings + port=8080, # Port for local Viser web server + bind_address="0.0.0.0", # Interface to listen on; use 0.0.0.0 for remote access + display_address="localhost", # Host/IP shown in the printed browser URL + open_browser=False, # Set True to auto-launch the browser + label="Isaac Lab Simulation", # Page title shown in the viewer + share=False, # Request a public share URL for remote viewing + verbose=True, # Print viewer server startup information -**Configuration options:** + # Camera settings + eye=(8.0, 8.0, 3.0), # Initial camera position (x, y, z) + lookat=(0.0, 0.0, 0.0), # Camera look-at target + + # Environment filtering + max_visible_envs=16, # Maximum number of environments to visualize + + # Recording + record_to_viser="recording.viser", # Path to save .viser file (None = no recording) + ) -- ``port`` (int, default ``8080``): Port of the local Viser web server. -- ``open_browser`` (bool, default ``True``): Automatically open the viewer URL in a browser. -- ``label`` (str or None, default ``"Isaac Lab Simulation"``): Page title shown in the viewer. -- ``share`` (bool, default ``False``): Request a public share URL from Viser for remote viewing. -- ``record_to_viser`` (str or None, default ``None``): Path to save a ``.viser`` recording file. -- ``verbose`` (bool, default ``True``): Print viewer server startup information. +Viser uses an in-process ``viser.ViserServer`` through ``newton.viewer.ViewerViser``. ``bind_address`` +controls the network interface that the server listens on, while ``display_address`` controls only the +URL printed by Isaac Lab. On a remote machine, set ``display_address`` to the machine hostname/IP and +ensure the configured ``port`` is reachable from your browser. Set ``share=True`` to request Viser's +public share/tunnel URL when that service is available. .. note:: diff --git a/docs/source/overview/core-concepts/renderers.rst b/docs/source/overview/core-concepts/renderers.rst index f04b476330b5..b4e7c2a6454e 100644 --- a/docs/source/overview/core-concepts/renderers.rst +++ b/docs/source/overview/core-concepts/renderers.rst @@ -32,6 +32,11 @@ Choosing a renderer backend .. note:: + Visualization markers are not yet supported by Newton-based renderer backends, + including the Newton Warp renderer. Use an RTX-based renderer, such as the + Isaac RTX renderer or OVRTX renderer, when marker visualization is needed. + +.. note:: **Temporal information for camera-based RL.** Unlike RTX modes with temporal anti-aliasing (DLSS, DLAA, TAA), the Newton Warp renderer does not inject prior-frame information into the current image. Camera-control tasks that depend diff --git a/source/isaaclab/changelog.d/mtrepte-update-debug-viz.rst b/source/isaaclab/changelog.d/mtrepte-update-debug-viz.rst new file mode 100644 index 000000000000..b7aa84db0dfc --- /dev/null +++ b/source/isaaclab/changelog.d/mtrepte-update-debug-viz.rst @@ -0,0 +1,9 @@ +Fixed +^^^^^ + +* Fixed visualization marker backend initialization so USD markers remain available during rendering even when standalone visualizers are not launched. + +Changed +^^^^^^^ + +* Removed temporary startup and runtime debug breadcrumbs from core simulation and environment setup logs. diff --git a/source/isaaclab/isaaclab/cli/commands/install.py b/source/isaaclab/isaaclab/cli/commands/install.py index 90fa929b69e5..2953919c4427 100644 --- a/source/isaaclab/isaaclab/cli/commands/install.py +++ b/source/isaaclab/isaaclab/cli/commands/install.py @@ -649,6 +649,8 @@ def _install_extra_feature(feature_name: str, selector: str = "") -> None: "websockets", "viser", "imgui_bundle", + "attr", + "attrs", ] """Package directory names in Isaac Sim prebundle directories to repoint. diff --git a/source/isaaclab/isaaclab/markers/visualization_markers.py b/source/isaaclab/isaaclab/markers/visualization_markers.py index 2e418bbecade..667f9b4ddf7d 100644 --- a/source/isaaclab/isaaclab/markers/visualization_markers.py +++ b/source/isaaclab/isaaclab/markers/visualization_markers.py @@ -250,7 +250,10 @@ def _ensure_backends_initialized(self) -> None: self._ensure_kit_backend() return - if any(viz.supports_markers() and viz.pumps_app_update() and viz.cfg.enable_markers for viz in sim.visualizers): + needs_kit_backend = sim.is_rendering or any( + viz.supports_markers() and viz.pumps_app_update() and viz.cfg.enable_markers for viz in sim.visualizers + ) + if needs_kit_backend: self._ensure_kit_backend() if any( viz.supports_markers() and not viz.pumps_app_update() and viz.cfg.enable_markers for viz in sim.visualizers diff --git a/source/isaaclab/isaaclab/sim/simulation_context.py b/source/isaaclab/isaaclab/sim/simulation_context.py index 175961fcd383..9975cf13100d 100644 --- a/source/isaaclab/isaaclab/sim/simulation_context.py +++ b/source/isaaclab/isaaclab/sim/simulation_context.py @@ -761,6 +761,9 @@ def update_visualizers(self, dt: float, skip_app_pumping: bool = False) -> None: if not self._visualizers: return + for viz in self._visualizers: + viz.flush_startup_messages() + if self._should_forward_before_visualizer_update(): self.physics_manager.forward() diff --git a/source/isaaclab/isaaclab/visualizers/base_visualizer.py b/source/isaaclab/isaaclab/visualizers/base_visualizer.py index 92f8c37ce947..9e41ddce3402 100644 --- a/source/isaaclab/isaaclab/visualizers/base_visualizer.py +++ b/source/isaaclab/isaaclab/visualizers/base_visualizer.py @@ -8,10 +8,12 @@ from __future__ import annotations import logging +import math import random import re from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Any +from urllib.parse import urlparse if TYPE_CHECKING: from isaaclab.scene.scene_data_provider import SceneDataProvider @@ -21,6 +23,8 @@ logger = logging.getLogger(__name__) +_USD_DEFAULT_VERTICAL_APERTURE_MM = 15.2908 + class BaseVisualizer(ABC): """Base class for all visualizer backends. @@ -38,6 +42,7 @@ def __init__(self, cfg: VisualizerCfg): self._scene_data_provider = None self._is_initialized = False self._is_closed = False + self._deferred_startup_messages: list[str] = [] @abstractmethod def initialize(self, scene_data_provider: SceneDataProvider) -> None: @@ -188,6 +193,13 @@ def _resolve_cfg_camera_pose( lookat = tuple(float(v) for v in self.cfg.lookat) return eye, lookat + def _focal_length_to_vertical_fov_degrees(self) -> float: + """Convert cfg focal length to vertical FOV using USD's default aperture.""" + focal_length = float(self.cfg.focal_length) + if focal_length <= 0.0: + raise ValueError("VisualizerCfg.focal_length must be positive.") + return math.degrees(2.0 * math.atan(_USD_DEFAULT_VERTICAL_APERTURE_MM / (2.0 * focal_length))) + def _resolve_camera_pose_from_usd_path( self, usd_path: str ) -> tuple[tuple[float, float, float], tuple[float, float, float]] | None: @@ -308,7 +320,43 @@ def _log_initialization_table(self, logger: logging.Logger, title: str, rows: li table.align["Value"] = "l" for key, value in rows: table.add_row([key, value]) - logger.info("Visualizer initialization:\n%s", table.get_string()) + logger.debug("Visualizer initialization:\n%s", table.get_string()) + + def _log_viewer_url( + self, + visualizer_name: str, + viewer_url: str, + ) -> None: + """Queue a visible browser URL block for web-based visualizers. + + Args: + visualizer_name: Name of the visualizer exposing the URL. + viewer_url: Browser URL for the visualizer. + """ + parsed_url = urlparse(viewer_url) + visualizer_label = visualizer_name.removesuffix("Visualizer").lower() + title = f" {visualizer_label} (listening *:{parsed_url.port}) " if parsed_url.port else f" {visualizer_label} " + label = "URL" + label_width = len(label) + value_width = max(len(viewer_url), len(title) + 2, 21) + inner_width = label_width + value_width + 9 + left_rule_width = max((inner_width - len(title)) // 2, 1) + right_rule_width = max(inner_width - len(title) - left_rule_width, 1) + + lines = [ + f"╭{'─' * left_rule_width}{title}{'─' * right_rule_width}╮", + f"│{' ' * (label_width + 4)}╷{' ' * (value_width + 4)}│", + f"│ {label:<{label_width}} │ {viewer_url:<{value_width}} │", + f"│{' ' * (label_width + 4)}╵{' ' * (value_width + 4)}│", + f"╰{'─' * inner_width}╯", + ] + self._deferred_startup_messages.append("\n" + "\n".join(lines) + "\n") + + def flush_startup_messages(self) -> None: + """Print deferred startup messages immediately before the workflow update loop starts.""" + for message in self._deferred_startup_messages: + print(message, flush=True) + self._deferred_startup_messages.clear() def play(self) -> None: """Handle simulation play/start. No-op by default.""" diff --git a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py index 8a8c7ba7a21b..ef64d0909e4f 100644 --- a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py +++ b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py @@ -40,6 +40,13 @@ class VisualizerCfg: lookat: tuple[float, float, float] = (0.0, 0.0, 0.0) """Initial camera look-at point (x, y, z) in world coordinates.""" + focal_length: float = 15.0 + """Camera focal length in millimeters for visualizer camera views. + + Kit applies this directly to USD cameras. Newton-style backends convert it + to a vertical field-of-view using the USD default vertical aperture. + """ + cam_source: Literal["cfg", "prim_path"] = "cfg" """Camera source mode: 'cfg' uses eye/lookat, 'prim_path' follows a camera prim.""" diff --git a/source/isaaclab/test/markers/test_visualization_markers.py b/source/isaaclab/test/markers/test_visualization_markers.py index 906c14ccb7d5..b9ae8387cf0f 100644 --- a/source/isaaclab/test/markers/test_visualization_markers.py +++ b/source/isaaclab/test/markers/test_visualization_markers.py @@ -12,15 +12,24 @@ """Rest everything follows.""" +import isaaclab_visualizers.newton.newton_visualization_markers as newton_markers +import isaaclab_visualizers.newton.newton_visualizer as newton_visualizer +import isaaclab_visualizers.rerun.rerun_visualizer as rerun_visualizer +import isaaclab_visualizers.viser.viser_visualizer as viser_visualizer +import numpy as np import pytest import torch +from isaaclab_visualizers.kit.kit_visualizer import KitVisualizer +from isaaclab_visualizers.kit.kit_visualizer_cfg import KitVisualizerCfg +from isaaclab_visualizers.newton.newton_visualizer_cfg import NewtonVisualizerCfg +from isaaclab_visualizers.rerun.rerun_visualizer_cfg import RerunVisualizerCfg +from isaaclab_visualizers.viser.viser_visualizer_cfg import ViserVisualizerCfg import isaaclab.sim as sim_utils from isaaclab.markers import VisualizationMarkers, VisualizationMarkersCfg from isaaclab.markers.config import FRAME_MARKER_CFG, POSITION_GOAL_MARKER_CFG from isaaclab.sim import SimulationCfg, SimulationContext from isaaclab.utils.math import random_orientation -from isaaclab.utils.timer import Timer @pytest.fixture @@ -40,6 +49,24 @@ def sim(): sim_utils.close_stage() +class _FakeMarkerVisualizer: + def __init__(self, *, enable_markers: bool = True, pumps_app_update: bool = False): + self.cfg = type("Cfg", (), {"enable_markers": enable_markers})() + self._pumps_app_update = pumps_app_update + + def supports_markers(self): + return True + + def pumps_app_update(self): + return self._pumps_app_update + + def stop(self): + pass + + def close(self): + pass + + def test_instantiation(sim): """Test that the class can be initialized properly.""" config = VisualizationMarkersCfg( @@ -54,6 +81,69 @@ def test_instantiation(sim): assert test_marker.num_prototypes == 1 +@pytest.mark.parametrize( + ("is_rendering", "visualizers", "expected_backends"), + [ + (True, [], ["kit"]), + (False, [], []), + (False, [KitVisualizer(KitVisualizerCfg())], ["kit"]), + (False, [newton_visualizer.NewtonVisualizer(NewtonVisualizerCfg())], ["newton"]), + (False, [rerun_visualizer.RerunVisualizer(RerunVisualizerCfg())], ["newton"]), + (False, [viser_visualizer.ViserVisualizer(ViserVisualizerCfg())], ["newton"]), + ], +) +def test_marker_backend_selection(monkeypatch, is_rendering: bool, visualizers: list, expected_backends: list[str]): + """Marker backend selection follows rendering state and active visualizer type.""" + marker = object.__new__(VisualizationMarkers) + marker._backends = [] + fake_sim = type("FakeSim", (), {"is_rendering": is_rendering, "visualizers": visualizers})() + + monkeypatch.setattr(sim_utils.SimulationContext, "instance", staticmethod(lambda: fake_sim)) + monkeypatch.setattr(VisualizationMarkers, "_ensure_kit_backend", lambda self: self._backends.append("kit")) + monkeypatch.setattr(VisualizationMarkers, "_ensure_newton_backend", lambda self: self._backends.append("newton")) + + marker._ensure_backends_initialized() + + assert marker._backends == expected_backends + + +def test_rendering_context_authors_visible_usd_point_instancer(sim): + """Rendering-active contexts should create visible USD marker prims.""" + from pxr import UsdGeom + + sim._has_offscreen_render = True + config = VisualizationMarkersCfg( + prim_path="/World/Visuals/rendered_marker", + markers={ + "failure": sim_utils.CuboidCfg( + size=(0.1, 0.1, 0.1), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.25, 0.15, 0.15)), + visible=True, + ), + "success": sim_utils.CuboidCfg( + size=(0.1, 0.1, 0.1), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.15, 0.25, 0.15)), + visible=True, + ), + }, + ) + test_marker = VisualizationMarkers(config) + test_marker.visualize( + translations=torch.tensor([[0.0, 0.0, 0.0], [0.2, 0.0, 0.0]], device=sim.device), + marker_indices=torch.tensor([0, 1], device=sim.device), + ) + + stage = sim_utils.get_current_stage() + instancer_prim = stage.GetPrimAtPath(test_marker.prim_path) + instancer = UsdGeom.PointInstancer(instancer_prim) + + assert instancer_prim.IsValid() + assert instancer + assert UsdGeom.Imageable(instancer_prim).GetVisibilityAttr().Get() != UsdGeom.Tokens.invisible + assert len(instancer.GetPositionsAttr().Get()) == 2 + assert list(instancer.GetProtoIndicesAttr().Get()) == [0, 1] + + def test_usd_marker(sim): """Test with marker from a USD.""" # create a marker @@ -80,29 +170,6 @@ def test_usd_marker(sim): assert test_marker.count == num_frames -def test_usd_marker_color(sim): - """Test with marker from a USD with its color modified.""" - # create a marker - config = FRAME_MARKER_CFG.copy() - config.prim_path = "/World/Visuals/test_frames" - config.markers["frame"].visual_material = sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0)) - test_marker = VisualizationMarkers(config) - - # play the simulation - sim.reset() - # run with randomization of poses - for count in range(1000): - # sample random poses - if count % 50 == 0: - num_frames = torch.randint(10, 1000, (1,)).item() - frame_translations = torch.randn(num_frames, 3, device=sim.device) - frame_rotations = random_orientation(num_frames, device=sim.device) - # set the marker - test_marker.visualize(translations=frame_translations, orientations=frame_rotations) - # update the kit - sim.step() - - def test_multiple_prototypes_marker(sim): """Test with multiple prototypes of spheres.""" # create a marker @@ -126,9 +193,8 @@ def test_multiple_prototypes_marker(sim): sim.step() -@pytest.mark.flaky(max_runs=3, min_passes=1) -def test_visualization_time_based_on_prototypes(sim): - """Test with time taken when number of prototypes is increased.""" +def test_visualization_skips_updates_when_invisible(sim): + """When invisible, visualize should not update marker state.""" # create a marker config = POSITION_GOAL_MARKER_CFG.copy() config.prim_path = "/World/Visuals/test_protos" @@ -136,69 +202,408 @@ def test_visualization_time_based_on_prototypes(sim): # play the simulation sim.reset() - # number of frames - num_frames = 4096 # check that visibility is true assert test_marker.is_visible() - # run with randomization of poses and indices - frame_translations = torch.randn(num_frames, 3, device=sim.device) - marker_indices = torch.randint(0, test_marker.num_prototypes, (num_frames,), device=sim.device) - # set the marker - with Timer("Marker visualization with explicit indices") as timer: - test_marker.visualize(translations=frame_translations, marker_indices=marker_indices) - # save the time - time_with_marker_indices = timer.time_elapsed - - with Timer("Marker visualization with no indices") as timer: - test_marker.visualize(translations=frame_translations) - # save the time - time_with_no_marker_indices = timer.time_elapsed + frame_translations = torch.randn(4, 3, device=sim.device) + marker_indices = torch.zeros(4, dtype=torch.int32, device=sim.device) + test_marker.visualize(translations=frame_translations, marker_indices=marker_indices) + assert test_marker.count == 4 # update the kit sim.step() - # check that the time is less - assert time_with_no_marker_indices < time_with_marker_indices + # make invisible + test_marker.set_visibility(False) + # check that visibility is false + assert not test_marker.is_visible() + test_marker.visualize( + translations=torch.randn(8, 3, device=sim.device), + marker_indices=torch.zeros(8, dtype=torch.int32, device=sim.device), + ) -def test_visualization_time_based_on_visibility(sim): - """Test with visibility of markers. When invisible, the visualize call should return.""" - # create a marker + assert test_marker.count == 4 + + +def test_newton_marker_backend_registers_and_updates_state_without_frame_capture(sim): + """Newton marker backend state should be registered and ready for Newton-family viewers.""" + sim._visualizers.append(_FakeMarkerVisualizer(pumps_app_update=False)) config = POSITION_GOAL_MARKER_CFG.copy() - config.prim_path = "/World/Visuals/test_protos" + config.prim_path = "/World/Visuals/newton_marker_state" test_marker = VisualizationMarkers(config) + translations = torch.arange(6, dtype=torch.float32, device=sim.device).reshape(2, 3) + marker_indices = torch.tensor([0, 0], device=sim.device) - # play the simulation - sim.reset() - # number of frames - num_frames = 4096 + test_marker.visualize(translations=translations, marker_indices=marker_indices) - # check that visibility is true - assert test_marker.is_visible() - # run with randomization of poses and indices - frame_translations = torch.randn(num_frames, 3, device=sim.device) - marker_indices = torch.randint(0, test_marker.num_prototypes, (num_frames,), device=sim.device) - # set the marker - with Timer("Marker visualization") as timer: - test_marker.visualize(translations=frame_translations, marker_indices=marker_indices) - # save the time - time_with_visualization = timer.time_elapsed + newton_backend = test_marker._backends[0] + assert isinstance(newton_backend, newton_markers.NewtonVisualizationMarkers) + assert sim.vis_marker_registry.get_groups()[newton_backend.group_id] is newton_backend + assert torch.equal(newton_backend.translations, translations) + assert torch.equal(newton_backend.marker_indices, marker_indices.to(dtype=torch.int32)) + assert newton_backend.count == 2 - # update the kit - sim.step() - # make invisible - test_marker.set_visibility(False) - # check that visibility is false - assert not test_marker.is_visible() - # run with randomization of poses and indices - frame_translations = torch.randn(num_frames, 3, device=sim.device) - marker_indices = torch.randint(0, test_marker.num_prototypes, (num_frames,), device=sim.device) - # set the marker - with Timer("Marker no visualization") as timer: - test_marker.visualize(translations=frame_translations, marker_indices=marker_indices) - # save the time - time_with_no_visualization = timer.time_elapsed - - # check that the time is less - assert time_with_no_visualization < time_with_visualization +def test_newton_visualizer_step_renders_markers(monkeypatch: pytest.MonkeyPatch): + """NewtonVisualizer.step should ask active Newton marker groups to render.""" + marker_calls = [] + + class _FakeViewer: + _update_frequency = 1 + + def __init__(self): + self.calls = [] + + def is_paused(self): + return False + + def begin_frame(self, sim_time): + self.calls.append(("begin_frame", sim_time)) + + def log_state(self, state): + self.calls.append(("log_state", state)) + + def end_frame(self): + self.calls.append(("end_frame",)) + + class _FakeNewtonManager: + @staticmethod + def get_state(scene_data_provider=None): + assert scene_data_provider == "provider" + return {"state": "ok"} + + @staticmethod + def get_num_envs() -> int: + return 4 + + def _fake_render_markers(viewer, visible_env_ids, num_envs): + marker_calls.append((viewer, visible_env_ids, num_envs)) + + import isaaclab_newton.physics as newton_physics + + monkeypatch.setattr(newton_physics, "NewtonManager", _FakeNewtonManager) + monkeypatch.setattr(newton_visualizer, "render_newton_visualization_markers", _fake_render_markers) + + viewer = _FakeViewer() + visualizer = newton_visualizer.NewtonVisualizer(NewtonVisualizerCfg(enable_markers=True)) + visualizer._is_initialized = True + visualizer._is_closed = False + visualizer._viewer = viewer + visualizer._scene_data_provider = "provider" + visualizer._resolved_visible_env_ids = [1, 3] + + visualizer.step(0.25) + + assert viewer.calls == [("begin_frame", pytest.approx(0.25)), ("log_state", {"state": "ok"}), ("end_frame",)] + assert marker_calls == [(viewer, [1, 3], 4)] + + +def test_viser_visualizer_marker_render_failure_does_not_interrupt_state_updates( + monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture +): + """Viser marker failures should be logged without dropping body state frames.""" + marker_calls = [] + + class _FakeViewer: + def __init__(self): + self.calls = [] + + def begin_frame(self, sim_time: float) -> None: + self.calls.append(("begin_frame", sim_time)) + + def log_state(self, state) -> None: + self.calls.append(("log_state", state)) + + def end_frame(self) -> None: + self.calls.append(("end_frame",)) + + class _FakeProvider: + num_envs = 4 + usd_stage = None + + def get_camera_transforms(self): + return {} + + class _FakeNewtonManager: + @staticmethod + def get_model(): + return "dummy-model" + + @staticmethod + def get_state(scene_data_provider=None): + assert scene_data_provider is provider + return {"state": "ok"} + + @staticmethod + def get_num_envs() -> int: + return provider.num_envs + + def _fake_create_viewer(self, record_to_viser: str | None, metadata: dict | None = None): + self._viewer = viewer + + def _raise_marker_render(*args, **kwargs): + marker_calls.append((args, kwargs)) + raise RuntimeError("marker overlay failed") + + import isaaclab_newton.physics as newton_physics + + provider = _FakeProvider() + viewer = _FakeViewer() + monkeypatch.setattr(newton_physics, "NewtonManager", _FakeNewtonManager) + monkeypatch.setattr(viser_visualizer.ViserVisualizer, "_create_viewer", _fake_create_viewer) + monkeypatch.setattr(viser_visualizer, "render_newton_visualization_markers", _raise_marker_render) + + visualizer = viser_visualizer.ViserVisualizer(ViserVisualizerCfg()) + visualizer.initialize(provider) + + with caplog.at_level("WARNING"): + visualizer.step(0.25) + + assert marker_calls + assert viewer.calls == [("begin_frame", pytest.approx(0.25)), ("log_state", {"state": "ok"}), ("end_frame",)] + assert "Marker rendering failed; continuing body updates" in caplog.text + + +def test_rerun_visualizer_marker_failure_still_ends_frame(monkeypatch: pytest.MonkeyPatch): + """Rerun should close the frame even if marker rendering raises.""" + captured = {} + + class _FakeViewer: + def __init__(self): + self.calls = [] + + def is_paused(self): + return False + + def begin_frame(self, sim_time): + self.calls.append(("begin_frame", sim_time)) + + def log_state(self, state): + self.calls.append(("log_state", state)) + + def end_frame(self): + self.calls.append(("end_frame",)) + + class _FakeProvider: + def get_metadata(self) -> dict: + return {"num_envs": 4} + + def get_newton_state(self): + return {"ok": True} + + def get_camera_transforms(self): + return {} + + class _FakeNewtonManager: + @staticmethod + def get_model(): + return "dummy-model" + + @staticmethod + def get_state(scene_data_provider=None): + captured["state_provider"] = scene_data_provider + return {"ok": True} + + @staticmethod + def get_num_envs() -> int: + return 4 + + def _raise_marker_render(*args, **kwargs): + raise RuntimeError("marker render failed") + + import isaaclab_newton.physics as newton_physics + + monkeypatch.setattr(newton_physics, "NewtonManager", _FakeNewtonManager) + monkeypatch.setattr(rerun_visualizer, "render_newton_visualization_markers", _raise_marker_render) + + visualizer = rerun_visualizer.RerunVisualizer(RerunVisualizerCfg()) + viewer = _FakeViewer() + visualizer._is_initialized = True + visualizer._is_closed = False + visualizer._viewer = viewer + visualizer._scene_data_provider = _FakeProvider() + visualizer._resolved_visible_env_ids = None + + with pytest.raises(RuntimeError, match="marker render failed"): + visualizer.step(0.25) + + assert captured["state_provider"] is visualizer._scene_data_provider + assert [call[0] for call in viewer.calls] == ["begin_frame", "log_state", "end_frame"] + + +def test_newton_marker_mesh_registration_is_per_viewer(monkeypatch: pytest.MonkeyPatch): + marker = object.__new__(newton_markers.NewtonVisualizationMarkers) + marker._registered_meshes = set() + + class _FakeMesh: + vertices = np.zeros((1, 3), dtype=np.float32) + indices = np.zeros((3,), dtype=np.int32) + normals = np.zeros((0, 3), dtype=np.float32) + uvs = np.zeros((0, 2), dtype=np.float32) + + class _FakeViewer: + def __init__(self): + self.meshes = [] + + def log_mesh(self, name, vertices, indices, **kwargs): + self.meshes.append((name, vertices, indices, kwargs)) + + monkeypatch.setattr(newton_markers, "_create_mesh", lambda cfg: _FakeMesh()) + monkeypatch.setattr(newton_markers.wp, "array", lambda value, dtype=None: value) + + spec = newton_markers._NewtonMarkerSpec(renderer="mesh", mesh_type="box", mesh_params={"size": (1.0, 1.0, 1.0)}) + viewer_a = _FakeViewer() + viewer_b = _FakeViewer() + + marker._ensure_mesh_registered(viewer_a, "/Visuals/marker/meshes/arrow", spec) + marker._ensure_mesh_registered(viewer_a, "/Visuals/marker/meshes/arrow", spec) + marker._ensure_mesh_registered(viewer_b, "/Visuals/marker/meshes/arrow", spec) + + assert len(viewer_a.meshes) == 1 + assert len(viewer_b.meshes) == 1 + + +class _FakeNewtonMarkerMesh: + vertices = np.zeros((1, 3), dtype=np.float32) + indices = np.zeros((3,), dtype=np.int32) + normals = np.zeros((0, 3), dtype=np.float32) + uvs = np.zeros((0, 2), dtype=np.float32) + + +class _FakeNewtonMarkerViewer: + def __init__(self): + self.meshes = [] + self.instances = [] + self.lines = [] + + def log_mesh(self, name, vertices, indices, **kwargs): + self.meshes.append((name, vertices, indices, kwargs)) + + def log_instances(self, batch_name, mesh_name, xforms, scales, colors, materials, hidden=False): + self.instances.append( + { + "batch_name": batch_name, + "mesh_name": mesh_name, + "xforms": xforms, + "scales": scales, + "colors": colors, + "materials": materials, + "hidden": hidden, + } + ) + + def log_lines(self, batch_name, starts, ends, colors, width=None, hidden=False): + self.lines.append( + { + "batch_name": batch_name, + "starts": starts, + "ends": ends, + "colors": colors, + "width": width, + "hidden": hidden, + } + ) + + +def _make_newton_marker_for_render( + *, + marker_names: list[str], + translations: torch.Tensor, + marker_indices: torch.Tensor | None = None, + visible: bool = True, +): + marker = object.__new__(newton_markers.NewtonVisualizationMarkers) + marker_cfg_type = type("MarkerCfg", (), {"visual_material": None}) + marker.cfg = type("Cfg", (), {"markers": {name: marker_cfg_type() for name in marker_names}})() + marker.group_id = "/Visuals/marker::test" + marker.visible = visible + marker.translations = translations + marker.orientations = torch.tensor([[0.0, 0.0, 0.0, 1.0]], dtype=torch.float32).repeat(translations.shape[0], 1) + marker.scales = torch.ones((translations.shape[0], 3), dtype=torch.float32) + marker.marker_indices = marker_indices + marker.count = translations.shape[0] + marker._registered_meshes = set() + marker._warned_unsupported = set() + return marker + + +def _patch_newton_marker_render_deps(monkeypatch: pytest.MonkeyPatch) -> None: + specs = { + "arrow": newton_markers._NewtonMarkerSpec( + renderer="mesh", + mesh_type="box", + mesh_params={"size": (1.0, 1.0, 1.0)}, + color=(1.0, 1.0, 1.0), + texture=np.zeros((2, 2, 3), dtype=np.uint8), + ), + "sphere": newton_markers._NewtonMarkerSpec(renderer="mesh", mesh_type="sphere", mesh_params={"radius": 1.0}), + "frame": newton_markers._NewtonMarkerSpec(renderer="frame"), + } + + monkeypatch.setattr(newton_markers, "_create_mesh", lambda cfg: _FakeNewtonMarkerMesh()) + monkeypatch.setattr(newton_markers.wp, "array", lambda value, dtype=None: value) + monkeypatch.setattr(newton_markers, "_resolve_newton_marker_cfg", lambda name, marker_cfg, cfg: specs[name]) + + +def test_newton_marker_render_filters_visible_envs(monkeypatch: pytest.MonkeyPatch): + _patch_newton_marker_render_deps(monkeypatch) + translations = torch.arange(8, dtype=torch.float32).unsqueeze(1).repeat(1, 3) + marker = _make_newton_marker_for_render( + marker_names=["arrow"], + translations=translations, + marker_indices=torch.zeros(8, dtype=torch.int32), + ) + viewer = _FakeNewtonMarkerViewer() + + marker.render(viewer, visible_env_ids=[1, 3], num_envs=4) + + assert len(viewer.instances) == 1 + assert viewer.instances[0]["hidden"] is False + assert viewer.instances[0]["xforms"][:, 0].tolist() == [1.0, 3.0, 5.0, 7.0] + + +def test_newton_marker_render_routes_instances_by_prototype(monkeypatch: pytest.MonkeyPatch): + _patch_newton_marker_render_deps(monkeypatch) + translations = torch.arange(4, dtype=torch.float32).unsqueeze(1).repeat(1, 3) + marker = _make_newton_marker_for_render( + marker_names=["arrow", "sphere"], + translations=translations, + marker_indices=torch.tensor([0, 1, 0, 1], dtype=torch.int32), + ) + viewer = _FakeNewtonMarkerViewer() + + marker.render(viewer, visible_env_ids=None, num_envs=4) + + visible_instances = [call for call in viewer.instances if not call["hidden"]] + assert [call["batch_name"] for call in visible_instances] == [ + "/Visuals/marker::test/arrow", + "/Visuals/marker::test/sphere", + ] + assert [call["xforms"].shape[0] for call in visible_instances] == [2, 2] + assert visible_instances[0]["materials"][:, 3].tolist() == [1.0, 1.0] + assert visible_instances[1]["materials"][:, 3].tolist() == [0.0, 0.0] + + +def test_newton_marker_render_hides_unselected_prototypes(monkeypatch: pytest.MonkeyPatch): + _patch_newton_marker_render_deps(monkeypatch) + marker = _make_newton_marker_for_render( + marker_names=["arrow", "sphere", "frame"], + translations=torch.zeros((3, 3), dtype=torch.float32), + marker_indices=torch.zeros(3, dtype=torch.int32), + ) + viewer = _FakeNewtonMarkerViewer() + + marker.render(viewer, visible_env_ids=None, num_envs=3) + + hidden_instances = [call for call in viewer.instances if call["hidden"]] + assert [call["batch_name"] for call in hidden_instances] == ["/Visuals/marker::test/sphere"] + assert viewer.lines == [ + { + "batch_name": "/Visuals/marker::test/frame", + "starts": None, + "ends": None, + "colors": None, + "width": None, + "hidden": True, + } + ] diff --git a/source/isaaclab/test/sim/test_newton_manager_visualization_state.py b/source/isaaclab/test/sim/test_newton_manager_visualization_state.py index de0515c57e07..7c3abf9f300a 100644 --- a/source/isaaclab/test/sim/test_newton_manager_visualization_state.py +++ b/source/isaaclab/test/sim/test_newton_manager_visualization_state.py @@ -26,8 +26,8 @@ def _reset_newton_manager_state(): NewtonManager._model = None NewtonManager._state_0 = None NewtonManager._num_envs = None - NewtonManager._physx_visualization_scene_data = None - NewtonManager._physx_visualization_mapping = None + NewtonManager._visualization_scene_data = None + NewtonManager._visualization_mapping = None def test_ensure_visualization_model_noop_when_backend_is_newton(monkeypatch): @@ -35,7 +35,7 @@ def test_ensure_visualization_model_noop_when_backend_is_newton(monkeypatch): from isaaclab_newton.physics import NewtonManager _reset_newton_manager_state() - monkeypatch.setattr(NewtonManager, "_backend_is_newton", classmethod(lambda cls: True)) + monkeypatch.setattr(NewtonManager, "_backend_is_newton", classmethod(lambda cls, scene_data_provider=None: True)) NewtonManager._ensure_visualization_model() assert NewtonManager._model is None assert NewtonManager._state_0 is None @@ -47,7 +47,7 @@ def test_ensure_visualization_model_builds_from_stage_when_backend_is_physx(monk from isaaclab_newton.physics import newton_manager as nm _reset_newton_manager_state() - monkeypatch.setattr(NewtonManager, "_backend_is_newton", classmethod(lambda cls: False)) + monkeypatch.setattr(NewtonManager, "_backend_is_newton", classmethod(lambda cls, scene_data_provider=None: False)) monkeypatch.setattr(nm, "get_current_stage", lambda *args, **kwargs: SimpleNamespace()) monkeypatch.setattr(nm, "replace_newton_shape_colors", lambda model, *a, **kw: 0) @@ -80,7 +80,7 @@ def test_ensure_visualization_model_empty_builder_logs_and_skips(monkeypatch, ca from isaaclab_newton.physics import newton_manager as nm _reset_newton_manager_state() - monkeypatch.setattr(NewtonManager, "_backend_is_newton", classmethod(lambda cls: False)) + monkeypatch.setattr(NewtonManager, "_backend_is_newton", classmethod(lambda cls, scene_data_provider=None: False)) monkeypatch.setattr(nm, "get_current_stage", lambda *args, **kwargs: SimpleNamespace()) class _EmptyBuilder: @@ -106,7 +106,7 @@ def test_ensure_visualization_model_populates_num_envs_when_backend_is_physx(mon from isaaclab_newton.physics import newton_manager as nm _reset_newton_manager_state() - monkeypatch.setattr(NewtonManager, "_backend_is_newton", classmethod(lambda cls: False)) + monkeypatch.setattr(NewtonManager, "_backend_is_newton", classmethod(lambda cls, scene_data_provider=None: False)) monkeypatch.setattr(nm, "get_current_stage", lambda *args, **kwargs: SimpleNamespace()) monkeypatch.setattr(nm, "replace_newton_shape_colors", lambda model, *a, **kw: 0) @@ -136,7 +136,7 @@ def test_ensure_visualization_model_missing_stage_leaves_state_unset(monkeypatch from isaaclab_newton.physics import newton_manager as nm _reset_newton_manager_state() - monkeypatch.setattr(NewtonManager, "_backend_is_newton", classmethod(lambda cls: False)) + monkeypatch.setattr(NewtonManager, "_backend_is_newton", classmethod(lambda cls, scene_data_provider=None: False)) monkeypatch.setattr(nm, "get_current_stage", lambda *args, **kwargs: None) with caplog.at_level("ERROR"): @@ -152,7 +152,7 @@ def test_update_visualization_state_noop_when_backend_is_newton(monkeypatch): from isaaclab_newton.physics import NewtonManager _reset_newton_manager_state() - monkeypatch.setattr(NewtonManager, "_backend_is_newton", classmethod(lambda cls: True)) + monkeypatch.setattr(NewtonManager, "_backend_is_newton", classmethod(lambda cls, scene_data_provider=None: True)) # Pre-set sentinel values to ensure update doesn't touch them. NewtonManager._model = "live-model" diff --git a/source/isaaclab/test/sim/test_simulation_context_visualizers.py b/source/isaaclab/test/sim/test_simulation_context_visualizers.py index cf136c37053a..9e590356932d 100644 --- a/source/isaaclab/test/sim/test_simulation_context_visualizers.py +++ b/source/isaaclab/test/sim/test_simulation_context_visualizers.py @@ -11,20 +11,21 @@ from typing import Any, cast import isaaclab_visualizers.kit.kit_visualizer as kit_visualizer -import isaaclab_visualizers.newton.newton_visualization_markers as newton_markers import isaaclab_visualizers.rerun.rerun_visualizer as rerun_visualizer import isaaclab_visualizers.viser.viser_visualizer as viser_visualizer -import numpy as np import pytest -import torch from isaaclab_visualizers.kit.kit_visualizer_cfg import KitVisualizerCfg from isaaclab_visualizers.rerun.rerun_visualizer_cfg import RerunVisualizerCfg from isaaclab_visualizers.viser.viser_visualizer_cfg import ViserVisualizerCfg -from isaaclab.markers.vis_marker_registry import VisMarkerRegistry from isaaclab.sim.simulation_context import SimulationContext +def test_web_visualizer_cfgs_do_not_open_browser_by_default(): + assert RerunVisualizerCfg().open_browser is False + assert ViserVisualizerCfg().open_browser is False + + class _FakePhysicsManager: def __init__(self): self.forward_calls = 0 @@ -114,6 +115,9 @@ def pumps_app_update(self): def supports_markers(self): return False + def flush_startup_messages(self): + pass + def _make_context(visualizers, provider=None): ctx = object.__new__(SimulationContext) @@ -188,26 +192,6 @@ def test_update_visualizers_handles_training_pause_loop(): assert viz.step_calls == [0.0, 0.2] -def test_vis_marker_registry_dispatch_allows_callback_mutation(): - registry = VisMarkerRegistry() - calls = [] - - def _remove_other_callback(event): - calls.append(("remove_other", event)) - registry.remove_callback("other") - - def _other_callback(event): - calls.append(("other", event)) - - registry.add_callback("remove_other", _remove_other_callback) - registry.add_callback("other", _other_callback) - - registry.dispatch_callbacks("tick") - - assert calls == [("remove_other", "tick"), ("other", "tick")] - assert "other" not in registry._callbacks - - class _DummyViserSceneDataProvider: @property def num_envs(self) -> int: @@ -249,7 +233,7 @@ def _fake_create_viewer(self, record_to_viser: str | None, metadata: dict | None monkeypatch.setattr(viser_visualizer.ViserVisualizer, "_create_viewer", _fake_create_viewer) - state_calls: list[None] = [] + state_calls: list[object] = [] class _FakeNewtonManager: @staticmethod @@ -257,8 +241,8 @@ def get_model(): return "dummy-model" @staticmethod - def get_state(): - state_calls.append(None) + def get_state(scene_data_provider=None): + state_calls.append(scene_data_provider) return {"state_call": len(state_calls)} @staticmethod @@ -274,241 +258,13 @@ def get_num_envs() -> int: visualizer.step(0.25) assert visualizer.is_initialized - assert state_calls == [None, None] + assert state_calls == [provider, provider] assert visualizer._sim_time == pytest.approx(0.25) assert viewer.calls[0][0] == "begin_frame" assert viewer.calls[0][1] == pytest.approx(0.25) - # log_state passes NewtonManager.get_state() through as-is; no env_ids merged in. - assert viewer.calls[1] == ("log_state", {"state_call": 2}) - assert viewer.calls[2] == ("end_frame",) - - -def test_viser_visualizer_marker_render_failure_does_not_interrupt_state_updates( - monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture -): - provider = _DummyViserSceneDataProvider() - viewer = _DummyViserViewer() - marker_calls = [] - - def _fake_create_viewer(self, record_to_viser: str | None, metadata: dict | None = None): - self._viewer = viewer - - def _raise_marker_render(*args, **kwargs): - marker_calls.append((args, kwargs)) - raise RuntimeError("marker overlay failed") - - monkeypatch.setattr(viser_visualizer.ViserVisualizer, "_create_viewer", _fake_create_viewer) - monkeypatch.setattr(viser_visualizer, "render_newton_visualization_markers", _raise_marker_render) - - state_calls: list[None] = [] - - class _FakeNewtonManager: - @staticmethod - def get_model(): - return "dummy-model" - - @staticmethod - def get_state(): - state_calls.append(None) - return {"state_call": len(state_calls)} - - @staticmethod - def get_num_envs() -> int: - return 1 - - import isaaclab_newton.physics as _np_mod - - monkeypatch.setattr(_np_mod, "NewtonManager", _FakeNewtonManager) - - visualizer = viser_visualizer.ViserVisualizer(ViserVisualizerCfg()) - visualizer.initialize(cast(Any, provider)) - - with caplog.at_level("WARNING"): - visualizer.step(0.25) - - assert marker_calls - assert viewer.calls[0][0] == "begin_frame" + # log_state passes NewtonManager.get_state(provider) through as-is; no env_ids merged in. assert viewer.calls[1] == ("log_state", {"state_call": 2}) assert viewer.calls[2] == ("end_frame",) - assert "Marker rendering failed; continuing body updates" in caplog.text - - -def test_newton_marker_mesh_registration_is_per_viewer(monkeypatch: pytest.MonkeyPatch): - marker = object.__new__(newton_markers.NewtonVisualizationMarkers) - marker._registered_meshes = set() - - class _FakeMesh: - vertices = np.zeros((1, 3), dtype=np.float32) - indices = np.zeros((3,), dtype=np.int32) - normals = np.zeros((0, 3), dtype=np.float32) - uvs = np.zeros((0, 2), dtype=np.float32) - - class _FakeViewer: - def __init__(self): - self.meshes = [] - - def log_mesh(self, name, vertices, indices, **kwargs): - self.meshes.append((name, vertices, indices, kwargs)) - - monkeypatch.setattr(newton_markers, "_create_mesh", lambda cfg: _FakeMesh()) - monkeypatch.setattr(newton_markers.wp, "array", lambda value, dtype=None: value) - - spec = newton_markers._NewtonMarkerSpec(renderer="mesh", mesh_type="box", mesh_params={"size": (1.0, 1.0, 1.0)}) - viewer_a = _FakeViewer() - viewer_b = _FakeViewer() - - marker._ensure_mesh_registered(viewer_a, "/Visuals/marker/meshes/arrow", spec) - marker._ensure_mesh_registered(viewer_a, "/Visuals/marker/meshes/arrow", spec) - marker._ensure_mesh_registered(viewer_b, "/Visuals/marker/meshes/arrow", spec) - - assert len(viewer_a.meshes) == 1 - assert len(viewer_b.meshes) == 1 - - -class _FakeNewtonMarkerMesh: - vertices = np.zeros((1, 3), dtype=np.float32) - indices = np.zeros((3,), dtype=np.int32) - normals = np.zeros((0, 3), dtype=np.float32) - uvs = np.zeros((0, 2), dtype=np.float32) - - -class _FakeNewtonMarkerViewer: - def __init__(self): - self.meshes = [] - self.instances = [] - self.lines = [] - - def log_mesh(self, name, vertices, indices, **kwargs): - self.meshes.append((name, vertices, indices, kwargs)) - - def log_instances(self, batch_name, mesh_name, xforms, scales, colors, materials, hidden=False): - self.instances.append( - { - "batch_name": batch_name, - "mesh_name": mesh_name, - "xforms": xforms, - "scales": scales, - "colors": colors, - "materials": materials, - "hidden": hidden, - } - ) - - def log_lines(self, batch_name, starts, ends, colors, width=None, hidden=False): - self.lines.append( - { - "batch_name": batch_name, - "starts": starts, - "ends": ends, - "colors": colors, - "width": width, - "hidden": hidden, - } - ) - - -def _make_newton_marker_for_render( - *, - marker_names: list[str], - translations: torch.Tensor, - marker_indices: torch.Tensor | None = None, - visible: bool = True, -): - marker = object.__new__(newton_markers.NewtonVisualizationMarkers) - marker_cfg_type = type("MarkerCfg", (), {"visual_material": None}) - marker.cfg = type("Cfg", (), {"markers": {name: marker_cfg_type() for name in marker_names}})() - marker.group_id = "/Visuals/marker::test" - marker.visible = visible - marker.translations = translations - marker.orientations = torch.tensor([[0.0, 0.0, 0.0, 1.0]], dtype=torch.float32).repeat(translations.shape[0], 1) - marker.scales = torch.ones((translations.shape[0], 3), dtype=torch.float32) - marker.marker_indices = marker_indices - marker.count = translations.shape[0] - marker._registered_meshes = set() - marker._warned_unsupported = set() - return marker - - -def _patch_newton_marker_render_deps(monkeypatch: pytest.MonkeyPatch) -> None: - specs = { - "arrow": newton_markers._NewtonMarkerSpec( - renderer="mesh", - mesh_type="box", - mesh_params={"size": (1.0, 1.0, 1.0)}, - color=(1.0, 1.0, 1.0), - texture=np.zeros((2, 2, 3), dtype=np.uint8), - ), - "sphere": newton_markers._NewtonMarkerSpec(renderer="mesh", mesh_type="sphere", mesh_params={"radius": 1.0}), - "frame": newton_markers._NewtonMarkerSpec(renderer="frame"), - } - - monkeypatch.setattr(newton_markers, "_create_mesh", lambda cfg: _FakeNewtonMarkerMesh()) - monkeypatch.setattr(newton_markers.wp, "array", lambda value, dtype=None: value) - monkeypatch.setattr(newton_markers, "_resolve_newton_marker_cfg", lambda name, marker_cfg, cfg: specs[name]) - - -def test_newton_marker_render_filters_visible_envs(monkeypatch: pytest.MonkeyPatch): - _patch_newton_marker_render_deps(monkeypatch) - translations = torch.arange(8, dtype=torch.float32).unsqueeze(1).repeat(1, 3) - marker = _make_newton_marker_for_render( - marker_names=["arrow"], - translations=translations, - marker_indices=torch.zeros(8, dtype=torch.int32), - ) - viewer = _FakeNewtonMarkerViewer() - - marker.render(viewer, visible_env_ids=[1, 3], num_envs=4) - - assert len(viewer.instances) == 1 - assert viewer.instances[0]["hidden"] is False - assert viewer.instances[0]["xforms"][:, 0].tolist() == [1.0, 3.0, 5.0, 7.0] - - -def test_newton_marker_render_routes_instances_by_prototype(monkeypatch: pytest.MonkeyPatch): - _patch_newton_marker_render_deps(monkeypatch) - translations = torch.arange(4, dtype=torch.float32).unsqueeze(1).repeat(1, 3) - marker = _make_newton_marker_for_render( - marker_names=["arrow", "sphere"], - translations=translations, - marker_indices=torch.tensor([0, 1, 0, 1], dtype=torch.int32), - ) - viewer = _FakeNewtonMarkerViewer() - - marker.render(viewer, visible_env_ids=None, num_envs=4) - - visible_instances = [call for call in viewer.instances if not call["hidden"]] - assert [call["batch_name"] for call in visible_instances] == [ - "/Visuals/marker::test/arrow", - "/Visuals/marker::test/sphere", - ] - assert [call["xforms"].shape[0] for call in visible_instances] == [2, 2] - assert visible_instances[0]["materials"][:, 3].tolist() == [1.0, 1.0] - assert visible_instances[1]["materials"][:, 3].tolist() == [0.0, 0.0] - - -def test_newton_marker_render_hides_unselected_prototypes(monkeypatch: pytest.MonkeyPatch): - _patch_newton_marker_render_deps(monkeypatch) - marker = _make_newton_marker_for_render( - marker_names=["arrow", "sphere", "frame"], - translations=torch.zeros((3, 3), dtype=torch.float32), - marker_indices=torch.zeros(3, dtype=torch.int32), - ) - viewer = _FakeNewtonMarkerViewer() - - marker.render(viewer, visible_env_ids=None, num_envs=3) - - hidden_instances = [call for call in viewer.instances if call["hidden"]] - assert [call["batch_name"] for call in hidden_instances] == ["/Visuals/marker::test/sphere"] - assert viewer.lines == [ - { - "batch_name": "/Visuals/marker::test/frame", - "starts": None, - "ends": None, - "colors": None, - "width": None, - "hidden": True, - } - ] @pytest.mark.parametrize( @@ -531,6 +287,7 @@ def __init__( self, *, port: int, + bind_address: str, label: str | None, verbose: bool, share: bool, @@ -539,6 +296,7 @@ def __init__( ): captured["init"] = { "port": port, + "bind_address": bind_address, "label": label, "verbose": verbose, "share": share, @@ -555,6 +313,10 @@ def set_visible_worlds(self, worlds) -> None: def set_world_offsets(self, spacing) -> None: captured["set_world_offsets"] = tuple(spacing) + @property + def share_url(self) -> str | None: + return None + monkeypatch.setattr(viser_visualizer, "NewtonViewerViser", _FakeNewtonViewerViser) monkeypatch.setattr( viser_visualizer.ViserVisualizer, @@ -574,6 +336,7 @@ def set_world_offsets(self, spacing) -> None: visualizer._create_viewer(record_to_viser="record.viser", metadata={"num_envs": 8}) assert captured["set_model"] == "dummy-model" + assert captured["init"]["bind_address"] == cfg.bind_address assert captured["visible_worlds"] == expected_visible assert captured["set_world_offsets"] == (0.0, 0.0, 0.0) @@ -605,6 +368,7 @@ def __init__( keep_historical_data: bool, keep_scalar_history: bool, record_to_rrd: str | None, + open_browser: bool, ): captured["init"] = { "app_id": app_id, @@ -615,6 +379,7 @@ def __init__( "keep_historical_data": keep_historical_data, "keep_scalar_history": keep_scalar_history, "record_to_rrd": record_to_rrd, + "open_browser": open_browser, } def set_model(self, model: Any) -> None: @@ -647,7 +412,8 @@ def get_model(): return "dummy-model" @staticmethod - def get_state(): + def get_state(scene_data_provider=None): + captured["state_provider"] = scene_data_provider return {"ok": True} @staticmethod @@ -683,69 +449,6 @@ def get_num_envs() -> int: assert captured["set_world_offsets"] == (0.0, 0.0, 0.0) -def test_rerun_visualizer_marker_failure_still_ends_frame(monkeypatch: pytest.MonkeyPatch): - class _FakeRerunViewer: - def __init__(self): - self.calls = [] - - def is_paused(self): - return False - - def begin_frame(self, sim_time): - self.calls.append(("begin_frame", sim_time)) - - def log_state(self, state): - self.calls.append(("log_state", state)) - - def end_frame(self): - self.calls.append(("end_frame",)) - - class _DummyRerunSceneDataProvider: - def get_metadata(self) -> dict: - return {"num_envs": 4} - - def get_newton_state(self): - return {"ok": True} - - def get_camera_transforms(self): - return {} - - def _raise_marker_render(*args, **kwargs): - raise RuntimeError("marker render failed") - - monkeypatch.setattr(rerun_visualizer, "render_newton_visualization_markers", _raise_marker_render) - - class _FakeNewtonManager: - @staticmethod - def get_model(): - return "dummy-model" - - @staticmethod - def get_state(): - return {"ok": True} - - @staticmethod - def get_num_envs() -> int: - return 4 - - import isaaclab_newton.physics as _np_mod - - monkeypatch.setattr(_np_mod, "NewtonManager", _FakeNewtonManager) - - visualizer = rerun_visualizer.RerunVisualizer(RerunVisualizerCfg()) - viewer = _FakeRerunViewer() - visualizer._is_initialized = True - visualizer._is_closed = False - visualizer._viewer = viewer - visualizer._scene_data_provider = _DummyRerunSceneDataProvider() - visualizer._resolved_visible_env_ids = None - - with pytest.raises(RuntimeError, match="marker render failed"): - visualizer.step(0.25) - - assert [call[0] for call in viewer.calls] == ["begin_frame", "log_state", "end_frame"] - - def test_kit_visualizer_default_camera_source_does_not_require_camera_prim(monkeypatch: pytest.MonkeyPatch): """Default ``--viz kit`` should work for envs without a camera prim.""" @@ -806,6 +509,23 @@ def get_usd_stage(self): assert visualizer._controlled_camera_path == "/OmniverseKit_Persp" +def test_kit_visualizer_default_camera_source_accepts_set_camera_view(monkeypatch: pytest.MonkeyPatch): + """Default Kit visualizer camera follows SimulationContext/ViewportCameraController updates.""" + applied_camera_poses = [] + monkeypatch.setattr( + kit_visualizer.KitVisualizer, + "_set_viewport_camera", + lambda self, eye, target: applied_camera_poses.append((tuple(eye), tuple(target))), + ) + + visualizer = kit_visualizer.KitVisualizer(KitVisualizerCfg()) + visualizer._is_initialized = True + + visualizer.set_camera_view((1.0, 2.0, 3.0), (0.0, 0.0, 1.0)) + + assert applied_camera_poses == [((1.0, 2.0, 3.0), (0.0, 0.0, 1.0))] + + def test_get_cli_visualizer_types_handles_non_string_setting_without_crashing(): ctx = object.__new__(SimulationContext) ctx.get_setting = lambda name: {"types": "newton,kit"} if name == "/isaaclab/visualizer/types" else None diff --git a/source/isaaclab_newton/changelog.d/mtrepte-update-debug-viz.rst b/source/isaaclab_newton/changelog.d/mtrepte-update-debug-viz.rst new file mode 100644 index 000000000000..2ca3ab374223 --- /dev/null +++ b/source/isaaclab_newton/changelog.d/mtrepte-update-debug-viz.rst @@ -0,0 +1,4 @@ +Fixed +^^^^^ + +* Fixed Newton visualization state updates for PhysX-backed simulations. diff --git a/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py b/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py index fb8188ff5909..24a99aba4282 100644 --- a/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py +++ b/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py @@ -1442,7 +1442,7 @@ def get_state_0(cls) -> State: return cls._state_0 @classmethod - def get_state(cls) -> State: + def get_state(cls, scene_data_provider=None) -> State: """Get the current Newton state for visualization. Use this method from visualizers/renderers/video recorders that need a @@ -1453,7 +1453,7 @@ def get_state(cls) -> State: :meth:`update_visualization_state` is a no-op and this is equivalent to :meth:`get_state_0`. """ - cls.update_visualization_state() + cls.update_visualization_state(scene_data_provider) return cls.get_state_0() @classmethod @@ -1461,8 +1461,10 @@ def get_num_envs(cls) -> int: return cls._num_envs @classmethod - def _backend_is_newton(cls) -> bool: + def _backend_is_newton(cls, scene_data_provider=None) -> bool: """Return ``True`` when the active sim backend is Newton.""" + if scene_data_provider is not None: + return isinstance(scene_data_provider.backend, NewtonSceneDataBackend) sim = PhysicsManager._sim if sim is None: return False @@ -1668,7 +1670,7 @@ def _build_visualization_model_from_stage(cls, stage) -> ModelBuilder | None: return builder @classmethod - def update_visualization_state(cls) -> None: + def update_visualization_state(cls, scene_data_provider=None) -> None: """Refresh visualization state for the active sim backend. Newton sim backend: no-op — ``_state_0`` is the live, authoritative state @@ -1683,16 +1685,19 @@ def update_visualization_state(cls) -> None: Invoked lazily from :meth:`get_state` so consumers do not need to coordinate the sync explicitly. """ - if cls._backend_is_newton(): + if cls._backend_is_newton(scene_data_provider): return cls._ensure_visualization_model() if cls._state_0 is None or cls._model is None or cls._state_0.body_q is None: return - sim = PhysicsManager._sim - if sim is None: + sdp = scene_data_provider + if sdp is None: + sim = PhysicsManager._sim + if sim is not None: + sdp = sim.get_scene_data_provider() + if sdp is None: return - sdp = sim.get_scene_data_provider() if cls._visualization_scene_data is None: cls._visualization_scene_data = SceneDataFormat.Transform() if cls._visualization_mapping is None: diff --git a/source/isaaclab_physx/changelog.d/mtrepte-update-debug-viz.skip b/source/isaaclab_physx/changelog.d/mtrepte-update-debug-viz.skip new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py b/source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py index bb23bc130ad5..00e9ed3007d6 100644 --- a/source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py +++ b/source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py @@ -335,7 +335,6 @@ def step(cls) -> None: cls._physx_sim.simulate(sim.cfg.dt, 0.0) cls._physx_sim.fetch_results() - device = PhysicsManager._device if "cuda" in device: torch.cuda.set_device(device) @@ -348,6 +347,7 @@ def play(cls) -> None: cls._timeline.play() # Pump events so timeline callbacks fire synchronously omni.kit.app.get_app().update() + cls._sync_fabric_after_resume() @classmethod def pause(cls) -> None: @@ -377,15 +377,20 @@ def wait_for_playing(cls) -> None: app.update() if cls._timeline.is_stopped(): break - # Force fabric to re-sync articulation transforms after resume. - # detach/attach resets the FabricManager, then we immediately push - # current poses so the first render after resume shows correct state. - if not cls._timeline.is_stopped(): - cls._re_sync_fabric() - if cls._view is not None: - cls._view.update_articulations_kinematic() - if cls._update_fabric is not None: - cls._update_fabric(0.0, 0.0) + cls._sync_fabric_after_resume() + + @classmethod + def _sync_fabric_after_resume(cls) -> None: + """Force Fabric to show current articulation transforms after timeline resume.""" + if cls._timeline.is_stopped(): + return + # detach/attach resets the FabricManager, then immediately push current + # poses so the first render after resume shows correct state. + cls._re_sync_fabric() + if cls._view is not None: + cls._view.update_articulations_kinematic() + if cls._update_fabric is not None: + cls._update_fabric(0.0, 0.0) @classmethod def close(cls) -> None: diff --git a/source/isaaclab_rl/changelog.d/mtrepte-update-debug-viz.skip b/source/isaaclab_rl/changelog.d/mtrepte-update-debug-viz.skip new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/source/isaaclab_tasks/changelog.d/mtrepte-update-debug-viz.skip b/source/isaaclab_tasks/changelog.d/mtrepte-update-debug-viz.skip new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/source/isaaclab_visualizers/changelog.d/mtrepte-update-debug-viz.rst b/source/isaaclab_visualizers/changelog.d/mtrepte-update-debug-viz.rst new file mode 100644 index 000000000000..c26216180202 --- /dev/null +++ b/source/isaaclab_visualizers/changelog.d/mtrepte-update-debug-viz.rst @@ -0,0 +1,5 @@ +Changed +^^^^^^^ + +* Changed Rerun and Viser visualizers to avoid opening browser tabs by default and to show browser URLs in the startup logs instead. +* Changed visualizer initialization tables to debug-level logging to reduce default startup log noise. diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py index 4766fbe7f68f..133c565ccbad 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py @@ -188,17 +188,10 @@ def set_camera_view( ) -> None: """Set active viewport camera eye/target. - When :attr:`self.cfg.cam_source` is ``"cfg"``, this is a no-op: the pose comes only from - :attr:`self.cfg.eye` / :attr:`self.cfg.lookat` (applied in :meth:`_setup_viewport`). Otherwise - :class:`~isaaclab.sim.simulation_context.SimulationContext` and :class:`ViewportCameraController` - would overwrite that pose with :class:`~isaaclab.envs.common.ViewerCfg`-driven views. - Args: eye: Camera eye position. target: Camera look-at target. """ - if self.cfg.cam_source == "cfg": - return if not self._is_initialized: logger.debug("[KitVisualizer] set_camera_view() ignored because visualizer is not initialized.") return diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py index 31c17a7b16d6..565c16b0251b 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py @@ -10,13 +10,13 @@ import logging from typing import TYPE_CHECKING -import numpy as np import warp as wp from newton.viewer import ViewerGL from pyglet.math import Vec3 as PygletVec3 from isaaclab.visualizers.base_visualizer import BaseVisualizer +from isaaclab_visualizers.newton.newton_visualization_markers import render_newton_visualization_markers from isaaclab_visualizers.newton_adapter import apply_viewer_visible_worlds, resolve_visible_env_indices from .newton_visualizer_cfg import NewtonVisualizerCfg @@ -290,7 +290,7 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: metadata = {"num_envs": num_envs} self._env_ids = self._compute_visualized_env_ids() self._model = NewtonManager.get_model() - self._state = NewtonManager.get_state() + self._state = NewtonManager.get_state(self._scene_data_provider) # Use pyglet's EGL headless backend when requested. Must run before the first # ``pyglet.window`` import so ``Window`` resolves to :class:`~pyglet.window.headless.HeadlessWindow`. @@ -316,6 +316,7 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: num_envs=num_envs, ) self._viewer.set_world_offsets((0.0, 0.0, 0.0)) + self._apply_camera_focal_length() initial_pose = self._resolve_initial_camera_pose() self._apply_camera_pose(initial_pose) self._viewer.up_axis = 2 # Z-up @@ -352,6 +353,7 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: tuple(float(x) for x in self._viewer.camera.pos) if self._viewer is not None else self.cfg.eye, ), ("lookat", self._last_camera_pose[1] if self._last_camera_pose else self.cfg.lookat), + ("focal_length", self.cfg.focal_length), ("cam_source", self.cfg.cam_source), ("num_visualized_envs", num_visualized_envs), ("headless", self.cfg.headless), @@ -374,28 +376,35 @@ def step(self, dt: float) -> None: from isaaclab_newton.physics import NewtonManager if self._viewer is None: - self._state = NewtonManager.get_state() + self._state = NewtonManager.get_state(self._scene_data_provider) return if self.cfg.cam_source == "prim_path": self._update_camera_from_usd_path() - self._state = NewtonManager.get_state() + self._state = NewtonManager.get_state(self._scene_data_provider) update_frequency = self._viewer._update_frequency if self._viewer else self._update_frequency if self._step_counter % update_frequency != 0: return + num_envs = NewtonManager.get_num_envs() + try: if not self._viewer.is_paused(): self._viewer.begin_frame(self._sim_time) - if self._state is not None: - body_q = getattr(self._state, "body_q", None) - if hasattr(body_q, "shape") and body_q.shape[0] == 0: - self._viewer.end_frame() - return - self._viewer.log_state(self._state) - self._viewer.end_frame() + try: + if self._state is not None: + body_q = getattr(self._state, "body_q", None) + if hasattr(body_q, "shape") and body_q.shape[0] == 0: + return + self._viewer.log_state(self._state) + if self.cfg.enable_markers: + render_newton_visualization_markers( + self._viewer, self._resolved_visible_env_ids, num_envs=num_envs + ) + finally: + self._viewer.end_frame() else: self._viewer._update() except Exception as exc: @@ -450,16 +459,15 @@ def _apply_camera_pose(self, pose: tuple[tuple[float, float, float], tuple[float cam_pos, cam_target = pose # Match Newton's Camera native pos type: PyVec3, not wp.vec3. self._viewer.camera.pos = PygletVec3(*cam_pos) - cam_pos_np = np.array(cam_pos, dtype=np.float32) - cam_target_np = np.array(cam_target, dtype=np.float32) - direction = cam_target_np - cam_pos_np - yaw = np.degrees(np.arctan2(direction[1], direction[0])) - horizontal_dist = np.sqrt(direction[0] ** 2 + direction[1] ** 2) - pitch = np.degrees(np.arctan2(direction[2], horizontal_dist)) - self._viewer.camera.yaw = float(yaw) - self._viewer.camera.pitch = float(pitch) + self._viewer.camera.look_at(cam_target) self._last_camera_pose = (cam_pos, cam_target) + def _apply_camera_focal_length(self) -> None: + """Apply cfg focal length to Newton's vertical-FOV camera.""" + if self._viewer is None: + return + self._viewer.camera.fov = self._focal_length_to_vertical_fov_degrees() + def _update_camera_from_usd_path(self) -> None: """Refresh camera pose from configured USD camera path when it changes.""" pose = self._resolve_camera_pose_from_usd_path(self.cfg.cam_prim_path) diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py index 7e6f9a00331a..cc55dcef7fa5 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py @@ -8,6 +8,8 @@ from __future__ import annotations import atexit +import contextlib +import inspect import logging import socket import webbrowser @@ -85,15 +87,32 @@ def _open_rerun_web_viewer(host: str, web_port: int, connect_to: str) -> None: def _rerun_web_viewer_url(host: str, web_port: int, connect_to: str) -> str: """Return rerun web UI URL with prefilled endpoint.""" - return f"http://{host}:{int(web_port)}/?url={quote(connect_to, safe='')}" + # Keep the nested URL readable while still encoding '+' in the rerun+http scheme. + return f"http://{host}:{int(web_port)}/?url={quote(connect_to, safe=':/')}" class NewtonViewerRerun(ViewerRerun): """Wrapper around Newton's ViewerRerun with rendering pause controls.""" - def __init__(self, *args, **kwargs): + def __init__(self, *args, open_browser: bool = False, **kwargs): """Initialize viewer wrapper and Isaac Lab pause state.""" - super().__init__(*args, **kwargs) + if open_browser: + super().__init__(*args, **kwargs) + else: + original_serve_web_viewer = rr.serve_web_viewer + + # Rerun Viewer launches a browser automatically, so here we suppress that behavior + def _serve_web_viewer_without_browser(*serve_args, **serve_kwargs): + with contextlib.suppress(TypeError, ValueError): + supports_open_browser = "open_browser" in inspect.signature(original_serve_web_viewer).parameters + if supports_open_browser: + serve_kwargs.setdefault("open_browser", False) + return original_serve_web_viewer(*serve_args, **serve_kwargs) + + with contextlib.ExitStack() as stack: + rr.serve_web_viewer = _serve_web_viewer_without_browser + stack.callback(setattr, rr, "serve_web_viewer", original_serve_web_viewer) + super().__init__(*args, **kwargs) self._paused_rendering = False def is_rendering_paused(self) -> bool: @@ -153,7 +172,7 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: num_envs = scene_data_provider.num_envs self._env_ids = self._compute_visualized_env_ids() self._model = NewtonManager.get_model() - self._state = NewtonManager.get_state() + self._state = NewtonManager.get_state(self._scene_data_provider) grpc_port = int(self.cfg.grpc_port) web_port = int(self.cfg.web_port) @@ -177,11 +196,14 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: keep_historical_data=self.cfg.keep_historical_data, keep_scalar_history=self.cfg.keep_scalar_history, record_to_rrd=self.cfg.record_to_rrd, + open_browser=self.cfg.open_browser, ) if start_server_in_viewer: rerun_address = getattr(self._viewer, "_grpc_server_uri", rerun_address) viewer_host = _normalize_host(bind_address) viewer_url = _rerun_web_viewer_url(viewer_host, web_port, rerun_address) + print() + self._log_viewer_url("RerunVisualizer", viewer_url) if self.cfg.open_browser and not start_server_in_viewer: _open_rerun_web_viewer(viewer_host, web_port, rerun_address) self._viewer.set_model(self._model) @@ -209,10 +231,10 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: rows=[ ("eye", self.cfg.eye), ("lookat", self.cfg.lookat), + ("focal_length", f"{self.cfg.focal_length} (not applied: Rerun EyeControls3D has no FOV field)"), ("cam_source", self.cfg.cam_source), ("num_visualized_envs", num_visualized_envs), ("endpoint", f"http://{viewer_host}:{web_port}"), - ("viewer_url", viewer_url), ("bind_address", bind_address), ("grpc_port", grpc_port), ("web_port", web_port), @@ -241,7 +263,7 @@ def step(self, dt: float) -> None: if self.cfg.cam_source == "prim_path": self._update_camera_from_usd_path() - self._state = NewtonManager.get_state() + self._state = NewtonManager.get_state(self._scene_data_provider) num_envs = NewtonManager.get_num_envs() if not self._viewer.is_paused(): diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer_cfg.py b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer_cfg.py index dad671bf57f7..c3229a1916de 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer_cfg.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer_cfg.py @@ -22,7 +22,7 @@ class RerunVisualizerCfg(VisualizerCfg): """Application identifier shown in viewer title.""" web_port: int = 9090 - """Port of the local rerun web viewer which is launched in the browser.""" + """Port of the local rerun web viewer whose URL is logged during initialization.""" grpc_port: int = 9876 """Port of the rerun gRPC server (used when serving web viewer externally).""" @@ -36,8 +36,11 @@ class RerunVisualizerCfg(VisualizerCfg): - Local browser links normalize common loopback/wildcard hosts to ``127.0.0.1``. """ - open_browser: bool = True - """Whether to attempt opening the rerun web viewer URL in a browser.""" + open_browser: bool = False + """Whether to attempt opening the rerun web viewer URL in a browser. + + The viewer URL is always logged during initialization. Set this to ``True`` to auto-launch it. + """ keep_historical_data: bool = False """Keep transform history for time scrubbing (False = constant memory for training).""" diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py index b3569b04dbf3..5848690263f1 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py @@ -10,6 +10,7 @@ import contextlib import io import logging +import math import os import webbrowser from pathlib import Path @@ -46,26 +47,8 @@ def _disable_viser_runtime_client_rebuild_if_bundled() -> None: client_autobuild.ensure_client_is_built = lambda: None -@contextlib.contextmanager -def _suppress_viser_startup_logs(enabled: bool): - """Temporarily quiet noisy viser/websockets startup output.""" - if not enabled: - yield - return - - websockets_logger = logging.getLogger("websockets.server") - previous_level = websockets_logger.level - websockets_logger.setLevel(logging.WARNING) - try: - with contextlib.redirect_stdout(io.StringIO()), contextlib.redirect_stderr(io.StringIO()): - yield - finally: - websockets_logger.setLevel(previous_level) - - -def _open_viser_web_viewer(port: int) -> None: - """Open the local viser web UI in a browser.""" - url = _viser_web_viewer_url(port) +def _open_viser_web_viewer(url: str) -> None: + """Open the Viser web UI in a browser.""" try: if not webbrowser.open_new_tab(url): logger.info("[ViserVisualizer] Could not auto-open browser tab. Open manually: %s", url) @@ -73,9 +56,9 @@ def _open_viser_web_viewer(port: int) -> None: logger.info("[ViserVisualizer] Could not auto-open browser tab. Open manually: %s", url) -def _viser_web_viewer_url(port: int) -> str: - """Return local viser web UI URL.""" - return f"http://localhost:{int(port)}" +def _viser_web_viewer_url(port: int, display_address: str) -> str: + """Return Viser web UI URL for display to users.""" + return f"http://{display_address}:{int(port)}" class NewtonViewerViser(ViewerViser): @@ -84,6 +67,7 @@ class NewtonViewerViser(ViewerViser): def __init__( self, port: int = 8080, + bind_address: str = "0.0.0.0", label: str | None = None, verbose: bool = True, share: bool = False, @@ -94,6 +78,7 @@ def __init__( Args: port: HTTP port for viser server. + bind_address: Host/interface for the Viser server to bind. label: Optional viewer label. verbose: Whether to keep verbose startup output enabled. share: Whether to enable sharing/tunneling. @@ -101,15 +86,34 @@ def __init__( metadata: Optional metadata attached to the viewer. """ _disable_viser_runtime_client_rebuild_if_bundled() - super().__init__( - port=port, - label=label, - verbose=verbose, - share=share, - record_to_viser=record_to_viser, - ) + viser = self._get_viser() + original_viser_server = viser.ViserServer + + def _viser_server_with_bind_address(*args, **kwargs): + kwargs["host"] = bind_address + kwargs["verbose"] = verbose + return original_viser_server(*args, **kwargs) + + with contextlib.ExitStack() as stack: + viser.ViserServer = _viser_server_with_bind_address + stack.callback(setattr, viser, "ViserServer", original_viser_server) + if not verbose: + stack.enter_context(contextlib.redirect_stdout(io.StringIO())) + stack.enter_context(contextlib.redirect_stderr(io.StringIO())) + super().__init__( + port=port, + label=label, + verbose=verbose, + share=share, + record_to_viser=record_to_viser, + ) self._metadata = metadata or {} + @property + def share_url(self) -> str | None: + """Return the public share URL created by Viser, if any.""" + return self._share_url + class ViserVisualizer(BaseVisualizer): """Viser web-based visualizer backed by Newton's ViewerViser.""" @@ -152,7 +156,7 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: metadata = {"num_envs": num_envs} self._env_ids = self._compute_visualized_env_ids() self._model = NewtonManager.get_model() - self._state = NewtonManager.get_state() + self._state = NewtonManager.get_state(self._scene_data_provider) self._active_record_path = self.cfg.record_to_viser self._create_viewer(record_to_viser=self.cfg.record_to_viser, metadata=metadata) @@ -160,17 +164,18 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: num_visualized_envs = ( len(self._resolved_visible_env_ids) if self._resolved_visible_env_ids is not None else num_envs ) - viewer_url = _viser_web_viewer_url(self.cfg.port) self._log_initialization_table( logger=logger, title="ViserVisualizer Configuration", rows=[ ("eye", self.cfg.eye), ("lookat", self.cfg.lookat), + ("focal_length", self.cfg.focal_length), ("cam_source", self.cfg.cam_source), ("num_visualized_envs", num_visualized_envs), + ("bind_address", self.cfg.bind_address), + ("display_address", self.cfg.display_address), ("port", self.cfg.port), - ("viewer_url", viewer_url), ("record_to_viser", self.cfg.record_to_viser or ""), ], ) @@ -191,7 +196,7 @@ def step(self, dt: float) -> None: self._update_camera_from_usd_path() self._apply_pending_camera_pose() - self._state = NewtonManager.get_state() + self._state = NewtonManager.get_state(self._scene_data_provider) num_envs = NewtonManager.get_num_envs() self._sim_time += dt @@ -266,14 +271,21 @@ def _create_viewer(self, record_to_viser: str | None, metadata: dict | None = No if self._model is None: raise RuntimeError("Viser visualizer requires a Newton model.") - with _suppress_viser_startup_logs(enabled=not self.cfg.verbose): - self._viewer = NewtonViewerViser( - port=self.cfg.port, - label=self.cfg.label, - verbose=self.cfg.verbose, - share=self.cfg.share, - record_to_viser=record_to_viser, - metadata=metadata or {}, + self._viewer = NewtonViewerViser( + port=self.cfg.port, + bind_address=self.cfg.bind_address, + label=self.cfg.label, + verbose=False, + share=self.cfg.share, + record_to_viser=record_to_viser, + metadata=metadata or {}, + ) + viewer_url = self._viewer.share_url or _viser_web_viewer_url(self.cfg.port, self.cfg.display_address) + if self.cfg.verbose: + print() + self._log_viewer_url( + "ViserVisualizer", + viewer_url, ) num_envs = int((metadata or {}).get("num_envs", 0)) self._viewer.set_model(self._model) @@ -286,7 +298,7 @@ def _create_viewer(self, record_to_viser: str | None, metadata: dict | None = No # Preserve simulation world positions (env_spacing) rather than adding viewer-side offsets. self._viewer.set_world_offsets((0.0, 0.0, 0.0)) if self.cfg.open_browser: - _open_viser_web_viewer(self.cfg.port) + _open_viser_web_viewer(viewer_url) initial_pose = self._resolve_initial_camera_pose() self._set_viser_camera_view(initial_pose) self._sim_time = 0.0 @@ -336,12 +348,16 @@ def _try_apply_viser_camera_view(self, pose: tuple[tuple[float, float, float], t client_iterable = clients.values() if isinstance(clients, dict) else clients cam_pos, cam_target = pose + fov_radians = math.radians(self._focal_length_to_vertical_fov_degrees()) applied = False for client in client_iterable: camera = getattr(client, "camera", None) if camera is None: continue try: + if hasattr(camera, "fov"): + camera.fov = fov_radians + applied = True if hasattr(camera, "position"): camera.position = cam_pos applied = True diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer_cfg.py b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer_cfg.py index 68ab3116b45a..b039924d392b 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer_cfg.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer_cfg.py @@ -21,8 +21,23 @@ class ViserVisualizerCfg(VisualizerCfg): port: int = 8080 """Port of the local viser web server.""" - open_browser: bool = True - """Whether to attempt opening the viser web viewer URL in a browser.""" + bind_address: str = "0.0.0.0" + """Host/interface for the Viser server to bind. + + Use ``"0.0.0.0"`` to listen on all interfaces for remote access. + """ + + display_address: str = "localhost" + """Host name or IP address shown in the printed browser URL. + + For remote access, set this to the hostname/IP reachable from your browser. + """ + + open_browser: bool = False + """Whether to attempt opening the viser web viewer URL in a browser. + + The viewer URL is always logged during initialization. Set this to ``True`` to auto-launch it. + """ label: str | None = "Isaac Lab Simulation" """Optional label shown in the viewer page title.""" diff --git a/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py b/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py index 79bad6ea1459..bcdeac5e2419 100644 --- a/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py +++ b/source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py @@ -522,6 +522,7 @@ def test_cartpole_newton_visualizer_tiled_camera_rgb_non_black( @pytest.mark.isaacsim_ci +@pytest.mark.skip(reason="ViewerGL frame motion is flaky on the current pinned Isaac Sim CI image.") @pytest.mark.parametrize("backend_kind", ["physx", "newton"]) def test_cartpole_newton_visualizer_viewergl_rgb_motion(backend_kind: str, caplog: pytest.LogCaptureFixture) -> None: """Newton GL (``ViewerGL.get_frame``): full motion steps, last frame non-black; early vs late differ; logs.""" From f605357f1d0a68bb7f3f9a8ca06972fc6e858025 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Wed, 20 May 2026 01:47:05 +0000 Subject: [PATCH 26/31] prep --- docs/source/features/visualization.rst | 8 ++- .../isaaclab/isaaclab/sim/simulation_cfg.py | 3 + .../isaaclab/sim/simulation_context.py | 15 ++++- .../isaaclab/visualizers/visualizer_cfg.py | 4 +- .../test_simulation_context_visualizers.py | 25 +++++++++ .../renderers/newton_warp_renderer.py | 56 ++----------------- .../manipulation/dexsuite/dexsuite_env_cfg.py | 1 + .../kit/kit_visualizer.py | 4 +- .../newton/newton_visualizer.py | 4 +- 9 files changed, 57 insertions(+), 63 deletions(-) diff --git a/docs/source/features/visualization.rst b/docs/source/features/visualization.rst index 7e1a807188f7..cd5a324af8b7 100644 --- a/docs/source/features/visualization.rst +++ b/docs/source/features/visualization.rst @@ -187,8 +187,8 @@ Also, there is a CLI arg ``--max_visible_envs`` that overrides ``VisualizerCfg.m - any - Run headless; ``--headless`` takes precedence. -Camera Pose Behavior -~~~~~~~~~~~~~~~~~~~~ +Camera Modes +~~~~~~~~~~~~ The default visualizer camera mode is interactive, with ``eye`` and ``lookat`` specifying the initial pose. Kit and Newton visualizers can also run additional tiled camera image panels. @@ -397,7 +397,9 @@ Newton Visualizer tiled_cam_env_indices=None, # Optional explicit env ids to show in the tiled view tiled_cam_prim_path=None, # Existing Camera sensor prim path, e.g. "/World/envs/*/Camera" tiled_cam_eye=(4.0, -4.0, 3.0), # Eye offset for generated tiled cameras - tiled_cam_target_prim_path="/World/envs/*/Robot/base", # Prim that generated cameras follow/look at + tiled_cam_target_prim_path=( # Prim that generated cameras follow/look at + "/World/envs/*/Robot/base" + ), # Performance tuning update_frequency=1, # Update every N frames (1=every frame) diff --git a/source/isaaclab/isaaclab/sim/simulation_cfg.py b/source/isaaclab/isaaclab/sim/simulation_cfg.py index 2c71c724ad3d..695b1a64959a 100644 --- a/source/isaaclab/isaaclab/sim/simulation_cfg.py +++ b/source/isaaclab/isaaclab/sim/simulation_cfg.py @@ -335,3 +335,6 @@ class SimulationCfg: visualizer_cfgs: list[VisualizerCfg] | VisualizerCfg = [] """The visualizer configuration(s). Default is an empty list.""" + + default_visualizer_cfg: VisualizerCfg = VisualizerCfg() + """Default visualizer settings applied to backend configs created from explicit CLI visualizer requests.""" diff --git a/source/isaaclab/isaaclab/sim/simulation_context.py b/source/isaaclab/isaaclab/sim/simulation_context.py index e035d3f9e94a..9d1dff664389 100644 --- a/source/isaaclab/isaaclab/sim/simulation_context.py +++ b/source/isaaclab/isaaclab/sim/simulation_context.py @@ -11,6 +11,7 @@ import traceback from collections.abc import Iterator from contextlib import contextmanager +from dataclasses import fields from typing import TYPE_CHECKING, Any import toml @@ -441,7 +442,9 @@ def _create_default_visualizer_configs(self, requested_visualizers: list[str]) - continue mod = importlib.import_module(f"isaaclab_visualizers.{viz_type}") cfg_cls = getattr(mod, cfg_class_names[viz_type]) - default_configs.append(cfg_cls()) + cfg = cfg_cls() + self._apply_default_visualizer_cfg(cfg) + default_configs.append(cfg) except (ImportError, ModuleNotFoundError) as exc: # isaaclab_visualizers is optional; log once at warning level if "isaaclab_visualizers" in str(exc): @@ -461,6 +464,16 @@ def _create_default_visualizer_configs(self, requested_visualizers: list[str]) - logger.error(f"[SimulationContext] Failed to create default config for visualizer '{viz_type}': {exc}") return default_configs + def _apply_default_visualizer_cfg(self, cfg: Any) -> None: + """Apply shared default visualizer settings to a backend-specific config.""" + default_cfg = getattr(self.cfg, "default_visualizer_cfg", None) + if default_cfg is None: + return + for field in fields(default_cfg): + if field.name == "visualizer_type" or not hasattr(cfg, field.name): + continue + setattr(cfg, field.name, getattr(default_cfg, field.name)) + def _get_cli_visualizer_types(self) -> list[str]: """Return list of visualizer types requested via CLI (setting).""" requested = self.get_setting("/isaaclab/visualizer/types") diff --git a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py index 9d09ae3228de..1c441944591c 100644 --- a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py +++ b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py @@ -43,10 +43,10 @@ class VisualizerCfg: focal_length: float = 15.0 """Camera focal length in millimeters for visualizer camera views.""" - tiled_cam_view: bool = False + tiled_cam_view: bool = True """Enable a non-interactive tiled camera image view.""" - tiled_cam_num: int = 57 + tiled_cam_num: int = 16 """Number of camera tiles to show when tiled_cam_env_indices is None, capped at 100.""" tiled_cam_env_indices: list[int] | None = None diff --git a/source/isaaclab/test/sim/test_simulation_context_visualizers.py b/source/isaaclab/test/sim/test_simulation_context_visualizers.py index d7645ec6be2a..094975033ab6 100644 --- a/source/isaaclab/test/sim/test_simulation_context_visualizers.py +++ b/source/isaaclab/test/sim/test_simulation_context_visualizers.py @@ -15,10 +15,12 @@ import isaaclab_visualizers.viser.viser_visualizer as viser_visualizer import pytest from isaaclab_visualizers.kit.kit_visualizer_cfg import KitVisualizerCfg +from isaaclab_visualizers.newton.newton_visualizer_cfg import NewtonVisualizerCfg from isaaclab_visualizers.rerun.rerun_visualizer_cfg import RerunVisualizerCfg from isaaclab_visualizers.viser.viser_visualizer_cfg import ViserVisualizerCfg from isaaclab.sim.simulation_context import SimulationContext +from isaaclab.visualizers.visualizer_cfg import VisualizerCfg def test_web_visualizer_cfgs_do_not_open_browser_by_default(): @@ -560,6 +562,7 @@ def initialize(self, provider): def _make_context_with_settings( settings: dict, visualizer_cfgs=None, + default_visualizer_cfg=None, *, has_gui: bool = False, has_offscreen_render: bool = False, @@ -575,6 +578,7 @@ def _make_context_with_settings( (), { "visualizer_cfgs": visualizer_cfgs, + "default_visualizer_cfg": default_visualizer_cfg, "physics": type("PhysicsCfg", (), {"dt": 0.01})(), "dt": 0.01, "render_interval": 1, @@ -596,6 +600,27 @@ def _make_context_with_settings( return ctx +def test_default_visualizer_cfg_applies_to_cli_created_configs(): + settings = { + "/isaaclab/visualizer/types": "newton", + "/isaaclab/visualizer/explicit": True, + "/isaaclab/visualizer/disable_all": False, + "/isaaclab/visualizer/max_visible_envs": None, + } + default_cfg = VisualizerCfg( + tiled_cam_target_prim_path="/World/envs/*/Object", + tiled_cam_eye=(1.0, -1.0, 0.5), + ) + ctx = _make_context_with_settings(settings, default_visualizer_cfg=default_cfg) + + cfgs = ctx._resolve_visualizer_cfgs() + + assert len(cfgs) == 1 + assert isinstance(cfgs[0], NewtonVisualizerCfg) + assert cfgs[0].tiled_cam_target_prim_path == "/World/envs/*/Object" + assert cfgs[0].tiled_cam_eye == (1.0, -1.0, 0.5) + + def test_is_rendering_true_when_only_cfg_visualizer_is_set(): cfg_visualizer = type("CfgVisualizer", (), {"visualizer_type": "newton"})() settings = { diff --git a/source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py b/source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py index a9d521acc894..cfa5aa2614ad 100644 --- a/source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py +++ b/source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py @@ -7,7 +7,6 @@ from __future__ import annotations -import inspect import logging from dataclasses import dataclass from typing import TYPE_CHECKING, Any @@ -119,12 +118,9 @@ def update(self, positions: ProxyArray, orientations: ProxyArray, intrinsics: Pr fov_radians_all = 2.0 * torch.atan(self.height / (2.0 * first_focal_length)) fov_warp = wp.from_torch(fov_radians_all, dtype=wp.float32) - if hasattr(self.newton_sensor, "utils"): - self.camera_rays = self.newton_sensor.utils.compute_pinhole_camera_rays( - self.width, self.height, fov_warp - ) - else: - self.camera_rays = self.newton_sensor.compute_pinhole_camera_rays(self.width, self.height, fov_warp) + self.camera_rays = self.newton_sensor.utils.compute_pinhole_camera_rays( + self.width, self.height, fov_warp + ) @wp.kernel def _update_transforms( @@ -189,49 +185,7 @@ def initialize(self) -> None: newton.geometry.build_bvh_shape(self._newton_model, self._newton_model.state()) if self.cfg.create_default_light: - if hasattr(self.newton_sensor, "utils"): - self.newton_sensor.utils.create_default_light(enable_shadows=self.cfg.enable_shadows) - elif hasattr(self.newton_sensor, "create_default_light"): - self.newton_sensor.create_default_light(enable_shadows=self.cfg.enable_shadows) - - def _create_sensor_config(self, sensor_cls): - """Create a tiled-camera config compatible with the loaded Newton package.""" - if hasattr(sensor_cls, "RenderConfig"): - config_cls = sensor_cls.RenderConfig - requested = { - "enable_textures": self.cfg.enable_textures, - "enable_shadows": self.cfg.enable_shadows, - "enable_ambient_lighting": self.cfg.enable_ambient_lighting, - "enable_backface_culling": self.cfg.enable_backface_culling, - "max_distance": self.cfg.max_distance, - } - else: - config_cls = sensor_cls.Config - requested = { - "default_light": self.cfg.create_default_light, - "default_light_shadows": self.cfg.enable_shadows, - "enable_ambient_lighting": self.cfg.enable_ambient_lighting, - "enable_textures": self.cfg.enable_textures, - "backface_culling": self.cfg.enable_backface_culling, - } - params = inspect.signature(config_cls).parameters - return config_cls(**{name: value for name, value in requested.items() if name in params}) - - def _apply_render_context_config(self) -> None: - """Fill renderer options that older Newton config objects do not accept.""" - render_config = getattr(getattr(self.newton_sensor, "render_context", None), "config", None) - if render_config is None: - return - optional_values = { - "enable_textures": self.cfg.enable_textures, - "enable_shadows": self.cfg.enable_shadows, - "enable_ambient_lighting": self.cfg.enable_ambient_lighting, - "enable_backface_culling": self.cfg.enable_backface_culling, - "max_distance": self.cfg.max_distance, - } - for name, value in optional_values.items(): - if hasattr(render_config, name): - setattr(render_config, name, value) + self.newton_sensor.utils.create_default_light(enable_shadows=self.cfg.enable_shadows) def supported_output_types(self) -> dict[RenderBufferKind, RenderBufferSpec]: """Publish the per-output layout this Newton Warp backend writes. @@ -287,7 +241,7 @@ def render(self, render_data: RenderData): # Refit the shape BVH against the current state since env body poses move every frame. # ``build_bvh_shape`` ran once in ``__init__``; ``refit_bvh_shape`` reuses that topology. - if self.newton_sensor.model.shape_count > 0 and hasattr(newton.geometry, "refit_bvh_shape"): + if self.newton_sensor.model.shape_count > 0: newton.geometry.refit_bvh_shape(self.newton_sensor.model, newton_state) self.newton_sensor.update( diff --git a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/dexsuite_env_cfg.py b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/dexsuite_env_cfg.py index eeea53b48415..1fe37f62c81b 100644 --- a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/dexsuite_env_cfg.py +++ b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/dexsuite_env_cfg.py @@ -490,6 +490,7 @@ def __post_init__(self): """Post initialization.""" # general settings self.decimation = 2 # 50 Hz + self.sim.default_visualizer_cfg.tiled_cam_target_prim_path = "/World/envs/*/Object" # *single-goal setup self.commands.object_pose.resampling_time_range = (2.0, 3.0) diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py index 143e34f6c085..2145af34be02 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py @@ -166,9 +166,7 @@ def close(self) -> None: return self._restore_env_visibility() if self._camera_sensor is not None and self._camera_is_owned: - cleanup = getattr(self._camera_sensor, "__del__", None) - if callable(cleanup): - cleanup() + self._camera_sensor.__del__() remove_generated_prims(self._generated_camera_prim_paths) self._camera_sensor = None self._camera_image_provider = None diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py index 68e2e63858ca..77fa17fa29d7 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py @@ -486,9 +486,7 @@ def close(self) -> None: if self._viewer is not None: self._viewer = None if self._camera_sensor is not None and self._camera_is_owned: - cleanup = getattr(self._camera_sensor, "__del__", None) - if callable(cleanup): - cleanup() + self._camera_sensor.__del__() remove_generated_prims(self._generated_camera_prim_paths) self._camera_sensor = None self._is_closed = True From 3896938e408add7a8042ad4652af2ef22a837503 Mon Sep 17 00:00:00 2001 From: Mike Yan Michelis <46975745+mmichelis@users.noreply.github.com> Date: Wed, 20 May 2026 03:47:17 +0200 Subject: [PATCH 27/31] Add experimental Newton deformable support with rigid-deformable coupling (#5443) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Introduces experimental Newton-backend deformable simulation alongside the existing PhysX deformables, refactors deformable APIs to be backend-neutral, and adds the manager-based Franka soft-body lifting environment used to validate two-way coupling. ### Backend-neutral deformable APIs (`isaaclab`) - Moved deformable schemas, materials, spawners, and `DeformableObjectCfg` out of `isaaclab_physx` into `isaaclab.sim` / `isaaclab.assets` so user code imports a single backend-agnostic API. - **Breaking (PhysX):** `isaaclab_physx.sim.DeformableBodyPropertiesCfg`, `DeformableObjectSpawnerCfg`, `DeformableBodyMaterialCfg`, `SurfaceDeformableBodyMaterialCfg`, `spawn_deformable_body_material`, `define/modify_deformable_body_properties` and `isaaclab_physx.assets.DeformableObjectCfg` are now imported from `isaaclab.sim` / `isaaclab.assets`. See `docs/source/migration/migrating_deformables.rst`. - USD spawners now support assets that embed tetrahedral mesh data, with automatic surface-mesh extraction. - Added `pytetwild` dependency for tet-mesh generation. ### Newton manager abstraction (`isaaclab_newton`) - Added deformable registration hooks to Newton cloning + Fabric sync fixes for particle meshes and particle-only scenes. ### Experimental Newton deformables (`isaaclab_contrib.deformable`) - `DeformableObject` (Newton backend), `VBDSolverCfg`, and coupled solver configs `CoupledMJWarpVBDSolverCfg` / `CoupledFeatherstoneVBDSolverCfg` providing one- and two-way rigid–deformable coupling. - New docs: `docs/source/features/newton-physics-integration/using-vbd-solver.rst` (VBD solver tuning + Franka soft-body lift guidance). ### Tasks, demos, tutorials - Added `Isaac-Lift-Soft-Franka-v0` (manager-based), the documented rigid–deformable coupling task. - Updated `scripts/demos/deformables.py`, the `01_assets/run_deformable_object` tutorial, and `00_sim/spawn_prims` to use the backend-neutral API with selectable PhysX or Newton backends. - Added IK + video-record state-machine demo `scripts/environments/state_machine/lift_franka_soft.py`. Continues on #5439 adding VBD solver and coupled MJWarp + VBD solver as newton managers in isaaclab_contrib. Fixes #5285 ## Type of change - New feature (non-breaking change which adds functionality) - Breaking change: Moved deformable schemas, materials, spawners, and `DeformableObjectCfg` out of `isaaclab_physx` into `isaaclab.sim` / `isaaclab.assets` so user code imports a single backend-agnostic API. ## Test plan - [x] `./isaaclab.sh -p -m pytest source/isaaclab/test/sim/test_deformable_backend_split.py` - [x] `./isaaclab.sh -p -m pytest source/isaaclab_physx/test/assets/test_deformable_object.py` - [x] `./isaaclab.sh -p -m pytest source/isaaclab_contrib/test/deformable/` - [x] Run `scripts/demos/deformables.py` against PhysX and Newton backends - [x] Run `Isaac-Lift-Soft-Franka-v0` training for sanity-check rollout ## Checklist - [x] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [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 - [x] 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: Mike Yan Michelis <46975745+mmichelis@users.noreply.github.com> Signed-off-by: Kelly Guo Co-authored-by: Copilot Co-authored-by: Donglai Co-authored-by: Anka Chen Co-authored-by: Kelly Guo Co-authored-by: ooctipus Co-authored-by: mingxueg --- .github/workflows/license-exceptions.json | 5 + docs/conf.py | 1 + docs/source/_static/css/custom.css | 7 + .../newton/franka-mjwarp-vbd-coupling.png | Bin 0 -> 1309804 bytes docs/source/api/index.rst | 48 +- docs/source/api/lab/isaaclab.assets.rst | 36 + docs/source/api/lab/isaaclab.sim.schemas.rst | 21 +- docs/source/api/lab/isaaclab.sim.spawners.rst | 56 +- .../isaaclab_contrib.deformable.rst | 73 ++ .../isaaclab_experimental.envs.rst | 4 + .../isaaclab_experimental.managers.rst | 4 + .../isaaclab_experimental.utils.rst | 4 + .../api/lab_newton/isaaclab_newton.assets.rst | 79 ++ .../lab_newton/isaaclab_newton.physics.rst | 100 ++ .../isaaclab_newton.sim.schemas.rst | 12 + .../isaaclab_newton.sim.spawners.rst | 33 + .../lab_ovphysx/isaaclab_ovphysx.assets.rst | 4 + .../lab_ovphysx/isaaclab_ovphysx.cloner.rst | 4 + .../lab_ovphysx/isaaclab_ovphysx.physics.rst | 4 + .../api/lab_physx/isaaclab_physx.assets.rst | 13 +- .../lab_physx/isaaclab_physx.sim.schemas.rst | 22 +- .../lab_physx/isaaclab_physx.sim.spawners.rst | 48 +- .../migration/migrating_deformables.rst | 96 +- .../migration/migrating_to_isaaclab_3-0.rst | 43 +- .../multi_backend_architecture.rst | 8 + .../physical-backends/newton/index.rst | 12 +- .../newton/mjwarp-solver.rst | 4 +- .../newton/newton-manager-abstraction.rst | 237 +++++ .../newton/supported-features.rst | 22 +- .../newton/using-vbd-solver.rst | 344 +++++++ .../newton/warp-environments.rst | 9 +- docs/source/overview/environments.rst | 15 + docs/source/overview/showroom.rst | 5 +- docs/source/tutorials/00_sim/spawn_prims.rst | 3 +- .../01_assets/run_deformable_object.rst | 39 +- scripts/demos/deformables.py | 121 ++- .../state_machine/lift_franka_soft.py | 382 +++++++ scripts/tutorials/00_sim/spawn_prims.py | 7 +- .../01_assets/run_deformable_object.py | 56 +- .../mym-deformable-backend-split.major.rst | 19 + .../mym-deformable_experimental.minor.rst | 34 + source/isaaclab/isaaclab/assets/__init__.pyi | 12 + .../assets/deformable_object/__init__.py | 10 + .../assets/deformable_object/__init__.pyi | 18 + .../base_deformable_object.py | 371 +++++++ .../base_deformable_object_data.py | 136 +++ .../deformable_object/deformable_object.py | 27 + .../deformable_object_cfg.py | 0 .../deformable_object_data.py | 25 + .../isaaclab/scene/interactive_scene.py | 6 +- source/isaaclab/isaaclab/sim/__init__.py | 7 +- source/isaaclab/isaaclab/sim/__init__.pyi | 28 +- .../isaaclab/isaaclab/sim/schemas/__init__.py | 3 +- .../isaaclab/sim/schemas/__init__.pyi | 8 + .../isaaclab/isaaclab/sim/schemas/schemas.py | 364 ++++++- .../isaaclab/sim/schemas/schemas_cfg.py | 14 +- .../isaaclab/sim/spawners/__init__.pyi | 21 +- .../sim/spawners/from_files/from_files.py | 16 +- .../sim/spawners/from_files/from_files_cfg.py | 5 +- .../sim/spawners/materials/__init__.py | 4 + .../sim/spawners/materials/__init__.pyi | 11 +- .../spawners/materials/physics_materials.py | 50 +- .../materials/physics_materials_cfg.py | 27 +- .../isaaclab/sim/spawners/meshes/__init__.pyi | 8 +- .../isaaclab/sim/spawners/meshes/meshes.py | 27 +- .../sim/spawners/meshes/meshes_cfg.py | 19 +- .../isaaclab/sim/spawners/spawner_cfg.py | 22 + .../sim/spawners/wrappers/wrappers_cfg.py | 19 +- source/isaaclab/setup.py | 2 + .../test/sim/test_deformable_backend_split.py | 131 +++ source/isaaclab/test/sim/test_schemas.py | 16 - source/isaaclab/test/sim/test_schemas_shim.py | 31 +- source/isaaclab/test/sim/test_spawn_meshes.py | 15 +- .../mym-deformable-backend-split.skip | 1 + .../mym-deformable_experimental.minor.rst | 16 + .../isaaclab_contrib/deformable/__init__.py | 13 + .../isaaclab_contrib/deformable/__init__.pyi | 22 + .../coupled_featherstone_vbd_manager.py | 509 +++++++++ .../deformable/coupled_mjwarp_vbd_manager.py | 452 ++++++++ .../deformable/deformable_object.py | 962 ++++++++++++++++++ .../deformable/deformable_object_data.py | 206 ++++ .../isaaclab_contrib/deformable/kernels.py | 384 +++++++ .../deformable/newton_manager_cfg.py | 211 ++++ .../deformable/vbd_manager.py | 285 ++++++ .../test_deformable_builder_hooks.py | 100 ++ .../test/deformable/test_deformable_object.py | 717 +++++++++++++ .../test_rigid_deformable_coupling.py | 332 ++++++ .../mym-deformable-backend-split.minor.rst | 4 + .../mym-deformable_experimental.minor.rst | 33 + .../isaaclab_newton/assets/__init__.pyi | 3 + .../assets/articulation/articulation.py | 6 + .../assets/deformable_object/__init__.py | 8 + .../assets/deformable_object/__init__.pyi | 12 + .../cloner/newton_replicate.py | 31 + .../physics/newton_collision_cfg.py | 8 +- .../isaaclab_newton/physics/newton_manager.py | 192 +++- .../isaaclab_newton/sim/__init__.pyi | 14 +- .../isaaclab_newton/sim/schemas/__init__.py | 15 +- .../isaaclab_newton/sim/schemas/__init__.pyi | 13 + .../sim/schemas/schemas_cfg.py | 19 + .../isaaclab_newton/sim/spawners/__init__.py | 10 + .../isaaclab_newton/sim/spawners/__init__.pyi | 10 + .../sim/spawners/materials/__init__.py | 10 + .../sim/spawners/materials/__init__.pyi | 18 + .../spawners/materials/physics_materials.py | 10 + .../materials/physics_materials_cfg.py | 79 ++ .../mym-deformable-backend-split.minor.rst | 11 + .../mym-deformable_experimental.major.rst | 24 + .../assets/deformable_object/__init__.pyi | 3 +- .../deformable_object/deformable_object.py | 38 +- .../deformable_object_data.py | 6 +- .../isaaclab_physx/sim/__init__.pyi | 12 +- .../isaaclab_physx/sim/schemas/__init__.py | 2 +- .../isaaclab_physx/sim/schemas/__init__.pyi | 6 +- .../isaaclab_physx/sim/schemas/schemas.py | 204 +--- .../isaaclab_physx/sim/schemas/schemas_cfg.py | 77 +- .../isaaclab_physx/sim/spawners/__init__.py | 2 +- .../isaaclab_physx/sim/spawners/__init__.pyi | 8 +- .../sim/spawners/materials/__init__.py | 2 +- .../sim/spawners/materials/__init__.pyi | 8 +- .../spawners/materials/physics_materials.py | 80 -- .../materials/physics_materials_cfg.py | 139 +-- .../sim/spawners/spawner_cfg.py | 39 - .../test/assets/test_deformable_object.py | 50 +- .../test/sim/test_spawn_materials.py | 28 +- .../test/sim/test_spawn_meshes.py | 58 +- .../mym-deformable-backend-split.rst | 6 + .../mym-deformable_experimental.minor.rst | 6 + .../lift/config/franka/ik_abs_env_cfg.py | 6 +- .../manipulation/lift_franka_soft/__init__.py | 33 + .../lift_franka_soft/agents/__init__.py | 4 + .../lift_franka_soft/agents/rsl_rl_ppo_cfg.py | 38 + .../lift_franka_soft/franka_cloth_env_cfg.py | 242 +++++ .../lift_franka_soft/franka_soft_env_cfg.py | 448 ++++++++ .../lift_franka_soft/mdp/__init__.py | 10 + .../lift_franka_soft/mdp/__init__.pyi | 28 + .../lift_franka_soft/mdp/observations.py | 115 +++ .../lift_franka_soft/mdp/rewards.py | 167 +++ 138 files changed, 9148 insertions(+), 944 deletions(-) create mode 100644 docs/source/_static/newton/franka-mjwarp-vbd-coupling.png create mode 100644 docs/source/api/lab_contrib/isaaclab_contrib.deformable.rst create mode 100644 docs/source/api/lab_experimental/isaaclab_experimental.envs.rst create mode 100644 docs/source/api/lab_experimental/isaaclab_experimental.managers.rst create mode 100644 docs/source/api/lab_experimental/isaaclab_experimental.utils.rst create mode 100644 docs/source/api/lab_newton/isaaclab_newton.sim.spawners.rst create mode 100644 docs/source/api/lab_ovphysx/isaaclab_ovphysx.assets.rst create mode 100644 docs/source/api/lab_ovphysx/isaaclab_ovphysx.cloner.rst create mode 100644 docs/source/api/lab_ovphysx/isaaclab_ovphysx.physics.rst create mode 100644 docs/source/overview/core-concepts/physical-backends/newton/newton-manager-abstraction.rst create mode 100644 docs/source/overview/core-concepts/physical-backends/newton/using-vbd-solver.rst create mode 100644 scripts/environments/state_machine/lift_franka_soft.py create mode 100644 source/isaaclab/changelog.d/mym-deformable-backend-split.major.rst create mode 100644 source/isaaclab/changelog.d/mym-deformable_experimental.minor.rst create mode 100644 source/isaaclab/isaaclab/assets/deformable_object/__init__.py create mode 100644 source/isaaclab/isaaclab/assets/deformable_object/__init__.pyi create mode 100644 source/isaaclab/isaaclab/assets/deformable_object/base_deformable_object.py create mode 100644 source/isaaclab/isaaclab/assets/deformable_object/base_deformable_object_data.py create mode 100644 source/isaaclab/isaaclab/assets/deformable_object/deformable_object.py rename source/{isaaclab_physx/isaaclab_physx => isaaclab/isaaclab}/assets/deformable_object/deformable_object_cfg.py (100%) create mode 100644 source/isaaclab/isaaclab/assets/deformable_object/deformable_object_data.py create mode 100644 source/isaaclab/test/sim/test_deformable_backend_split.py create mode 100644 source/isaaclab_contrib/changelog.d/mym-deformable-backend-split.skip create mode 100644 source/isaaclab_contrib/changelog.d/mym-deformable_experimental.minor.rst create mode 100644 source/isaaclab_contrib/isaaclab_contrib/deformable/__init__.py create mode 100644 source/isaaclab_contrib/isaaclab_contrib/deformable/__init__.pyi create mode 100644 source/isaaclab_contrib/isaaclab_contrib/deformable/coupled_featherstone_vbd_manager.py create mode 100644 source/isaaclab_contrib/isaaclab_contrib/deformable/coupled_mjwarp_vbd_manager.py create mode 100644 source/isaaclab_contrib/isaaclab_contrib/deformable/deformable_object.py create mode 100644 source/isaaclab_contrib/isaaclab_contrib/deformable/deformable_object_data.py create mode 100644 source/isaaclab_contrib/isaaclab_contrib/deformable/kernels.py create mode 100644 source/isaaclab_contrib/isaaclab_contrib/deformable/newton_manager_cfg.py create mode 100644 source/isaaclab_contrib/isaaclab_contrib/deformable/vbd_manager.py create mode 100644 source/isaaclab_contrib/test/deformable/test_deformable_builder_hooks.py create mode 100644 source/isaaclab_contrib/test/deformable/test_deformable_object.py create mode 100644 source/isaaclab_contrib/test/deformable/test_rigid_deformable_coupling.py create mode 100644 source/isaaclab_newton/changelog.d/mym-deformable-backend-split.minor.rst create mode 100644 source/isaaclab_newton/changelog.d/mym-deformable_experimental.minor.rst create mode 100644 source/isaaclab_newton/isaaclab_newton/assets/deformable_object/__init__.py create mode 100644 source/isaaclab_newton/isaaclab_newton/assets/deformable_object/__init__.pyi create mode 100644 source/isaaclab_newton/isaaclab_newton/sim/spawners/__init__.py create mode 100644 source/isaaclab_newton/isaaclab_newton/sim/spawners/__init__.pyi create mode 100644 source/isaaclab_newton/isaaclab_newton/sim/spawners/materials/__init__.py create mode 100644 source/isaaclab_newton/isaaclab_newton/sim/spawners/materials/__init__.pyi create mode 100644 source/isaaclab_newton/isaaclab_newton/sim/spawners/materials/physics_materials.py create mode 100644 source/isaaclab_newton/isaaclab_newton/sim/spawners/materials/physics_materials_cfg.py create mode 100644 source/isaaclab_physx/changelog.d/mym-deformable-backend-split.minor.rst create mode 100644 source/isaaclab_physx/changelog.d/mym-deformable_experimental.major.rst delete mode 100644 source/isaaclab_physx/isaaclab_physx/sim/spawners/materials/physics_materials.py delete mode 100644 source/isaaclab_physx/isaaclab_physx/sim/spawners/spawner_cfg.py create mode 100644 source/isaaclab_tasks/changelog.d/mym-deformable-backend-split.rst create mode 100644 source/isaaclab_tasks/changelog.d/mym-deformable_experimental.minor.rst create mode 100644 source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/__init__.py create mode 100644 source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/agents/__init__.py create mode 100644 source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/agents/rsl_rl_ppo_cfg.py create mode 100644 source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/franka_cloth_env_cfg.py create mode 100644 source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/franka_soft_env_cfg.py create mode 100644 source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/__init__.py create mode 100644 source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/__init__.pyi create mode 100644 source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/observations.py create mode 100644 source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/rewards.py diff --git a/.github/workflows/license-exceptions.json b/.github/workflows/license-exceptions.json index 461439328380..49b1bb44a351 100644 --- a/.github/workflows/license-exceptions.json +++ b/.github/workflows/license-exceptions.json @@ -502,5 +502,10 @@ "package": "decorator", "license": "UNKNOWN", "comment": "BSD-2" + }, + { + "package": "pytetwild", + "license": "Mozilla Public License 2.0 (MPL 2.0)", + "comment": "MPL-2.0 / OSRB" } ] diff --git a/docs/conf.py b/docs/conf.py index 66a441be9718..680ae165513e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -224,6 +224,7 @@ "hydra.core", "hydra.core.config_store", "omegaconf", + "newton", ] # List of zero or more Sphinx-specific warning categories to be squelched (i.e., diff --git a/docs/source/_static/css/custom.css b/docs/source/_static/css/custom.css index 7f4f1601c6c5..e9b6b0bb603d 100644 --- a/docs/source/_static/css/custom.css +++ b/docs/source/_static/css/custom.css @@ -110,3 +110,10 @@ a { text-align: center; vertical-align: middle; } + +.square-crop-figure img { + aspect-ratio: 1 / 1; + object-fit: cover; + object-position: center; + width: min(100%, 480px); +} diff --git a/docs/source/_static/newton/franka-mjwarp-vbd-coupling.png b/docs/source/_static/newton/franka-mjwarp-vbd-coupling.png new file mode 100644 index 0000000000000000000000000000000000000000..9066a3c65c7f41b62daace9202d1b8b035d1b62a GIT binary patch literal 1309804 zcmV*tKtjKXP)U-g) zbyb$N73Q^-mbH~;7+F(pT3c>hTLDAU+6v>EGX2e^x?9L@USIc{k{Yeu47R@R7B-q^ zO{xBtGTj<9!`gDgEoFMNQd&uw4yM3Ix2&%PyBUnvP*T07RJRtV*WA3W>gIKoYu4A4 zR2WLD3}w~E@)}D;t+meVvbm#?RBNs%s^k$k2o_qzimR%!$i-=-%2Gmx z)2ny}tDJ5vskVWwC!=vIjoVAmZ0mS!>N+jj4U?OPo3yUNSW;<*Z&_b$DXX8fQBh~BsIyn<9Iz!-hI%;8)rR_NqpM2iEU&ecR+-jU7)mPi>&kUxFrqK1 zFs`pMmsA?*Pcd;VSeb4e%v@<)Uul9NT7?0p(EOz}R@mk8T3dOYy}ZsrLwiM?vjR3o zS6`v4hlwylS&glfjE=BneU+uO8q7*&;VC8f?dJ7hRru%nYSnON`XXADomW*_ZG-W0 z_%8S&7}mjFJIY}GN<&F`9aw24oQ1 zcEh&r;hAW*vs_2V zO&A_MoL*}e!&+D;{1%4NN+TS`^%Xj>vMOUG985T|Z2`L{9*8wJwD#r)rh7)_I|rs4 z+WNeajL{LQF?s21bX6K$RR(vJ(Nhg(^45^mS$uk%AJ$-YhT+n12h+YtQ#jd@XzEC3 zJDb|Nn>+ejI|p-JL%FWu_U>UE!gyhbrVPQ9j^5FH&nR3MIHfSyn(uGv=!FFvS_;Xg zj(9^`B-Ij(XT0GCcQ94&PuSg2OMS@f2$~!LqumcfvomCMMc~@Bxg&7-pn2moj9J}L zbA8xo59qBv*wb2*tH$80);p?ncG!2gt(8}s;W&^Pz{;zQ6*cC{I%}2A2D4S`a89&3 z8_Zu>OT#)_b)CIhXRp!O(CVz!b=F$F-C*(5d*g|weD~1I%+mIit@{=??H-w4Zq5&d zk~xPjWv-9Hfznz0b!K0!*;{M&)yTt$hZK%29${A%UTaP^y7uXMM6=2f8~w7%73xr~ z7qChlkK>ICuUR?)_T=jX>deqEx{UdfUVl(uQ3JcEJ33K zj%qBN$YygraAn>7;Mwz6pFel`xziV)K7HY77@oQK^w~?#oWJz!BbT3j^y+UfUVHAb z>(5<&{P`B(nbe)_rJKl{R~&;9PT=U;mLcQ60p#oxd2(yMR& z{BpaZ{^@65eD?X5 zpMUxFmtTGJ)z{yC{mpmZeEa=(-~I6Y4?q6!-15dmZff+3P3HT|aUDaki6SkFY&)^3f+wk--T5Np^zmJee|1AMwnm!7e;; z=E9S!j3$cdXD|MWodbL9DYmm0o;Z2#%6-Qk*|7DH%@x*}>Wx;9(dISTd?vf!>;MZ` zoIz_nSO{jYyCV({JbGeIZ`|pN*ZUH1A-KQ-Np~RW38s9Zls}B-4JNz+uvjpX>K`24 zyKn!|qsJ~>xbXPn*I#<^g}2}O!)Kp<^zAoa{rJOoVBdZF%@?14_V!zEzWBm(7tWtK zuy@bI)a-`kEi;Rorsh|MCl(r8yUI$|fx*Mw=m;8ZexuE2uzC$vkKTf&Bhy>FdaIX* z2AdCOv*WdC;F)0xjN548zOos)X{*)`!Snp54=?bSV+`YY_bU2=A z?dYFg+;ZTKd+&MR_`MIEgpq}fyZT0E+q*}b+xr_^dzxCiv+aHO$S4eT_Ko)p&Ge1V z^^MLBjLr^@&kv0+42~}hPb`j3FON+xk58{m%zk}cqy`J?E?!2`=1Y4C*8?ob+BJ69kM7neJjbcd4e zV8RnjdP3muHTZDm#y~6+jAa5b7;gwfGq5=9mm4obxE|rcj61zihbLlphwSb!>^S_% zQylmwUd1ro?h3*<%nvJf(Sq=G9(=&1;i9hh#c(NK4Aw?ll7>%V1N_m94-aF?7fykz z*_t1mUE03q(BWMN?%J?DvGmD$YXO?@1CgI11pBC;u;3A_RTxlZs;cqu8cAnviPjWE5vvmGSH6)~ zYofzNek#pCxPrcBsq`tyt%Oh6%QvmBGOeS7iMGCGZCPCj_|rAkN}XM2^}#Iwe(Xr9 z4K9+N!RgWIrK$N%GmBfmtDauiG&;4RXJ`^G$i`d`kW9R>BhlEA%5}tah4=ijIYyhsoxE{ZhM!>zUGF8^u;dUHO=kk5y(`ngtn+ioS~ungkw!b^sE3ccsCNM4?Cb6i!0?y#QH}TJ`JEfb`73 zx_YbM=}Q65wH8MD#ukUBmU~C%n{xedI++|H0AN7k@>)Sh<@5s){AgK-amdLaL_es& zR6(INw(=SqxyW|*o?)xN1xL&V#x}U%TWU=1I?{*4WIBM9d(JoB9XgeR1S2S6~< z98KrI?@u=u8rymhc=7|euECD(5dfQf?^tKwSZDuuVPFD=o&6JqfyvJPNjN=#eA>H5 zS_?zjj{c^$o>WU$BGVad$c2-wfoK+P+46aiz=tR~e+~j0yoi8m93eg6gw?Mz`|3=d z8l$UP@2t`}5K(F@71id7Y7>l9)>^7{Hj~W@_enryxHm3s-nDTnpz@ZU!O3(s505UB z-B)LH*64w1YydXZ#5}MzT%j;J6l9DjRs4Da5k+AS2`V*KVOm@%A{KG9%jLU<3Ve}) z^WaFG6Hhi3RII>hyd{aJ$w1LCmwwY zy#I&KUO#sJiR0(N#eeb?5zE=fo;e4^0=RPJ*$Y>H``ESTE)lL=eG+lyiDzGa>bJjt z=J{8DORVz3%YXPCu*xfMzWnN2fGe;5;hoptc=r!)z4zwZ@4x--2k*Z3(R-3rKKkUd zPd)`)`SSBGzWM@iWk05`}XVa;V6Fl&1au}^2Q%t zKY#Z09k<;&H9b2xGC485u(WC0?Bd3$g-viNdBZ92cb$O-yBD2Q^l{PIjVKPc@UsD9 zz~}))7Gx|j;#&+N7C|q>Dh~3?9k>)(0EV!$W+z}w&_s~KyAM;~xn=OEIVjToIrWDW1Z|5RG@);tI=SD4~VO#O6&%N?{sN#^E5#W#P2MbCTW)G0YIp zIQ>bW6!uIIvt1EysG+rEVAJ;fci;E$gU8O?b??!YZTsMk4XzRR&2T2$y#zeOImA2! zLU_(I`QQs;G#{Pz{v=ueynL_}`sLt@M*uvs{&;gR)dH&puN`jt`0N1k$@PK*296dy zI|}{dy+c#|ggwKPOQSO@d^^3rMvFA$f{7NyA!Yyu z2DCI`13ZjUxFhr$4xUF$HBN#YxWH-e(mr@>1rh*7{zyYZHV>ZU_T9JcJ#_aHAWGkO zB-soA!;n+&gQf7EAm3#qkW3JzQZB^^R9yV+L7d_aFuJ74@Q~8xiH&p-+uU>ukp{yC zjH44@W)wsA>C1#qzfSS^$+>4TQK)8}X1cI4l46K(ZYI3BrL>O37Cm^V;1_~NqPO}0 z<`RwV9U6Nc!~g&g07*naR9!W4n5U1GI zRazAkSt|)-twz#8qv^ysEW!}j@QZdulfggPF}1R{$|?cwW*O=dtWK;ctAi1IPg!k& zgK4n);S!29v}baC`QGvV(Yfw{sn)`9B%OoXWsT8YUIUP00N)XudpM*leW2C>>nYPo z83g%<9lp{U@bazfh@hyVcfv|)Odjy?^!OS(#5W74JfQ*lf%wCTmPon{oU>>{I~ZIl zkwzSeH+6vLhnC3$8o@}kp(C7X3xN+8&%&h+*H3*Q1-AoyQABT^&~LK`!Eb|ags%oK zlduQX0P?DNYo%-gBZ_PynWUU$1R0k3po zX2a5^9bj-~cJ@s~Q#rVw!f6SvUIhY}6@0H!T$>4x6*=)JGO*P;>DwKp$U=72Y6X1q zr)8{r)TlTr#SH=;R#7y_QO05gN*Uy`>-g&&g2$v-R934ZuJRQ{-%>549!17kFQpqT z4hboyR>hJEor|R>Wl!a}^i%1`SLhso1n?tz!cFPc?tK4L|M+76*kWh@bh@?A6V4bN zVf&>!lQG)|4qPK^1wosilP-_M@@CZ4Dev3-dQgDB3N%=uUv6P9Uy2|9S ztx*p(^9lbna5=yahxn>PAqB24_;p~la8|fN8Q6d-gI8moN`nU|#q5j*qnUKJb8cn# z(R0_2J@Vv(r?1|3{1VvxCoVs5@(TP3FaX?g>>T*<3@uNg#`4(HDzRLD?(!4QOI&&B zDVA1VeEs<%R(Ts(Yn>E}PfY*(K6-Mz;yJ#_l&iE|nur5IFx%^uf+%86oQDF%V0ST;Fx zQNb0(R93kWT7YCup1*$d%$0q29<{mSRXSU((OGAz*O}dVi-+_I5+!Wttg{Etp1Rc~ zaRt4@n1jrTq#dhAi#pw5dwtMWA8`AkorS)omCZZ%9JuYyyYGMCfm5eWUb%en`RAT_ z>&@3c`S`=HzWm}FIF?_2{{DM!A31VlWOTf*f3T;guTbdf?&=yC7#tiL9+{k5+_*E_ z-skY476G1XINcs=gl9(~+1#CK=}m*R_BOP^um|of4ftBEr?IUUEZx#g@6-`H2fn{&&o~a>MS!7F5b^`T3|`dI#aC1a za`D;wkD3TYU^ZkVTIAO>9P-QELc@`uMc+b9#cyfB8J%C*zGe4qb1OT#2B%<0>isF3 zJ4`Z5h}nP?7zIMa`bk}h^0$~(wI^y77Ly}l8;Ti*7DRrCT7~q1WN0n=3I#djNPR>L z8$}bvM;faa--yTuLuJEYB4dn{N=P{Yc!<)JxKvJJ%o0{NQG_RoWa3LW14;Nbo#1K% zb>RH?DYzwx#*X2M4f_t=dGy4Y2acZHec-O4$tAd>y8N(00&kRG62u{JqmJQ3S{%-H zs|yhwhAOd;u1H)IC5zF52{Pg(ygDLwPlV@G4gVxfl2C_VQHc!zBtVIh;K$D+2_gs7 zF?`5BO(>+qv!O%_SR~Z~CqtsCJ>A@y$@SptGvc2f;GdD1!SRJ5u!)7?$ql1ZOF$Gb z9GO~zDFb7(V1wgxU_;~cL`sW;qq6``UHxOQhJ4RRd)Hul*HByMP)kQ&rVU?JC$pXL z29$te>DG>};klJ^jstG80QR`$9>tP)^VT1dknu!6b@81Q;ZwAPdpn zC5ZIc=ue!IsFz0oXA30iab_SNS|mW`Puv9XK*3t1ih%DfJ^ppf9uhnlNXW(4b3hkr zDPs|KJ0_u z>aRAqzy&F(FrXt|3eL2S-RR$}t6WoBi$O#ch(K#;{cBk{DL1WCqz?%s3M8v6)Rh}bgzR9Iul#Ll2vC)LB~RP14rMVTEzWWW3qh!5DN#ujp+p`( znOsE7z`iehR~fx$ML*9_Uu*K{EZ{|7z2b8Yv_nI zqQ#r?apWA-e}KpEg+ZznuHIn0IS_01$AES+o-n>manchAZg%jHFaqjWd^H%WCOj}@ zh#RAG)EZrIV}i96`X{CrwybR1yS!x&U}nbLaj$58d^^fjjQozW4Tpjl1E_*)u!?mwZ!OHzG|#F4oWnLwJBS zwsvPQfMF=tJ=)$g-rhTr>zQcl9&0I#Hg}F>^23eogQ?d3M7Af=&>2j&`(rJ>7&>t7 zP*Z)N(HUrP_)<1+63vrf@6GCtvnvK{X#m2p2H^ffA!HalrUa5z(|!@~psjw44eK6Lhp`%hjya`Z8< zdyYMJ@3F^_SWYmpTz>G>)rU`CJ$m-qF$%IeNv{0ai%*@u^z zV~cx8sk2t=9W@5H2O_KJ%p#J?z=&%LNL&e-g|HAN?=50e4FD!&s}EV70khp_arlCf z^x*LL{DzGix9r%pXaB9Y-*NAK_ntm|^7^&QFTMD}yYIaH>8Brm@%g78y#LODgNFu( z$GUq528TxG=NISUG+$os>+9|9?duyDnps$xUfdFI>I8Qg{81UF6gH`GL^0`nQ-Bb@ z^9ENH9UXjM8*221n|#qGcrFCU$%AKQB%Q;z-A$dzY$1ifBM7IV6$z&?*9WxUnCpiT zM4*-)V4ieyPa2~M0Dig?*#f>?OSgxTZ9yU*xOVXJiDo>JCJI?;a0Sx!ffV}9z9a@L z`M{EPU)t_Xkz7C*lq(8H6$8>RGRPdd%!W-n-~lwZysZbLh}*#_vY~$; zvN(hITF`?*Qzn*=RDvQc$PqG0fKgCE8v=#G0Q=QqJk5{McD8qw2;@X)3N>oRFD2+_7# z-C?UB7T#>ZKQizv%E-jH!i(E@9sxFjSQvg7U`RqoV*v+O4i^qwKH`g%*8yQ#mN`d6 zU6Na$MDsBh!MEc&VP@VJK}Q0j5RKqaV0>F+G9&X7GqUO^^mr}Nn$e=q1u3b3Y z7Q#TdY#^QqM9E?qxMtzX^{Am@04njD;0A=TI%W?%a;l8*v~Z&HfliFI1igGcM{sez z3a6K_h=F+~CDj|{6t(7 znN~;CWMr~p9%B)S6{K6pRC*x=X^qfcq?PekGpZRSoG`!`ix7nvMlz4eEtuf#9aYIa+ISi&y*PapI~f z3<4NL7!`$D73Dv$e{7W_Hi1l>P(jEfgVUr!C2&r!P}4e*K|y&tmBoUaW*689EQ?Hj zs$W~iBt|F&zcNn28B{u|BH09Agj>smPGS=xkIqqz%!a8BsrG&UTN`+a zu0R4VdpMs>80bu~1Q^~%j+)6+je-EWWgMMfg0i6GOeH=_>&+`fp~VuR_S7nlzEoX!JJmcEyHso(p%K=IV4{$W>S?@) zRmufE*@_@XQd4>H?(l3ft4Djxl`~3V8||_-^?)cQf}JQbN=IR7IweZVt`U^f5UC0g zgeHw-3bYza1;%k%F`5gk&IF?pt>qxO3z7eZ3=dkyN|c8LSn& zZD& z*A;IGCN&_r>i_@{07*naRNF$S91O#$_Gn{1*3=o#b|ukE1M7-5c7lZ)^1{;Kw{--Q zIan6f0IRAGHn~F?ZzRj{$Mu0UMIn>F7|p;_$`x$zCvvV(rrP8!ud8R?rk}MQPD?-< zcOV6q>e$TY2Txyn;Pmx-j$b}<^wQyn0aPwBt1zzI%L2zSm@M6^-j}D_Eg(4?z zt@fadl?uv;Bay7d(PBoM*I;p*;grhc21h0)rsroDmX}txY}>JW|AB*t4<9~x;`o)z zmtJ`Ox39hS%1bZ3xO3N@p8nzf!I5n{c3pe?iFe+8|NRd>`rxCFpMK`I_doE!+`>{% z|H#PrOncWb+{59Q_(ZCOm01h>^ z_t68WHDk;LtN~unwb$GprXWyd*-u7*ue6-l-5-SgwKj zgcA|zcM+l-%RU07SY}~{q4Aia;joD3kg1GzA-r%5a9k)ha*D$zM+Bbn5s2C(I7yTW z7h8xD%m9JHX#6&ThIZ1LoD_SE)Z}A}JEf)}Gh4!;cvGQoVrk2sy$A2!vG2CYxsBOe zPaxLhCG3%cPbLs=4knuUML-ya2@Za1PhcKFXG3duQ(JFSt}m182hXalFx=javC~}x z6TkvJLsM|J_m9mDj?a?RGdDOkH!z07p|QE)$%Wy`#o@^fFubX=cdUD00sv@uVgVer zvDxLZDdePKREy?%2PX=>BbZ~F?@Ko366w}xG80K;;|*=;Y)4aTp)KFv(LLBRG&wr8 zVPR#*rtSN-?mVz@+urGg&3(i8LK+^raO#7zvc3|0&^nP&QCdr(TvcF{oq|*NW&heT z@=)ome+j?K$5d>b0<+zWL0^?%nBoHCB{i%FVXl7(hn9&%c>x}!dP=0xD_J+Jb*vF^ z!Vscou%VbYAr>hyZ(z3w?MPo7HfAIgG0Joo8Q-O3?F%JG1rl;^zCwr1ru!~@-)Fu>GUNjOEO9!Z5$gA1P=fnkQmrTFQ<`tl(*5G zl3uG64-g3$OfRLZ07%zaq5Uzmh;N{C*{d3PZW* zb+{My0@yFz+yytC!oXxl?>Ib_ebFpDEGd*rWKpZBBQ;hfDW9_!)KD%OGF+(!f65#f z&sx*lx#pY7S336zJa#Re;M3)2}4*`!)F&z)Bc+wi|cmh+- zZq$NrKobBz&Oie&5BPK5NXAF!%1*{t>GbeIyq-QEWI-qqnFFVh+sXoVap|7C(0O z>d`Y-f$KK!y{FdbrZ7Bn1?3CDsYv%dhtTvCVzEeKv2wmgu@MjGnFP!(oyp~JM{@a| z-ht79;fc|SnaP=j`3)-@H*ej&XYbv2-*xiDv5OZkTzc%{rmZ{rz$;l?zWvU_pM3h+ zx8HrwIX_@9avfdmoqg_5+CUX4$ozT=v!s*ICrwNw_7H|BA~?ja zEX5|bU!4;^g*}D+tuwo6$5C9;iF|zlNc@V*sOG~2IVV$$bCj2G+(jUh4Yi;+usUUx zL?~n+4v&w<)^2dWw(L5z_t0JYZ$GkQ@2zvoJGuua8{4|0={9`ZoowM8gG5tDGJ_gv zx*0=p8rym@LqS1J<`0=u$e*?-6yzfLLZ}&0h7%d67DIsroOsks5^CHRt_0liV7)Jv%yf)QF77>a=L1KN zKk)F$JqPa`7@Z3xvJQ`^al&rBM5(amiy}hU!S50V3(2`f3^x+(N-=F*u%Z=yHXj`IT+k_Z-@~>)_<<#!Pz; zkTWLVV@y{XK!wsv0Bo=ZKz)G2)lwpMi*r;chsVXqJR2=RsTG43qN%oEEJH^Deu+3--s0J8gl8u_=IxYsH77EjbpP7r& zUC8w>AuK7ym&$g^E%v$m$v7sK^tN>LXLEhc9sS8{0USZN@U7I!gpgSxB-D1hyGlw*&nA@bP0U4;L{}ukb`gR z@Xtgplxhzp_>t3s5pIztU!;*MX}Bn*2dhg_gd68!vtO$T!clXhHU9UiI4J3qtCha& z9L)uX6rSI3cY~)UoSB`yqn*8DE%^cPKH(+>Us`RzkRFca(u6$OGzFtAgD-IF;e|5Z3!=7?qbtUcgd|)%Y3aeIs(?B-@CKh&87Zp=_qT=$z4Xc(FTeWcE3c!pLXlwazVY_^ToVkF>_5V4V4r>dCF?5ReE0oN zKmYRAzy9@afBV~i{KtR%`@jGDzy9mL{nKCm3Y5a?%1=N2{PCxszwqK~m##l|{;{Wz zpS`vkFf^bdnFQ66Q)I*&JVJK<`meLfIj$}CKN$%rR~|YA2kP=YM=!Q^PnMMH5mYct z5knTsDKV@9E6drd=zRp9at4f1WHyGo$_?g0Gr8Gn^bP=(K%^mG=#MEsDNmoH0ZDKSpu;s+>LD`a*AxR9FN9R|m%tuQpVw0&jUzLjnJ z$I<`m_XN{shxGd?5)3$j%XuluFrsRfEI!6jRf-V7Y6E$z==g$D>%v6j25%TMyo2%9 za55Lg!0FCpwmV5haX6R_h8Ww{+TGBG!EX(1s!<3WszB+ME+8e&DUIVwFk&Z?%7qdD zKh2c(lLhlevp_-aP@{{Je|$+26QXvJ0WYY)B9#`E1OyL+II&(EpQik26$%VTh#}J0 zFh43Dh&6Tgjc(t4=*WE!KXmNWp*!!LUfA5&(nTrqNWM6?I7P-~=t2fvP)FUK8U79C<2DmS8@LGs z`{G47AVhJddkwkNScnN+I(Q;tKmyg+f#05}F$w-wFy0J@2Hf;ib7!WlC)?iFlJC!T z4Z-=>JviAjG~I_8LYM%Ap>N1PGd+WoUHusN-qAe*o>OB>cM51H-Ac3r-dh@eOE@Ee z<_#F11J7@&rOT<$wOk)bCV|MPHh%ZNrd&z9(F;MQtk~3AVKQ;QxWIs{F~Ayvi4!?E z=pP&@Djwi>kyMgx@6UCQ@Ga;%_1u7mQv{Y+pLW_fOT`{JgZbIaR?CpYA}M&S8W?@z&H1=ke73Fnzml8IR)ue0{T(NmnZK}SX{ z$t6vxXTXptO)Z$FQ8Tn)<1D5HHQGv}d;9~_^2QC|p-=}E2GD|vb~c4f;5Slz{VEzK zGI7YrE;0;FM2D}|?6J9HiKfE9_yRa6I}hBsW8WR~8+R23rs9oxhbOML`rwdO>191) zAQMd1cPkZyMFxn;gJxW1tMG%iN6(BZwhd;`E>dN5ucj9Y8>1n7N@j3ZQeo%{t}?71 zZL9!RreqmaGO>u#e8m3-D%63s_|}Q4<$8`d1#cMca-nqI=1szZrQ9v16nr_ucoMx_a-4D*!5roU%$(xrap+R#Z+XiVCLJA3yi_2`-Q&LFJL+YG4YeaPeYb zmEZnOu*!?S{{!hNnyi&K-xI9z9@Q`Y@RN@|`3zX)voF5-@~dxt{ORYv{PnMY|NGzn z>%ac%KmYST0at)kD69S#5fAqBFMu>}zWv^Fzk8Ks4Z#)XuAKxsf9;gSlK&f%lya@e z&IvnygB?4I5)&9u%7Z5_-GBVz11B#XI&u;mwRL5>^;IHIOr(mb&_an-1TB=s=M*LB z4Wdk3jbcVIq}p_Lqs^OY%C&X$wB!mf>?rgRu8aMBjnN6Fw zo;q{x%dfuyMEUyL@8LhHrKVw(pML)S`yW31@T1#qzq6}nAP~!NRTa{oy|oyr&g|q7 zKiuoJGl5F-SG^%E?He!kP30C!Ac7ppst* z7z8r`1`oJVhmvi5BMS=~cLAR)Y}^TsV4|@DTskXxDJ(d!Zh(u^D}0S8fj9i3DHyCJ z3Zs(+Wl1j9b3&G)0$ev#E>^QjWNBbEu!@7P68tKyo|0|qu_9BN3R^XDC;>cHFtM8w zeyB!HKAG)mXz6Ln^0*4xG+qLa;JbivT28iYp?*dbp-eFo~kgSB~*HjhY-#{&fiO(W?@J%^@Xu80&&7%dcfkphkaGuliQkuBqi+lzz8DC}I; z5K|z5ONR+zgmYtr-oli^D8ix&=b|VQ3Wt-?#H6B}h-4MFpcLM6=0k@-pqR3?_;o(< zEg0p+m4Z>+hy#iwn%cVuCYQJF*|GQ5P22YkPi(*vyU`|aDqwyXdZSIO$hj~DD~UV8 zCyAf*AJ&58#*>870825SkX}R?^8u!4S(}(^f3Y$n>VT#BE7ba6^wCWncX-SCS|E`BwN@GeNwr$A7-fn&scdM) z4ZRXW#R7%M4=L61fF{jdrjc;Gkn$28{1|#o>ww2OhUJ*tI*SMH1@KG2&iEslP_j9h z>7WdRf%dMULf>f5;6&f>RR8Go;21;BR4@EFJl#J!Gl&^KIHhN3vU_l%tA8xtI|2&= zETyT|HIWZo9u15==t-EU>J*i);WyLpe5Hr6LB5qYa~?U|_hD1vHVQWvSVMEiKwIZf z&mesJ48RponZpyUF*>=q1Q*>}Q(D6XnmGh+4cGPJm^VQt?8K{S(A-6YoqJhT)~0A3 z>qJt%MU$gX$K@Em1oxAU;*pnXHJqZ&({|lrbWUQ`=Pj;b=5Xyq3}mh9lFY=D_V-)Lt&tq+n3Q6zvEi3d$gG zsB4XGaAi<7=^2||+_GiY!7aNF&Mxoh9GDKL+rhH|2dmn!IzUUNns}6ilOhd7@6u>B zWcq(#qRN(>O{dxZB9lRetE^6&cq3G%3iX()rHYlvK;jk_c**gtRO*Zq7>WmN*h*xdUw`|Z(x3*TzrHI1m7jk4`G+5Vc z03_jLo78*?R1vWUJWl~Rx!&gX-nPPEp>J$#dTI0aeZVKD&OCDZ+=V0e9o@8T&*0cx zzIQCwHJk+y!g`6Fscb%-#V!lUY-g;gBb>&->_80ji%=6_K_J*rk41MkR6}M8E{MgL$rtSOp-FkS- z&VxO})8SMLH&MX?Tmg>ABI>{?X%U$gV3d{NYJN?W7aU%W4GRj`P)No}F>hji@hKHb z4yBdQFBu-Dwd74pTldW_Zy%aigbNAa7@!SfW9qQ81a^Bw4yi_4SIt<098#vg1?Yma zFUn=UN4dx>=iGEc5gjM2i?)w2qNaS{#GA!DYD38)lOb5tXi{{CR2xkSI;xdWRHrp< z0{0qO&leG*(hNQdhT2orEF%^iL^%&mqk3Q56KV*?GSPGnPV|PBZeX64j(%YGeD7#y z-xypch2GJgp$UMTk*US;nWeF*h4JZy@tF-%^BZS3Y*|>@0p9b{=AB@R8@JCaZXTXo z>>HkLD-5Tb3xNCZ8^C2>XLi?MNSg!QEPk(CN$oq>2bF0#LXTo40squ-#1}??DG_>D z!=RS)?>VtY6*01>@OFuD5GoVqk2pdtegAy^$vH0H}$lu|#%~kr8q7Ju7CIV0ABW(5XIi1m)A$U*YmGuE%hY@-xXIYd>guZy z>fHW#Ydq8K4rgG))>o+)7v7eutpKsz!;5!4gp`5_^v5o7=$ACfDg-J=R8YCRs$lUy z0+ovZD%YRCtn^nQMdhjIMD!PSS$XmG7hdArdSI2eUg6w&V3jwq8W`&;SihKhtpIpX zmz6I7Lcpv3mw)+}|M-vp`1{}g{%`-!zy0$+|MQ>z^rxSH`9*@tFF*eH6L{pX)KkyC zbP}QBssNQot|6s7Dp*Cql~v=vLM*=mDpyZvC`EwEnJYjk51)qP1C(;%eqfbjkGA(r zlAK~H6;B_@DwQT>ytEivgk-7_TNG%a3jwbJc&G)VjSZRhWJ61`u{G7y)|hS2wB(y} zg^F07*naRP;)2Ns%dWUqOi|BDf2UTzsQL`1+i5u8~tjii;8dq?NGz zq5&ITvr$wZ4sp&sf(oasP(MoW#**2t!SThVt$R1`JUG8`NAJiS_}^3y0Qo=$zW^Jg zQ1Uw0=89T1jV1|rBHYzP(-S7BM4cH$`LNK=~n$ZO-Q$)Os-F}R>IZQa9@3tM*V-?sbU;-;NF zL(|c8yBo!vxSQLX5LmKo!kD1|CrfShyo1Cu90P^|i;&|Q2t@!0Da}R-bmAjn_@|54 zCO#sGhAS7-abv`~3GO>3n9{8mjo~S~UB+>VRCP6Nqs*w3$s*EF=t74^9q4~kQb#=^ zYe{KY#U6TPS%EHuG`t{nuUBR;a7%kiGEsSEjZzj_C7fE3YJ!3f2AiJJoGe0xECDxYoz+K~?(MCeL)}ADeN>38 zYhbdoZ=zY8*rDy2fm_1M zo{7XK7229O;bRbc4}QQR221$*JSKyo5af@>IE%D zdXCJ!5m`nG8CJ@0I7Qv!ieW-h)AX3vX3}f&Oq8-+oHYT*%|UI;TMI*QqIM5V!P%H> z%EQgB*5m?op++J`8Np>y+G41IRYl&sO_SDQ)l@1Y<&(<|t98sQ%2HgYP1_u>QfWIV zdXjkRtW>nx9#m6$yh)ar`8h;^*BLG?k1`IG9HD4wq9D=hw~DyUH9;t!P=Flw}d8TOxl@fG;l;8_3Z&wu`> zfBL6C|M|~<{3G(oPe1((&h}3LDnI=U&Nq1EZ@l&1qnDmMe)h_Vb5~BDzj_)#<&GA%oB$0_FvoMlqAY5tgXl^SMx(E7)#s-GQwrtz+%B!yd zqI~wb?4+sbPD5GsKPZ_haP)ro;fGgWdwtWE?Xh$-__dlKE~-ajSBOQEH_yZ(`WlId zFKytKCgMvAIevq!D6>VGpCE@of|Lr0IziS(%#I-32fza!n_k(p%yKX*spc)o8UFNSCxy<=eQ-J|VYBk)kbk)E+!_bAK{u46Ms#Pwy{`!kda zlxi+82!&H^;)#aM;6LPbV?fpWUOh~FX5t*7o&0A!kSG_+eI=XnGZhxY& zwQG8D3p}%S?Z0zjWm`)}Z!p?K86!~^Mx3(MNxk?Y%6@?v{BftY3yMF%y)Owu7|9Yi zkB9+F)GPENqPZH_G`;6j^-6}lN;^r#czdXiiqs`;UQaH*nt(1U`AU65FS$Y|$%Gk( zNkdFWP+8B$Gbo?JYJoAZxYjMIuEMM$SIe`J*uv|Vabn`Tl&|CIFpp#&J=OlIqo66O zS**6CR4eLoml7p=1hW*-b*4s&(&bf}jliuOlR@=PxVi%IEYN46Z>(>0re|oTH9r_i zX5pNuF*w1)TSxV#kZRya3MfKM0@5RuK=he`q2jHU$|9}cMpRRQDV@a&H%hpADXS^v z38ugsi=;7uC)v~iCsww-51FTHxX=$^1HdzjEl$VghsWosO55THmU3Gd8lUeOn(7{$ zDD;nGlkCocOgpxai#N815-t8HIB?j4hbdn$4`%W(@W?X9#BYkN6U<{Qk%ftilJbjk zT{O)@idwS^V;>qkDtMy6Rn*Yh4SwG8=3RjGTXyc>uyN>L+wD*wKUFlAh;s7dOMn3Sr@Qqoa!Z*FLEQmrX1P^sNbMO-qMs4|Pj zDmCDt94JjS=c2S9F|%?oRVyJCdxeoK869QuZq?tJf9_uynl z&u9}h*n!)J-in<}P|72vg_V<1?p8@^NqL>7EN_h_-$v`sYH~cZm5w#w#9?eAT#aIZ zjFe-|o{*8HLg^Km)qItzqAJGR%tZ64hyqpPQYd4)LZtsv&^!PSGmj`;D*953+#Z*B zkA*edf@xnQ2s?<<#7g;-vOvRp@NE8r24K6>m_zN_CGPP5z1C7~$VieEIyVqbPiENXo)F@05fy#`dcJTc8rb&6?0{8KJS zY6j~STmqQ%6O-K!kI_tf|K$AUjobDvZ{0gFx2YvR2#;4QR?Wh@4cD!*QDrTx5~Kuc z$-I1lPxhd~48|f*;cEDPwaA$4Dl5sPh+ecX!(XHUGnHqG)}Wag=}T6m)RqlUd=jprF5JU9f87M@&&b%*<~LRkvGDL@0@Qb`4 z!5!e{#ZmU5%%5aS1imPifd^SIo`EM+wyk?;d~Rv;uDyruI&k|vD_i&UkIW_-+rcgL zMbItuMH+pW^3cSxh5!$4|3cX{P9H-H_pju%la!`grDT#|x}KyMe97ofDmBey_?ZYB zs~1yJTr)teBA}U{kK|3<3{e#AEth=~H3fFHpFjGLx~T+r1pXvKV0TahBr<8i zq*PD^k4KMcNeX^!pn^Qr1#Sl?FW2vlHp68Mego#>i^{@gZY+akkvRxWwK`2enocRj zOYic{qJoylDHlGps1+vSvW%iMa0Qp^6)7w0C@!lgMFF>jcY!Nq*|6iD$pd#=xU*nU zT~8EFAYZsK7|TXet;uY@skIyExi#OPD-5=G4^d#-NN4Xzz6WeL-#vuZGu+ubTIe6^ z#<%Za?8 z!?YOZOGOQT2PIec2eB3T{|u!--StE0tyu8OUi%RI~?vbnt-TvEh$q;Re^$KRz_E00axSwqT+KP6N`Q zY+83jaX3oVl3&!{utt=ckf!ZHskEDv9qze_WSwlxBFtI7syUn(MX*m?jyq&~qtu)f3;0r-#bA?cvzW5Dxw_el$S}d=c%`nF6iI}tM)Fs2Y?xTQLJ`Mw4mGWXWSse-QU*lE1+&U0pM8G$+SA9+Tm~{Z z3w8nQ@v}v&a!nXfibii)4Jw*Um8%MZ&-!rKmScKsmf5t%S7YzX%nA4o?)kltTab zw%rGh-1pG&Q|AufcXVmfZg9k1{-nf)5McxLpj30>Lj)?+M^dK0%LXa3nIrtrVnpe7 z5|g+RwaN7;%~C|EVzm#WqS8oWVbwxq7+Ew~)59JW8=2yw$<&uAF4Xp3p&+H(y~zI8 zuAD=;JR&@dkRayr$3<1&P|}0()oE`yt!-u>PPI}G`&>hFzO}P|Y;s}C_I-!%d+5QV zrye?X`tW@Z@7jO+?1n8pLzA6-;~4PP1+L(DzJIcNXd14deBWey&sba6XiMji2!U(E zLI_j>8=rbyn1_nLW&K$Bjl>_8fVlH1r=|pwLTQnG=wc)Y{+O*nvmS0;?Sy<=;b`7 z;uRVcQetiq3HoHR7#H_>}10G!avZUn_c#tm8)Y<=UFRil1th z2_D5hYlT`nOEQo0Eow@Mh&Zay%rEB=T@=xeCp^O$6roAf5R(nUk=Cfdt_$b_?wD}( z+OR_qTo?&&DD96lg%jCWLn~YW$);Q?(~-^fw&nW^y`#N@n7uPTy9i#yBplc$>5&O z@0qH87aY2+K(kD=3q0m0cZ{ftGx;2_w+iR$h-CCNWZ6U98S}e{}w`fyYxT4qU zNG+km$a#wWcufX;u_mK2O+bZmmyfkE$L`F z){Aka3ko$tsdWl~(R7ig5H+h(a$2)yP5%>ZZ8RzA6m@B=(Gm`mNm0Lf{!_b;MfoSJ z32>GLRocbUx>d%C8cUVVt~a~Ap%k3b6SK=(cI@A=_f{B=&#VBJxB_WX`JAGOY7Qh~N7<{irhd*uirLe1`CuwmfEN&c{T?vy5$wzMw=>U6dfjX;S?b}0j zoVbq)N8*X(7EYjNQ;HL)+@iFtv?&SJV)sb};x*Tp{d#*C_8qpz8*Kq9bNG^QTGD+< z-0R?`RBdpt?7I8Jg=g+Nd5uz2E@@L!?$(z4I;si%`gKrItA1hc_Qyo%7lF!eFUcN(A6@gTt*T14?!{+xOr9@Xos*oH>6Pz~kJ-$I&jJ306@k z<&kS=q$yKQJ#zKbd9;(OOvHU*MA?m?@*siAk%uqbcF(C~rmLh}j|wW5ng0KXC>kr1 z)z5Q3r#!0ehzUMzEuHKL$-qb@k&-wJkr8ZR>7r z@9pRwg6UK9n^v~$-nRSDmfZ(u7Poc{Oe7lHVXy7(u)`Cvt6d1it9{gbI6@37fN_); znU$Mq`jjXvq7<(l+qSr21Rj3|n^*R@^wJccx;Q=}31b{e9!tRJltEt(O;ZNzG@TSFD=OqD14D|k{{Tx zY1bV`9z1sH{QVD|*t+XLSO26xma(AL5wO7gv>w3@mD6wPL45I z%{j)LV^!e<4is-&kgFuqf`WWB@nfRyu)-~}Q)E;-8`laGi|`bewu59uqxnQ6nZ%UF zj z8fohpZR;Lw!zj4X_TF(Ypq=*aG5n}&xU~zj7)T%*$hHr_!?dBL8w^&3m9H}x!Vqg{ z$IKrTf?9)#<{*~sY2sLJU!>tCrB#SV@i20ycJidH;8@E(>qgmXT`9|psnTmnff0~X zZsabC8Xz&;qORN|a_+VB(-iXE&1=MWaqmTw(0SB3wXNa>9;`}eS)-<})X83o_!<>l zUN|A(5Tu$5&4496a6m@@*5SU!uk+#g3f@dfl?h+cv*g0b8%nuhk!#MP6V)b`>d-Bk zp7(1stsxarMd>)n8_X>!i$F!~$6`^NFjgx~OQeaOC)hhu?cpfnk<^TKwGoQQOQD{L zN`V1`bY8KXs3tE~Rs8u0gem?aom%r&o9g^>1=3P1#wBDZAeP~Y`K3*}b|1KX*SxPwJY+JI$gY~Acmpm1J#P?Mg z*oL$t{7a?ulT5BKYd}RNuPa$DYUdU$NUTc4w-!f{i42zF2J=>tRj*PFH?6l7@r-P? zvyQTi*5j{Z13wb({+>`{D%;i3GX`V=58Z4>f4nj84mH3t2G9jN!vKaM!Wqh{)sn$Q zl0s3@WTnEG!egS9r0fr)M(&l>+!%XiSAc{ge0Zb?SBgybqpVga#i9~pQJ2P5o#tgd zv>QRC2%?mR^lDAv;tW1 zZ@&HRcQ3wj=8>zwC+9Cd0mir@brs^1M^{tI=~a|+uE+$aoV^iLE|cm#kS=?*08eK8kW-0hFM0|{>^=?|wvvBpG0 zOTN&%@4#)pfAtS=xaiP*ELcVAQQV#8OW915F;9ppKmP)Mo;h~`XaQUna9m7y?yA!kAs@ohNG1B6#@1>13v( zXJBG>Y1{Tax9&c87x2l#%8tp|<-xJJ9_)pU&9eK3rw7L1c{B~yH#*w`&!&;tzLD7h zcus+h&B1UG+h@=BkIv!@!?Rt3Qz-j%jkM&48ryo4*-miLLdiBi3O<}rhi{{S#3&|8 z2nZroUG$Y_Q-l|paKhA~33w}#N90nfQ4IiwAC7|KKv``9jHa=DP}ND)ym=87#@YEo zY;h``97W?7g)6uyFg%^X&yJ>A2S?|2AGqV;6K5VcdUD^PyC>#0rZSygY%;-l4slT? zSn1U$8lfnaf{7DcQAsS+XVb;fM@(%w?};genC)DP+0C8lBb?JO`%#9Ar)WzOYx>!% z%_4;|BUUAQ*mM6%K}SMtQSnu%D{iGROPBymX-+Rw=T%mT$wn?p#bGtW9=$0gFGVUg zHq0Q1h2qWmp0O=E_V3<*+xFcDCuWw?*-rGt0tswg5=c_gKQ^Qm=_pj-3(Ek6QryYJ z9aO6ZyJg{EHPDMvN?>M6nolzMP{12?N(gaj0W50li#AdQX*Lj}mI=w0aEi)Xr`zLA z9m#CIp|z_i*PChYqky-e)&jr}iahPzBRS0Q8M*0Z=}B{OEgF}v?L~~1K_wFrp|rY@ zh^7FsL9|M&5JkYWCfc+!+X%ujt5#{DMFh0a6wz04EejD9iXfx3gx7AQiHccSz#Y+9 zd`@2~+Sma%f_&dZd(T)qhHCV{BNL{>vliR}%5%VAFqTTz$Z}yaHcZJbpeQf$_|YmW(pW%#1N74s+6m znW0IVrj(}4JkyyDw=;9*od5fOzs0-Yp)EV@ob%>-E?u%B$?}%8_xkO%?)%=BBgfLr z=oQ&l;rK`+nPiK$&$5eYG0`U>I80S{7VR3I98>p^@s!zuh_o!8hsRV`!BUcL6Q)(H zN4K-v*cQV|D*?~Lq5EpKvkZAXcP47ODZ77Y@8-mDT)(g#AJ;1$a?nU1_?ln5_#;XQXwS@@FsAK!~#; zmc1SJ;D$dL4iB&&@rRolV|_qtZQaAQOswl%&1Uq*)Mt1g%&n@j<-F=(gr+))Lg&c~XC{X$6 znV>=)l^?$S{&(LQ#$TwX=jkueRQanaxOtW-UUY%8OpGi4&wu})|MR!M{q*GNhj$*l z`O2$r!@Bnnhy_^X%{vlQ6r5NVg399?yzYv{jg;$m9vh(Y8n3IjU%L#Ta{VE7RPN93 zJi0B_mPqF^wrM!|)wKb%n8X5%3LgVkiLU?~3qH6~NT+gLM zuE76dr-ClISplC_M}bEqqq{u3;L}UMQ*6sEfieyb2XM$(+;L!a_u=Vn&Z;HX;@jk+QBpgsZfvd~H~!m8!Ma`?hhvbzcu5guG-{Jfi|wrq?;dJ>E; z%fr_NoveC_6$3)hy`4#A(`_El5S zTt&nv%&+JLi#aaI1_mDJRtXXaB{}*8y@+HO_V5jp?Q}N>Qctibfay>(rzfyArFuip;MRx3HdGjZ~2j z7$zqMB27@?EUfI^v;XMg>fY}DaX@a2lb2EPG7_Fmy{5s#ljXnq12V6pbAGA1q@!be}IBVs(OlKKS*YI%> z4(BX>ND!{Ib{Su&9c-bR*Lm<;(^;6oc2ca!S0W7+{Z7ZEEZJKSiSLqss4XKnL}o9l zOciB4oIq;zM4q`4Is{HE$;vH*zX!gY^)1~|@|CNOcEFExzS#rEc=jnm;U>%LSusKf zOEDQ`%xiIe%T}2&&*Z=@9VQJa*HQCq28?NMUlCf*vVML}N^KXgA(_pb0n3%z72YNq z(&TG!sLdtc4u}3-a%qMXeH%50CUpF=OOPvE1mwgNdj!9*CFO<9$(s_=Sa6w^Q|NS6 zz+MlqWN3W*{4%QES9Tp7n^}fEEQUgq1Tq|@P!38_yEFvbKn;>7Ggu5mWMMBR-U5{6 z2-`9HmW|eC(c_Y?Wl}V+ToVKcQ6MHK+;hp8O0^xlRE~;QrczLN!LbA=pJZ*Vk%ky> zOG9f;v}?GvYnTGshfvkiIoR4Y1aw5`-yg-n=&tH$ zJ36>mV|;H(Wi=YLN!nqhri?unvp}$0hp%E)eFtpPPT;FSJnorRv%%)Er; z>@C|e;7x(GIU#LJV#bzjnOn)qS-N#m(O@|_DiSRcEKber&Ln#m@$CyEY|#=)$yg$n z?4rbxNphx?YiAdU1@w{$-#(W)6{aypXE?^&<|diC{0td6Z_taOwV)hEX-=h0nQ6Es zi5LUPq}b=LZt3hFpIzFu`@qp%`;X2p?QZWKgDnh42)KiqTuRcR>@thynIR-9iloqD zsHMc{mfU)V8Fjfnz{E^zF_6X#YLqu}lB4yNSkh6nKqX5Ap(vZL_zf%O<(!FHosz4? zC%OwH(s+_HobZ86$#9aP8BkwgHWYSc0dJ%w+}K^$Hc;KtTU-%_4Gy1hd%maK-%#kO zbruJpAOOrxOm$LsmyQC7S@2GE4lP`{_vFIux6WL9ta>SuQXbIKpmKU0sGO(KuV*CF z{~D;gYXy~$5mY{spz@gjmEU~*?Qb^(71dPX>90STph5#++*kSMKmSFomH+xL(OSXc z8?OKU_kVc!_z6(T{YOt;c?7tkSOq}k^^FjPmz7fPyk=dRQm)-bP`PsJ(WM&%Dwkdv zm{{JrEi*C2woObt{`;)5A-dSPA)jF{%0-(BoH@zI^ShM=#a^hH3d{V!D&>{oNNv-? z^4hx}eDd4h|MC0Z{{A;V{P+X13iefg{3C|aU|&TK&RLo&=uJSL1b_SK=YxljR|INd zUn){ia+SxIh(IxHyWBas(#T8t4yz$0hdk47ryw&wBy~9&W^Q-$7cOD#_ zS*dI8EvbkI0=J9)jL5AgMxvm}5}s0KUdoiPgm;Epaz#H!Ekl%|muQLbh!mfdH}n!j zK7LWpxhc1s5*W+fNm#?3{d}G(XBk($X zRndIZY4PL~_5usp*l2*-_z_Ci*-S4bGB;p#4kbrnvEzOsim@2DLFaD*zT>VrEZ1{HB5mJaETSNMGA#n+CCoNxt zmU1rU-^-tefH3l6b$f}g*b}O%ZGm@i$DYFnj-A@M@7TcTjK^PHNWu?k^-Jj`72!gU z%0j7P$vdG4{}?0>0O7bLeZ4rYQ7n46(x0$C-oz5M_-<@lY>;-T|W9T+OKGQwYOga{*Y4cV!dxFtYZ*5UU#8n;!jDs&c zwOq@y^4#f;5;&^y$yzZYPtz<~S&^*I@pJ!Sv+TpDk*ya*QcC)ZTZQ}pCj^phuup{N zhhJA(Sskg1HMaG3^p3@PMxlldRJRnA1~cr%+tQs|l5>QW83#}jN^Rm!5m&VE8dOc6 zq_{}5KB&yaN}7<2n;|@wCK^P_7~n-7a!WGXBitL2p@(#-sY4_o8Z05ZNyeib8eO<^ z!ndd98f4g@ElvvMIx)_T-#|GzXL>84f5rLhzA* zkNfoej^(xeOFQ?^%nGf*z|6?D7k|5?oh>>9MvBp`5_TmxiSK6UCFh`o-Ih~!8&IoFcDexb z7&OlL$K2$By#{^%gBTd%jkxjPQRfRs1ChpHb#u6`jSUQdYP$MI`-i7SCbv(_F3m0N zTHLXBW%r?-dk^o}a|qaPVRhf+-0I-i_O5}6md?T2#?DYpb7i2mqylFuTxGZ?%9H18 zSoE1qC&DxAm>x*UJW?B-E@$}&RAj1*_Vdy9KH_%jXbD$0TY^l(P#TXUYnMaefnW?> zYGWi~N=^w}CTGJcLQpZ=ELpM-!wngJtidX>{gNjQm-8)dW8!x)4hcw*niEpSr{}WN z1P=Tafrb{+ysqs(w*Sb>tGf@w!MMJq500;*E5&+x0VHxFOJ0uy32S&_9KEbJ|23dX zZk!FG72`l<9Tcr=!cada{=eeyX1pp%L<;{bO)YMhX*UIn5XCeE!}cAOwkbJyK0ei! zlx9!ME`ZXixU!})(%R59);2g>-#%E=+~W?`Lm2^88oUeXIfaGph~0%_V-6S8BHpdA z7ZoR)aOYF#u0C?+7J!OSr)WxfU|rAXs7P<(jX;GuDh~yy-2XLDd5=Nm6BAUv`{HYg zQXz+8E2tRLUnr@MZ>s$Dr=NfN`R_lWK%B~M$tp^<0_z|D_~*x;e(~V7Cl6nH2QKde ztHgoIJzkGt-IPmqQ7n9-$V5|$0F_6VZ@x+$l?TVq-mH#xZ2?drMONZ7*Z+iA(y-r{ zX)lKQlH~-sR8#UeN&(IHmRD9qn`0+Vo`o&>?Qeen&G$bLtKc^L9lAUttKin-O(#vj zR5J?azmQe_FKoJ3Uwti9-2{axpW?~R^Q_NNp&LEs>=R$HvF==21B(f$yfS#%rIit= ze4ut;*m+=a?a;{d3gAkyCj$2rqh$g%*e2K*faEAA?&?tPqyQ%(s2m*`>6SLi%2KRM zPors>N;x~To910M$z4jPZZj3?dYX&Jz<7uYr{ILqmYmU|wg%@>R`7XxJZ9nP>PTs2 zEr5qRP*)iO=4lGzC{A0XAy(7aQQy)NZR>097;Nhv0ea~ioa!E)q3P?{{;}-?UH#vboGx7j7-nWukJZ;?9BNq*Kggsb@%>-D>wEX zdTDTMuCA#ID8(J9E%(-vp?rix205lsn0m3Vm)5mcN-=r`x5BVj1D`$m__1>rYwm^q zo`2K}duV)r(M*#veW}!!USuVzZ4xlSLpUi5v*Tca1PNjrp9rTfR`-;t^8!5lid)Qh zdnMD)FXlc;&?RET$VRzb22hG5N;Ve5t_WEYOr zB3y)K9nH1Mm^Z390!8|3@iU7j7f@8jv62Bn6vE>xBm+;XC@!-~O2q>7Jc?e7GK7s@_(cuNNg6`I=zU^TS%z=xacYfE4+33b(G zRLo;o3U&jwq@uOy0rmg@5CBO;K~zC739Kcb$ix*Ytof2|%zR<1>Ih}ZJ7kM;nNdnK zc5+W8EW@vn%r}dzNVywUx~&k7UYoZ8 z_@&VnPQ?ZExGjsux0ps~sfc=hCCPejZL_6KB)dtv6O|+947E={ynsjYiFj+YN=i(- zsUba!1+eh);y9NAUh(n?>0zbD-Z%t}>vb;HS)yZDA?2p<8F#9WkqSt-3&VgXMqR(18lC092a~Ya&k)? z`DLzBU%9Ua`#klrhL-N8_I?}^fCEPVcxT^u7haCF_m0FUjg)#mcx+&WPYjzsgU#*z z@O`P_4o{@HA{d1yEXE1oP#$H*+sL3(#wHe}k)qmcEEpN^roR@Ui_CYIS?01Wkj4Dq zV#SE{EWSVyqmWBXsEuU)K>@fP&(PH(_up79ifk6o;xcSt2CmQ5?2e;A zDd#ca@$soE97VsLQXZVV{6KV6sHtMH{1P3N*PjI{cNM6-{_lf|h*J3#P{EXXoc^Mw zid3pt2EgL`Dsl{raOJ=L`wxHo)0qB3_>8pUai4Z&ArD+5}8TmlP-yEtOemwIItkhpZ~> z1u7=8T&Z`ARl-VrxRN7>-;ihcx=43%+g+9cFuR-zBNCu-rb5=4uwxbXc>)D=Qk=93 z;S$m|lz6HDIVywo_03%qv&%l$imM<-I8 zplK30BsBAeA@rn(sFfik2tQ`byj&HzmLIt@Q>hj6DwAF-RlJ%*WH2RnS7=1dGTCLZ zNS60mFIJI>jb5V##6>>=fGpOLUdUBn&NL6l?};$sgn3(!i=2DA{0SB;bAjzshlln`OSNZc8a7L zmrSq`vub(;rRx_X3O-MPc6|0LP|4b?M`%&~qN zIau(eiXcClCYLssIACChLLn`u0CsKg4S{_b{Qc#=YHz3k*ru&(cx+~6$L>Q%PMkh< z_R{HdmrtBLcj)L#E4vPiPcOnpvpLoe_8U>2RZ&U+g5?vhTW?UCtVt8fBoo7twvo=>QQ=;I!k%$bg%S&q^-W91 zV6?TbuB9i^(1AySFwjmtM(|REBJNBvKaus;?5K<3iI}r)D@he~yvqzhS5bi5Od!cH zWIyYsr?mEipor-L{$AMYg=*V+htL>)*Z!ls_aC2I+SN5M8K`c79bBfpI3<(20jTHU z(3KR{wTnO!m(d>4iHc?mB91x~lBsZ#4M4>*!?vEsHo9A;6<4xM7AlH=ksE|LGS~=8 zNzN`b5*}6VO`J=c`qQ*!nc-QSr#5wly(F&$C%bA}`r7&?+Xto_J4ZteUG88byyJi_ z05FMUjhKkDcDR$l6k6)J&vOFUNz58vQasj=GfBKyOm5rM!hViao6`8+|^*5}4{NtaWfBDU;kKcLq^>^U%{;O}jLZEWz zmB+X5J-&7KwVRSmG@{&nbo~x33!gl?N>~zi%avQNUb^w{!Ziey<=w}!>_xD**(N$; zHp}{@9?LW7B?)OikB{a`r=)NNYA-lC`+_x7GYgO3dKb3g=U;vI)i+{}&SVwtO8x22 z+*J8FPT|8`2cYt|pMKi4XMeFfNat3{$g`Vip_HhgTxF-oD3vnV*$^^%_z+eE>smU8 z#%EWdq?=vZ-8V8@)7V*59i3cAEStx+oZ+KJdApf{sV)+_1RT|mT}uvlN-tw1!L_|A4} z(Fk#ih#aZRXP+zVW+17;jrm2D04jhevkNUb2Q3u_f@gvq(pUHgur*YNymOM8Dsu(q%a6=F0} zn^%7cO;#Gg#P6y-8puDC+=WSBaF1S(+}_$Udd zOvR)Kw_?dB1Wrg|qW8k>7uk{uJhvIGfmHxJi%g(G&Dtch=S3#Hl!mjcT-5He8>23@ zY6WMvC?}q-a9aH4Z4?QgVoT3)!6&sc)BvBkCiDd!Y3UjUGWJ)u0tmwQB;8h+oS8?n zD;%1UB{MW|!G05CI)ug=_dZ4LP+4Vl1%99#{WNkDs%;6^$Eq;#qos<())87^O2*-E z9JnW-XSpAl2i{lUUHGDAVsDFjN$kShFa zrNrRyK{;K<+n-@6Th!vB?3OxsGegj@FqQ%j0hUzb3hSz*%slu*N-83C&E11zvx}>{ zS9b23TU>*0XNgn!ds{fx&2Y7;4^c}Q?n8Oq^p=U5`?>#sqwiV zh6>;GEM_|dn-r4887rGV>i-pyDKf?}N$L|+6o%obTZp+gLL`c3(O~}wUoLY0gfCYV z_TUj5hVE``??ZfGxdhq9f;$?1;7lVqs z3DUDG-#P@PHTswkV)gH`Na}xyLriX24>NJBvVlCtnC#NST`5^6r4(7S9Zgm#NK&9^ zu`bA!@hP;$GV?NQ#Q>k)aC3dzKzsjWYu`jeY{*~RUg)U?5Y0eMqp)+p-V|FEQ;Mq& zsTAQVt~6UQzoAg#!0&o-D;^R4V^b2mDF7&;bsJsrKa_b&a%FVlvZrl;;x^)>q^6J%F<|1g3 zpmOp0gA3R0pSya0YGDt&x!B{CBYeh^-If0VsIb+902Mx450~Vti{`m@a+QVZEm9NR zf9Uwr4?ctK`6)K3aC<7vk6@J_#Q>O6)mxe>a%2uR;MMCl{oyE-<1~Ehmi>)#H2_A_ zQ#5ABa>cHicF|9!Yg(7iJ1Vs2He?W+^BD`PX<6x!ZDI@3Kc9_twe9)_bN zl(-zo$PFKFDC)uBky>CdB(cgcx@NhtWl?~ld5TOa^@-d=(LC`R-o##4iLWx)(AG0L zv$%WDffKv-ADvlPi?;Rxx47ULis264LhPjkXk6UO=EeMQkzp^4!lctNS{LB2rt2ci zQRKkDl2P;uOTgG%1cgyDN0ck zr`0%O@hg9hy@O?EK-8BagjW&KwO6v>fVCrPGEioUWhscGtJ z>mG)8v9)Wcwy_I98TKhqF+kyk%!85=wYc3OIv(ow=eK4&zcu}Ngr5wdV-fesBKtC$ z=Au4{RLtYTNJJ{sl4U`bVK2ec&jp^`LJ`nIZn<~{+6$;tO=FtTPM%(?YwoFU?Txne z!)j>lgHLl!v=e~GU(@Ub;O37V`cmUi9s7Gyeyt~4dE?R{9I7A6?hvX8X&*oIJz!8bF~*f}t^eR=0TI6Uk-cx(V%L8HELb#K?e zWJM?n`#6k+K_)@u+brce%skw4P(ktu*cQv(lB_^TqKNJw8J!IU4@v8BLRke^;9;|p z*{oYihqE>QQAXxWyl0wH0r7}ku4+{o^J7ju5@rcx#=$QttWc(NYN*h?ofJbtlWK7tu!v_; zE&gD_LoD6!5mwrpR7+fAoB&+}HA*RnbPCqK%#v;5jDRg`)fUc2G|eytDCFpV>M0aMHt0+-M*$+Ut?Quw6z;9TRZwYdPe$&r^cq2 z7FPDG?LBk%IZ*k2QW$dz;sK` zc%-?vJkXe58ba}*y@cb8c| zh0|L><>NR|(NQYTmZ?Zku}*&(O%;PxkXDdYunPa%-+q4oqt9M_{hddzKYf5Ql{fF+ zfBm)q6-Ja-ugi7qwq+?expGskTeytd9}=iMICJ@K=in@5>Dm%E02PB+p4niD8~PHS zc<|xLv=^}pmLL`n&m4Fb+m_*N0Ih8RnzLLBmZ&h7vV0>YIb??HC{S(`Fz^(?4 z)KFNfSF6M!LUUgr;_4+==#eeyed4xEf|-+DPJNlJn<~s39)&B0jzwr%;@Cs`0SJ2W zm`ip@DsrS+hDOD7N97su^Bj2M5efA2I!4iJm5YjN^l(U}o!&%;jAv0T7tviIK5-V5 zm$-v%T|=w851l!G_3HK8r_NnoUONaCe_3T1gFXsH@QBkwDWQBC8RBsuteQ9=g?xl@ zu_#|GDO)cU8S`w|#b#o}PI_XE^e0lL!in`FGNsaB8;??|I9zTuuW0s*M63!oI=CyM zk{n6lt*{48IxkYq8Si#RvRm^HM<9}(KtWZczLQ+Z39WzBNO1{GP3C8K{_1$Ev z=Q-spj7%ak7CC{D47h9-VTP`rktL`DfW<3uMLHC7a15i&hX_Dw6GmMQx8WupsBY|p zY92mPP$(V17g zLba&5!3`f=bR+eA(v)_edO@5UNSWefNl;IqLaF$~`<`khv7u^zR`zu0kED|RB zR*Cv5>T#V7fDz$h)HlJW>$9{Jo?g^ z6Q|A{JbH3rWjAn3Lra&hs-e^q%rElfy2^74-TB28#pMCmN5k#my4KpJPO@w3h1DGE zYwj3m>OfT+z+_7&n&H4|MN1s)^PuLfW1tZ`Jm_o#M;AQfRn_PT;CHYHXPSd>IOO00 zPUB&3A1UTRJ&z3MSvNYBZY*P-N_rBki=ng%V3IAWeL*1Wtc}$wk(VU|FvaU;zzX%q zqU0-}jtobs-Q}&Q!nR4rz*P6pOk3}Gq_L;eTbElLfC?$yhUr78Sp})W@=M0gGxmrP zK4B4tIv)ZRsj;DkPi7u$E*lxY0x061A9nk&cXpPZx_tl2y(dg5qMhs-G~ zP@xbN6I5Dc64Z%aNLumq9&GvB>I53Ow9}rM`~t!5eoTe)QQl z?|<|KY+Kl{pMNF9Kd@DK222fr!8YWrr<*F&fclpdt9<#@H$&rd@WrG&V&VikCn5){ zuzylm7Jx4`RD`q3yJr`7LCx3D(gW2SRG--nc#(xTtCm;Bq7LnG%ywRMne8-hDU&RL zMSPAWO30IA(edjcF4K!Fn~_6zOFWWxq?ShFheQ#85YM`viM*wtB7V7aKD|wbFxm>t zMEMHNHqjaOE|ttE#VukyOy#0fh?K@c!>fwo6tTNP!WAe~svFv;=Xb)6`r?%vm#*D9 zc`J&kR?O2uQc9jz;%A#RnM^Mw9M~tp%=kQF4@z1DL?M@9v#(O@k87}~z(=R- z_9&}mxhiz-3i)f9p(svaw6&2&MQiIlN@Q=*-;3KlxvbQ8xpUE}j0%0O?0JbedDRK= zvFe?RZ}Q&dB0eZAgK^pout(&LREFwF&fVazidNOO);D!`^^eUh?A(9&r4y$v96oV+ zZSUc!xz*9B`Tn8FzM)AtV&cr(eM zG%e=R3VyOA=GKZp5jl|NFDUg{gfXc6@tMycf>{ju}bB zaCeu(RS7f+HFrzLAjZD-j6qottZgl!Y_HTDS3;TtikMB(V)(@b?wn9pgl3||u$jQ5 z9O~+*3>M+6h57VJHW^N@GEC^s98W2zMnprtXlO`YH2w+=Ia-R7Enh>%3wlP2SOtGw zCMw7YU_@s_1g((AF;Bip17$TgWw;xY4;4@8q~*%N8=ifOx*IiH1!RL= zS`l{pB9(!fK)4}N*HYKm0fkpr-`LpH;^OL_y$6mRIezl!iPHxTpV+bMz~t;o_uy1h zdp~^C{9&rJDkAV{fG5NffsPqDbOnwg>9#@~+sNSP4<`?OQBXY%dI_h2Z0_U;KWIG+ zrurwQqrkdbtS(*_3$OKZ5!L-Lq$8R-G^NB5ORnYNmM+BlMGc}fZWa|NRXsJ1@@3ef zgL7Tr4T2gGC=}k8a(`VzYj4-UIGWZEOagCLRyD#q1tk}h+KJRBVdr07iCDBzze1Q+ zaRW{DPjov-8du1roR^bL<1+ge)9zVAl$BvDEgFaF*KHM`!p)YvL~U!PkRy*bi;k*P zsKep({2LWA$!1G}2}LSOQcjeqh{L2zaYav9p@Fhp)jd2nl1?EEaBPEmVtMVrp`#~( zOn_1bMz=@m+u<9LmYt7}il3+FbGj(hNzzh&(K=xLNh;2Ip@0XAr(DtD!LJ*|A!8a3 zbxn|t6LZ;W9YPb&_sjkWi}8t=f%(RQiiY3~_nmyQLksOjv7N~k)D z8Xj*|V@0T;481!!8>@;YlJR6A9pk-ROz-0j zg)4GXNU2E*@dlK4&O&cxsIji4w-XIar(1hQs~fw@{k3q2g-wK}CUj5SdN>M5fh}b1 zO@0ILoOz<-gs`d<)dVWYNc5|-$@z_38Sr#64F$ntHz#I1zc~di2PRf-Ja~HkCOUm_ z6#YqK?rXi}R~)FEc_yg5`70e21eFgq0+r9-`{;{b0~J$G|6`n<-T)OTQ~9d^mH4I# zEy*gVTKU5t{`C4=?*XMSsNA{#_}0BgHxW@DUSH2AfxN!eV9|Zx@-hlk z?q9g};K->fzDO(V&+#oo4$1x>;EH6GZ31Mp7tV&S^SQ`=X=buC3@0ig(2X(VDy%Q8Hs_=kYO%$4_B z7yK6x4rb2E*b}dKRG|ot6wl18Vt^3e!J!fu3v~b`{g;SF$hS)SAF4DZH`5 zRSI=r$W`hu_5_2~P5q-YE4vTvIe2`>oDJfVVOFOW~6oWde2;UFL)n;DP+ zX{{$v57dFtPFeE3?C#s#IY`GijM#>EvmXO|W`+TBCKtwMP~^jlhTK*r=T>oT z-!TEqGrKYh6oipJvlw;SKN0I0Zs{Cs!qM*D`ewAws)=?)@Z4Vu{A!f?1nb>6c~n#G zi;%aQ2#6F>Dy*&7LtkqA%%`qDw@J?S&;VGL2_*7tdN#F63bJy`iz_2l4V_IL!`(yE zT|?8|L$i&sL0`BDj{5MwaFl`~2A&Nd%#A`$Z$) zT?g0;VsVHRNTa=@phetSw_F6_=-ZR^|4MqXZA#K}N_@Kp8>54yV(;ans8Tm)pY(O@ zt;~s;oCYHTn3L^N`9Qr3_^)l5FA4{(6gYA}zd0G#mK3;g^R`SV;4J;n8H1gpr(n0+kJEEo~Rk%)e1+0<#Vr)A|iTyEHN#kz*4=U4Y0 zJhuPvON%@A_l?X3t6OsmE7P(IxP>T0@C+o>&opy3wxV=faRzo!OS6l3~#<=&dgU_Dh%vHF%C1fH&;Hl&&{_G;@I zoJ5OH+IRQ^eI&coQdQgg!MhkWeIHODJ>b_nt7KShG_$0+qP5N9?FP+F)(EQ?}JZY zdG*cvkKRI15$zO!lLxS_-h6NsFYjNKWFi(ZiAI#`_i^30FK~tE0&R>1y!0`)( z?yAijfJ(Mp&&HKFP)W11Iz@pBe9Yh�zl+Sp~g4Y-_vp=cpMC(_`7@xw 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
k+W3um{|r1(1LTd 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⁣^ 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

*s&^ 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

uKRn8iZ`fv6F#SbFI#K$8MT_ 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

xbJ2*0WfQgOVC)A%U=hY(Oyp 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
Lq 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
?KKRt8ng=`*AtDx_V*LYu0Rzyb$ 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�(r+SAIWzC8i6A7+BTmA*_A-mje=@&w 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 Hu0mjfHHsZd literal 0 HcmV?d00001 diff --git a/docs/source/api/index.rst b/docs/source/api/index.rst index 43ee461e124a..452cadfebcc4 100644 --- a/docs/source/api/index.rst +++ b/docs/source/api/index.rst @@ -83,6 +83,7 @@ The following modules are available in the ``isaaclab_contrib`` extension: actuators assets controllers + deformable mdp rl sensors @@ -129,17 +130,10 @@ The following modules are available in the ``isaaclab_physx`` extension: physics renderers sensors - sim.schemas - sim.spawners .. toctree:: :hidden: - lab_physx/isaaclab_physx.assets - lab_physx/isaaclab_physx.cloner - lab_physx/isaaclab_physx.physics - lab_physx/isaaclab_physx.renderers - lab_physx/isaaclab_physx.sensors lab_physx/isaaclab_physx.sim.schemas lab_physx/isaaclab_physx.sim.spawners @@ -163,12 +157,7 @@ The following modules are available in the ``isaaclab_newton`` extension: .. toctree:: :hidden: - lab_newton/isaaclab_newton.assets - lab_newton/isaaclab_newton.cloner - lab_newton/isaaclab_newton.physics - lab_newton/isaaclab_newton.renderers - lab_newton/isaaclab_newton.sensors - lab_newton/isaaclab_newton.sim.schemas + lab_newton/isaaclab_newton.sim.spawners isaaclab_ov extension --------------------- @@ -182,11 +171,6 @@ The following modules are available in the ``isaaclab_ov`` extension: renderers -.. toctree:: - :hidden: - - lab_ov/isaaclab_ov.renderers - isaaclab_assets extension ------------------------- @@ -200,12 +184,6 @@ The following modules are available in the ``isaaclab_assets`` extension: robots sensors -.. toctree:: - :hidden: - - lab_assets/isaaclab_assets.robots - lab_assets/isaaclab_assets.sensors - isaaclab_visualizers extension ------------------------------ @@ -221,14 +199,6 @@ The following modules are available in the ``isaaclab_visualizers`` extension: rerun viser -.. toctree:: - :hidden: - - lab_visualizers/isaaclab_visualizers.kit - lab_visualizers/isaaclab_visualizers.newton - lab_visualizers/isaaclab_visualizers.rerun - lab_visualizers/isaaclab_visualizers.viser - isaaclab_ovphysx extension --------------------------- @@ -244,13 +214,6 @@ The following modules are available in the ``isaaclab_ovphysx`` extension: cloner physics -.. toctree:: - :hidden: - - lab_ovphysx/isaaclab_ovphysx.assets - lab_ovphysx/isaaclab_ovphysx.cloner - lab_ovphysx/isaaclab_ovphysx.physics - isaaclab_experimental extension -------------------------------- @@ -266,13 +229,6 @@ The following modules are available in the ``isaaclab_experimental`` extension: managers utils -.. toctree:: - :hidden: - - lab_experimental/isaaclab_experimental.envs - lab_experimental/isaaclab_experimental.managers - lab_experimental/isaaclab_experimental.utils - isaaclab_tasks_experimental extension -------------------------------------- diff --git a/docs/source/api/lab/isaaclab.assets.rst b/docs/source/api/lab/isaaclab.assets.rst index 28356330b600..b7b980c24231 100644 --- a/docs/source/api/lab/isaaclab.assets.rst +++ b/docs/source/api/lab/isaaclab.assets.rst @@ -15,6 +15,11 @@ RigidObjectCollection RigidObjectCollectionData RigidObjectCollectionCfg + BaseDeformableObject + BaseDeformableObjectData + DeformableObject + DeformableObjectData + DeformableObjectCfg Articulation ArticulationData ArticulationCfg @@ -71,6 +76,37 @@ Rigid Object Collection :show-inheritance: :exclude-members: __init__, class_type +Deformable Object +----------------- + +.. autoclass:: DeformableObject + :members: + :inherited-members: + :show-inheritance: + +.. autoclass:: BaseDeformableObject + :members: + :inherited-members: + :show-inheritance: + +.. autoclass:: DeformableObjectData + :members: + :inherited-members: + :show-inheritance: + :exclude-members: __init__ + +.. autoclass:: BaseDeformableObjectData + :members: + :inherited-members: + :show-inheritance: + :exclude-members: __init__ + +.. autoclass:: DeformableObjectCfg + :members: + :inherited-members: + :show-inheritance: + :exclude-members: __init__, class_type, InitialStateCfg + Articulation ------------ diff --git a/docs/source/api/lab/isaaclab.sim.schemas.rst b/docs/source/api/lab/isaaclab.sim.schemas.rst index bb0651c83ec2..ea29860cbe4e 100644 --- a/docs/source/api/lab/isaaclab.sim.schemas.rst +++ b/docs/source/api/lab/isaaclab.sim.schemas.rst @@ -19,6 +19,9 @@ isaaclab.sim.schemas JointDriveBaseCfg MeshCollisionBaseCfg MassPropertiesCfg + JointDrivePropertiesCfg + FixedTendonPropertiesCfg + DeformableBodyPropertiesBaseCfg .. rubric:: Mesh collision approximations (USD-only, no PhysX schema) @@ -44,9 +47,8 @@ isaaclab.sim.schemas define_mesh_collision_properties modify_mesh_collision_properties modify_fixed_tendon_properties - modify_spatial_tendon_properties - -.. currentmodule:: isaaclab.sim.schemas + define_deformable_body_properties + modify_deformable_body_properties Articulation Root ----------------- @@ -158,11 +160,10 @@ Tendon cfg classes are PhysX-only and live in Deformable Body --------------- -.. note:: - - Deformable body schemas have moved to the PhysX backend extension. See - :class:`isaaclab_physx.sim.schemas.DeformableBodyPropertiesCfg`, - :func:`isaaclab_physx.sim.schemas.define_deformable_body_properties`, and - :func:`isaaclab_physx.sim.schemas.modify_deformable_body_properties`. +.. autoclass:: DeformableBodyPropertiesBaseCfg + :members: + :show-inheritance: + :exclude-members: __init__ - For migration details, see :ref:`migrating-deformables`. +.. autofunction:: define_deformable_body_properties +.. autofunction:: modify_deformable_body_properties diff --git a/docs/source/api/lab/isaaclab.sim.spawners.rst b/docs/source/api/lab/isaaclab.sim.spawners.rst index a31968edfe07..d42fa13e524e 100644 --- a/docs/source/api/lab/isaaclab.sim.spawners.rst +++ b/docs/source/api/lab/isaaclab.sim.spawners.rst @@ -21,6 +21,7 @@ SpawnerCfg RigidObjectSpawnerCfg + DeformableObjectSpawnerCfg Spawners -------- @@ -34,12 +35,10 @@ Spawners :show-inheritance: :exclude-members: __init__ -.. note:: - - ``DeformableObjectSpawnerCfg`` has moved to the PhysX backend extension. See - :class:`isaaclab_physx.sim.spawners.DeformableObjectSpawnerCfg`. - - For migration details, see :ref:`migrating-deformables`. +.. autoclass:: DeformableObjectSpawnerCfg + :members: + :show-inheritance: + :exclude-members: __init__ Shapes ------ @@ -110,6 +109,7 @@ Meshes MeshConeCfg MeshCuboidCfg MeshCylinderCfg + MeshRectangleCfg MeshSphereCfg .. autoclass:: MeshCfg @@ -144,6 +144,13 @@ Meshes :show-inheritance: :exclude-members: __init__, func +.. autofunction:: spawn_mesh_rectangle + +.. autoclass:: MeshRectangleCfg + :members: + :show-inheritance: + :exclude-members: __init__, func + .. autofunction:: spawn_mesh_sphere .. autoclass:: MeshSphereCfg @@ -261,6 +268,10 @@ Materials GlassMdlCfg PhysicsMaterialCfg RigidBodyMaterialCfg + DeformableBodyMaterialBaseCfg + SurfaceDeformableBodyMaterialBaseCfg + DeformableBodyMaterialCfg + SurfaceDeformableBodyMaterialCfg Visual Materials ~~~~~~~~~~~~~~~~ @@ -298,15 +309,36 @@ Physical Materials :members: :exclude-members: __init__, func +.. autofunction:: spawn_deformable_body_material + +.. autoclass:: DeformableBodyMaterialBaseCfg + :members: + :show-inheritance: + :exclude-members: __init__, func + +.. autoclass:: SurfaceDeformableBodyMaterialBaseCfg + :members: + :show-inheritance: + :exclude-members: __init__, func + .. note:: - ``DeformableBodyMaterialCfg``, ``SurfaceDeformableBodyMaterialCfg``, and - ``spawn_deformable_body_material`` have moved to the PhysX backend extension. See - :class:`isaaclab_physx.sim.spawners.materials.DeformableBodyMaterialCfg`, - :class:`isaaclab_physx.sim.spawners.materials.SurfaceDeformableBodyMaterialCfg`, and - :func:`isaaclab_physx.sim.spawners.materials.spawn_deformable_body_material`. + Backend-specific deformable material cfgs live in + :mod:`isaaclab_physx.sim.spawners.materials` and + :mod:`isaaclab_newton.sim.spawners.materials`. The legacy default names below + are forwarded to the deprecated PhysX aliases for compatibility. - For migration details, see :ref:`migrating-deformables`. +.. autoclass:: DeformableBodyMaterialCfg + :no-index: + :members: + :show-inheritance: + :exclude-members: __init__, func + +.. autoclass:: SurfaceDeformableBodyMaterialCfg + :no-index: + :members: + :show-inheritance: + :exclude-members: __init__, func Wrappers -------- diff --git a/docs/source/api/lab_contrib/isaaclab_contrib.deformable.rst b/docs/source/api/lab_contrib/isaaclab_contrib.deformable.rst new file mode 100644 index 000000000000..0267a922c5c8 --- /dev/null +++ b/docs/source/api/lab_contrib/isaaclab_contrib.deformable.rst @@ -0,0 +1,73 @@ +isaaclab_contrib.deformable +=========================== + +.. automodule:: isaaclab_contrib.deformable + + .. rubric:: Classes + + .. autosummary:: + + deformable_object.DeformableObject + deformable_object_data.DeformableObjectData + newton_manager_cfg.VBDSolverCfg + newton_manager_cfg.CoupledMJWarpVBDSolverCfg + newton_manager_cfg.CoupledFeatherstoneVBDSolverCfg + newton_manager_cfg.NewtonModelCfg + vbd_manager.NewtonVBDManager + coupled_mjwarp_vbd_manager.NewtonCoupledMJWarpVBDManager + coupled_featherstone_vbd_manager.NewtonCoupledFeatherstoneVBDManager + +Deformable Object +----------------- + +.. autoclass:: isaaclab_contrib.deformable.deformable_object.DeformableObject + :members: + :inherited-members: + :show-inheritance: + +.. autoclass:: isaaclab_contrib.deformable.deformable_object_data.DeformableObjectData + :members: + :inherited-members: + :show-inheritance: + :exclude-members: __init__ + +Newton Solver Configurations +---------------------------- + +.. autoclass:: isaaclab_contrib.deformable.newton_manager_cfg.VBDSolverCfg + :members: + :show-inheritance: + :exclude-members: __init__ + +.. autoclass:: isaaclab_contrib.deformable.newton_manager_cfg.CoupledMJWarpVBDSolverCfg + :members: + :show-inheritance: + :exclude-members: __init__ + +.. autoclass:: isaaclab_contrib.deformable.newton_manager_cfg.CoupledFeatherstoneVBDSolverCfg + :members: + :show-inheritance: + :exclude-members: __init__ + +.. autoclass:: isaaclab_contrib.deformable.newton_manager_cfg.NewtonModelCfg + :members: + :show-inheritance: + :exclude-members: __init__ + +Newton Solver Managers +---------------------- + +.. autoclass:: isaaclab_contrib.deformable.vbd_manager.NewtonVBDManager + :members: + :inherited-members: + :show-inheritance: + +.. autoclass:: isaaclab_contrib.deformable.coupled_mjwarp_vbd_manager.NewtonCoupledMJWarpVBDManager + :members: + :inherited-members: + :show-inheritance: + +.. autoclass:: isaaclab_contrib.deformable.coupled_featherstone_vbd_manager.NewtonCoupledFeatherstoneVBDManager + :members: + :inherited-members: + :show-inheritance: diff --git a/docs/source/api/lab_experimental/isaaclab_experimental.envs.rst b/docs/source/api/lab_experimental/isaaclab_experimental.envs.rst new file mode 100644 index 000000000000..6e87a9a17cb9 --- /dev/null +++ b/docs/source/api/lab_experimental/isaaclab_experimental.envs.rst @@ -0,0 +1,4 @@ +isaaclab\_experimental.envs +=========================== + +.. automodule:: isaaclab_experimental.envs diff --git a/docs/source/api/lab_experimental/isaaclab_experimental.managers.rst b/docs/source/api/lab_experimental/isaaclab_experimental.managers.rst new file mode 100644 index 000000000000..c0f56ed1a6a7 --- /dev/null +++ b/docs/source/api/lab_experimental/isaaclab_experimental.managers.rst @@ -0,0 +1,4 @@ +isaaclab\_experimental.managers +=============================== + +.. automodule:: isaaclab_experimental.managers diff --git a/docs/source/api/lab_experimental/isaaclab_experimental.utils.rst b/docs/source/api/lab_experimental/isaaclab_experimental.utils.rst new file mode 100644 index 000000000000..49d7bd6dcdb9 --- /dev/null +++ b/docs/source/api/lab_experimental/isaaclab_experimental.utils.rst @@ -0,0 +1,4 @@ +isaaclab\_experimental.utils +============================ + +.. automodule:: isaaclab_experimental.utils diff --git a/docs/source/api/lab_newton/isaaclab_newton.assets.rst b/docs/source/api/lab_newton/isaaclab_newton.assets.rst index 8dc6f9b8f58d..f044f9b089fb 100644 --- a/docs/source/api/lab_newton/isaaclab_newton.assets.rst +++ b/docs/source/api/lab_newton/isaaclab_newton.assets.rst @@ -2,3 +2,82 @@ ======================= .. automodule:: isaaclab_newton.assets + + .. rubric:: Classes + + .. autosummary:: + + Articulation + ArticulationData + RigidObject + RigidObjectData + RigidObjectCollection + RigidObjectCollectionData + DeformableObject + DeformableObjectData + +.. currentmodule:: isaaclab_newton.assets + +Articulation +------------ + +.. autoclass:: Articulation + :members: + :inherited-members: + :show-inheritance: + +.. autoclass:: ArticulationData + :members: + :inherited-members: + :show-inheritance: + :exclude-members: __init__ + +Rigid Object +------------ + +.. autoclass:: RigidObject + :members: + :inherited-members: + :show-inheritance: + +.. autoclass:: RigidObjectData + :members: + :inherited-members: + :show-inheritance: + :exclude-members: __init__ + +Rigid Object Collection +----------------------- + +.. autoclass:: RigidObjectCollection + :members: + :inherited-members: + :show-inheritance: + +.. autoclass:: RigidObjectCollectionData + :members: + :inherited-members: + :show-inheritance: + :exclude-members: __init__ + +Deformable Object +----------------- + +.. note:: + + :class:`isaaclab.assets.DeformableObjectCfg` is the shared configuration + class for deformable objects. The Newton extension exposes the Newton + implementation of :class:`isaaclab.assets.DeformableObject`, while + deformable schema and material cfgs referenced by ``spawn`` remain + backend-specific. + +.. autoclass:: DeformableObject + :members: + :inherited-members: + :show-inheritance: + +.. autoclass:: DeformableObjectData + :members: + :inherited-members: + :show-inheritance: + :exclude-members: __init__ diff --git a/docs/source/api/lab_newton/isaaclab_newton.physics.rst b/docs/source/api/lab_newton/isaaclab_newton.physics.rst index 692293ce88e7..b452f3ad538b 100644 --- a/docs/source/api/lab_newton/isaaclab_newton.physics.rst +++ b/docs/source/api/lab_newton/isaaclab_newton.physics.rst @@ -2,3 +2,103 @@ ======================== .. automodule:: isaaclab_newton.physics + + .. rubric:: Classes + + .. autosummary:: + + NewtonManager + NewtonCfg + NewtonSolverCfg + MJWarpSolverCfg + XPBDSolverCfg + FeatherstoneSolverCfg + KaminoSolverCfg + NewtonCollisionPipelineCfg + HydroelasticSDFCfg + NewtonShapeCfg + NewtonMJWarpManager + NewtonXPBDManager + NewtonFeatherstoneManager + NewtonKaminoManager + +.. currentmodule:: isaaclab_newton.physics + +Physics Manager +--------------- + +.. autoclass:: NewtonManager + :members: + :inherited-members: + :show-inheritance: + +Physics Configuration +--------------------- + +.. autoclass:: NewtonCfg + :members: + :show-inheritance: + :exclude-members: __init__ + +.. autoclass:: NewtonSolverCfg + :members: + :show-inheritance: + :exclude-members: __init__ + +.. autoclass:: MJWarpSolverCfg + :members: + :show-inheritance: + :exclude-members: __init__ + +.. autoclass:: XPBDSolverCfg + :members: + :show-inheritance: + :exclude-members: __init__ + +.. autoclass:: FeatherstoneSolverCfg + :members: + :show-inheritance: + :exclude-members: __init__ + +.. autoclass:: KaminoSolverCfg + :members: + :show-inheritance: + :exclude-members: __init__ + +.. autoclass:: NewtonCollisionPipelineCfg + :members: + :show-inheritance: + :exclude-members: __init__ + +.. autoclass:: HydroelasticSDFCfg + :members: + :show-inheritance: + :exclude-members: __init__ + +.. autoclass:: NewtonShapeCfg + :members: + :show-inheritance: + :exclude-members: __init__ + +Solver Managers +--------------- + +.. autoclass:: NewtonMJWarpManager + :members: + :inherited-members: + :show-inheritance: + +.. autoclass:: NewtonXPBDManager + :members: + :inherited-members: + :show-inheritance: + +.. autoclass:: NewtonFeatherstoneManager + :members: + :inherited-members: + :show-inheritance: + +.. autoclass:: NewtonKaminoManager + :members: + :inherited-members: + :show-inheritance: diff --git a/docs/source/api/lab_newton/isaaclab_newton.sim.schemas.rst b/docs/source/api/lab_newton/isaaclab_newton.sim.schemas.rst index f0dde258fe8c..78fb53b7098c 100644 --- a/docs/source/api/lab_newton/isaaclab_newton.sim.schemas.rst +++ b/docs/source/api/lab_newton/isaaclab_newton.sim.schemas.rst @@ -16,6 +16,7 @@ isaaclab_newton.sim.schemas .. autosummary:: + NewtonDeformableBodyPropertiesCfg NewtonRigidBodyPropertiesCfg NewtonJointDrivePropertiesCfg NewtonCollisionPropertiesCfg @@ -32,6 +33,17 @@ isaaclab_newton.sim.schemas .. currentmodule:: isaaclab_newton.sim.schemas +Deformable Body +--------------- + +.. autoclass:: NewtonDeformableBodyPropertiesCfg + :members: + :show-inheritance: + :exclude-members: __init__ + +Schema define and modify functions remain unified in +:mod:`isaaclab.sim.schemas`. + Rigid Body ---------- diff --git a/docs/source/api/lab_newton/isaaclab_newton.sim.spawners.rst b/docs/source/api/lab_newton/isaaclab_newton.sim.spawners.rst new file mode 100644 index 000000000000..d60b953ce5c8 --- /dev/null +++ b/docs/source/api/lab_newton/isaaclab_newton.sim.spawners.rst @@ -0,0 +1,33 @@ +isaaclab_newton.sim.spawners +============================ + +.. automodule:: isaaclab_newton.sim.spawners.materials + + .. rubric:: Classes + + .. autosummary:: + + NewtonDeformableBodyMaterialCfg + NewtonDeformableMaterialCfg + NewtonSurfaceDeformableBodyMaterialCfg + +Deformable Materials +-------------------- + +Newton provides the backend-specific deformable material cfgs. Deformable material spawning is unified in +:func:`isaaclab.sim.spawners.materials.spawn_deformable_body_material`. + +.. autoclass:: NewtonDeformableBodyMaterialCfg + :members: + :show-inheritance: + :exclude-members: __init__, func + +.. autoclass:: NewtonDeformableMaterialCfg + :members: + :show-inheritance: + :exclude-members: __init__, func + +.. autoclass:: NewtonSurfaceDeformableBodyMaterialCfg + :members: + :show-inheritance: + :exclude-members: __init__, func diff --git a/docs/source/api/lab_ovphysx/isaaclab_ovphysx.assets.rst b/docs/source/api/lab_ovphysx/isaaclab_ovphysx.assets.rst new file mode 100644 index 000000000000..165c12772c06 --- /dev/null +++ b/docs/source/api/lab_ovphysx/isaaclab_ovphysx.assets.rst @@ -0,0 +1,4 @@ +isaaclab\_ovphysx.assets +======================== + +.. automodule:: isaaclab_ovphysx.assets diff --git a/docs/source/api/lab_ovphysx/isaaclab_ovphysx.cloner.rst b/docs/source/api/lab_ovphysx/isaaclab_ovphysx.cloner.rst new file mode 100644 index 000000000000..7467364b075d --- /dev/null +++ b/docs/source/api/lab_ovphysx/isaaclab_ovphysx.cloner.rst @@ -0,0 +1,4 @@ +isaaclab\_ovphysx.cloner +======================== + +.. automodule:: isaaclab_ovphysx.cloner diff --git a/docs/source/api/lab_ovphysx/isaaclab_ovphysx.physics.rst b/docs/source/api/lab_ovphysx/isaaclab_ovphysx.physics.rst new file mode 100644 index 000000000000..aa8c37a4af57 --- /dev/null +++ b/docs/source/api/lab_ovphysx/isaaclab_ovphysx.physics.rst @@ -0,0 +1,4 @@ +isaaclab\_ovphysx.physics +========================= + +.. automodule:: isaaclab_ovphysx.physics diff --git a/docs/source/api/lab_physx/isaaclab_physx.assets.rst b/docs/source/api/lab_physx/isaaclab_physx.assets.rst index b631dcbcb7b5..851a01f9b6e0 100644 --- a/docs/source/api/lab_physx/isaaclab_physx.assets.rst +++ b/docs/source/api/lab_physx/isaaclab_physx.assets.rst @@ -16,7 +16,6 @@ RigidObjectCollectionData DeformableObject DeformableObjectData - DeformableObjectCfg SurfaceGripper SurfaceGripperCfg @@ -78,11 +77,13 @@ Deformable Object :show-inheritance: :exclude-members: __init__ -.. autoclass:: DeformableObjectCfg - :members: - :inherited-members: - :show-inheritance: - :exclude-members: __init__, class_type, InitialStateCfg +.. note:: + + :class:`isaaclab.assets.DeformableObjectCfg` is the shared configuration + class for deformable objects. The PhysX extension provides the PhysX + implementation of :class:`isaaclab.assets.DeformableObject`, while + deformable schema and material cfgs referenced by ``spawn`` remain + backend-specific. Surface Gripper --------------- diff --git a/docs/source/api/lab_physx/isaaclab_physx.sim.schemas.rst b/docs/source/api/lab_physx/isaaclab_physx.sim.schemas.rst index 305269068991..7a01585545da 100644 --- a/docs/source/api/lab_physx/isaaclab_physx.sim.schemas.rst +++ b/docs/source/api/lab_physx/isaaclab_physx.sim.schemas.rst @@ -49,6 +49,9 @@ isaaclab_physx.sim.schemas .. autosummary:: + OmniPhysicsDeformableBodyPropertiesCfg + PhysxDeformableCollisionPropertiesCfg + PhysxDeformableBodyPropertiesCfg DeformableBodyPropertiesCfg .. rubric:: Functions @@ -136,10 +139,25 @@ Tendon Deformable Body --------------- +.. autoclass:: OmniPhysicsDeformableBodyPropertiesCfg + :members: + :show-inheritance: + :exclude-members: __init__ + +.. autoclass:: PhysxDeformableCollisionPropertiesCfg + :members: + :show-inheritance: + :exclude-members: __init__ + +.. autoclass:: PhysxDeformableBodyPropertiesCfg + :members: + :show-inheritance: + :exclude-members: __init__ + .. autoclass:: DeformableBodyPropertiesCfg :members: :show-inheritance: :exclude-members: __init__ -.. autofunction:: define_deformable_body_properties -.. autofunction:: modify_deformable_body_properties +Schema define and modify functions remain unified in +:mod:`isaaclab.sim.schemas`. diff --git a/docs/source/api/lab_physx/isaaclab_physx.sim.spawners.rst b/docs/source/api/lab_physx/isaaclab_physx.sim.spawners.rst index 27463a44425d..7a2ace2a33ff 100644 --- a/docs/source/api/lab_physx/isaaclab_physx.sim.spawners.rst +++ b/docs/source/api/lab_physx/isaaclab_physx.sim.spawners.rst @@ -1,45 +1,41 @@ isaaclab_physx.sim.spawners =========================== -.. automodule:: isaaclab_physx.sim.spawners - - .. rubric:: Submodules - - .. autosummary:: - - materials +.. automodule:: isaaclab_physx.sim.spawners.materials .. rubric:: Classes .. autosummary:: - DeformableObjectSpawnerCfg + PhysxDeformableBodyMaterialCfg + PhysxSurfaceDeformableBodyMaterialCfg + PhysXDeformableMaterialCfg + DeformableBodyMaterialCfg + SurfaceDeformableBodyMaterialCfg -.. currentmodule:: isaaclab_physx.sim.spawners +Deformable Materials +-------------------- -Spawners --------- +PhysX provides the backend-specific deformable material cfgs. Deformable material spawning is unified in +:func:`isaaclab.sim.spawners.materials.spawn_deformable_body_material`. -.. autoclass:: DeformableObjectSpawnerCfg +.. autoclass:: PhysxDeformableBodyMaterialCfg :members: :show-inheritance: - :exclude-members: __init__ - -Materials ---------- - -.. automodule:: isaaclab_physx.sim.spawners.materials - - .. rubric:: Classes - - .. autosummary:: + :exclude-members: __init__, func - DeformableBodyMaterialCfg - SurfaceDeformableBodyMaterialCfg +.. autoclass:: PhysxSurfaceDeformableBodyMaterialCfg + :members: + :show-inheritance: + :exclude-members: __init__, func -.. currentmodule:: isaaclab_physx.sim.spawners.materials +.. autoclass:: PhysXDeformableMaterialCfg + :members: + :show-inheritance: + :exclude-members: __init__, func -.. autofunction:: spawn_deformable_body_material +Deprecated Aliases +------------------ .. autoclass:: DeformableBodyMaterialCfg :members: diff --git a/docs/source/migration/migrating_deformables.rst b/docs/source/migration/migrating_deformables.rst index efb7f7da689e..cce2e15fa3e4 100644 --- a/docs/source/migration/migrating_deformables.rst +++ b/docs/source/migration/migrating_deformables.rst @@ -7,8 +7,11 @@ Migration of Deformables In the newer versions of Omni Physics (107.0 and later), the old deformable body functionality has become deprecated. The following sections describe the changes to migrate to the new Omni Physics API, specifically moving away from -Soft Bodies and towards Surface and Volume Deformables. We currently only support deformable bodies in the PhysX -backend, hence these features are implemented in ``isaaclab_physx``. +Soft Bodies and towards Surface and Volume Deformables. The deformable object asset classes remain in +``isaaclab.assets``. Schema define/modify functions remain unified in ``isaaclab.sim.schemas``, and deformable +material spawning remains unified in ``isaaclab.sim.spawners.materials``. Deformable property and material +configuration classes are backend-specific: PhysX configurations live in ``isaaclab_physx.sim`` and Newton +configurations live in ``isaaclab_newton.sim``. .. note:: @@ -31,8 +34,12 @@ With the new Omni Physics API, deformable bodies are split into two distinct typ The type of deformable is determined by the **physics material** assigned to the object: -- :class:`~isaaclab_physx.sim.DeformableBodyMaterialCfg` creates a **volume** deformable. -- :class:`~isaaclab_physx.sim.SurfaceDeformableBodyMaterialCfg` creates a **surface** deformable. +- :class:`~isaaclab_physx.sim.PhysxDeformableBodyMaterialCfg` creates a PhysX **volume** deformable. +- :class:`~isaaclab_physx.sim.PhysxSurfaceDeformableBodyMaterialCfg` creates a PhysX **surface** deformable. +- :class:`~isaaclab_newton.sim.spawners.materials.NewtonDeformableBodyMaterialCfg` creates a Newton + **volume** deformable. +- :class:`~isaaclab_newton.sim.spawners.materials.NewtonSurfaceDeformableBodyMaterialCfg` creates a Newton + **surface** deformable. Migration from the Old API @@ -41,8 +48,8 @@ Migration from the Old API Import Changes ^^^^^^^^^^^^^^ -All deformable-related classes have moved from ``isaaclab`` to ``isaaclab_physx``. The table below summarizes the -import changes: +Deformable object cfgs remain in ``isaaclab.assets``. Deformable schema and material cfgs should be imported +from the physics backend package: .. list-table:: :header-rows: 1 @@ -51,15 +58,26 @@ import changes: * - Old Import - New Import * - ``from isaaclab.sim import DeformableBodyPropertiesCfg`` - - ``from isaaclab_physx.sim import DeformableBodyPropertiesCfg`` + - ``from isaaclab_physx.sim import PhysxDeformableBodyPropertiesCfg`` * - ``from isaaclab.sim import DeformableBodyMaterialCfg`` - - ``from isaaclab_physx.sim import DeformableBodyMaterialCfg`` + - ``from isaaclab_physx.sim import PhysxDeformableBodyMaterialCfg`` + * - ``from isaaclab.sim import SurfaceDeformableBodyMaterialCfg`` + - ``from isaaclab_physx.sim import PhysxSurfaceDeformableBodyMaterialCfg`` + * - ``from isaaclab.sim import DeformableBodyPropertiesCfg`` + - ``from isaaclab_newton.sim.schemas import NewtonDeformableBodyPropertiesCfg`` + * - ``from isaaclab.sim import DeformableBodyMaterialCfg`` + - ``from isaaclab_newton.sim.spawners.materials import NewtonDeformableBodyMaterialCfg`` + * - ``from isaaclab.sim import SurfaceDeformableBodyMaterialCfg`` + - ``from isaaclab_newton.sim.spawners.materials import NewtonSurfaceDeformableBodyMaterialCfg`` + * - ``from isaaclab_physx.assets import DeformableObjectCfg`` + - ``from isaaclab.assets import DeformableObjectCfg`` Removed Properties ^^^^^^^^^^^^^^^^^^ -The following properties have been **removed** from :class:`~isaaclab_physx.sim.DeformableBodyPropertiesCfg`: +The following properties have been **removed** from +:class:`~isaaclab_physx.sim.PhysxDeformableBodyPropertiesCfg`: - ``collision_simplification`` and related parameters (``collision_simplification_remeshing``, ``collision_simplification_target_triangle_count``, ``collision_simplification_force_conforming``, @@ -75,8 +93,11 @@ The following properties have been **removed** from :class:`~isaaclab_physx.sim. Added Properties ^^^^^^^^^^^^^^^^ -The following properties have been **added** to :class:`~isaaclab_physx.sim.DeformableBodyPropertiesCfg`: +The following properties have been **added** to +:class:`~isaaclab_physx.sim.PhysxDeformableBodyPropertiesCfg`: +- ``deformable_body_enabled``, ``kinematic_enabled``, and ``mass`` — OmniPhysics + deformable body properties owned by the PhysX backend cfg. - ``linear_damping`` — linear damping coefficient [1/s]. - ``max_linear_velocity`` — maximum allowable linear velocity [m/s]. A negative value lets the simulation choose a per-vertex value dynamically (currently only supported for surface deformables). @@ -93,14 +114,20 @@ For a full description of all available properties, refer to the `PhysX deformab Material Changes ^^^^^^^^^^^^^^^^ -The old :class:`DeformableBodyMaterialCfg` (from ``isaaclab.sim``) has been replaced by a new hierarchy in -``isaaclab_physx``: +The deformable material hierarchy is now split by backend: -- :class:`~isaaclab_physx.sim.DeformableBodyMaterialCfg` — for volume deformables. Contains ``density``, +- :class:`~isaaclab.sim.DeformableBodyMaterialBaseCfg` and + :class:`~isaaclab.sim.SurfaceDeformableBodyMaterialBaseCfg` — empty base classes for backend-specific + deformable material configs. +- :class:`~isaaclab_physx.sim.PhysxDeformableBodyMaterialCfg` — for PhysX volume deformables. Contains ``density``, ``static_friction``, ``dynamic_friction``, ``youngs_modulus``, ``poissons_ratio``, and ``elasticity_damping``. -- :class:`~isaaclab_physx.sim.SurfaceDeformableBodyMaterialCfg` — extends the volume material config with +- :class:`~isaaclab_physx.sim.PhysxSurfaceDeformableBodyMaterialCfg` — extends the PhysX volume material config with surface-specific properties: ``surface_thickness``, ``surface_stretch_stiffness``, ``surface_shear_stiffness``, ``surface_bend_stiffness``, and ``bend_damping``. +- :class:`~isaaclab_newton.sim.spawners.materials.NewtonDeformableBodyMaterialCfg` and + :class:`~isaaclab_newton.sim.spawners.materials.NewtonSurfaceDeformableBodyMaterialCfg` contain Newton-specific + fields such as density, particle radius, direct Lame parameters ``k_mu``/``k_lambda`` for volume deformables, + and VBD stiffness parameters for surface deformables. The old ``damping_scale`` property has been removed. Use ``elasticity_damping`` directly instead. @@ -139,19 +166,19 @@ Volume Deformable (Before and After) **After**: .. code-block:: python - :emphasize-lines: 1,2,3 + :emphasize-lines: 1,2 import isaaclab.sim as sim_utils - from isaaclab_physx.assets import DeformableObject, DeformableObjectCfg - from isaaclab_physx.sim import DeformableBodyPropertiesCfg, DeformableBodyMaterialCfg + from isaaclab.assets import DeformableObject, DeformableObjectCfg + from isaaclab_physx.sim import PhysxDeformableBodyMaterialCfg, PhysxDeformableBodyPropertiesCfg cfg = DeformableObjectCfg( prim_path="/World/Origin.*/Cube", spawn=sim_utils.MeshCuboidCfg( size=(0.2, 0.2, 0.2), - deformable_props=DeformableBodyPropertiesCfg(), + deformable_props=PhysxDeformableBodyPropertiesCfg(), visual_material=sim_utils.PreviewSurfaceCfg(), - physics_material=DeformableBodyMaterialCfg(poissons_ratio=0.4, youngs_modulus=1e5), + physics_material=PhysxDeformableBodyMaterialCfg(poissons_ratio=0.4, youngs_modulus=1e5), ), ) cube_object = DeformableObject(cfg=cfg) @@ -159,23 +186,23 @@ Volume Deformable (Before and After) Surface Deformable (New) ^^^^^^^^^^^^^^^^^^^^^^^^ -Surface deformables use :class:`~isaaclab.sim.spawners.meshes.MeshSquareCfg` for 2D meshes, combined with -:class:`~isaaclab_physx.sim.SurfaceDeformableBodyMaterialCfg`: +Surface deformables use :class:`~isaaclab.sim.spawners.meshes.MeshRectangleCfg` for 2D meshes, combined with +:class:`~isaaclab_physx.sim.PhysxSurfaceDeformableBodyMaterialCfg`: .. code-block:: python import isaaclab.sim as sim_utils - from isaaclab_physx.assets import DeformableObject, DeformableObjectCfg - from isaaclab_physx.sim import DeformableBodyPropertiesCfg, SurfaceDeformableBodyMaterialCfg + from isaaclab.assets import DeformableObject, DeformableObjectCfg + from isaaclab_physx.sim import PhysxDeformableBodyPropertiesCfg, PhysxSurfaceDeformableBodyMaterialCfg cfg = DeformableObjectCfg( prim_path="/World/Origin.*/Cloth", - spawn=sim_utils.MeshSquareCfg( - size=1.5, + spawn=sim_utils.MeshRectangleCfg( + size=(1.5, 1.5), resolution=(21, 21), - deformable_props=DeformableBodyPropertiesCfg(), + deformable_props=PhysxDeformableBodyPropertiesCfg(), visual_material=sim_utils.PreviewSurfaceCfg(), - physics_material=SurfaceDeformableBodyMaterialCfg(poissons_ratio=0.4, youngs_modulus=1e5), + physics_material=PhysxSurfaceDeformableBodyMaterialCfg(poissons_ratio=0.4, youngs_modulus=1e5), ), ) cloth_object = DeformableObject(cfg=cfg) @@ -189,8 +216,8 @@ Deformable properties can also be applied to imported USD assets using .. code-block:: python import isaaclab.sim as sim_utils - from isaaclab_physx.assets import DeformableObject, DeformableObjectCfg - from isaaclab_physx.sim import DeformableBodyPropertiesCfg, DeformableBodyMaterialCfg + from isaaclab.assets import DeformableObject, DeformableObjectCfg + from isaaclab_physx.sim import PhysxDeformableBodyMaterialCfg, PhysxDeformableBodyPropertiesCfg from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR @@ -198,8 +225,8 @@ Deformable properties can also be applied to imported USD assets using prim_path="/World/Origin.*/Teddy", spawn=sim_utils.UsdFileCfg( usd_path=f"{ISAACLAB_NUCLEUS_DIR}/Objects/Teddy_Bear/teddy_bear.usd", - deformable_props=DeformableBodyPropertiesCfg(), - physics_material=DeformableBodyMaterialCfg(poissons_ratio=0.4, youngs_modulus=1e5), + deformable_props=PhysxDeformableBodyPropertiesCfg(), + physics_material=PhysxDeformableBodyMaterialCfg(poissons_ratio=0.4, youngs_modulus=1e5), scale=[0.05, 0.05, 0.05], ), ) @@ -210,12 +237,13 @@ Limitations ~~~~~~~~~~~ - **Kinematic targets are volume-only.** Calling - :meth:`~isaaclab_physx.assets.DeformableObject.write_nodal_kinematic_target_to_sim_index` on a surface + :meth:`~isaaclab.assets.DeformableObject.write_nodal_kinematic_target_to_sim_index` on a surface deformable will raise a ``ValueError``. - **Surface-specific solver properties** (``collision_pair_update_frequency``, ``collision_iteration_multiplier``) have no effect on volume deformables. -- **Deformables are PhysX-only.** The ``isaaclab_physx`` extension is required; other physics backends - do not support deformable bodies through Isaac Lab yet. +- **Newton deformables are experimental.** They are implemented in + :mod:`isaaclab_contrib.deformable` and currently target VBD-based solvers and + coupled rigid-deformable workflows. .. _Omni Physics documentation: https://docs.omniverse.nvidia.com/kit/docs/omni_physics/110.0/dev_guide/deformables/deformable_bodies.html diff --git a/docs/source/migration/migrating_to_isaaclab_3-0.rst b/docs/source/migration/migrating_to_isaaclab_3-0.rst index efa7d8678023..fc8657658aa6 100644 --- a/docs/source/migration/migrating_to_isaaclab_3-0.rst +++ b/docs/source/migration/migrating_to_isaaclab_3-0.rst @@ -69,9 +69,9 @@ New ``isaaclab_physx`` and ``isaaclab_newton`` Extensions Two new backend extensions have been introduced: -- **``isaaclab_physx``** — PhysX-specific implementations of all asset and sensor classes. -- **``isaaclab_newton``** — Newton-specific implementations of asset classes (Articulation and - RigidObject). +- **``isaaclab_physx``** — PhysX-specific implementations of asset and sensor classes. +- **``isaaclab_newton``** — Newton-specific implementations of supported asset classes, including + articulations, rigid objects, and deformable objects. The following classes have been moved to ``isaaclab_physx``: @@ -81,17 +81,18 @@ The following classes have been moved to ``isaaclab_physx``: * - Isaac Lab 2.x - Isaac Lab 3.0 - * - ``from isaaclab.assets import DeformableObject`` - - ``from isaaclab_physx.assets import DeformableObject`` - * - ``from isaaclab.assets import DeformableObjectCfg`` - - ``from isaaclab_physx.assets import DeformableObjectCfg`` - * - ``from isaaclab.assets import DeformableObjectData`` - - ``from isaaclab_physx.assets import DeformableObjectData`` * - ``from isaaclab.assets import SurfaceGripper`` - ``from isaaclab_physx.assets import SurfaceGripper`` * - ``from isaaclab.assets import SurfaceGripperCfg`` - ``from isaaclab_physx.assets import SurfaceGripperCfg`` +.. note:: + + Deformable object public APIs remain in the backend-neutral ``isaaclab`` + package. Continue importing :class:`~isaaclab.assets.DeformableObject`, + :class:`~isaaclab.assets.DeformableObjectCfg`, and + :class:`~isaaclab.assets.DeformableObjectData` from ``isaaclab.assets``. + .. note:: The ``isaaclab_physx`` extension is installed automatically with Isaac Lab. No additional @@ -382,13 +383,17 @@ release. The old soft body API has been deprecated and replaced by two distinct types: **volume deformables** (3D FEM tetrahedral meshes) and **surface deformables** (2D triangle cloth meshes). The deformable type is determined by the physics material assigned: -- :class:`~isaaclab_physx.sim.DeformableBodyMaterialCfg` for volume deformables. -- :class:`~isaaclab_physx.sim.SurfaceDeformableBodyMaterialCfg` for surface deformables. +- :class:`~isaaclab_physx.sim.PhysxDeformableBodyMaterialCfg` for PhysX volume deformables. +- :class:`~isaaclab_physx.sim.PhysxSurfaceDeformableBodyMaterialCfg` for PhysX surface deformables. +- :class:`~isaaclab_newton.sim.spawners.materials.NewtonDeformableBodyMaterialCfg` for Newton volume deformables. +- :class:`~isaaclab_newton.sim.spawners.materials.NewtonSurfaceDeformableBodyMaterialCfg` for Newton surface + deformables. -All deformable-related classes have moved from ``isaaclab`` to ``isaaclab_physx``, as shown -in the import table above. Several properties on -:class:`~isaaclab_physx.sim.DeformableBodyPropertiesCfg` have been removed or added to match -the new Omni Physics schema. +Deformable property and material cfgs are backend-specific. Several properties on +:class:`~isaaclab_physx.sim.PhysxDeformableBodyPropertiesCfg` have been removed or added to +match the new Omni Physics schema. The common +:class:`~isaaclab.sim.DeformableBodyPropertiesBaseCfg` is now empty; OmniPhysics +deformable body fields are owned by :class:`~isaaclab_physx.sim.PhysxDeformableBodyPropertiesCfg`. For a comprehensive guide covering the full deformable API migration — including removed and added properties, material changes, code examples for both volume and surface deformables, and @@ -877,7 +882,7 @@ Here's a complete example showing how to update your code: .. code-block:: python - from isaaclab_physx.assets import DeformableObject, DeformableObjectCfg + from isaaclab.assets import DeformableObject, DeformableObjectCfg from isaaclab_physx.assets import SurfaceGripper, SurfaceGripperCfg from isaaclab.assets import RigidObjectCollection # unchanged @@ -1219,7 +1224,7 @@ All ``.data.*`` properties on asset and sensor classes now return the underlying ``wp.array`` and exposes explicit ``.torch`` and ``.warp`` accessors. This change applies to all asset classes (:class:`~isaaclab.assets.Articulation`, :class:`~isaaclab.assets.RigidObject`, :class:`~isaaclab.assets.RigidObjectCollection`, -:class:`~isaaclab_physx.assets.DeformableObject`) and all sensor classes +:class:`~isaaclab.assets.DeformableObject`) and all sensor classes (:class:`~isaaclab_physx.sensors.ContactSensor`, :class:`~isaaclab_physx.sensors.Imu`, :class:`~isaaclab_physx.sensors.Pva`, :class:`~isaaclab_physx.sensors.FrameTransformer`). @@ -1276,8 +1281,8 @@ Common patterns that need updating: - ``isaaclab`` / ``isaaclab_physx`` * - :class:`~isaaclab.assets.RigidObjectCollection` - ``isaaclab`` / ``isaaclab_physx`` - * - :class:`~isaaclab_physx.assets.DeformableObject` - - ``isaaclab_physx`` + * - :class:`~isaaclab.assets.DeformableObject` + - ``isaaclab`` / ``isaaclab_physx`` / ``isaaclab_newton`` * - :class:`~isaaclab_physx.sensors.ContactSensor` - ``isaaclab_physx`` * - :class:`~isaaclab_physx.sensors.Imu` diff --git a/docs/source/overview/core-concepts/multi_backend_architecture.rst b/docs/source/overview/core-concepts/multi_backend_architecture.rst index 5d894c6820f8..8a946b50023c 100644 --- a/docs/source/overview/core-concepts/multi_backend_architecture.rst +++ b/docs/source/overview/core-concepts/multi_backend_architecture.rst @@ -43,6 +43,10 @@ This pattern applies to all simulation components: - :class:`~isaaclab.assets.RigidObject` - :class:`~isaaclab_physx.assets.RigidObject` - :class:`~isaaclab_newton.assets.RigidObject` + * - Deformable Object + - :class:`~isaaclab.assets.DeformableObject` + - :class:`~isaaclab_physx.assets.DeformableObject` + - :class:`~isaaclab_newton.assets.DeformableObject` * - Contact Sensor - :class:`~isaaclab.sensors.ContactSensor` - :class:`~isaaclab_physx.sensors.ContactSensor` @@ -254,6 +258,8 @@ the established conventions: │ │ └── articulation_data.py │ ├── rigid_object/ │ │ └── ... + │ ├── deformable_object/ + │ │ └── ... │ └── rigid_object_collection/ │ └── ... ├── sensors/ @@ -384,4 +390,6 @@ See Also - :doc:`/source/features/hydra` — preset system for multi-backend environment configurations - :doc:`physical-backends/index` — feature matrix and per-backend guides (PhysX, Newton, OvPhysX) - :doc:`physical-backends/newton/index` — Newton backend guide +- :doc:`physical-backends/newton/newton-manager-abstraction` — adding Newton solver managers and + coupled solvers - :doc:`renderers` — renderer backend architecture diff --git a/docs/source/overview/core-concepts/physical-backends/newton/index.rst b/docs/source/overview/core-concepts/physical-backends/newton/index.rst index 087adda9cb23..71e1ffed14e6 100644 --- a/docs/source/overview/core-concepts/physical-backends/newton/index.rst +++ b/docs/source/overview/core-concepts/physical-backends/newton/index.rst @@ -20,10 +20,18 @@ have successfully deployed a Newton-trained locomotion policy to a G1 robot. Newton can support `multiple solvers `_ for handling different types of physics simulation. The Isaac Lab integration ships -two solver pages: +the following solver pages: * :doc:`mjwarp-solver` — the primary, validated solver path. * :doc:`kamino-solver` — beta support on selected classic tasks. +* :doc:`using-vbd-solver` — experimental VBD solver for cloth and soft bodies, + available through :mod:`isaaclab_contrib.deformable` and the MJWarp + VBD or + Featherstone + VBD coupled managers. + +Each solver is exposed as a small subclass of +:class:`~isaaclab_newton.physics.NewtonManager`. See +:doc:`newton-manager-abstraction` for the developer-facing guide to adding a +new solver or a coupled solver. During the beta phase, breaking changes and incomplete documentation are still expected. Official support and debugging assistance will follow once the framework @@ -41,5 +49,7 @@ new backend, see :doc:`../../multi_backend_architecture`. supported-features mjwarp-solver kamino-solver + using-vbd-solver + newton-manager-abstraction warp-environments warp-env-migration diff --git a/docs/source/overview/core-concepts/physical-backends/newton/mjwarp-solver.rst b/docs/source/overview/core-concepts/physical-backends/newton/mjwarp-solver.rst index 805f51a7ec2d..18bdeabfc614 100644 --- a/docs/source/overview/core-concepts/physical-backends/newton/mjwarp-solver.rst +++ b/docs/source/overview/core-concepts/physical-backends/newton/mjwarp-solver.rst @@ -7,7 +7,9 @@ for the Newton backend in Isaac Lab. It is enabled by setting :class:`~isaaclab_newton.physics.MJWarpSolverCfg`, usually exposed as the ``newton_mjwarp`` physics preset on a task configuration. Newton ships with beta support for an alternative Kamino solver — see :doc:`kamino-solver` and -:ref:`hydra-backend-solver-presets` for how presets are selected. +:ref:`hydra-backend-solver-presets` for how presets are selected. For details +on how solver-specific managers are implemented, or how to add a new solver +manager, see :doc:`newton-manager-abstraction`. .. note:: diff --git a/docs/source/overview/core-concepts/physical-backends/newton/newton-manager-abstraction.rst b/docs/source/overview/core-concepts/physical-backends/newton/newton-manager-abstraction.rst new file mode 100644 index 000000000000..312d0c5bb792 --- /dev/null +++ b/docs/source/overview/core-concepts/physical-backends/newton/newton-manager-abstraction.rst @@ -0,0 +1,237 @@ +Newton Manager Abstraction +========================== + +Newton exposes multiple solver families, and Isaac Lab keeps that flexibility by +making each solver an implementation detail of a small +:class:`~isaaclab_newton.physics.NewtonManager` subclass. The simulation context +still sees a normal physics manager; the solver configuration decides which +manager class is used. + +For most new Newton solvers, the integration surface is intentionally small: + +* define a solver config that inherits from + :class:`~isaaclab_newton.physics.NewtonSolverCfg`; +* point the config's ``class_type`` at a manager subclass; +* implement ``_build_solver()`` in that manager; +* set the three base-manager slots: ``_solver``, ``_use_single_state``, and + ``_needs_collision_pipeline``. + +The existing MuJoCo Warp, XPBD, Featherstone, and Kamino managers are examples +of this pattern. + + +Adding a Solver Manager +----------------------- + +The solver config carries both user-tunable solver parameters and the manager +dispatch target: + +.. code-block:: python + + from isaaclab_newton.physics import NewtonManager, NewtonSolverCfg + from isaaclab.utils.configclass import configclass + + + @configclass + class MySolverCfg(NewtonSolverCfg): + class_type: type[NewtonManager] | str = "{DIR}.my_solver_manager:NewtonMySolverManager" + solver_type: str = "my_solver" + iterations: int = 16 + + +``NewtonCfg`` copies ``solver_cfg.class_type`` into its own ``class_type`` in +``__post_init__``. User code keeps the normal shape: + +.. code-block:: python + + from isaaclab.sim import SimulationCfg + from isaaclab_newton.physics import NewtonCfg + + sim_cfg = SimulationCfg( + physics=NewtonCfg( + solver_cfg=MySolverCfg(iterations=32), + num_substeps=2, + ) + ) + + +The manager then owns solver construction: + +.. code-block:: python + + from newton import Model + from newton.solvers import SolverMySolver + + from isaaclab_newton.physics import NewtonManager + + + class NewtonMySolverManager(NewtonManager): + @classmethod + def _build_solver(cls, model: Model, solver_cfg: MySolverCfg) -> None: + NewtonManager._solver = SolverMySolver(model, iterations=solver_cfg.iterations) + NewtonManager._use_single_state = False + NewtonManager._needs_collision_pipeline = True + + +``_use_single_state`` tells the base manager whether the solver advances in +place or swaps input/output states. ``_needs_collision_pipeline`` tells the base +manager whether to allocate and pass Newton collision-pipeline contacts to the +solver. A solver with its own internal contact detector can set it to ``False``. + +Optional Overrides +------------------ + +Most managers only implement ``_build_solver()``. Override more only when the +solver actually needs it: + +* ``_initialize_contacts()``: allocate custom contact buffers or support an + internal contact detector. +* ``_step_solver(state_0, state_1, control, substep_dt)``: change one substep of + solver execution while keeping the base simulation loop. +* ``_simulate_physics_only()``: add per-step work around the base substep loop, + such as rebuilding a BVH. +* ``step()``: handle solver-specific reset masks, graph capture, or model-change + notification before delegating to the base manager. +* ``start_simulation()`` or ``instantiate_builder_from_stage()``: customize model + building or post-finalize setup. +* ``_solver_specific_clear()``: release any class-level state owned by the + solver manager. + +Keep the manager name prefixed with ``Newton`` and the solver config grouped +with the other Newton solver configs so autocomplete and backend discovery stay +predictable. + + +Custom Coupled Solvers +---------------------- + +Coupled solvers use the same abstraction. Instead of wrapping one Newton solver, +a coupled manager constructs two or more sub-solvers and overrides +``_step_solver()`` to define the substep order. +That means a custom coupling usually needs only a config that stores existing +solver configs plus a manager that defines how data flows between them; the +component solvers can stay unchanged. + +The MJWarp + VBD deformable manager is a concrete example: + +* :class:`~isaaclab_contrib.deformable.CoupledMJWarpVBDSolverCfg` stores a + ``rigid_solver_cfg`` for :class:`~isaaclab_newton.physics.MJWarpSolverCfg`, a + ``soft_solver_cfg`` for :class:`~isaaclab_contrib.deformable.VBDSolverCfg`, + and a ``coupling_mode``. +* ``NewtonCoupledMJWarpVBDManager._build_solver()`` constructs + ``SolverMuJoCo`` and ``SolverVBD`` from those sub-configs. +* ``_step_solver()`` dispatches to either one-way or two-way coupling. +* The base ``NewtonManager`` still owns state allocation, substep iteration, + Fabric synchronization, and reset/clear lifecycle. + +The two-way MJWarp + VBD substep stays compact because it is expressed as a +short coupling algorithm: + +.. admonition:: Algorithm: Two-Way MJWarp + VBD Substep + :class: note + + **Inputs:** rigid body state, deformable particle state, and the shared + Newton collision pipeline. + + **Output:** updated rigid body and deformable particle state for one Newton + substep. + + 1. **Reset force accumulators.** + Clear the rigid body and particle force buffers before evaluating the + next contact pass. + + 2. **Detect coupled contacts.** + Run Newton collision detection once over the current rigid and + deformable state. + + 3. **Apply soft-to-rigid reactions.** + Inject body-particle contact reactions into ``body_f`` so the rigid + bodies can be pushed back by the deformable contact penalties. + + 4. **Advance the rigid solver.** + Step the MJWarp rigid solver with the coupled contact forces applied. + + 5. **Preserve shared contacts for the soft solve.** + Clear particle forces written during the rigid step while keeping the + detected contact information available. + + 6. **Advance the deformable solver.** + Step the VBD soft solver against the same coupled contacts. + + +This keeps the custom part focused on the coupling policy. The manager does not +need to reimplement scene loading, asset buffers, reset handling, or the outer +simulation loop. + +.. figure:: ../../../../_static/newton/franka-mjwarp-vbd-coupling.png + :align: center + :figwidth: 480px + :class: square-crop-figure + :alt: Franka manipulating a deformable object with MJWarp and VBD coupling + + Franka manipulation using MJWarp for rigid bodies and VBD for the deformable + object. + +You can exercise this coupling path with the Franka soft-body lifting task: + +.. code-block:: bash + + ./isaaclab.sh -p scripts/environments/zero_agent.py --task Isaac-Lift-Soft-Franka-v0 --num_envs 1 --visualizer kit + +For the surface-deformable cloth variant, use ``--task Isaac-Lift-Cloth-Franka-v0``. + + +This environment configures +:class:`~isaaclab_contrib.deformable.CoupledMJWarpVBDSolverCfg` with +``coupling_mode="two_way"``. + +Tuning the Franka Soft-Body Lift +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Tune the coupled contact behavior before training a policy: + +* Start with ``coupling_mode="two_way"``. Compared with one-way coupling, two-way + coupling can prevent clipping more easily because body-particle contact + penalties can push the robot back instead of only moving the deformable. +* Use a small scripted grasp/lift check before training to confirm that grasping + is possible and to inspect what clips when the grasp fails. +* Lower the arm actuator stiffness enough that the arm can respond to contact + penalties. Prefer the arm being pushed back over the gripper clipping into the + deformable. +* Tune :attr:`~isaaclab_contrib.deformable.NewtonModelCfg.soft_contact_ke` + first. Increase it only as much as needed to prevent clipping, then adjust + :attr:`~isaaclab_contrib.deformable.NewtonModelCfg.soft_contact_mu` so the + gripper can carry the object without requiring an obviously unphysical + friction value. Use + :attr:`~isaaclab_contrib.deformable.NewtonModelCfg.soft_contact_kd` for + stabilization if contacts chatter. +* Tune the ``soft_contact_*`` values together with ``shape_material_*`` values + because rigid shape material parameters also affect the effective contact. +* If ``soft_contact_ke`` is not sufficient, or ``soft_contact_mu`` must be + unphysically high, tune the Franka arm and hand actuator stiffness and maximum + effort. For the gripper command, fully close the fingers and let the actuator + maximum effort limit the actual squeeze. +* If the deformable no longer visibly deforms, ``soft_contact_ke`` is likely too + high. +* If contacts are unstable or missed, increase the deformable mesh resolution or + increase ``particle_radius`` in the deformable material so contact is detected + earlier from a larger distance. +* If the rigid shapes still clip through the deformable, increase + :attr:`~isaaclab_contrib.deformable.VBDSolverCfg.iterations`; more VBD + iterations can improve contact convergence. + + +When to Add a Coupled Manager +----------------------------- + +Add a coupled manager when one solver cannot own the whole model step by itself: + +* rigid bodies should use one solver while particles or cloth use another; +* contact detection is shared, but each solver consumes the contacts + differently; +* you need a custom force, impulse, or state exchange between solvers; +* the substep order is part of the algorithm. + +Use a normal single-solver manager when all physics can be advanced by one +Newton solver. Use a coupled manager only for the small amount of glue that is +truly solver-specific. diff --git a/docs/source/overview/core-concepts/physical-backends/newton/supported-features.rst b/docs/source/overview/core-concepts/physical-backends/newton/supported-features.rst index d7fd6aabf0d7..def15735fc98 100644 --- a/docs/source/overview/core-concepts/physical-backends/newton/supported-features.rst +++ b/docs/source/overview/core-concepts/physical-backends/newton/supported-features.rst @@ -35,10 +35,18 @@ isaaclab * Rigid Object and Rigid Object Collection APIs * Sensors: Contact Sensor, IMU, Frame Transformer, Joint Wrench, PVA * Direct and Manager-based single-agent workflows +* Backend-neutral deformable object API * Omniverse Kit visualizer (when Isaac Sim is installed) * Newton-Warp visualizer (kit-less) * Tiled rendering via the Newton-Warp renderer +isaaclab_contrib +^^^^^^^^^^^^^^^^ + +* Experimental Newton deformable objects +* VBD deformable solver (see :doc:`using-vbd-solver`) +* Coupled MJWarp + VBD and Featherstone + VBD solver managers + The following sensors are backend-agnostic (implemented in ``isaaclab`` core) and work transparently with Newton: @@ -71,6 +79,8 @@ Manager-based workflows: Unitree G1, Go1, Go2, Unitree H1, Spot * Locomotion velocity, rough terrain: Anymal-C, Cassie, Go1, Go2 * Manipulation: reach (Franka, UR10), cabinet, dexsuite +* Manipulation lift with deformable objects: Franka soft-body lift, Franka cloth + lift (via coupled MJWarp + VBD) Solver Coverage @@ -80,15 +90,19 @@ Solver Coverage * **Kamino solver**: beta. Currently validated on ``Isaac-Cartpole-Direct-v0``, ``Isaac-Ant-Direct-v0``, ``Isaac-Cartpole-v0``, and ``Isaac-Ant-v0``. See :doc:`kamino-solver`. - -Other Newton solvers (e.g. VBD) are not yet exposed through Isaac Lab. +* **VBD solver**: experimental, exposed through :mod:`isaaclab_contrib.deformable` + for cloth and soft-body simulation. Most often used inside the coupled + MJWarp + VBD or Featherstone + VBD managers so one solver advances rigid + bodies and VBD advances deformable particles. See :doc:`using-vbd-solver` + and :doc:`newton-manager-abstraction`. Known Gaps ---------- -* Soft bodies, particles, and other non-rigid PhysX features are not yet - available through Newton. +* Soft bodies and particles are available through the experimental VBD path in + :mod:`isaaclab_contrib.deformable`; other non-rigid PhysX features are not + yet covered. * Behaviour on stiff contact stacks can diverge from PhysX; expect to retune contact and substep parameters when porting tasks across backends. * Multi-agent and self-play workflows are not yet wired up for Newton. diff --git a/docs/source/overview/core-concepts/physical-backends/newton/using-vbd-solver.rst b/docs/source/overview/core-concepts/physical-backends/newton/using-vbd-solver.rst new file mode 100644 index 000000000000..4e50b1f4339b --- /dev/null +++ b/docs/source/overview/core-concepts/physical-backends/newton/using-vbd-solver.rst @@ -0,0 +1,344 @@ +.. _newton-using-vbd: + +Using the VBD Solver +==================== + +Vertex Block Descent (VBD) is a Newton solver for cloth and soft-body +simulation. In Isaac Lab, VBD is enabled by selecting a +:class:`~isaaclab_newton.physics.NewtonCfg` whose ``solver_cfg`` is provided by +:mod:`isaaclab_contrib.deformable`. + +VBD support is experimental. The solver managers, configuration fields, and +recommended tuning values may change while Newton deformable support is under +active development. A task that works with PhysX or with Newton's MuJoCo-Warp +solver may still need deformable assets, materials, contacts, and coupling tuned +before it works well with VBD. + +VBD is usually exposed through a task-specific physics preset rather than a +general ``newton_vbd`` preset. Deformable-only scenes can use +:class:`~isaaclab_contrib.deformable.VBDSolverCfg` directly. Robot or +rigid-body scenes usually use +:class:`~isaaclab_contrib.deformable.CoupledMJWarpVBDSolverCfg` or +:class:`~isaaclab_contrib.deformable.CoupledFeatherstoneVBDSolverCfg` so one +solver advances rigid bodies and VBD advances deformable particles. + +Start from a Supported Deformable Task +-------------------------------------- + +Before adding VBD to a new task, first run one of the experimental Franka +deformable tasks: + +.. code-block:: bash + + ./isaaclab.sh -p scripts/environments/zero_agent.py --task Isaac-Lift-Soft-Franka-v0 --num_envs 1 --visualizer kit + +For the surface-deformable cloth variant, use: + +.. code-block:: bash + + ./isaaclab.sh -p scripts/environments/zero_agent.py --task Isaac-Lift-Cloth-Franka-v0 --num_envs 1 --visualizer kit + +Both tasks configure MJWarp for the rigid Franka and VBD for the deformable +object through +:class:`~isaaclab_contrib.deformable.CoupledMJWarpVBDSolverCfg`. +Use these tasks as starting points for asset setup, solver coupling, and contact +tuning. + +Add a VBD Physics Preset +------------------------ + +Tasks that support multiple physics options usually store ``SimulationCfg.physics`` +as a :class:`~isaaclab_tasks.utils.hydra.PresetCfg`. For deformable Newton tasks, +the preset can use a small :class:`~isaaclab_newton.physics.NewtonCfg` subclass +to carry :class:`~isaaclab_contrib.deformable.NewtonModelCfg` alongside the +normal Newton fields: + +.. code-block:: python + + from isaaclab.utils.configclass import configclass + from isaaclab_newton.physics import NewtonCfg + + from isaaclab_contrib.deformable import NewtonModelCfg + + + @configclass + class DeformableNewtonCfg(NewtonCfg): + model_cfg: NewtonModelCfg | None = None + + +The Franka soft-body task defines a ``newton_mjwarp_vbd`` preset that couples +MJWarp and VBD: + +.. literalinclude:: ../../../../../../source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/franka_soft_env_cfg.py + :language: python + :start-at: class PhysicsCfg + :end-at: default = newton_mjwarp_vbd + :emphasize-lines: 4-32 + +The important pieces are: + +* Add a Newton physics preset whose value is ``DeformableNewtonCfg``. +* Use :class:`~isaaclab_contrib.deformable.CoupledMJWarpVBDSolverCfg` when rigid + bodies and deformables must interact in the same scene. +* Use ``soft_solver_cfg=VBDSolverCfg(integrate_with_external_rigid_solver=True)`` + inside a coupled solver so VBD advances only the deformable particles. +* Add :class:`~isaaclab_contrib.deformable.NewtonModelCfg` when body-particle or + self-contact values need task-level tuning. +* Keep the preset at the same config path used by the task's + :class:`~isaaclab.sim.SimulationCfg`, for example ``env.sim.physics``. + +You can select the deformable Newton preset globally: + +.. code-block:: bash + + ./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/train.py --task=Isaac-Lift-Soft-Franka-v0 presets=newton_mjwarp_vbd + +or select the physics field directly: + +.. code-block:: bash + + ./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/train.py --task=Isaac-Lift-Soft-Franka-v0 env.sim.physics=newton_mjwarp_vbd + +Use the direct path override when only one task field should use the VBD preset. +Use ``presets=newton_mjwarp_vbd`` when you want every matching preset field in +the task config to resolve to that preset. Isaac Lab training scripts accept +these Hydra overrides after the regular command line flags; no separator is +needed for the examples above. + + +Check Task and Asset Compatibility +---------------------------------- + +VBD uses the Newton model built from the task assets. When adding VBD to a new +task, validate the following before tuning solver parameters: + +* The task must already be compatible with the Newton backend. If a rigid-only + ``newton_mjwarp`` preset fails during model construction, fix the asset or task + configuration first. +* The scene must include Newton-compatible deformable assets and materials. Use + :class:`~isaaclab_newton.sim.spawners.materials.NewtonDeformableBodyMaterialCfg` + for volume deformables and + :class:`~isaaclab_newton.sim.spawners.materials.NewtonSurfaceDeformableBodyMaterialCfg` + for cloth or surface deformables. +* Coupled robot tasks should start with ``coupling_mode="two_way"`` when the + robot should feel contact forces from the deformable object. +* Contact-heavy scenes usually need task-specific ``num_substeps``, + :class:`~isaaclab_contrib.deformable.VBDSolverCfg`, and + :class:`~isaaclab_contrib.deformable.NewtonModelCfg` values. Start from the + Franka soft-body or cloth preset that most closely resembles the scene. +* Use a small visual smoke test before training. Confirm that the deformable + spawns, renders, deforms, and contacts rigid bodies as expected. + +VBD Solver Parameters +--------------------- + +The following fields are specific to +:class:`~isaaclab_contrib.deformable.VBDSolverCfg`. They are grouped by the part +of the solver they affect. + +Core Solve +^^^^^^^^^^ + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Parameter + - Description + * - ``iterations`` + - Default: ``10``. Number of VBD iterations per substep. Increasing this value improves deformation and contact convergence, especially for stiff materials or rigid gripper contacts, but increases runtime. + * - ``integrate_with_external_rigid_solver`` + - Default: ``False``. Set to ``True`` when VBD is used inside a coupled solver so the rigid sub-solver owns rigid-body integration. Leave ``False`` for deformable-only VBD scenes. + + +Self-Contact +^^^^^^^^^^^^ + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Parameter + - Description + * - ``particle_enable_self_contact`` + - Default: ``False``. Enables deformable self-contact. Use this for cloth folds or soft bodies that collide with themselves. It increases contact work and usually needs additional tuning. + * - ``particle_self_contact_radius`` + - Default: ``0.005`` [m]. Effective self-contact thickness. VBD applies vertex-triangle and edge-edge self-contact response when the current primitive distance is smaller than this radius. + * - ``particle_self_contact_margin`` + - Default: ``0.005`` [m]. Self-contact candidate search distance. VBD uses this envelope when building self-contact lists, then applies contact response using ``particle_self_contact_radius``. Keep this greater than or equal to the radius to avoid missed contacts. + * - ``particle_collision_detection_interval`` + - Default: ``-1``. Controls how often self-contact detection runs. A negative value detects before initialization only. ``0`` detects before and immediately after initialization. A positive value ``k`` detects before every ``k`` VBD iterations. + * - ``particle_vertex_contact_buffer_size`` + - Default: ``32``. Preallocation size for each vertex's vertex-triangle self-contact buffer. Increase it if dense folds or high-resolution cloth exceed the default capacity. + * - ``particle_edge_contact_buffer_size`` + - Default: ``64``. Preallocation size for each edge's edge-edge self-contact buffer. Increase it if dense folds or high-resolution cloth exceed the default capacity. + * - ``particle_topological_contact_filter_threshold`` + - Default: ``2``. Filters contacts between mesh primitives that are close in topology. Increase this to suppress contact between neighboring elements of the same surface. Values greater than ``3`` can significantly increase compute time. + * - ``particle_rest_shape_contact_exclusion_radius`` + - Default: ``0.0`` [m]. Filters self-contact candidates whose rest-configuration distance is shorter than this distance. Increase it when rest-neighbor contacts produce unwanted resistance. + + +Coupled Solver Parameters +------------------------- + +Use the coupled solver configs when one solver should advance rigid bodies and +VBD should advance deformables: + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Parameter + - Description + * - ``rigid_solver_cfg`` + - Rigid-body sub-solver configuration. :class:`~isaaclab_contrib.deformable.CoupledMJWarpVBDSolverCfg` uses :class:`~isaaclab_newton.physics.MJWarpSolverCfg`; :class:`~isaaclab_contrib.deformable.CoupledFeatherstoneVBDSolverCfg` uses :class:`~isaaclab_newton.physics.FeatherstoneSolverCfg`. + * - ``soft_solver_cfg`` + - VBD sub-solver configuration. In coupled scenes, set ``integrate_with_external_rigid_solver=True`` so VBD advances only deformable particles. + * - ``coupling_mode="one_way"`` + - Rigid solver advances first, and VBD reacts to the updated rigid poses. The rigid solver does not feel particle contact forces. + * - ``coupling_mode="two_way"`` + - Contact reactions from deformables are injected into the rigid solver before the rigid step, then VBD advances deformables against the shared contacts. Use this for manipulation tasks where the robot should be pushed back by deformable contact. + * - ``coupling_mode="kinematic"`` + - Available on :class:`~isaaclab_contrib.deformable.CoupledFeatherstoneVBDSolverCfg`. Rigid bodies are kinematically updated by Featherstone, and VBD reacts to them. The rigid solver does not feel particle contacts. + +The rigid solver parameters still matter. For example, MJWarp's ``nconmax`` and +``njmax`` must be large enough for the rigid contacts in the scene, and +``ccd_iterations`` can affect fast rigid contacts near deformables. See +:doc:`mjwarp-solver` for the MJWarp-side parameters. + + +Contact and Material Parameters +------------------------------- + +Contact Model +^^^^^^^^^^^^^ + +:class:`~isaaclab_contrib.deformable.NewtonModelCfg` applies contact parameters +to the finalized Newton model: + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Parameter + - Description + * - ``soft_contact_ke`` + - Default: ``1.0e3`` [N/m]. Stiffness for body-particle and particle self-contact. Increase it to reduce clipping through rigid shapes or through other deformable particles. If it is too high, the object can stop visibly deforming or require more VBD iterations and substeps. + * - ``soft_contact_kd`` + - Default: ``1.0e-2`` [N*s/m]. Contact damping. Increase it to reduce chatter or bouncing. Too much damping can make contact response sticky or overdamped. + * - ``soft_contact_mu`` + - Default: ``0.5``. Friction coefficient for body-particle and particle self-contact. Increase it when a gripper cannot carry the deformable object without slipping. + * - ``shape_material_ke`` + - Default: ``None`` [N/m]. Optional override for all rigid collision-shape contact stiffness values in the Newton model. Use this when the rigid-side material parsed from the asset is not appropriate for deformable contact. + * - ``shape_material_kd`` + - Default: ``None`` [N*s/m]. Optional override for all rigid collision-shape contact damping values in the Newton model. + * - ``shape_material_mu`` + - Default: ``None``. Optional override for all rigid collision-shape friction values in the Newton model. Body-particle friction depends on both the soft contact and rigid shape friction coefficients. + + +Volume Deformable Materials +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Use +:class:`~isaaclab_newton.sim.spawners.materials.NewtonDeformableBodyMaterialCfg` +for volume deformables: + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Parameter + - Description + * - ``density`` + - Default: ``1.0`` [kg/m^3]. Material density. Higher density increases particle mass and inertia, so the object accelerates and deforms less for the same contact forces. + * - ``particle_radius`` + - Default: ``0.008`` [m]. Particle contact radius used by Newton. Increase it when contacts are missed or detected too late. If it is too large relative to the mesh resolution, contacts can start too early. + * - ``k_mu`` + - Default: ``1.0e5`` [Pa]. First Lame material parameter. Higher values make the deformable object stiffer and usually require more VBD iterations, more substeps, or a smaller timestep. + * - ``k_lambda`` + - Default: ``1.0e5`` [Pa]. Second Lame material parameter. Higher values make the deformable object stiffer and usually require more VBD iterations, more substeps, or a smaller timestep. + * - ``k_damp`` + - Default: ``0.0`` [Pa*s]. Damping for tetrahedral elements. Increase it to reduce oscillations after deformation, but avoid overdamping if the object should rebound. + + +Surface Deformable Materials +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Use +:class:`~isaaclab_newton.sim.spawners.materials.NewtonSurfaceDeformableBodyMaterialCfg` +for cloth or surface deformables: + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Parameter + - Description + * - ``density`` + - Default: ``1.0`` [kg/m^3]. Material density. Higher density increases particle mass and inertia. + * - ``particle_radius`` + - Default: ``0.008`` [m]. Particle contact radius used by Newton. + * - ``tri_ke`` + - Default: ``1.0e4`` [Pa]. Triangle area-preserving stiffness. Increase it to reduce cloth stretch. + * - ``tri_ka`` + - Default: ``1.0e4`` [Pa]. Triangle area stiffness. Increase it to reduce cloth area change. + * - ``tri_kd`` + - Default: ``1.5e-6`` [Pa*s]. Triangle area damping. Increase it to reduce cloth vibration after stretching. + * - ``edge_ke`` + - Default: ``5.0`` [N*m]. Bending stiffness. Increase it for stiffer cloth folds; decrease it for softer draping. + * - ``edge_kd`` + - Default: ``1.0e-2`` [N*m*s]. Bending damping. Increase it to damp fold oscillations. + +Tuning Workflow +--------------- + +Use the following sequence when bringing up a new VBD task: + +1. Run one of the supported Franka deformable tasks and confirm your + installation, visualizer, and deformable rendering path work. +2. Add a task-specific VBD or coupled VBD preset copied from the closest + supported task. +3. Run a small visual smoke test with ``--num_envs 1`` before training. +4. Tune deformable material stiffness and damping until the object deforms in + the expected range without rigid contact. +5. Increase ``num_substeps`` or decrease ``dt`` if the object is unstable before + increasing stiffness further. +6. Increase :attr:`~isaaclab_contrib.deformable.VBDSolverCfg.iterations` when + contacts or stiff materials do not converge within a substep. +7. Tune :attr:`~isaaclab_contrib.deformable.NewtonModelCfg.soft_contact_ke` to + reduce rigid/deformable clipping, then tune + :attr:`~isaaclab_contrib.deformable.NewtonModelCfg.soft_contact_mu` for grip + and :attr:`~isaaclab_contrib.deformable.NewtonModelCfg.soft_contact_kd` for + chatter. +8. Enable self-contact only after body-particle contact is stable, then tune + ``particle_self_contact_radius`` for active self-contact thickness, + ``particle_self_contact_margin`` for missed contacts, and + ``particle_collision_detection_interval`` for detection frequency. +9. Increase ``num_envs`` and profile only after the single-environment scene is + stable. + + +Symptoms and First Parameters to Check +-------------------------------------- + +.. list-table:: + :header-rows: 1 + :widths: 35 65 + + * - Symptom + - First parameters to check + * - Rigid bodies visibly clip through the deformable. + - Increase ``soft_contact_ke``, VBD ``iterations``, ``num_substeps``, or the deformable material ``particle_radius``. + * - The robot cannot lift the deformable. + - Use ``coupling_mode="two_way"``, then increase ``soft_contact_mu`` and rigid-side ``shape_material_mu``. Also check gripper actuator stiffness and effort limits. + * - The deformable barely deforms. + - Reduce material stiffness, ``soft_contact_ke``, or shape contact stiffness. + * - Contact chatters or bounces. + - Increase ``soft_contact_kd`` or material damping, and consider using more substeps. + * - Cloth passes through itself. + - Enable ``particle_enable_self_contact``, increase ``particle_self_contact_radius`` if the active self-contact thickness is too small, increase ``particle_self_contact_margin`` if contacts are missed, and use a positive ``particle_collision_detection_interval``. + * - Self-contact is too expensive. + - Increase ``particle_collision_detection_interval``, reduce mesh resolution, or disable self-contact until the rest of the scene is tuned. + +For implementation details of the VBD and coupled solver managers, see +:doc:`newton-manager-abstraction`. diff --git a/docs/source/overview/core-concepts/physical-backends/newton/warp-environments.rst b/docs/source/overview/core-concepts/physical-backends/newton/warp-environments.rst index a5dcbd648724..3c0e1ba7d6b0 100644 --- a/docs/source/overview/core-concepts/physical-backends/newton/warp-environments.rst +++ b/docs/source/overview/core-concepts/physical-backends/newton/warp-environments.rst @@ -321,11 +321,4 @@ Migrating Existing Environments For step-by-step instructions on porting an existing stable env (or writing a new warp env from scratch) — covering project layout, the kernel + launch pattern shared by observations / rewards / events / terminations / actions, capture-safety rules, and -parity testing — see :doc:`warp-env-migration` below. - - -.. toctree:: - :maxdepth: 2 - :hidden: - - warp-env-migration +parity testing — see :doc:`warp-env-migration`. diff --git a/docs/source/overview/environments.rst b/docs/source/overview/environments.rst index 61a13d058ffd..384f2dbff515 100644 --- a/docs/source/overview/environments.rst +++ b/docs/source/overview/environments.rst @@ -151,6 +151,9 @@ for the lift-cube environment: +-------------------------+------------------------------+-----------------------------------------------------------------------------+------------------------------+ | |lift-cube| | |lift-cube-link| | Pick a cube and bring it to a sampled target position with the Franka robot | | +-------------------------+------------------------------+-----------------------------------------------------------------------------+------------------------------+ + | |lift-soft-franka| | |lift-soft-franka-link| | Pick a deformable soft body and bring it to a sampled target position with | ``newton_mjwarp_vdb``, | + | | | the Franka robot | ``physx`` | + +-------------------------+------------------------------+-----------------------------------------------------------------------------+------------------------------+ | |stack-cube| | |stack-cube-link| | Stack three cubes (bottom to top: blue, red, green) with the Franka robot. | | | | | Blueprint env used for the NVIDIA Isaac GR00T blueprint for synthetic | | | | |stack-cube-bp-link| | manipulation motion generation | | @@ -245,6 +248,7 @@ for the lift-cube environment: .. |reach-ur10| image:: ../_static/tasks/manipulation/ur10_reach.jpg .. |deploy-reach-ur10e| image:: ../_static/tasks/manipulation/ur10e_reach.jpg .. |lift-cube| image:: ../_static/tasks/manipulation/franka_lift.jpg +.. |lift-soft-franka| image:: ../_static/newton/franka-mjwarp-vbd-coupling.png .. |cabi-franka| image:: ../_static/tasks/manipulation/franka_open_drawer.jpg .. |cube-allegro| image:: ../_static/tasks/manipulation/allegro_cube.jpg .. |cube-shadow| image:: ../_static/tasks/manipulation/shadow_cube.jpg @@ -272,6 +276,7 @@ for the lift-cube environment: .. |lift-cube-link| replace:: `Isaac-Lift-Cube-Franka-v0 <../../../source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift/config/franka/joint_pos_env_cfg.py>`__ .. |lift-cube-ik-abs-link| replace:: `Isaac-Lift-Cube-Franka-IK-Abs-v0 <../../../source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift/config/franka/ik_abs_env_cfg.py>`__ .. |lift-cube-ik-rel-link| replace:: `Isaac-Lift-Cube-Franka-IK-Rel-v0 <../../../source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift/config/franka/ik_rel_env_cfg.py>`__ +.. |lift-soft-franka-link| replace:: `Isaac-Lift-Soft-Franka-v0 <../../../source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/franka_soft_env_cfg.py>`__ .. |cabi-franka-link| replace:: `Isaac-Open-Drawer-Franka-v0 <../../../source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/cabinet/config/franka/joint_pos_env_cfg.py>`__ .. |franka-direct-link| replace:: `Isaac-Franka-Cabinet-Direct-v0 <../../../source/isaaclab_tasks/isaaclab_tasks/direct/franka_cabinet/franka_cabinet_env.py>`__ .. |cube-allegro-link| replace:: `Isaac-Repose-Cube-Allegro-v0 <../../../source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/inhand/config/allegro_hand/allegro_env_cfg.py>`__ @@ -1047,6 +1052,16 @@ inferencing, including reading from an already trained checkpoint and disabling - Manager Based - **rsl_rl** (PPO), **skrl** (PPO), **rl_games** (PPO), **sb3** (PPO) - + * - Isaac-Lift-Soft-Franka-v0 + - + - Manager Based + - **rsl_rl** (PPO) + - ``newton_mjwarp_vdb``, ``physx`` + * - Isaac-Lift-Cloth-Franka-v0 + - + - Manager Based + - **rsl_rl** (PPO) + - ``newton_mjwarp_vdb`` * - Isaac-Lift-Teddy-Bear-Franka-IK-Abs-v0 - - Manager Based diff --git a/docs/source/overview/showroom.rst b/docs/source/overview/showroom.rst index bb2248375749..358a310e9e1e 100644 --- a/docs/source/overview/showroom.rst +++ b/docs/source/overview/showroom.rst @@ -57,7 +57,7 @@ A few quick showroom scripts to run and checkout: :alt: Biped robots in Isaac Lab -- Spawn different deformable (soft) bodies and let them fall from a height: +- Spawn different deformable objects and let them fall from a height: .. tab-set:: :sync-group: os @@ -76,6 +76,9 @@ A few quick showroom scripts to run and checkout: isaaclab.bat -p scripts\demos\deformables.py + Add ``--backend newton`` to run the same demo with the experimental Newton + deformable backend. + .. image:: ../_static/demos/deformables.jpg :width: 100% :alt: Deformable primitive-shaped objects in Isaac Lab diff --git a/docs/source/tutorials/00_sim/spawn_prims.rst b/docs/source/tutorials/00_sim/spawn_prims.rst index 1c4555687a28..86672091b2f7 100644 --- a/docs/source/tutorials/00_sim/spawn_prims.rst +++ b/docs/source/tutorials/00_sim/spawn_prims.rst @@ -139,7 +139,8 @@ default to the default values set by USD Physics. Lastly, we spawn a cuboid ``CuboidDeformable`` which contains deformable body physics properties. Unlike the rigid body simulation, a deformable body can have relative motion between its vertices. This is useful for simulating soft bodies like cloth, rubber, or jello. It is important to note that deformable bodies are only supported in -GPU simulation and require a mesh object to be spawned with the deformable body physics properties. +GPU simulation and require a mesh object to be spawned with deformable body physics properties and a deformable +physics material. This example uses the PhysX-specific deformable property and material cfgs. .. literalinclude:: ../../../../scripts/tutorials/00_sim/spawn_prims.py :language: python diff --git a/docs/source/tutorials/01_assets/run_deformable_object.rst b/docs/source/tutorials/01_assets/run_deformable_object.rst index 61092353cec8..bb2607a20949 100644 --- a/docs/source/tutorials/01_assets/run_deformable_object.rst +++ b/docs/source/tutorials/01_assets/run_deformable_object.rst @@ -7,14 +7,17 @@ Interacting with a deformable object .. currentmodule:: isaaclab While deformable objects sometimes refer to a broader class of objects, such as cloths, fluids and soft bodies, -in PhysX, deformable objects are represented as either surface or volume deformables. Unlike rigid objects, soft bodies can deform -under external forces and collisions. In this tutorial, we will focus on volume deformable bodies. For an example of surface -deformables (cloth), see the deformable demo at ``scripts/demos/deformables.py``. - -Soft bodies are simulated using Finite Element Method (FEM) in PhysX. The volume deformable comprises of two tetrahedral -meshes -- a simulation mesh and a collision mesh. The simulation mesh is used to simulate the deformations of -the soft body, while the collision mesh is used to detect collisions with other objects in the scene. -For more details, please check the `PhysX documentation`_. +Isaac Lab represents deformable objects as either surface or volume deformables. Unlike rigid objects, soft bodies can +deform under external forces and collisions. In this tutorial, we focus on volume deformable bodies. For an example of +surface deformables (cloth), see the deformable demo at ``scripts/demos/deformables.py``. + +The deformable object API and schema define/modify functions are shared across backends, while deformable +property and material configuration classes are backend-specific. PhysX simulates soft bodies using the Finite +Element Method (FEM); the Newton experimental backend uses VBD-based deformable support from +:mod:`isaaclab_contrib.deformable`. +The volume deformable comprises of two tetrahedral meshes -- a simulation mesh and a collision mesh. The simulation +mesh is used to simulate the deformations of the soft body, while the collision mesh is used to detect collisions +with other objects in the scene. For PhysX-specific details, please check the `PhysX documentation`_. This tutorial shows how to interact with a deformable object in the simulation. We will spawn a set of soft cubes and see how to set their nodal positions and velocities, along with apply kinematic @@ -31,7 +34,7 @@ The tutorial corresponds to the ``run_deformable_object.py`` script in the ``scr .. literalinclude:: ../../../../scripts/tutorials/01_assets/run_deformable_object.py :language: python - :emphasize-lines: 68-82, 110-126, 131-139, 141-149 + :emphasize-lines: 65-98, 119-124, 126-135, 140-148, 150-158 :linenos: @@ -54,8 +57,10 @@ the :class:`assets.DeformableObject` class, it spawns the object and initializes when the simulation is played. .. note:: - The deformable object is only supported in GPU simulation and requires a mesh object to be spawned with the - deformable body physics properties on it. + Deformable objects require a mesh object to be spawned with backend-specific deformable body physics + properties and a matching deformable physics material. + Use ``--backend physx`` for the PhysX implementation or ``--backend newton`` for the experimental Newton + implementation. As seen in the rigid body tutorial, we can spawn the deformable object into the scene in a similar fashion by creating @@ -63,7 +68,7 @@ an instance of the :class:`assets.DeformableObject` class by passing the configu .. literalinclude:: ../../../../scripts/tutorials/01_assets/run_deformable_object.py :language: python - :start-at: # Create separate groups called "Origin0", "Origin1", ... + :start-at: # Create separate groups called "env_0", "env_1", ... :end-at: cube_object = DeformableObject(cfg=cfg) Running the simulation loop @@ -112,8 +117,8 @@ Stepping the simulation """"""""""""""""""""""" Deformable bodies support user-driven kinematic control where a user can specify position targets for some of -the mesh nodes while the rest of the nodes are simulated using the FEM solver. This `partial kinematic`_ control -is useful for applications where the user wants to interact with the deformable object in a controlled manner. +the mesh nodes while the rest of the nodes are simulated by the active deformable solver. This `partial kinematic`_ +control is useful for applications where the user wants to interact with the deformable object in a controlled manner. In this tutorial, we apply kinematic commands to two out of the four cubes in the scene. We set the position targets for the node at index 0 (bottom-left corner) to move the cube along the z-axis. @@ -162,6 +167,12 @@ Now that we have gone through the code, let's run the script and see the result: ./isaaclab.sh -p scripts/tutorials/01_assets/run_deformable_object.py --visualizer kit +To run the same tutorial with the experimental Newton deformable backend: + +.. code-block:: bash + + ./isaaclab.sh -p scripts/tutorials/01_assets/run_deformable_object.py --backend newton --visualizer kit + This should open a stage with a ground plane, lights, and several cubes. Two of the four cubes must be dropping from a height and settling on to the ground. Meanwhile the other two cubes must be moving along the z-axis. You diff --git a/scripts/demos/deformables.py b/scripts/demos/deformables.py index 71f40b5f9c4a..9da0ba196597 100644 --- a/scripts/demos/deformables.py +++ b/scripts/demos/deformables.py @@ -21,6 +21,7 @@ # create argparser parser = argparse.ArgumentParser(description="This script demonstrates how to spawn deformable prims into the scene.") +parser.add_argument("--backend", type=str, default="physx", choices=["physx", "newton"], help="Physics backend.") # append AppLauncher cli args AppLauncher.add_app_launcher_args(parser) # demos should open Kit visualizer by default @@ -39,13 +40,27 @@ import torch import tqdm -# deformables supported in PhysX -from isaaclab_physx.assets import DeformableObject, DeformableObjectCfg -from isaaclab_physx.sim import DeformableBodyMaterialCfg, DeformableBodyPropertiesCfg, SurfaceDeformableBodyMaterialCfg - import isaaclab.sim as sim_utils +from isaaclab.assets import DeformableObject, DeformableObjectCfg from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR +if args_cli.backend == "newton": + from isaaclab_newton.sim.schemas import NewtonDeformableBodyPropertiesCfg as DeformableBodyPropertiesCfg + from isaaclab_newton.sim.spawners.materials import ( + NewtonDeformableBodyMaterialCfg as VolumeDeformableMaterialCfg, + ) + from isaaclab_newton.sim.spawners.materials import ( + NewtonSurfaceDeformableBodyMaterialCfg as SurfaceDeformableMaterialCfg, + ) +else: + from isaaclab_physx.sim.schemas import PhysxDeformableBodyPropertiesCfg as DeformableBodyPropertiesCfg + from isaaclab_physx.sim.spawners.materials import ( + PhysxDeformableBodyMaterialCfg as VolumeDeformableMaterialCfg, + ) + from isaaclab_physx.sim.spawners.materials import ( + PhysxSurfaceDeformableBodyMaterialCfg as SurfaceDeformableMaterialCfg, + ) + def define_origins(num_origins: int, radius: float = 2.0, center_height: float = 3.0) -> list[list[float]]: """Defines origins distributed on the surface of a sphere, sampled according to a Fibonacci lattice. @@ -84,47 +99,47 @@ def design_scene() -> tuple[dict, list[list[float]]]: radius=0.4, deformable_props=DeformableBodyPropertiesCfg(), visual_material=sim_utils.PreviewSurfaceCfg(), - physics_material=DeformableBodyMaterialCfg(), + physics_material=VolumeDeformableMaterialCfg(), ) cfg_cuboid = sim_utils.MeshCuboidCfg( size=(0.6, 0.6, 0.6), deformable_props=DeformableBodyPropertiesCfg(), visual_material=sim_utils.PreviewSurfaceCfg(), - physics_material=DeformableBodyMaterialCfg(), + physics_material=VolumeDeformableMaterialCfg(), ) cfg_cylinder = sim_utils.MeshCylinderCfg( radius=0.25, height=0.5, deformable_props=DeformableBodyPropertiesCfg(), visual_material=sim_utils.PreviewSurfaceCfg(), - physics_material=DeformableBodyMaterialCfg(), + physics_material=VolumeDeformableMaterialCfg(), ) cfg_capsule = sim_utils.MeshCapsuleCfg( radius=0.35, height=0.5, deformable_props=DeformableBodyPropertiesCfg(), visual_material=sim_utils.PreviewSurfaceCfg(), - physics_material=DeformableBodyMaterialCfg(), + physics_material=VolumeDeformableMaterialCfg(), ) cfg_cone = sim_utils.MeshConeCfg( radius=0.35, height=0.75, deformable_props=DeformableBodyPropertiesCfg(), visual_material=sim_utils.PreviewSurfaceCfg(), - physics_material=DeformableBodyMaterialCfg(), + physics_material=VolumeDeformableMaterialCfg(), ) - cfg_cloth = sim_utils.MeshSquareCfg( - size=1.5, + cfg_cloth = sim_utils.MeshRectangleCfg( + size=(1.5, 1.0), resolution=(21, 21), deformable_props=DeformableBodyPropertiesCfg(), visual_material=sim_utils.PreviewSurfaceCfg(), - physics_material=SurfaceDeformableBodyMaterialCfg(), + physics_material=SurfaceDeformableMaterialCfg(), ) cfg_usd = sim_utils.UsdFileCfg( usd_path=f"{ISAACLAB_NUCLEUS_DIR}/Objects/Teddy_Bear/teddy_bear.usd", deformable_props=DeformableBodyPropertiesCfg(), visual_material=sim_utils.PreviewSurfaceCfg(), - physics_material=DeformableBodyMaterialCfg(), + physics_material=VolumeDeformableMaterialCfg(), scale=[0.05, 0.05, 0.05], ) # create a dictionary of all the objects to be spawned @@ -141,50 +156,48 @@ def design_scene() -> tuple[dict, list[list[float]]]: # Create separate groups of deformable objects origins = define_origins(num_origins=12, radius=1.5, center_height=2.0) print("[INFO]: Spawning objects...") - num_volumes = 0 - num_surfaces = 0 - # Iterate over all the origins and randomly spawn objects + # Iterate over all the origins, spawn objects, and create a view for all the deformables + # note: since we manually spawned random deformable meshes above, we don't need to + # specify the spawn configuration for the deformable object + scene_entities = {} for idx, origin in tqdm.tqdm(enumerate(origins), total=len(origins)): # randomly select an object to spawn obj_name = random.choice(list(objects_cfg.keys())) obj_cfg = objects_cfg[obj_name] - # randomize the young modulus - obj_cfg.physics_material.youngs_modulus = random.uniform(5e5, 1e8) - # higher mesh resolution causes instability at low stiffness - if obj_name in ["sphere", "capsule", "cloth", "usd"]: - obj_cfg.physics_material.youngs_modulus = random.uniform(1e8, 5e9) - # randomize the poisson's ratio - obj_cfg.physics_material.poissons_ratio = random.uniform(0.25, 0.45) + # randomize the deformable material stiffness + if args_cli.backend == "newton" and obj_name == "cloth": + obj_cfg.physics_material.tri_ke = random.uniform(5e3, 5e4) + obj_cfg.physics_material.tri_ka = random.uniform(5e3, 5e4) + else: + youngs_modulus = random.uniform(5e5, 1e8) + poissons_ratio = random.uniform(0.25, 0.45) + if args_cli.backend == "newton": + obj_cfg.physics_material.k_mu = youngs_modulus / (2.0 * (1.0 + poissons_ratio)) + obj_cfg.physics_material.k_lambda = ( + youngs_modulus * poissons_ratio / ((1.0 + poissons_ratio) * (1.0 - 2.0 * poissons_ratio)) + ) + else: + obj_cfg.physics_material.youngs_modulus = youngs_modulus + obj_cfg.physics_material.poissons_ratio = poissons_ratio # randomize the color obj_cfg.visual_material.diffuse_color = (random.random(), random.random(), random.random()) # spawn the object, separate groups for surface and volume deformables if obj_name in ["cloth"]: - obj_cfg.func(f"/World/Origin/Surface{idx:02d}", obj_cfg, translation=origin) - num_surfaces += 1 + prim_path = f"/World/Origin/Surface{idx:02d}" + cfg = DeformableObjectCfg( + prim_path=prim_path, + spawn=obj_cfg, + init_state=DeformableObjectCfg.InitialStateCfg(pos=origin), + ) + scene_entities[f"Surface{idx:02d}"] = DeformableObject(cfg=cfg) else: - obj_cfg.func(f"/World/Origin/Volume{idx:02d}", obj_cfg, translation=origin) - num_volumes += 1 - - # create a view for all the deformables, separate views for volume and surface deformables - # note: since we manually spawned random deformable meshes above, we don't need to - # specify the spawn configuration for the deformable object - scene_entities = {} - if num_volumes > 0: - cfg = DeformableObjectCfg( - prim_path="/World/Origin/Volume.*", - spawn=None, - init_state=DeformableObjectCfg.InitialStateCfg(), - ) - volume_deformable_object = DeformableObject(cfg=cfg) - scene_entities["volume_deformable_object"] = volume_deformable_object - if num_surfaces > 0: - cfg = DeformableObjectCfg( - prim_path="/World/Origin/Surface.*", - spawn=None, - init_state=DeformableObjectCfg.InitialStateCfg(), - ) - surface_deformable_object = DeformableObject(cfg=cfg) - scene_entities["surface_deformable_object"] = surface_deformable_object + prim_path = f"/World/Origin/Volume{idx:02d}" + cfg = DeformableObjectCfg( + prim_path=prim_path, + spawn=obj_cfg, + init_state=DeformableObjectCfg.InitialStateCfg(pos=origin), + ) + scene_entities[f"Volume{idx:02d}"] = DeformableObject(cfg=cfg) # return the scene information return scene_entities, origins @@ -224,7 +237,17 @@ def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Deformab def main(): """Main function.""" # Initialize the simulation context - sim_cfg = sim_utils.SimulationCfg(dt=0.01, device=args_cli.device) + if args_cli.backend == "newton": + from isaaclab_newton.physics import NewtonCfg + + from isaaclab_contrib.deformable.newton_manager_cfg import VBDSolverCfg + + physics_cfg = NewtonCfg(solver_cfg=VBDSolverCfg(iterations=10), num_substeps=4) + else: + from isaaclab_physx.physics import PhysxCfg + + physics_cfg = PhysxCfg() + sim_cfg = sim_utils.SimulationCfg(dt=0.01, device=args_cli.device, physics=physics_cfg) sim = sim_utils.SimulationContext(sim_cfg) # Set main camera sim.set_camera_view([4.0, 4.0, 3.0], [0.5, 0.5, 0.0]) diff --git a/scripts/environments/state_machine/lift_franka_soft.py b/scripts/environments/state_machine/lift_franka_soft.py new file mode 100644 index 000000000000..a9561718a591 --- /dev/null +++ b/scripts/environments/state_machine/lift_franka_soft.py @@ -0,0 +1,382 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +""" +Script to demonstrate lifting a deformable object with a robotic arm. + +The state machine is implemented in the kernel function `infer_state_machine`. +It uses the `warp` library to run the state machine in parallel on the GPU. + +.. code-block:: bash + + ./isaaclab.sh -p scripts/environments/state_machine/lift_franka_soft.py + +""" + +"""Launch Omniverse Toolkit first.""" + +import argparse + +from isaaclab.app import AppLauncher + +# add argparse arguments +parser = argparse.ArgumentParser(description="Pick and lift a deformable with a robotic arm.") +parser.add_argument("--num_envs", type=int, default=1, help="Number of environments to simulate.") +parser.add_argument("--task", type=str, default="Isaac-Lift-Soft-Franka-v0", help="The task to run.") +parser.add_argument("--video", action="store_true", default=False, help="Record a video of the rollout.") +parser.add_argument("--video_length", type=int, default=500, help="Length of the recorded video (in env steps).") +parser.add_argument( + "--video_folder", + type=str, + default="videos/lift_franka_soft", + help="Directory to write recorded videos into.", +) +# append AppLauncher cli args +AppLauncher.add_app_launcher_args(parser) +# parse the arguments +args_cli = parser.parse_args() + +# RecordVideo needs an rgb_array render mode, which in turn requires cameras to be enabled. +if args_cli.video: + args_cli.enable_cameras = True + +# launch omniverse app +app_launcher = AppLauncher(args_cli) +simulation_app = app_launcher.app + +# disable metrics assembler due to scene graph instancing +from isaacsim.core.experimental.utils.app import enable_extension + +enable_extension("omni.usd.metrics.assembler.ui", enabled=False) + +"""Rest everything else.""" + +import os +from collections.abc import Sequence + +import gymnasium as gym +import torch +import warp as wp + +from isaaclab.assets.deformable_object.deformable_object_data import DeformableObjectData + +import isaaclab_tasks # noqa: F401 +from isaaclab_tasks.manager_based.manipulation.lift_franka_soft.franka_cloth_env_cfg import FrankaClothEnvCfg +from isaaclab_tasks.manager_based.manipulation.lift_franka_soft.franka_soft_env_cfg import FrankaSoftEnvCfg +from isaaclab_tasks.utils.parse_cfg import parse_env_cfg + +# initialize warp +wp.init() + + +class GripperState: + """States for the gripper.""" + + OPEN = wp.constant(1.0) + CLOSE = wp.constant(-1.0) + + +class PickSmState: + """States for the pick state machine.""" + + REST = wp.constant(0) + APPROACH_ABOVE_OBJECT = wp.constant(1) + APPROACH_OBJECT = wp.constant(2) + GRASP_OBJECT = wp.constant(3) + LIFT_OBJECT = wp.constant(4) + OPEN_GRIPPER = wp.constant(5) + + +@wp.func +def distance_below_threshold(current_pos: wp.vec3, desired_pos: wp.vec3, threshold: float) -> bool: + return wp.length(current_pos - desired_pos) < threshold + + +@wp.kernel +def infer_state_machine( + dt: wp.array(dtype=float), + sm_state: wp.array(dtype=int), + sm_wait_time: wp.array(dtype=float), + ee_pose: wp.array(dtype=wp.transform), + object_pose: wp.array(dtype=wp.transform), + des_object_pose: wp.array(dtype=wp.transform), + des_ee_pose: wp.array(dtype=wp.transform), + gripper_state: wp.array(dtype=float), + offset: wp.array(dtype=wp.transform), + position_threshold: float, +): + # retrieve thread id + tid = wp.tid() + # retrieve state machine state + state = sm_state[tid] + # decide next state + if state == PickSmState.REST: + des_ee_pose[tid] = ee_pose[tid] + gripper_state[tid] = GripperState.OPEN + # wait for a while + if sm_wait_time[tid] >= PickSmWaitTime.REST: + # move to next state and reset wait time + sm_state[tid] = PickSmState.APPROACH_ABOVE_OBJECT + sm_wait_time[tid] = 0.0 + elif state == PickSmState.APPROACH_ABOVE_OBJECT: + des_ee_pose[tid] = wp.transform_multiply(offset[tid], object_pose[tid]) + gripper_state[tid] = GripperState.OPEN + if distance_below_threshold( + wp.transform_get_translation(ee_pose[tid]), + wp.transform_get_translation(des_ee_pose[tid]), + position_threshold, + ): + # wait for a while + if sm_wait_time[tid] >= PickSmWaitTime.APPROACH_OBJECT: + # move to next state and reset wait time + sm_state[tid] = PickSmState.APPROACH_OBJECT + sm_wait_time[tid] = 0.0 + elif state == PickSmState.APPROACH_OBJECT: + des_ee_pose[tid] = object_pose[tid] + gripper_state[tid] = GripperState.OPEN + if distance_below_threshold( + wp.transform_get_translation(ee_pose[tid]), + wp.transform_get_translation(des_ee_pose[tid]), + position_threshold, + ): + # wait for a while + if sm_wait_time[tid] >= PickSmWaitTime.APPROACH_OBJECT: + # move to next state and reset wait time + sm_state[tid] = PickSmState.GRASP_OBJECT + sm_wait_time[tid] = 0.0 + elif state == PickSmState.GRASP_OBJECT: + des_ee_pose[tid] = object_pose[tid] + gripper_state[tid] = GripperState.CLOSE + # wait for a while + if sm_wait_time[tid] >= PickSmWaitTime.GRASP_OBJECT: + # move to next state and reset wait time + sm_state[tid] = PickSmState.LIFT_OBJECT + sm_wait_time[tid] = 0.0 + elif state == PickSmState.LIFT_OBJECT: + des_ee_pose[tid] = des_object_pose[tid] + gripper_state[tid] = GripperState.CLOSE + if distance_below_threshold( + wp.transform_get_translation(ee_pose[tid]), + wp.transform_get_translation(des_ee_pose[tid]), + position_threshold, + ): + # wait for a while + if sm_wait_time[tid] >= PickSmWaitTime.LIFT_OBJECT: + # move to next state and reset wait time + sm_state[tid] = PickSmState.OPEN_GRIPPER + sm_wait_time[tid] = 0.0 + elif state == PickSmState.OPEN_GRIPPER: + # des_ee_pose[tid] = object_pose[tid] + gripper_state[tid] = GripperState.OPEN + # wait for a while + if sm_wait_time[tid] >= PickSmWaitTime.OPEN_GRIPPER: + # move to next state and reset wait time + sm_state[tid] = PickSmState.OPEN_GRIPPER + sm_wait_time[tid] = 0.0 + # increment wait time + sm_wait_time[tid] = sm_wait_time[tid] + dt[tid] + + +class PickSmWaitTime: + """Additional wait times (in s) for states for before switching. + + Wait times are generous because the low-PD Franka takes a while to settle on each IK target. + """ + + REST = wp.constant(0.2) + APPROACH_ABOVE_OBJECT = wp.constant(1.0) + APPROACH_OBJECT = wp.constant(1.0) + GRASP_OBJECT = wp.constant(1.0) + LIFT_OBJECT = wp.constant(1.5) + OPEN_GRIPPER = wp.constant(0.0) + + +class PickAndLiftSm: + """A simple state machine in a robot's task space to pick and lift an object. + + The state machine is implemented as a warp kernel. It takes in the current state of + the robot's end-effector and the object, and outputs the desired state of the robot's + end-effector and the gripper. The state machine is implemented as a finite state + machine with the following states: + + 1. REST: The robot is at rest. + 2. APPROACH_ABOVE_OBJECT: The robot moves above the object. + 3. APPROACH_OBJECT: The robot moves to the object. + 4. GRASP_OBJECT: The robot grasps the object. + 5. LIFT_OBJECT: The robot lifts the object to the desired pose. This is the final state. + """ + + def __init__(self, dt: float, num_envs: int, device: torch.device | str = "cpu", position_threshold=0.03): + """Initialize the state machine. + + Args: + dt: The environment time step. + num_envs: The number of environments to simulate. + device: The device to run the state machine on. + """ + # save parameters + self.dt = float(dt) + self.num_envs = num_envs + self.device = device + self.position_threshold = position_threshold + # initialize state machine + self.sm_dt = torch.full((self.num_envs,), self.dt, device=self.device) + self.sm_state = torch.full((self.num_envs,), 0, dtype=torch.int32, device=self.device) + self.sm_wait_time = torch.zeros((self.num_envs,), device=self.device) + + # desired state + self.des_ee_pose = torch.zeros((self.num_envs, 7), device=self.device) + self.des_gripper_state = torch.full((self.num_envs,), 0.0, device=self.device) + + # approach above object offset + self.offset = torch.zeros((self.num_envs, 7), device=self.device) + self.offset[:, 2] = 0.1 + self.offset[:, -1] = 1.0 # warp expects quaternion as (x, y, z, w) + + # convert to warp + self.sm_dt_wp = wp.from_torch(self.sm_dt, wp.float32) + self.sm_state_wp = wp.from_torch(self.sm_state, wp.int32) + self.sm_wait_time_wp = wp.from_torch(self.sm_wait_time, wp.float32) + self.des_ee_pose_wp = wp.from_torch(self.des_ee_pose, wp.transform) + self.des_gripper_state_wp = wp.from_torch(self.des_gripper_state, wp.float32) + self.offset_wp = wp.from_torch(self.offset, wp.transform) + + def reset_idx(self, env_ids: Sequence[int] = None): + """Reset the state machine.""" + if env_ids is None: + env_ids = slice(None) + self.sm_state[env_ids] = 0 + self.sm_wait_time[env_ids] = 0.0 + + def compute(self, ee_pose: torch.Tensor, object_pose: torch.Tensor, des_object_pose: torch.Tensor): + """Compute the desired state of the robot's end-effector and the gripper.""" + + # convert to warp + ee_pose_wp = wp.from_torch(ee_pose.contiguous(), wp.transform) + object_pose_wp = wp.from_torch(object_pose.contiguous(), wp.transform) + des_object_pose_wp = wp.from_torch(des_object_pose.contiguous(), wp.transform) + + # run state machine + wp.launch( + kernel=infer_state_machine, + dim=self.num_envs, + inputs=[ + self.sm_dt_wp, + self.sm_state_wp, + self.sm_wait_time_wp, + ee_pose_wp, + object_pose_wp, + des_object_pose_wp, + self.des_ee_pose_wp, + self.des_gripper_state_wp, + self.offset_wp, + self.position_threshold, + ], + device=self.device, + ) + + # convert to torch + return torch.cat([self.des_ee_pose, self.des_gripper_state.unsqueeze(-1)], dim=-1) + + +def main(): + # create environment + render_mode = "rgb_array" if args_cli.video else None + if args_cli.task == "Isaac-Lift-Soft-Franka-v0": + # parse configuration + env_cfg: FrankaSoftEnvCfg = parse_env_cfg( + "Isaac-Lift-Soft-Franka-v0", + device=args_cli.device, + num_envs=args_cli.num_envs, + ) + env_cfg.viewer.eye = (2.1, 1.0, 1.3) + env = gym.make("Isaac-Lift-Soft-Franka-v0", cfg=env_cfg, render_mode=render_mode) + elif args_cli.task == "Isaac-Lift-Soft-Franka-Cloth-v0": + # parse configuration + env_cfg: FrankaClothEnvCfg = parse_env_cfg( + "Isaac-Lift-Cloth-Franka-v0", + device=args_cli.device, + num_envs=args_cli.num_envs, + ) + env_cfg.viewer.eye = (2.1, 1.0, 1.3) + env = gym.make("Isaac-Lift-Cloth-Franka-v0", cfg=FrankaClothEnvCfg(), render_mode=render_mode) + else: + raise ValueError(f"Unknown task: {args_cli.task}") + + # wrap for video recording + if args_cli.video: + video_folder = os.path.abspath(args_cli.video_folder) + os.makedirs(video_folder, exist_ok=True) + env = gym.wrappers.RecordVideo( + env, + video_folder=video_folder, + step_trigger=lambda step: step == 0, + video_length=args_cli.video_length, + disable_logger=True, + ) + print(f"[INFO] Recording video to {video_folder} (length={args_cli.video_length} steps)") + + # reset environment at start + env.reset() + + # create action buffers (position + quaternion) + actions = torch.zeros(env.unwrapped.action_space.shape, device=env.unwrapped.device) + actions[:, 3] = 1.0 + # desired rotation after grasping + desired_orientation = torch.zeros((env.unwrapped.num_envs, 4), device=env.unwrapped.device) + desired_orientation[:, 0] = 1.0 + + # Top-down approach: identity quaternion (wxyz, w=1) aligns panda_hand with the Franka root, + # giving the canonical top-down grasp pose. The bar lies along world-X, so the gripper + # closes across its short side without any wrist twist. + object_grasp_orientation = torch.zeros((env.unwrapped.num_envs, 4), device=env.unwrapped.device) + object_grasp_orientation[:, 0] = 1.0 + # Grasp at the deformable's centre of mass. + object_local_grasp_position = torch.tensor([0.0, 0.0, 0.0], device=env.unwrapped.device) + + # create state machine + pick_sm = PickAndLiftSm(env_cfg.sim.dt * env_cfg.decimation, env.unwrapped.num_envs, env.unwrapped.device) + + while simulation_app.is_running(): + # run everything in inference mode + with torch.inference_mode(): + # step environment + dones = env.step(actions)[-2] + + # observations + # -- end-effector frame + ee_frame_sensor = env.unwrapped.scene["ee_frame"] + tcp_rest_position = ( + ee_frame_sensor.data.target_pos_w.torch[..., 0, :].clone() - env.unwrapped.scene.env_origins + ) + tcp_rest_orientation = ee_frame_sensor.data.target_quat_w.torch[..., 0, :].clone() + # -- object frame + object_data: DeformableObjectData = env.unwrapped.scene["deformable"].data + object_position = object_data.root_pos_w.torch - env.unwrapped.scene.env_origins + object_position += object_local_grasp_position + + # -- target object frame + desired_position = env.unwrapped.command_manager.get_command("deformable_pose")[..., :3] + + # advance state machine + actions = pick_sm.compute( + torch.cat([tcp_rest_position, tcp_rest_orientation], dim=-1), + torch.cat([object_position, object_grasp_orientation], dim=-1), + torch.cat([desired_position, desired_orientation], dim=-1), + ) + + # reset state machine + if dones.any(): + pick_sm.reset_idx(dones.nonzero(as_tuple=False).squeeze(-1)) + + # close the environment + env.close() + + +if __name__ == "__main__": + # run the main function + main() + # close sim app + simulation_app.close() diff --git a/scripts/tutorials/00_sim/spawn_prims.py b/scripts/tutorials/00_sim/spawn_prims.py index 7c120bd308dd..8d366c650e6b 100644 --- a/scripts/tutorials/00_sim/spawn_prims.py +++ b/scripts/tutorials/00_sim/spawn_prims.py @@ -31,6 +31,9 @@ """Rest everything follows.""" +from isaaclab_physx.sim.schemas import PhysxDeformableBodyPropertiesCfg +from isaaclab_physx.sim.spawners.materials import PhysxDeformableBodyMaterialCfg + import isaaclab.sim as sim_utils from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR @@ -75,9 +78,9 @@ def design_scene(): # spawn a blue cuboid with deformable body cfg_cuboid_deformable = sim_utils.MeshCuboidCfg( size=(0.2, 0.5, 0.2), - deformable_props=sim_utils.DeformableBodyPropertiesCfg(), + deformable_props=PhysxDeformableBodyPropertiesCfg(), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.0, 1.0)), - physics_material=sim_utils.DeformableBodyMaterialCfg(), + physics_material=PhysxDeformableBodyMaterialCfg(), ) cfg_cuboid_deformable.func("/World/Objects/CuboidDeformable", cfg_cuboid_deformable, translation=(0.15, 0.0, 2.0)) diff --git a/scripts/tutorials/01_assets/run_deformable_object.py b/scripts/tutorials/01_assets/run_deformable_object.py index 368fe7c3358e..9d42a5301ba3 100644 --- a/scripts/tutorials/01_assets/run_deformable_object.py +++ b/scripts/tutorials/01_assets/run_deformable_object.py @@ -22,6 +22,7 @@ # add argparse arguments parser = argparse.ArgumentParser(description="Tutorial on interacting with a deformable object.") +parser.add_argument("--backend", type=str, default="physx", choices=["physx", "newton"], help="Physics backend.") # append AppLauncher cli args AppLauncher.add_app_launcher_args(parser) # demos should open Kit visualizer by default @@ -36,14 +37,10 @@ """Rest everything follows.""" import torch -from isaaclab_physx.assets import DeformableObject, DeformableObjectCfg - -# deformables supported in PhysX -from isaaclab_physx.sim import DeformableBodyMaterialCfg, DeformableBodyPropertiesCfg import isaaclab.sim as sim_utils import isaaclab.utils.math as math_utils -from isaaclab.sim import SimulationContext +from isaaclab.assets import DeformableObject, DeformableObjectCfg def design_scene(): @@ -58,20 +55,43 @@ def design_scene(): # Create a dictionary for the scene entities scene_entities = {} - # Create separate groups called "Origin0", "Origin1", ... - # Each group will have a robot in it + # Create separate groups called "env_0", "env_1", ... + # Newton's scene loader requires the "env_\d+" naming convention to + # detect per-environment Xforms and replicate them as separate worlds. origins = [[0.25, 0.25, 0.0], [-0.25, 0.25, 0.0], [0.25, -0.25, 0.0], [-0.25, -0.25, 0.0]] for i, origin in enumerate(origins): - sim_utils.create_prim(f"/World/Origin{i}", "Xform", translation=origin) + sim_utils.create_prim(f"/World/env_{i}", "Xform", translation=origin) + + youngs_modulus = 1e5 + poissons_ratio = 0.4 + density = 500.0 + if args_cli.backend == "newton": + from isaaclab_newton.sim.schemas import NewtonDeformableBodyPropertiesCfg + from isaaclab_newton.sim.spawners.materials import NewtonDeformableBodyMaterialCfg + + deformable_props = NewtonDeformableBodyPropertiesCfg() + physics_material = NewtonDeformableBodyMaterialCfg( + k_mu=youngs_modulus / (2.0 * (1.0 + poissons_ratio)), + k_lambda=youngs_modulus * poissons_ratio / ((1.0 + poissons_ratio) * (1.0 - 2.0 * poissons_ratio)), + density=density, + ) + else: + from isaaclab_physx.sim.schemas import PhysxDeformableBodyPropertiesCfg + from isaaclab_physx.sim.spawners.materials import PhysxDeformableBodyMaterialCfg + + deformable_props = PhysxDeformableBodyPropertiesCfg(rest_offset=0.0, contact_offset=0.001) + physics_material = PhysxDeformableBodyMaterialCfg( + poissons_ratio=poissons_ratio, youngs_modulus=youngs_modulus, density=density + ) # 3D Deformable Object cfg = DeformableObjectCfg( - prim_path="/World/Origin.*/Cube", + prim_path="/World/env_.*/Cube", spawn=sim_utils.MeshCuboidCfg( size=(0.2, 0.2, 0.2), - deformable_props=DeformableBodyPropertiesCfg(), + deformable_props=deformable_props, visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.5, 0.1, 0.0)), - physics_material=DeformableBodyMaterialCfg(poissons_ratio=0.4, youngs_modulus=1e5), + physics_material=physics_material, ), init_state=DeformableObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 1.0)), debug_vis=True, @@ -155,8 +175,18 @@ def run_simulator(sim: sim_utils.SimulationContext, entities: dict, origins: tor def main(): """Main function.""" # Load kit helper - sim_cfg = sim_utils.SimulationCfg(dt=0.01, device=args_cli.device) - sim = SimulationContext(sim_cfg) + if args_cli.backend == "newton": + from isaaclab_newton.physics import NewtonCfg + + from isaaclab_contrib.deformable.newton_manager_cfg import VBDSolverCfg + + physics_cfg = NewtonCfg(solver_cfg=VBDSolverCfg(iterations=10), num_substeps=4) + else: + from isaaclab_physx.physics import PhysxCfg + + physics_cfg = PhysxCfg() + sim_cfg = sim_utils.SimulationCfg(dt=0.01, device=args_cli.device, physics=physics_cfg) + sim = sim_utils.SimulationContext(sim_cfg) # Set main camera sim.set_camera_view(eye=[2.0, 2.0, 2.0], target=[0.0, 0.0, 0.75]) # Design scene diff --git a/source/isaaclab/changelog.d/mym-deformable-backend-split.major.rst b/source/isaaclab/changelog.d/mym-deformable-backend-split.major.rst new file mode 100644 index 000000000000..1fd4b8967bea --- /dev/null +++ b/source/isaaclab/changelog.d/mym-deformable-backend-split.major.rst @@ -0,0 +1,19 @@ +Added +^^^^^ + +* Added common deformable property and material base cfgs in :mod:`isaaclab.sim`. + +Changed +^^^^^^^ + +* Changed deformable spawners to accept backend-specific deformable property and + material cfgs. Use PhysX cfgs from :mod:`isaaclab_physx.sim` or Newton cfgs + from :mod:`isaaclab_newton.sim`. + +Deprecated +^^^^^^^^^^ + +* Deprecated generic deformable property and material cfgs in favor of + ``PhysxDeformableBodyPropertiesCfg``, ``PhysxDeformableBodyMaterialCfg``, + ``PhysxSurfaceDeformableBodyMaterialCfg``, ``NewtonDeformableBodyPropertiesCfg``, + ``NewtonDeformableBodyMaterialCfg``, and ``NewtonSurfaceDeformableBodyMaterialCfg``. diff --git a/source/isaaclab/changelog.d/mym-deformable_experimental.minor.rst b/source/isaaclab/changelog.d/mym-deformable_experimental.minor.rst new file mode 100644 index 000000000000..059828bf03ab --- /dev/null +++ b/source/isaaclab/changelog.d/mym-deformable_experimental.minor.rst @@ -0,0 +1,34 @@ +Added +^^^^^ + +* Added backend-neutral deformable asset APIs, including + :class:`~isaaclab.assets.DeformableObject`, + :class:`~isaaclab.assets.DeformableObjectCfg`, and shared base/data classes. +* Added deformable body spawner, schema, and material APIs under + :mod:`isaaclab.sim`, including + :class:`~isaaclab.sim.DeformableObjectSpawnerCfg`, + :class:`~isaaclab.sim.DeformableBodyPropertiesCfg`, + :func:`~isaaclab.sim.define_deformable_body_properties`, + :func:`~isaaclab.sim.modify_deformable_body_properties`, + :class:`~isaaclab.sim.DeformableBodyMaterialCfg`, + :class:`~isaaclab.sim.SurfaceDeformableBodyMaterialCfg`, and + :func:`~isaaclab.sim.spawn_deformable_body_material`. +* Added ``pytetwild`` as a package dependency for tetrahedral mesh generation. +* Added deformable API, migration, and tutorial documentation for + backend-neutral imports and Newton backend selection. + +Changed +^^^^^^^ + +* Changed deformable demos and tutorials to use the backend-neutral + :mod:`isaaclab.assets` and :mod:`isaaclab.sim` APIs with selectable PhysX or + Newton backends. +* Changed USD spawning to support deformable objects whose USD assets contain + embedded tetrahedral mesh data. +* **Breaking:** Renamed :class:`~isaaclab.sim.spawners.meshes.MeshSquareCfg` + to :class:`~isaaclab.sim.spawners.meshes.MeshRectangleCfg` and + :func:`~isaaclab.sim.spawners.meshes.spawn_mesh_square` to + :func:`~isaaclab.sim.spawners.meshes.spawn_mesh_rectangle`. The ``size`` + attribute is now a ``tuple[float, float]`` of X- and Y-axis lengths instead + of a single edge length. Migrate ``MeshSquareCfg(size=s)`` to + ``MeshRectangleCfg(size=(s, s))``. diff --git a/source/isaaclab/isaaclab/assets/__init__.pyi b/source/isaaclab/isaaclab/assets/__init__.pyi index 727eae33002b..55b42b8819ab 100644 --- a/source/isaaclab/isaaclab/assets/__init__.pyi +++ b/source/isaaclab/isaaclab/assets/__init__.pyi @@ -21,6 +21,11 @@ __all__ = [ "RigidObjectCollection", "RigidObjectCollectionCfg", "RigidObjectCollectionData", + "BaseDeformableObject", + "BaseDeformableObjectData", + "DeformableObject", + "DeformableObjectCfg", + "DeformableObjectData", ] from .articulation import ( @@ -46,3 +51,10 @@ from .rigid_object_collection import ( RigidObjectCollectionCfg, RigidObjectCollectionData, ) +from .deformable_object import ( + BaseDeformableObject, + BaseDeformableObjectData, + DeformableObject, + DeformableObjectCfg, + DeformableObjectData, +) diff --git a/source/isaaclab/isaaclab/assets/deformable_object/__init__.py b/source/isaaclab/isaaclab/assets/deformable_object/__init__.py new file mode 100644 index 000000000000..525b6ae5fd98 --- /dev/null +++ b/source/isaaclab/isaaclab/assets/deformable_object/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Sub-module for deformable object assets.""" + +from isaaclab.utils.module import lazy_export + +lazy_export() diff --git a/source/isaaclab/isaaclab/assets/deformable_object/__init__.pyi b/source/isaaclab/isaaclab/assets/deformable_object/__init__.pyi new file mode 100644 index 000000000000..ebe78d5b6dbc --- /dev/null +++ b/source/isaaclab/isaaclab/assets/deformable_object/__init__.pyi @@ -0,0 +1,18 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +from .base_deformable_object import BaseDeformableObject +from .base_deformable_object_data import BaseDeformableObjectData +from .deformable_object import DeformableObject +from .deformable_object_cfg import DeformableObjectCfg +from .deformable_object_data import DeformableObjectData + +__all__ = [ + "BaseDeformableObject", + "BaseDeformableObjectData", + "DeformableObject", + "DeformableObjectCfg", + "DeformableObjectData", +] diff --git a/source/isaaclab/isaaclab/assets/deformable_object/base_deformable_object.py b/source/isaaclab/isaaclab/assets/deformable_object/base_deformable_object.py new file mode 100644 index 000000000000..e21895a10bfb --- /dev/null +++ b/source/isaaclab/isaaclab/assets/deformable_object/base_deformable_object.py @@ -0,0 +1,371 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +from __future__ import annotations + +import warnings +from abc import abstractmethod +from collections.abc import Sequence +from typing import TYPE_CHECKING + +import torch +import warp as wp + +import isaaclab.utils.math as math_utils +from isaaclab.assets.asset_base import AssetBase +from isaaclab.utils.warp import ProxyArray + +if TYPE_CHECKING: + from .base_deformable_object_data import BaseDeformableObjectData + from .deformable_object_cfg import DeformableObjectCfg + + +class BaseDeformableObject(AssetBase): + """Abstract base class for deformable object assets. + + Deformable objects are assets that can be deformed in the simulation. They are typically used for + soft bodies, such as stuffed animals, food items, and cloth. + + Unlike rigid object assets, deformable objects have a more complex structure and require additional + handling for simulation. The state of a deformable object comprises of its nodal positions and + velocities, and not the object's root position and orientation. The nodal positions and velocities + are in the simulation frame. + + Soft bodies can be `partially kinematic`_, where some nodes are driven by kinematic targets, and + the rest are simulated. The kinematic targets are the desired positions of the nodes, and the + simulation drives the nodes towards these targets. + + .. _partially kinematic: https://nvidia-omniverse.github.io/PhysX/physx/5.4.1/docs/SoftBodies.html#kinematic-soft-bodies + """ + + cfg: DeformableObjectCfg + """Configuration instance for the deformable object.""" + + __backend_name__: str = "base" + """The name of the backend for the deformable object.""" + + def __init__(self, cfg: DeformableObjectCfg): + """Initialize the deformable object. + + Args: + cfg: A configuration instance. + """ + super().__init__(cfg) + + """ + Properties + """ + + @property + @abstractmethod + def data(self) -> BaseDeformableObjectData: + """Data container for the deformable object.""" + raise NotImplementedError() + + @property + @abstractmethod + def num_instances(self) -> int: + """Number of instances of the asset.""" + raise NotImplementedError() + + @property + @abstractmethod + def num_bodies(self) -> int: + """Number of bodies in the asset. + + This is always 1 since each object is a single deformable body. + """ + raise NotImplementedError() + + @property + @abstractmethod + def max_sim_vertices_per_body(self) -> int: + """The maximum number of simulation mesh vertices per deformable body.""" + raise NotImplementedError() + + """ + Operations. + """ + + @abstractmethod + def reset(self, env_ids: Sequence[int] | None = None, env_mask: wp.array | None = None) -> None: + """Reset the deformable object. + + Args: + env_ids: Environment indices. If None, then all indices are used. + env_mask: Environment mask. If None, then all the instances are updated. + Shape is (num_instances,). + """ + raise NotImplementedError() + + @abstractmethod + def write_data_to_sim(self): + """Write data to the simulator.""" + raise NotImplementedError() + + @abstractmethod + def update(self, dt: float): + """Update the internal buffers. + + Args: + dt: The amount of time passed from last :meth:`update` call [s]. + """ + raise NotImplementedError() + + """ + Operations - Write to simulation (index variants). + """ + + def write_nodal_state_to_sim_index( + self, + nodal_state: torch.Tensor | wp.array | ProxyArray, + env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, + full_data: bool = False, + ) -> None: + """Set the nodal state over selected environment indices into the simulation. + + The nodal state comprises of the nodal positions and velocities. Since these are nodes, + the velocity only has a translational component. All the quantities are in the simulation frame. + + Args: + nodal_state: Nodal state in simulation frame [m, m/s]. + Shape is (len(env_ids), max_sim_vertices_per_body, 6) + or (num_instances, max_sim_vertices_per_body, 6). + env_ids: Environment indices. If None, then all indices are used. + full_data: Whether to expect full data. Defaults to False. + """ + # Convert array wrappers to torch for slicing into position and velocity views. + if isinstance(nodal_state, ProxyArray): + nodal_state = nodal_state.torch + elif isinstance(nodal_state, wp.array): + nodal_state = wp.to_torch(nodal_state) + # set into simulation + self.write_nodal_pos_to_sim_index(nodal_state[..., :3], env_ids=env_ids, full_data=full_data) + self.write_nodal_velocity_to_sim_index(nodal_state[..., 3:], env_ids=env_ids, full_data=full_data) + + @abstractmethod + def write_nodal_pos_to_sim_index( + self, + nodal_pos: torch.Tensor | wp.array | ProxyArray, + env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, + full_data: bool = False, + ) -> None: + """Set the nodal positions over selected environment indices into the simulation. + + Args: + nodal_pos: Nodal positions in simulation frame [m]. + Shape is (len(env_ids), max_sim_vertices_per_body, 3) + or (num_instances, max_sim_vertices_per_body, 3). + env_ids: Environment indices. If None, then all indices are used. + full_data: Whether to expect full data. Defaults to False. + """ + raise NotImplementedError() + + @abstractmethod + def write_nodal_velocity_to_sim_index( + self, + nodal_vel: torch.Tensor | wp.array | ProxyArray, + env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, + full_data: bool = False, + ) -> None: + """Set the nodal velocity over selected environment indices into the simulation. + + Args: + nodal_vel: Nodal velocities in simulation frame [m/s]. + Shape is (len(env_ids), max_sim_vertices_per_body, 3) + or (num_instances, max_sim_vertices_per_body, 3). + env_ids: Environment indices. If None, then all indices are used. + full_data: Whether to expect full data. Defaults to False. + """ + raise NotImplementedError() + + @abstractmethod + def write_nodal_kinematic_target_to_sim_index( + self, + targets: torch.Tensor | wp.array | ProxyArray, + env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, + full_data: bool = False, + ) -> None: + """Set the kinematic targets of the simulation mesh for the deformable bodies using indices. + + The kinematic targets comprise of individual nodal positions of the simulation mesh + for the deformable body and a flag indicating whether the node is kinematically driven or not. + The positions are in the simulation frame. + + .. note:: + The flag is set to 0.0 for kinematically driven nodes and 1.0 for free nodes. + + Args: + targets: The kinematic targets comprising of nodal positions and flags [m]. + Shape is (len(env_ids), max_sim_vertices_per_body, 4) + or (num_instances, max_sim_vertices_per_body, 4). + env_ids: Environment indices. If None, then all indices are used. + full_data: Whether to expect full data. Defaults to False. + """ + raise NotImplementedError() + + """ + Operations - Write to simulation (mask variants). + """ + + def write_nodal_state_to_sim_mask( + self, + nodal_state: torch.Tensor | wp.array | ProxyArray, + env_mask: wp.array | None = None, + ) -> None: + """Set the nodal state over selected environment mask into the simulation. + + Args: + nodal_state: Nodal state in simulation frame [m, m/s]. + Shape is (num_instances, max_sim_vertices_per_body, 6). + env_mask: Environment mask. If None, then all indices are used. + """ + if env_mask is not None: + env_ids = wp.nonzero(env_mask) + else: + env_ids = self._ALL_INDICES + self.write_nodal_state_to_sim_index(nodal_state, env_ids=env_ids, full_data=True) + + def write_nodal_pos_to_sim_mask( + self, + nodal_pos: torch.Tensor | wp.array | ProxyArray, + env_mask: wp.array | None = None, + ) -> None: + """Set the nodal positions over selected environment mask into the simulation. + + Args: + nodal_pos: Nodal positions in simulation frame [m]. + Shape is (num_instances, max_sim_vertices_per_body, 3). + env_mask: Environment mask. If None, then all indices are used. + """ + if env_mask is not None: + env_ids = wp.nonzero(env_mask) + else: + env_ids = self._ALL_INDICES + self.write_nodal_pos_to_sim_index(nodal_pos, env_ids=env_ids, full_data=True) + + def write_nodal_velocity_to_sim_mask( + self, + nodal_vel: torch.Tensor | wp.array | ProxyArray, + env_mask: wp.array | None = None, + ) -> None: + """Set the nodal velocity over selected environment mask into the simulation. + + Args: + nodal_vel: Nodal velocities in simulation frame [m/s]. + Shape is (num_instances, max_sim_vertices_per_body, 3). + env_mask: Environment mask. If None, then all indices are used. + """ + if env_mask is not None: + env_ids = wp.nonzero(env_mask) + else: + env_ids = self._ALL_INDICES + self.write_nodal_velocity_to_sim_index(nodal_vel, env_ids=env_ids, full_data=True) + + def write_nodal_kinematic_target_to_sim_mask( + self, + targets: torch.Tensor | wp.array | ProxyArray, + env_mask: wp.array | None = None, + ) -> None: + """Set the kinematic targets of the simulation mesh for the deformable bodies using mask. + + Args: + targets: The kinematic targets comprising of nodal positions and flags [m]. + Shape is (num_instances, max_sim_vertices_per_body, 4). + env_mask: Environment mask. If None, then all indices are used. + """ + if env_mask is not None: + env_ids = wp.nonzero(env_mask) + else: + env_ids = self._ALL_INDICES + self.write_nodal_kinematic_target_to_sim_index(targets, env_ids=env_ids, full_data=True) + + """ + Operations - Deprecated wrappers. + """ + + def write_nodal_state_to_sim( + self, + nodal_state: torch.Tensor | wp.array | ProxyArray, + env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, + ) -> None: + """Deprecated. Please use :meth:`write_nodal_state_to_sim_index` instead.""" + warnings.warn( + "The method 'write_nodal_state_to_sim' is deprecated. Please use 'write_nodal_state_to_sim_index' instead.", + DeprecationWarning, + stacklevel=2, + ) + self.write_nodal_state_to_sim_index(nodal_state, env_ids=env_ids) + + def write_nodal_kinematic_target_to_sim( + self, + targets: torch.Tensor | wp.array | ProxyArray, + env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, + ) -> None: + """Deprecated. Please use :meth:`write_nodal_kinematic_target_to_sim_index` instead.""" + warnings.warn( + "The method 'write_nodal_kinematic_target_to_sim' is deprecated." + " Please use 'write_nodal_kinematic_target_to_sim_index' instead.", + DeprecationWarning, + stacklevel=2, + ) + self.write_nodal_kinematic_target_to_sim_index(targets, env_ids=env_ids) + + def write_nodal_pos_to_sim( + self, + nodal_pos: torch.Tensor | wp.array | ProxyArray, + env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, + ) -> None: + """Deprecated. Please use :meth:`write_nodal_pos_to_sim_index` instead.""" + warnings.warn( + "The method 'write_nodal_pos_to_sim' is deprecated. Please use 'write_nodal_pos_to_sim_index' instead.", + DeprecationWarning, + stacklevel=2, + ) + self.write_nodal_pos_to_sim_index(nodal_pos, env_ids=env_ids) + + def write_nodal_velocity_to_sim( + self, + nodal_vel: torch.Tensor | wp.array | ProxyArray, + env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, + ) -> None: + """Deprecated. Please use :meth:`write_nodal_velocity_to_sim_index` instead.""" + warnings.warn( + "The method 'write_nodal_velocity_to_sim' is deprecated." + " Please use 'write_nodal_velocity_to_sim_index' instead.", + DeprecationWarning, + stacklevel=2, + ) + self.write_nodal_velocity_to_sim_index(nodal_vel, env_ids=env_ids) + + """ + Operations - Helper. + """ + + def transform_nodal_pos( + self, nodal_pos: torch.Tensor, pos: torch.Tensor | None = None, quat: torch.Tensor | None = None + ) -> torch.Tensor: + """Transform the nodal positions based on the pose transformation. + + This function computes the transformation of the nodal positions based on the pose transformation. + It multiplies the nodal positions with the rotation matrix of the pose and adds the translation. + Internally, it calls the :meth:`isaaclab.utils.math.transform_points` function. + + Args: + nodal_pos: The nodal positions in the simulation frame [m]. + Shape is (N, max_sim_vertices_per_body, 3). + pos: The position transformation [m]. Shape is (N, 3). + Defaults to None, in which case the position is assumed to be zero. + quat: The orientation transformation as quaternion (x, y, z, w). Shape is (N, 4). + Defaults to None, in which case the orientation is assumed to be identity. + + Returns: + The transformed nodal positions [m]. Shape is (N, max_sim_vertices_per_body, 3). + """ + # offset the nodal positions to center them around the origin + mean_nodal_pos = nodal_pos.mean(dim=1, keepdim=True) + nodal_pos = nodal_pos - mean_nodal_pos + # transform the nodal positions based on the pose around the origin + return math_utils.transform_points(nodal_pos, pos, quat) + mean_nodal_pos diff --git a/source/isaaclab/isaaclab/assets/deformable_object/base_deformable_object_data.py b/source/isaaclab/isaaclab/assets/deformable_object/base_deformable_object_data.py new file mode 100644 index 000000000000..e0b1ae497511 --- /dev/null +++ b/source/isaaclab/isaaclab/assets/deformable_object/base_deformable_object_data.py @@ -0,0 +1,136 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +from __future__ import annotations + +from abc import ABC, abstractmethod + +from isaaclab.utils.warp import ProxyArray + + +class BaseDeformableObjectData(ABC): + """Abstract data container for a deformable object. + + This class defines the interface for deformable object data in the simulation. + The data includes the nodal states of the root deformable body in the object. + The data is stored in the simulation world frame unless otherwise specified. + + The data is lazily updated, meaning that the data is only updated when it is accessed. + This is useful when the data is expensive to compute or retrieve. The data is updated + when the timestamp of the buffer is older than the current simulation timestamp. + """ + + def __init__(self, device: str): + """Initialize the deformable object data. + + Args: + device: The device used for processing. + """ + self.device = device + # Set initial time stamp + self._sim_timestamp = 0.0 + + def update(self, dt: float): + """Update the data for the deformable object. + + Args: + dt: The time step for the update [s]. This must be a positive value. + """ + # update the simulation timestamp + self._sim_timestamp += dt + + ## + # Defaults. + ## + + default_nodal_state_w: ProxyArray | None = None + """Default nodal state ``[nodal_pos, nodal_vel]`` in simulation world frame. + + Shape is (num_instances, max_sim_vertices_per_body), dtype ``vec6f``. + Use :attr:`ProxyArray.warp` for the underlying :class:`warp.array` or + :attr:`ProxyArray.torch` for a cached zero-copy :class:`torch.Tensor` view. + """ + + ## + # Kinematic commands. + ## + + nodal_kinematic_target: ProxyArray | None = None + """Simulation mesh kinematic targets for the deformable bodies. + + Shape is (num_instances, max_sim_vertices_per_body), dtype ``wp.vec4f``. + Use :attr:`ProxyArray.warp` for the underlying :class:`warp.array` or + :attr:`ProxyArray.torch` for a cached zero-copy :class:`torch.Tensor` view. + + The kinematic targets are used to drive the simulation mesh vertices to the target positions. + The targets are stored as (x, y, z, is_not_kinematic) where "is_not_kinematic" is a binary + flag indicating whether the vertex is kinematic or not. The flag is set to 0 for kinematic vertices + and 1 for non-kinematic vertices. + """ + + ## + # Properties. + ## + + @property + @abstractmethod + def nodal_pos_w(self) -> ProxyArray: + """Nodal positions in simulation world frame [m]. + + Shape is (num_instances, max_sim_vertices_per_body), dtype ``wp.vec3f``. + Use :attr:`ProxyArray.warp` for the underlying :class:`warp.array` or + :attr:`ProxyArray.torch` for a cached zero-copy :class:`torch.Tensor` view. + """ + raise NotImplementedError() + + @property + @abstractmethod + def nodal_vel_w(self) -> ProxyArray: + """Nodal velocities in simulation world frame [m/s]. + + Shape is (num_instances, max_sim_vertices_per_body), dtype ``wp.vec3f``. + Use :attr:`ProxyArray.warp` for the underlying :class:`warp.array` or + :attr:`ProxyArray.torch` for a cached zero-copy :class:`torch.Tensor` view. + """ + raise NotImplementedError() + + @property + @abstractmethod + def nodal_state_w(self) -> ProxyArray: + """Nodal state ``[nodal_pos, nodal_vel]`` in simulation world frame [m, m/s]. + + Shape is (num_instances, max_sim_vertices_per_body), dtype ``vec6f``. + Use :attr:`ProxyArray.warp` for the underlying :class:`warp.array` or + :attr:`ProxyArray.torch` for a cached zero-copy :class:`torch.Tensor` view. + """ + raise NotImplementedError() + + ## + # Derived properties. + ## + + @property + @abstractmethod + def root_pos_w(self) -> ProxyArray: + """Root position from nodal positions of the simulation mesh for the deformable bodies + in simulation world frame [m]. Shape is (num_instances,) vec3f. + + This quantity is computed as the mean of the nodal positions. + Use :attr:`ProxyArray.warp` for the underlying :class:`warp.array` or + :attr:`ProxyArray.torch` for a cached zero-copy :class:`torch.Tensor` view. + """ + raise NotImplementedError() + + @property + @abstractmethod + def root_vel_w(self) -> ProxyArray: + """Root velocity from vertex velocities for the deformable bodies in simulation + world frame [m/s]. Shape is (num_instances,) vec3f. + + This quantity is computed as the mean of the nodal velocities. + Use :attr:`ProxyArray.warp` for the underlying :class:`warp.array` or + :attr:`ProxyArray.torch` for a cached zero-copy :class:`torch.Tensor` view. + """ + raise NotImplementedError() diff --git a/source/isaaclab/isaaclab/assets/deformable_object/deformable_object.py b/source/isaaclab/isaaclab/assets/deformable_object/deformable_object.py new file mode 100644 index 000000000000..3bd41c8728be --- /dev/null +++ b/source/isaaclab/isaaclab/assets/deformable_object/deformable_object.py @@ -0,0 +1,27 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from isaaclab.utils.backend_utils import FactoryBase + +from .base_deformable_object import BaseDeformableObject +from .base_deformable_object_data import BaseDeformableObjectData + +if TYPE_CHECKING: + from isaaclab_physx.assets.deformable_object import DeformableObject as PhysXDeformableObject + from isaaclab_physx.assets.deformable_object import DeformableObjectData as PhysXDeformableObjectData + + +class DeformableObject(FactoryBase, BaseDeformableObject): + """Factory for creating deformable object instances.""" + + data: BaseDeformableObjectData | PhysXDeformableObjectData + + def __new__(cls, *args, **kwargs) -> BaseDeformableObject | PhysXDeformableObject: + """Create a new instance of a deformable object based on the backend.""" + return super().__new__(cls, *args, **kwargs) diff --git a/source/isaaclab_physx/isaaclab_physx/assets/deformable_object/deformable_object_cfg.py b/source/isaaclab/isaaclab/assets/deformable_object/deformable_object_cfg.py similarity index 100% rename from source/isaaclab_physx/isaaclab_physx/assets/deformable_object/deformable_object_cfg.py rename to source/isaaclab/isaaclab/assets/deformable_object/deformable_object_cfg.py diff --git a/source/isaaclab/isaaclab/assets/deformable_object/deformable_object_data.py b/source/isaaclab/isaaclab/assets/deformable_object/deformable_object_data.py new file mode 100644 index 000000000000..35567aceb847 --- /dev/null +++ b/source/isaaclab/isaaclab/assets/deformable_object/deformable_object_data.py @@ -0,0 +1,25 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from isaaclab.utils.backend_utils import FactoryBase + +from .base_deformable_object_data import BaseDeformableObjectData + +if TYPE_CHECKING: + from isaaclab_physx.assets.deformable_object.deformable_object_data import ( + DeformableObjectData as PhysXDeformableObjectData, + ) + + +class DeformableObjectData(FactoryBase): + """Factory for creating deformable object data instances.""" + + def __new__(cls, *args, **kwargs) -> BaseDeformableObjectData | PhysXDeformableObjectData: + """Create a new instance of a deformable object data based on the backend.""" + return super().__new__(cls, *args, **kwargs) diff --git a/source/isaaclab/isaaclab/scene/interactive_scene.py b/source/isaaclab/isaaclab/scene/interactive_scene.py index 70756007d6fb..b8df495d1e79 100644 --- a/source/isaaclab/isaaclab/scene/interactive_scene.py +++ b/source/isaaclab/isaaclab/scene/interactive_scene.py @@ -10,7 +10,7 @@ from typing import TYPE_CHECKING, Any if TYPE_CHECKING: - from isaaclab_physx.assets import DeformableObject, SurfaceGripper + from isaaclab_physx.assets import SurfaceGripper from isaaclab.renderers.base_renderer import BaseRenderer @@ -25,6 +25,8 @@ Articulation, ArticulationCfg, AssetBaseCfg, + DeformableObject, + DeformableObjectCfg, RigidObject, RigidObjectCfg, RigidObjectCollection, @@ -878,7 +880,7 @@ def _is_scene_setup_from_cfg(self) -> bool: def _add_entities_from_cfg(self): # noqa: C901 """Add scene entities from the config.""" - from isaaclab_physx.assets import DeformableObjectCfg, SurfaceGripperCfg # noqa: PLC0415 + from isaaclab_physx.assets import SurfaceGripperCfg # noqa: PLC0415 # store paths that are in global collision filter self._global_prim_paths = list() diff --git a/source/isaaclab/isaaclab/sim/__init__.py b/source/isaaclab/isaaclab/sim/__init__.py index e1458e7873f3..a0f1d706e1eb 100644 --- a/source/isaaclab/isaaclab/sim/__init__.py +++ b/source/isaaclab/isaaclab/sim/__init__.py @@ -40,8 +40,9 @@ "PhysxJointDrivePropertiesCfg", "CollisionPropertiesCfg", "PhysxCollisionPropertiesCfg", - "PhysXCollisionPropertiesCfg", + "DeformableBodyPropertiesCfg", "PhysxDeformableCollisionPropertiesCfg", + "PhysxDeformableBodyPropertiesCfg", "ArticulationRootPropertiesCfg", "PhysxArticulationRootPropertiesCfg", "MeshCollisionPropertiesCfg", @@ -63,8 +64,12 @@ # Names that moved out of this package into ``isaaclab_physx.sim.spawners.materials``. _PHYSX_FORWARDS_MATERIALS = frozenset({ + "DeformableBodyMaterialCfg", "RigidBodyMaterialCfg", + "SurfaceDeformableBodyMaterialCfg", "PhysxRigidBodyMaterialCfg", + "PhysxDeformableBodyMaterialCfg", + "PhysxSurfaceDeformableBodyMaterialCfg", }) _PHYSX_FORWARDS = _PHYSX_FORWARDS_SCHEMAS | _PHYSX_FORWARDS_MATERIALS diff --git a/source/isaaclab/isaaclab/sim/__init__.pyi b/source/isaaclab/isaaclab/sim/__init__.pyi index 2d5edfdf921f..0c787cc64c67 100644 --- a/source/isaaclab/isaaclab/sim/__init__.pyi +++ b/source/isaaclab/isaaclab/sim/__init__.pyi @@ -22,11 +22,13 @@ __all__ = [ "activate_contact_sensors", "define_articulation_root_properties", "define_collision_properties", + "define_deformable_body_properties", "define_mass_properties", "define_mesh_collision_properties", "define_rigid_body_properties", "modify_articulation_root_properties", "modify_collision_properties", + "modify_deformable_body_properties", "modify_fixed_tendon_properties", "modify_joint_drive_properties", "modify_mass_properties", @@ -39,6 +41,8 @@ __all__ = [ "CollisionBaseCfg", "ConvexDecompositionPropertiesCfg", "ConvexHullPropertiesCfg", + "DeformableBodyPropertiesBaseCfg", + "DeformableBodyPropertiesCfg", "FixedTendonPropertiesCfg", "JointDriveBaseCfg", "MassPropertiesCfg", @@ -60,6 +64,7 @@ __all__ = [ "TriangleMeshSimplificationPropertiesCfg", "SpawnerCfg", "RigidObjectSpawnerCfg", + "DeformableObjectSpawnerCfg", "spawn_from_mjcf", "spawn_from_urdf", "spawn_from_usd", @@ -78,8 +83,13 @@ __all__ = [ "LightCfg", "SphereLightCfg", "spawn_rigid_body_material", + "spawn_deformable_body_material", "PhysicsMaterialCfg", "RigidBodyMaterialCfg", + "DeformableBodyMaterialBaseCfg", + "DeformableBodyMaterialCfg", + "SurfaceDeformableBodyMaterialBaseCfg", + "SurfaceDeformableBodyMaterialCfg", "spawn_from_mdl_file", "spawn_preview_surface", "GlassMdlCfg", @@ -90,15 +100,15 @@ __all__ = [ "spawn_mesh_cone", "spawn_mesh_cuboid", "spawn_mesh_cylinder", + "spawn_mesh_rectangle", "spawn_mesh_sphere", - "spawn_mesh_square", "MeshCapsuleCfg", "MeshCfg", "MeshConeCfg", "MeshCuboidCfg", "MeshCylinderCfg", + "MeshRectangleCfg", "MeshSphereCfg", - "MeshSquareCfg", "spawn_camera", "spawn_sensor_frame", "FisheyeCameraCfg", @@ -198,11 +208,13 @@ from .schemas import ( activate_contact_sensors, define_articulation_root_properties, define_collision_properties, + define_deformable_body_properties, define_mass_properties, define_mesh_collision_properties, define_rigid_body_properties, modify_articulation_root_properties, modify_collision_properties, + modify_deformable_body_properties, modify_fixed_tendon_properties, modify_joint_drive_properties, modify_mass_properties, @@ -215,6 +227,8 @@ from .schemas import ( CollisionBaseCfg, ConvexDecompositionPropertiesCfg, ConvexHullPropertiesCfg, + DeformableBodyPropertiesBaseCfg, + DeformableBodyPropertiesCfg, FixedTendonPropertiesCfg, JointDriveBaseCfg, MassPropertiesCfg, @@ -240,6 +254,7 @@ NewtonRigidBodyPropertiesCfg = ... from .spawners import ( SpawnerCfg, RigidObjectSpawnerCfg, + DeformableObjectSpawnerCfg, spawn_from_mjcf, spawn_from_urdf, spawn_from_usd, @@ -258,8 +273,13 @@ from .spawners import ( LightCfg, SphereLightCfg, spawn_rigid_body_material, + spawn_deformable_body_material, PhysicsMaterialCfg, RigidBodyMaterialCfg, + DeformableBodyMaterialBaseCfg, + DeformableBodyMaterialCfg, + SurfaceDeformableBodyMaterialBaseCfg, + SurfaceDeformableBodyMaterialCfg, spawn_from_mdl_file, spawn_preview_surface, GlassMdlCfg, @@ -270,15 +290,15 @@ from .spawners import ( spawn_mesh_cone, spawn_mesh_cuboid, spawn_mesh_cylinder, + spawn_mesh_rectangle, spawn_mesh_sphere, - spawn_mesh_square, MeshCapsuleCfg, MeshCfg, MeshConeCfg, MeshCuboidCfg, MeshCylinderCfg, + MeshRectangleCfg, MeshSphereCfg, - MeshSquareCfg, spawn_camera, spawn_sensor_frame, FisheyeCameraCfg, diff --git a/source/isaaclab/isaaclab/sim/schemas/__init__.py b/source/isaaclab/isaaclab/sim/schemas/__init__.py index 223627d4e523..ed0e5a30ef07 100644 --- a/source/isaaclab/isaaclab/sim/schemas/__init__.py +++ b/source/isaaclab/isaaclab/sim/schemas/__init__.py @@ -46,8 +46,9 @@ "PhysxJointDrivePropertiesCfg", "CollisionPropertiesCfg", "PhysxCollisionPropertiesCfg", - "PhysXCollisionPropertiesCfg", + "DeformableBodyPropertiesCfg", "PhysxDeformableCollisionPropertiesCfg", + "PhysxDeformableBodyPropertiesCfg", "ArticulationRootPropertiesCfg", "PhysxArticulationRootPropertiesCfg", "MeshCollisionPropertiesCfg", diff --git a/source/isaaclab/isaaclab/sim/schemas/__init__.pyi b/source/isaaclab/isaaclab/sim/schemas/__init__.pyi index a9306afdf414..8d753b553d6c 100644 --- a/source/isaaclab/isaaclab/sim/schemas/__init__.pyi +++ b/source/isaaclab/isaaclab/sim/schemas/__init__.pyi @@ -11,11 +11,13 @@ __all__ = [ "define_actuator_properties", "define_articulation_root_properties", "define_collision_properties", + "define_deformable_body_properties", "define_mass_properties", "define_mesh_collision_properties", "define_rigid_body_properties", "modify_articulation_root_properties", "modify_collision_properties", + "modify_deformable_body_properties", "modify_fixed_tendon_properties", "modify_joint_drive_properties", "modify_mass_properties", @@ -26,6 +28,8 @@ __all__ = [ "BoundingCubePropertiesCfg", "BoundingSpherePropertiesCfg", "CollisionBaseCfg", + "DeformableBodyPropertiesBaseCfg", + "DeformableBodyPropertiesCfg", "JointDriveBaseCfg", "MassPropertiesCfg", "MeshCollisionBaseCfg", @@ -47,11 +51,13 @@ from .schemas import ( activate_contact_sensors, define_articulation_root_properties, define_collision_properties, + define_deformable_body_properties, define_mass_properties, define_mesh_collision_properties, define_rigid_body_properties, modify_articulation_root_properties, modify_collision_properties, + modify_deformable_body_properties, modify_fixed_tendon_properties, modify_joint_drive_properties, modify_mass_properties, @@ -67,6 +73,8 @@ from .schemas_cfg import ( BoundingCubePropertiesCfg, BoundingSpherePropertiesCfg, CollisionBaseCfg, + DeformableBodyPropertiesBaseCfg, + DeformableBodyPropertiesCfg, JointDriveBaseCfg, MassPropertiesCfg, MeshCollisionBaseCfg, diff --git a/source/isaaclab/isaaclab/sim/schemas/schemas.py b/source/isaaclab/isaaclab/sim/schemas/schemas.py index b7a74e27ff25..8bd2c314bf93 100644 --- a/source/isaaclab/isaaclab/sim/schemas/schemas.py +++ b/source/isaaclab/isaaclab/sim/schemas/schemas.py @@ -10,14 +10,19 @@ import logging import math -from pxr import Usd, UsdPhysics +import numpy as np +import warp as wp + +from pxr import Sdf, Usd, UsdGeom, UsdPhysics from isaaclab.sim.utils.stage import get_current_stage from isaaclab.utils.string import to_camel_case from ..utils import ( apply_nested, + create_prim, find_global_fixed_joint_prim, + get_all_matching_child_prims, safe_set_attribute_on_usd_prim, safe_set_attribute_on_usd_schema, ) @@ -1071,3 +1076,360 @@ def modify_mesh_collision_properties( # success return True + + +""" +Deformable body properties. +""" + + +@wp.kernel +def _fix_tet_winding_kernel( + points: wp.array(dtype=wp.vec3), + tet_indices: wp.array(ndim=2, dtype=wp.int32), +): + """Flip any tet with negative signed volume by swapping its last two vertex indices. + + ``UsdGeom.TetMesh`` and :meth:`UsdGeom.TetMesh.ComputeSurfaceFaces` require a + right-handed tet winding (positive signed volume). Swapping indices 2 and 3 + reverses the orientation without changing which four vertices form the tet. + """ + i = wp.tid() + v0 = tet_indices[i, 0] + v1 = tet_indices[i, 1] + v2 = tet_indices[i, 2] + v3 = tet_indices[i, 3] + p0 = points[v0] + e1 = points[v1] - p0 + e2 = points[v2] - p0 + e3 = points[v3] - p0 + signed_volume = wp.dot(e1, wp.cross(e2, e3)) + if signed_volume < 0.0: + tet_indices[i, 2] = v3 + tet_indices[i, 3] = v2 + + +def define_deformable_body_properties( + prim_path: str, + cfg: schemas_cfg.DeformableBodyPropertiesBaseCfg, + stage: Usd.Stage | None = None, + deformable_type: str = "volume", + sim_mesh_prim_path: str | None = None, +): + """Apply the deformable body schema on the input prim and set its properties. The input prim should + have a visual surface mesh as child. Volume deformables will have their simulation tetrahedral mesh + automatically computed from the surface mesh of the input prim. Surface deformables simply copy the visual mesh + as simulation mesh. + + See :func:`modify_deformable_body_properties` for more details on how the properties are set. + + .. note:: + If the input prim is not a mesh, this function will traverse the prim and find the first mesh + under it. If no mesh or multiple meshes are found, an error is raised. This is because the deformable + body schema can only be applied to a single mesh. + + Args: + prim_path: The prim path where to apply the deformable body schema. + cfg: The configuration for the deformable body. + stage: The stage where to find the prim. Defaults to None, in which case the + current stage is used. + deformable_type: The type of the deformable body (surface or volume). + This is used to determine which USD API to use for the deformable body. Defaults to "volume". + sim_mesh_prim_path: Optional override for the simulation mesh creation prim path. + Ignored when pre-tetrahedralized mesh is found for volume deformables. + If None, it is set to ``{prim_path}/sim_mesh``. + + Raises: + ValueError: When the prim path is not valid. + ValueError: When the prim has no mesh or multiple meshes. + RuntimeError: When setting the deformable body properties fails. + """ + from omni.physx.scripts import deformableUtils + + # get stage handle + if stage is None: + stage = get_current_stage() + + # get USD prim + root_prim = stage.GetPrimAtPath(prim_path) + # check if prim path is valid + if not root_prim.IsValid(): + raise ValueError(f"Prim path '{prim_path}' is not valid.") + + sim_mesh_prim = None + # for volume deformables, we check if a pre-tetrahedralized TetMesh exists for the sim_mesh + if deformable_type == "volume": + matching_prims = get_all_matching_child_prims(prim_path, lambda p: p.GetTypeName() == "TetMesh") + if len(matching_prims) == 0: + sim_mesh_prim = None + elif len(matching_prims) > 1: + # get list of all meshes found + mesh_paths = [p.GetPrimPath() for p in matching_prims] + raise ValueError( + f"Found multiple tetrahedral meshes in '{prim_path}': {mesh_paths}." + " Deformable body schema can only be applied to one mesh for now." + ) + else: + # found existing tetmesh + sim_mesh_prim = matching_prims[0] + if not sim_mesh_prim.IsValid(): + raise ValueError(f"Mesh prim path '{sim_mesh_prim.GetPrimPath()}' is not valid.") + + # Search for a visual surface mesh for both surface and volume deformables + matching_prims = get_all_matching_child_prims(prim_path, lambda p: p.GetTypeName() == "Mesh") + # check if the visual surface mesh is valid + if len(matching_prims) == 0: + # in case a TetMesh is found but no Mesh is found, we use the TetMesh surface as visual. + if sim_mesh_prim is not None: + tet_mesh_prim = UsdGeom.TetMesh(sim_mesh_prim) + surface_indices = UsdGeom.TetMesh.ComputeSurfaceFaces(tet_mesh_prim, Usd.TimeCode.Default()) + if surface_indices is None or len(surface_indices) == 0: + raise ValueError( + f"Deformable body at '{prim_path}' has no surface indices on its TetMesh prim; " + "cannot sync to visual mesh." + ) + # create visual mesh + vis_mesh_prim = create_prim( + prim_path + "/vis_mesh", + prim_type="Mesh", + attributes={ + "points": tet_mesh_prim.GetPointsAttr().Get(), + "faceVertexIndices": np.asarray(surface_indices).flatten(), + "faceVertexCounts": [3] * len(surface_indices), + }, + stage=stage, + ) + matching_prims = [vis_mesh_prim] + else: + raise ValueError(f"Could not find any visual mesh in '{prim_path}'. Please check asset.") + if len(matching_prims) > 1: + # get list of all meshes found + mesh_paths = [p.GetPrimPath() for p in matching_prims] + raise ValueError( + f"Found multiple visual meshes in '{prim_path}': {mesh_paths}." + " Deformable body schema can only be applied to one mesh for now." + ) + vis_mesh_prim = matching_prims[0] + + # check if the prim is valid + if not vis_mesh_prim.IsValid(): + raise ValueError(f"Mesh prim path '{vis_mesh_prim.GetPrimPath()}' is not valid.") + + # remove potential previous configuration + deformableUtils.remove_deformable_body(stage, prim_path) + + # create and set simulation/root prim properties based on the type of the deformable mesh (surface vs volume) + sim_mesh_prim_path = prim_path + "/sim_mesh" if sim_mesh_prim_path is None else sim_mesh_prim_path + # extract visual surface mesh vertices and faces + vertices = np.array(vis_mesh_prim.GetAttribute("points").Get()) + faces = np.array(vis_mesh_prim.GetAttribute("faceVertexIndices").Get()).flatten() + face_counts = np.array(vis_mesh_prim.GetAttribute("faceVertexCounts").Get()) + if deformable_type == "surface": + # create simulation mesh as copy of visual mesh + sim_mesh_prim = create_prim( + sim_mesh_prim_path, + prim_type="Mesh", + attributes={ + "points": vertices, + "faceVertexIndices": faces, + "faceVertexCounts": face_counts, + }, + stage=stage, + ) + + # apply sim API + if not sim_mesh_prim.ApplyAPI("OmniPhysicsSurfaceDeformableSimAPI"): + raise RuntimeError(f"Failed to set surface deformable body API on prim '{sim_mesh_prim_path}'.") + + # apply collision API + if not sim_mesh_prim.ApplyAPI(UsdPhysics.CollisionAPI): + raise RuntimeError(f"Failed to set surface deformable collision API on prim '{sim_mesh_prim_path}'.") + + # set rest-shape attributes required by OmniPhysicsSurfaceDeformableSimAPI + sim_mesh_prim.GetAttribute("omniphysics:restShapePoints").Set(vertices) + sim_mesh_prim.GetAttribute("omniphysics:restTriVtxIndices").Set(faces) + + elif deformable_type == "volume": + if sim_mesh_prim is None: + try: + from pytetwild import tetrahedralize + except ImportError as exc: + raise ImportError( + "Automatic tetrahedralization of volume deformables requires the optional 'pytetwild' " + "package. Install pytetwild or provide a pre-tetrahedralized UsdGeom.TetMesh under the " + f"deformable prim '{prim_path}'." + ) from exc + + tet_mesh_points, tet_mesh_indices = tetrahedralize( + vertices, + faces.reshape(-1, 3), + edge_length_fac=0.1, + simplify=False, + epsilon=1e-2, + coarsen=True, + ) + # pytetwild's default ordering does not guarantee positive signed volume, which + # ``UsdGeom.TetMesh`` and ``ComputeSurfaceFaces`` require. Flip any inverted tets. + device = "cpu" + _tet_points_wp = wp.array(tet_mesh_points.astype(np.float32), dtype=wp.vec3, device=device) + _tet_indices_wp = wp.array( + np.asarray(tet_mesh_indices, dtype=np.int32).reshape(-1, 4), dtype=wp.int32, device=device + ) + wp.launch( + _fix_tet_winding_kernel, + dim=_tet_indices_wp.shape[0], + inputs=[_tet_points_wp, _tet_indices_wp], + device=device, + ) + tet_mesh_indices = _tet_indices_wp.numpy() + sim_mesh_prim = create_prim( + sim_mesh_prim_path, + prim_type="TetMesh", + attributes={ + "points": tet_mesh_points, + "tetVertexIndices": tet_mesh_indices, + }, + stage=stage, + ) + + # apply sim API + if not sim_mesh_prim.ApplyAPI("OmniPhysicsVolumeDeformableSimAPI"): + raise RuntimeError(f"Failed to set volume deformable body API on prim '{sim_mesh_prim_path}'.") + + # apply collision API + if not sim_mesh_prim.ApplyAPI(UsdPhysics.CollisionAPI): + raise RuntimeError(f"Failed to set volume deformable collision API on prim '{sim_mesh_prim_path}'.") + + # set surface faces and rest-shape attributes required by OmniPhysicsVolumeDeformableSimAPI + surface_face_indices = UsdGeom.TetMesh.ComputeSurfaceFaces( + UsdGeom.TetMesh(sim_mesh_prim), Usd.TimeCode.Default() + ) + UsdGeom.TetMesh(sim_mesh_prim).GetSurfaceFaceVertexIndicesAttr().Set(surface_face_indices) + sim_mesh_prim.GetAttribute("omniphysics:restShapePoints").Set(sim_mesh_prim.GetAttribute("points").Get()) + sim_mesh_prim.GetAttribute("omniphysics:restTetVtxIndices").Set( + sim_mesh_prim.GetAttribute("tetVertexIndices").Get() + ) + + else: + raise ValueError( + f"""Unsupported deformable type: '{deformable_type}'. + Only surface and volume deformables are supported.""" + ) + + # TODO: Temporary solution: Overwrite visual mesh with tet mesh surface points or copy + # surface sim mesh to vis mesh. In the future we can have separate visual from simulation mesh. + # This currently does not work if an asset is loaded where the visual mesh is not the simulation mesh surface. + vis_mesh = UsdGeom.Mesh(vis_mesh_prim) + if deformable_type == "volume": + tet_mesh_prim = UsdGeom.TetMesh(sim_mesh_prim) + surface_indices = tet_mesh_prim.GetSurfaceFaceVertexIndicesAttr().Get() + if surface_indices is None or len(surface_indices) == 0: + raise ValueError( + f"Deformable body at '{prim_path}' has no surface indices on its TetMesh prim; " + "cannot sync to visual mesh." + ) + vis_mesh.GetPointsAttr().Set(tet_mesh_prim.GetPointsAttr().Get()) + vis_mesh.GetFaceVertexIndicesAttr().Set(np.asarray(surface_indices).flatten()) + vis_mesh.GetFaceVertexCountsAttr().Set([3] * len(surface_indices)) + else: + sim_mesh = UsdGeom.Mesh(sim_mesh_prim) + vis_mesh.GetFaceVertexIndicesAttr().Set(sim_mesh.GetFaceVertexIndicesAttr().Get()) + vis_mesh.GetFaceVertexCountsAttr().Set(sim_mesh.GetFaceVertexCountsAttr().Get()) + + # bind visual to sim mesh by applying bind pose deformable pose API + purposes = ["bindPose"] + vis_mesh_prim.ApplyAPI("OmniPhysicsDeformablePoseAPI", "default") + vis_mesh_prim.CreateAttribute("deformablePose:default:omniphysics:purposes", Sdf.ValueTypeNames.TokenArray).Set( + purposes + ) + points = UsdGeom.PointBased(vis_mesh_prim).GetPointsAttr().Get() + vis_mesh_prim.CreateAttribute("deformablePose:default:omniphysics:points", Sdf.ValueTypeNames.Point3fArray).Set( + points + ) + + sim_mesh_prim.ApplyAPI("OmniPhysicsDeformablePoseAPI", "default") + sim_mesh_prim.CreateAttribute("deformablePose:default:omniphysics:purposes", Sdf.ValueTypeNames.TokenArray).Set( + purposes + ) + + # disable simulation mesh for rendering + UsdGeom.Imageable(sim_mesh_prim).GetPurposeAttr().Set(UsdGeom.Tokens.guide) + + # apply deformable body api + if not root_prim.ApplyAPI("OmniPhysicsDeformableBodyAPI"): + raise RuntimeError(f"Failed to set deformable body API on prim '{prim_path}'.") + + # set deformable body properties + modify_deformable_body_properties(prim_path, cfg, stage) + + +@apply_nested +def modify_deformable_body_properties( + prim_path: str, cfg: schemas_cfg.DeformableBodyPropertiesBaseCfg, stage: Usd.Stage | None = None +): + """Modify deformable body parameters for a deformable body prim. + + A `deformable body`_ is a single body (either surface or volume deformable) that can be simulated by PhysX + or Newton. Unlike rigid bodies, deformable bodies support relative motion of the nodes in the mesh. + Consequently, they can be used to simulate deformations under applied forces. + + PhysX deformable body simulation employs Finite Element Analysis (FEA) to simulate the deformations of the mesh. + It uses two meshes to represent the deformable body: + + 1. **Simulation mesh**: This mesh is used for the simulation and is the one that is deformed by the solver. + 2. **Collision mesh**: This mesh only needs to match the surface of the simulation mesh and is used for + collision detection. + + For most applications, we assume that the above two meshes are computed from the "render mesh" of the deformable + body. The render mesh is the mesh that is visible in the scene and is used for rendering purposes. It is composed + of triangles, while the simulation mesh is composed of tetrahedrons for volume deformables, + and triangles for surface deformables. + + We apply similar design choices to the simulation in Newton with a separate visual, simulation and collision mesh. + + .. caution:: + The deformable body schema is still under development by the Omniverse team. The current implementation + works with the PhysX schemas shipped with Isaac Sim 6.0.0 onwards. It may change in future releases. + + .. note:: + This function is decorated with :func:`apply_nested` that sets the properties to all the prims + (that have the schema applied on them) under the input prim path. + + .. _deformable body: https://nvidia-omniverse.github.io/PhysX/physx/5.6.1/docs/DeformableVolume.html + .. _PhysxDeformableBodyAPI: https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/latest/physxschema/annotated.html + + Args: + prim_path: The prim path to the deformable body. + cfg: The configuration for the deformable body. + stage: The stage where to find the prim. Defaults to None, in which case the + current stage is used. + + Returns: + True if the properties were successfully set, False otherwise. + """ + # get stage handle + if stage is None: + stage = get_current_stage() + + # get deformable-body USD prim + deformable_body_prim = stage.GetPrimAtPath(prim_path) + # check if the prim is valid + if not deformable_body_prim.IsValid(): + return False + # check if deformable body API is applied + if "OmniPhysicsDeformableBodyAPI" not in deformable_body_prim.GetAppliedSchemas(): + return False + + # build cfg dict from dataclass fields only; USD routing is driven by the + # declaring classes' ``_usd_namespace`` / ``_usd_applied_schema`` metadata. + cfg_dict = {f.name: getattr(cfg, f.name) for f in dataclasses.fields(cfg)} + + if cfg_dict.get("kinematic_enabled"): + logger.warning( + "Kinematic deformable bodies are not fully supported in the current version of Omni Physics. " + "Setting kinematic_enabled to True may lead to unexpected behavior." + ) + + _apply_namespaced_schemas(deformable_body_prim, cfg, cfg_dict) + # success + return True diff --git a/source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py b/source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py index b154d91f4cc5..d6dc99a84748 100644 --- a/source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py +++ b/source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py @@ -22,8 +22,9 @@ "PhysxJointDrivePropertiesCfg", "CollisionPropertiesCfg", "PhysxCollisionPropertiesCfg", - "PhysXCollisionPropertiesCfg", + "DeformableBodyPropertiesCfg", "PhysxDeformableCollisionPropertiesCfg", + "PhysxDeformableBodyPropertiesCfg", "ArticulationRootPropertiesCfg", "PhysxArticulationRootPropertiesCfg", "MeshCollisionPropertiesCfg", @@ -543,3 +544,14 @@ class BoundingSpherePropertiesCfg(MeshCollisionBaseCfg): mesh_approximation_name: str = "boundingSphere" """Name of mesh collision approximation method. Default: "boundingSphere".""" + + +@configclass +class DeformableBodyPropertiesBaseCfg: + """Base deformable body properties for backend-specific extensions. + + This class is currently empty. It will be populated once the USD deformable + schemas can be unified more cleanly between physics backends. + """ + + pass diff --git a/source/isaaclab/isaaclab/sim/spawners/__init__.pyi b/source/isaaclab/isaaclab/sim/spawners/__init__.pyi index dae1a432b47e..c936d166ba3c 100644 --- a/source/isaaclab/isaaclab/sim/spawners/__init__.pyi +++ b/source/isaaclab/isaaclab/sim/spawners/__init__.pyi @@ -6,6 +6,7 @@ __all__ = [ "SpawnerCfg", "RigidObjectSpawnerCfg", + "DeformableObjectSpawnerCfg", "spawn_from_mjcf", "spawn_from_urdf", "spawn_from_usd", @@ -24,8 +25,13 @@ __all__ = [ "LightCfg", "SphereLightCfg", "spawn_rigid_body_material", + "spawn_deformable_body_material", "PhysicsMaterialCfg", "RigidBodyMaterialCfg", + "DeformableBodyMaterialBaseCfg", + "DeformableBodyMaterialCfg", + "SurfaceDeformableBodyMaterialBaseCfg", + "SurfaceDeformableBodyMaterialCfg", "spawn_from_mdl_file", "spawn_preview_surface", "GlassMdlCfg", @@ -36,15 +42,15 @@ __all__ = [ "spawn_mesh_cone", "spawn_mesh_cuboid", "spawn_mesh_cylinder", + "spawn_mesh_rectangle", "spawn_mesh_sphere", - "spawn_mesh_square", "MeshCapsuleCfg", "MeshCfg", "MeshConeCfg", "MeshCuboidCfg", "MeshCylinderCfg", + "MeshRectangleCfg", "MeshSphereCfg", - "MeshSquareCfg", "spawn_camera", "spawn_sensor_frame", "FisheyeCameraCfg", @@ -67,7 +73,7 @@ __all__ = [ "MultiUsdFileCfg", ] -from .spawner_cfg import SpawnerCfg, RigidObjectSpawnerCfg +from .spawner_cfg import SpawnerCfg, RigidObjectSpawnerCfg, DeformableObjectSpawnerCfg from .from_files import ( spawn_from_mjcf, spawn_from_urdf, @@ -91,8 +97,13 @@ from .lights import ( ) from .materials import ( spawn_rigid_body_material, + spawn_deformable_body_material, PhysicsMaterialCfg, RigidBodyMaterialCfg, + DeformableBodyMaterialBaseCfg, + DeformableBodyMaterialCfg, + SurfaceDeformableBodyMaterialBaseCfg, + SurfaceDeformableBodyMaterialCfg, spawn_from_mdl_file, spawn_preview_surface, GlassMdlCfg, @@ -105,14 +116,14 @@ from .meshes import ( spawn_mesh_cone, spawn_mesh_cuboid, spawn_mesh_cylinder, + spawn_mesh_rectangle, spawn_mesh_sphere, - spawn_mesh_square, MeshCapsuleCfg, MeshCfg, MeshConeCfg, MeshCuboidCfg, MeshCylinderCfg, - MeshSquareCfg, + MeshRectangleCfg, MeshSphereCfg, ) from .sensors import spawn_camera, spawn_sensor_frame, FisheyeCameraCfg, PinholeCameraCfg, SensorFrameCfg diff --git a/source/isaaclab/isaaclab/sim/spawners/from_files/from_files.py b/source/isaaclab/isaaclab/sim/spawners/from_files/from_files.py index 18ffcaf37c98..9367ef3cc801 100644 --- a/source/isaaclab/isaaclab/sim/spawners/from_files/from_files.py +++ b/source/isaaclab/isaaclab/sim/spawners/from_files/from_files.py @@ -13,14 +13,10 @@ from filelock import FileLock -# deformables only supported on PhysX backend -from isaaclab_physx.sim import schemas as schemas_physx -from isaaclab_physx.sim.spawners.materials import SurfaceDeformableBodyMaterialCfg - from pxr import Gf, Sdf, Usd, UsdGeom from isaaclab.sim import converters, schemas -from isaaclab.sim.spawners.materials import RigidBodyMaterialCfg +from isaaclab.sim.spawners.materials import RigidBodyMaterialCfg, SurfaceDeformableBodyMaterialBaseCfg from isaaclab.sim.utils import ( add_labels, bind_physics_material, @@ -387,15 +383,17 @@ def _spawn_from_usd_file( # define deformable body properties, or modify if deformable body API is present (PhysX only) if cfg.deformable_props is not None: prim = stage.GetPrimAtPath(prim_path) - deformable_type = "surface" if isinstance(cfg.physics_material, SurfaceDeformableBodyMaterialCfg) else "volume" + deformable_type = ( + "surface" if isinstance(cfg.physics_material, SurfaceDeformableBodyMaterialBaseCfg) else "volume" + ) if "OmniPhysicsDeformableBodyAPI" in prim.GetAppliedSchemas(): - schemas_physx.modify_deformable_body_properties(prim_path, cfg.deformable_props, stage) + schemas.modify_deformable_body_properties(prim_path, cfg.deformable_props, stage) else: - schemas_physx.define_deformable_body_properties(prim_path, cfg.deformable_props, stage, deformable_type) + schemas.define_deformable_body_properties(prim_path, cfg.deformable_props, stage, deformable_type) if cfg.mass_props is not None: raise ValueError( """MassPropertiesCfg are not supported for deformable bodies - and should be set through DeformableBodyPropertiesCfg(mass=).""" + and should be set through deformable_props with mass=.""" ) # apply visual material diff --git a/source/isaaclab/isaaclab/sim/spawners/from_files/from_files_cfg.py b/source/isaaclab/isaaclab/sim/spawners/from_files/from_files_cfg.py index 1d61cde5725d..22857f8d45b1 100644 --- a/source/isaaclab/isaaclab/sim/spawners/from_files/from_files_cfg.py +++ b/source/isaaclab/isaaclab/sim/spawners/from_files/from_files_cfg.py @@ -8,12 +8,9 @@ from collections.abc import Callable from dataclasses import MISSING -# deformables only supported on PhysX backend -from isaaclab_physx.sim.spawners.spawner_cfg import DeformableObjectSpawnerCfg - from isaaclab.sim import converters, schemas from isaaclab.sim.spawners import materials -from isaaclab.sim.spawners.spawner_cfg import RigidObjectSpawnerCfg, SpawnerCfg +from isaaclab.sim.spawners.spawner_cfg import DeformableObjectSpawnerCfg, RigidObjectSpawnerCfg, SpawnerCfg from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR from isaaclab.utils.configclass import configclass diff --git a/source/isaaclab/isaaclab/sim/spawners/materials/__init__.py b/source/isaaclab/isaaclab/sim/spawners/materials/__init__.py index 3158625219c5..0df8e552ea2a 100644 --- a/source/isaaclab/isaaclab/sim/spawners/materials/__init__.py +++ b/source/isaaclab/isaaclab/sim/spawners/materials/__init__.py @@ -60,8 +60,12 @@ # Resolved lazily on first access so importing ``isaaclab.sim.spawners.materials`` does # not require ``isaaclab_physx`` to be installed. _PHYSX_FORWARDS = frozenset({ + "DeformableBodyMaterialCfg", "RigidBodyMaterialCfg", + "SurfaceDeformableBodyMaterialCfg", "PhysxRigidBodyMaterialCfg", + "PhysxDeformableBodyMaterialCfg", + "PhysxSurfaceDeformableBodyMaterialCfg", }) diff --git a/source/isaaclab/isaaclab/sim/spawners/materials/__init__.pyi b/source/isaaclab/isaaclab/sim/spawners/materials/__init__.pyi index 0dd023c2998f..7ce7815955c2 100644 --- a/source/isaaclab/isaaclab/sim/spawners/materials/__init__.pyi +++ b/source/isaaclab/isaaclab/sim/spawners/materials/__init__.pyi @@ -5,8 +5,13 @@ __all__ = [ "spawn_rigid_body_material", + "spawn_deformable_body_material", "PhysicsMaterialCfg", "RigidBodyMaterialBaseCfg", + "DeformableBodyMaterialBaseCfg", + "DeformableBodyMaterialCfg", + "SurfaceDeformableBodyMaterialBaseCfg", + "SurfaceDeformableBodyMaterialCfg", "spawn_from_mdl_file", "spawn_preview_surface", "GlassMdlCfg", @@ -15,10 +20,14 @@ __all__ = [ "VisualMaterialCfg", ] -from .physics_materials import spawn_rigid_body_material +from .physics_materials import spawn_rigid_body_material, spawn_deformable_body_material from .physics_materials_cfg import ( PhysicsMaterialCfg, RigidBodyMaterialBaseCfg, + DeformableBodyMaterialBaseCfg, + DeformableBodyMaterialCfg, + SurfaceDeformableBodyMaterialBaseCfg, + SurfaceDeformableBodyMaterialCfg, ) from .visual_materials import spawn_from_mdl_file, spawn_preview_surface from .visual_materials_cfg import GlassMdlCfg, MdlFileCfg, PreviewSurfaceCfg, VisualMaterialCfg diff --git a/source/isaaclab/isaaclab/sim/spawners/materials/physics_materials.py b/source/isaaclab/isaaclab/sim/spawners/materials/physics_materials.py index b76077b38bed..8c12bee9442d 100644 --- a/source/isaaclab/isaaclab/sim/spawners/materials/physics_materials.py +++ b/source/isaaclab/isaaclab/sim/spawners/materials/physics_materials.py @@ -6,7 +6,6 @@ from __future__ import annotations import dataclasses -from typing import TYPE_CHECKING from pxr import Usd, UsdPhysics, UsdShade @@ -14,8 +13,7 @@ from isaaclab.sim.utils import clone from isaaclab.sim.utils.stage import get_current_stage -if TYPE_CHECKING: - from . import physics_materials_cfg +from . import physics_materials_cfg @clone @@ -76,3 +74,49 @@ def spawn_rigid_body_material(prim_path: str, cfg: physics_materials_cfg.RigidBo # return the prim return prim + + +@clone +def spawn_deformable_body_material( + prim_path: str, cfg: physics_materials_cfg.DeformableBodyMaterialBaseCfg +) -> Usd.Prim: + """Create material with deformable-body physics properties. + + Deformable body materials are used to define the physical properties to meshes of a deformable body. These + include the friction and deformable body properties. For more information on deformable body material, + please refer to the documentation on `PxFEMSoftBodyMaterial`_. + + .. note:: + This function is decorated with :func:`clone` that resolves prim path into list of paths + if the input prim path is a regex pattern. This is done to support spawning multiple assets + from a single and cloning the USD prim at the given path expression. + + Args: + prim_path: The prim path or pattern to spawn the asset at. If the prim path is a regex pattern, + then the asset is spawned at all the matching prim paths. + cfg: The configuration for the physics material. + + Returns: + The spawned deformable body material prim. + + Raises: + ValueError: When a prim already exists at the specified prim path and is not a material. + + .. _PxFEMSoftBodyMaterial: https://nvidia-omniverse.github.io/PhysX/physx/5.4.1/_api_build/structPxFEMSoftBodyMaterialModel.html + """ + # get stage handle + stage = get_current_stage() + + # create material prim if no prim exists + if not stage.GetPrimAtPath(prim_path).IsValid(): + _ = UsdShade.Material.Define(stage, prim_path) + + # obtain prim + prim = stage.GetPrimAtPath(prim_path) + # check if prim is a material + if not prim.IsA(UsdShade.Material): + raise ValueError(f"A prim already exists at path: '{prim_path}' but is not a material.") + cfg_dict = {f.name: getattr(cfg, f.name) for f in dataclasses.fields(cfg) if f.name != "func"} + _apply_namespaced_schemas(prim, cfg, cfg_dict) + # return the prim + return prim diff --git a/source/isaaclab/isaaclab/sim/spawners/materials/physics_materials_cfg.py b/source/isaaclab/isaaclab/sim/spawners/materials/physics_materials_cfg.py index 886b049b77b0..5c88731cf8d5 100644 --- a/source/isaaclab/isaaclab/sim/spawners/materials/physics_materials_cfg.py +++ b/source/isaaclab/isaaclab/sim/spawners/materials/physics_materials_cfg.py @@ -15,7 +15,16 @@ # Resolved lazily so callers using ``from isaaclab.sim.spawners.materials.physics_materials_cfg # import RigidBodyMaterialCfg`` continue to work without importing ``isaaclab_physx`` at module # load time. -_PHYSX_FORWARDS = frozenset({"RigidBodyMaterialCfg", "PhysxRigidBodyMaterialCfg"}) +_PHYSX_FORWARDS = frozenset( + { + "DeformableBodyMaterialCfg", + "RigidBodyMaterialCfg", + "SurfaceDeformableBodyMaterialCfg", + "PhysxRigidBodyMaterialCfg", + "PhysxDeformableBodyMaterialCfg", + "PhysxSurfaceDeformableBodyMaterialCfg", + } +) def __getattr__(name): @@ -78,3 +87,19 @@ class RigidBodyMaterialBaseCfg(PhysicsMaterialCfg): restitution: float = 0.0 """The restitution coefficient. Defaults to 0.0.""" + + +@configclass +class DeformableBodyMaterialBaseCfg(PhysicsMaterialCfg): + """Base physics material parameters for volume deformable bodies. + + Backend-specific subclasses provide the material fields and spawning function + through :attr:`func`. + """ + + func: Callable | str | None = None + + +@configclass +class SurfaceDeformableBodyMaterialBaseCfg(DeformableBodyMaterialBaseCfg): + """Base physics material parameters for surface deformable bodies.""" diff --git a/source/isaaclab/isaaclab/sim/spawners/meshes/__init__.pyi b/source/isaaclab/isaaclab/sim/spawners/meshes/__init__.pyi index 06490befb33f..c853dca4d1d0 100644 --- a/source/isaaclab/isaaclab/sim/spawners/meshes/__init__.pyi +++ b/source/isaaclab/isaaclab/sim/spawners/meshes/__init__.pyi @@ -8,15 +8,15 @@ __all__ = [ "spawn_mesh_cone", "spawn_mesh_cuboid", "spawn_mesh_cylinder", + "spawn_mesh_rectangle", "spawn_mesh_sphere", - "spawn_mesh_square", "MeshCapsuleCfg", "MeshCfg", "MeshConeCfg", "MeshCuboidCfg", "MeshCylinderCfg", + "MeshRectangleCfg", "MeshSphereCfg", - "MeshSquareCfg", ] from .meshes import ( @@ -24,8 +24,8 @@ from .meshes import ( spawn_mesh_cone, spawn_mesh_cuboid, spawn_mesh_cylinder, + spawn_mesh_rectangle, spawn_mesh_sphere, - spawn_mesh_square, ) from .meshes_cfg import ( MeshCapsuleCfg, @@ -33,6 +33,6 @@ from .meshes_cfg import ( MeshConeCfg, MeshCuboidCfg, MeshCylinderCfg, + MeshRectangleCfg, MeshSphereCfg, - MeshSquareCfg, ) diff --git a/source/isaaclab/isaaclab/sim/spawners/meshes/meshes.py b/source/isaaclab/isaaclab/sim/spawners/meshes/meshes.py index 75ead1c8fb67..cfc7f51b9ff2 100644 --- a/source/isaaclab/isaaclab/sim/spawners/meshes/meshes.py +++ b/source/isaaclab/isaaclab/sim/spawners/meshes/meshes.py @@ -11,16 +11,12 @@ import trimesh import trimesh.transformations -# deformables only supported on PhysX backend -from isaaclab_physx.sim import schemas as schemas_physx -from isaaclab_physx.sim.spawners.materials import DeformableBodyMaterialCfg, SurfaceDeformableBodyMaterialCfg - from pxr import Usd, UsdPhysics from isaaclab.sim import schemas from isaaclab.sim.utils import bind_physics_material, bind_visual_material, clone, create_prim, get_current_stage -from ..materials import RigidBodyMaterialCfg +from ..materials import DeformableBodyMaterialBaseCfg, RigidBodyMaterialCfg, SurfaceDeformableBodyMaterialBaseCfg if TYPE_CHECKING: from . import meshes_cfg @@ -261,14 +257,14 @@ def spawn_mesh_cone( @clone -def spawn_mesh_square( +def spawn_mesh_rectangle( prim_path: str, - cfg: meshes_cfg.MeshSquareCfg, + cfg: meshes_cfg.MeshRectangleCfg, translation: tuple[float, float, float] | None = None, orientation: tuple[float, float, float, float] | None = None, **kwargs, ) -> Usd.Prim: - """Create a USD-Mesh 2D square prim with the given attributes. + """Create a USD-Mesh 2D rectangle prim with the given attributes. .. note:: This function is decorated with :func:`clone` that resolves prim path into list of paths @@ -294,12 +290,13 @@ def spawn_mesh_square( # create a 2D triangle mesh grid from omni.physx.scripts import deformableUtils - vertices, faces = deformableUtils.create_triangle_mesh_square(cfg.resolution[0], cfg.resolution[1], scale=cfg.size) + vertices, faces = deformableUtils.create_triangle_mesh_square(cfg.resolution[0], cfg.resolution[1], scale=1.0) + vertices = np.array([(v[0] * cfg.size[0], v[1] * cfg.size[1], v[2]) for v in vertices], dtype=np.float32) grid = trimesh.Trimesh(vertices=vertices, faces=np.array(faces).reshape(-1, 3), process=False) # obtain stage handle stage = get_current_stage() - # spawn the square as a mesh + # spawn the rectangle as a mesh _spawn_mesh_geom_from_mesh(prim_path, cfg, grid, translation, orientation, None, stage=stage) # return the prim return stage.GetPrimAtPath(prim_path) @@ -367,7 +364,7 @@ def _spawn_mesh_geom_from_mesh( raise ValueError("Cannot use both deformable and collision properties at the same time.") # check material types are correct if cfg.deformable_props is not None and cfg.physics_material is not None: - if not isinstance(cfg.physics_material, DeformableBodyMaterialCfg): + if not isinstance(cfg.physics_material, DeformableBodyMaterialBaseCfg): raise ValueError("Deformable properties require a deformable physics material.") if cfg.rigid_props is not None and cfg.physics_material is not None: if not isinstance(cfg.physics_material, RigidBodyMaterialCfg): @@ -393,14 +390,16 @@ def _spawn_mesh_geom_from_mesh( if cfg.deformable_props is not None: # apply deformable body properties - deformable_type = "surface" if isinstance(cfg.physics_material, SurfaceDeformableBodyMaterialCfg) else "volume" - schemas_physx.define_deformable_body_properties( + deformable_type = ( + "surface" if isinstance(cfg.physics_material, SurfaceDeformableBodyMaterialBaseCfg) else "volume" + ) + schemas.define_deformable_body_properties( prim_path, cfg.deformable_props, stage=stage, deformable_type=deformable_type ) if cfg.mass_props is not None: raise ValueError( """MassPropertiesCfg are not supported for deformable bodies - and should be set through DeformableBodyPropertiesCfg(mass=).""" + and should be set through deformable_props with mass=.""" ) elif cfg.collision_props is not None: # decide on type of collision approximation based on the mesh diff --git a/source/isaaclab/isaaclab/sim/spawners/meshes/meshes_cfg.py b/source/isaaclab/isaaclab/sim/spawners/meshes/meshes_cfg.py index 6bdf9ebf9baa..c6eb26507d3a 100644 --- a/source/isaaclab/isaaclab/sim/spawners/meshes/meshes_cfg.py +++ b/source/isaaclab/isaaclab/sim/spawners/meshes/meshes_cfg.py @@ -9,11 +9,8 @@ from dataclasses import MISSING from typing import Literal -# deformables only supported on PhysX backend -from isaaclab_physx.sim.spawners.spawner_cfg import DeformableObjectSpawnerCfg - from isaaclab.sim.spawners import materials -from isaaclab.sim.spawners.spawner_cfg import RigidObjectSpawnerCfg +from isaaclab.sim.spawners.spawner_cfg import DeformableObjectSpawnerCfg, RigidObjectSpawnerCfg from isaaclab.utils.configclass import configclass @@ -146,15 +143,15 @@ class MeshConeCfg(MeshCfg): @configclass -class MeshSquareCfg(MeshCfg): - """Configuration parameters for a 2D square mesh prim. +class MeshRectangleCfg(MeshCfg): + """Configuration parameters for a 2D rectangle mesh prim. - See :meth:`spawn_mesh_square` for more information. + See :meth:`spawn_mesh_rectangle` for more information. """ - func: Callable | str = "{DIR}.meshes:spawn_mesh_square" + func: Callable | str = "{DIR}.meshes:spawn_mesh_rectangle" - size: float = MISSING - """Edge length of the square (in m).""" + size: tuple[float, float] = MISSING + """Edge lengths of the rectangle along the X and Y axes [m].""" resolution: tuple[int, int] = (5, 5) - """Resolution of the square (in elements/edges per side).""" + """Resolution of the rectangle (in elements/edges per side).""" diff --git a/source/isaaclab/isaaclab/sim/spawners/spawner_cfg.py b/source/isaaclab/isaaclab/sim/spawners/spawner_cfg.py index 3cc632e2c0d5..3f1eef72a2fa 100644 --- a/source/isaaclab/isaaclab/sim/spawners/spawner_cfg.py +++ b/source/isaaclab/isaaclab/sim/spawners/spawner_cfg.py @@ -100,3 +100,25 @@ class RigidObjectSpawnerCfg(SpawnerCfg): This adds the PhysxContactReporter API to all the rigid bodies in the given prim path and its children. """ + + +@configclass +class DeformableObjectSpawnerCfg(SpawnerCfg): + """Configuration parameters for spawning a deformable asset. + + Unlike rigid objects, deformable objects are affected by forces and can deform when subjected to + external forces. This class is used to configure the properties of the deformable object. + + Deformable bodies don't have a separate collision mesh. The collision mesh is the same as the visual mesh. + The collision properties such as rest and collision offsets are specified in the :attr:`deformable_props`. + + Note: + By default, all properties are set to None. This means that no properties will be added or modified + to the prim outside of the properties available by default when spawning the prim. + """ + + mass_props: schemas.MassPropertiesCfg | None = None + """Mass properties.""" + + deformable_props: schemas.DeformableBodyPropertiesBaseCfg | None = None + """Deformable body properties.""" diff --git a/source/isaaclab/isaaclab/sim/spawners/wrappers/wrappers_cfg.py b/source/isaaclab/isaaclab/sim/spawners/wrappers/wrappers_cfg.py index dafe5beb812f..285c1765016b 100644 --- a/source/isaaclab/isaaclab/sim/spawners/wrappers/wrappers_cfg.py +++ b/source/isaaclab/isaaclab/sim/spawners/wrappers/wrappers_cfg.py @@ -3,27 +3,10 @@ # # SPDX-License-Identifier: BSD-3-Clause -import warnings from dataclasses import MISSING -# deformables only supported in PhysX backend -try: - from isaaclab_physx.sim.spawners.spawner_cfg import DeformableObjectSpawnerCfg -except ImportError as e: - warnings.warn( - f"""Could not import DeformableObjectSpawnerCfg, is isaaclab_physx installed? - Safe to ignore if using newton only. Complete exception: {e}""" - ) - # import dummy class to avoid errors in type hints - from isaaclab.utils.configclass import configclass - - @configclass - class DeformableObjectSpawnerCfg: - deformable_props = None - - from isaaclab.sim.spawners.from_files import UsdFileCfg -from isaaclab.sim.spawners.spawner_cfg import RigidObjectSpawnerCfg, SpawnerCfg +from isaaclab.sim.spawners.spawner_cfg import DeformableObjectSpawnerCfg, RigidObjectSpawnerCfg, SpawnerCfg from isaaclab.utils.configclass import configclass diff --git a/source/isaaclab/setup.py b/source/isaaclab/setup.py index bc64609de3fd..57f732d4c589 100644 --- a/source/isaaclab/setup.py +++ b/source/isaaclab/setup.py @@ -30,6 +30,8 @@ # procedural-generation "trimesh", "pyglet>=2.1.6,<3", + # tetrahedralization for deformable bodies (pinned: >=0.3 unconditionally imports pyvista at package import time) + "pytetwild==0.2.3", # image processing "transformers==4.57.6", "einops", # needed for transformers, doesn't always auto-install diff --git a/source/isaaclab/test/sim/test_deformable_backend_split.py b/source/isaaclab/test/sim/test_deformable_backend_split.py new file mode 100644 index 000000000000..dc170fe55c5a --- /dev/null +++ b/source/isaaclab/test/sim/test_deformable_backend_split.py @@ -0,0 +1,131 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Tests for the backend split of deformable schemas and materials.""" + +import dataclasses + +import isaaclab_physx.sim.schemas as physx_schemas +from isaaclab_newton.sim.schemas import NewtonDeformableBodyPropertiesCfg +from isaaclab_newton.sim.spawners.materials import ( + NewtonDeformableBodyMaterialCfg, + NewtonDeformableMaterialCfg, + NewtonSurfaceDeformableBodyMaterialCfg, +) +from isaaclab_physx.sim.schemas import ( + OmniPhysicsDeformableBodyPropertiesCfg, + PhysxDeformableBodyPropertiesCfg, + PhysxDeformableCollisionPropertiesCfg, +) +from isaaclab_physx.sim.schemas.schemas_cfg import PhysXDeformableBodyPropertiesCfg +from isaaclab_physx.sim.spawners.materials import ( + PhysxDeformableBodyMaterialCfg, + PhysXDeformableMaterialCfg, + PhysxSurfaceDeformableBodyMaterialCfg, +) + +import isaaclab.sim.schemas as schemas +import isaaclab.sim.spawners.materials.physics_materials_cfg as core_materials_cfg +from isaaclab.sim.schemas import DeformableBodyPropertiesBaseCfg +from isaaclab.sim.spawners.materials import ( + DeformableBodyMaterialBaseCfg, + SurfaceDeformableBodyMaterialBaseCfg, +) + + +def _field_names(cls) -> set[str]: + return {field.name for field in dataclasses.fields(cls)} + + +def _assert_no_property_prefix_field(cls): + assert "_property_prefix" not in _field_names(cls) + + +def test_common_deformable_property_cfg_has_no_backend_fields(): + """Common deformable properties are empty backend extension points.""" + fields = _field_names(DeformableBodyPropertiesBaseCfg) + + assert fields == set() + _assert_no_property_prefix_field(DeformableBodyPropertiesBaseCfg) + assert not hasattr(DeformableBodyPropertiesBaseCfg, "_usd_namespace") + assert not hasattr(DeformableBodyPropertiesBaseCfg, "_usd_applied_schema") + + +def test_common_deformable_material_cfg_has_no_backend_fields(): + """Common deformable material bases are empty backend extension points.""" + fields = _field_names(DeformableBodyMaterialBaseCfg) + surface_fields = _field_names(SurfaceDeformableBodyMaterialBaseCfg) + + assert fields == {"func"} + assert surface_fields == {"func"} + assert "DeformableBodyMaterialCfg" not in core_materials_cfg.__dict__ + assert "SurfaceDeformableBodyMaterialCfg" not in core_materials_cfg.__dict__ + assert not hasattr(core_materials_cfg, "PhysXDeformableMaterialCfg") + assert not hasattr(core_materials_cfg, "NewtonDeformableMaterialCfg") + assert not hasattr(DeformableBodyMaterialBaseCfg, "_usd_namespace") + assert not hasattr(DeformableBodyMaterialBaseCfg, "_usd_applied_schema") + assert not hasattr(SurfaceDeformableBodyMaterialBaseCfg, "_usd_namespace") + assert not hasattr(SurfaceDeformableBodyMaterialBaseCfg, "_usd_applied_schema") + _assert_no_property_prefix_field(DeformableBodyMaterialBaseCfg) + _assert_no_property_prefix_field(SurfaceDeformableBodyMaterialBaseCfg) + + +def test_physx_deformable_cfgs_use_core_schema_and_material_functions(): + """PhysX deformable cfgs own PhysX fields while schema and material functions stay in core.""" + props = PhysxDeformableBodyPropertiesCfg() + material = PhysxDeformableBodyMaterialCfg() + surface_material = PhysxSurfaceDeformableBodyMaterialCfg() + + assert not hasattr(props, "define_func") + assert not hasattr(props, "modify_func") + assert physx_schemas.define_deformable_body_properties is schemas.define_deformable_body_properties + assert physx_schemas.modify_deformable_body_properties is schemas.modify_deformable_body_properties + assert str(material.func) == "isaaclab.sim.spawners.materials.physics_materials:spawn_deformable_body_material" + assert str(surface_material.func) == str(material.func) + _assert_no_property_prefix_field(type(props)) + _assert_no_property_prefix_field(type(material)) + _assert_no_property_prefix_field(type(surface_material)) + assert PhysXDeformableBodyPropertiesCfg._usd_namespace == "physxDeformableBody" + assert PhysXDeformableBodyPropertiesCfg._usd_applied_schema == "PhysxBaseDeformableBodyAPI" + assert OmniPhysicsDeformableBodyPropertiesCfg._usd_namespace == "omniphysics" + assert OmniPhysicsDeformableBodyPropertiesCfg._usd_applied_schema is None + assert PhysxDeformableCollisionPropertiesCfg._usd_namespace == "physxCollision" + assert PhysxDeformableCollisionPropertiesCfg._usd_applied_schema == "PhysxCollisionAPI" + assert {"deformable_body_enabled", "kinematic_enabled", "mass"}.issubset(_field_names(type(props))) + assert {"density", "static_friction", "dynamic_friction", "youngs_modulus", "poissons_ratio"}.issubset( + _field_names(type(material)) + ) + assert "surface_thickness" in _field_names(type(surface_material)) + assert "bend_damping" in _field_names(type(surface_material)) + assert PhysXDeformableMaterialCfg._usd_namespace == "physxDeformableMaterial" + assert PhysXDeformableMaterialCfg._usd_applied_schema == "PhysxDeformableMaterialAPI" + assert "_usd_applied_schema" not in type(material).__dict__ + assert type(surface_material)._usd_namespace == "physxDeformableMaterial" + assert type(surface_material)._usd_applied_schema == "PhysxSurfaceDeformableMaterialAPI" + + +def test_newton_deformable_cfgs_use_core_schema_and_material_functions(): + """Newton deformable cfgs own Newton fields while schema and material functions stay in core.""" + props = NewtonDeformableBodyPropertiesCfg() + material = NewtonDeformableBodyMaterialCfg() + surface_material = NewtonSurfaceDeformableBodyMaterialCfg() + + assert not hasattr(props, "define_func") + assert not hasattr(props, "modify_func") + assert NewtonDeformableBodyPropertiesCfg._usd_namespace == "newton" + assert NewtonDeformableBodyPropertiesCfg._usd_applied_schema is None + assert str(material.func) == "isaaclab.sim.spawners.materials.physics_materials:spawn_deformable_body_material" + assert str(surface_material.func) == str(material.func) + _assert_no_property_prefix_field(type(props)) + _assert_no_property_prefix_field(type(material)) + _assert_no_property_prefix_field(type(surface_material)) + assert "deformable_body_enabled" not in _field_names(type(props)) + assert "kinematic_enabled" not in _field_names(type(props)) + assert "mass" not in _field_names(type(props)) + assert "youngs_modulus" not in _field_names(type(material)) + assert "poissons_ratio" not in _field_names(type(material)) + assert {"density", "particle_radius", "k_mu", "k_lambda", "k_damp"}.issubset(_field_names(type(material))) + assert NewtonDeformableMaterialCfg._usd_namespace == "newton" + assert NewtonDeformableMaterialCfg._usd_applied_schema is None diff --git a/source/isaaclab/test/sim/test_schemas.py b/source/isaaclab/test/sim/test_schemas.py index 1fb03ede1433..32143a8bb430 100644 --- a/source/isaaclab/test/sim/test_schemas.py +++ b/source/isaaclab/test/sim/test_schemas.py @@ -28,9 +28,6 @@ PhysxJointDrivePropertiesCfg, PhysxRigidBodyPropertiesCfg, ) -from isaaclab_physx.sim.schemas import ( - PhysXCollisionPropertiesCfg as PhysxDeformableCollisionAliasCfg, -) from isaaclab_physx.sim.spawners.materials import PhysxRigidBodyMaterialCfg, RigidBodyMaterialCfg from pxr import UsdPhysics @@ -418,19 +415,6 @@ def test_collision_deprecation_alias(setup_simulation): assert "5.0" in str(deprecations[0].message) -@pytest.mark.isaacsim_ci -def test_physx_capitalx_collision_deprecation_alias(setup_simulation): - """Instantiating the legacy ``PhysXCollisionPropertiesCfg`` (capital X, deformable) - name emits exactly one ``DeprecationWarning`` pointing to - ``PhysxDeformableCollisionPropertiesCfg``.""" - with warnings.catch_warnings(record=True) as caught: - warnings.simplefilter("always") - PhysxDeformableCollisionAliasCfg() - deprecations = [w for w in caught if issubclass(w.category, DeprecationWarning)] - assert len(deprecations) == 1, f"expected exactly one DeprecationWarning, got {len(deprecations)}" - assert "PhysxDeformableCollisionPropertiesCfg" in str(deprecations[0].message) - - @pytest.mark.isaacsim_ci def test_articulation_root_base_cfg_writes_articulation_enabled(setup_simulation): """Setting ``articulation_enabled`` on the base ``ArticulationRootBaseCfg`` must author diff --git a/source/isaaclab/test/sim/test_schemas_shim.py b/source/isaaclab/test/sim/test_schemas_shim.py index 11e6e5d1ba24..9a9c510e09bd 100644 --- a/source/isaaclab/test/sim/test_schemas_shim.py +++ b/source/isaaclab/test/sim/test_schemas_shim.py @@ -29,8 +29,9 @@ "PhysxJointDrivePropertiesCfg", "CollisionPropertiesCfg", "PhysxCollisionPropertiesCfg", + "DeformableBodyPropertiesCfg", + "PhysxDeformableBodyPropertiesCfg", "PhysxDeformableCollisionPropertiesCfg", - "PhysXCollisionPropertiesCfg", "ArticulationRootPropertiesCfg", "PhysxArticulationRootPropertiesCfg", "MeshCollisionPropertiesCfg", @@ -54,7 +55,7 @@ "RigidBodyPropertiesCfg", "JointDrivePropertiesCfg", "CollisionPropertiesCfg", - "PhysXCollisionPropertiesCfg", + "DeformableBodyPropertiesCfg", "ArticulationRootPropertiesCfg", "MeshCollisionPropertiesCfg", "ConvexHullPropertiesCfg", @@ -67,8 +68,18 @@ ] FORWARDED_MATERIAL_NAMES = [ + "DeformableBodyMaterialCfg", "RigidBodyMaterialCfg", + "SurfaceDeformableBodyMaterialCfg", "PhysxRigidBodyMaterialCfg", + "PhysxDeformableBodyMaterialCfg", + "PhysxSurfaceDeformableBodyMaterialCfg", +] + +DEPRECATED_FORWARDED_MATERIAL_NAMES = [ + "DeformableBodyMaterialCfg", + "RigidBodyMaterialCfg", + "SurfaceDeformableBodyMaterialCfg", ] @@ -132,13 +143,14 @@ def test_deprecated_aliases_emit_deprecation_warning(name): assert len(deprecations) == 1, f"{name}: expected one DeprecationWarning, got {len(deprecations)}" -def test_deprecated_material_alias_emits_deprecation_warning(): - """Instantiating ``RigidBodyMaterialCfg`` via the shim still emits ``DeprecationWarning``.""" +@pytest.mark.parametrize("name", DEPRECATED_FORWARDED_MATERIAL_NAMES) +def test_deprecated_material_aliases_emit_deprecation_warning(name): + """Instantiating a deprecated material alias via the shim still emits ``DeprecationWarning``.""" with warnings.catch_warnings(record=True) as caught: warnings.simplefilter("always") - materials.RigidBodyMaterialCfg() + getattr(materials, name)() deprecations = [w for w in caught if issubclass(w.category, DeprecationWarning)] - assert len(deprecations) == 1 + assert len(deprecations) == 1, f"{name}: expected one DeprecationWarning, got {len(deprecations)}" assert "5.0" in str(deprecations[0].message) @@ -158,6 +170,13 @@ def test_new_material_class_does_not_emit_deprecation_warning(): assert not any(issubclass(w.category, DeprecationWarning) for w in caught) +def test_deformable_component_cfg_is_not_forwarded_from_core(): + """Component deformable cfgs are backend-owned and not forwarded from ``isaaclab``.""" + assert not hasattr(schemas, "PhysXDeformableBodyPropertiesCfg") + assert not hasattr(sim_utils, "PhysXDeformableBodyPropertiesCfg") + assert not hasattr(schemas_cfg_submodule, "PhysXDeformableBodyPropertiesCfg") + + def test_dir_lists_forwarded_names(): """``dir(isaaclab.sim.schemas)`` includes the forwarded names so IDE autocomplete works.""" listing = dir(schemas) diff --git a/source/isaaclab/test/sim/test_spawn_meshes.py b/source/isaaclab/test/sim/test_spawn_meshes.py index 7b4c4678ca54..97aca4a23f78 100644 --- a/source/isaaclab/test/sim/test_spawn_meshes.py +++ b/source/isaaclab/test/sim/test_spawn_meshes.py @@ -120,18 +120,19 @@ def test_spawn_sphere(sim): @pytest.mark.parametrize("resolution", [(1, 1), (3, 2)]) -def test_spawn_square(sim, resolution): - """Test spawning of UsdGeomMesh as a square prim.""" - # Spawn square - cfg = sim_utils.MeshSquareCfg(size=1.0, resolution=resolution) - prim = cfg.func("/World/Square", cfg) +@pytest.mark.parametrize("size", [(1.0, 1.0), (1.5, 0.8)]) +def test_spawn_rectangle(sim, resolution, size): + """Test spawning of UsdGeomMesh as a rectangle prim.""" + # Spawn rectangle + cfg = sim_utils.MeshRectangleCfg(size=size, resolution=resolution) + prim = cfg.func("/World/Rectangle", cfg) # Check validity assert prim.IsValid() - assert sim.stage.GetPrimAtPath("/World/Square").IsValid() + assert sim.stage.GetPrimAtPath("/World/Rectangle").IsValid() assert prim.GetPrimTypeInfo().GetTypeName() == "Xform" # Check properties - prim = sim.stage.GetPrimAtPath("/World/Square/geometry/mesh") + prim = sim.stage.GetPrimAtPath("/World/Rectangle/geometry/mesh") assert prim.GetPrimTypeInfo().GetTypeName() == "Mesh" diff --git a/source/isaaclab_contrib/changelog.d/mym-deformable-backend-split.skip b/source/isaaclab_contrib/changelog.d/mym-deformable-backend-split.skip new file mode 100644 index 000000000000..37d59e91ab7b --- /dev/null +++ b/source/isaaclab_contrib/changelog.d/mym-deformable-backend-split.skip @@ -0,0 +1 @@ +Test-only updates for backend-specific deformable cfg imports. diff --git a/source/isaaclab_contrib/changelog.d/mym-deformable_experimental.minor.rst b/source/isaaclab_contrib/changelog.d/mym-deformable_experimental.minor.rst new file mode 100644 index 000000000000..4167f0a33328 --- /dev/null +++ b/source/isaaclab_contrib/changelog.d/mym-deformable_experimental.minor.rst @@ -0,0 +1,16 @@ +Added +^^^^^ + +* Added :mod:`isaaclab_contrib.deformable` with contributed Newton deformable + asset and VBD solver support, including + :class:`~isaaclab_contrib.deformable.DeformableObject`, + :class:`~isaaclab_contrib.deformable.VBDSolverCfg`, + :class:`~isaaclab_contrib.deformable.CoupledMJWarpVBDSolverCfg`, and + :class:`~isaaclab_contrib.deformable.CoupledFeatherstoneVBDSolverCfg` for + one- and two-way rigid-deformable coupling. +* Added :class:`~isaaclab_contrib.deformable.NewtonModelCfg` for shared Newton + deformable contact parameters. +* Added Newton deformable coupling documentation with Franka soft-body lift + tuning guidance for + :class:`~isaaclab_contrib.deformable.CoupledMJWarpVBDSolverCfg` and + :class:`~isaaclab_contrib.deformable.NewtonModelCfg`. diff --git a/source/isaaclab_contrib/isaaclab_contrib/deformable/__init__.py b/source/isaaclab_contrib/isaaclab_contrib/deformable/__init__.py new file mode 100644 index 000000000000..02c5059784e6 --- /dev/null +++ b/source/isaaclab_contrib/isaaclab_contrib/deformable/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Sub-package for externally contributed assets. + +This package contains contributed code that depends on Isaac Lab's public API but is not required for core functionality. This includes implementations of Newton solvers for deformables. +""" + +from isaaclab.utils.module import lazy_export + +lazy_export() diff --git a/source/isaaclab_contrib/isaaclab_contrib/deformable/__init__.pyi b/source/isaaclab_contrib/isaaclab_contrib/deformable/__init__.pyi new file mode 100644 index 000000000000..b2438a247a44 --- /dev/null +++ b/source/isaaclab_contrib/isaaclab_contrib/deformable/__init__.pyi @@ -0,0 +1,22 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +__all__ = [ + "CoupledFeatherstoneVBDSolverCfg", + "CoupledMJWarpVBDSolverCfg", + "DeformableObject", + "DeformableObjectData", + "NewtonModelCfg", + "VBDSolverCfg", +] + +from .deformable_object import DeformableObject +from .deformable_object_data import DeformableObjectData +from .newton_manager_cfg import ( + CoupledFeatherstoneVBDSolverCfg, + CoupledMJWarpVBDSolverCfg, + NewtonModelCfg, + VBDSolverCfg, +) diff --git a/source/isaaclab_contrib/isaaclab_contrib/deformable/coupled_featherstone_vbd_manager.py b/source/isaaclab_contrib/isaaclab_contrib/deformable/coupled_featherstone_vbd_manager.py new file mode 100644 index 000000000000..312d9fc69f10 --- /dev/null +++ b/source/isaaclab_contrib/isaaclab_contrib/deformable/coupled_featherstone_vbd_manager.py @@ -0,0 +1,509 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Coupled Featherstone + VBD Newton manager.""" + +from __future__ import annotations + +import inspect +import logging +from typing import TYPE_CHECKING + +import warp as wp +from isaaclab_newton.physics.newton_manager import NewtonManager +from newton import Contacts, Control, Model, ModelBuilder, State +from newton._src.usd.schemas import SchemaResolverNewton, SchemaResolverPhysx +from newton.solvers import SolverBase, SolverFeatherstone, SolverVBD + +from isaaclab.sim.utils.stage import get_current_stage + +from .deformable_object import ( + add_deformable_entry_to_builder, + clear_deformable_builder_hooks, + install_deformable_builder_hooks, +) +from .kernels import _kernel_body_particle_reaction +from .newton_manager_cfg import CoupledFeatherstoneVBDSolverCfg + +if TYPE_CHECKING: + from isaaclab.sim.simulation_context import SimulationContext + +logger = logging.getLogger(__name__) + + +class NewtonCoupledFeatherstoneVBDManager(NewtonManager): + """:class:`NewtonManager` specialization for the coupled Featherstone + VBD + solver. Due to Newton deformables not being properly integrated yet, this + manager uses the same temporary solutions from VBD Manager. + + Always uses Newton's :class:`CollisionPipeline` for contact handling. + """ + + _rigid_solver: SolverFeatherstone + _soft_solver: SolverVBD + _coupling_mode: str | None = None + + @classmethod + def initialize(cls, sim_context: SimulationContext) -> None: + """Initialize the manager with simulation context. + + Args: + sim_context: Parent simulation context. + + TODO: Subclass should not override this method, once deformables + supported on Newton import_usd, this can be unified with NewtonManager's + implementation. + """ + + # Deformable body registry and extension hooks. + # Experimental deformable support registers callbacks here so the manager + # and cloner can invoke them without hard-coding deformable logic. + install_deformable_builder_hooks() + + super().initialize(sim_context) + + @classmethod + def step(cls) -> None: + """Step the physics simulation.""" + from isaaclab.physics import PhysicsManager + + sim = PhysicsManager._sim + if sim is None or not sim.is_playing(): + return + + # Notify solver of model changes + if cls._model_changes: + with wp.ScopedDevice(PhysicsManager._device): + for change in cls._model_changes: + cls._rigid_solver.notify_model_changed(change) + cls._soft_solver.notify_model_changed(change) + NewtonManager._model_changes = set() + super().step() + + @classmethod + def _solver_specific_clear(cls): + """Clear VBD-specific state.""" + clear_deformable_builder_hooks() + + @classmethod + def _get_deformable_ignore_paths(cls) -> list[str]: + """Return USD prim paths to skip when calling ``builder.add_usd``. + + For each registered deformable body, both the simulation mesh (which + carries ``UsdPhysics.CollisionAPI``) and the visual mesh are returned. + The sim mesh must be skipped so Newton does not create a redundant + static mesh collider alongside the particles produced by + ``add_soft_mesh``. The visual mesh is skipped so Newton does not + treat it as a collider — Kit reads it directly from USD for rendering. + + Paths may contain regex patterns; Newton's ``add_usd`` matches them + via :func:`re.match`. + """ + paths: list[str] = [] + for entry in cls._deformable_registry: + paths.append(entry.sim_mesh_prim_path) + paths.append(entry.vis_mesh_prim_path) + return paths + + @classmethod + def start_simulation(cls) -> None: + """Start simulation by finalizing model and initializing state. + + This function finalizes the model and initializes the simulation state. + Note: Collision pipeline is initialized later in initialize_solver() after + we determine whether the solver needs external collision detection. + + TODO: Subclass should not override this method, missing piece is + having Newton bind a surface mesh to volume deformable tetrahedral mesh + in addition to removing the deformable_registry data structure. + """ + super().start_simulation() + + # Apply global model parameters from :class:`NewtonModelCfg` to the finalized model. + # Sets ``soft_contact_ke/kd/mu`` and optionally overrides per-shape + # ``shape_material_ke/kd/mu`` on the Newton model. + from isaaclab.physics import PhysicsManager + + cfg = PhysicsManager._cfg + if cfg is not None and hasattr(cfg, "model_cfg") and cfg.model_cfg is not None: + model = cls._model + if model is None: + return + + model_cfg = cfg.model_cfg + model.soft_contact_ke = float(model_cfg.soft_contact_ke) + model.soft_contact_kd = float(model_cfg.soft_contact_kd) + model.soft_contact_mu = float(model_cfg.soft_contact_mu) + + if model_cfg.shape_material_ke is not None: + model.shape_material_ke.fill_(float(model_cfg.shape_material_ke)) + if model_cfg.shape_material_kd is not None: + model.shape_material_kd.fill_(float(model_cfg.shape_material_kd)) + if model_cfg.shape_material_mu is not None: + model.shape_material_mu.fill_(float(model_cfg.shape_material_mu)) + + # Setup USD/Fabric sync for Kit viewport deformable rendering + if not cls._clone_physics_only and cls._deformable_registry: + import re + + import usdrt + + if NewtonManager._usdrt_stage is None: + NewtonManager._usdrt_stage = get_current_stage(fabric=True) + + stage = get_current_stage() + for entry in cls._deformable_registry: + for inst_idx, offset in enumerate(entry.particle_offsets): + # Resolve regex pattern to concrete instance path of visual mesh + resolved_vis = re.sub(r"(?<=[Ee]nv_)\.\*", str(inst_idx), entry.vis_mesh_prim_path) + resolved_vis = re.sub(r"\.\*", str(inst_idx), resolved_vis) + vis_prim = stage.GetPrimAtPath(resolved_vis) + + if not vis_prim or not vis_prim.IsValid(): + logger.warning("[setup_fabric_particle_sync] vis prim not found at %s", resolved_vis) + continue + + # Create per-instance particle offset and count attributes on the visual mesh + # prim so the Fabric sync kernel can find the right slice of particle_q + # and iterate only over this body's particles (counts vary across bodies). + fab_prim = NewtonManager._usdrt_stage.GetPrimAtPath(vis_prim.GetPath().pathString) + fab_prim.CreateAttribute( + NewtonManager._newton_particle_offset_attr, usdrt.Sdf.ValueTypeNames.UInt, True + ) + fab_prim.GetAttribute(NewtonManager._newton_particle_offset_attr).Set(offset) + fab_prim.CreateAttribute( + NewtonManager._newton_particle_count_attr, usdrt.Sdf.ValueTypeNames.UInt, True + ) + fab_prim.GetAttribute(NewtonManager._newton_particle_count_attr).Set(entry.particles_per_body) + + cls._mark_particles_dirty() + cls.sync_particles_to_usd() + + @classmethod + def instantiate_builder_from_stage(cls): + """Create builder from USD stage with special treatment for deformable + bodies, as these are not read from USD yet. + + Detects env Xforms (e.g. ``/World/Env_0``, ``/World/Env_1``) and builds + each as a separate Newton world via ``begin_world``/``end_world``. + Falls back to a flat ``add_usd`` when no env Xforms are found. + + TODO: Subclass should not override this method, once deformables + supported on Newton import_usd, this can be unified with NewtonManager's + implementation. + """ + import re + + from pxr import UsdGeom + + stage = get_current_stage() + up_axis = UsdGeom.GetStageUpAxis(stage) + + # Scan /World children for env-like Xforms (Env_0, env_1, ...) + env_pattern = re.compile(r"^[Ee]nv_(\d+)$") + world_prim = stage.GetPrimAtPath("/World") + env_paths: list[tuple[int, str]] = [] + if world_prim and world_prim.IsValid(): + for child in world_prim.GetChildren(): + m = env_pattern.match(child.GetName()) + if m: + env_paths.append((int(m.group(1)), child.GetPath().pathString)) + env_paths.sort(key=lambda x: x[0]) + + builder = ModelBuilder(up_axis=up_axis) + + schema_resolvers = [SchemaResolverNewton(), SchemaResolverPhysx()] + + # Deformable sim/visual mesh paths must be skipped by ``add_usd`` + # so they don't get duplicated as static colliders. + deformable_ignore_paths = cls._get_deformable_ignore_paths() + + if not env_paths: + # No env Xforms — flat loading + builder.add_usd(stage, ignore_paths=deformable_ignore_paths, schema_resolvers=schema_resolvers) + + # Add deformable bodies from the registry (single world at origin). + for entry in cls._deformable_registry: + add_deformable_entry_to_builder(builder, entry, 0, [0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0]) + else: + # Load everything except the env subtrees (ground plane, lights, etc.) + ignore_paths = [path for _, path in env_paths] + deformable_ignore_paths + builder.add_usd(stage, ignore_paths=ignore_paths, schema_resolvers=schema_resolvers) + + # Build a prototype from the first env (all envs assumed identical) + _, proto_path = env_paths[0] + proto = ModelBuilder(up_axis=up_axis) + proto.add_usd( + stage, + root_path=proto_path, + ignore_paths=deformable_ignore_paths, + schema_resolvers=schema_resolvers, + ) + + # Inject registered sites into the proto before replication + global_sites, proto_sites = cls._cl_inject_sites(builder, {proto_path: proto}) + global_site_map: dict[str, tuple[int, None]] = {label: (idx, None) for label, idx in global_sites.items()} + num_worlds = len(env_paths) + local_site_map: dict[str, list[list[int]]] = {} + site_entries = proto_sites.get(id(proto), {}) + + # Add each env as a separate Newton world + xform_cache = UsdGeom.XformCache() + for col, (_, env_path) in enumerate(env_paths): + builder.begin_world() + offset = builder.shape_count + world_xform = xform_cache.GetLocalToWorldTransform(stage.GetPrimAtPath(env_path)) + translation = world_xform.ExtractTranslation() + rotation = world_xform.ExtractRotationQuat() + pos = (translation[0], translation[1], translation[2]) + quat = ( + rotation.GetImaginary()[0], + rotation.GetImaginary()[1], + rotation.GetImaginary()[2], + rotation.GetReal(), + ) + builder.add_builder(proto, xform=wp.transform(pos, quat)) + for label, proto_shape_indices in site_entries.items(): + if label not in local_site_map: + local_site_map[label] = [[] for _ in range(num_worlds)] + for proto_shape_idx in proto_shape_indices: + local_site_map[label][col].append(offset + proto_shape_idx) + + # Add deformable bodies from the registry into this world. + for entry in cls._deformable_registry: + add_deformable_entry_to_builder(builder, entry, col, list(pos), quat) + + builder.end_world() + + NewtonManager._cl_site_index_map = { + **global_site_map, + **{label: (None, per_world) for label, per_world in local_site_map.items()}, + } + NewtonManager._num_envs = len(env_paths) + + # Call builder.color() if any deformable entries were added (required by VBD solver) + if cls._deformable_registry: + builder.color() + + cls.set_builder(builder) + + @classmethod + def _build_solver(cls, model: Model, solver_cfg: CoupledFeatherstoneVBDSolverCfg) -> None: + """Construct a custom coupling between two solvers and populate the + base-class slots. + + VBD always uses Newton's :class:`CollisionPipeline` and steps with + separate input/output states, so the flags are fixed. + """ + cls._coupling_mode = solver_cfg.coupling_mode + + valid = set(inspect.signature(SolverFeatherstone.__init__).parameters) - {"self", "model"} + kwargs = {k: v for k, v in solver_cfg.rigid_solver_cfg.to_dict().items() if k in valid} + cls._rigid_solver = SolverFeatherstone(model, **kwargs) + + valid = set(inspect.signature(SolverVBD.__init__).parameters) - {"self", "model"} + kwargs = {k: v for k, v in solver_cfg.soft_solver_cfg.to_dict().items() if k in valid} + cls._soft_solver = SolverVBD(model, **kwargs) + + # Dummy solver for the newtonmanager + NewtonManager._solver = SolverBase(model) + + NewtonManager._use_single_state = False + NewtonManager._needs_collision_pipeline = True + + if solver_cfg.coupling_mode == "kinematic": + cls._gravity_zero = wp.zeros(1, dtype=wp.vec3) + cls._gravity_saved = wp.clone(model.gravity) + # Save original PD gains and create zeroed versions for kinematic step + cls._ke_saved = wp.clone(model.joint_target_ke) + cls._kd_saved = wp.clone(model.joint_target_kd) + cls._ke_zero = wp.zeros_like(model.joint_target_ke) + cls._kd_zero = wp.zeros_like(model.joint_target_kd) + + @classmethod + def _step_solver( + cls, state_in: State, state_out: State, control: Control, contacts: Contacts | None, substep_dt: float + ) -> None: + """One coupled substep. + + Args: + state_in: Current state (read/write). + state_out: Next state (write). + control: Joint-level control inputs. + contacts: Ignored -- the solver uses its own internal contacts. + dt: Substep timestep [s]. + """ + if cls._coupling_mode == "kinematic": + cls._step_kinematic(state_in, state_out, control, substep_dt) + elif cls._coupling_mode == "one_way": + cls._step_one_way(state_in, state_out, control, substep_dt) + else: + cls._step_two_way(state_in, state_out, control, substep_dt) + + @classmethod + def _simulate_physics_only(cls) -> None: + # Rebuild BVH once per step for solvers that require it (e.g. VBD cloth). + if hasattr(cls._soft_solver, "rebuild_bvh"): + cls._soft_solver.rebuild_bvh(cls._state_0) + super()._simulate_physics_only() + + @classmethod + def _step_kinematic(cls, state_in: State, state_out: State, control: Control, dt: float) -> None: + """Kinematic coupling: mirrors some Newton examples (e.g. softbody_franka) exactly. + + 1. Clear forces. + 2. Assign joint_qd from control targets (velocity = (target - current) / frame_dt). + 3. Disable gravity and rigid contacts for the rigid solver step. + 4. Step rigid solver as kinematic integrator (q += qd * dt). + 5. Restore gravity, collision detect, VBD step. + """ + model = cls._model + + # 1. Clear forces + state_in.clear_forces() + state_out.clear_forces() + + # 2. Kinematic rigid step: assign qd, disable gravity/contacts/PD gains + saved_particle_count = model.particle_count + saved_shape_contact_pair_count = model.shape_contact_pair_count + model.particle_count = 0 + model.gravity.assign(cls._gravity_zero) + model.shape_contact_pair_count = 0 + + # Zero out PD gains so rigid solver (Featherstone) acts as a pure kinematic integrator + model.joint_target_ke.assign(cls._ke_zero) + model.joint_target_kd.assign(cls._kd_zero) + + # Assign joint velocities from control targets + state_in.joint_qd.assign(control.joint_target_vel) + + cls._rigid_solver.step(state_in, state_out, control, None, dt) + + # 3. Restore everything + state_in.particle_f.zero_() + model.particle_count = saved_particle_count + model.gravity.assign(cls._gravity_saved) + model.shape_contact_pair_count = saved_shape_contact_pair_count + model.joint_target_ke.assign(cls._ke_saved) + model.joint_target_kd.assign(cls._kd_saved) + + # 4. Collision detection + cls._collision_pipeline.collide(state_in, cls._contacts) + + # 5. VBD step + cls._soft_solver.step(state_in, state_out, control, cls._contacts, dt) + + @classmethod + def _step_one_way(cls, state_in: State, state_out: State, control: Control, dt: float) -> None: + """One-way coupling: collide, then rigid step, then VBD.""" + # 1. Clear forces + state_in.clear_forces() + state_out.clear_forces() + + # 2. Collision detection (cloth-body contacts) + cls._collision_pipeline.collide(state_in, cls._contacts) + + # 3. Rigid-body step (does not read soft-contact reactions) + cls._rigid_step(state_in, state_out, control, dt) + + # 4. Clear spurious particle forces from rigid step + state_in.particle_f.zero_() + + # 5. VBD step -- particles only, reads updated rigid poses + cls._soft_solver.step(state_in, state_out, control, cls._contacts, dt) + + @classmethod + def _step_two_way(cls, state_in: State, state_out: State, control: Control, dt: float) -> None: + """Two-way coupling: collide, inject reactions into body_f, rigid step, VBD step.""" + # 1. Clear forces + state_in.clear_forces() + state_out.clear_forces() + + # 2. Collision detection BEFORE rigid step + cls._collision_pipeline.collide(state_in, cls._contacts) + + # 3. Inject contact reaction forces into body_f. + # state_out holds the previous substep's body_q (states swap each + # substep), used for finite-difference body velocity in friction. + # particle_q_prev is reconstructed from particle_qd inside the + # kernel because VBD mutates particle_q in place, so the swapped + # state's particle_q is not a clean prior-substep snapshot. + if state_in.body_f is not None: + cls._apply_reactions(state_in, state_out, dt) + + # 4. Rigid-body step (reads body_f for soft-contact reactions) + cls._rigid_step(state_in, state_out, control, dt) + + # 5. Clear spurious particle forces from rigid step + state_in.particle_f.zero_() + + # 6. VBD step -- uses same contacts detected in step 2 + cls._soft_solver.step(state_in, state_out, control, cls._contacts, dt) + + @classmethod + def _rigid_step(cls, state_in: State, state_out: State, control: Control, dt: float) -> None: + """Advance rigid bodies with the configured sub-solver.""" + model = cls._model + + # set particle_count = 0 to disable particle simulation in robot solver + saved_particle_count = model.particle_count + model.particle_count = 0 + + cls._rigid_solver.step(state_in, state_out, control, None, dt) + + # restore original settings + model.particle_count = saved_particle_count + + @classmethod + def _apply_reactions(cls, state: State, state_prev: State, dt: float) -> None: + """Launch the reaction kernel to inject normal + friction forces into body_f. + + Args: + state: Current state with particle positions/velocities and body state. + state_prev: Previous substep state whose ``body_q`` provides + the reference poses for finite-difference body velocity. + dt: Substep timestep [s]. + """ + model = cls._model + contacts = cls._contacts + + if contacts is None: + return + + contact_capacity = int(contacts.soft_contact_particle.shape[0]) + if contact_capacity == 0: + return + + # The kernel reconstructs particle_q_prev from particle_qd internally: + # state_prev.particle_q is unreliable because VBD mutates particle_q + # in place during its iteration, so the swapped state's particle_q is + # not a clean snapshot of the prior substep. + wp.launch( + _kernel_body_particle_reaction, + dim=contact_capacity, + inputs=[ + contacts.soft_contact_count, + contacts.soft_contact_particle, + contacts.soft_contact_shape, + contacts.soft_contact_body_pos, + contacts.soft_contact_body_vel, + contacts.soft_contact_normal, + state.particle_q, + state.particle_qd, + model.particle_radius, + state.body_q, + state_prev.body_q, + state.body_qd, + model.body_com, + model.shape_body, + model.shape_material_mu, + float(model.soft_contact_ke), + float(model.soft_contact_kd), + float(model.soft_contact_mu), + float(cls._soft_solver.friction_epsilon), + float(dt), + state.body_f, + ], + ) diff --git a/source/isaaclab_contrib/isaaclab_contrib/deformable/coupled_mjwarp_vbd_manager.py b/source/isaaclab_contrib/isaaclab_contrib/deformable/coupled_mjwarp_vbd_manager.py new file mode 100644 index 000000000000..5097284bafc7 --- /dev/null +++ b/source/isaaclab_contrib/isaaclab_contrib/deformable/coupled_mjwarp_vbd_manager.py @@ -0,0 +1,452 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Coupled MJWarp + VBD Newton manager.""" + +from __future__ import annotations + +import inspect +import logging +from typing import TYPE_CHECKING + +import warp as wp +from isaaclab_newton.physics.newton_manager import NewtonManager +from newton import Contacts, Control, Model, ModelBuilder, State +from newton._src.usd.schemas import SchemaResolverNewton, SchemaResolverPhysx +from newton.solvers import SolverBase, SolverMuJoCo, SolverVBD + +from isaaclab.sim.utils.stage import get_current_stage + +from .deformable_object import ( + add_deformable_entry_to_builder, + clear_deformable_builder_hooks, + install_deformable_builder_hooks, +) +from .kernels import _kernel_body_particle_reaction +from .newton_manager_cfg import CoupledMJWarpVBDSolverCfg + +if TYPE_CHECKING: + from isaaclab.sim.simulation_context import SimulationContext + +logger = logging.getLogger(__name__) + + +class NewtonCoupledMJWarpVBDManager(NewtonManager): + """:class:`NewtonManager` specialization for the coupled MJWarp + VBD + solver. Due to Newton deformables not being properly integrated yet, this + manager uses the same temporary solutions from VBD Manager. + + Always uses Newton's :class:`CollisionPipeline` for contact handling. + """ + + _rigid_solver: SolverMuJoCo + _soft_solver: SolverVBD + _coupling_mode: str | None = None + + @classmethod + def initialize(cls, sim_context: SimulationContext) -> None: + """Initialize the manager with simulation context. + + Args: + sim_context: Parent simulation context. + + TODO: Subclass should not override this method, once deformables + supported on Newton import_usd, this can be unified with NewtonManager's + implementation. + """ + + # Deformable body registry and extension hooks. + # Experimental deformable support registers callbacks here so the manager + # and cloner can invoke them without hard-coding deformable logic. + install_deformable_builder_hooks() + + super().initialize(sim_context) + + @classmethod + def step(cls) -> None: + """Step the physics simulation.""" + from isaaclab.physics import PhysicsManager + + sim = PhysicsManager._sim + if sim is None or not sim.is_playing(): + return + + # Notify solver of model changes + if cls._model_changes: + with wp.ScopedDevice(PhysicsManager._device): + for change in cls._model_changes: + cls._rigid_solver.notify_model_changed(change) + cls._soft_solver.notify_model_changed(change) + NewtonManager._model_changes = set() + super().step() + + @classmethod + def _solver_specific_clear(cls): + """Clear VBD-specific state.""" + clear_deformable_builder_hooks() + + @classmethod + def _get_deformable_ignore_paths(cls) -> list[str]: + """Return USD prim paths to skip when calling ``builder.add_usd``. + + For each registered deformable body, both the simulation mesh (which + carries ``UsdPhysics.CollisionAPI``) and the visual mesh are returned. + The sim mesh must be skipped so Newton does not create a redundant + static mesh collider alongside the particles produced by + ``add_soft_mesh``. The visual mesh is skipped so Newton does not + treat it as a collider — Kit reads it directly from USD for rendering. + + Paths may contain regex patterns; Newton's ``add_usd`` matches them + via :func:`re.match`. + """ + paths: list[str] = [] + for entry in cls._deformable_registry: + paths.append(entry.sim_mesh_prim_path) + paths.append(entry.vis_mesh_prim_path) + return paths + + @classmethod + def start_simulation(cls) -> None: + """Start simulation by finalizing model and initializing state. + + This function finalizes the model and initializes the simulation state. + Note: Collision pipeline is initialized later in initialize_solver() after + we determine whether the solver needs external collision detection. + + TODO: Subclass should not override this method, missing piece is + having Newton bind a surface mesh to volume deformable tetrahedral mesh + in addition to removing the deformable_registry data structure. + """ + super().start_simulation() + + # Apply global model parameters from :class:`NewtonModelCfg` to the finalized model. + # Sets ``soft_contact_ke/kd/mu`` and optionally overrides per-shape + # ``shape_material_ke/kd/mu`` on the Newton model. + from isaaclab.physics import PhysicsManager + + cfg = PhysicsManager._cfg + if cfg is not None and hasattr(cfg, "model_cfg") and cfg.model_cfg is not None: + model = cls._model + if model is None: + return + + model_cfg = cfg.model_cfg + model.soft_contact_ke = float(model_cfg.soft_contact_ke) + model.soft_contact_kd = float(model_cfg.soft_contact_kd) + model.soft_contact_mu = float(model_cfg.soft_contact_mu) + + if model_cfg.shape_material_ke is not None: + model.shape_material_ke.fill_(float(model_cfg.shape_material_ke)) + if model_cfg.shape_material_kd is not None: + model.shape_material_kd.fill_(float(model_cfg.shape_material_kd)) + if model_cfg.shape_material_mu is not None: + model.shape_material_mu.fill_(float(model_cfg.shape_material_mu)) + + # Setup USD/Fabric sync for Kit viewport deformable rendering + if not cls._clone_physics_only and cls._deformable_registry: + import re + + import usdrt + + if NewtonManager._usdrt_stage is None: + NewtonManager._usdrt_stage = get_current_stage(fabric=True) + + stage = get_current_stage() + for entry in cls._deformable_registry: + for inst_idx, offset in enumerate(entry.particle_offsets): + # Resolve regex pattern to concrete instance path of visual mesh + resolved_vis = re.sub(r"(?<=[Ee]nv_)\.\*", str(inst_idx), entry.vis_mesh_prim_path) + resolved_vis = re.sub(r"\.\*", str(inst_idx), resolved_vis) + vis_prim = stage.GetPrimAtPath(resolved_vis) + + if not vis_prim or not vis_prim.IsValid(): + logger.warning("[setup_fabric_particle_sync] vis prim not found at %s", resolved_vis) + continue + + # Create per-instance particle offset and count attributes on the visual mesh + # prim so the Fabric sync kernel can find the right slice of particle_q + # and iterate only over this body's particles (counts vary across bodies). + fab_prim = NewtonManager._usdrt_stage.GetPrimAtPath(vis_prim.GetPath().pathString) + fab_prim.CreateAttribute( + NewtonManager._newton_particle_offset_attr, usdrt.Sdf.ValueTypeNames.UInt, True + ) + fab_prim.GetAttribute(NewtonManager._newton_particle_offset_attr).Set(offset) + fab_prim.CreateAttribute( + NewtonManager._newton_particle_count_attr, usdrt.Sdf.ValueTypeNames.UInt, True + ) + fab_prim.GetAttribute(NewtonManager._newton_particle_count_attr).Set(entry.particles_per_body) + + cls._mark_particles_dirty() + cls.sync_particles_to_usd() + + @classmethod + def instantiate_builder_from_stage(cls): + """Create builder from USD stage with special treatment for deformable + bodies, as these are not read from USD yet. + + Detects env Xforms (e.g. ``/World/Env_0``, ``/World/Env_1``) and builds + each as a separate Newton world via ``begin_world``/``end_world``. + Falls back to a flat ``add_usd`` when no env Xforms are found. + + TODO: Subclass should not override this method, once deformables + supported on Newton import_usd, this can be unified with NewtonManager's + implementation. + """ + import re + + from pxr import UsdGeom + + stage = get_current_stage() + up_axis = UsdGeom.GetStageUpAxis(stage) + + # Scan /World children for env-like Xforms (Env_0, env_1, ...) + env_pattern = re.compile(r"^[Ee]nv_(\d+)$") + world_prim = stage.GetPrimAtPath("/World") + env_paths: list[tuple[int, str]] = [] + if world_prim and world_prim.IsValid(): + for child in world_prim.GetChildren(): + m = env_pattern.match(child.GetName()) + if m: + env_paths.append((int(m.group(1)), child.GetPath().pathString)) + env_paths.sort(key=lambda x: x[0]) + + builder = ModelBuilder(up_axis=up_axis) + + schema_resolvers = [SchemaResolverNewton(), SchemaResolverPhysx()] + + # Deformable sim/visual mesh paths must be skipped by ``add_usd`` + # so they don't get duplicated as static colliders. + deformable_ignore_paths = cls._get_deformable_ignore_paths() + + if not env_paths: + # No env Xforms — flat loading + builder.add_usd(stage, ignore_paths=deformable_ignore_paths, schema_resolvers=schema_resolvers) + + # Add deformable bodies from the registry (single world at origin). + for entry in cls._deformable_registry: + add_deformable_entry_to_builder(builder, entry, 0, [0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0]) + else: + # Load everything except the env subtrees (ground plane, lights, etc.) + ignore_paths = [path for _, path in env_paths] + deformable_ignore_paths + builder.add_usd(stage, ignore_paths=ignore_paths, schema_resolvers=schema_resolvers) + + # Build a prototype from the first env (all envs assumed identical) + _, proto_path = env_paths[0] + proto = ModelBuilder(up_axis=up_axis) + proto.add_usd( + stage, + root_path=proto_path, + ignore_paths=deformable_ignore_paths, + schema_resolvers=schema_resolvers, + ) + + # Inject registered sites into the proto before replication + global_sites, proto_sites = cls._cl_inject_sites(builder, {proto_path: proto}) + global_site_map: dict[str, tuple[int, None]] = {label: (idx, None) for label, idx in global_sites.items()} + num_worlds = len(env_paths) + local_site_map: dict[str, list[list[int]]] = {} + site_entries = proto_sites.get(id(proto), {}) + + # Add each env as a separate Newton world + xform_cache = UsdGeom.XformCache() + for col, (_, env_path) in enumerate(env_paths): + builder.begin_world() + offset = builder.shape_count + world_xform = xform_cache.GetLocalToWorldTransform(stage.GetPrimAtPath(env_path)) + translation = world_xform.ExtractTranslation() + rotation = world_xform.ExtractRotationQuat() + pos = (translation[0], translation[1], translation[2]) + quat = ( + rotation.GetImaginary()[0], + rotation.GetImaginary()[1], + rotation.GetImaginary()[2], + rotation.GetReal(), + ) + builder.add_builder(proto, xform=wp.transform(pos, quat)) + for label, proto_shape_indices in site_entries.items(): + if label not in local_site_map: + local_site_map[label] = [[] for _ in range(num_worlds)] + for proto_shape_idx in proto_shape_indices: + local_site_map[label][col].append(offset + proto_shape_idx) + + # Add deformable bodies from the registry into this world. + for entry in cls._deformable_registry: + add_deformable_entry_to_builder(builder, entry, col, list(pos), quat) + + builder.end_world() + + NewtonManager._cl_site_index_map = { + **global_site_map, + **{label: (None, per_world) for label, per_world in local_site_map.items()}, + } + NewtonManager._num_envs = len(env_paths) + + # Call builder.color() if any deformable entries were added (required by VBD solver) + if cls._deformable_registry: + builder.color() + + cls.set_builder(builder) + + @classmethod + def _build_solver(cls, model: Model, solver_cfg: CoupledMJWarpVBDSolverCfg) -> None: + """Construct a custom coupling between two solvers and populate the + base-class slots. + + VBD always uses Newton's :class:`CollisionPipeline` and steps with + separate input/output states, so the flags are fixed. + """ + cls._coupling_mode = solver_cfg.coupling_mode + + valid = set(inspect.signature(SolverMuJoCo.__init__).parameters) - {"self", "model"} + kwargs = {k: v for k, v in solver_cfg.rigid_solver_cfg.to_dict().items() if k in valid} + cls._rigid_solver = SolverMuJoCo(model, **kwargs) + + valid = set(inspect.signature(SolverVBD.__init__).parameters) - {"self", "model"} + kwargs = {k: v for k, v in solver_cfg.soft_solver_cfg.to_dict().items() if k in valid} + cls._soft_solver = SolverVBD(model, **kwargs) + + # Dummy solver for the newtonmanager + NewtonManager._solver = SolverBase(model) + + NewtonManager._use_single_state = False + NewtonManager._needs_collision_pipeline = True + + @classmethod + def _step_solver( + cls, state_in: State, state_out: State, control: Control, contacts: Contacts | None, substep_dt: float + ) -> None: + """One coupled substep. + + Args: + state_in: Current state (read/write). + state_out: Next state (write). + control: Joint-level control inputs. + contacts: Ignored -- the solver uses its own internal contacts. + dt: Substep timestep [s]. + """ + if cls._coupling_mode == "one_way": + cls._step_one_way(state_in, state_out, control, substep_dt) + else: + cls._step_two_way(state_in, state_out, control, substep_dt) + + @classmethod + def _simulate_physics_only(cls) -> None: + # Rebuild BVH once per step for solvers that require it (e.g. VBD cloth). + if hasattr(cls._soft_solver, "rebuild_bvh"): + cls._soft_solver.rebuild_bvh(cls._state_0) + super()._simulate_physics_only() + + @classmethod + def _step_one_way(cls, state_in: State, state_out: State, control: Control, dt: float) -> None: + """One-way coupling: collide, then rigid step, then VBD.""" + # 1. Clear forces + state_in.clear_forces() + state_out.clear_forces() + + # 2. Collision detection (cloth-body contacts) + cls._collision_pipeline.collide(state_in, cls._contacts) + + # 3. Rigid-body step (does not read soft-contact reactions) + cls._rigid_step(state_in, state_out, control, dt) + + # 4. Clear spurious particle forces from rigid step + state_in.particle_f.zero_() + + # 5. VBD step -- particles only, reads updated rigid poses + cls._soft_solver.step(state_in, state_out, control, cls._contacts, dt) + + @classmethod + def _step_two_way(cls, state_in: State, state_out: State, control: Control, dt: float) -> None: + """Two-way coupling: collide, inject reactions into body_f, rigid step, VBD step.""" + # 1. Clear forces + state_in.clear_forces() + state_out.clear_forces() + + # 2. Collision detection BEFORE rigid step + cls._collision_pipeline.collide(state_in, cls._contacts) + + # 3. Inject contact reaction forces into body_f. + # state_out holds the previous substep's body_q (states swap each + # substep), used for finite-difference body velocity in friction. + # particle_q_prev is reconstructed from particle_qd inside the + # kernel because VBD mutates particle_q in place, so the swapped + # state's particle_q is not a clean prior-substep snapshot. + if state_in.body_f is not None: + cls._apply_reactions(state_in, state_out, dt) + + # 4. Rigid-body step (reads body_f for soft-contact reactions) + cls._rigid_step(state_in, state_out, control, dt) + + # 5. Clear spurious particle forces from rigid step + state_in.particle_f.zero_() + + # 6. VBD step -- uses same contacts detected in step 2 + cls._soft_solver.step(state_in, state_out, control, cls._contacts, dt) + + @classmethod + def _rigid_step(cls, state_in: State, state_out: State, control: Control, dt: float) -> None: + """Advance rigid bodies with the configured sub-solver.""" + model = cls._model + + # set particle_count = 0 to disable particle simulation in robot solver + saved_particle_count = model.particle_count + model.particle_count = 0 + + cls._rigid_solver.step(state_in, state_out, control, None, dt) + + # restore original settings + model.particle_count = saved_particle_count + + @classmethod + def _apply_reactions(cls, state: State, state_prev: State, dt: float) -> None: + """Launch the reaction kernel to inject normal + friction forces into body_f. + + Args: + state: Current state with particle positions/velocities and body state. + state_prev: Previous substep state whose ``body_q`` provides + the reference poses for finite-difference body velocity. + dt: Substep timestep [s]. + """ + model = cls._model + contacts = cls._contacts + + if contacts is None: + return + + contact_capacity = int(contacts.soft_contact_particle.shape[0]) + if contact_capacity == 0: + return + + # The kernel reconstructs particle_q_prev from particle_qd internally: + # state_prev.particle_q is unreliable because VBD mutates particle_q + # in place during its iteration, so the swapped state's particle_q is + # not a clean snapshot of the prior substep. + wp.launch( + _kernel_body_particle_reaction, + dim=contact_capacity, + inputs=[ + contacts.soft_contact_count, + contacts.soft_contact_particle, + contacts.soft_contact_shape, + contacts.soft_contact_body_pos, + contacts.soft_contact_body_vel, + contacts.soft_contact_normal, + state.particle_q, + state.particle_qd, + model.particle_radius, + state.body_q, + state_prev.body_q, + state.body_qd, + model.body_com, + model.shape_body, + model.shape_material_mu, + float(model.soft_contact_ke), + float(model.soft_contact_kd), + float(model.soft_contact_mu), + float(cls._soft_solver.friction_epsilon), + float(dt), + state.body_f, + ], + ) diff --git a/source/isaaclab_contrib/isaaclab_contrib/deformable/deformable_object.py b/source/isaaclab_contrib/isaaclab_contrib/deformable/deformable_object.py new file mode 100644 index 000000000000..3c7fd10eb03e --- /dev/null +++ b/source/isaaclab_contrib/isaaclab_contrib/deformable/deformable_object.py @@ -0,0 +1,962 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +from __future__ import annotations + +import logging +from collections.abc import Sequence +from dataclasses import dataclass, field +from typing import TYPE_CHECKING + +import numpy as np +import torch +import warp as wp +from isaaclab_newton.physics import NewtonManager as SimulationManager + +import isaaclab.sim as sim_utils +from isaaclab.assets.deformable_object.base_deformable_object import BaseDeformableObject +from isaaclab.markers import VisualizationMarkers +from isaaclab.physics import PhysicsEvent +from isaaclab.utils.warp import ProxyArray + +from .deformable_object_data import DeformableObjectData +from .kernels import ( + compute_nodal_state_w, + enforce_kinematic_targets, + scatter_particles_state_vec6f_mask, + scatter_particles_vec3f_index, + scatter_particles_vec3f_mask, + set_kinematic_flags_to_one, + vec6f, + write_nodal_kinematic_target_index, + write_nodal_kinematic_target_mask, +) + + +@dataclass +class DeformableRegistryEntry: + """Entry in the deformable body registry. + + Registered by :class:`DeformableObject` during ``__init__``, consumed by + ``newton_physics_replicate`` inside the per-world ``begin_world``/``end_world`` loop. + After replication, ``particle_offsets`` and ``particles_per_body`` are filled in + so the asset can bind to the correct particle ranges. + """ + + prim_path: str + sim_mesh_prim_path: str + vis_mesh_prim_path: str + vertices: list + indices: list + init_pos: tuple[float, float, float] + init_rot: tuple[float, float, float, float] # (x, y, z, w) + deformable_type: str | None = None # "volume" or "surface" + # Cloth params + density: float = 1.0 + tri_ke: float = 1e4 + tri_ka: float = 1e4 + tri_kd: float = 1.5e-6 + edge_ke: float = 5.0 + edge_kd: float = 1e-2 + particle_radius: float = 0.008 + # Tet params + k_mu: float = 1e5 + k_lambda: float = 1e5 + k_damp: float = 0.0 + # Filled by newton_physics_replicate: + particle_offsets: list[int] = field(default_factory=list) + particles_per_body: int = 0 + + +if TYPE_CHECKING: + from isaaclab.assets.deformable_object.deformable_object_cfg import DeformableObjectCfg + +logger = logging.getLogger(__name__) + + +def add_deformable_entry_to_builder( + builder, + entry: DeformableRegistryEntry, + env_idx: int, + env_position: list[float], + env_rotation: list[float] | tuple[float, float, float, float], +) -> None: + """Add a deformable registry entry to a Newton ``ModelBuilder`` for one environment. + + Depending on the deformable type (``"volume"`` or ``"surface"``), calls + ``builder.add_soft_mesh()`` or ``builder.add_cloth_mesh()`` with the mesh + data and material properties stored in the registry entry. + + Also records the particle offset for the instance and, on the first + environment, records the per-body particle count. + + Args: + builder: The Newton ``ModelBuilder``. + entry: A :class:`DeformableRegistryEntry` with mesh data and config. + env_idx: The environment index. + env_position: World position [x, y, z] [m] for this environment. + env_rotation: World orientation as quaternion ``(x, y, z, w)`` for this environment. + """ + if env_idx == 0: + entry.particle_offsets.clear() + entry.particles_per_body = 0 + + before_count = getattr(builder, "particle_count", 0) + + env_pos = wp.vec3(float(env_position[0]), float(env_position[1]), float(env_position[2])) + env_rot = wp.quat( + float(env_rotation[0]), + float(env_rotation[1]), + float(env_rotation[2]), + float(env_rotation[3]), + ) + init_pos = wp.vec3(float(entry.init_pos[0]), float(entry.init_pos[1]), float(entry.init_pos[2])) + init_rot = wp.quat( + float(entry.init_rot[0]), + float(entry.init_rot[1]), + float(entry.init_rot[2]), + float(entry.init_rot[3]), + ) + body_pos = env_pos + wp.quat_rotate(env_rot, init_pos) + body_rot = env_rot * init_rot + + if entry.deformable_type == "volume": + builder.add_soft_mesh( + pos=body_pos, + rot=body_rot, + scale=1.0, + vel=wp.vec3(0.0, 0.0, 0.0), + vertices=entry.vertices, + indices=entry.indices, + density=entry.density, + k_mu=entry.k_mu, + k_lambda=entry.k_lambda, + k_damp=entry.k_damp, + particle_radius=entry.particle_radius, + ) + elif entry.deformable_type == "surface": + builder.add_cloth_mesh( + pos=body_pos, + rot=body_rot, + scale=1.0, + vel=wp.vec3(0.0, 0.0, 0.0), + vertices=entry.vertices, + indices=entry.indices, + density=entry.density, + tri_ke=entry.tri_ke, + tri_ka=entry.tri_ka, + tri_kd=entry.tri_kd, + edge_ke=entry.edge_ke, + edge_kd=entry.edge_kd, + particle_radius=entry.particle_radius, + ) + else: + raise ValueError( + f"Invalid deformable type '{entry.deformable_type}' for registry entry with prim path '{entry.prim_path}'" + ) + + after_count = getattr(builder, "particle_count", 0) + delta = after_count - before_count + + entry.particle_offsets.append(before_count) + if env_idx == 0: + entry.particles_per_body = delta + elif entry.particles_per_body != delta: + raise RuntimeError( + f"Deformable body '{entry.prim_path}' produced {delta} particles in env {env_idx}, " + f"but env 0 produced {entry.particles_per_body}." + ) + + +def add_registered_deformables_to_builder( + builder, + world_idx: int, + env_position: list[float], + env_rotation: list[float] | tuple[float, float, float, float], +) -> None: + """Add all registered deformable entries to one Newton builder world.""" + for entry in SimulationManager._deformable_registry: + add_deformable_entry_to_builder(builder, entry, world_idx, env_position, env_rotation) + + +def color_registered_deformables(builder) -> None: + """Color the Newton builder when deformables were registered.""" + if SimulationManager._deformable_registry: + builder.color() + + +def install_deformable_builder_hooks() -> None: + """Install deformable builder hooks without removing hooks owned by other extensions.""" + SimulationManager._deformable_registry = [] + if not hasattr(SimulationManager, "_per_world_builder_hooks"): + SimulationManager._per_world_builder_hooks = [] + if not hasattr(SimulationManager, "_post_replicate_hooks"): + SimulationManager._post_replicate_hooks = [] + if add_registered_deformables_to_builder not in SimulationManager._per_world_builder_hooks: + SimulationManager._per_world_builder_hooks.append(add_registered_deformables_to_builder) + if color_registered_deformables not in SimulationManager._post_replicate_hooks: + SimulationManager._post_replicate_hooks.append(color_registered_deformables) + + +def clear_deformable_builder_hooks() -> None: + """Clear deformable registry state and remove only deformable-owned builder hooks.""" + SimulationManager._deformable_registry = [] + if hasattr(SimulationManager, "_per_world_builder_hooks"): + SimulationManager._per_world_builder_hooks = [ + hook + for hook in SimulationManager._per_world_builder_hooks + if hook is not add_registered_deformables_to_builder + ] + if hasattr(SimulationManager, "_post_replicate_hooks"): + SimulationManager._post_replicate_hooks = [ + hook for hook in SimulationManager._post_replicate_hooks if hook is not color_registered_deformables + ] + + +class DeformableObject(BaseDeformableObject): + """A deformable object asset class (Newton backend). + + This class manages cloth/deformable bodies in the Newton physics engine. Newton stores all + particles in flat arrays (``state.particle_q``, ``state.particle_qd``). This class builds + a per-instance indexing layer on top of those flat arrays, enabling the standard + :class:`BaseDeformableObject` interface for reading/writing nodal state. + + The cloth mesh is added to the Newton :class:`ModelBuilder` during the ``MODEL_INIT`` phase. + The mesh data is read from the USD prim at :attr:`cfg.prim_path`, and cloth simulation + parameters (density, stiffness, etc.) come from :attr:`DeformableObjectCfg`. + """ + + cfg: DeformableObjectCfg + """Configuration instance for the deformable object.""" + + __backend_name__: str = "newton" + """The name of the backend for the deformable object.""" + + def __init__(self, cfg: DeformableObjectCfg): + """Initialize the deformable object. + + Args: + cfg: A configuration instance. + """ + super().__init__(cfg) + + # initialize deformable type to None, should be set to either surface or volume on initialization + self._deformable_type: str | None = None + + # Read mesh from the spawned USD prim and register in the deformable registry. + self._registry_entry = self._register_deformable() + + # Register custom vec6f type for nodal state validation. + self._DTYPE_TO_TORCH_TRAILING_DIMS = {**self._DTYPE_TO_TORCH_TRAILING_DIMS, vec6f: (6,)} + + """ + Properties + """ + + @property + def data(self) -> DeformableObjectData: + return self._data + + @property + def num_instances(self) -> int: + return self._num_instances + + @property + def num_bodies(self) -> int: + """Number of bodies in the asset. + + This is always 1 since each object is a single deformable body. + """ + return 1 + + @property + def max_sim_vertices_per_body(self) -> int: + """The maximum number of simulation mesh vertices per deformable body.""" + return self._particles_per_body + + """ + Operations. + """ + + def reset(self, env_ids: Sequence[int] | None = None, env_mask: wp.array | None = None) -> None: + """Reset the deformable object. + + No-op to match the PhysX deformable object convention. + + Args: + env_ids: Environment indices. If None, then all indices are used. + env_mask: Environment mask. If None, then all the instances are updated. + Shape is (num_instances,). + """ + pass + + def write_data_to_sim(self): + """Apply kinematic targets to the Newton simulation. + + Reads the stored kinematic target buffer and enforces it on particles: + kinematic particles (flag=0) get inv_mass=0, particle_flags=0, target position, + and zero velocity; free particles (flag=1) get their original inv_mass and + particle_flags=1 (ACTIVE) restored. + + Writes to both ``state_0`` and ``state_1`` so kinematic positions survive + the state swaps that happen between substeps. + """ + if ( + self._data.nodal_kinematic_target is None + or self._default_particle_inv_mass is None + or self._default_particle_flags is None + ): + return + + model = SimulationManager.get_model() + if model is None: + return + + for state in self._iter_particle_states(): + wp.launch( + enforce_kinematic_targets, + dim=(self._num_instances, self._particles_per_body), + inputs=[ + self._data.nodal_kinematic_target.warp, + self._particle_offsets, + self._default_particle_inv_mass, + self._default_particle_flags, + ], + outputs=[ + state.particle_q, + state.particle_qd, + model.particle_inv_mass, + model.particle_flags, + ], + device=self.device, + ) + + def update(self, dt: float): + self._data.update(dt) + + """ + Operations - Write to simulation. + """ + + def write_nodal_pos_to_sim_index( + self, + nodal_pos: torch.Tensor | wp.array | ProxyArray, + env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, + full_data: bool = False, + ) -> None: + """Set the nodal positions over selected environment indices into the simulation. + + Args: + nodal_pos: Nodal positions in simulation frame [m]. + Shape is (len(env_ids), max_sim_vertices_per_body, 3) + or (num_instances, max_sim_vertices_per_body, 3). + env_ids: Environment indices. If None, then all indices are used. + full_data: Whether to expect full data. Defaults to False. + """ + env_ids = self._resolve_env_ids(env_ids) + if isinstance(nodal_pos, ProxyArray): + nodal_pos = nodal_pos.warp + if full_data: + self.assert_shape_and_dtype( + nodal_pos, (self.num_instances, self._particles_per_body), wp.vec3f, "nodal_pos" + ) + else: + self.assert_shape_and_dtype(nodal_pos, (env_ids.shape[0], self._particles_per_body), wp.vec3f, "nodal_pos") + if isinstance(nodal_pos, torch.Tensor): + nodal_pos = wp.from_torch(nodal_pos.contiguous(), dtype=wp.vec3f) + + for state in self._iter_particle_states(): + wp.launch( + scatter_particles_vec3f_index, + dim=(env_ids.shape[0], self._particles_per_body), + inputs=[nodal_pos, env_ids, self._particle_offsets, full_data], + outputs=[state.particle_q], + device=self.device, + ) + + self._invalidate_nodal_pos_cache() + + def write_nodal_velocity_to_sim_index( + self, + nodal_vel: torch.Tensor | wp.array | ProxyArray, + env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, + full_data: bool = False, + ) -> None: + """Set the nodal velocity over selected environment indices into the simulation. + + Args: + nodal_vel: Nodal velocities in simulation frame [m/s]. + Shape is (len(env_ids), max_sim_vertices_per_body, 3) + or (num_instances, max_sim_vertices_per_body, 3). + env_ids: Environment indices. If None, then all indices are used. + full_data: Whether to expect full data. Defaults to False. + """ + env_ids = self._resolve_env_ids(env_ids) + if isinstance(nodal_vel, ProxyArray): + nodal_vel = nodal_vel.warp + if full_data: + self.assert_shape_and_dtype( + nodal_vel, (self.num_instances, self._particles_per_body), wp.vec3f, "nodal_vel" + ) + else: + self.assert_shape_and_dtype(nodal_vel, (env_ids.shape[0], self._particles_per_body), wp.vec3f, "nodal_vel") + if isinstance(nodal_vel, torch.Tensor): + nodal_vel = wp.from_torch(nodal_vel.contiguous(), dtype=wp.vec3f) + + for state in self._iter_particle_states(): + wp.launch( + scatter_particles_vec3f_index, + dim=(env_ids.shape[0], self._particles_per_body), + inputs=[nodal_vel, env_ids, self._particle_offsets, full_data], + outputs=[state.particle_qd], + device=self.device, + ) + + self._invalidate_nodal_vel_cache() + + def write_nodal_kinematic_target_to_sim_index( + self, + targets: torch.Tensor | wp.array | ProxyArray, + env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, + full_data: bool = False, + ) -> None: + """Set the kinematic targets of the simulation mesh for the deformable bodies. + + Newton has no native kinematic target API. Instead: + - Kinematic (flag=0.0): set ``particle_inv_mass`` to 0, write target pos, zero vel + - Free (flag=1.0): restore original ``particle_inv_mass`` + + Args: + targets: The kinematic targets comprising of nodal positions and flags [m]. + Shape is (len(env_ids), max_sim_vertices_per_body, 4) + or (num_instances, max_sim_vertices_per_body, 4). + env_ids: Environment indices. If None, then all indices are used. + full_data: Whether to expect full data. Defaults to False. + """ + env_ids = self._resolve_env_ids(env_ids) + if isinstance(targets, ProxyArray): + targets = targets.warp + if full_data: + self.assert_shape_and_dtype(targets, (self.num_instances, self._particles_per_body), wp.vec4f, "targets") + else: + self.assert_shape_and_dtype(targets, (env_ids.shape[0], self._particles_per_body), wp.vec4f, "targets") + if isinstance(targets, torch.Tensor): + if targets.dim() == 2: + targets = targets.unsqueeze(0) + targets = wp.from_torch(targets.contiguous(), dtype=wp.vec4f) + + # Store kinematic targets in our data buffer + if self._data.nodal_kinematic_target is not None: + wp.launch( + write_nodal_kinematic_target_index, + dim=(env_ids.shape[0], self._particles_per_body), + inputs=[targets, env_ids, full_data], + outputs=[self._data.nodal_kinematic_target.warp], + device=self.device, + ) + + """ + Operations - Write to simulation (mask variants). + """ + + def write_nodal_state_to_sim_mask( + self, + nodal_state: torch.Tensor | wp.array | ProxyArray, + env_mask: wp.array | torch.Tensor | None = None, + ) -> None: + """Set the nodal state over selected environment mask into the simulation. + + Args: + nodal_state: Nodal state in simulation frame [m, m/s]. + Shape is (num_instances, max_sim_vertices_per_body, 6). + env_mask: Environment mask. If None, then all indices are used. + Shape is (num_instances,). + """ + env_mask = self._resolve_mask(env_mask, self._ALL_ENV_MASK) + if isinstance(nodal_state, ProxyArray): + nodal_state = nodal_state.warp + self.assert_shape_and_dtype(nodal_state, (env_mask.shape[0], self._particles_per_body), vec6f, "nodal_state") + if isinstance(nodal_state, torch.Tensor): + nodal_state = wp.from_torch(nodal_state.contiguous(), dtype=vec6f) + + for state in self._iter_particle_states(): + wp.launch( + scatter_particles_state_vec6f_mask, + dim=(env_mask.shape[0], self._particles_per_body), + inputs=[nodal_state, env_mask, self._particle_offsets], + outputs=[state.particle_q, state.particle_qd], + device=self.device, + ) + + self._invalidate_nodal_state_cache() + + def write_nodal_pos_to_sim_mask( + self, + nodal_pos: torch.Tensor | wp.array | ProxyArray, + env_mask: wp.array | torch.Tensor | None = None, + ) -> None: + """Set the nodal positions over selected environment mask into the simulation. + + Args: + nodal_pos: Nodal positions in simulation frame [m]. + Shape is (num_instances, max_sim_vertices_per_body, 3). + env_mask: Environment mask. If None, then all indices are used. + Shape is (num_instances,). + """ + env_mask = self._resolve_mask(env_mask, self._ALL_ENV_MASK) + if isinstance(nodal_pos, ProxyArray): + nodal_pos = nodal_pos.warp + self.assert_shape_and_dtype(nodal_pos, (env_mask.shape[0], self._particles_per_body), wp.vec3f, "nodal_pos") + if isinstance(nodal_pos, torch.Tensor): + nodal_pos = wp.from_torch(nodal_pos.contiguous(), dtype=wp.vec3f) + + for state in self._iter_particle_states(): + wp.launch( + scatter_particles_vec3f_mask, + dim=(env_mask.shape[0], self._particles_per_body), + inputs=[nodal_pos, env_mask, self._particle_offsets], + outputs=[state.particle_q], + device=self.device, + ) + + self._invalidate_nodal_pos_cache() + + def write_nodal_velocity_to_sim_mask( + self, + nodal_vel: torch.Tensor | wp.array | ProxyArray, + env_mask: wp.array | torch.Tensor | None = None, + ) -> None: + """Set the nodal velocity over selected environment mask into the simulation. + + Args: + nodal_vel: Nodal velocities in simulation frame [m/s]. + Shape is (num_instances, max_sim_vertices_per_body, 3). + env_mask: Environment mask. If None, then all indices are used. + Shape is (num_instances,). + """ + env_mask = self._resolve_mask(env_mask, self._ALL_ENV_MASK) + if isinstance(nodal_vel, ProxyArray): + nodal_vel = nodal_vel.warp + self.assert_shape_and_dtype(nodal_vel, (env_mask.shape[0], self._particles_per_body), wp.vec3f, "nodal_vel") + if isinstance(nodal_vel, torch.Tensor): + nodal_vel = wp.from_torch(nodal_vel.contiguous(), dtype=wp.vec3f) + + for state in self._iter_particle_states(): + wp.launch( + scatter_particles_vec3f_mask, + dim=(env_mask.shape[0], self._particles_per_body), + inputs=[nodal_vel, env_mask, self._particle_offsets], + outputs=[state.particle_qd], + device=self.device, + ) + + self._invalidate_nodal_vel_cache() + + def write_nodal_kinematic_target_to_sim_mask( + self, + targets: torch.Tensor | wp.array | ProxyArray, + env_mask: wp.array | torch.Tensor | None = None, + ) -> None: + """Set the kinematic targets over selected environment mask into the target buffer. + + Args: + targets: The kinematic targets comprising of nodal positions and flags [m]. + Shape is (num_instances, max_sim_vertices_per_body, 4). + env_mask: Environment mask. If None, then all indices are used. + Shape is (num_instances,). + """ + env_mask = self._resolve_mask(env_mask, self._ALL_ENV_MASK) + if isinstance(targets, ProxyArray): + targets = targets.warp + self.assert_shape_and_dtype(targets, (env_mask.shape[0], self._particles_per_body), wp.vec4f, "targets") + if isinstance(targets, torch.Tensor): + targets = wp.from_torch(targets.contiguous(), dtype=wp.vec4f) + + if self._data.nodal_kinematic_target is not None: + wp.launch( + write_nodal_kinematic_target_mask, + dim=(env_mask.shape[0], self._particles_per_body), + inputs=[targets, env_mask], + outputs=[self._data.nodal_kinematic_target.warp], + device=self.device, + ) + + """ + Internal helper. + """ + + def _resolve_env_ids(self, env_ids): + """Resolve environment indices to a warp int32 array.""" + if env_ids is None or (isinstance(env_ids, slice) and env_ids == slice(None)): + return self._ALL_INDICES + elif isinstance(env_ids, list): + return wp.array(env_ids, dtype=wp.int32, device=self.device) + elif isinstance(env_ids, torch.Tensor): + return wp.from_torch(env_ids.to(torch.int32), dtype=wp.int32) + return env_ids + + def _resolve_mask(self, mask: wp.array | torch.Tensor | None, full_mask: wp.array) -> wp.array: + """Resolve an environment mask to a warp bool array.""" + if mask is None: + return full_mask + if isinstance(mask, torch.Tensor): + if mask.dtype != torch.bool: + mask = mask.to(torch.bool) + return wp.from_torch(mask, dtype=wp.bool) + return mask + + def _iter_particle_states(self): + """Yield active Newton states.""" + for state in (SimulationManager.get_state_0(), SimulationManager.get_state_1()): + if state is None: + continue + yield state + + def _invalidate_nodal_pos_cache(self) -> None: + """Invalidate cached position-derived deformable data.""" + self._data._nodal_pos_w.timestamp = -1.0 + self._data._nodal_state_w.timestamp = -1.0 + self._data._root_pos_w.timestamp = -1.0 + + def _invalidate_nodal_vel_cache(self) -> None: + """Invalidate cached velocity-derived deformable data.""" + self._data._nodal_vel_w.timestamp = -1.0 + self._data._nodal_state_w.timestamp = -1.0 + self._data._root_vel_w.timestamp = -1.0 + + def _invalidate_nodal_state_cache(self) -> None: + """Invalidate all cached nodal state data.""" + self._invalidate_nodal_pos_cache() + self._invalidate_nodal_vel_cache() + + def _register_deformable(self) -> DeformableRegistryEntry: + """Read mesh from the spawned USD prim and register in NewtonManager's deformable registry. + + Returns: + The registry entry (also stored on NewtonManager._deformable_registry). + + Note: + pxr imports are deferred to this method (not module level) so that + ``resolve_task_config`` can import the env-cfg module before Kit + starts without polluting the ``pxr`` module cache. + """ + from pxr import Gf, UsdGeom, UsdShade + + # Resolve the path of the actually-spawned template prim. This must mirror + # :meth:`AssetBase.__init__`: ``spawn_path`` is set by ``InteractiveScene`` + # when the asset is part of the template-based cloning flow (the spawn + # lives at ``/World/template//proto_asset_*`` and per-env clones at + # ``/World/envs/env_*/`` are not yet authored). For Direct envs + # that spawn straight at the cloned regex, ``spawn_path`` is unset, so + # we fall back to ``prim_path`` — which already matches the spawned prim. + # The cloned-regex ``cfg.prim_path`` is still used below to build the + # registry entry's :attr:`sim_mesh_prim_path` / :attr:`vis_mesh_prim_path` + # so post-replicate consumers resolve all per-env clones. + lookup_path = ( + self.cfg.spawn.spawn_path + if self.cfg.spawn is not None and self.cfg.spawn.spawn_path is not None + else self.cfg.prim_path + ) + template_prim = sim_utils.find_first_matching_prim(lookup_path) + if template_prim is None: + raise RuntimeError(f"Failed to find prim for expression: '{lookup_path}'.") + template_prim_path = template_prim.GetPrimPath() + + # Discover sim / visual mesh prims under the template. + # The spawner authors a visual UsdGeom.Mesh and a separate simulation mesh + # (UsdGeom.TetMesh for volume, UsdGeom.Mesh for surface) with a + # ``*DeformableSimAPI`` applied, so we split candidates by that schema. + def _is_sim_mesh(prim) -> bool: + return any("DeformableSimAPI" in api for api in prim.GetAppliedSchemas()) + + tet_prims = sim_utils.get_all_matching_child_prims(template_prim_path, lambda p: p.GetTypeName() == "TetMesh") + mesh_prims = sim_utils.get_all_matching_child_prims(template_prim_path, lambda p: p.GetTypeName() == "Mesh") + + if len(tet_prims) > 1: + raise ValueError( + f"Found multiple TetMesh prims under '{template_prim_path}': " + f"{[p.GetPrimPath() for p in tet_prims]}." + " Deformable body schema supports only one simulation mesh per asset." + ) + + # Pick simulation and visual mesh prims. + if len(tet_prims) == 1: + deformable_type = "volume" + mesh_prim = tet_prims[0] + vis_candidates = [p for p in mesh_prims if not _is_sim_mesh(p)] + elif len(mesh_prims) > 0: + deformable_type = "surface" + sim_candidates = [p for p in mesh_prims if _is_sim_mesh(p)] + vis_candidates = [p for p in mesh_prims if not _is_sim_mesh(p)] + if len(sim_candidates) > 1: + raise ValueError( + f"Found multiple simulation Mesh prims under '{template_prim_path}': " + f"{[p.GetPrimPath() for p in sim_candidates]}." + " Deformable body schema supports only one simulation mesh per asset." + ) + # Fall back to the single authored Mesh when no explicit sim mesh was tagged + # (legacy / self-simulated surfaces where the visual mesh *is* the sim mesh). + mesh_prim = sim_candidates[0] if sim_candidates else vis_candidates[0] + if not sim_candidates: + vis_candidates = [] # visual == sim, no separate embedding target + else: + raise ValueError( + f"Could not find any surface or volume mesh in '{template_prim_path}'. Please check asset." + ) + + # Revert visual and simulation mesh prim paths back to template-relative form for registry storage, + # since the actual prim paths will differ per world instance after replication. + # When vis_candidates is empty the visual mesh IS the simulation mesh + # (e.g. a plain surface cloth with no separate visual embedding). + vis_mesh_prim = vis_candidates[0] if vis_candidates else mesh_prim + vis_mesh_prim_path = str(vis_mesh_prim.GetPrimPath()) + vis_mesh_prim_path = self.cfg.prim_path + vis_mesh_prim_path[len(template_prim_path.pathString) :] + sim_mesh_prim_path = str(mesh_prim.GetPrimPath()) + sim_mesh_prim_path = self.cfg.prim_path + sim_mesh_prim_path[len(template_prim_path.pathString) :] + logger.info("Registered visual UsdGeom.Mesh at %s.", vis_mesh_prim_path) + + # Bake the template prim's xform directly into the vertex positions. + xform_cache = UsdGeom.XformCache() + mesh_to_parent_frame = ( + xform_cache.GetLocalToWorldTransform(mesh_prim) + * xform_cache.GetLocalToWorldTransform(template_prim.GetParent()).GetInverse() + ) + + def _bake_points(raw_pts) -> list[wp.vec3]: + out = [] + for p in raw_pts: + q = mesh_to_parent_frame.Transform(Gf.Vec3d(float(p[0]), float(p[1]), float(p[2]))) + out.append(wp.vec3(float(q[0]), float(q[1]), float(q[2]))) + return out + + if deformable_type == "volume": + tet_mesh = UsdGeom.TetMesh(mesh_prim) + pts = tet_mesh.GetPointsAttr().Get() + vertices = _bake_points(pts) + raw_tet_indices = tet_mesh.GetTetVertexIndicesAttr().Get() + indices = [] + for vec4i in raw_tet_indices: + indices.extend([int(vec4i[0]), int(vec4i[1]), int(vec4i[2]), int(vec4i[3])]) + logger.info("Registered UsdGeom.TetMesh: %d vertices, %d tetrahedra.", len(pts), len(indices) // 4) + else: # surface + usd_mesh = UsdGeom.Mesh(mesh_prim) + pts = usd_mesh.GetPointsAttr().Get() + vertices = _bake_points(pts) + indices = list(usd_mesh.GetFaceVertexIndicesAttr().Get()) + logger.info("Registered UsdGeom.Mesh: %d vertices.", len(pts)) + + # init_pos/init_rot are already baked into the vertices by the Xform + # transform above. Setting them to identity prevents add_cloth_mesh/add_soft_mesh + # from applying them a second time. + # Note: add_deformable_entry_to_builder passes init_rot directly to + # wp.quat(x, y, z, w), so identity must be (0, 0, 0, 1) not (1, 0, 0, 0). + init_pos = (0.0, 0.0, 0.0) + init_rot = (0.0, 0.0, 0.0, 1.0) + + # Look up the bound deformable physics material + if not template_prim.HasAPI(UsdShade.MaterialBindingAPI): + raise ValueError( + f"Template prim '{template_prim_path}' must have a UsdShade.MaterialBindingAPI applied" + " with a Newton deformable physics material target." + ) + material_targets = UsdShade.MaterialBindingAPI(template_prim).GetDirectBindingRel("physics").GetTargets() + stage = template_prim.GetStage() + material_prim = None + for mat_path in material_targets: + mat_prim = stage.GetPrimAtPath(mat_path) + if mat_prim.GetAttribute("newton:density").IsValid(): + material_prim = mat_prim + break + if material_prim is None: + raise ValueError( + f"Could not find a Newton deformable physics material" + f" among the physics material targets of '{template_prim_path}'." + ) + + def _get_material_attr(name: str, default): + attr = material_prim.GetAttribute(name) + return attr.Get() if attr.IsValid() else default + + density = _get_material_attr("newton:density", DeformableRegistryEntry.density) + particle_radius = _get_material_attr("newton:particleRadius", DeformableRegistryEntry.particle_radius) + k_mu = _get_material_attr("newton:kMu", DeformableRegistryEntry.k_mu) + k_lambda = _get_material_attr("newton:kLambda", DeformableRegistryEntry.k_lambda) + k_damp = _get_material_attr("newton:kDamp", DeformableRegistryEntry.k_damp) + + tri_ke = _get_material_attr("newton:triKe", DeformableRegistryEntry.tri_ke) + tri_ka = _get_material_attr("newton:triKa", DeformableRegistryEntry.tri_ka) + tri_kd = _get_material_attr("newton:triKd", DeformableRegistryEntry.tri_kd) + edge_ke = _get_material_attr("newton:edgeKe", DeformableRegistryEntry.edge_ke) + edge_kd = _get_material_attr("newton:edgeKd", DeformableRegistryEntry.edge_kd) + + entry = DeformableRegistryEntry( + prim_path=self.cfg.prim_path, + sim_mesh_prim_path=sim_mesh_prim_path, + vis_mesh_prim_path=vis_mesh_prim_path, + vertices=vertices, + indices=indices, + deformable_type=deformable_type, + init_pos=init_pos, + init_rot=init_rot, + density=density, + tri_ke=tri_ke, + tri_ka=tri_ka, + tri_kd=tri_kd, + edge_ke=edge_ke, + edge_kd=edge_kd, + particle_radius=particle_radius, + k_mu=k_mu, + k_lambda=k_lambda, + k_damp=k_damp, + ) + SimulationManager._deformable_registry.append(entry) + self._deformable_type = deformable_type + return entry + + def _initialize_impl(self): + """Initialize physics handles and buffers after the Newton model is ready.""" + entry = self._registry_entry + self._num_instances = len(entry.particle_offsets) + self._particles_per_body = entry.particles_per_body + self._recorded_particle_offsets = entry.particle_offsets + + if self._num_instances == 0: + raise RuntimeError( + f"No deformable body instances found for '{self.cfg.prim_path}'. " + "Ensure newton_physics_replicate or MODEL_INIT processed the registry." + ) + + logger.info("Newton deformable object initialized at: %s", self.cfg.prim_path) + logger.info("Number of instances: %d", self._num_instances) + logger.info("Particles per body: %d", self._particles_per_body) + + # Build particle offset array on device + self._particle_offsets = wp.array(self._recorded_particle_offsets, dtype=wp.int32, device=self.device) + + # Create data container + self._data = DeformableObjectData( + particle_offsets=self._particle_offsets, + particles_per_body=self._particles_per_body, + num_instances=self._num_instances, + device=self.device, + ) + + # Create buffers + self._create_buffers() + + # Update data once + self.update(0.0) + + # Register rebind callback for full resets + self._physics_ready_handle = SimulationManager.register_callback( + lambda _: self._data._create_simulation_bindings(), + PhysicsEvent.PHYSICS_READY, + name=f"deformable_object_rebind_{self.cfg.prim_path}", + ) + + def _create_buffers(self): + """Create buffers for storing data.""" + # Constants + self._ALL_INDICES = wp.array(np.arange(self._num_instances, dtype=np.int32), device=self.device) + self._ALL_ENV_MASK = wp.ones((self._num_instances,), dtype=wp.bool, device=self.device) + + # Snapshot default positions from current state (after finalize + FK) + state = SimulationManager.get_state_0() + if state is not None and state.particle_q is not None: + from .kernels import gather_particles_vec3f + + self._default_nodal_pos_w = wp.zeros( + (self._num_instances, self._particles_per_body), dtype=wp.vec3f, device=self.device + ) + wp.launch( + gather_particles_vec3f, + dim=(self._num_instances, self._particles_per_body), + inputs=[state.particle_q, self._particle_offsets, self._particles_per_body], + outputs=[self._default_nodal_pos_w], + device=self.device, + ) + + # Compute default nodal state as vec6f (positions + zero velocities) + nodal_velocities = wp.zeros( + (self._num_instances, self._particles_per_body), dtype=wp.vec3f, device=self.device + ) + default_nodal_state_w = wp.zeros( + (self._num_instances, self._particles_per_body), dtype=vec6f, device=self.device + ) + wp.launch( + compute_nodal_state_w, + dim=(self._num_instances, self._particles_per_body), + inputs=[self._default_nodal_pos_w, nodal_velocities], + outputs=[default_nodal_state_w], + device=self.device, + ) + self._data.default_nodal_state_w = ProxyArray(default_nodal_state_w) + else: + self._default_nodal_pos_w = None + + # Snapshot default particle_inv_mass for kinematic target restoration + model = SimulationManager.get_model() + if model is not None and hasattr(model, "particle_inv_mass") and model.particle_inv_mass is not None: + self._default_particle_inv_mass = wp.clone(model.particle_inv_mass) + else: + self._default_particle_inv_mass = None + if model is not None and hasattr(model, "particle_flags") and model.particle_flags is not None: + self._default_particle_flags = wp.clone(model.particle_flags) + else: + self._default_particle_flags = None + + # Kinematic targets -- allocate and initialize with free flags + nodal_kinematic_target = wp.zeros( + (self._num_instances, self._particles_per_body), dtype=wp.vec4f, device=self.device + ) + wp.launch( + set_kinematic_flags_to_one, + dim=(self._num_instances * self._particles_per_body,), + inputs=[nodal_kinematic_target.reshape((self._num_instances * self._particles_per_body,))], + device=self.device, + ) + self._data.nodal_kinematic_target = ProxyArray(nodal_kinematic_target) + + # Set up the model parameters + model = SimulationManager.get_model() + if model is not None: + if hasattr(model, "edge_rest_angle"): + model.edge_rest_angle.zero_() + + """ + Internal simulation callbacks. + """ + + def _set_debug_vis_impl(self, debug_vis: bool): + if debug_vis: + if not hasattr(self, "target_visualizer"): + self.target_visualizer = VisualizationMarkers(self.cfg.visualizer_cfg) + self.target_visualizer.set_visibility(True) + else: + if hasattr(self, "target_visualizer"): + self.target_visualizer.set_visibility(False) + + def _debug_vis_callback(self, event): + num_enabled = 0 + if self._deformable_type == "volume": + kinematic_target_torch = self.data.nodal_kinematic_target.torch + targets_enabled = kinematic_target_torch[:, :, 3] == 0.0 + num_enabled = int(torch.sum(targets_enabled).item()) + if num_enabled == 0: + positions = torch.tensor([[0.0, 0.0, -10.0]], device=self.device) + else: + positions = kinematic_target_torch[targets_enabled][..., :3] + self.target_visualizer.visualize(positions) + + def _clear_callbacks(self) -> None: + """Clears all registered callbacks.""" + super()._clear_callbacks() + if hasattr(self, "_physics_ready_handle") and self._physics_ready_handle is not None: + self._physics_ready_handle.deregister() + self._physics_ready_handle = None + + def _invalidate_initialize_callback(self, event): + """Invalidates the scene elements.""" + super()._invalidate_initialize_callback(event) diff --git a/source/isaaclab_contrib/isaaclab_contrib/deformable/deformable_object_data.py b/source/isaaclab_contrib/isaaclab_contrib/deformable/deformable_object_data.py new file mode 100644 index 000000000000..d516f1fc76d9 --- /dev/null +++ b/source/isaaclab_contrib/isaaclab_contrib/deformable/deformable_object_data.py @@ -0,0 +1,206 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +from __future__ import annotations + +import warp as wp +from isaaclab_newton.physics import NewtonManager as SimulationManager + +from isaaclab.assets.deformable_object.base_deformable_object_data import BaseDeformableObjectData +from isaaclab.utils.buffers import TimestampedBufferWarp as TimestampedBuffer +from isaaclab.utils.warp import ProxyArray + +from .kernels import compute_mean_vec3f_over_vertices, compute_nodal_state_w, gather_particles_vec3f, vec6f + + +class DeformableObjectData(BaseDeformableObjectData): + """Data container for a deformable object (Newton backend). + + Newton stores all particles in flat arrays (``model.particle_q``, ``state.particle_qd``). + This data class builds a per-instance view by gathering from the flat arrays using + precomputed offsets. + + The data is lazily updated, meaning that the data is only updated when it is accessed. + """ + + def __init__( + self, + particle_offsets: wp.array, + particles_per_body: int, + num_instances: int, + device: str, + ): + """Initialize the Newton deformable object data. + + Args: + particle_offsets: Per-instance start offset into the flat particle array. + Shape is (num_instances,) with dtype int32. + particles_per_body: Number of particles per deformable body instance. + num_instances: Number of deformable body instances. + device: The device used for processing. + """ + super().__init__(device) + + # Store dimensions and indexing + self._particle_offsets = particle_offsets + self._particles_per_body = particles_per_body + self._num_instances = num_instances + + # Initialize lazy buffers + self._nodal_pos_w = TimestampedBuffer((num_instances, particles_per_body), device, wp.vec3f) + self._nodal_vel_w = TimestampedBuffer((num_instances, particles_per_body), device, wp.vec3f) + self._nodal_state_w = TimestampedBuffer((num_instances, particles_per_body), device, vec6f) + self._root_pos_w = TimestampedBuffer((num_instances,), device, wp.vec3f) + self._root_vel_w = TimestampedBuffer((num_instances,), device, wp.vec3f) + self._nodal_pos_w_ta: ProxyArray | None = None + self._nodal_vel_w_ta: ProxyArray | None = None + self._nodal_state_w_ta: ProxyArray | None = None + self._root_pos_w_ta: ProxyArray | None = None + self._root_vel_w_ta: ProxyArray | None = None + + self._create_simulation_bindings() + + ## + # Defaults. + ## + + default_nodal_state_w: ProxyArray = None + """Default nodal state ``[nodal_pos, nodal_vel]`` in simulation world frame. + Shape is (num_instances, particles_per_body) with dtype vec6f. + """ + + ## + # Kinematic commands. + ## + + nodal_kinematic_target: ProxyArray = None + """Simulation mesh kinematic targets for the deformable bodies. + Shape is (num_instances, particles_per_body) with dtype vec4f. + """ + + def _create_simulation_bindings(self) -> None: + """Validate the current Newton particle state and invalidate gathered buffers. + + Newton may swap :attr:`state_0` and :attr:`state_1` across substeps, so deformable data does not keep + long-lived particle array bindings. Read properties query :meth:`SimulationManager.get_state_0` at gather time + and materialize object-local views from the current flat particle arrays. + """ + self._get_current_particle_state() + + # Invalidate lazy buffers gathered from the previous simulation state. + self._nodal_pos_w.timestamp = -1.0 + self._nodal_vel_w.timestamp = -1.0 + self._nodal_state_w.timestamp = -1.0 + self._root_pos_w.timestamp = -1.0 + self._root_vel_w.timestamp = -1.0 + + def _get_current_particle_state(self): + """Return the current Newton state containing deformable particle arrays.""" + state = SimulationManager.get_state_0() + if state is None or state.particle_q is None or state.particle_qd is None: + raise RuntimeError( + "Failed to access Newton deformable particle state. Ensure the Newton model has been finalized and " + "contains particle position and velocity arrays." + ) + return state + + ## + # Properties. + ## + + @property + def nodal_pos_w(self) -> ProxyArray: + """Nodal positions in simulation world frame [m]. Shape is (num_instances, particles_per_body) vec3f.""" + if self._nodal_pos_w.timestamp < self._sim_timestamp: + state = self._get_current_particle_state() + wp.launch( + gather_particles_vec3f, + dim=(self._num_instances, self._particles_per_body), + inputs=[state.particle_q, self._particle_offsets, self._particles_per_body], + outputs=[self._nodal_pos_w.data], + device=self.device, + ) + self._nodal_pos_w.timestamp = self._sim_timestamp + if self._nodal_pos_w_ta is None: + self._nodal_pos_w_ta = ProxyArray(self._nodal_pos_w.data) + return self._nodal_pos_w_ta + + @property + def nodal_vel_w(self) -> ProxyArray: + """Nodal velocities in simulation world frame [m/s]. Shape is (num_instances, particles_per_body) vec3f.""" + if self._nodal_vel_w.timestamp < self._sim_timestamp: + state = self._get_current_particle_state() + wp.launch( + gather_particles_vec3f, + dim=(self._num_instances, self._particles_per_body), + inputs=[state.particle_qd, self._particle_offsets, self._particles_per_body], + outputs=[self._nodal_vel_w.data], + device=self.device, + ) + self._nodal_vel_w.timestamp = self._sim_timestamp + if self._nodal_vel_w_ta is None: + self._nodal_vel_w_ta = ProxyArray(self._nodal_vel_w.data) + return self._nodal_vel_w_ta + + @property + def nodal_state_w(self) -> ProxyArray: + """Nodal state ``[nodal_pos, nodal_vel]`` in simulation world frame [m, m/s]. + + Shape is (num_instances, particles_per_body) vec6f. + """ + if self._nodal_state_w.timestamp < self._sim_timestamp: + wp.launch( + compute_nodal_state_w, + dim=(self._num_instances, self._particles_per_body), + inputs=[self.nodal_pos_w.warp, self.nodal_vel_w.warp], + outputs=[self._nodal_state_w.data], + device=self.device, + ) + self._nodal_state_w.timestamp = self._sim_timestamp + if self._nodal_state_w_ta is None: + self._nodal_state_w_ta = ProxyArray(self._nodal_state_w.data) + return self._nodal_state_w_ta + + ## + # Derived properties. + ## + + @property + def root_pos_w(self) -> ProxyArray: + """Root position from nodal positions [m]. Shape is (num_instances,) vec3f. + + This quantity is computed as the mean of the nodal positions. + """ + if self._root_pos_w.timestamp < self._sim_timestamp: + wp.launch( + compute_mean_vec3f_over_vertices, + dim=(self._num_instances,), + inputs=[self.nodal_pos_w.warp, self._particles_per_body], + outputs=[self._root_pos_w.data], + device=self.device, + ) + self._root_pos_w.timestamp = self._sim_timestamp + if self._root_pos_w_ta is None: + self._root_pos_w_ta = ProxyArray(self._root_pos_w.data) + return self._root_pos_w_ta + + @property + def root_vel_w(self) -> ProxyArray: + """Root velocity from nodal velocities [m/s]. Shape is (num_instances,) vec3f. + + This quantity is computed as the mean of the nodal velocities. + """ + if self._root_vel_w.timestamp < self._sim_timestamp: + wp.launch( + compute_mean_vec3f_over_vertices, + dim=(self._num_instances,), + inputs=[self.nodal_vel_w.warp, self._particles_per_body], + outputs=[self._root_vel_w.data], + device=self.device, + ) + self._root_vel_w.timestamp = self._sim_timestamp + if self._root_vel_w_ta is None: + self._root_vel_w_ta = ProxyArray(self._root_vel_w.data) + return self._root_vel_w_ta diff --git a/source/isaaclab_contrib/isaaclab_contrib/deformable/kernels.py b/source/isaaclab_contrib/isaaclab_contrib/deformable/kernels.py new file mode 100644 index 000000000000..179f62e53367 --- /dev/null +++ b/source/isaaclab_contrib/isaaclab_contrib/deformable/kernels.py @@ -0,0 +1,384 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Warp kernels for Newton deformable object gather/scatter operations.""" + +import warp as wp +from newton._src.solvers.vbd.rigid_vbd_kernels import ( + evaluate_body_particle_contact as _evaluate_body_particle_contact, +) + +vec6f = wp.types.vector(length=6, dtype=wp.float32) + + +@wp.kernel +def gather_particles_vec3f( + src: wp.array(dtype=wp.vec3f), + offsets: wp.array(dtype=wp.int32), + num_particles: int, + dst: wp.array2d(dtype=wp.vec3f), +): + """Gather particle data from a flat array into a per-instance 2D array. + + Args: + src: Flat source particle array (all instances concatenated). Shape is (total_particles,). + offsets: Per-instance start offset into the flat array. Shape is (num_instances,). + num_particles: Number of particles per instance. + dst: Output 2D array. Shape is (num_instances, num_particles). + """ + i, j = wp.tid() + dst[i, j] = src[offsets[i] + j] + + +@wp.kernel +def scatter_particles_vec3f_index( + src: wp.array2d(dtype=wp.vec3f), + env_ids: wp.array(dtype=wp.int32), + offsets: wp.array(dtype=wp.int32), + full_data: bool, + dst: wp.array(dtype=wp.vec3f), +): + """Scatter per-instance particle data into the flat simulation array using indices. + + Args: + src: Input 2D array. Shape is (len(env_ids), num_particles) or (num_instances, num_particles). + env_ids: Environment indices to scatter to. Shape is (num_selected,). + offsets: Per-instance start offset into the flat array. Shape is (num_instances,). + full_data: If True, index src with env_ids[i]; otherwise index src with i. + dst: Flat destination particle array. Shape is (total_particles,). + """ + i, j = wp.tid() + env_id = env_ids[i] + if full_data: + dst[offsets[env_id] + j] = src[env_id, j] + else: + dst[offsets[env_id] + j] = src[i, j] + + +@wp.kernel +def scatter_particles_vec3f_mask( + src: wp.array2d(dtype=wp.vec3f), + env_mask: wp.array(dtype=wp.bool), + offsets: wp.array(dtype=wp.int32), + dst: wp.array(dtype=wp.vec3f), +): + """Scatter per-instance particle data into the flat simulation array using a mask. + + Args: + src: Input particle data [m] or [m/s]. Shape is (num_instances, num_particles). + env_mask: Environment mask. Shape is (num_instances,). + offsets: Per-instance start offset into the flat array. Shape is (num_instances,). + dst: Flat destination particle array. Shape is (total_particles,). + """ + i, j = wp.tid() + if env_mask[i]: + dst[offsets[i] + j] = src[i, j] + + +@wp.kernel +def scatter_particles_state_vec6f_mask( + src: wp.array2d(dtype=vec6f), + env_mask: wp.array(dtype=wp.bool), + offsets: wp.array(dtype=wp.int32), + particle_q: wp.array(dtype=wp.vec3f), + particle_qd: wp.array(dtype=wp.vec3f), +): + """Scatter per-instance nodal state into the flat simulation arrays using a mask. + + Args: + src: Input nodal state data [m, m/s]. Shape is (num_instances, num_particles). + env_mask: Environment mask. Shape is (num_instances,). + offsets: Per-instance start offset into the flat arrays. Shape is (num_instances,). + particle_q: Flat destination particle positions [m]. Shape is (total_particles,). + particle_qd: Flat destination particle velocities [m/s]. Shape is (total_particles,). + """ + i, j = wp.tid() + if env_mask[i]: + state = src[i, j] + flat_idx = offsets[i] + j + particle_q[flat_idx] = wp.vec3f(state[0], state[1], state[2]) + particle_qd[flat_idx] = wp.vec3f(state[3], state[4], state[5]) + + +@wp.kernel +def write_nodal_kinematic_target_mask( + src: wp.array2d(dtype=wp.vec4f), + env_mask: wp.array(dtype=wp.bool), + dst: wp.array2d(dtype=wp.vec4f), +): + """Write kinematic target data into the per-instance target buffer using a mask. + + Args: + src: Input kinematic targets [m]. Shape is (num_instances, num_particles). + env_mask: Environment mask. Shape is (num_instances,). + dst: Destination kinematic target buffer [m]. Shape is (num_instances, num_particles). + """ + i, j = wp.tid() + if env_mask[i]: + dst[i, j] = src[i, j] + + +@wp.kernel +def write_nodal_kinematic_target_index( + src: wp.array2d(dtype=wp.vec4f), + env_ids: wp.array(dtype=wp.int32), + full_data: bool, + dst: wp.array2d(dtype=wp.vec4f), +): + """Write kinematic target data into the per-instance target buffer using indices. + + Args: + src: Input kinematic targets [m]. Shape is (len(env_ids), num_particles) + or (num_instances, num_particles). + env_ids: Environment indices to write. Shape is (num_selected,). + full_data: If True, index src with env_ids[i]; otherwise index src with i. + dst: Destination kinematic target buffer [m]. Shape is (num_instances, num_particles). + """ + i, j = wp.tid() + env_id = env_ids[i] + if full_data: + dst[env_id, j] = src[env_id, j] + else: + dst[env_id, j] = src[i, j] + + +@wp.kernel +def compute_nodal_state_w( + nodal_pos: wp.array2d(dtype=wp.vec3f), + nodal_vel: wp.array2d(dtype=wp.vec3f), + nodal_state: wp.array2d(dtype=vec6f), +): + """Concatenate nodal positions and velocities into a 6-element state vector. + + Args: + nodal_pos: Input array of nodal positions. Shape is (num_instances, num_vertices). + nodal_vel: Input array of nodal velocities. Shape is (num_instances, num_vertices). + nodal_state: Output array where concatenated state vectors are written. + Shape is (num_instances, num_vertices). + """ + i, j = wp.tid() + p = nodal_pos[i, j] + v = nodal_vel[i, j] + nodal_state[i, j] = vec6f(p[0], p[1], p[2], v[0], v[1], v[2]) + + +@wp.kernel +def compute_mean_vec3f_over_vertices( + data: wp.array2d(dtype=wp.vec3f), + num_vertices: int, + result: wp.array(dtype=wp.vec3f), +): + """Compute the mean of vec3f data over the vertex dimension. + + Args: + data: Input array of vec3f data. Shape is (num_instances, num_vertices). + num_vertices: Number of vertices per instance. + result: Output array where mean values are written. Shape is (num_instances,). + """ + i = wp.tid() + acc = wp.vec3f(0.0, 0.0, 0.0) + for j in range(num_vertices): + acc = acc + data[i, j] + result[i] = acc / float(num_vertices) + + +@wp.kernel +def scatter_zero_vel_index( + env_ids: wp.array(dtype=wp.int32), + offsets: wp.array(dtype=wp.int32), + num_particles: int, + dst: wp.array(dtype=wp.vec3f), +): + """Zero the velocity of particles for selected environments. + + Args: + env_ids: Environment indices to zero velocities for. Shape is (num_selected,). + offsets: Per-instance start offset into the flat array. Shape is (num_instances,). + num_particles: Number of particles per instance. + dst: Flat destination velocity array. Shape is (total_particles,). + """ + i, j = wp.tid() + env_id = env_ids[i] + dst[offsets[env_id] + j] = wp.vec3f(0.0, 0.0, 0.0) + + +@wp.kernel +def scatter_default_pos_index( + default_pos: wp.array2d(dtype=wp.vec3f), + env_ids: wp.array(dtype=wp.int32), + offsets: wp.array(dtype=wp.int32), + dst: wp.array(dtype=wp.vec3f), +): + """Scatter default positions for selected environments into the flat simulation array. + + Args: + default_pos: Default positions per instance. Shape is (num_instances, num_particles). + env_ids: Environment indices to reset. Shape is (num_selected,). + offsets: Per-instance start offset into the flat array. Shape is (num_instances,). + dst: Flat destination particle array. Shape is (total_particles,). + """ + i, j = wp.tid() + env_id = env_ids[i] + dst[offsets[env_id] + j] = default_pos[env_id, j] + + +@wp.kernel +def set_kinematic_flags_to_one( + data: wp.array(dtype=wp.vec4f), +): + """Set the w-component (kinematic flag) of all vec4f entries to 1.0. + + This is used to initialize all vertices as non-kinematic (free) nodes. + + Args: + data: Input/output array of vec4f kinematic targets. Shape is (N*V,). + """ + i = wp.tid() + v = data[i] + data[i] = wp.vec4f(v[0], v[1], v[2], 1.0) + + +@wp.kernel +def enforce_kinematic_targets( + targets: wp.array2d(dtype=wp.vec4f), + offsets: wp.array(dtype=wp.int32), + default_inv_mass: wp.array(dtype=wp.float32), + default_flags: wp.array(dtype=wp.int32), + particle_q: wp.array(dtype=wp.vec3f), + particle_qd: wp.array(dtype=wp.vec3f), + particle_inv_mass: wp.array(dtype=wp.float32), + particle_flags: wp.array(dtype=wp.int32), +): + """Enforce kinematic targets on Newton particles. + + For each particle, reads the kinematic target flag (w-component): + - flag == 0.0 (kinematic): set inv_mass to 0, particle_flags to 0, write target position, zero velocity. + - flag != 0.0 (free): restore the default inv_mass and set particle_flags to 1 (ACTIVE). + + Args: + targets: Per-instance kinematic targets. Shape is (num_instances, particles_per_body). + Each vec4f contains (target_x, target_y, target_z, flag). + offsets: Per-instance start offset into the flat particle array. + default_inv_mass: Saved default inverse masses. Shape is (total_particles,). + default_flags: Saved default particle flags. Shape is (total_particles,). + particle_q: Flat particle positions to write. Shape is (total_particles,). + particle_qd: Flat particle velocities to write. Shape is (total_particles,). + particle_inv_mass: Flat particle inverse masses to write. Shape is (total_particles,). + particle_flags: Flat particle flags to write. Shape is (total_particles,). + 0 = kinematic (solver skips integration), 1 = ACTIVE. + """ + i, j = wp.tid() + t = targets[i, j] + flat_idx = offsets[i] + j + flag = t[3] + if flag == 0.0: + particle_inv_mass[flat_idx] = 0.0 + particle_flags[flat_idx] = 0 + particle_q[flat_idx] = wp.vec3f(t[0], t[1], t[2]) + particle_qd[flat_idx] = wp.vec3f(0.0, 0.0, 0.0) + else: + particle_inv_mass[flat_idx] = default_inv_mass[flat_idx] + particle_flags[flat_idx] = default_flags[flat_idx] + + +@wp.kernel +def _kernel_body_particle_reaction( + contact_count: wp.array(dtype=wp.int32), + contact_particle: wp.array(dtype=wp.int32), + contact_shape: wp.array(dtype=wp.int32), + contact_body_pos: wp.array(dtype=wp.vec3), + contact_body_vel: wp.array(dtype=wp.vec3), + contact_normal: wp.array(dtype=wp.vec3), + particle_q: wp.array(dtype=wp.vec3), + particle_qd: wp.array(dtype=wp.vec3), + particle_radius: wp.array(dtype=wp.float32), + body_q: wp.array(dtype=wp.transform), + body_q_prev: wp.array(dtype=wp.transform), + body_qd: wp.array(dtype=wp.spatial_vector), + body_com: wp.array(dtype=wp.vec3), + shape_body: wp.array(dtype=wp.int32), + shape_material_mu: wp.array(dtype=wp.float32), + soft_contact_ke: float, + soft_contact_kd: float, + soft_contact_mu: float, + friction_epsilon: float, + dt: float, + body_f: wp.array(dtype=wp.spatial_vector), +): + """Newton's-third-law reaction from soft particles onto rigid bodies. + + Delegates to Newton's ``evaluate_body_particle_contact()`` for the contact + force computation (normal + damping + Coulomb friction) so the model stays + in sync with the VBD solver. The force on the particle is negated and + applied as a wrench on the rigid body via ``body_f``. + + One thread per contact slot; threads beyond the actual contact count + early-exit. + + The "previous" particle position required by the contact model is + reconstructed from the current velocity (``particle_q - particle_qd * dt``) + rather than read from a stored previous-state array. VBD mutates + ``particle_q`` in place during its iteration, so the swapped state's + ``particle_q`` is no longer a reliable snapshot of the prior substep. + """ + tid = wp.tid() + if tid >= contact_count[0]: + return + + p_idx = contact_particle[tid] + s_idx = contact_shape[tid] + body_idx = shape_body[s_idx] + if body_idx < 0: + return + + # Reconstruct previous particle position from velocity so that + # dx = particle_qd * dt regardless of what VBD wrote into stored states. + p_pos = particle_q[p_idx] + p_pos_prev = p_pos - particle_qd[p_idx] * dt + + # Delegate to Newton's canonical contact model + f_on_particle, _ = _evaluate_body_particle_contact( + p_idx, + p_pos, + p_pos_prev, + tid, + soft_contact_ke, + soft_contact_kd, + soft_contact_mu, + friction_epsilon, + particle_radius, + shape_material_mu, + shape_body, + body_q, + body_q_prev, + body_qd, + body_com, + contact_shape, + contact_body_pos, + contact_body_vel, + contact_normal, + dt, + ) + + # Newton's third law: negate particle force → rigid body wrench + X_wb = body_q[body_idx] + bx = wp.transform_point(X_wb, contact_body_pos[tid]) + com_w = wp.transform_point(X_wb, body_com[body_idx]) + + reaction = -f_on_particle + torque = wp.cross(bx - com_w, reaction) + + wp.atomic_add( + body_f, + body_idx, + wp.spatial_vector( + reaction[0], + reaction[1], + reaction[2], + torque[0], + torque[1], + torque[2], + ), + ) diff --git a/source/isaaclab_contrib/isaaclab_contrib/deformable/newton_manager_cfg.py b/source/isaaclab_contrib/isaaclab_contrib/deformable/newton_manager_cfg.py new file mode 100644 index 000000000000..e9358ff0be23 --- /dev/null +++ b/source/isaaclab_contrib/isaaclab_contrib/deformable/newton_manager_cfg.py @@ -0,0 +1,211 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Configuration classes for VBD, coupled solver, and global Newton model parameters.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from isaaclab_newton.physics import FeatherstoneSolverCfg, MJWarpSolverCfg, NewtonSolverCfg + +from isaaclab.utils.configclass import configclass + +if TYPE_CHECKING: + from isaaclab_newton.physics import NewtonManager + + +@configclass +class VBDSolverCfg(NewtonSolverCfg): + """Configuration for the Vertex Block Descent (VBD) solver. + + Supports particle simulation (cloth, soft bodies) and coupled rigid-body systems. + Requires ``ModelBuilder.color()`` to be called before ``finalize()`` to build + the parallel vertex colouring needed by the solver. + """ + + class_type: type[NewtonManager] | str = "{DIR}.vbd_manager:NewtonVBDManager" + """Manager class for the VBD solver.""" + + solver_type: str = "vbd" + + iterations: int = 10 + """Number of VBD iterations per substep.""" + + integrate_with_external_rigid_solver: bool = False + """Whether rigid bodies are integrated by an external solver (one-way coupling). + + Set to ``True`` when coupling cloth with a separate rigid-body solver + (e.g. ``SolverFeatherstone``) so that VBD only integrates the cloth particles. + """ + + particle_enable_self_contact: bool = False + """Whether to enable VBD deformable's self-contact.""" + + particle_self_contact_radius: float = 0.005 + """Particle radius used for self-contact detection [m].""" + + particle_self_contact_margin: float = 0.005 + """Self-contact detection margin [m]. Should be >= particle_self_contact_radius.""" + + particle_collision_detection_interval: int = -1 + """Controls how frequently particle self-contact detection is applied. + + If set to a value < 0, collision detection is only performed once before the + initialization step. If set to 0, collision detection is applied twice: once + before and once immediately after initialization. If set to a value ``k`` >= 1, + collision detection is applied before every ``k`` VBD iterations. + """ + + particle_vertex_contact_buffer_size: int = 32 + """Preallocation size for each vertex's vertex-triangle collision buffer.""" + + particle_edge_contact_buffer_size: int = 64 + """Preallocation size for each edge's edge-edge collision buffer.""" + + particle_topological_contact_filter_threshold: int = 2 + """Maximum topological distance (in rings) below which self-contacts are discarded. + + Only used when ``particle_enable_self_contact`` is ``True``. + Increase to suppress contacts between closely connected mesh elements. + Values > 3 significantly increase computation time. + """ + + particle_rest_shape_contact_exclusion_radius: float = 0.0 + """World-space distance threshold for filtering topologically close primitives [m]. + + Candidate self-contacts whose rest-configuration separation is shorter than + this value are ignored. Only used when ``particle_enable_self_contact`` is ``True``. + """ + + rigid_contact_k_start: float = 1.0e2 + """Initial stiffness seed for all rigid body contacts (body-body and body-particle) [N/m]. + + Used by the AVBD rigid contact solver. Increase to make rigid contacts stiffer. + """ + + +@configclass +class CoupledMJWarpVBDSolverCfg(NewtonSolverCfg): + """Configuration for the coupled rigid-body MJWarp + VBD solver. + + Alternates a rigid-body solver (:class:`MJWarpSolverCfg`) and a soft-body solver (:class:`SolverVBD`) per + substep. The coupling direction is controlled by :attr:`coupling_mode`: + + - ``"one_way"`` (default): Rigid solver advances first, then VBD reads + the updated body poses. The rigid solver does not feel particle contacts. + - ``"two_way"``: Same-substep two-way coupling with normal + Coulomb + friction. Contact detection runs first, reaction forces are injected + into ``body_f``, then the rigid solver reads ``body_f`` and feels + resistance from the deformable object. The friction reaction lets + actuators carry the object against gravity during a lift. + """ + + class_type: type[NewtonManager] | str = "{DIR}.coupled_mjwarp_vbd_manager:NewtonCoupledMJWarpVBDManager" + """Manager class for the VBD solver.""" + + solver_type: str = "coupledmjwarpvbd" + + rigid_solver_cfg: MJWarpSolverCfg = MJWarpSolverCfg() + """Rigid-body sub-solver configuration for :class:`MJWarpSolverCfg`.""" + + soft_solver_cfg: VBDSolverCfg = VBDSolverCfg(integrate_with_external_rigid_solver=True) + """VBD sub-solver configuration for cloth/particle dynamics.""" + + coupling_mode: str = "two_way" + """Coupling direction between the rigid and VBD solvers. + + - ``"one_way"``: Rigid -> soft only (default, existing behavior). + - ``"two_way"``: Same-substep two-way coupling with normal + Coulomb friction. + """ + + +@configclass +class CoupledFeatherstoneVBDSolverCfg(NewtonSolverCfg): + """Configuration for the coupled rigid-body Featherstone + VBD solver. + + Alternates a rigid-body solver (:class:`FeatherstoneSolverCfg`) and a soft-body solver (:class:`SolverVBD`) per + substep. The coupling direction is controlled by :attr:`coupling_mode`: + + - ``"kinematic"`` (default): Rigid -> soft only. Rigid bodies are kinematically updated by the rigid solver, + then VBD reads the updated body poses and reacts to them. The rigid solver does not feel particle contacts. + - ``"one_way"``: Rigid solver advances first, then VBD reads + the updated body poses. The rigid solver does not feel particle contacts. + - ``"two_way"``: Same-substep two-way coupling with normal + Coulomb + friction. Contact detection runs first, reaction forces are injected + into ``body_f``, then the rigid solver reads ``body_f`` and feels + resistance from the deformable object. The friction reaction lets + actuators carry the object against gravity during a lift. + """ + + class_type: type[NewtonManager] | str = "{DIR}.coupled_featherstone_vbd_manager:NewtonCoupledFeatherstoneVBDManager" + """Manager class for the VBD solver.""" + + solver_type: str = "coupledfeatherstonevbd" + + rigid_solver_cfg: FeatherstoneSolverCfg = FeatherstoneSolverCfg() + """Rigid-body sub-solver configuration for :class:`FeatherstoneSolverCfg`.""" + + soft_solver_cfg: VBDSolverCfg = VBDSolverCfg(integrate_with_external_rigid_solver=True) + """VBD sub-solver configuration for cloth/particle dynamics.""" + + coupling_mode: str = "kinematic" + """Coupling direction between the rigid and VBD solvers. + + - ``"kinematic"``: Rigid -> soft only (default) + - ``"one_way"``: Rigid -> soft only (existing behavior). + - ``"two_way"``: Same-substep two-way coupling with normal + Coulomb friction. + """ + + +@configclass +class NewtonModelCfg: + """Global Newton model parameters. + + These parameters are applied to the ``newton.Model`` after finalization. + They control model-level contact behavior shared across all objects. + """ + + soft_contact_ke: float = 1.0e3 + """Body-particle contact stiffness [N/m]. + + Controls the stiffness of the penalty force of contacts between cloth/soft-body particles + and rigid body shapes, and self-contacts of cloth/soft-body particles. The effective stiffness per contact is the + average of this value and the rigid shape's material stiffness. + """ + + soft_contact_kd: float = 1.0e-2 + """Body-particle contact damping [N*s/m].""" + + soft_contact_mu: float = 0.5 + """Body-particle contact friction coefficient. + + The effective friction per contact is ``sqrt(soft_contact_mu * shape_material_mu)``. + Increase for better grip (e.g. gripper picking up cloth). + """ + + shape_material_ke: float | None = None + """Per-shape contact stiffness override [N/m]. + + When set, all collision shapes in the model will have their contact + stiffness overwritten to this value. If ``None`` (default), the + per-shape values parsed from USD/MJCF are kept. + """ + + shape_material_kd: float | None = None + """Per-shape contact damping override [N*s/m]. + + When set, all collision shapes in the model will have their contact + damping overwritten to this value. If ``None`` (default), the + per-shape values parsed from USD/MJCF are kept. + """ + + shape_material_mu: float | None = None + """Per-shape friction coefficient override [dimensionless]. + + When set, all collision shapes in the model will have their friction + coefficient overwritten to this value. If ``None`` (default), the + per-shape values parsed from USD/MJCF are kept. + """ diff --git a/source/isaaclab_contrib/isaaclab_contrib/deformable/vbd_manager.py b/source/isaaclab_contrib/isaaclab_contrib/deformable/vbd_manager.py new file mode 100644 index 000000000000..88380f078673 --- /dev/null +++ b/source/isaaclab_contrib/isaaclab_contrib/deformable/vbd_manager.py @@ -0,0 +1,285 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""VBD Newton manager.""" + +from __future__ import annotations + +import inspect +import logging +from typing import TYPE_CHECKING + +import warp as wp +from isaaclab_newton.physics.newton_manager import NewtonManager +from newton import Model, ModelBuilder +from newton._src.usd.schemas import SchemaResolverNewton, SchemaResolverPhysx +from newton.solvers import SolverVBD + +from isaaclab.sim.utils.stage import get_current_stage + +from .deformable_object import ( + add_deformable_entry_to_builder, + clear_deformable_builder_hooks, + install_deformable_builder_hooks, +) +from .newton_manager_cfg import VBDSolverCfg + +if TYPE_CHECKING: + from isaaclab.sim.simulation_context import SimulationContext + +logger = logging.getLogger(__name__) + + +class NewtonVBDManager(NewtonManager): + """:class:`NewtonManager` specialization for the VBD solver. + + Always uses Newton's :class:`CollisionPipeline` for contact handling. + """ + + @classmethod + def initialize(cls, sim_context: SimulationContext) -> None: + """Initialize the manager with simulation context. + + Args: + sim_context: Parent simulation context. + + TODO: Subclass should not override this method, once deformables + supported on Newton import_usd, this can be unified with NewtonManager's + implementation. + """ + + # Deformable body registry and extension hooks. + # Experimental deformable support registers callbacks here so the manager + # and cloner can invoke them without hard-coding deformable logic. + install_deformable_builder_hooks() + + super().initialize(sim_context) + + @classmethod + def _solver_specific_clear(cls): + """Clear VBD-specific state.""" + clear_deformable_builder_hooks() + + @classmethod + def _get_deformable_ignore_paths(cls) -> list[str]: + """Return USD prim paths to skip when calling ``builder.add_usd``. + + For each registered deformable body, both the simulation mesh (which + carries ``UsdPhysics.CollisionAPI``) and the visual mesh are returned. + The sim mesh must be skipped so Newton does not create a redundant + static mesh collider alongside the particles produced by + ``add_soft_mesh``. The visual mesh is skipped so Newton does not + treat it as a collider — Kit reads it directly from USD for rendering. + + Paths may contain regex patterns; Newton's ``add_usd`` matches them + via :func:`re.match`. + """ + paths: list[str] = [] + for entry in cls._deformable_registry: + paths.append(entry.sim_mesh_prim_path) + paths.append(entry.vis_mesh_prim_path) + return paths + + @classmethod + def start_simulation(cls) -> None: + """Start simulation by finalizing model and initializing state. + + This function finalizes the model and initializes the simulation state. + Note: Collision pipeline is initialized later in initialize_solver() after + we determine whether the solver needs external collision detection. + + TODO: Subclass should not override this method, missing piece is + having Newton bind a surface mesh to volume deformable tetrahedral mesh + in addition to removing the deformable_registry data structure. + """ + super().start_simulation() + + # Apply global model parameters from :class:`NewtonModelCfg` to the finalized model. + # Sets ``soft_contact_ke/kd/mu`` and optionally overrides per-shape + # ``shape_material_ke/kd/mu`` on the Newton model. + from isaaclab.physics import PhysicsManager + + cfg = PhysicsManager._cfg + if cfg is not None and hasattr(cfg, "model_cfg") and cfg.model_cfg is not None: + model = cls._model + if model is None: + return + + model_cfg = cfg.model_cfg + model.soft_contact_ke = float(model_cfg.soft_contact_ke) + model.soft_contact_kd = float(model_cfg.soft_contact_kd) + model.soft_contact_mu = float(model_cfg.soft_contact_mu) + + if model_cfg.shape_material_ke is not None: + model.shape_material_ke.fill_(float(model_cfg.shape_material_ke)) + if model_cfg.shape_material_kd is not None: + model.shape_material_kd.fill_(float(model_cfg.shape_material_kd)) + if model_cfg.shape_material_mu is not None: + model.shape_material_mu.fill_(float(model_cfg.shape_material_mu)) + + # Setup USD/Fabric sync for Kit viewport deformable rendering + if not cls._clone_physics_only and cls._deformable_registry: + import re + + import usdrt + + if NewtonManager._usdrt_stage is None: + NewtonManager._usdrt_stage = get_current_stage(fabric=True) + + stage = get_current_stage() + for entry in cls._deformable_registry: + for inst_idx, offset in enumerate(entry.particle_offsets): + # Resolve regex pattern to concrete instance path of visual mesh + resolved_vis = re.sub(r"(?<=[Ee]nv_)\.\*", str(inst_idx), entry.vis_mesh_prim_path) + resolved_vis = re.sub(r"\.\*", str(inst_idx), resolved_vis) + vis_prim = stage.GetPrimAtPath(resolved_vis) + + if not vis_prim or not vis_prim.IsValid(): + logger.warning("[setup_fabric_particle_sync] vis prim not found at %s", resolved_vis) + continue + + # Create per-instance particle offset and count attributes on the visual mesh + # prim so the Fabric sync kernel can find the right slice of particle_q + # and iterate only over this body's particles (counts vary across bodies). + fab_prim = NewtonManager._usdrt_stage.GetPrimAtPath(vis_prim.GetPath().pathString) + fab_prim.CreateAttribute( + NewtonManager._newton_particle_offset_attr, usdrt.Sdf.ValueTypeNames.UInt, True + ) + fab_prim.GetAttribute(NewtonManager._newton_particle_offset_attr).Set(offset) + fab_prim.CreateAttribute( + NewtonManager._newton_particle_count_attr, usdrt.Sdf.ValueTypeNames.UInt, True + ) + fab_prim.GetAttribute(NewtonManager._newton_particle_count_attr).Set(entry.particles_per_body) + + cls._mark_particles_dirty() + cls.sync_particles_to_usd() + + @classmethod + def instantiate_builder_from_stage(cls): + """Create builder from USD stage with special treatment for deformable + bodies, as these are not read from USD yet. + + Detects env Xforms (e.g. ``/World/Env_0``, ``/World/Env_1``) and builds + each as a separate Newton world via ``begin_world``/``end_world``. + Falls back to a flat ``add_usd`` when no env Xforms are found. + + TODO: Subclass should not override this method, once deformables + supported on Newton import_usd, this can be unified with NewtonManager's + implementation. + """ + import re + + from pxr import UsdGeom + + stage = get_current_stage() + up_axis = UsdGeom.GetStageUpAxis(stage) + + # Scan /World children for env-like Xforms (Env_0, env_1, ...) + env_pattern = re.compile(r"^[Ee]nv_(\d+)$") + world_prim = stage.GetPrimAtPath("/World") + env_paths: list[tuple[int, str]] = [] + if world_prim and world_prim.IsValid(): + for child in world_prim.GetChildren(): + m = env_pattern.match(child.GetName()) + if m: + env_paths.append((int(m.group(1)), child.GetPath().pathString)) + env_paths.sort(key=lambda x: x[0]) + + builder = ModelBuilder(up_axis=up_axis) + + schema_resolvers = [SchemaResolverNewton(), SchemaResolverPhysx()] + + # Deformable sim/visual mesh paths must be skipped by ``add_usd`` + # so they don't get duplicated as static colliders. + deformable_ignore_paths = cls._get_deformable_ignore_paths() + + if not env_paths: + # No env Xforms — flat loading + builder.add_usd(stage, ignore_paths=deformable_ignore_paths, schema_resolvers=schema_resolvers) + + # Add deformable bodies from the registry (single world at origin). + for entry in cls._deformable_registry: + add_deformable_entry_to_builder(builder, entry, 0, [0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0]) + else: + # Load everything except the env subtrees (ground plane, lights, etc.) + ignore_paths = [path for _, path in env_paths] + deformable_ignore_paths + builder.add_usd(stage, ignore_paths=ignore_paths, schema_resolvers=schema_resolvers) + + # Build a prototype from the first env (all envs assumed identical) + _, proto_path = env_paths[0] + proto = ModelBuilder(up_axis=up_axis) + proto.add_usd( + stage, + root_path=proto_path, + ignore_paths=deformable_ignore_paths, + schema_resolvers=schema_resolvers, + ) + + # Inject registered sites into the proto before replication + global_sites, proto_sites = cls._cl_inject_sites(builder, {proto_path: proto}) + global_site_map: dict[str, tuple[int, None]] = {label: (idx, None) for label, idx in global_sites.items()} + num_worlds = len(env_paths) + local_site_map: dict[str, list[list[int]]] = {} + site_entries = proto_sites.get(id(proto), {}) + + # Add each env as a separate Newton world + xform_cache = UsdGeom.XformCache() + for col, (_, env_path) in enumerate(env_paths): + builder.begin_world() + offset = builder.shape_count + world_xform = xform_cache.GetLocalToWorldTransform(stage.GetPrimAtPath(env_path)) + translation = world_xform.ExtractTranslation() + rotation = world_xform.ExtractRotationQuat() + pos = (translation[0], translation[1], translation[2]) + quat = ( + rotation.GetImaginary()[0], + rotation.GetImaginary()[1], + rotation.GetImaginary()[2], + rotation.GetReal(), + ) + builder.add_builder(proto, xform=wp.transform(pos, quat)) + for label, proto_shape_indices in site_entries.items(): + if label not in local_site_map: + local_site_map[label] = [[] for _ in range(num_worlds)] + for proto_shape_idx in proto_shape_indices: + local_site_map[label][col].append(offset + proto_shape_idx) + + # Add deformable bodies from the registry into this world. + for entry in cls._deformable_registry: + add_deformable_entry_to_builder(builder, entry, col, list(pos), quat) + + builder.end_world() + + NewtonManager._cl_site_index_map = { + **global_site_map, + **{label: (None, per_world) for label, per_world in local_site_map.items()}, + } + NewtonManager._num_envs = len(env_paths) + + # Call builder.color() if any deformable entries were added (required by VBD solver) + if cls._deformable_registry: + builder.color() + + cls.set_builder(builder) + + @classmethod + def _build_solver(cls, model: Model, solver_cfg: VBDSolverCfg) -> None: + """Construct :class:`SolverVBD` and populate the base-class slots. + + VBD always uses Newton's :class:`CollisionPipeline` and steps with + separate input/output states, so the flags are fixed. + """ + valid = set(inspect.signature(SolverVBD.__init__).parameters) - {"self", "model"} + kwargs = {k: v for k, v in solver_cfg.to_dict().items() if k in valid} + NewtonManager._solver = SolverVBD(model, **kwargs) + NewtonManager._use_single_state = False + NewtonManager._needs_collision_pipeline = True + + @classmethod + def _simulate_physics_only(cls) -> None: + # Rebuild BVH once per step for solvers that require it (e.g. VBD cloth). + if hasattr(cls._solver, "rebuild_bvh"): + cls._solver.rebuild_bvh(cls._state_0) + super()._simulate_physics_only() diff --git a/source/isaaclab_contrib/test/deformable/test_deformable_builder_hooks.py b/source/isaaclab_contrib/test/deformable/test_deformable_builder_hooks.py new file mode 100644 index 000000000000..4a9c03932c5b --- /dev/null +++ b/source/isaaclab_contrib/test/deformable/test_deformable_builder_hooks.py @@ -0,0 +1,100 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +import math + +import pytest +import warp as wp +from isaaclab_newton.sim.spawners.materials import NewtonDeformableMaterialCfg + +from isaaclab_contrib.deformable import DeformableObject, VBDSolverCfg +from isaaclab_contrib.deformable.deformable_object import DeformableRegistryEntry, add_deformable_entry_to_builder + + +class _FakeBuilder: + def __init__(self): + self.particle_count = 0 + self.cloth_meshes = [] + + def add_cloth_mesh(self, **kwargs) -> None: + self.cloth_meshes.append(kwargs) + self.particle_count += len(kwargs["vertices"]) + + +def _make_surface_entry() -> DeformableRegistryEntry: + half_sqrt = math.sqrt(0.5) + return DeformableRegistryEntry( + prim_path="/World/envs/env_.*/cloth", + sim_mesh_prim_path="/World/envs/env_.*/cloth/mesh", + vis_mesh_prim_path="/World/envs/env_.*/cloth/mesh", + vertices=[ + wp.vec3(0.0, 0.0, 0.0), + wp.vec3(1.0, 0.0, 0.0), + wp.vec3(0.0, 1.0, 0.0), + ], + indices=[0, 1, 2], + init_pos=(1.0, 0.0, 0.0), + init_rot=(0.0, 0.0, half_sqrt, half_sqrt), + deformable_type="surface", + ) + + +def _vec3_as_tuple(value) -> tuple[float, float, float]: + return (float(value[0]), float(value[1]), float(value[2])) + + +def test_deformable_package_exports_public_symbols(): + """Test that deformable symbols are exported from the package root.""" + assert DeformableObject.__name__ == "DeformableObject" + assert VBDSolverCfg.__name__ == "VBDSolverCfg" + + +def test_newton_material_defaults_match_registry_defaults(): + """Test that Newton material cfg defaults match the deformable registry defaults.""" + material_cfg = NewtonDeformableMaterialCfg() + + assert material_cfg.density == DeformableRegistryEntry.density + assert material_cfg.particle_radius == DeformableRegistryEntry.particle_radius + + +def test_builder_hook_applies_env_quaternion_to_deformable_entry(): + """Test that deformable builder placement honors the environment quaternion.""" + entry = _make_surface_entry() + builder = _FakeBuilder() + half_sqrt = math.sqrt(0.5) + + add_deformable_entry_to_builder( + builder, + entry, + env_idx=0, + env_position=[10.0, 20.0, 30.0], + env_rotation=[0.0, 0.0, half_sqrt, half_sqrt], + ) + + mesh = builder.cloth_meshes[0] + rotated_x_axis = wp.quat_rotate(mesh["rot"], wp.vec3(1.0, 0.0, 0.0)) + + assert _vec3_as_tuple(mesh["pos"]) == pytest.approx((10.0, 21.0, 30.0)) + assert _vec3_as_tuple(rotated_x_axis) == pytest.approx((-1.0, 0.0, 0.0), abs=1e-6) + assert entry.particle_offsets == [0] + assert entry.particles_per_body == 3 + + +def test_builder_hook_resets_entry_offsets_on_first_environment(): + """Test that repeated model rebuilds do not accumulate stale particle offsets.""" + entry = _make_surface_entry() + builder = _FakeBuilder() + identity = [0.0, 0.0, 0.0, 1.0] + + add_deformable_entry_to_builder(builder, entry, 0, [0.0, 0.0, 0.0], identity) + add_deformable_entry_to_builder(builder, entry, 1, [1.0, 0.0, 0.0], identity) + + assert entry.particle_offsets == [0, 3] + + rebuilt_builder = _FakeBuilder() + add_deformable_entry_to_builder(rebuilt_builder, entry, 0, [0.0, 0.0, 0.0], identity) + + assert entry.particle_offsets == [0] + assert entry.particles_per_body == 3 diff --git a/source/isaaclab_contrib/test/deformable/test_deformable_object.py b/source/isaaclab_contrib/test/deformable/test_deformable_object.py new file mode 100644 index 000000000000..d64cc39331bd --- /dev/null +++ b/source/isaaclab_contrib/test/deformable/test_deformable_object.py @@ -0,0 +1,717 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +# ignore private usage of variables warning +# pyright: reportPrivateUsage=none + + +"""Launch Isaac Sim Simulator first.""" + +from isaaclab.app import AppLauncher + +# launch omniverse app +simulation_app = AppLauncher(headless=True).app + +"""Rest everything follows.""" + +import pytest +import torch +import warp as wp +from flaky import flaky +from isaaclab_newton.physics import NewtonCfg +from isaaclab_newton.sim.schemas import NewtonDeformableBodyPropertiesCfg +from isaaclab_newton.sim.spawners.materials import ( + NewtonDeformableBodyMaterialCfg, + NewtonSurfaceDeformableBodyMaterialCfg, +) + +import isaaclab.sim as sim_utils +import isaaclab.utils.math as math_utils +from isaaclab.assets import DeformableObject, DeformableObjectCfg +from isaaclab.sim import SimulationCfg, build_simulation_context + +from isaaclab_contrib.deformable.newton_manager_cfg import VBDSolverCfg + +NEWTON_VBD_CFG = SimulationCfg( + physics=NewtonCfg( + solver_cfg=VBDSolverCfg(iterations=3), + num_substeps=2, + ), +) + + +def _newton_sim_context(device="cuda:0", gravity_enabled=True): + """Helper to create a Newton VBD simulation context.""" + NEWTON_VBD_CFG.device = device + NEWTON_VBD_CFG.gravity = (0.0, 0.0, -9.81) if gravity_enabled else (0.0, 0.0, 0.0) + return build_simulation_context(device=device, sim_cfg=NEWTON_VBD_CFG, auto_add_lighting=True) + + +def generate_cubes_scene( + num_cubes: int = 1, + height: float = 1.0, + device: str = "cuda:0", +) -> DeformableObject: + """Generate a scene with deformable tet-mesh cubes. + + Args: + num_cubes: Number of cubes to generate. + height: Height of the cubes. + device: Device to use for the simulation. + + Returns: + The deformable object representing the cubes. + """ + origins = torch.tensor([(i * 1.0, 0, height) for i in range(num_cubes)]).to(device) + for i, origin in enumerate(origins): + sim_utils.create_prim(f"/World/env_{i}", "Xform", translation=origin) + + cube_object_cfg = DeformableObjectCfg( + prim_path="/World/env_.*/Cube", + spawn=sim_utils.MeshCuboidCfg( + size=(0.1, 0.1, 0.1), + deformable_props=NewtonDeformableBodyPropertiesCfg(), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.2, 0.8, 0.2)), + physics_material=NewtonDeformableBodyMaterialCfg( + density=500.0, + k_mu=1e4, + k_lambda=1e4, + ), + ), + init_state=DeformableObjectCfg.InitialStateCfg( + pos=(0.0, 0.0, height), + rot=(1.0, 0.0, 0.0, 0.0), + ), + ) + cube_object = DeformableObject(cfg=cube_object_cfg) + return cube_object + + +def generate_cloth_scene( + num_cloths: int = 1, + height: float = 1.0, + device: str = "cuda:0", +) -> DeformableObject: + """Generate a scene with surface deformable cloth squares. + + Args: + num_cloths: Number of cloths to generate. + height: Height of the cloths. + device: Device to use for the simulation. + + Returns: + The deformable object representing the cloths. + """ + origins = torch.tensor([(i * 1.0, 0, height) for i in range(num_cloths)]).to(device) + for i, origin in enumerate(origins): + sim_utils.create_prim(f"/World/env_{i}", "Xform", translation=origin) + + cloth_object_cfg = DeformableObjectCfg( + prim_path="/World/env_.*/Cloth", + spawn=sim_utils.MeshRectangleCfg( + size=(0.2, 0.2), + resolution=(3, 3), + deformable_props=NewtonDeformableBodyPropertiesCfg(), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.2, 0.2, 0.8)), + physics_material=NewtonSurfaceDeformableBodyMaterialCfg(density=0.02, particle_radius=0.005), + ), + init_state=DeformableObjectCfg.InitialStateCfg( + pos=(0.0, 0.0, height), + rot=(1.0, 0.0, 0.0, 0.0), + ), + ) + return DeformableObject(cfg=cloth_object_cfg) + + +def generate_cuboid_and_cylinder_scene(height: float = 1.0) -> tuple[DeformableObject, DeformableObject]: + """Generate two independent deformable assets with different mesh shapes.""" + sim_utils.create_prim("/World/env_0", "Xform", translation=(0.0, 0.0, 0.0)) + + cuboid_cfg = DeformableObjectCfg( + prim_path="/World/env_.*/Cuboid", + spawn=sim_utils.MeshCuboidCfg( + size=(0.16, 0.08, 0.12), + deformable_props=NewtonDeformableBodyPropertiesCfg(), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.2, 0.8, 0.2)), + physics_material=NewtonDeformableBodyMaterialCfg( + density=500.0, + k_mu=1e4, + k_lambda=1e4, + ), + ), + init_state=DeformableObjectCfg.InitialStateCfg(pos=(0.0, 0.0, height)), + ) + cylinder_cfg = DeformableObjectCfg( + prim_path="/World/env_.*/Cylinder", + spawn=sim_utils.MeshCylinderCfg( + radius=0.06, + height=0.14, + deformable_props=NewtonDeformableBodyPropertiesCfg(), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.8, 0.2, 0.2)), + physics_material=NewtonDeformableBodyMaterialCfg( + density=500.0, + k_mu=1e4, + k_lambda=1e4, + ), + ), + init_state=DeformableObjectCfg.InitialStateCfg(pos=(0.4, 0.0, height + 0.2)), + ) + return DeformableObject(cfg=cuboid_cfg), DeformableObject(cfg=cylinder_cfg) + + +@pytest.fixture +def sim(): + """Create Newton VBD simulation context.""" + with _newton_sim_context() as sim: + sim._app_control_on_stop_handle = None + yield sim + + +def test_initialization(sim): + """Test initialization of Newton deformable objects.""" + num_cubes = 2 + cube_object = generate_cubes_scene(num_cubes=num_cubes) + + sim.reset() + + assert cube_object.is_initialized + assert cube_object.num_instances == num_cubes + assert cube_object.max_sim_vertices_per_body > 0 + + particles_per_body = cube_object.max_sim_vertices_per_body + + # nodal_state_w: (N, V, 6) + nodal_state = cube_object.data.nodal_state_w.torch + assert nodal_state.shape == (num_cubes, particles_per_body, 6) + + # nodal_pos_w: (N, V, 3) + nodal_pos = cube_object.data.nodal_pos_w.torch + assert nodal_pos.shape == (num_cubes, particles_per_body, 3) + + # nodal_vel_w: (N, V, 3) + nodal_vel = cube_object.data.nodal_vel_w.torch + assert nodal_vel.shape == (num_cubes, particles_per_body, 3) + + # root_pos_w: (N, 3) + root_pos = cube_object.data.root_pos_w.torch + assert root_pos.shape == (num_cubes, 3) + + # root_vel_w: (N, 3) + root_vel = cube_object.data.root_vel_w.torch + assert root_vel.shape == (num_cubes, 3) + + +def test_surface_initialization_and_freefall(sim): + """Test initialization and stepping for surface deformable objects.""" + num_cloths = 2 + cloth_object = generate_cloth_scene(num_cloths=num_cloths, height=5.0) + + sim.reset() + + assert cloth_object.is_initialized + assert cloth_object.num_instances == num_cloths + assert cloth_object._deformable_type == "surface" + assert cloth_object.max_sim_vertices_per_body > 0 + assert cloth_object.data.nodal_pos_w.torch.shape == ( + num_cloths, + cloth_object.max_sim_vertices_per_body, + 3, + ) + + initial_root_z = cloth_object.data.root_pos_w.torch[:, 2].clone() + for _ in range(5): + sim.step() + cloth_object.update(sim.cfg.dt) + + assert torch.all(cloth_object.data.root_pos_w.torch[:, 2] < initial_root_z) + + +def test_set_nodal_state(sim): + """Test setting the state of the deformable object.""" + num_cubes = 2 + cube_object = generate_cubes_scene(num_cubes=num_cubes) + + sim.reset() + + for state_type_to_randomize in ["nodal_pos_w", "nodal_vel_w"]: + state_dict = { + "nodal_pos_w": torch.zeros_like(cube_object.data.nodal_pos_w.torch), + "nodal_vel_w": torch.zeros_like(cube_object.data.nodal_vel_w.torch), + } + + for _ in range(5): + state_dict[state_type_to_randomize] = torch.randn( + num_cubes, cube_object.max_sim_vertices_per_body, 3, device=sim.device + ) + + for _ in range(5): + nodal_state = torch.cat( + [ + state_dict["nodal_pos_w"], + state_dict["nodal_vel_w"], + ], + dim=-1, + ) + cube_object.write_nodal_state_to_sim_index(nodal_state) + + torch.testing.assert_close(cube_object.data.nodal_state_w.torch, nodal_state, rtol=1e-5, atol=1e-5) + + sim.step() + cube_object.update(sim.cfg.dt) + + +def test_write_partial_env_ids(sim): + """Test writing to a subset of environments using env_ids.""" + num_cubes = 2 + cube_object = generate_cubes_scene(num_cubes=num_cubes) + + sim.reset() + + particles_per_body = cube_object.max_sim_vertices_per_body + default_pos = cube_object.data.nodal_pos_w.torch.clone() + + # Write new positions only for env 0 + new_pos = torch.randn(1, particles_per_body, 3, device=sim.device) + cube_object.write_nodal_pos_to_sim_index(new_pos, env_ids=torch.tensor([0], device=sim.device)) + cube_object.update(sim.cfg.dt) + + read_pos = cube_object.data.nodal_pos_w.torch + + # env 0 should have new positions + torch.testing.assert_close(read_pos[0], new_pos[0], rtol=1e-5, atol=1e-5) + + # other envs should be unchanged + torch.testing.assert_close(read_pos[1:], default_pos[1:], rtol=1e-5, atol=1e-5) + + +def test_write_partial_velocity_env_ids(sim): + """Test writing nodal velocities to a subset of environments.""" + num_cubes = 4 + cube_object = generate_cubes_scene(num_cubes=num_cubes) + + sim.reset() + + particles_per_body = cube_object.max_sim_vertices_per_body + default_vel = cube_object.data.nodal_vel_w.torch.clone() + + env_ids = torch.tensor([1], device=sim.device) + new_vel = torch.full((1, particles_per_body, 3), 0.25, device=sim.device) + new_vel[..., 2] = 1.0 + cube_object.write_nodal_velocity_to_sim_index(new_vel, env_ids=env_ids) + cube_object.update(sim.cfg.dt) + + read_vel = cube_object.data.nodal_vel_w.torch + torch.testing.assert_close(read_vel[1], new_vel[0], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(cube_object.data.root_vel_w.torch[1], new_vel[0].mean(dim=0), rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_vel[0], default_vel[0], rtol=1e-5, atol=1e-5) + if num_cubes > 2: + torch.testing.assert_close(read_vel[2:], default_vel[2:], rtol=1e-5, atol=1e-5) + + +def test_full_data_writes_selected_env(sim): + """Test full-sized write buffers with selected environment ids.""" + num_cubes = 3 + cube_object = generate_cubes_scene(num_cubes=num_cubes) + + sim.reset() + + particles_per_body = cube_object.max_sim_vertices_per_body + env_ids = torch.tensor([1], device=sim.device) + + default_pos = cube_object.data.nodal_pos_w.torch.clone() + full_pos = default_pos + torch.linspace(0.1, 0.3, num_cubes, device=sim.device).view(num_cubes, 1, 1) + cube_object.write_nodal_pos_to_sim_index(full_pos, env_ids=env_ids, full_data=True) + cube_object.update(sim.cfg.dt) + + read_pos = cube_object.data.nodal_pos_w.torch + torch.testing.assert_close(read_pos[1], full_pos[1], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_pos[0], default_pos[0], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_pos[2], default_pos[2], rtol=1e-5, atol=1e-5) + + default_vel = cube_object.data.nodal_vel_w.torch.clone() + full_vel = torch.zeros(num_cubes, particles_per_body, 3, device=sim.device) + full_vel[0, :, 0] = 0.5 + full_vel[1, :, 1] = 0.75 + full_vel[2, :, 2] = 1.0 + cube_object.write_nodal_velocity_to_sim_index(full_vel, env_ids=env_ids, full_data=True) + cube_object.update(sim.cfg.dt) + + read_vel = cube_object.data.nodal_vel_w.torch + torch.testing.assert_close(read_vel[1], full_vel[1], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_vel[0], default_vel[0], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_vel[2], default_vel[2], rtol=1e-5, atol=1e-5) + + default_targets = cube_object.data.nodal_kinematic_target.torch.clone() + full_targets = torch.zeros(num_cubes, particles_per_body, 4, device=sim.device) + full_targets[..., :3] = cube_object.data.default_nodal_state_w.torch[..., :3] + full_targets[..., 3] = 1.0 + full_targets[1, :, :3] += torch.tensor([0.0, 0.0, 0.1], device=sim.device) + full_targets[1, :, 3] = 0.0 + cube_object.write_nodal_kinematic_target_to_sim_index(full_targets, env_ids=env_ids, full_data=True) + + read_targets = cube_object.data.nodal_kinematic_target.torch + torch.testing.assert_close(read_targets[1], full_targets[1], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_targets[0], default_targets[0], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_targets[2], default_targets[2], rtol=1e-5, atol=1e-5) + + +def test_kinematic_target_partial_env_ids_with_warp_input(sim): + """Test indexed kinematic target writes with device-native input arrays.""" + num_cubes = 3 + cube_object = generate_cubes_scene(num_cubes=num_cubes) + + sim.reset() + + particles_per_body = cube_object.max_sim_vertices_per_body + env_ids = torch.tensor([2], device=sim.device) + + default_targets = cube_object.data.nodal_kinematic_target.torch.clone() + targets = torch.zeros(1, particles_per_body, 4, device=sim.device) + targets[0, :, :3] = cube_object.data.default_nodal_state_w.torch[2, :, :3] + targets[0, :, :3] += torch.tensor([0.0, 0.0, 0.1], device=sim.device) + targets[0, :, 3] = 0.0 + + cube_object.write_nodal_kinematic_target_to_sim_index( + wp.from_torch(targets.contiguous(), dtype=wp.vec4f), env_ids=env_ids + ) + + read_targets = cube_object.data.nodal_kinematic_target.torch + torch.testing.assert_close(read_targets[2], targets[0], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_targets[0], default_targets[0], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_targets[1], default_targets[1], rtol=1e-5, atol=1e-5) + + +def test_mask_writes_selected_env(sim): + """Test full-sized write buffers with selected environment masks.""" + num_cubes = 3 + cube_object = generate_cubes_scene(num_cubes=num_cubes) + + sim.reset() + + env_mask = wp.array([False, True, False], dtype=wp.bool, device=sim.device) + + default_state = cube_object.data.nodal_state_w.torch.clone() + full_state = default_state.clone() + full_state[:, :, :3] += torch.tensor([10.0, 10.0, 10.0], device=sim.device) + full_state[:, :, 3:] = 10.0 + full_state[1, :, :3] = default_state[1, :, :3] + torch.tensor([0.1, 0.2, 0.3], device=sim.device) + full_state[1, :, 3:] = torch.tensor([0.4, 0.5, 0.6], device=sim.device) + cube_object.write_nodal_state_to_sim_mask(full_state, env_mask=env_mask) + cube_object.update(sim.cfg.dt) + + read_state = cube_object.data.nodal_state_w.torch + torch.testing.assert_close(read_state[1], full_state[1], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_state[0], default_state[0], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_state[2], default_state[2], rtol=1e-5, atol=1e-5) + + pos_before = cube_object.data.nodal_pos_w.torch.clone() + full_pos = pos_before + torch.tensor([5.0, 5.0, 5.0], device=sim.device) + full_pos[1] = pos_before[1] + torch.tensor([0.0, -0.1, 0.2], device=sim.device) + cube_object.write_nodal_pos_to_sim_mask(full_pos, env_mask=env_mask) + cube_object.update(sim.cfg.dt) + + read_pos = cube_object.data.nodal_pos_w.torch + torch.testing.assert_close(read_pos[1], full_pos[1], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_pos[0], pos_before[0], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_pos[2], pos_before[2], rtol=1e-5, atol=1e-5) + + vel_before = cube_object.data.nodal_vel_w.torch.clone() + full_vel = torch.full_like(vel_before, 7.0) + full_vel[1] = torch.tensor([0.7, 0.8, 0.9], device=sim.device) + cube_object.write_nodal_velocity_to_sim_mask(full_vel, env_mask=env_mask) + cube_object.update(sim.cfg.dt) + + read_vel = cube_object.data.nodal_vel_w.torch + torch.testing.assert_close(read_vel[1], full_vel[1], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_vel[0], vel_before[0], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_vel[2], vel_before[2], rtol=1e-5, atol=1e-5) + + default_targets = cube_object.data.nodal_kinematic_target.torch.clone() + full_targets = default_targets.clone() + full_targets[:, :, :3] = 3.0 + full_targets[:, :, 3] = 0.0 + full_targets[1, :, :3] = read_pos[1] + torch.tensor([0.0, 0.0, 0.1], device=sim.device) + cube_object.write_nodal_kinematic_target_to_sim_mask(full_targets, env_mask=env_mask) + + read_targets = cube_object.data.nodal_kinematic_target.torch + torch.testing.assert_close(read_targets[1], full_targets[1], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_targets[0], default_targets[0], rtol=1e-5, atol=1e-5) + torch.testing.assert_close(read_targets[2], default_targets[2], rtol=1e-5, atol=1e-5) + + +@pytest.mark.parametrize( + "num_cubes, randomize_pos, randomize_rot", + [ + (1, False, False), + (1, True, False), + (1, False, True), + (2, True, True), + ], +) +@flaky(max_runs=3, min_passes=1) +def test_set_nodal_state_with_applied_transform(num_cubes, randomize_pos, randomize_rot): + """Test setting the state of the deformable object with applied transform. + + Applies random position/rotation transforms to the default nodal state, + writes it to simulation, steps with no gravity, and verifies the mean + nodal position (root_pos_w) matches the expected transformed centroid. + """ + cfg = SimulationCfg( + physics=NewtonCfg( + solver_cfg=VBDSolverCfg(iterations=3), + num_substeps=2, + ), + ) + cfg.device = "cuda:0" + cfg.gravity = (0.0, 0.0, 0.0) + + with build_simulation_context(device="cuda:0", sim_cfg=cfg, auto_add_lighting=True) as sim: + sim._app_control_on_stop_handle = None + cube_object = generate_cubes_scene(num_cubes=num_cubes, height=5.0) + sim.reset() + + for _ in range(5): + nodal_state = cube_object.data.default_nodal_state_w.torch.clone() + mean_nodal_pos_default = nodal_state[..., :3].mean(dim=1) + + if randomize_pos: + pos_w = 0.5 * torch.rand(cube_object.num_instances, 3, device=sim.device) + pos_w[:, 2] += 0.5 + else: + pos_w = None + if randomize_rot: + quat_w = math_utils.random_orientation(cube_object.num_instances, device=sim.device) + else: + quat_w = None + + # transform_nodal_pos: center, rotate, translate, un-center + nodal_pos = nodal_state[..., :3] + mean_pos = nodal_pos.mean(dim=1, keepdim=True) + centered = nodal_pos - mean_pos + nodal_state[..., :3] = math_utils.transform_points(centered, pos_w, quat_w) + mean_pos + mean_nodal_pos_init = nodal_state[..., :3].mean(dim=1) + + if pos_w is None: + torch.testing.assert_close(mean_nodal_pos_init, mean_nodal_pos_default, rtol=1e-5, atol=1e-5) + else: + torch.testing.assert_close(mean_nodal_pos_init, mean_nodal_pos_default + pos_w, rtol=1e-5, atol=1e-5) + + cube_object.write_nodal_state_to_sim_index(nodal_state) + + for _ in range(50): + sim.step() + cube_object.update(sim.cfg.dt) + + torch.testing.assert_close(cube_object.data.root_pos_w.torch, mean_nodal_pos_init, rtol=1e-4, atol=1e-4) + + +def test_freefall_analytical(sim): + """Test that one step of free-fall matches the inertia target prediction. + + VBD computes an inertia target per substep (h = sub_dt):: + + v_new = v + g * h + x_new = x + v_new * h + + then optimizes elastic + contact potentials around it. For free-fall + (no contacts, negligible elastic forces over one step), the final + position equals the inertia target. + + Starting from rest (v_0 = 0) with N substeps of h = dt/N: + + substep 1: v_1 = g*h, dx_1 = g*h^2 (1 * g*h^2) + substep 2: v_2 = 2*g*h, dx_2 = 2*g*h^2 (2 * g*h^2) + ... + substep k: v_k = k*g*h, dx_k = k*g*h^2 (k * g*h^2) + + total dz = g*h^2 * (1 + 2 + ... + N) = g*h^2 * N*(N+1)/2 + + """ + g = -9.81 + dt = 1.0 / 60.0 + num_substeps = 2 + sub_dt = dt / num_substeps + expected_dz = g * sub_dt**2 * num_substeps * (num_substeps + 1) / 2 + + cube_object = generate_cubes_scene(num_cubes=1, height=5.0) + sim.reset() + + x0 = cube_object.data.nodal_pos_w.torch.clone() + sim.step() + cube_object.update(sim.cfg.dt) + x1 = cube_object.data.nodal_pos_w.torch + + dz = x1[..., 2] - x0[..., 2] + # Every vertex should have the same Z displacement under uniform gravity + torch.testing.assert_close(dz, torch.full_like(dz, expected_dz), rtol=1e-2, atol=1e-5) + + +def test_nodal_pos_reads_current_state_after_odd_substep_swap(): + """Test deformable reads use the current Newton state after state swapping.""" + cfg = SimulationCfg( + physics=NewtonCfg( + solver_cfg=VBDSolverCfg(iterations=3), + num_substeps=1, + use_cuda_graph=False, + ), + ) + cfg.device = "cuda:0" + cfg.gravity = (0.0, 0.0, -9.81) + + with build_simulation_context(device="cuda:0", sim_cfg=cfg, auto_add_lighting=True) as sim: + sim._app_control_on_stop_handle = None + cube_object = generate_cubes_scene(num_cubes=1, height=5.0) + + sim.reset() + + initial_pos = cube_object.data.nodal_pos_w.torch.clone() + sim.step() + cube_object.update(sim.cfg.dt) + + stepped_pos = cube_object.data.nodal_pos_w.torch + assert torch.all(stepped_pos[..., 2] < initial_pos[..., 2]) + + +def test_set_kinematic_targets(sim): + """Test setting kinematic targets for the deformable object. + + Env 0 is kinematically constrained (all vertices pinned at default positions, + flag=0). Other envs are free (flag=1) and fall under gravity. After several + steps, env 0 should stay in place while the others have fallen. + """ + num_cubes = 4 + cube_object = generate_cubes_scene(num_cubes=num_cubes, height=5.0) + + sim.reset() + + particles_per_body = cube_object.max_sim_vertices_per_body + default_state = cube_object.data.default_nodal_state_w.torch + default_pos = default_state[..., :3].clone() + + # Build kinematic target buffer: (N, V, 4) = [x, y, z, flag] + nodal_kinematic_targets = torch.zeros(num_cubes, particles_per_body, 4, device=sim.device) + + for _ in range(5): + # Restore default state + cube_object.write_nodal_state_to_sim_index(default_state) + + # Env 0: pin all vertices at default positions (flag=0 = kinematic) + nodal_kinematic_targets[0, :, :3] = default_pos[0] + nodal_kinematic_targets[0, :, 3] = 0.0 + + # Other envs: free (flag=1) + nodal_kinematic_targets[1:, :, :3] = default_pos[1:] + nodal_kinematic_targets[1:, :, 3] = 1.0 + + cube_object.write_nodal_kinematic_target_to_sim_index(nodal_kinematic_targets) + + for _ in range(20): + cube_object.write_data_to_sim() + sim.step() + cube_object.update(sim.cfg.dt) + + # Env 0 should stay at default position (kinematically constrained) + torch.testing.assert_close( + cube_object.data.nodal_pos_w.torch[0], + default_pos[0], + rtol=1e-5, + atol=1e-5, + ) + + # Other envs should have fallen + final_root_z = cube_object.data.root_pos_w.torch[1:, 2] + default_root_z = default_pos[1:, :, 2].mean(dim=1) + assert torch.all(final_root_z < default_root_z) + + +def test_kinematic_target_release_restores_free_motion(sim): + """Test that a pinned deformable falls again after kinematic targets are released.""" + cube_object = generate_cubes_scene(num_cubes=1, height=5.0) + + sim.reset() + + particles_per_body = cube_object.max_sim_vertices_per_body + default_state = cube_object.data.default_nodal_state_w.torch + default_pos = default_state[..., :3].clone() + + targets = torch.zeros(1, particles_per_body, 4, device=sim.device) + targets[0, :, :3] = default_pos[0] + targets[0, :, 3] = 0.0 + cube_object.write_nodal_kinematic_target_to_sim_index(targets) + + for _ in range(5): + cube_object.write_data_to_sim() + sim.step() + cube_object.update(sim.cfg.dt) + + torch.testing.assert_close(cube_object.data.nodal_pos_w.torch[0], default_pos[0], rtol=1e-5, atol=1e-5) + + targets[0, :, 3] = 1.0 + cube_object.write_nodal_kinematic_target_to_sim_index(targets) + + for _ in range(20): + cube_object.write_data_to_sim() + sim.step() + cube_object.update(sim.cfg.dt) + + assert cube_object.data.root_pos_w.torch[0, 2] < default_pos[0, :, 2].mean() + + +def test_multiple_deformable_assets_do_not_alias(sim): + """Test independent writes for two different deformable assets in one scene.""" + cuboid, cylinder = generate_cuboid_and_cylinder_scene(height=2.0) + + sim.reset() + + cuboid_default = cuboid.data.nodal_pos_w.torch.clone() + cylinder_default = cylinder.data.nodal_pos_w.torch.clone() + + cuboid_pos = cuboid_default + torch.tensor([0.15, -0.05, 0.1], device=sim.device) + cuboid.write_nodal_pos_to_sim_index(cuboid_pos) + cuboid.update(sim.cfg.dt) + cylinder.update(sim.cfg.dt) + + torch.testing.assert_close(cuboid.data.nodal_pos_w.torch, cuboid_pos, rtol=1e-5, atol=1e-5) + torch.testing.assert_close(cylinder.data.nodal_pos_w.torch, cylinder_default, rtol=1e-5, atol=1e-5) + assert cuboid._recorded_particle_offsets != cylinder._recorded_particle_offsets + + +def test_rebind_after_sim_reset(sim): + """Test that deformable write paths remain valid after a simulation reset.""" + cube_object = generate_cubes_scene(num_cubes=1, height=2.0) + + sim.reset() + + initial_pos = cube_object.data.default_nodal_state_w.torch[..., :3].clone() + first_pos = initial_pos + torch.tensor([0.1, 0.0, 0.0], device=sim.device) + cube_object.write_nodal_pos_to_sim_index(first_pos) + cube_object.update(sim.cfg.dt) + torch.testing.assert_close(cube_object.data.nodal_pos_w.torch, first_pos, rtol=1e-5, atol=1e-5) + + sim.reset() + + second_pos = initial_pos + torch.tensor([0.0, -0.1, 0.2], device=sim.device) + cube_object.write_nodal_pos_to_sim_index(second_pos) + cube_object.update(sim.cfg.dt) + torch.testing.assert_close(cube_object.data.nodal_pos_w.torch, second_pos, rtol=1e-5, atol=1e-5) + + +def test_write_shape_validation(sim): + """Test public write APIs reject wrong tensor shapes.""" + cube_object = generate_cubes_scene(num_cubes=2) + + sim.reset() + + particles_per_body = cube_object.max_sim_vertices_per_body + wrong_pos = torch.zeros(particles_per_body, 3, device=sim.device) + wrong_vel = torch.zeros(1, particles_per_body, 2, device=sim.device) + wrong_targets = torch.zeros(2, particles_per_body, 3, device=sim.device) + + with pytest.raises(AssertionError, match="Shape mismatch"): + cube_object.write_nodal_pos_to_sim_index(wrong_pos) + with pytest.raises(AssertionError, match="Shape mismatch"): + cube_object.write_nodal_velocity_to_sim_index(wrong_vel, env_ids=torch.tensor([0], device=sim.device)) + with pytest.raises(AssertionError, match="Shape mismatch"): + cube_object.write_nodal_kinematic_target_to_sim_index(wrong_targets) diff --git a/source/isaaclab_contrib/test/deformable/test_rigid_deformable_coupling.py b/source/isaaclab_contrib/test/deformable/test_rigid_deformable_coupling.py new file mode 100644 index 000000000000..4b00c0d2e371 --- /dev/null +++ b/source/isaaclab_contrib/test/deformable/test_rigid_deformable_coupling.py @@ -0,0 +1,332 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +# ignore private usage of variables warning +# pyright: reportPrivateUsage=none + + +"""Launch Isaac Sim Simulator first.""" + +from isaaclab.app import AppLauncher + +# launch omniverse app +simulation_app = AppLauncher(headless=True).app + +"""Rest everything follows.""" + +import pytest +import torch +from isaaclab_newton.assets import Articulation, RigidObject +from isaaclab_newton.physics import FeatherstoneSolverCfg, MJWarpSolverCfg, NewtonCfg +from isaaclab_newton.sim.schemas import NewtonDeformableBodyPropertiesCfg +from isaaclab_newton.sim.spawners.materials import NewtonDeformableBodyMaterialCfg + +import isaaclab.sim as sim_utils +from isaaclab.assets import RigidObjectCfg +from isaaclab.assets.deformable_object import DeformableObjectCfg +from isaaclab.sim import SimulationCfg, build_simulation_context + +from isaaclab_contrib.deformable import ( + CoupledFeatherstoneVBDSolverCfg, + CoupledMJWarpVBDSolverCfg, + DeformableObject, + VBDSolverCfg, +) + +from isaaclab_assets import FRANKA_PANDA_CFG # isort:skip + + +def _make_coupled_cfg(coupling_mode: str, rigid_solver: str = "mjwarp") -> SimulationCfg: + """Create a simulation config for a coupled rigid-deformable solver.""" + if rigid_solver == "mjwarp": + solver_cfg = CoupledMJWarpVBDSolverCfg( + rigid_solver_cfg=MJWarpSolverCfg( + njmax=40, + nconmax=20, + ls_iterations=20, + cone="pyramidal", + impratio=1, + ls_parallel=False, + integrator="implicitfast", + ), + soft_solver_cfg=VBDSolverCfg( + iterations=3, + integrate_with_external_rigid_solver=True, + particle_enable_self_contact=False, + particle_collision_detection_interval=-1, + ), + coupling_mode=coupling_mode, + ) + elif rigid_solver == "featherstone": + solver_cfg = CoupledFeatherstoneVBDSolverCfg( + rigid_solver_cfg=FeatherstoneSolverCfg(), + soft_solver_cfg=VBDSolverCfg( + iterations=3, + integrate_with_external_rigid_solver=True, + particle_enable_self_contact=False, + particle_collision_detection_interval=-1, + ), + coupling_mode=coupling_mode, + ) + else: + raise ValueError(f"Unknown rigid solver: {rigid_solver}") + + return SimulationCfg( + dt=1.0 / 60.0, + physics=NewtonCfg( + solver_cfg=solver_cfg, + num_substeps=5, + use_cuda_graph=True, + ), + ) + + +def _coupled_sim_context(cfg: SimulationCfg, device="cuda:0"): + """Helper to create a coupled solver simulation context.""" + cfg.device = device + return build_simulation_context(device=device, sim_cfg=cfg, auto_add_lighting=True) + + +@pytest.fixture +def sim(request): + """Create a coupled solver simulation context. + + Defaults to one-way coupling. Tests can parametrize this fixture with + ``"two_way"`` when both coupling paths should be exercised. + """ + param = getattr(request, "param", "one_way") + if isinstance(param, tuple): + rigid_solver, coupling_mode = param + else: + rigid_solver, coupling_mode = "mjwarp", param + with _coupled_sim_context(_make_coupled_cfg(coupling_mode, rigid_solver)) as sim: + sim._app_control_on_stop_handle = None + yield sim + + +def generate_robot_and_two_cubes( + colliding_cube_pos: tuple = (0.3, 0.0, 1.0), + free_cube_pos: tuple = (2.0, 0.0, 1.0), +) -> tuple[Articulation, DeformableObject, DeformableObject]: + """Generate a scene with one Franka robot and two deformable cubes. + + A single env contains a robot and two cube objects at different positions. + One cube is placed above the robot arm (will collide), the other is placed + far away (falls freely). + + Args: + colliding_cube_pos: Position of the cube above the robot arm. + free_cube_pos: Position of the cube that falls freely. + + Returns: + Tuple of (robot, colliding_cube, free_cube). + """ + sim_utils.create_prim("/World/env_0", "Xform", translation=(0.0, 0.0, 0.0)) + + cfg = sim_utils.GroundPlaneCfg() + cfg.func("/World/defaultGroundPlane", cfg) + + robot_cfg = FRANKA_PANDA_CFG.replace(prim_path="/World/env_.*/Robot") + robot = Articulation(robot_cfg) + + colliding_cube = DeformableObject( + cfg=DeformableObjectCfg( + prim_path="/World/env_.*/cube_collide", + spawn=sim_utils.MeshCuboidCfg( + size=(0.05, 0.05, 0.05), + deformable_props=NewtonDeformableBodyPropertiesCfg(), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.2, 0.8, 0.2)), + physics_material=NewtonDeformableBodyMaterialCfg( + density=500.0, + k_mu=1e5, + k_lambda=1e5, + particle_radius=0.005, + ), + ), + init_state=DeformableObjectCfg.InitialStateCfg(pos=colliding_cube_pos), + ) + ) + + free_cube = DeformableObject( + cfg=DeformableObjectCfg( + prim_path="/World/env_.*/cube_free", + spawn=sim_utils.MeshCuboidCfg( + size=(0.05, 0.05, 0.05), + deformable_props=NewtonDeformableBodyPropertiesCfg(), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.8, 0.2, 0.2)), + physics_material=NewtonDeformableBodyMaterialCfg( + density=500.0, + k_mu=1e4, + k_lambda=1e4, + particle_radius=0.005, + ), + ), + init_state=DeformableObjectCfg.InitialStateCfg(pos=free_cube_pos), + ) + ) + + return robot, colliding_cube, free_cube + + +def generate_lateral_rigid_and_deformable_cubes( + rigid_cube_pos: tuple = (0.0, 0.0, 1.0), + deformable_cube_pos: tuple = (-0.16, 0.0, 1.0), +) -> tuple[RigidObject, DeformableObject]: + """Generate rigid and deformable cubes arranged for lateral contact. + + Args: + rigid_cube_pos: Initial position of the rigid cube. + deformable_cube_pos: Initial position of the deformable cube. + + Returns: + Tuple of (rigid cube, deformable cube). + """ + sim_utils.create_prim("/World/env_0", "Xform", translation=(0.0, 0.0, 0.0)) + + rigid_cube = RigidObject( + cfg=RigidObjectCfg( + prim_path="/World/env_.*/rigid_cube", + spawn=sim_utils.CuboidCfg( + size=(0.2, 0.2, 0.2), + rigid_props=sim_utils.RigidBodyPropertiesCfg(), + mass_props=sim_utils.MassPropertiesCfg(mass=0.05), + collision_props=sim_utils.CollisionPropertiesCfg(), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.2, 0.2, 0.8)), + ), + init_state=RigidObjectCfg.InitialStateCfg(pos=rigid_cube_pos), + ) + ) + + deformable_cube = DeformableObject( + cfg=DeformableObjectCfg( + prim_path="/World/env_.*/deformable_cube", + spawn=sim_utils.MeshCuboidCfg( + size=(0.08, 0.08, 0.08), + deformable_props=NewtonDeformableBodyPropertiesCfg(), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.8, 0.2, 0.2)), + physics_material=NewtonDeformableBodyMaterialCfg( + density=1000.0, + k_mu=1e5, + k_lambda=1e5, + particle_radius=0.005, + ), + ), + init_state=DeformableObjectCfg.InitialStateCfg(pos=deformable_cube_pos), + ) + ) + + return rigid_cube, deformable_cube + + +@pytest.mark.parametrize( + "sim", + [("featherstone", "kinematic")], + indirect=True, + ids=["featherstone_kinematic"], +) +def test_smoke_featherstone_kinematic(sim): + """Smoke test: Featherstone + VBD kinematic coupling initializes and steps.""" + robot, colliding_cube, free_cube = generate_robot_and_two_cubes() + sim.reset() + + assert robot.is_initialized + assert colliding_cube.is_initialized + assert free_cube.is_initialized + + initial_z_collide = colliding_cube.data.root_pos_w.torch[0, 2].item() + initial_z_free = free_cube.data.root_pos_w.torch[0, 2].item() + + for _ in range(10): + sim.step() + robot.update(sim.cfg.dt) + colliding_cube.update(sim.cfg.dt) + free_cube.update(sim.cfg.dt) + + assert colliding_cube.data.root_pos_w.torch[0, 2].item() < initial_z_collide - 0.01 + assert free_cube.data.root_pos_w.torch[0, 2].item() < initial_z_free - 0.01 + + +def _run_lateral_rigid_cube_response(coupling_mode: str) -> float: + """Run a compact lateral contact scene and return rigid cube X displacement.""" + with _coupled_sim_context(_make_coupled_cfg(coupling_mode)) as sim: + sim._app_control_on_stop_handle = None + rigid_cube, deformable_cube = generate_lateral_rigid_and_deformable_cubes() + sim.reset() + + initial_rigid_x = rigid_cube.data.root_pos_w.torch[0, 0].item() + nodal_vel = torch.zeros_like(deformable_cube.data.nodal_vel_w.torch) + nodal_vel[..., 0] = 2.0 + deformable_cube.write_nodal_velocity_to_sim_index(nodal_vel) + + for _ in range(60): + sim.step() + rigid_cube.update(sim.cfg.dt) + deformable_cube.update(sim.cfg.dt) + + return rigid_cube.data.root_pos_w.torch[0, 0].item() - initial_rigid_x + + +def test_two_way_coupling_applies_reaction_to_rigid_body(): + """Test that two-way coupling laterally pushes a rigid body.""" + one_way_dx = _run_lateral_rigid_cube_response("one_way") + two_way_dx = _run_lateral_rigid_cube_response("two_way") + + assert abs(one_way_dx) < 1e-2 + assert two_way_dx > one_way_dx + 1e-2 + + +def test_deformable_deflected_by_rigid_contact(sim): + """Test that a cube falling onto the robot is deflected horizontally. + + Two cubes start at the same height (Z=1.0m). Cube 0 (env 0) is placed + above the robot arm at X=0.3m. Cube 1 (env 1) is shifted +2m in X away + from the robot so it falls freely. + + Expected timeline (dt=1/60s): + + - Steps 0-15: Both cubes in free-fall, identical Z trajectories. + No horizontal motion. Z drops from ~1.0m to ~0.65m. + - Step ~20: Cube 0 hits the robot arm at Z~0.54m. Contact deflects it + horizontally (X starts increasing). Cube 1 is still in free-fall. + - Step ~28: Cube 1 reaches the ground (Z~0.005m) and stops. Zero + horizontal displacement. + - Steps 20-40: Cube 0 continues falling while sliding off the arm, + gaining horizontal velocity. Reaches the ground around step 40. + - Steps 40-70: Cube 0 slides along the ground, decelerating due to + friction. Settles around X~1.0m. + - Steps 70+: Both cubes at rest on the ground. + + The test asserts that cube 0 has a significantly larger horizontal + displacement than cube 1 (which should be ~zero). + """ + robot, colliding_cube, free_cube = generate_robot_and_two_cubes( + colliding_cube_pos=(0.3, 0.0, 1.0), # above robot arm + free_cube_pos=(2.0, 0.0, 1.0), # far from robot, same height + ) + sim.reset() + + initial_xy_collide = colliding_cube.data.root_pos_w.torch[0, :2].clone() + initial_xy_free = free_cube.data.root_pos_w.torch[0, :2].clone() + + # Free-fall from 1.0m takes sqrt(2*1.0/9.81) ~ 0.45s ~ 27 steps at dt=1/60. + # Collision with the robot arm happens around step 20 (Z~0.5m). + # 120 steps (2s) gives ample time for collision, bounce, and settling. + for _ in range(120): + sim.step() + robot.update(sim.cfg.dt) + colliding_cube.update(sim.cfg.dt) + free_cube.update(sim.cfg.dt) + + final_xy_collide = colliding_cube.data.root_pos_w.torch[0, :2] + final_xy_free = free_cube.data.root_pos_w.torch[0, :2] + + displacement_collide = (final_xy_collide - initial_xy_collide).norm().item() + displacement_free = (final_xy_free - initial_xy_free).norm().item() + + # Colliding cube should be deflected; free cube should fall straight + assert displacement_collide > displacement_free + 0.01, ( + f"Colliding cube should be deflected more than free cube: " + f"collide={displacement_collide:.4f}, free={displacement_free:.4f}" + ) diff --git a/source/isaaclab_newton/changelog.d/mym-deformable-backend-split.minor.rst b/source/isaaclab_newton/changelog.d/mym-deformable-backend-split.minor.rst new file mode 100644 index 000000000000..3dade7d6fb9f --- /dev/null +++ b/source/isaaclab_newton/changelog.d/mym-deformable-backend-split.minor.rst @@ -0,0 +1,4 @@ +Added +^^^^^ + +* Added Newton-specific deformable property and material cfgs. diff --git a/source/isaaclab_newton/changelog.d/mym-deformable_experimental.minor.rst b/source/isaaclab_newton/changelog.d/mym-deformable_experimental.minor.rst new file mode 100644 index 000000000000..aacba4481af9 --- /dev/null +++ b/source/isaaclab_newton/changelog.d/mym-deformable_experimental.minor.rst @@ -0,0 +1,33 @@ +Added +^^^^^ + +* Added Newton deformable asset exports under + :mod:`isaaclab_newton.assets.deformable_object`. +* Added deformable registration hooks to Newton cloning so deformable assets can + be added per replicated world while their USD proxy meshes are skipped by the + Newton USD importer. +* Added Newton manager abstraction documentation for adding solver managers and + custom coupled solvers. + +Changed +^^^^^^^ + +* Changed Newton solver configuration exports so + :class:`~isaaclab_newton.physics.MJWarpSolverCfg`, + :class:`~isaaclab_newton.physics.XPBDSolverCfg`, + :class:`~isaaclab_newton.physics.FeatherstoneSolverCfg`, and + :class:`~isaaclab_newton.physics.KaminoSolverCfg` are provided from + :mod:`isaaclab_newton.physics.newton_manager_cfg`. +* Changed :class:`~isaaclab_newton.physics.NewtonCfg` to use + :class:`~isaaclab_newton.physics.MJWarpSolverCfg` as its explicit default + solver configuration. +* Changed :class:`~isaaclab_newton.physics.NewtonCfg` validation to reject + :class:`~isaaclab_newton.physics.MJWarpSolverCfg` configurations that combine + ``use_mujoco_contacts=True`` with ``collision_cfg``. Remove ``collision_cfg`` + or set ``use_mujoco_contacts=False``. + +Fixed +^^^^^ + +* Fixed Newton Fabric synchronization for deformable particle meshes and + particle-only scenes. diff --git a/source/isaaclab_newton/isaaclab_newton/assets/__init__.pyi b/source/isaaclab_newton/isaaclab_newton/assets/__init__.pyi index b9359445960c..5476609e4eac 100644 --- a/source/isaaclab_newton/isaaclab_newton/assets/__init__.pyi +++ b/source/isaaclab_newton/isaaclab_newton/assets/__init__.pyi @@ -6,6 +6,8 @@ __all__ = [ "Articulation", "ArticulationData", + "DeformableObject", + "DeformableObjectData", "RigidObject", "RigidObjectData", "RigidObjectCollection", @@ -15,3 +17,4 @@ __all__ = [ from .articulation import Articulation, ArticulationData from .rigid_object import RigidObject, RigidObjectData from .rigid_object_collection import RigidObjectCollection, RigidObjectCollectionData +from .deformable_object import DeformableObject, DeformableObjectData diff --git a/source/isaaclab_newton/isaaclab_newton/assets/articulation/articulation.py b/source/isaaclab_newton/isaaclab_newton/assets/articulation/articulation.py index 690065980945..f921001cb8cf 100644 --- a/source/isaaclab_newton/isaaclab_newton/assets/articulation/articulation.py +++ b/source/isaaclab_newton/isaaclab_newton/assets/articulation/articulation.py @@ -712,6 +712,7 @@ def write_root_velocity_to_sim_index( """Set the root center of mass velocity over selected environment indices into the simulation. The velocity comprises linear velocity (x, y, z) and angular velocity (x, y, z) in that order. + .. note:: This sets the velocity of the root's center of mass rather than the root's frame. @@ -738,6 +739,7 @@ def write_root_velocity_to_sim_mask( """Set the root center of mass velocity over selected environment mask into the simulation. The velocity comprises linear velocity (x, y, z) and angular velocity (x, y, z) in that order. + .. note:: This sets the velocity of the root's center of mass rather than the root's frame. @@ -764,6 +766,7 @@ def write_root_com_velocity_to_sim_index( """Set the root center of mass velocity over selected environment indices into the simulation. The velocity comprises linear velocity (x, y, z) and angular velocity (x, y, z) in that order. + .. note:: This sets the velocity of the root's center of mass rather than the root's frame. @@ -812,6 +815,7 @@ def write_root_com_velocity_to_sim_mask( """Set the root center of mass velocity over selected environment mask into the simulation. The velocity comprises linear velocity (x, y, z) and angular velocity (x, y, z) in that order. + .. note:: This sets the velocity of the root's center of mass rather than the root's frame. @@ -858,6 +862,7 @@ def write_root_link_velocity_to_sim_index( """Set the root link velocity over selected environment indices into the simulation. The velocity comprises linear velocity (x, y, z) and angular velocity (x, y, z) in that order. + .. note:: This sets the velocity of the root's frame rather than the root's center of mass. @@ -912,6 +917,7 @@ def write_root_link_velocity_to_sim_mask( """Set the root link velocity over selected environment mask into the simulation. The velocity comprises linear velocity (x, y, z) and angular velocity (x, y, z) in that order. + .. note:: This sets the velocity of the root's frame rather than the root's center of mass. diff --git a/source/isaaclab_newton/isaaclab_newton/assets/deformable_object/__init__.py b/source/isaaclab_newton/isaaclab_newton/assets/deformable_object/__init__.py new file mode 100644 index 000000000000..e14e0f6d52c5 --- /dev/null +++ b/source/isaaclab_newton/isaaclab_newton/assets/deformable_object/__init__.py @@ -0,0 +1,8 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +from isaaclab.utils.module import lazy_export + +lazy_export() diff --git a/source/isaaclab_newton/isaaclab_newton/assets/deformable_object/__init__.pyi b/source/isaaclab_newton/isaaclab_newton/assets/deformable_object/__init__.pyi new file mode 100644 index 000000000000..5878e22680c6 --- /dev/null +++ b/source/isaaclab_newton/isaaclab_newton/assets/deformable_object/__init__.pyi @@ -0,0 +1,12 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +__all__ = [ + "DeformableObject", + "DeformableObjectData", +] + +from isaaclab_contrib.deformable.deformable_object import DeformableObject +from isaaclab_contrib.deformable.deformable_object_data import DeformableObjectData diff --git a/source/isaaclab_newton/isaaclab_newton/cloner/newton_replicate.py b/source/isaaclab_newton/isaaclab_newton/cloner/newton_replicate.py index e2751e2274f4..e99b1eb7abdd 100644 --- a/source/isaaclab_newton/isaaclab_newton/cloner/newton_replicate.py +++ b/source/isaaclab_newton/isaaclab_newton/cloner/newton_replicate.py @@ -62,6 +62,25 @@ def _build_newton_builder_from_mapping( # The prototype is built from env_0 in absolute world coordinates. # add_builder xforms are deltas from env_0 so positions don't get double-counted. env0_pos = positions[0] + + # Deformable prim paths are handled by per_world_builder_hooks, not add_usd. + # Resolve the regex prim_path patterns to concrete env_0 paths so add_usd + # can skip them via ignore_paths. + import re + + _deformable_ignore_paths: list[str] = [] + if hasattr(NewtonManager, "_deformable_registry"): + for entry in NewtonManager._deformable_registry: + pat = re.compile(entry.prim_path.replace(".*", "[^/]*") + "$") + for src_path in sources: + # Check if any prim under this source matches the deformable pattern + prim = stage.GetPrimAtPath(src_path) + if prim.IsValid(): + for child in Usd.PrimRange(prim): + child_path = str(child.GetPath()) + if pat.match(child_path): + _deformable_ignore_paths.append(child_path) + protos: dict[str, ModelBuilder] = {} for src_path in sources: p = NewtonManager.create_builder(up_axis=up_axis) @@ -72,6 +91,7 @@ def _build_newton_builder_from_mapping( load_visual_shapes=True, skip_mesh_approximation=True, schema_resolvers=schema_resolvers, + ignore_paths=_deformable_ignore_paths if _deformable_ignore_paths else None, ) if simplify_meshes: p.approximate_meshes("convex_hull", keep_visual_shapes=True) @@ -107,9 +127,20 @@ def _build_newton_builder_from_mapping( local_site_map[label] = [[] for _ in range(num_worlds)] for proto_shape_idx in proto_shape_indices: local_site_map[label][col].append(offset + proto_shape_idx) + + # Run per-world builder hooks (e.g. deformable body registration). + if hasattr(NewtonManager, "_per_world_builder_hooks"): + for hook in NewtonManager._per_world_builder_hooks: + hook(builder, col, positions[col].tolist(), quaternions[col].tolist()) + # end the world context builder.end_world() + # Run post-replicate hooks (e.g. builder.color() for deformable coloring). + if hasattr(NewtonManager, "_post_replicate_hooks"): + for hook in NewtonManager._post_replicate_hooks: + hook(builder) + site_index_map = { **global_site_map, **{label: (None, per_world) for label, per_world in local_site_map.items()}, diff --git a/source/isaaclab_newton/isaaclab_newton/physics/newton_collision_cfg.py b/source/isaaclab_newton/isaaclab_newton/physics/newton_collision_cfg.py index 86014d13a885..9e75d3153308 100644 --- a/source/isaaclab_newton/isaaclab_newton/physics/newton_collision_cfg.py +++ b/source/isaaclab_newton/isaaclab_newton/physics/newton_collision_cfg.py @@ -19,9 +19,9 @@ class HydroelasticSDFCfg: Hydroelastic contacts generate distributed contact areas instead of point contacts, providing more realistic force distribution for manipulation and compliant surfaces. - For more details, see the `Newton Collisions Guide`_. + For more details, see the `Newton hydroelastic contacts guide`_. - .. _Newton Collisions Guide: https://newton-physics.github.io/newton/latest/concepts/collisions.html#hydroelastic-contacts + .. _Newton hydroelastic contacts guide: https://newton-physics.github.io/newton/latest/concepts/collisions.html#hydroelastic-contacts """ reduce_contacts: bool = True @@ -88,9 +88,9 @@ class NewtonCollisionPipelineCfg: - Mesh-mesh collision via SDF with contact reduction - Optional hydroelastic contact model for compliant surfaces - For more details, see the `Newton Collisions Guide`_ and `CollisionPipeline API`_. + For more details, see the `Newton collision pipeline guide`_ and `CollisionPipeline API`_. - .. _Newton Collisions Guide: https://newton-physics.github.io/newton/latest/concepts/collisions.html + .. _Newton collision pipeline guide: https://newton-physics.github.io/newton/latest/concepts/collisions.html .. _CollisionPipeline API: https://newton-physics.github.io/newton/api/_generated/newton.CollisionPipeline.html """ diff --git a/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py b/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py index 24a99aba4282..73f1ac535049 100644 --- a/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py +++ b/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py @@ -40,7 +40,7 @@ from isaaclab.utils.string import resolve_matching_names from isaaclab.utils.timer import Timer -from .newton_manager_cfg import NewtonCfg, NewtonShapeCfg, NewtonSolverCfg +from .newton_manager_cfg import NewtonCfg, NewtonShapeCfg if TYPE_CHECKING: from isaaclab.sim.simulation_context import SimulationContext @@ -74,6 +74,35 @@ def _set_fabric_transforms( fabric_transforms[i] = wp.transpose(wp.mat44d(wp.transform_to_matrix(transform))) +@wp.kernel(enable_backward=False) +def _sync_particle_points( + fabric_points: wp.fabricarrayarray(dtype=wp.vec3f), + fabric_world_matrices: wp.fabricarray(dtype=wp.mat44d), + offsets: wp.fabricarray(dtype=wp.uint32), + counts: wp.fabricarray(dtype=wp.uint32), + particle_q: wp.array(dtype=wp.vec3f), +): + """Write Newton particle positions into Fabric mesh point arrays as local-frame points. + + Newton stores particle positions in world space in ``state.particle_q``. The Fabric + ``points`` attribute on a ``UsdGeom.Mesh`` is local-space -- Kit multiplies by the + mesh prim's resolved ``omni:fabric:worldMatrix`` at render time. + + This kernel inverts the mesh prim's world matrix to convert each world-space particle + position into local-space before writing. + """ + i = wp.tid() + offset = int(offsets[i]) + num_points = int(counts[i]) + + # Un-transpose Fabric's stored matrix to get the standard homogeneous form + world_matrix = wp.transpose(wp.mat44f(fabric_world_matrices[i])) + inv_world_matrix = wp.inverse(world_matrix) + + for j in range(num_points): + fabric_points[i][j] = wp.transform_point(inv_world_matrix, particle_q[offset + j]) + + @wp.kernel(enable_backward=False) def _or_reset_masks_from_mask( env_mask: wp.array(dtype=wp.bool), @@ -217,6 +246,9 @@ class NewtonManager(PhysicsManager): _newton_index_attr = "newton:index" _clone_physics_only = False _transforms_dirty: bool = False + _particles_dirty: bool = False + _newton_particle_offset_attr = "newton:particleOffset" + _newton_particle_count_attr = "newton:particleCount" # cubric GPU transform hierarchy (replaces CPU update_world_xforms) _cubric = None @@ -307,6 +339,7 @@ def forward(cls) -> None: def pre_render(cls) -> None: """Flush deferred Fabric writes before cameras/visualizers read the scene.""" cls.sync_transforms_to_usd() + cls.sync_particles_to_usd() @classmethod def sync_transforms_to_usd(cls) -> None: @@ -406,15 +439,79 @@ def sync_transforms_to_usd(cls) -> None: except Exception: logger.exception("[NewtonManager] sync_transforms_to_usd FAILED") + @classmethod + def sync_particles_to_usd(cls) -> None: + """Write Newton particle_q to Fabric mesh point arrays for Kit viewport rendering. + + For each deformable body whose mesh prim carries a ``newton:particleOffset`` + attribute, this function copies the corresponding slice of ``state_0.particle_q`` + into the Fabric ``points`` array so the Kit viewport reflects the current + deformation. + + No-op when there is no ``_usdrt_stage``, no simulation state, or no + deformable bodies registered. + """ + if cls._usdrt_stage is None or cls._state_0 is None or cls._state_0.particle_q is None: + return + if not cls._particles_dirty: + return + pq = cls._state_0.particle_q + try: + import usdrt + + selection = cls._usdrt_stage.SelectPrims( + require_attrs=[ + (usdrt.Sdf.ValueTypeNames.Point3fArray, "points", usdrt.Usd.Access.ReadWrite), + (usdrt.Sdf.ValueTypeNames.UInt, cls._newton_particle_offset_attr, usdrt.Usd.Access.Read), + (usdrt.Sdf.ValueTypeNames.UInt, cls._newton_particle_count_attr, usdrt.Usd.Access.Read), + (usdrt.Sdf.ValueTypeNames.Matrix4d, "omni:fabric:worldMatrix", usdrt.Usd.Access.Read), + ], + device=str(PhysicsManager._device), + ) + if selection.GetCount() == 0: + return + fabric_points = wp.fabricarrayarray(data=selection, attrib="points", dtype=wp.vec3f) + fabric_offsets = wp.fabricarray(data=selection, attrib=cls._newton_particle_offset_attr) + fabric_counts = wp.fabricarray(data=selection, attrib=cls._newton_particle_count_attr) + fabric_world_matrices = wp.fabricarray(data=selection, attrib="omni:fabric:worldMatrix") + wp.launch( + _sync_particle_points, + dim=selection.GetCount(), + inputs=[fabric_points, fabric_world_matrices, fabric_offsets, fabric_counts, pq], + device=PhysicsManager._device, + ) + NewtonManager._particles_dirty = False + except Exception as exc: + logger.debug("[sync_particles_to_usd] %s", exc) + @classmethod def _mark_transforms_dirty(cls) -> None: - """Flag that physics state has changed and Fabric needs re-sync. + """Flag that rigid-body transforms have changed and Fabric needs re-sync. - Called by :meth:`_simulate` after stepping. The actual sync is deferred - to :meth:`sync_transforms_to_usd`, which runs at render cadence. + The actual sync is deferred to :meth:`sync_transforms_to_usd`, + which runs at render cadence via :meth:`pre_render`. """ NewtonManager._transforms_dirty = True + @classmethod + def _mark_particles_dirty(cls) -> None: + """Flag that particle positions have changed and Fabric needs re-sync. + + The actual sync is deferred to the particle sync callback (if registered), + which runs at render cadence via :meth:`pre_render`. + """ + NewtonManager._particles_dirty = True + + @classmethod + def _mark_state_dirty(cls) -> None: + """Flag that all physics state has changed and Fabric needs re-sync. + + Convenience method that marks both transforms and particles dirty. + Called by :meth:`_simulate` after stepping. + """ + cls._mark_transforms_dirty() + cls._mark_particles_dirty() + @classmethod def step(cls) -> None: """Step the physics simulation. @@ -500,7 +597,7 @@ def step(cls) -> None: PhysicsManager._sim_time += physics_dt if cls._usdrt_stage is not None: - cls._mark_transforms_dirty() + cls._mark_state_dirty() # Launch solver-specific debug logging after stepping. cls._log_solver_debug() @@ -547,9 +644,7 @@ def is_fabric_enabled(cls) -> bool: @classmethod def clear(cls): - """Clear all Newton-specific state (callbacks cleared by super().close()). - Start with solver specific cleanup.""" - cls._solver_specific_clear() + """Clear all Newton-specific state (callbacks cleared by super().close()).""" if cls._cubric is not None and cls._cubric_adapter is not None: cls._cubric.release_adapter(cls._cubric_adapter) NewtonManager._cubric = None @@ -586,6 +681,7 @@ def clear(cls): NewtonManager._newton_stage_path = None NewtonManager._usdrt_stage = None NewtonManager._transforms_dirty = False + NewtonManager._particles_dirty = False NewtonManager._up_axis = "Z" NewtonManager._visualization_scene_data = None NewtonManager._visualization_mapping = None @@ -596,6 +692,7 @@ def clear(cls): NewtonManager._pending_extended_state_attributes = set() NewtonManager._pending_extended_contact_attributes = set() NewtonManager._views = [] + cls._solver_specific_clear() @classmethod def set_builder(cls, builder: ModelBuilder) -> None: @@ -877,6 +974,7 @@ def start_simulation(cls) -> None: if cls._pending_extended_contact_attributes: cls._model.request_contact_attributes(*cls._pending_extended_contact_attributes) NewtonManager._pending_extended_contact_attributes = set() + NewtonManager._state_0 = cls._model.state() NewtonManager._state_1 = cls._model.state() NewtonManager._control = cls._model.control() @@ -902,23 +1000,29 @@ def start_simulation(cls) -> None: import usdrt body_paths = getattr(cls._model, "body_label", None) or getattr(cls._model, "body_key", None) - if body_paths is None: - raise RuntimeError("NewtonManager: model has no body_label/body_key, skipping USD/Fabric sync for RTX.") - NewtonManager._usdrt_stage = get_current_stage(fabric=True) - for i, prim_path in enumerate(body_paths): - prim = cls._usdrt_stage.GetPrimAtPath(prim_path) - prim.CreateAttribute(cls._newton_index_attr, usdrt.Sdf.ValueTypeNames.UInt, True) - prim.GetAttribute(cls._newton_index_attr).Set(i) - # Tag with PhysicsRigidBodyAPI so cubric's eRigidBody mode - # applies Inverse propagation (preserves Newton's world - # transforms and derives local) instead of Forward. - prim.AddAppliedSchema("PhysicsRigidBodyAPI") - xformable_prim = usdrt.Rt.Xformable(prim) - if not xformable_prim.HasWorldXform(): - xformable_prim.SetWorldXformFromUsd() - - cls._mark_transforms_dirty() - cls.sync_transforms_to_usd() + if not body_paths: + logger.warning( + "NewtonManager: model has no rigid bodies (body_label/body_key is empty). " + "USD/Fabric body sync for RTX is skipped. " + "Particle-only scenes (e.g. cloth) must register their own USD mesh update." + ) + NewtonManager._usdrt_stage = None + else: + NewtonManager._usdrt_stage = get_current_stage(fabric=True) + for i, prim_path in enumerate(body_paths): + prim = cls._usdrt_stage.GetPrimAtPath(prim_path) + prim.CreateAttribute(cls._newton_index_attr, usdrt.Sdf.ValueTypeNames.UInt, True) + prim.GetAttribute(cls._newton_index_attr).Set(i) + # Tag with PhysicsRigidBodyAPI so cubric's eRigidBody mode + # applies Inverse propagation (preserves Newton's world + # transforms and derives local) instead of Forward. + prim.AddAppliedSchema("PhysicsRigidBodyAPI") + xformable_prim = usdrt.Rt.Xformable(prim) + if not xformable_prim.HasWorldXform(): + xformable_prim.SetWorldXformFromUsd() + + cls._mark_transforms_dirty() + cls.sync_transforms_to_usd() @classmethod def instantiate_builder_from_stage(cls): @@ -1033,7 +1137,7 @@ def _initialize_contacts(cls) -> None: @classmethod @abstractmethod - def _build_solver(cls, model: Model, solver_cfg: NewtonSolverCfg) -> None: + def _build_solver(cls, model: Model, solver_cfg) -> None: """Construct the solver this manager owns and assign it onto the base class. Subclasses must populate the canonical :class:`NewtonManager` slots: @@ -1062,16 +1166,14 @@ def _build_solver(cls, model: Model, solver_cfg: NewtonSolverCfg) -> None: raise NotImplementedError("NewtonManager subclasses must implement _build_solver()") @classmethod - def _step_solver(cls, state_0: State, state_1: State, control: Control, substep_dt: float) -> None: + def _step_solver( + cls, state_0: State, state_1: State, control: Control, contacts: Contacts | None, substep_dt: float + ) -> None: """Run one solver substep. Default invokes :attr:`_solver` once. Subclasses can override to batch multiple solvers within a single substep. """ - # Only solvers that consume Newton collision-pipeline contacts receive ``_contacts`` here. Solvers with - # internal contact handling receive ``None`` even when ``_contacts`` is allocated for later reporting via - # ``solver.update_contacts`` (e.g. MuJoCo with ``use_mujoco_contacts=True``). - contacts = cls._contacts if cls._needs_collision_pipeline else None cls._solver.step(state_0, state_1, control, contacts, substep_dt) @classmethod @@ -1118,17 +1220,13 @@ def initialize_solver(cls) -> None: NewtonManager._solver_dt = cls.get_physics_dt() / cls._num_substeps NewtonManager._collision_cfg = cfg.collision_cfg # type: ignore[union-attr] - cls._build_solver( - cls._model, - cfg.solver_cfg, # type: ignore[union-attr] - ) + cls._build_solver(cls._model, cfg.solver_cfg) # type: ignore[union-attr] if NewtonManager._solver is None: raise RuntimeError( f"{cls.__name__}._build_solver did not assign NewtonManager._solver. " "Subclasses of NewtonManager must populate NewtonManager._solver, " "NewtonManager._use_single_state, and NewtonManager._needs_collision_pipeline." ) - cls._initialize_contacts() if cls._usdrt_stage is not None: @@ -1342,17 +1440,17 @@ def _run_solver_substeps(cls, contacts) -> None: """Run ``num_substeps`` solver iterations, handling double-buffered state swap.""" if cls._use_single_state: for _ in range(cls._num_substeps): - cls._solver.step(cls._state_0, cls._state_0, cls._control, contacts, cls._solver_dt) + cls._step_solver(cls._state_0, cls._state_0, cls._control, contacts, cls._solver_dt) cls._state_0.clear_forces() else: cfg = PhysicsManager._cfg need_copy_on_last = (cfg is not None and cfg.use_cuda_graph) and cls._num_substeps % 2 == 1 # type: ignore[union-attr] for i in range(cls._num_substeps): - cls._solver.step(cls._state_0, cls._state_1, cls._control, contacts, cls._solver_dt) + cls._step_solver(cls._state_0, cls._state_1, cls._control, contacts, cls._solver_dt) if need_copy_on_last and i == cls._num_substeps - 1: cls._state_0.assign(cls._state_1) else: - cls._state_0, cls._state_1 = cls._state_1, cls._state_0 + NewtonManager._state_0, NewtonManager._state_1 = cls._state_1, cls._state_0 cls._state_0.clear_forces() @classmethod @@ -1413,14 +1511,6 @@ def _simulate_physics_only(cls) -> None: cls._run_solver_substeps(contacts) cls._update_sensors(contacts) - @classmethod - def get_solver_convergence_steps(cls) -> dict[str, float | int]: - """Get the solver convergence steps. Needs to be implemented in solver-specific managers.""" - if hasattr(cls, "_get_solver_convergence_steps"): - return cls._get_solver_convergence_steps() - else: - raise NotImplementedError("NewtonManager subclasses must implement _get_solver_convergence_steps()") - # State accessors (used extensively by articulation/rigid object data) @classmethod def get_model(cls) -> Model: @@ -1521,8 +1611,8 @@ def _ensure_visualization_model(cls) -> None: device = PhysicsManager._device or "cpu" try: - cls._model = builder.finalize(device=device) - cls._state_0 = cls._model.state() + NewtonManager._model = builder.finalize(device=device) + NewtonManager._state_0 = cls._model.state() cls._model.num_envs = cls._num_envs replace_newton_shape_colors(cls._model) @@ -1531,8 +1621,8 @@ def _ensure_visualization_model(cls) -> None: "[NewtonManager] Failed to finalize the shadow Newton ModelBuilder for " "visualization (sim backend is PhysX)." ) - cls._model = None - cls._state_0 = None + NewtonManager._model = None + NewtonManager._state_0 = None @classmethod def _build_visualization_model_from_stage(cls, stage) -> ModelBuilder | None: diff --git a/source/isaaclab_newton/isaaclab_newton/sim/__init__.pyi b/source/isaaclab_newton/isaaclab_newton/sim/__init__.pyi index aac4c8327ccb..699fc15ad4df 100644 --- a/source/isaaclab_newton/isaaclab_newton/sim/__init__.pyi +++ b/source/isaaclab_newton/isaaclab_newton/sim/__init__.pyi @@ -4,7 +4,19 @@ # SPDX-License-Identifier: BSD-3-Clause __all__ = [ + "NewtonDeformableBodyPropertiesCfg", + "NewtonDeformableBodyMaterialCfg", + "NewtonDeformableMaterialCfg", + "NewtonSurfaceDeformableBodyMaterialCfg", + "schemas", + "spawners", "views", ] -from . import views +from . import schemas, spawners, views +from .schemas import NewtonDeformableBodyPropertiesCfg +from .spawners.materials import ( + NewtonDeformableBodyMaterialCfg, + NewtonDeformableMaterialCfg, + NewtonSurfaceDeformableBodyMaterialCfg, +) diff --git a/source/isaaclab_newton/isaaclab_newton/sim/schemas/__init__.py b/source/isaaclab_newton/isaaclab_newton/sim/schemas/__init__.py index 80f943ad46ee..e79e72e10dff 100644 --- a/source/isaaclab_newton/isaaclab_newton/sim/schemas/__init__.py +++ b/source/isaaclab_newton/isaaclab_newton/sim/schemas/__init__.py @@ -3,15 +3,8 @@ # # SPDX-License-Identifier: BSD-3-Clause -"""Newton and MuJoCo simulation schema configuration classes.""" +"""Sub-module containing Newton schema configuration exports.""" -from .schemas_cfg import ( - MujocoJointDrivePropertiesCfg, - MujocoRigidBodyPropertiesCfg, - NewtonArticulationRootPropertiesCfg, - NewtonCollisionPropertiesCfg, - NewtonJointDrivePropertiesCfg, - NewtonMaterialPropertiesCfg, - NewtonMeshCollisionPropertiesCfg, - NewtonRigidBodyPropertiesCfg, -) +from isaaclab.utils.module import lazy_export + +lazy_export() diff --git a/source/isaaclab_newton/isaaclab_newton/sim/schemas/__init__.pyi b/source/isaaclab_newton/isaaclab_newton/sim/schemas/__init__.pyi index 6dae7b8cf2b7..1ae2d74c217b 100644 --- a/source/isaaclab_newton/isaaclab_newton/sim/schemas/__init__.pyi +++ b/source/isaaclab_newton/isaaclab_newton/sim/schemas/__init__.pyi @@ -3,11 +3,24 @@ # # SPDX-License-Identifier: BSD-3-Clause +__all__ = [ + "MujocoJointDrivePropertiesCfg", + "MujocoRigidBodyPropertiesCfg", + "NewtonArticulationRootPropertiesCfg", + "NewtonCollisionPropertiesCfg", + "NewtonDeformableBodyPropertiesCfg", + "NewtonJointDrivePropertiesCfg", + "NewtonMaterialPropertiesCfg", + "NewtonMeshCollisionPropertiesCfg", + "NewtonRigidBodyPropertiesCfg", +] + from .schemas_cfg import ( MujocoJointDrivePropertiesCfg, MujocoRigidBodyPropertiesCfg, NewtonArticulationRootPropertiesCfg, NewtonCollisionPropertiesCfg, + NewtonDeformableBodyPropertiesCfg, NewtonJointDrivePropertiesCfg, NewtonMaterialPropertiesCfg, NewtonMeshCollisionPropertiesCfg, diff --git a/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py b/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py index d98fbc3a2368..db039306375c 100644 --- a/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py +++ b/source/isaaclab_newton/isaaclab_newton/sim/schemas/schemas_cfg.py @@ -10,6 +10,7 @@ from isaaclab.sim.schemas.schemas_cfg import ( ArticulationRootBaseCfg, CollisionBaseCfg, + DeformableBodyPropertiesBaseCfg, JointDriveBaseCfg, MeshCollisionBaseCfg, RigidBodyBaseCfg, @@ -39,6 +40,24 @@ class NewtonRigidBodyPropertiesCfg(RigidBodyBaseCfg): _usd_field_exceptions: ClassVar[dict] = {} +@configclass +class NewtonDeformableBodyPropertiesCfg(DeformableBodyPropertiesBaseCfg): + """Newton-specific properties to apply to a deformable body. + + Currently empty. Backend-specific fields can be added here when Newton exposes + a registered deformable body property schema. + + The ``newton:`` namespace is reserved here so future Newton-native + deformable-body fields can be added without an API change. + + See :meth:`~isaaclab.sim.schemas.modify_deformable_body_properties` for more information. + """ + + _usd_namespace: ClassVar[str | None] = "newton" + _usd_applied_schema: ClassVar[str | None] = None + _usd_field_exceptions: ClassVar[dict] = {} + + @configclass class MujocoRigidBodyPropertiesCfg(NewtonRigidBodyPropertiesCfg): """MuJoCo-solver-specific rigid body properties. diff --git a/source/isaaclab_newton/isaaclab_newton/sim/spawners/__init__.py b/source/isaaclab_newton/isaaclab_newton/sim/spawners/__init__.py new file mode 100644 index 000000000000..9e75b2442524 --- /dev/null +++ b/source/isaaclab_newton/isaaclab_newton/sim/spawners/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Newton spawner utilities.""" + +from isaaclab.utils.module import lazy_export + +lazy_export() diff --git a/source/isaaclab_newton/isaaclab_newton/sim/spawners/__init__.pyi b/source/isaaclab_newton/isaaclab_newton/sim/spawners/__init__.pyi new file mode 100644 index 000000000000..ad99e610b437 --- /dev/null +++ b/source/isaaclab_newton/isaaclab_newton/sim/spawners/__init__.pyi @@ -0,0 +1,10 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +__all__ = [ + "materials", +] + +from . import materials diff --git a/source/isaaclab_newton/isaaclab_newton/sim/spawners/materials/__init__.py b/source/isaaclab_newton/isaaclab_newton/sim/spawners/materials/__init__.py new file mode 100644 index 000000000000..7ed9dfa18e29 --- /dev/null +++ b/source/isaaclab_newton/isaaclab_newton/sim/spawners/materials/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Sub-module for Newton material configuration exports.""" + +from isaaclab.utils.module import lazy_export + +lazy_export() diff --git a/source/isaaclab_newton/isaaclab_newton/sim/spawners/materials/__init__.pyi b/source/isaaclab_newton/isaaclab_newton/sim/spawners/materials/__init__.pyi new file mode 100644 index 000000000000..c3f13216f781 --- /dev/null +++ b/source/isaaclab_newton/isaaclab_newton/sim/spawners/materials/__init__.pyi @@ -0,0 +1,18 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +__all__ = [ + "spawn_deformable_body_material", + "NewtonDeformableBodyMaterialCfg", + "NewtonDeformableMaterialCfg", + "NewtonSurfaceDeformableBodyMaterialCfg", +] + +from .physics_materials import spawn_deformable_body_material +from .physics_materials_cfg import ( + NewtonDeformableBodyMaterialCfg, + NewtonDeformableMaterialCfg, + NewtonSurfaceDeformableBodyMaterialCfg, +) diff --git a/source/isaaclab_newton/isaaclab_newton/sim/spawners/materials/physics_materials.py b/source/isaaclab_newton/isaaclab_newton/sim/spawners/materials/physics_materials.py new file mode 100644 index 000000000000..1ace5ae38675 --- /dev/null +++ b/source/isaaclab_newton/isaaclab_newton/sim/spawners/materials/physics_materials.py @@ -0,0 +1,10 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Newton-compatible deformable physics material spawning exports.""" + +from isaaclab.sim.spawners.materials.physics_materials import spawn_deformable_body_material + +__all__ = ["spawn_deformable_body_material"] diff --git a/source/isaaclab_newton/isaaclab_newton/sim/spawners/materials/physics_materials_cfg.py b/source/isaaclab_newton/isaaclab_newton/sim/spawners/materials/physics_materials_cfg.py new file mode 100644 index 000000000000..f7b63a8701c6 --- /dev/null +++ b/source/isaaclab_newton/isaaclab_newton/sim/spawners/materials/physics_materials_cfg.py @@ -0,0 +1,79 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +from __future__ import annotations + +from collections.abc import Callable +from typing import ClassVar + +from isaaclab.sim.spawners.materials.physics_materials_cfg import ( + DeformableBodyMaterialBaseCfg, + SurfaceDeformableBodyMaterialBaseCfg, +) +from isaaclab.utils.configclass import configclass + + +@configclass +class NewtonDeformableMaterialCfg: + """Newton-specific material properties for a deformable body. + + These properties are set with the prefix ``newton:``. + """ + + _usd_namespace: ClassVar[str | None] = "newton" + _usd_applied_schema: ClassVar[str | None] = None + _usd_field_exceptions: ClassVar[dict] = {} + + density: float = 1.0 + """The material density [kg/m^3]. Defaults to 1.0 kg/m^3.""" + + particle_radius: float = 0.008 + """Particle radius [m] used by the Newton backend.""" + + +@configclass +class NewtonDeformableBodyMaterialCfg(DeformableBodyMaterialBaseCfg, NewtonDeformableMaterialCfg): + """Newton-specific physics material parameters for volume deformable bodies.""" + + _usd_namespace: ClassVar[str | None] = "newton" + _usd_applied_schema: ClassVar[str | None] = None + _usd_field_exceptions: ClassVar[dict] = {} + + func: Callable | str = "isaaclab.sim.spawners.materials.physics_materials:spawn_deformable_body_material" + + k_mu: float = 1e5 + """First Lame material parameter [Pa]. Defaults to 1e5 Pa.""" + + k_lambda: float = 1e5 + """Second Lame material parameter [Pa]. Defaults to 1e5 Pa.""" + + k_damp: float = 0.0 + """Damping stiffness for tetrahedral elements [Pa*s]. Defaults to 0.0.""" + + +@configclass +class NewtonSurfaceDeformableBodyMaterialCfg(SurfaceDeformableBodyMaterialBaseCfg, NewtonDeformableMaterialCfg): + """Newton-specific physics material parameters for surface deformable bodies.""" + + _usd_namespace: ClassVar[str | None] = "newton" + _usd_applied_schema: ClassVar[str | None] = None + _usd_field_exceptions: ClassVar[dict] = {} + + func: Callable | str = "isaaclab.sim.spawners.materials.physics_materials:spawn_deformable_body_material" + + tri_ke: float = 1e4 + """Triangle area-preserving stiffness [Pa]. Used by Newton backend for cloth meshes.""" + + tri_ka: float = 1e4 + """Triangle area stiffness [Pa]. Used by Newton backend for cloth meshes.""" + + tri_kd: float = 1.5e-6 + """Triangle area damping [Pa*s]. Used by Newton backend for cloth meshes.""" + + edge_ke: float = 5.0 + """Bending stiffness [N*m]. Used by Newton backend for cloth meshes.""" + + edge_kd: float = 1e-2 + """Bending damping [N*m*s]. Used by Newton backend for cloth meshes.""" diff --git a/source/isaaclab_physx/changelog.d/mym-deformable-backend-split.minor.rst b/source/isaaclab_physx/changelog.d/mym-deformable-backend-split.minor.rst new file mode 100644 index 000000000000..0879ea0a90f9 --- /dev/null +++ b/source/isaaclab_physx/changelog.d/mym-deformable-backend-split.minor.rst @@ -0,0 +1,11 @@ +Added +^^^^^ + +* Added PhysX-specific deformable property and material cfgs. + +Deprecated +^^^^^^^^^^ + +* Deprecated generic PhysX deformable cfg aliases in favor of + ``PhysxDeformableBodyPropertiesCfg``, ``PhysxDeformableBodyMaterialCfg``, + and ``PhysxSurfaceDeformableBodyMaterialCfg``. diff --git a/source/isaaclab_physx/changelog.d/mym-deformable_experimental.major.rst b/source/isaaclab_physx/changelog.d/mym-deformable_experimental.major.rst new file mode 100644 index 000000000000..aa4a2dad99fd --- /dev/null +++ b/source/isaaclab_physx/changelog.d/mym-deformable_experimental.major.rst @@ -0,0 +1,24 @@ +Changed +^^^^^^^ + +* **Breaking:** Moved deformable body schema and material APIs from + :mod:`isaaclab_physx.sim` to :mod:`isaaclab.sim`, and moved deformable object + configuration from :mod:`isaaclab_physx.assets` to :mod:`isaaclab.assets`. + Import :class:`~isaaclab.sim.DeformableBodyPropertiesCfg`, + :func:`~isaaclab.sim.define_deformable_body_properties`, + :func:`~isaaclab.sim.modify_deformable_body_properties`, + :class:`~isaaclab.sim.DeformableObjectSpawnerCfg`, + :class:`~isaaclab.sim.DeformableBodyMaterialCfg`, + :class:`~isaaclab.sim.SurfaceDeformableBodyMaterialCfg`, and + :func:`~isaaclab.sim.spawn_deformable_body_material` from :mod:`isaaclab.sim` + instead of :mod:`isaaclab_physx.sim`; import + :class:`~isaaclab.assets.DeformableObjectCfg` from :mod:`isaaclab.assets` + instead of :mod:`isaaclab_physx.assets`. +* Changed PhysX deformable API documentation to direct users to the + backend-neutral :mod:`isaaclab.assets` and :mod:`isaaclab.sim` imports. + +Fixed +^^^^^ + +* Fixed :class:`~isaaclab_physx.assets.DeformableObject` state writer methods + to accept ``ProxyArray`` inputs without requiring manual conversion. diff --git a/source/isaaclab_physx/isaaclab_physx/assets/deformable_object/__init__.pyi b/source/isaaclab_physx/isaaclab_physx/assets/deformable_object/__init__.pyi index 5ed56fc4f384..8effe50b9d85 100644 --- a/source/isaaclab_physx/isaaclab_physx/assets/deformable_object/__init__.pyi +++ b/source/isaaclab_physx/isaaclab_physx/assets/deformable_object/__init__.pyi @@ -9,6 +9,7 @@ __all__ = [ "DeformableObjectData", ] +from isaaclab.assets.deformable_object import DeformableObjectCfg + from .deformable_object import DeformableObject -from .deformable_object_cfg import DeformableObjectCfg from .deformable_object_data import DeformableObjectData diff --git a/source/isaaclab_physx/isaaclab_physx/assets/deformable_object/deformable_object.py b/source/isaaclab_physx/isaaclab_physx/assets/deformable_object/deformable_object.py index a240cb8203b5..759e31d9fb87 100644 --- a/source/isaaclab_physx/isaaclab_physx/assets/deformable_object/deformable_object.py +++ b/source/isaaclab_physx/isaaclab_physx/assets/deformable_object/deformable_object.py @@ -180,7 +180,7 @@ def update(self, dt: float): def write_nodal_state_to_sim_index( self, - nodal_state: torch.Tensor | wp.array, + nodal_state: torch.Tensor | wp.array | ProxyArray, env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, full_data: bool = False, ) -> None: @@ -195,8 +195,10 @@ def write_nodal_state_to_sim_index( env_ids: Environment indices. If None, then all indices are used. full_data: Whether to expect full data. Defaults to False. """ - # Convert warp to torch if needed - if isinstance(nodal_state, wp.array): + # Convert array wrappers to torch for slicing into position and velocity views. + if isinstance(nodal_state, ProxyArray): + nodal_state = nodal_state.torch + elif isinstance(nodal_state, wp.array): nodal_state = wp.to_torch(nodal_state) # set into simulation self.write_nodal_pos_to_sim_index(nodal_state[..., :3], env_ids=env_ids, full_data=full_data) @@ -204,7 +206,7 @@ def write_nodal_state_to_sim_index( def write_nodal_state_to_sim_mask( self, - nodal_state: torch.Tensor | wp.array, + nodal_state: torch.Tensor | wp.array | ProxyArray, env_mask: wp.array | None = None, ) -> None: """Set the nodal state over selected environment mask into the simulation. @@ -225,7 +227,7 @@ def write_nodal_state_to_sim_mask( def write_nodal_pos_to_sim_index( self, - nodal_pos: torch.Tensor | wp.array, + nodal_pos: torch.Tensor | wp.array | ProxyArray, env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, full_data: bool = False, ) -> None: @@ -242,6 +244,8 @@ def write_nodal_pos_to_sim_index( """ # resolve env_ids env_ids = self._resolve_env_ids(env_ids) + if isinstance(nodal_pos, ProxyArray): + nodal_pos = nodal_pos.warp if full_data: self.assert_shape_and_dtype( nodal_pos, (self.num_instances, self.max_sim_vertices_per_body), wp.vec3f, "nodal_pos" @@ -271,7 +275,7 @@ def write_nodal_pos_to_sim_index( def write_nodal_pos_to_sim_mask( self, - nodal_pos: torch.Tensor | wp.array, + nodal_pos: torch.Tensor | wp.array | ProxyArray, env_mask: wp.array | None = None, ) -> None: """Set the nodal positions over selected environment mask into the simulation. @@ -292,7 +296,7 @@ def write_nodal_pos_to_sim_mask( def write_nodal_velocity_to_sim_index( self, - nodal_vel: torch.Tensor | wp.array, + nodal_vel: torch.Tensor | wp.array | ProxyArray, env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, full_data: bool = False, ) -> None: @@ -310,6 +314,8 @@ def write_nodal_velocity_to_sim_index( """ # resolve env_ids env_ids = self._resolve_env_ids(env_ids) + if isinstance(nodal_vel, ProxyArray): + nodal_vel = nodal_vel.warp if full_data: self.assert_shape_and_dtype( nodal_vel, (self.num_instances, self.max_sim_vertices_per_body), wp.vec3f, "nodal_vel" @@ -339,7 +345,7 @@ def write_nodal_velocity_to_sim_index( def write_nodal_velocity_to_sim_mask( self, - nodal_vel: torch.Tensor | wp.array, + nodal_vel: torch.Tensor | wp.array | ProxyArray, env_mask: wp.array | None = None, ) -> None: """Set the nodal velocity over selected environment mask into the simulation. @@ -361,7 +367,7 @@ def write_nodal_velocity_to_sim_mask( def write_nodal_kinematic_target_to_sim_index( self, - targets: torch.Tensor | wp.array, + targets: torch.Tensor | wp.array | ProxyArray, env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, full_data: bool = False, ) -> None: @@ -385,6 +391,8 @@ def write_nodal_kinematic_target_to_sim_index( # resolve env_ids env_ids = self._resolve_env_ids(env_ids) + if isinstance(targets, ProxyArray): + targets = targets.warp if full_data: self.assert_shape_and_dtype( targets, (self.num_instances, self.max_sim_vertices_per_body), wp.vec4f, "targets" @@ -403,7 +411,7 @@ def write_nodal_kinematic_target_to_sim_index( write_nodal_vec4f_to_buffer, dim=(env_ids.shape[0], self.max_sim_vertices_per_body), inputs=[targets, env_ids, full_data], - outputs=[self._data.nodal_kinematic_target], + outputs=[self._data.nodal_kinematic_target.warp], device=self.device, ) # set into simulation @@ -413,7 +421,7 @@ def write_nodal_kinematic_target_to_sim_index( def write_nodal_kinematic_target_to_sim_mask( self, - targets: torch.Tensor | wp.array, + targets: torch.Tensor | wp.array | ProxyArray, env_mask: wp.array | None = None, ) -> None: """Set the kinematic targets of the simulation mesh for the deformable bodies using mask. @@ -442,7 +450,7 @@ def write_nodal_kinematic_target_to_sim_mask( def write_nodal_state_to_sim( self, - nodal_state: torch.Tensor | wp.array, + nodal_state: torch.Tensor | wp.array | ProxyArray, env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, ) -> None: """Deprecated. Please use :meth:`write_nodal_state_to_sim_index` instead.""" @@ -455,7 +463,7 @@ def write_nodal_state_to_sim( def write_nodal_kinematic_target_to_sim( self, - targets: torch.Tensor | wp.array, + targets: torch.Tensor | wp.array | ProxyArray, env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, ) -> None: """Deprecated. Please use :meth:`write_nodal_kinematic_target_to_sim_index` instead.""" @@ -469,7 +477,7 @@ def write_nodal_kinematic_target_to_sim( def write_nodal_pos_to_sim( self, - nodal_pos: torch.Tensor | wp.array, + nodal_pos: torch.Tensor | wp.array | ProxyArray, env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, ) -> None: """Deprecated. Please use :meth:`write_nodal_pos_to_sim_index` instead.""" @@ -482,7 +490,7 @@ def write_nodal_pos_to_sim( def write_nodal_velocity_to_sim( self, - nodal_vel: torch.Tensor | wp.array, + nodal_vel: torch.Tensor | wp.array | ProxyArray, env_ids: Sequence[int] | torch.Tensor | wp.array | None = None, ) -> None: """Deprecated. Please use :meth:`write_nodal_velocity_to_sim_index` instead.""" diff --git a/source/isaaclab_physx/isaaclab_physx/assets/deformable_object/deformable_object_data.py b/source/isaaclab_physx/isaaclab_physx/assets/deformable_object/deformable_object_data.py index 30307c1b08ff..df22c8b061bd 100644 --- a/source/isaaclab_physx/isaaclab_physx/assets/deformable_object/deformable_object_data.py +++ b/source/isaaclab_physx/isaaclab_physx/assets/deformable_object/deformable_object_data.py @@ -156,7 +156,7 @@ def nodal_state_w(self) -> ProxyArray: wp.launch( compute_nodal_state_w, dim=(self._num_instances, self._max_sim_vertices), - inputs=[self.nodal_pos_w, self.nodal_vel_w], + inputs=[self.nodal_pos_w.warp, self.nodal_vel_w.warp], outputs=[self._nodal_state_w.data], device=self.device, ) @@ -180,7 +180,7 @@ def root_pos_w(self) -> ProxyArray: wp.launch( compute_mean_vec3f_over_vertices, dim=(self._num_instances,), - inputs=[self.nodal_pos_w, self._max_sim_vertices], + inputs=[self.nodal_pos_w.warp, self._max_sim_vertices], outputs=[self._root_pos_w.data], device=self.device, ) @@ -200,7 +200,7 @@ def root_vel_w(self) -> ProxyArray: wp.launch( compute_mean_vec3f_over_vertices, dim=(self._num_instances,), - inputs=[self.nodal_vel_w, self._max_sim_vertices], + inputs=[self.nodal_vel_w.warp, self._max_sim_vertices], outputs=[self._root_vel_w.data], device=self.device, ) diff --git a/source/isaaclab_physx/isaaclab_physx/sim/__init__.pyi b/source/isaaclab_physx/isaaclab_physx/sim/__init__.pyi index 9a522d96ff8f..b3a0bbfcd16e 100644 --- a/source/isaaclab_physx/isaaclab_physx/sim/__init__.pyi +++ b/source/isaaclab_physx/isaaclab_physx/sim/__init__.pyi @@ -8,12 +8,14 @@ __all__ = [ "modify_deformable_body_properties", "DeformableBodyPropertiesCfg", "JointDrivePropertiesCfg", + "OmniPhysicsDeformableBodyPropertiesCfg", + "PhysxDeformableBodyPropertiesCfg", "PhysxJointDrivePropertiesCfg", "PhysxRigidBodyPropertiesCfg", "RigidBodyPropertiesCfg", - "DeformableObjectSpawnerCfg", - "spawn_deformable_body_material", "DeformableBodyMaterialCfg", + "PhysxDeformableBodyMaterialCfg", + "PhysxSurfaceDeformableBodyMaterialCfg", "SurfaceDeformableBodyMaterialCfg", "views", ] @@ -23,14 +25,16 @@ from .schemas import ( modify_deformable_body_properties, DeformableBodyPropertiesCfg, JointDrivePropertiesCfg, + OmniPhysicsDeformableBodyPropertiesCfg, + PhysxDeformableBodyPropertiesCfg, PhysxJointDrivePropertiesCfg, PhysxRigidBodyPropertiesCfg, RigidBodyPropertiesCfg, ) from .spawners import ( - DeformableObjectSpawnerCfg, - spawn_deformable_body_material, DeformableBodyMaterialCfg, + PhysxDeformableBodyMaterialCfg, + PhysxSurfaceDeformableBodyMaterialCfg, SurfaceDeformableBodyMaterialCfg, ) from . import views diff --git a/source/isaaclab_physx/isaaclab_physx/sim/schemas/__init__.py b/source/isaaclab_physx/isaaclab_physx/sim/schemas/__init__.py index b0d1477483d6..47f0b1e8ae93 100644 --- a/source/isaaclab_physx/isaaclab_physx/sim/schemas/__init__.py +++ b/source/isaaclab_physx/isaaclab_physx/sim/schemas/__init__.py @@ -3,7 +3,7 @@ # # SPDX-License-Identifier: BSD-3-Clause -"""Sub-module containing utilities for schemas used in Omniverse for PhysX backend.""" +"""Sub-module containing PhysX schema configuration exports.""" from isaaclab.utils.module import lazy_export diff --git a/source/isaaclab_physx/isaaclab_physx/sim/schemas/__init__.pyi b/source/isaaclab_physx/isaaclab_physx/sim/schemas/__init__.pyi index 6d2a05bf803c..b542edf9f454 100644 --- a/source/isaaclab_physx/isaaclab_physx/sim/schemas/__init__.pyi +++ b/source/isaaclab_physx/isaaclab_physx/sim/schemas/__init__.pyi @@ -14,11 +14,12 @@ __all__ = [ "FixedTendonPropertiesCfg", "JointDrivePropertiesCfg", "MeshCollisionPropertiesCfg", + "OmniPhysicsDeformableBodyPropertiesCfg", "PhysxArticulationRootPropertiesCfg", "PhysxCollisionPropertiesCfg", - "PhysXCollisionPropertiesCfg", "PhysxConvexDecompositionPropertiesCfg", "PhysxConvexHullPropertiesCfg", + "PhysxDeformableBodyPropertiesCfg", "PhysxDeformableCollisionPropertiesCfg", "PhysxFixedTendonPropertiesCfg", "PhysxJointDrivePropertiesCfg", @@ -47,11 +48,12 @@ from .schemas_cfg import ( FixedTendonPropertiesCfg, JointDrivePropertiesCfg, MeshCollisionPropertiesCfg, + OmniPhysicsDeformableBodyPropertiesCfg, PhysxArticulationRootPropertiesCfg, PhysxCollisionPropertiesCfg, - PhysXCollisionPropertiesCfg, PhysxConvexDecompositionPropertiesCfg, PhysxConvexHullPropertiesCfg, + PhysxDeformableBodyPropertiesCfg, PhysxDeformableCollisionPropertiesCfg, PhysxFixedTendonPropertiesCfg, PhysxJointDrivePropertiesCfg, diff --git a/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas.py b/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas.py index 5b57b93d7ff4..713c1cc3264c 100644 --- a/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas.py +++ b/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas.py @@ -3,206 +3,12 @@ # # SPDX-License-Identifier: BSD-3-Clause -# needed to import for allowing type-hinting: Usd.Stage | None -from __future__ import annotations - -import logging - -from omni.physx.scripts import deformableUtils -from pxr import Usd - -from isaaclab.sim.utils import ( - apply_nested, - get_all_matching_child_prims, - safe_set_attribute_on_usd_prim, -) -from isaaclab.sim.utils.stage import get_current_stage -from isaaclab.utils.string import to_camel_case - -from isaaclab_physx.sim.schemas.schemas_cfg import DeformableBodyPropertiesCfg - -# import logger -logger = logging.getLogger(__name__) - +"""Compatibility wrappers for deformable schema writers. +The deformable schema writers are backend-aware but remain unified in +:mod:`isaaclab.sim.schemas`. """ -Deformable body properties. -""" - - -def define_deformable_body_properties( - prim_path: str, - cfg: DeformableBodyPropertiesCfg, - stage: Usd.Stage | None = None, - deformable_type: str = "volume", - sim_mesh_prim_path: str | None = None, -): - """Apply the deformable body schema on the input prim and set its properties. - - See :func:`modify_deformable_body_properties` for more details on how the properties are set. - - .. note:: - If the input prim is not a mesh, this function will traverse the prim and find the first mesh - under it. If no mesh or multiple meshes are found, an error is raised. This is because the deformable - body schema can only be applied to a single mesh. - - Args: - prim_path: The prim path where to apply the deformable body schema. - cfg: The configuration for the deformable body. - stage: The stage where to find the prim. Defaults to None, in which case the - current stage is used. - deformable_type: The type of the deformable body (surface or volume). - This is used to determine which PhysX API to use for the deformable body. Defaults to "volume". - sim_mesh_prim_path: Optional override for the simulation mesh prim path. - If None, it is set to ``{prim_path}/sim_mesh`` for surface deformables - and ``{prim_path}/sim_tetmesh`` for volume deformables. - - Raises: - ValueError: When the prim path is not valid. - ValueError: When the prim has no mesh or multiple meshes. - RuntimeError: When setting the deformable body properties fails. - """ - # get stage handle - if stage is None: - stage = get_current_stage() - - # get USD prim - prim = stage.GetPrimAtPath(prim_path) - # check if prim path is valid - if not prim.IsValid(): - raise ValueError(f"Prim path '{prim_path}' is not valid.") - - # traverse the prim and get the mesh. If none or multiple meshes are found, raise error. - matching_prims = get_all_matching_child_prims(prim_path, lambda p: p.GetTypeName() == "Mesh") - # check if the volume deformable mesh is valid - if len(matching_prims) == 0: - raise ValueError(f"Could not find any mesh in '{prim_path}'. Please check asset.") - if len(matching_prims) > 1: - # get list of all meshes found - mesh_paths = [p.GetPrimPath() for p in matching_prims] - raise ValueError( - f"Found multiple meshes in '{prim_path}': {mesh_paths}." - " Deformable body schema can only be applied to one mesh." - ) - mesh_prim = matching_prims[0] - mesh_prim_path = mesh_prim.GetPrimPath() - - # check if the prim is valid - if not mesh_prim.IsValid(): - raise ValueError(f"Mesh prim path '{mesh_prim_path}' is not valid.") - - # set root prim properties based on the type of the deformable mesh (surface vs volume) - if deformable_type == "surface": - sim_mesh_prim_path = prim_path + "/sim_mesh" if sim_mesh_prim_path is None else sim_mesh_prim_path - success = deformableUtils.create_auto_surface_deformable_hierarchy( - stage=stage, - root_prim_path=prim_path, - simulation_mesh_path=sim_mesh_prim_path, - cooking_src_mesh_path=mesh_prim_path, - cooking_src_simplification_enabled=False, - set_visibility_with_guide_purpose=True, - ) - elif deformable_type == "volume": - sim_mesh_prim_path = prim_path + "/sim_tetmesh" if sim_mesh_prim_path is None else sim_mesh_prim_path - success = deformableUtils.create_auto_volume_deformable_hierarchy( - stage=stage, - root_prim_path=prim_path, - simulation_tetmesh_path=sim_mesh_prim_path, - collision_tetmesh_path=sim_mesh_prim_path, - cooking_src_mesh_path=mesh_prim_path, - simulation_hex_mesh_enabled=False, - cooking_src_simplification_enabled=False, - set_visibility_with_guide_purpose=True, - ) - else: - raise ValueError( - f"""Unsupported deformable type: '{deformable_type}'. - Only surface and volume deformables are supported.""" - ) - - # api failure - if not success: - raise RuntimeError(f"Failed to set deformable body properties on prim '{mesh_prim_path}'.") - - # set deformable body properties - modify_deformable_body_properties(prim_path, cfg, stage) - - -@apply_nested -def modify_deformable_body_properties(prim_path: str, cfg: DeformableBodyPropertiesCfg, stage: Usd.Stage | None = None): - """Modify PhysX parameters for a deformable body prim. - - A `deformable body`_ is a single body (either surface or volume deformable) that can be simulated by PhysX. - Unlike rigid bodies, deformable bodies support relative motion of the nodes in the mesh. - Consequently, they can be used to simulate deformations under applied forces. - - PhysX deformable body simulation employs Finite Element Analysis (FEA) to simulate the deformations of the mesh. - It uses two meshes to represent the deformable body: - - 1. **Simulation mesh**: This mesh is used for the simulation and is the one that is deformed by the solver. - 2. **Collision mesh**: This mesh only needs to match the surface of the simulation mesh and is used for - collision detection. - - For most applications, we assume that the above two meshes are computed from the "render mesh" of the deformable - body. The render mesh is the mesh that is visible in the scene and is used for rendering purposes. It is composed - of triangles, while the simulation mesh is composed of tetrahedrons for volume deformables, - and triangles for surface deformables. - - .. caution:: - The deformable body schema is still under development by the Omniverse team. The current implementation - works with the PhysX schemas shipped with Isaac Sim 6.0.0 onwards. It may change in future releases. - - .. note:: - This function is decorated with :func:`apply_nested` that sets the properties to all the prims - (that have the schema applied on them) under the input prim path. - - .. _deformable body: https://nvidia-omniverse.github.io/PhysX/physx/5.6.1/docs/DeformableVolume.html - .. _PhysxDeformableBodyAPI: https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/latest/physxschema/annotated.html - - Args: - prim_path: The prim path to the deformable body. - cfg: The configuration for the deformable body. - stage: The stage where to find the prim. Defaults to None, in which case the - current stage is used. - - Returns: - True if the properties were successfully set, False otherwise. - """ - # get stage handle - if stage is None: - stage = get_current_stage() - - # get deformable-body USD prim - deformable_body_prim = stage.GetPrimAtPath(prim_path) - # check if the prim is valid - if not deformable_body_prim.IsValid(): - return False - # check if deformable body API is applied - if "OmniPhysicsDeformableBodyAPI" not in deformable_body_prim.GetAppliedSchemas(): - return False - - # apply customization to deformable API - if "PhysxBaseDeformableBodyAPI" not in deformable_body_prim.GetAppliedSchemas(): - deformable_body_prim.AddAppliedSchema("PhysxBaseDeformableBodyAPI") - # ensure PhysX collision API is applied on the collision mesh - if "PhysxCollisionAPI" not in deformable_body_prim.GetAppliedSchemas(): - deformable_body_prim.AddAppliedSchema("PhysxCollisionAPI") +from isaaclab.sim.schemas.schemas import define_deformable_body_properties, modify_deformable_body_properties - # convert to dict - cfg = cfg.to_dict() - # set into PhysX API - if cfg["kinematic_enabled"]: - logger.warning( - "Kinematic deformable bodies are not fully supported in the current version of Omni Physics. " - "Setting kinematic_enabled to True may lead to unexpected behavior." - ) - # prefixes for each attribute (collision attributes: physxCollision:*, and physxDeformable:* for rest) - property_prefixes = cfg["_property_prefix"] - for prefix, attr_list in property_prefixes.items(): - for attr_name in attr_list: - safe_set_attribute_on_usd_prim( - deformable_body_prim, f"{prefix}:{to_camel_case(attr_name, 'cC')}", cfg[attr_name], camel_case=False - ) - # success - return True +__all__ = ["define_deformable_body_properties", "modify_deformable_body_properties"] diff --git a/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas_cfg.py b/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas_cfg.py index 88729c2b0f0f..2eacd969e949 100644 --- a/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas_cfg.py +++ b/source/isaaclab_physx/isaaclab_physx/sim/schemas/schemas_cfg.py @@ -5,13 +5,13 @@ from __future__ import annotations -import dataclasses import warnings from typing import ClassVar from isaaclab.sim.schemas.schemas_cfg import ( ArticulationRootBaseCfg, CollisionBaseCfg, + DeformableBodyPropertiesBaseCfg, JointDriveBaseCfg, MeshCollisionBaseCfg, RigidBodyBaseCfg, @@ -20,15 +20,16 @@ @configclass -class OmniPhysicsPropertiesCfg: +class OmniPhysicsDeformableBodyPropertiesCfg(DeformableBodyPropertiesBaseCfg): """OmniPhysics properties for a deformable body. - These properties are set with the prefix ``omniphysics:``. For example, to set the mass of the - deformable body, you would set the property ``omniphysics:mass``. - - See the OmniPhysics documentation for more information on the available properties. + These properties are set with the prefix ``omniphysics:``. """ + _usd_namespace: ClassVar[str | None] = "omniphysics" + _usd_applied_schema: ClassVar[str | None] = None + _usd_field_exceptions: ClassVar[dict] = {} + deformable_body_enabled: bool | None = None """Enables deformable body.""" @@ -36,7 +37,7 @@ class OmniPhysicsPropertiesCfg: """Enables kinematic body. Defaults to False, which means that the body is not kinematic.""" mass: float | None = None - """The material mass in [kg]. Defaults to None, in which case the material density is used to compute the mass.""" + """The material mass [kg]. Defaults to None, in which case the material density is used to compute the mass.""" @configclass @@ -48,6 +49,10 @@ class PhysXDeformableBodyPropertiesCfg: For more information on the available properties, please refer to the `documentation `_. """ + _usd_namespace: ClassVar[str | None] = "physxDeformableBody" + _usd_applied_schema: ClassVar[str | None] = "PhysxBaseDeformableBodyAPI" + _usd_field_exceptions: ClassVar[dict] = {} + solver_position_iteration_count: int = 16 """Number of the solver positional iterations per step. Range is [1,255], default to 16.""" @@ -134,6 +139,10 @@ class PhysxDeformableCollisionPropertiesCfg: as a base of :class:`DeformableBodyPropertiesCfg`. """ + _usd_namespace: ClassVar[str | None] = "physxCollision" + _usd_applied_schema: ClassVar[str | None] = "PhysxCollisionAPI" + _usd_field_exceptions: ClassVar[dict] = {} + contact_offset: float | None = None """Contact offset for the collision shape [m]. @@ -152,31 +161,12 @@ class PhysxDeformableCollisionPropertiesCfg: @configclass -class PhysXCollisionPropertiesCfg(PhysxDeformableCollisionPropertiesCfg): - """Deprecated: use :class:`PhysxDeformableCollisionPropertiesCfg`. - - .. deprecated:: 4.6.23 - ``PhysXCollisionPropertiesCfg`` (capital X) was renamed to - :class:`PhysxDeformableCollisionPropertiesCfg` to clear the namespace for the - new rigid-body :class:`PhysxCollisionPropertiesCfg` (lowercase x). The capital-X - name is preserved as a deprecation alias and is scheduled for removal in 5.0. - """ - - def __post_init__(self): - warnings.warn( - "'PhysXCollisionPropertiesCfg' (capital X) is deprecated and will be removed in 5.0." - " Use 'isaaclab_physx.sim.schemas.PhysxDeformableCollisionPropertiesCfg' instead.", - DeprecationWarning, - stacklevel=2, - ) - super().__post_init__() - - -@configclass -class DeformableBodyPropertiesCfg( - OmniPhysicsPropertiesCfg, PhysXDeformableBodyPropertiesCfg, PhysxDeformableCollisionPropertiesCfg +class PhysxDeformableBodyPropertiesCfg( + OmniPhysicsDeformableBodyPropertiesCfg, + PhysXDeformableBodyPropertiesCfg, + PhysxDeformableCollisionPropertiesCfg, ): - """Properties to apply to a deformable body. + """PhysX-specific properties to apply to a deformable body. A deformable body is a body that can deform under forces, both surface and volume deformables. The configuration allows users to specify the properties of the deformable body, @@ -192,12 +182,25 @@ class DeformableBodyPropertiesCfg( the properties and leave the rest as-is. """ - _property_prefix: dict[str, list[str]] = { - "omniphysics": [field.name for field in dataclasses.fields(OmniPhysicsPropertiesCfg)], - "physxDeformableBody": [field.name for field in dataclasses.fields(PhysXDeformableBodyPropertiesCfg)], - "physxCollision": [field.name for field in dataclasses.fields(PhysxDeformableCollisionPropertiesCfg)], - } - """Mapping between the property prefixes and the properties that fall under each prefix.""" + +@configclass +class DeformableBodyPropertiesCfg(PhysxDeformableBodyPropertiesCfg): + """Deprecated: use :class:`PhysxDeformableBodyPropertiesCfg`. + + .. deprecated:: 4.6.x + ``DeformableBodyPropertiesCfg`` has moved to + :class:`PhysxDeformableBodyPropertiesCfg` for PhysX-specific deformable properties + and is scheduled for removal in 5.0. + """ + + def __post_init__(self): + warnings.warn( + "'DeformableBodyPropertiesCfg' is deprecated and will be removed in 5.0. Use" + " 'isaaclab_physx.sim.schemas.PhysxDeformableBodyPropertiesCfg' instead.", + DeprecationWarning, + stacklevel=2, + ) + super().__post_init__() @configclass diff --git a/source/isaaclab_physx/isaaclab_physx/sim/spawners/__init__.py b/source/isaaclab_physx/isaaclab_physx/sim/spawners/__init__.py index 82b931452f4f..767a7adeb7e6 100644 --- a/source/isaaclab_physx/isaaclab_physx/sim/spawners/__init__.py +++ b/source/isaaclab_physx/isaaclab_physx/sim/spawners/__init__.py @@ -3,7 +3,7 @@ # # SPDX-License-Identifier: BSD-3-Clause -"""Sub-module containing utilities for creating prims in Omniverse for the PhysX backend.""" +"""Sub-module containing PhysX spawner compatibility exports.""" from isaaclab.utils.module import lazy_export diff --git a/source/isaaclab_physx/isaaclab_physx/sim/spawners/__init__.pyi b/source/isaaclab_physx/isaaclab_physx/sim/spawners/__init__.pyi index 805a9f79fd52..617e4566c1d1 100644 --- a/source/isaaclab_physx/isaaclab_physx/sim/spawners/__init__.pyi +++ b/source/isaaclab_physx/isaaclab_physx/sim/spawners/__init__.pyi @@ -4,15 +4,15 @@ # SPDX-License-Identifier: BSD-3-Clause __all__ = [ - "DeformableObjectSpawnerCfg", - "spawn_deformable_body_material", "DeformableBodyMaterialCfg", + "PhysxDeformableBodyMaterialCfg", + "PhysxSurfaceDeformableBodyMaterialCfg", "SurfaceDeformableBodyMaterialCfg", ] -from .spawner_cfg import DeformableObjectSpawnerCfg from .materials import ( - spawn_deformable_body_material, DeformableBodyMaterialCfg, + PhysxDeformableBodyMaterialCfg, + PhysxSurfaceDeformableBodyMaterialCfg, SurfaceDeformableBodyMaterialCfg, ) diff --git a/source/isaaclab_physx/isaaclab_physx/sim/spawners/materials/__init__.py b/source/isaaclab_physx/isaaclab_physx/sim/spawners/materials/__init__.py index 4877bb0c1470..8ef9db1d4d8e 100644 --- a/source/isaaclab_physx/isaaclab_physx/sim/spawners/materials/__init__.py +++ b/source/isaaclab_physx/isaaclab_physx/sim/spawners/materials/__init__.py @@ -3,7 +3,7 @@ # # SPDX-License-Identifier: BSD-3-Clause -"""Sub-module for spawners that spawn PhysX-based materials.""" +"""Sub-module for PhysX material configuration compatibility exports.""" from isaaclab.utils.module import lazy_export diff --git a/source/isaaclab_physx/isaaclab_physx/sim/spawners/materials/__init__.pyi b/source/isaaclab_physx/isaaclab_physx/sim/spawners/materials/__init__.pyi index 1a3e833d61d9..315458ea5d24 100644 --- a/source/isaaclab_physx/isaaclab_physx/sim/spawners/materials/__init__.pyi +++ b/source/isaaclab_physx/isaaclab_physx/sim/spawners/materials/__init__.pyi @@ -4,17 +4,21 @@ # SPDX-License-Identifier: BSD-3-Clause __all__ = [ - "spawn_deformable_body_material", "DeformableBodyMaterialCfg", + "PhysXDeformableMaterialCfg", + "PhysxDeformableBodyMaterialCfg", "PhysxRigidBodyMaterialCfg", + "PhysxSurfaceDeformableBodyMaterialCfg", "RigidBodyMaterialCfg", "SurfaceDeformableBodyMaterialCfg", ] -from .physics_materials import spawn_deformable_body_material from .physics_materials_cfg import ( DeformableBodyMaterialCfg, + PhysXDeformableMaterialCfg, + PhysxDeformableBodyMaterialCfg, PhysxRigidBodyMaterialCfg, + PhysxSurfaceDeformableBodyMaterialCfg, RigidBodyMaterialCfg, SurfaceDeformableBodyMaterialCfg, ) diff --git a/source/isaaclab_physx/isaaclab_physx/sim/spawners/materials/physics_materials.py b/source/isaaclab_physx/isaaclab_physx/sim/spawners/materials/physics_materials.py deleted file mode 100644 index 78920cca9f8d..000000000000 --- a/source/isaaclab_physx/isaaclab_physx/sim/spawners/materials/physics_materials.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). -# All rights reserved. -# -# SPDX-License-Identifier: BSD-3-Clause - -from __future__ import annotations - -from pxr import Usd, UsdShade - -from isaaclab.sim.utils import clone, safe_set_attribute_on_usd_prim -from isaaclab.sim.utils.stage import get_current_stage -from isaaclab.utils.string import to_camel_case - -from . import physics_materials_cfg - - -@clone -def spawn_deformable_body_material(prim_path: str, cfg: physics_materials_cfg.DeformableBodyMaterialCfg) -> Usd.Prim: - """Create material with deformable-body physics properties. - - Deformable body materials are used to define the physical properties to meshes of a deformable body. These - include the friction and deformable body properties. For more information on deformable body material, - please refer to the documentation on `PxFEMSoftBodyMaterial`_. - - .. note:: - This function is decorated with :func:`clone` that resolves prim path into list of paths - if the input prim path is a regex pattern. This is done to support spawning multiple assets - from a single and cloning the USD prim at the given path expression. - - Args: - prim_path: The prim path or pattern to spawn the asset at. If the prim path is a regex pattern, - then the asset is spawned at all the matching prim paths. - cfg: The configuration for the physics material. - - Returns: - The spawned deformable body material prim. - - Raises: - ValueError: When a prim already exists at the specified prim path and is not a material. - - .. _PxFEMSoftBodyMaterial: https://nvidia-omniverse.github.io/PhysX/physx/5.4.1/_api_build/structPxFEMSoftBodyMaterialModel.html - """ - # get stage handle - stage = get_current_stage() - - # create material prim if no prim exists - if not stage.GetPrimAtPath(prim_path).IsValid(): - _ = UsdShade.Material.Define(stage, prim_path) - - # obtain prim - prim = stage.GetPrimAtPath(prim_path) - # check if prim is a material - if not prim.IsA(UsdShade.Material): - raise ValueError(f"A prim already exists at path: '{prim_path}' but is not a material.") - # ensure PhysX deformable body material API is applied - applied = prim.GetAppliedSchemas() - if "OmniPhysicsDeformableMaterialAPI" not in applied: - prim.AddAppliedSchema("OmniPhysicsDeformableMaterialAPI") - if "PhysxDeformableMaterialAPI" not in applied: - prim.AddAppliedSchema("PhysxDeformableMaterialAPI") - # surface deformable material API - is_surface_deformable = isinstance(cfg, physics_materials_cfg.SurfaceDeformableBodyMaterialCfg) - if is_surface_deformable: - if "OmniPhysicsSurfaceDeformableMaterialAPI" not in applied: - prim.AddAppliedSchema("OmniPhysicsSurfaceDeformableMaterialAPI") - if "PhysxSurfaceDeformableMaterialAPI" not in applied: - prim.AddAppliedSchema("PhysxSurfaceDeformableMaterialAPI") - - # convert to dict - cfg = cfg.to_dict() - del cfg["func"] - # set into PhysX API, gather prefixes for each attribute - property_prefixes = cfg["_property_prefix"] - for prefix, attr_list in property_prefixes.items(): - for attr_name in attr_list: - safe_set_attribute_on_usd_prim( - prim, f"{prefix}:{to_camel_case(attr_name, 'cC')}", cfg[attr_name], camel_case=False - ) - # return the prim - return prim diff --git a/source/isaaclab_physx/isaaclab_physx/sim/spawners/materials/physics_materials_cfg.py b/source/isaaclab_physx/isaaclab_physx/sim/spawners/materials/physics_materials_cfg.py index 7128a080b502..345fadc88704 100644 --- a/source/isaaclab_physx/isaaclab_physx/sim/spawners/materials/physics_materials_cfg.py +++ b/source/isaaclab_physx/isaaclab_physx/sim/spawners/materials/physics_materials_cfg.py @@ -5,13 +5,15 @@ from __future__ import annotations -import dataclasses import warnings from collections.abc import Callable from typing import ClassVar, Literal -from isaaclab.sim.spawners.materials import PhysicsMaterialCfg -from isaaclab.sim.spawners.materials.physics_materials_cfg import RigidBodyMaterialBaseCfg +from isaaclab.sim.spawners.materials.physics_materials_cfg import ( + DeformableBodyMaterialBaseCfg, + RigidBodyMaterialBaseCfg, + SurfaceDeformableBodyMaterialBaseCfg, +) from isaaclab.utils.configclass import configclass @@ -19,50 +21,39 @@ class OmniPhysicsDeformableMaterialCfg: """OmniPhysics material properties for a deformable body. - These properties are set with the prefix ``omniphysics:``. For example, to set the density of the - deformable body, you would set the property ``omniphysics:density``. - - See the OmniPhysics documentation for more information on the available properties. + These properties are set with the prefix ``omniphysics:``. """ - density: float | None = None - """The material density in [kg/m^3]. Defaults to None, in which case the simulation decides the default density.""" + _usd_namespace: ClassVar[str | None] = "omniphysics" + _usd_applied_schema: ClassVar[str | None] = "OmniPhysicsDeformableMaterialAPI" + _usd_field_exceptions: ClassVar[dict] = {} + + density: float = 1000.0 + """The material density [kg/m^3]. Defaults to 1000.0 kg/m^3.""" static_friction: float = 0.25 - """The static friction. Defaults to 0.25.""" + """The static friction coefficient. Defaults to 0.25.""" dynamic_friction: float = 0.25 - """The dynamic friction. Defaults to 0.25.""" + """The dynamic friction coefficient. Defaults to 0.25.""" youngs_modulus: float = 1000000.0 - """The Young's modulus, which defines the body's stiffness. Defaults to 1[MPa]. - - The Young's modulus is a measure of the material's ability to deform under stress. It is measured in Pascals ([Pa]). - """ + """The Young's modulus, which defines the body's stiffness [Pa]. Defaults to 1 MPa.""" poissons_ratio: float = 0.45 - """The Poisson's ratio which defines the body's volume preservation. Defaults to 0.45. - - The Poisson's ratio is a measure of the material's ability to expand in the lateral direction when compressed - in the axial direction. It is a dimensionless number between 0 and 0.5. Using a value of 0.5 will make the - material incompressible. - """ + """The Poisson's ratio which defines the body's volume preservation.""" @configclass class OmniPhysicsSurfaceDeformableMaterialCfg(OmniPhysicsDeformableMaterialCfg): - """OmniPhysics material properties for a surface deformable body, - extending on :class:`OmniPhysicsDeformableMaterialCfg` with additional parameters for surface deformable bodies. + """OmniPhysics material properties for a surface deformable body.""" - These properties are set with the prefix ``omniphysics:``. - For example, to set the surface thickness of the surface deformable body, - you would set the property ``omniphysics:surfaceThickness``. - - See the OmniPhysics documentation for more information on the available properties. - """ + _usd_namespace: ClassVar[str | None] = "omniphysics" + _usd_applied_schema: ClassVar[str | None] = "OmniPhysicsSurfaceDeformableMaterialAPI" + _usd_field_exceptions: ClassVar[dict] = {} surface_thickness: float = 0.01 - """The thickness of the deformable body's surface. Defaults to 0.01 meters ([m]).""" + """The thickness of the deformable body's surface [m]. Defaults to 0.01.""" surface_stretch_stiffness: float = 0.0 """The stretch stiffness of the deformable body's surface. Defaults to 0.0.""" @@ -73,56 +64,88 @@ class OmniPhysicsSurfaceDeformableMaterialCfg(OmniPhysicsDeformableMaterialCfg): surface_bend_stiffness: float = 0.0 """The bend stiffness of the deformable body's surface. Defaults to 0.0.""" - bend_damping: float = 0.0 - """The bend damping for the deformable body's surface. Defaults to 0.0.""" - @configclass class PhysXDeformableMaterialCfg: """PhysX-specific material properties for a deformable body. - These properties are set with the prefix ``physxDeformableBody:``. - For example, to set the elasticity damping of the deformable body, - you would set the property ``physxDeformableBody:elasticityDamping``. - - See the PhysX documentation for more information on the available properties. + These properties are set with the prefix ``physxDeformableMaterial:``. """ + _usd_namespace: ClassVar[str | None] = "physxDeformableMaterial" + _usd_applied_schema: ClassVar[str | None] = "PhysxDeformableMaterialAPI" + _usd_field_exceptions: ClassVar[dict] = {} + elasticity_damping: float = 0.005 """The elasticity damping for the deformable material. Defaults to 0.005.""" @configclass -class DeformableBodyMaterialCfg(PhysicsMaterialCfg, OmniPhysicsDeformableMaterialCfg, PhysXDeformableMaterialCfg): - """Physics material parameters for deformable bodies. +class PhysxDeformableBodyMaterialCfg( + DeformableBodyMaterialBaseCfg, + OmniPhysicsDeformableMaterialCfg, + PhysXDeformableMaterialCfg, +): + """PhysX-specific physics material parameters for deformable bodies.""" - See :meth:`spawn_deformable_body_material` for more information. - """ + func: Callable | str = "isaaclab.sim.spawners.materials.physics_materials:spawn_deformable_body_material" + + +@configclass +class PhysxSurfaceDeformableBodyMaterialCfg( + SurfaceDeformableBodyMaterialBaseCfg, + OmniPhysicsSurfaceDeformableMaterialCfg, + PhysXDeformableMaterialCfg, +): + """PhysX-specific physics material parameters for surface deformable bodies.""" + + _usd_namespace: ClassVar[str | None] = "physxDeformableMaterial" + _usd_applied_schema: ClassVar[str | None] = "PhysxSurfaceDeformableMaterialAPI" - func: Callable | str = "{DIR}.physics_materials:spawn_deformable_body_material" + func: Callable | str = "isaaclab.sim.spawners.materials.physics_materials:spawn_deformable_body_material" - _property_prefix: dict[str, list[str]] = { - "omniphysics": [field.name for field in dataclasses.fields(OmniPhysicsDeformableMaterialCfg)], - "physxDeformableBody": [field.name for field in dataclasses.fields(PhysXDeformableMaterialCfg)], - } - """Mapping between the property prefixes and the properties that fall under each prefix.""" + bend_damping: float = 0.0 + """Damping acting against bend-resistance forces [1/s]. Defaults to 0.0.""" @configclass -class SurfaceDeformableBodyMaterialCfg(DeformableBodyMaterialCfg, OmniPhysicsSurfaceDeformableMaterialCfg): - """Physics material parameters for surface deformable bodies, - extending on :class:`DeformableBodyMaterialCfg` with additional parameters for surface deformable bodies. +class DeformableBodyMaterialCfg(PhysxDeformableBodyMaterialCfg): + """Deprecated: use :class:`PhysxDeformableBodyMaterialCfg`. - See :meth:`spawn_deformable_body_material` for more information. + .. deprecated:: 4.6.x + ``DeformableBodyMaterialCfg`` has moved to + :class:`PhysxDeformableBodyMaterialCfg` for PhysX-specific deformable materials + and is scheduled for removal in 5.0. """ - func: Callable | str = "{DIR}.physics_materials:spawn_deformable_body_material" + def __post_init__(self): + warnings.warn( + "'DeformableBodyMaterialCfg' is deprecated and will be removed in 5.0. Use" + " 'isaaclab_physx.sim.spawners.materials.PhysxDeformableBodyMaterialCfg' instead.", + DeprecationWarning, + stacklevel=2, + ) + super().__post_init__() + - _property_prefix: dict[str, list[str]] = { - "omniphysics": [field.name for field in dataclasses.fields(OmniPhysicsSurfaceDeformableMaterialCfg)], - "physxDeformableBody": [field.name for field in dataclasses.fields(PhysXDeformableMaterialCfg)], - } - """Extend DeformableBodyMaterialCfg properties under each prefix.""" +@configclass +class SurfaceDeformableBodyMaterialCfg(PhysxSurfaceDeformableBodyMaterialCfg): + """Deprecated: use :class:`PhysxSurfaceDeformableBodyMaterialCfg`. + + .. deprecated:: 4.6.x + ``SurfaceDeformableBodyMaterialCfg`` has moved to + :class:`PhysxSurfaceDeformableBodyMaterialCfg` for PhysX-specific surface + deformable materials and is scheduled for removal in 5.0. + """ + + def __post_init__(self): + warnings.warn( + "'SurfaceDeformableBodyMaterialCfg' is deprecated and will be removed in 5.0. Use" + " 'isaaclab_physx.sim.spawners.materials.PhysxSurfaceDeformableBodyMaterialCfg' instead.", + DeprecationWarning, + stacklevel=2, + ) + super().__post_init__() @configclass diff --git a/source/isaaclab_physx/isaaclab_physx/sim/spawners/spawner_cfg.py b/source/isaaclab_physx/isaaclab_physx/sim/spawners/spawner_cfg.py deleted file mode 100644 index d4843ec83d3e..000000000000 --- a/source/isaaclab_physx/isaaclab_physx/sim/spawners/spawner_cfg.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). -# All rights reserved. -# -# SPDX-License-Identifier: BSD-3-Clause - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from isaaclab.sim.spawners.spawner_cfg import SpawnerCfg -from isaaclab.utils.configclass import configclass - -if TYPE_CHECKING: - from isaaclab.sim import schemas - - # deformables only supported on PhysX backend - from isaaclab_physx.sim.schemas.schemas_cfg import DeformableBodyPropertiesCfg - - -@configclass -class DeformableObjectSpawnerCfg(SpawnerCfg): - """Configuration parameters for spawning a deformable asset. - - Unlike rigid objects, deformable objects are affected by forces and can deform when subjected to - external forces. This class is used to configure the properties of the deformable object. - - Deformable bodies don't have a separate collision mesh. The collision mesh is the same as the visual mesh. - The collision properties such as rest and collision offsets are specified in the :attr:`deformable_props`. - - Note: - By default, all properties are set to None. This means that no properties will be added or modified - to the prim outside of the properties available by default when spawning the prim. - """ - - mass_props: schemas.MassPropertiesCfg | None = None - """Mass properties.""" - - deformable_props: DeformableBodyPropertiesCfg | None = None - """Deformable body properties. Only supported on PhysX backend for now.""" diff --git a/source/isaaclab_physx/test/assets/test_deformable_object.py b/source/isaaclab_physx/test/assets/test_deformable_object.py index 31a199938c94..7a884fe4e0e7 100644 --- a/source/isaaclab_physx/test/assets/test_deformable_object.py +++ b/source/isaaclab_physx/test/assets/test_deformable_object.py @@ -22,13 +22,18 @@ import torch import warp as wp from flaky import flaky -from isaaclab_physx.assets import DeformableObject, DeformableObjectCfg -from isaaclab_physx.sim import DeformableBodyMaterialCfg, DeformableBodyPropertiesCfg, SurfaceDeformableBodyMaterialCfg +from isaaclab_physx.assets import DeformableObject +from isaaclab_physx.sim import ( + PhysxDeformableBodyMaterialCfg, + PhysxDeformableBodyPropertiesCfg, + PhysxSurfaceDeformableBodyMaterialCfg, +) import carb import isaaclab.sim as sim_utils import isaaclab.utils.math as math_utils +from isaaclab.assets import DeformableObjectCfg from isaaclab.sim import build_simulation_context # Temporarily disabled: this suite intermittently aborts with SIGABRT on CI. @@ -72,14 +77,14 @@ def generate_cubes_scene( if has_api: spawn_cfg = sim_utils.MeshCuboidCfg( size=(0.2, 0.2, 0.2), - deformable_props=DeformableBodyPropertiesCfg(kinematic_enabled=kinematic_enabled), + deformable_props=PhysxDeformableBodyPropertiesCfg(kinematic_enabled=kinematic_enabled), ) # Add physics material if provided if material_path is not None: if deformable_type == "surface": - spawn_cfg.physics_material = SurfaceDeformableBodyMaterialCfg() + spawn_cfg.physics_material = PhysxSurfaceDeformableBodyMaterialCfg() else: - spawn_cfg.physics_material = DeformableBodyMaterialCfg() + spawn_cfg.physics_material = PhysxDeformableBodyMaterialCfg() spawn_cfg.physics_material_path = material_path else: spawn_cfg.physics_material = None @@ -108,8 +113,15 @@ def sim(): yield sim -@pytest.mark.parametrize("num_cubes", [1, 2]) -@pytest.mark.parametrize("material_path", [None, "/World/SoftMaterial", "material"]) +@pytest.mark.parametrize( + "num_cubes, material_path", + [ + (1, "material"), + (2, None), + (2, "/World/SoftMaterial"), + (2, "material"), + ], +) def test_initialization(sim, num_cubes, material_path): """Test initialization for prim with deformable body API at the provided prim path.""" cube_object = generate_cubes_scene(num_cubes=num_cubes, material_path=material_path) @@ -153,10 +165,10 @@ def test_initialization(sim, num_cubes, material_path): assert cube_object.data.root_vel_w.torch.shape == (num_cubes, 3) -@pytest.mark.parametrize("num_cubes", [1, 2]) @pytest.mark.isaacsim_ci -def test_initialization_surface_deformable(sim, num_cubes): +def test_initialization_surface_deformable(sim): """Test initialization of a surface deformable body.""" + num_cubes = 2 cube_object = generate_cubes_scene(num_cubes=num_cubes, deformable_type="surface") # Play sim @@ -203,10 +215,10 @@ def test_initialization_on_device_cpu(): sim.reset() -@pytest.mark.parametrize("num_cubes", [1, 2]) @pytest.mark.isaacsim_ci -def test_set_nodal_state(sim, num_cubes): +def test_set_nodal_state(sim): """Test setting the state of the deformable object.""" + num_cubes = 2 cube_object = generate_cubes_scene(num_cubes=num_cubes) # Play the simulator @@ -241,9 +253,15 @@ def test_set_nodal_state(sim, num_cubes): cube_object.update(sim.cfg.dt) -@pytest.mark.parametrize("num_cubes", [1, 2]) -@pytest.mark.parametrize("randomize_pos", [True, False]) -@pytest.mark.parametrize("randomize_rot", [True, False]) +@pytest.mark.parametrize( + "num_cubes, randomize_pos, randomize_rot", + [ + (1, False, False), + (1, True, False), + (1, False, True), + (2, True, True), + ], +) @flaky(max_runs=3, min_passes=1) @pytest.mark.isaacsim_ci def test_set_nodal_state_with_applied_transform(num_cubes, randomize_pos, randomize_rot): @@ -289,10 +307,10 @@ def test_set_nodal_state_with_applied_transform(num_cubes, randomize_pos, random torch.testing.assert_close(cube_object.data.root_pos_w.torch, mean_nodal_pos_init, rtol=1e-4, atol=1e-4) -@pytest.mark.parametrize("num_cubes", [2, 4]) @pytest.mark.isaacsim_ci -def test_set_kinematic_targets(sim, num_cubes): +def test_set_kinematic_targets(sim): """Test setting kinematic targets for the deformable object.""" + num_cubes = 2 cube_object = generate_cubes_scene(num_cubes=num_cubes, height=1.0) sim.reset() diff --git a/source/isaaclab_physx/test/sim/test_spawn_materials.py b/source/isaaclab_physx/test/sim/test_spawn_materials.py index 477132c5c5aa..bdd39a2d242e 100644 --- a/source/isaaclab_physx/test/sim/test_spawn_materials.py +++ b/source/isaaclab_physx/test/sim/test_spawn_materials.py @@ -14,7 +14,10 @@ import pytest -from isaaclab_physx.sim.spawners.materials.physics_materials_cfg import DeformableBodyMaterialCfg +from isaaclab_physx.sim.spawners.materials.physics_materials_cfg import ( + PhysxDeformableBodyMaterialCfg, + PhysxSurfaceDeformableBodyMaterialCfg, +) import isaaclab.sim as sim_utils from isaaclab.sim import SimulationCfg, SimulationContext @@ -36,7 +39,7 @@ def sim(): def test_spawn_deformable_body_material(sim): """Test spawning a deformable body material.""" - cfg = DeformableBodyMaterialCfg( + cfg = PhysxDeformableBodyMaterialCfg( density=1.0, dynamic_friction=0.25, youngs_modulus=50000000.0, @@ -52,4 +55,23 @@ def test_spawn_deformable_body_material(sim): assert prim.GetAttribute("omniphysics:dynamicFriction").Get() == cfg.dynamic_friction assert prim.GetAttribute("omniphysics:youngsModulus").Get() == cfg.youngs_modulus assert prim.GetAttribute("omniphysics:poissonsRatio").Get() == cfg.poissons_ratio - assert prim.GetAttribute("physxDeformableBody:elasticityDamping").Get() == pytest.approx(cfg.elasticity_damping) + assert prim.GetAttribute("physxDeformableMaterial:elasticityDamping").Get() == pytest.approx(cfg.elasticity_damping) + + +def test_spawn_surface_deformable_body_material(sim): + """Test spawning a surface deformable body material.""" + cfg = PhysxSurfaceDeformableBodyMaterialCfg( + density=1.0, + youngs_modulus=50000000.0, + poissons_ratio=0.5, + elasticity_damping=0.005, + bend_damping=0.01, + ) + prim = cfg.func("/Looks/SurfaceDeformableBodyMaterial", cfg) + # Check validity + assert prim.IsValid() + assert sim.stage.GetPrimAtPath("/Looks/SurfaceDeformableBodyMaterial").IsValid() + # Check PhysX properties + assert "PhysxSurfaceDeformableMaterialAPI" in prim.GetAppliedSchemas() + assert prim.GetAttribute("physxDeformableMaterial:elasticityDamping").Get() == pytest.approx(cfg.elasticity_damping) + assert prim.GetAttribute("physxDeformableMaterial:bendDamping").Get() == pytest.approx(cfg.bend_damping) diff --git a/source/isaaclab_physx/test/sim/test_spawn_meshes.py b/source/isaaclab_physx/test/sim/test_spawn_meshes.py index 7c24e289ef9f..02a21641071b 100644 --- a/source/isaaclab_physx/test/sim/test_spawn_meshes.py +++ b/source/isaaclab_physx/test/sim/test_spawn_meshes.py @@ -14,8 +14,8 @@ import pytest -from isaaclab_physx.sim.schemas.schemas_cfg import DeformableBodyPropertiesCfg -from isaaclab_physx.sim.spawners.materials.physics_materials_cfg import DeformableBodyMaterialCfg +from isaaclab_physx.sim.schemas.schemas_cfg import PhysxDeformableBodyPropertiesCfg +from isaaclab_physx.sim.spawners.materials.physics_materials_cfg import PhysxDeformableBodyMaterialCfg import isaaclab.sim as sim_utils from isaaclab.sim import SimulationCfg, SimulationContext @@ -46,33 +46,13 @@ def sim(): """ -def test_spawn_cone_with_deformable_props(sim): - """Test spawning of UsdGeomMesh prim for a cone with deformable body API.""" - # Spawn cone - cfg = sim_utils.MeshConeCfg( - radius=1.0, - height=2.0, - deformable_props=DeformableBodyPropertiesCfg(deformable_body_enabled=True), - ) - prim = cfg.func("/World/Cone", cfg) - - # Check validity - assert prim.IsValid() - assert sim.stage.GetPrimAtPath("/World/Cone").IsValid() - - # Check properties - # Unlike rigid body, deformable body properties are on the mesh prim - prim = sim.stage.GetPrimAtPath("/World/Cone") - assert prim.GetAttribute("omniphysics:deformableBodyEnabled").Get() == cfg.deformable_props.deformable_body_enabled - - def test_spawn_cone_with_deformable_and_mass_props(sim): """Test spawning of UsdGeomMesh prim for a cone with deformable body and mass API.""" # Spawn cone cfg = sim_utils.MeshConeCfg( radius=1.0, height=2.0, - deformable_props=DeformableBodyPropertiesCfg(deformable_body_enabled=True, mass=1.0), + deformable_props=PhysxDeformableBodyPropertiesCfg(deformable_body_enabled=True, mass=1.0), ) prim = cfg.func("/World/Cone", cfg) @@ -81,6 +61,7 @@ def test_spawn_cone_with_deformable_and_mass_props(sim): assert sim.stage.GetPrimAtPath("/World/Cone").IsValid() # Check properties prim = sim.stage.GetPrimAtPath("/World/Cone") + assert prim.GetAttribute("omniphysics:deformableBodyEnabled").Get() == cfg.deformable_props.deformable_body_enabled assert prim.GetAttribute("omniphysics:mass").Get() == cfg.deformable_props.mass # check sim playing @@ -97,8 +78,8 @@ def test_spawn_cone_with_deformable_and_density_props(sim): cfg = sim_utils.MeshConeCfg( radius=1.0, height=2.0, - deformable_props=DeformableBodyPropertiesCfg(deformable_body_enabled=True), - physics_material=DeformableBodyMaterialCfg(density=10.0), + deformable_props=PhysxDeformableBodyPropertiesCfg(deformable_body_enabled=True), + physics_material=PhysxDeformableBodyMaterialCfg(density=10.0), ) prim = cfg.func("/World/Cone", cfg) @@ -113,30 +94,3 @@ def test_spawn_cone_with_deformable_and_density_props(sim): sim.play() for _ in range(10): sim.step() - - -def test_spawn_cone_with_all_deformable_props(sim): - """Test spawning of UsdGeomMesh prim for a cone with all deformable properties.""" - # Spawn cone - cfg = sim_utils.MeshConeCfg( - radius=1.0, - height=2.0, - deformable_props=DeformableBodyPropertiesCfg(mass=1.0), - visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.75, 0.5)), - physics_material=DeformableBodyMaterialCfg(), - ) - prim = cfg.func("/World/Cone", cfg) - - # Check validity - assert prim.IsValid() - assert sim.stage.GetPrimAtPath("/World/Cone").IsValid() - assert sim.stage.GetPrimAtPath("/World/Cone/geometry/material").IsValid() - # Check properties - # -- deformable body - prim = sim.stage.GetPrimAtPath("/World/Cone") - assert prim.GetAttribute("omniphysics:deformableBodyEnabled").Get() is True - - # check sim playing - sim.play() - for _ in range(10): - sim.step() diff --git a/source/isaaclab_tasks/changelog.d/mym-deformable-backend-split.rst b/source/isaaclab_tasks/changelog.d/mym-deformable-backend-split.rst new file mode 100644 index 000000000000..e0cd3070591c --- /dev/null +++ b/source/isaaclab_tasks/changelog.d/mym-deformable-backend-split.rst @@ -0,0 +1,6 @@ +Changed +^^^^^^^ + +* Changed Franka soft-object task configs to use backend-specific deformable cfgs. + Use Newton deformable cfgs from :mod:`isaaclab_newton.sim` or PhysX deformable + cfgs from :mod:`isaaclab_physx.sim` when customizing these tasks. diff --git a/source/isaaclab_tasks/changelog.d/mym-deformable_experimental.minor.rst b/source/isaaclab_tasks/changelog.d/mym-deformable_experimental.minor.rst new file mode 100644 index 000000000000..561a08c3e261 --- /dev/null +++ b/source/isaaclab_tasks/changelog.d/mym-deformable_experimental.minor.rst @@ -0,0 +1,6 @@ +Added +^^^^^ + +* Added manager-based Franka soft-body lifting environment + ``Isaac-Lift-Soft-Franka-v0`` as the documented rigid-deformable coupling + task. diff --git a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift/config/franka/ik_abs_env_cfg.py b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift/config/franka/ik_abs_env_cfg.py index 388837be4127..e9a151a89a2a 100644 --- a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift/config/franka/ik_abs_env_cfg.py +++ b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift/config/franka/ik_abs_env_cfg.py @@ -3,8 +3,10 @@ # # SPDX-License-Identifier: BSD-3-Clause -from isaaclab_physx.assets import DeformableObjectCfg +from isaaclab_physx.sim.schemas import PhysxDeformableBodyPropertiesCfg +from isaaclab_physx.sim.spawners.materials import PhysxDeformableBodyMaterialCfg +from isaaclab.assets import DeformableObjectCfg from isaaclab.controllers.differential_ik_cfg import DifferentialIKControllerCfg from isaaclab.envs.mdp.actions.actions_cfg import DifferentialInverseKinematicsActionCfg from isaaclab.managers import EventTermCfg as EventTerm @@ -77,6 +79,8 @@ def __post_init__(self): spawn=UsdFileCfg( usd_path=f"{ISAACLAB_NUCLEUS_DIR}/Objects/Teddy_Bear/teddy_bear.usd", scale=(0.01, 0.01, 0.01), + deformable_props=PhysxDeformableBodyPropertiesCfg(), + physics_material=PhysxDeformableBodyMaterialCfg(), ), ) diff --git a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/__init__.py b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/__init__.py new file mode 100644 index 000000000000..0a4c52549e07 --- /dev/null +++ b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/__init__.py @@ -0,0 +1,33 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +import gymnasium as gym + +from . import agents + +## +# Register Gym environments. +## + + +gym.register( + id="Isaac-Lift-Soft-Franka-v0", + entry_point="isaaclab.envs:ManagerBasedRLEnv", + disable_env_checker=True, + kwargs={ + "env_cfg_entry_point": f"{__name__}.franka_soft_env_cfg:FrankaSoftEnvCfg", + "rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:FrankaDeformablePPORunnerCfg", + }, +) + +gym.register( + id="Isaac-Lift-Cloth-Franka-v0", + entry_point="isaaclab.envs:ManagerBasedRLEnv", + disable_env_checker=True, + kwargs={ + "env_cfg_entry_point": f"{__name__}.franka_cloth_env_cfg:FrankaClothEnvCfg", + "rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:FrankaDeformablePPORunnerCfg", + }, +) diff --git a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/agents/__init__.py b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/agents/__init__.py new file mode 100644 index 000000000000..460a30569089 --- /dev/null +++ b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/agents/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause diff --git a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/agents/rsl_rl_ppo_cfg.py b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/agents/rsl_rl_ppo_cfg.py new file mode 100644 index 000000000000..dfbad6c6fb8a --- /dev/null +++ b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/agents/rsl_rl_ppo_cfg.py @@ -0,0 +1,38 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +from isaaclab.utils.configclass import configclass + +from isaaclab_rl.rsl_rl import RslRlOnPolicyRunnerCfg, RslRlPpoActorCriticCfg, RslRlPpoAlgorithmCfg + + +@configclass +class FrankaDeformablePPORunnerCfg(RslRlOnPolicyRunnerCfg): + num_steps_per_env = 24 + max_iterations = 50000 + save_interval = 50 + experiment_name = "franka_deformable" + policy = RslRlPpoActorCriticCfg( + init_noise_std=1.0, + actor_obs_normalization=False, + critic_obs_normalization=False, + actor_hidden_dims=[256, 128, 64], + critic_hidden_dims=[256, 128, 64], + activation="elu", + ) + algorithm = RslRlPpoAlgorithmCfg( + value_loss_coef=1.0, + use_clipped_value_loss=True, + clip_param=0.2, + entropy_coef=0.006, + num_learning_epochs=5, + num_mini_batches=4, + learning_rate=1.0e-4, + schedule="adaptive", + gamma=0.98, + lam=0.95, + desired_kl=0.01, + max_grad_norm=1.0, + ) diff --git a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/franka_cloth_env_cfg.py b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/franka_cloth_env_cfg.py new file mode 100644 index 000000000000..1f89cf012c2a --- /dev/null +++ b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/franka_cloth_env_cfg.py @@ -0,0 +1,242 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Configuration for the Franka surface deformable lifting environment.""" + +from __future__ import annotations + +from isaaclab_newton.physics import MJWarpSolverCfg +from isaaclab_newton.sim.schemas import NewtonDeformableBodyPropertiesCfg +from isaaclab_newton.sim.spawners.materials import NewtonSurfaceDeformableBodyMaterialCfg + +import isaaclab.sim as sim_utils +from isaaclab.assets import AssetBaseCfg +from isaaclab.assets.deformable_object import DeformableObjectCfg +from isaaclab.managers import EventTermCfg as EventTerm +from isaaclab.managers import RewardTermCfg as RewTerm +from isaaclab.managers import SceneEntityCfg +from isaaclab.utils.configclass import configclass + +from isaaclab_contrib.deformable.newton_manager_cfg import CoupledMJWarpVBDSolverCfg, NewtonModelCfg, VBDSolverCfg + +from isaaclab_tasks.utils import PresetCfg + +from . import mdp +from .franka_soft_env_cfg import DeformableNewtonCfg, FrankaSoftEnvCfg, _FrankaSoftSceneCfg +from .franka_soft_env_cfg import EventCfg as FrankaSoftEventCfg + +## +# Scene definition +## + +ROBOT_SHAPE_MATERIAL_MU = 100.0 +"""Franka collision-shape friction coefficient [dimensionless] used for Newton cloth contact.""" + +ROBOT_SHAPE_MATERIAL_BODY_NAMES = ".*" +"""Franka body-name regex receiving :data:`ROBOT_SHAPE_MATERIAL_MU`.""" + + +@configclass +class PhysicsCfg(PresetCfg): + # Newton physics: MJWarp rigid + VBD soft, one-way coupled + # (matches newton/examples/softbody/example_softbody_franka.py) + newton_mjwarp_vdb: DeformableNewtonCfg = DeformableNewtonCfg( + solver_cfg=CoupledMJWarpVBDSolverCfg( + rigid_solver_cfg=MJWarpSolverCfg( + njmax=40, + nconmax=20, + ls_iterations=20, + cone="pyramidal", + impratio=1, + ls_parallel=False, + integrator="implicitfast", + ccd_iterations=100, + ), + soft_solver_cfg=VBDSolverCfg( + iterations=10, + integrate_with_external_rigid_solver=True, + particle_enable_self_contact=False, + particle_collision_detection_interval=-1, + ), + coupling_mode="two_way", + ), + model_cfg=NewtonModelCfg( + soft_contact_ke=1e3, + soft_contact_kd=1e-5, + soft_contact_mu=0.5, + shape_material_ke=1e3, + shape_material_kd=1e-5, + shape_material_mu=1e-4, + ), + num_substeps=10, + use_cuda_graph=True, + ) + + default = newton_mjwarp_vdb + + +@configclass +class DeformableCfg(PresetCfg): + """Preset config for the deformable object, matching the Newton example.""" + + newton_mjwarp_vdb: DeformableObjectCfg = DeformableObjectCfg( + prim_path="/World/envs/env_.*/Deformable", + init_state=DeformableObjectCfg.InitialStateCfg(pos=(0.4, 0.0, 0.2)), + spawn=sim_utils.MeshRectangleCfg( + size=(0.2, 0.2), + resolution=(30, 30), + deformable_props=NewtonDeformableBodyPropertiesCfg(), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.95, 0.85, 0.1)), + physics_material=NewtonSurfaceDeformableBodyMaterialCfg( + density=50.0, + particle_radius=0.005, + tri_ke=5e2, + tri_ka=5e2, + tri_kd=1e-3, + edge_ke=2.0, + edge_kd=1e-3, + ), + ), + ) + + default = newton_mjwarp_vdb + + +@configclass +class FrankaClothSceneCfg(_FrankaSoftSceneCfg): + """Scene for the Franka surface deformable environment.""" + + deformable: DeformableCfg = DeformableCfg() + + # static collidable cubes the cloth drops onto (sits on the table top at z = 0). + # Modeled as a static asset (no rigid body / no DOFs) so adding it does not + # extend the Newton model's joint state. + cube: AssetBaseCfg = AssetBaseCfg( + prim_path="{ENV_REGEX_NS}/Cube", + init_state=AssetBaseCfg.InitialStateCfg(pos=(0.45, 0.0, 0.04)), + spawn=sim_utils.CuboidCfg( + size=(0.03, 0.01, 0.08), + collision_props=sim_utils.CollisionPropertiesCfg(), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.2, 0.2, 0.25)), + ), + ) + + +@configclass +class ActionsCfg: + """7-dim arm joint position + 1-dim binary gripper.""" + + arm_action = mdp.JointPositionActionCfg( + asset_name="robot", joint_names=["panda_joint.*"], scale=0.1, use_default_offset=True + ) + gripper_action = mdp.BinaryJointPositionActionCfg( + asset_name="robot", + joint_names=["panda_finger.*"], + open_command_expr={"panda_finger_.*": 0.05}, + close_command_expr={"panda_finger_.*": 0.0}, + ) + + +@configclass +class RewardsCfg: + """Lift-to-target reward for a deformable object.""" + + reaching_deformable = RewTerm( + func=mdp.deformable_ee_distance, + params={"std": 0.1, "asset_cfg": SceneEntityCfg("deformable")}, + weight=5.0, + ) + lifting_deformable = RewTerm( + func=mdp.deformable_lifted, + params={"minimal_height": 0.04, "asset_cfg": SceneEntityCfg("deformable")}, + weight=5.0, + ) + deformable_goal_tracking = RewTerm( + func=mdp.deformable_com_goal_distance, + params={ + "std": 0.3, + "minimal_height": 0.075, + "command_name": "deformable_pose", + "asset_cfg": SceneEntityCfg("deformable"), + }, + weight=16.0, + ) + deformable_goal_tracking_fine_grained = RewTerm( + func=mdp.deformable_com_goal_distance, + params={ + "std": 0.05, + "minimal_height": 0.075, + "command_name": "deformable_pose", + "asset_cfg": SceneEntityCfg("deformable"), + }, + weight=5.0, + ) + + action_rate = RewTerm(func=mdp.action_rate_l2, weight=-1e-2) + gripper_close = RewTerm( + func=mdp.gripper_close_action, + params={"action_name": "gripper_action"}, + weight=-1.0, + ) + joint_vel = RewTerm(func=mdp.joint_vel_l2, weight=-1e-2) + joint_torque = RewTerm(func=mdp.joint_torques_l2, weight=-1e-4) + joint_acc = RewTerm(func=mdp.joint_acc_l2, weight=-1e-4) + + +@configclass +class EventCfg(FrankaSoftEventCfg): + """Reset and startup events for the Franka cloth environment.""" + + robot_physics_material = EventTerm( + func=mdp.randomize_rigid_body_material, + mode="startup", + params={ + "asset_cfg": SceneEntityCfg("robot", body_names=ROBOT_SHAPE_MATERIAL_BODY_NAMES), + "static_friction_range": (ROBOT_SHAPE_MATERIAL_MU, ROBOT_SHAPE_MATERIAL_MU), + "dynamic_friction_range": (ROBOT_SHAPE_MATERIAL_MU, ROBOT_SHAPE_MATERIAL_MU), + "restitution_range": (0.0, 0.0), + "num_buckets": 1, + }, + ) + + +## +# Environment configuration +## + + +@configclass +class FrankaClothEnvCfg(FrankaSoftEnvCfg): + """Manager-based RL environment: Franka Panda lifting a surface deformable.""" + + # Scene settings + scene: FrankaClothSceneCfg = FrankaClothSceneCfg(num_envs=128, env_spacing=2.5, replicate_physics=True) + # Basic settings + actions: ActionsCfg = ActionsCfg() + # MDP settings + rewards: RewardsCfg = RewardsCfg() + events: EventCfg = EventCfg() + + def __post_init__(self) -> None: + # general settings + self.decimation = 1 + self.episode_length_s = 5.0 + + # simulation settings + self.sim.dt = 1 / 60.0 + self.sim.render_interval = self.decimation + + # viewer settings + self.viewer.origin_type = "asset_root" + self.viewer.asset_name = "robot" + self.viewer.env_index = 0 + self.viewer.eye = (1.25, -1.5, 0.6) + self.viewer.resolution = (1920, 1080) + self.sim.physics = PhysicsCfg() + + # increase franka gripper stiffness + self.scene.robot.actuators["panda_hand"].effort_limit_sim = 500.0 + self.scene.robot.actuators["panda_hand"].stiffness = 2000.0 + self.scene.robot.actuators["panda_hand"].damping = 100.0 diff --git a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/franka_soft_env_cfg.py b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/franka_soft_env_cfg.py new file mode 100644 index 000000000000..9aba49d303c9 --- /dev/null +++ b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/franka_soft_env_cfg.py @@ -0,0 +1,448 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Configuration for the Franka deformable lifting environment. + +The scene mirrors ``newton/examples/softbody/example_softbody_franka.py``: +a Franka Panda manipulator on a tabletop with a tetrahedral deformable object simulated +by VBD. The RL task is to lift the deformable object's centre of mass to a randomised target +position sampled in the robot's root frame. +""" + +from __future__ import annotations + +from isaaclab_newton.physics import MJWarpSolverCfg, NewtonCfg +from isaaclab_newton.sim.schemas import NewtonDeformableBodyPropertiesCfg +from isaaclab_newton.sim.spawners.materials import NewtonDeformableBodyMaterialCfg +from isaaclab_physx.physics import PhysxCfg +from isaaclab_physx.sim.schemas import PhysxDeformableBodyPropertiesCfg +from isaaclab_physx.sim.spawners.materials import PhysxDeformableBodyMaterialCfg + +import isaaclab.sim as sim_utils +from isaaclab.assets import ArticulationCfg, AssetBaseCfg +from isaaclab.assets.deformable_object import DeformableObjectCfg +from isaaclab.controllers.differential_ik_cfg import DifferentialIKControllerCfg +from isaaclab.envs import ManagerBasedRLEnvCfg +from isaaclab.envs.mdp.actions.actions_cfg import DifferentialInverseKinematicsActionCfg +from isaaclab.managers import EventTermCfg as EventTerm +from isaaclab.managers import ObservationGroupCfg as ObsGroup +from isaaclab.managers import ObservationTermCfg as ObsTerm +from isaaclab.managers import RewardTermCfg as RewTerm +from isaaclab.managers import SceneEntityCfg +from isaaclab.managers import TerminationTermCfg as DoneTerm +from isaaclab.markers import VisualizationMarkersCfg +from isaaclab.scene import InteractiveSceneCfg +from isaaclab.sensors import FrameTransformerCfg +from isaaclab.sensors.frame_transformer.frame_transformer_cfg import OffsetCfg +from isaaclab.sim.spawners.from_files.from_files_cfg import GroundPlaneCfg, UsdFileCfg +from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR +from isaaclab.utils.configclass import configclass + +from isaaclab_contrib.deformable.newton_manager_cfg import CoupledMJWarpVBDSolverCfg, NewtonModelCfg, VBDSolverCfg + +from isaaclab_tasks.utils import PresetCfg + +from . import mdp + +## +# Pre-defined configs +## + +from isaaclab_assets.robots.franka import FRANKA_PANDA_CFG # isort:skip + + +## +# Helpers +## + + +# Shared volume material parameters. The Newton config below uses the equivalent Lame parameters. +YOUNGS_MODULUS = 8e4 +POISSONS_RATIO = 0.25 + + +@configclass +class DeformableNewtonCfg(NewtonCfg): + """NewtonCfg extended with model-level contact parameters for deformable objects. + + Uses a distinct class name so that ``_is_kitless_physics`` does not + match it, ensuring Kit is launched for USD deformable spawning. + """ + + model_cfg: NewtonModelCfg | None = None + """Global Newton model parameters applied after builder finalization.""" + + +@configclass +class DeformableCfg(PresetCfg): + """Preset config for the deformable object, matching the Newton example.""" + + newton_mjwarp_vbd: DeformableObjectCfg = DeformableObjectCfg( + prim_path="/World/envs/env_.*/Deformable", + init_state=DeformableObjectCfg.InitialStateCfg(pos=(0.5, 0.0, 0.05)), + spawn=sim_utils.MeshCuboidCfg( + size=(0.3, 0.05, 0.05), + deformable_props=NewtonDeformableBodyPropertiesCfg(), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.95, 0.85, 0.1)), + physics_material=NewtonDeformableBodyMaterialCfg( + density=300.0, + k_mu=YOUNGS_MODULUS / (2.0 * (1.0 + POISSONS_RATIO)), + k_lambda=(YOUNGS_MODULUS * POISSONS_RATIO / ((1.0 + POISSONS_RATIO) * (1.0 - 2.0 * POISSONS_RATIO))), + particle_radius=0.01, + ), + ), + ) + + physx: DeformableObjectCfg = DeformableObjectCfg( + prim_path="/World/envs/env_.*/Deformable", + init_state=DeformableObjectCfg.InitialStateCfg(pos=(0.5, 0.0, 0.05)), + spawn=sim_utils.MeshCuboidCfg( + size=(0.3, 0.05, 0.05), + deformable_props=PhysxDeformableBodyPropertiesCfg(), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.95, 0.85, 0.1)), + physics_material=PhysxDeformableBodyMaterialCfg( + density=300.0, + youngs_modulus=YOUNGS_MODULUS, + poissons_ratio=POISSONS_RATIO, + static_friction=10.0, + dynamic_friction=5.0, + ), + ), + ) + + default = newton_mjwarp_vbd + + +@configclass +class PhysicsCfg(PresetCfg): + # Newton physics: MJWarp rigid + VBD soft, one-way coupled + # (matches newton/examples/softbody/example_softbody_franka.py) + newton_mjwarp_vbd: DeformableNewtonCfg = DeformableNewtonCfg( + solver_cfg=CoupledMJWarpVBDSolverCfg( + rigid_solver_cfg=MJWarpSolverCfg( + njmax=40, + nconmax=20, + ls_iterations=20, + cone="pyramidal", + impratio=1, + ls_parallel=False, + integrator="implicitfast", + ccd_iterations=100, + ), + soft_solver_cfg=VBDSolverCfg( + iterations=10, + integrate_with_external_rigid_solver=True, + particle_enable_self_contact=False, + particle_collision_detection_interval=-1, + ), + coupling_mode="two_way", + ), + model_cfg=NewtonModelCfg( + soft_contact_ke=1e4, + soft_contact_kd=1e-5, + soft_contact_mu=5.0, + shape_material_ke=4e4, + shape_material_kd=1e-5, + shape_material_mu=5.0, + ), + num_substeps=10, + use_cuda_graph=True, + ) + + physx: PhysxCfg = PhysxCfg() + + default = newton_mjwarp_vbd + + +## +# Scene definition +## + + +@configclass +class _FrankaSoftSceneCfg(InteractiveSceneCfg): + """Scene for the Franka deformable environment.""" + + robot: ArticulationCfg = FRANKA_PANDA_CFG.replace(prim_path="/World/envs/env_.*/Robot") + + # end-effector frame for reward shaping + ee_frame: FrameTransformerCfg = FrameTransformerCfg( + prim_path="/World/envs/env_.*/Robot/panda_link0", + debug_vis=False, + target_frames=[ + FrameTransformerCfg.FrameCfg( + prim_path="/World/envs/env_.*/Robot/panda_hand", + name="end_effector", + offset=OffsetCfg(pos=[0.0, 0.0, 0.1034]), + ), + ], + ) + + deformable: DeformableCfg = DeformableCfg() + + # static table matching the Newton example: half-extents (0.4, 0.4, 0.1) → top at z = 0.2 + # NOTE: SeattleLabTable USD has its origin on the top surface, so the deformable object + # sits directly on it when placed at z = 0.05. + table: AssetBaseCfg = AssetBaseCfg( + prim_path="{ENV_REGEX_NS}/Table", + init_state=AssetBaseCfg.InitialStateCfg(pos=[0.5, 0.0, 0.0], rot=[0.0, 0.0, 0.707, 0.707]), + spawn=UsdFileCfg(usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/Mounts/SeattleLabTable/table_instanceable.usd"), + ) + + # ground plane + ground: AssetBaseCfg = AssetBaseCfg( + prim_path="/World/GroundPlane", + init_state=AssetBaseCfg.InitialStateCfg(pos=[0.0, 0.0, -1.05]), + spawn=GroundPlaneCfg(), + ) + + # lights + # dome_light: AssetBaseCfg = AssetBaseCfg( + # prim_path="/World/light", + # spawn=sim_utils.DomeLightCfg(color=(0.75, 0.75, 0.75), intensity=2000.0), + # ) + sky_light = AssetBaseCfg( + prim_path="/World/skyLight", + spawn=sim_utils.DomeLightCfg( + intensity=750.0, + texture_file=f"{ISAAC_NUCLEUS_DIR}/Materials/Textures/Skies/PolyHaven/kloofendal_43d_clear_puresky_4k.hdr", + ), + ) + + def __post_init__(self) -> None: + # disable gravity on the arm so the low-PD actuators do not need to fight gravity sag, + # which is the dominant source of steady-state IK tracking error. + self.robot.spawn.rigid_props.disable_gravity = True + + # increase franka gripper stiffness + self.robot.actuators["panda_hand"].effort_limit_sim = 500.0 + self.robot.actuators["panda_hand"].stiffness = 1000.0 + self.robot.actuators["panda_hand"].damping = 100.0 + + +## +# MDP settings +## + + +@configclass +class CommandsCfg: + """Commands for the deformable goal pose (xyz + identity quat in robot root frame).""" + + deformable_pose = mdp.UniformPoseCommandCfg( + asset_name="robot", + body_name="panda_hand", + resampling_time_range=(5.0, 5.0), + debug_vis=True, + ranges=mdp.UniformPoseCommandCfg.Ranges( + pos_x=(0.4, 0.6), + pos_y=(-0.25, 0.25), + pos_z=(0.25, 0.5), + roll=(0.0, 0.0), + pitch=(0.0, 0.0), + yaw=(0.0, 0.0), + ), + # Render the goal as a transparent colored sphere (a point) instead of a coordinate frame. + goal_pose_visualizer_cfg=VisualizationMarkersCfg( + prim_path="/Visuals/Command/goal_pose", + markers={ + "sphere": sim_utils.SphereCfg( + radius=0.03, + visual_material=sim_utils.PreviewSurfaceCfg( + diffuse_color=(0.1, 0.9, 0.2), + opacity=0.4, + ), + ), + }, + ), + ) + + +@configclass +class ActionsCfg: + """7-dim absolute end-effector pose (xyz + quaternion) via differential IK + 1-dim binary gripper.""" + + arm_action = DifferentialInverseKinematicsActionCfg( + asset_name="robot", + joint_names=["panda_joint.*"], + body_name="panda_hand", + controller=DifferentialIKControllerCfg( + command_type="pose", + use_relative_mode=False, + ik_method="dls", + ik_params={"lambda_val": 0.6}, + ), + body_offset=DifferentialInverseKinematicsActionCfg.OffsetCfg(pos=[0.0, 0.0, 0.107]), + ) + gripper_action = mdp.BinaryJointPositionActionCfg( + asset_name="robot", + joint_names=["panda_finger.*"], + open_command_expr={"panda_finger_.*": 0.05}, + close_command_expr={"panda_finger_.*": 0.0}, + ) + + +@configclass +class ObservationsCfg: + """Policy observations: joint state, deformable COM in robot frame, target, last action.""" + + @configclass + class PolicyCfg(ObsGroup): + joint_pos = ObsTerm(func=mdp.joint_pos_rel) + joint_vel = ObsTerm(func=mdp.joint_vel_rel) + deformable_sampled_points = ObsTerm( + func=mdp.DeformableSampledPointsInRobotRootFrame, + params={"asset_cfg": SceneEntityCfg("deformable"), "num_points": 20}, + ) + target_position = ObsTerm(func=mdp.generated_commands, params={"command_name": "deformable_pose"}) + actions = ObsTerm(func=mdp.last_action) + + def __post_init__(self) -> None: + self.enable_corruption = True + self.concatenate_terms = True + + policy: PolicyCfg = PolicyCfg() + + +@configclass +class EventCfg: + """Reset events: robot to default joint config, deformable with small position randomization.""" + + reset_robot_joints = EventTerm( + func=mdp.reset_joints_by_scale, + mode="reset", + params={"position_range": (0.9, 1.1), "velocity_range": (0.0, 0.0)}, + ) + + reset_deformable = EventTerm( + func=mdp.reset_nodal_state_uniform, + mode="reset", + params={ + "position_range": {"x": (-0.05, 0.05), "y": (-0.05, 0.05), "z": (0.0, 0.0)}, + "velocity_range": {}, + "asset_cfg": SceneEntityCfg("deformable"), + }, + ) + + +@configclass +class RewardsCfg: + """Lift-to-target reward for a deformable object.""" + + reaching_deformable = RewTerm( + func=mdp.deformable_ee_distance, + params={"std": 0.1, "asset_cfg": SceneEntityCfg("deformable")}, + weight=5.0, + ) + lifting_deformable = RewTerm( + func=mdp.deformable_lifted, + params={"minimal_height": 0.04, "asset_cfg": SceneEntityCfg("deformable")}, + weight=5.0, + ) + deformable_goal_tracking = RewTerm( + func=mdp.deformable_com_goal_distance, + params={ + "std": 0.3, + "minimal_height": 0.075, + "command_name": "deformable_pose", + "asset_cfg": SceneEntityCfg("deformable"), + }, + weight=16.0, + ) + deformable_goal_tracking_fine_grained = RewTerm( + func=mdp.deformable_com_goal_distance, + params={ + "std": 0.05, + "minimal_height": 0.075, + "command_name": "deformable_pose", + "asset_cfg": SceneEntityCfg("deformable"), + }, + weight=5.0, + ) + + action_rate = RewTerm(func=mdp.action_rate_l2, weight=-1e-2) + gripper_close = RewTerm( + func=mdp.gripper_close_action, + params={"action_name": "gripper_action"}, + weight=-1.0, + ) + joint_vel = RewTerm(func=mdp.joint_vel_l2, weight=-1e-2) + joint_torque = RewTerm(func=mdp.joint_torques_l2, weight=-1e-4) + joint_acc = RewTerm(func=mdp.joint_acc_l2, weight=-1e-4) + + +@configclass +class TerminationsCfg: + """Time out + table bounds/drop termination.""" + + time_out = DoneTerm(func=mdp.time_out, time_out=True) + + deformable_outside_table = DoneTerm( + func=mdp.deformable_outside_table_bounds, + params={ + "x_bounds": (0.0, 1.0), + "y_bounds": (-0.5, 0.5), + "asset_cfg": SceneEntityCfg("deformable"), + }, + ) + + deformable_dropped = DoneTerm( + func=mdp.deformable_com_below_minimum, + params={"minimum_height": -0.1, "asset_cfg": SceneEntityCfg("deformable")}, + ) + + ee_below_table = DoneTerm( + func=mdp.ee_below_minimum, + params={"minimum_height": 0.0, "ee_frame_cfg": SceneEntityCfg("ee_frame")}, + ) + + +## +# Environment configuration +## + + +@configclass +class FrankaSoftSceneCfg(PresetCfg): + newton_mjwarp_vbd: _FrankaSoftSceneCfg = _FrankaSoftSceneCfg(num_envs=128, env_spacing=2.5, replicate_physics=True) + + # PhysX does not support replicating physics for deformable objects + physx: _FrankaSoftSceneCfg = _FrankaSoftSceneCfg(num_envs=128, env_spacing=2.5, replicate_physics=False) + + default = newton_mjwarp_vbd + + +@configclass +class FrankaSoftEnvCfg(ManagerBasedRLEnvCfg): + """Manager-based RL environment: Franka Panda lifting a volume deformable.""" + + # Scene settings + scene: FrankaSoftSceneCfg = FrankaSoftSceneCfg() + # Basic settings + observations: ObservationsCfg = ObservationsCfg() + actions: ActionsCfg = ActionsCfg() + commands: CommandsCfg = CommandsCfg() + # MDP settings + rewards: RewardsCfg = RewardsCfg() + terminations: TerminationsCfg = TerminationsCfg() + events: EventCfg = EventCfg() + + def __post_init__(self) -> None: + # general settings + self.decimation = 1 + self.episode_length_s = 5.0 + + # simulation settings + self.sim.dt = 1 / 60.0 + self.sim.render_interval = self.decimation + self.sim.gravity = (0.0, 0.0, 0.0) + self.sim.physics = PhysicsCfg() + + # viewer settings + self.viewer.origin_type = "asset_root" + self.viewer.asset_name = "robot" + self.viewer.env_index = 0 + self.viewer.eye = (1.25, -1.5, 0.75) + self.viewer.resolution = (1920, 1080) diff --git a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/__init__.py b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/__init__.py new file mode 100644 index 000000000000..8596e8efb4e7 --- /dev/null +++ b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""This sub-module contains the functions that are specific to the environment.""" + +from isaaclab.utils.module import lazy_export + +lazy_export() diff --git a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/__init__.pyi b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/__init__.pyi new file mode 100644 index 000000000000..bb8b3af19852 --- /dev/null +++ b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/__init__.pyi @@ -0,0 +1,28 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +__all__ = [ + "deformable_com_below_minimum", + "deformable_ee_distance", + "deformable_com_goal_distance", + "deformable_com_in_robot_root_frame", + "DeformableSampledPointsInRobotRootFrame", + "deformable_lifted", + "deformable_outside_table_bounds", + "ee_below_minimum", + "gripper_close_action", +] + +from .observations import DeformableSampledPointsInRobotRootFrame, deformable_com_in_robot_root_frame +from .rewards import ( + deformable_com_below_minimum, + deformable_ee_distance, + deformable_com_goal_distance, + deformable_lifted, + deformable_outside_table_bounds, + ee_below_minimum, + gripper_close_action, +) +from isaaclab.envs.mdp import * diff --git a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/observations.py b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/observations.py new file mode 100644 index 000000000000..a1aa2fa0aa64 --- /dev/null +++ b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/observations.py @@ -0,0 +1,115 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Observation functions for the Franka deformable lifting environment.""" + +from __future__ import annotations + +from collections.abc import Sequence +from typing import TYPE_CHECKING + +import torch +import warp as wp + +from isaaclab.managers import ManagerTermBase, SceneEntityCfg +from isaaclab.utils.math import subtract_frame_transforms + +if TYPE_CHECKING: + from isaaclab.assets import Articulation, DeformableObject + from isaaclab.envs import ManagerBasedRLEnv + + +def deformable_com_in_robot_root_frame( + env: ManagerBasedRLEnv, + asset_cfg: SceneEntityCfg = SceneEntityCfg("deformable"), + robot_cfg: SceneEntityCfg = SceneEntityCfg("robot"), +) -> torch.Tensor: + """Position of the deformable object's COM in the robot's root frame [m]. + + The COM is the mean of the deformable's nodal positions (see + :attr:`~isaaclab.assets.DeformableObject.data.root_pos_w`). + + Returns: + Tensor of shape ``(num_envs, 3)``. + """ + asset: DeformableObject = env.scene[asset_cfg.name] + robot: Articulation = env.scene[robot_cfg.name] + com_w = wp.to_torch(asset.data.root_pos_w) + com_b, _ = subtract_frame_transforms(wp.to_torch(robot.data.root_pos_w), wp.to_torch(robot.data.root_quat_w), com_w) + return com_b + + +class DeformableSampledPointsInRobotRootFrame(ManagerTermBase): + """Sampled deformable nodal points expressed in the robot's root frame. + + The point indices are sampled on reset, then reused within the episode so + each observed point follows the same material node over time. + """ + + def __init__(self, cfg, env: ManagerBasedRLEnv): + super().__init__(cfg, env) + + self.asset_cfg: SceneEntityCfg = cfg.params.get("asset_cfg", SceneEntityCfg("deformable")) + self.robot_cfg: SceneEntityCfg = cfg.params.get("robot_cfg", SceneEntityCfg("robot")) + self.num_points: int = cfg.params.get("num_points", 20) + + asset: DeformableObject = env.scene[self.asset_cfg.name] + self.num_nodes = asset.data.nodal_pos_w.shape[1] + self.node_ids = torch.empty(env.num_envs, self.num_points, dtype=torch.long, device=env.device) + self.reset() + + def reset(self, env_ids: Sequence[int] | None = None) -> None: + """Resample observed deformable nodes for the selected environments.""" + if env_ids is None: + env_ids = slice(None) + num_envs = self.num_envs + else: + num_envs = len(env_ids) + + if self.num_points <= self.num_nodes: + self.node_ids[env_ids] = ( + torch.rand((num_envs, self.num_nodes), device=self.device).topk(self.num_points, dim=1).indices + ) + else: + self.node_ids[env_ids] = torch.randint(self.num_nodes, (num_envs, self.num_points), device=self.device) + + def __call__( + self, + env: ManagerBasedRLEnv, + asset_cfg: SceneEntityCfg = SceneEntityCfg("deformable"), + robot_cfg: SceneEntityCfg = SceneEntityCfg("robot"), + num_points: int = 20, + ) -> torch.Tensor: + """Sample deformable nodal positions in the robot's root frame. + + Args: + env: The environment instance. + asset_cfg: The deformable object entity. + robot_cfg: The robot entity providing the reference frame. + num_points: Number of sampled points. + + Returns: + Flattened tensor of shape ``(num_envs, 3 * num_points)`` with sampled + point positions [m] in the robot root frame. + """ + asset: DeformableObject = env.scene[asset_cfg.name] + robot: Articulation = env.scene[robot_cfg.name] + if num_points != self.num_points: + raise ValueError( + f"Requested {num_points} deformable points, but this term was initialized with {self.num_points}." + ) + + nodal_pos_w = asset.data.nodal_pos_w.torch + sampled_points_w = nodal_pos_w.gather(1, self.node_ids.unsqueeze(-1).expand(-1, -1, 3)) + + flat_sampled_points_w = sampled_points_w.reshape(-1, 3) + root_pos_w = robot.data.root_pos_w.torch.unsqueeze(1).expand(-1, num_points, -1) + root_quat_w = robot.data.root_quat_w.torch.unsqueeze(1).expand(-1, num_points, -1) + sampled_points_b, _ = subtract_frame_transforms( + root_pos_w.reshape(-1, 3), + root_quat_w.reshape(-1, 4), + flat_sampled_points_w, + ) + return sampled_points_b.view(env.num_envs, -1) diff --git a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/rewards.py b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/rewards.py new file mode 100644 index 000000000000..9811051cd370 --- /dev/null +++ b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/rewards.py @@ -0,0 +1,167 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Reward and termination functions for the Franka deformable lifting environment.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import torch +import warp as wp + +from isaaclab.managers import SceneEntityCfg +from isaaclab.utils.math import combine_frame_transforms + +if TYPE_CHECKING: + from isaaclab.assets import Articulation, DeformableObject + from isaaclab.envs import ManagerBasedRLEnv + from isaaclab.sensors import FrameTransformer + + +def deformable_lifted( + env: ManagerBasedRLEnv, + minimal_height: float, + asset_cfg: SceneEntityCfg = SceneEntityCfg("deformable"), +) -> torch.Tensor: + """Reward if the deformable COM is above a minimum height. + + Args: + env: The environment instance. + minimal_height: Minimum COM height [m]. + asset_cfg: The deformable object entity. + + Returns: + Reward tensor with shape ``(num_envs,)``. + """ + asset: DeformableObject = env.scene[asset_cfg.name] + com_z = wp.to_torch(asset.data.root_pos_w)[:, 2] + return torch.where(com_z > minimal_height, 1.0, 0.0) + + +def deformable_ee_distance( + env: ManagerBasedRLEnv, + std: float, + asset_cfg: SceneEntityCfg = SceneEntityCfg("deformable"), + ee_frame_cfg: SceneEntityCfg = SceneEntityCfg("ee_frame"), +) -> torch.Tensor: + """Reward reaching the deformable's nearest nodal point with the end-effector. + + Args: + env: The environment instance. + std: The tanh kernel standard deviation [m]. + asset_cfg: The deformable object entity. + ee_frame_cfg: The end-effector frame entity. + + Returns: + Reward tensor with shape ``(num_envs,)``. + """ + asset: DeformableObject = env.scene[asset_cfg.name] + ee_frame: FrameTransformer = env.scene[ee_frame_cfg.name] + nodal_pos_w = wp.to_torch(asset.data.nodal_pos_w) + ee_w = wp.to_torch(ee_frame.data.target_pos_w)[..., 0, :] + distance = torch.linalg.norm(nodal_pos_w - ee_w.unsqueeze(1), dim=2).min(dim=1).values + return 1.0 - torch.tanh(distance / std) + + +def deformable_com_goal_distance( + env: ManagerBasedRLEnv, + std: float, + minimal_height: float, + command_name: str, + robot_cfg: SceneEntityCfg = SceneEntityCfg("robot"), + asset_cfg: SceneEntityCfg = SceneEntityCfg("deformable"), +) -> torch.Tensor: + """Reward tracking of the goal position by the deformable's COM (tanh kernel). + + Only credits when the COM is above ``minimal_height`` (i.e. the object is lifted). + The command is interpreted as ``[x, y, z, qw, qx, qy, qz]`` in the robot's root frame. + """ + robot: Articulation = env.scene[robot_cfg.name] + asset: DeformableObject = env.scene[asset_cfg.name] + command = env.command_manager.get_command(command_name) + des_pos_b = command[:, :3] + des_pos_w, _ = combine_frame_transforms( + wp.to_torch(robot.data.root_pos_w), wp.to_torch(robot.data.root_quat_w), des_pos_b + ) + com_w = wp.to_torch(asset.data.root_pos_w) + distance = torch.linalg.norm(des_pos_w - com_w, dim=1) + return (com_w[:, 2] > minimal_height) * (1.0 - torch.tanh(distance / std)) + + +def gripper_close_action(env: ManagerBasedRLEnv, action_name: str = "gripper_action") -> torch.Tensor: + """Penalty signal for commanding the gripper to close. + + The binary gripper action uses negative float actions for close commands and + non-negative actions for open commands. + + Args: + env: The environment instance. + action_name: Name of the gripper action term. + + Returns: + Tensor with shape ``(num_envs,)`` containing ``1`` when the gripper is + commanded closed and ``0`` otherwise. + """ + gripper_action = env.action_manager.get_term(action_name).raw_actions + return torch.any(gripper_action < 0.0, dim=1).float() + + +def deformable_com_below_minimum( + env: ManagerBasedRLEnv, + minimum_height: float, + asset_cfg: SceneEntityCfg = SceneEntityCfg("deformable"), +) -> torch.Tensor: + """Termination signal when the deformable's COM falls below ``minimum_height`` [m].""" + asset: DeformableObject = env.scene[asset_cfg.name] + com_z = wp.to_torch(asset.data.root_pos_w)[:, 2] + return com_z < minimum_height + + +def deformable_outside_table_bounds( + env: ManagerBasedRLEnv, + x_bounds: tuple[float, float], + y_bounds: tuple[float, float], + asset_cfg: SceneEntityCfg = SceneEntityCfg("deformable"), +) -> torch.Tensor: + """Terminate if any deformable nodal point leaves the table footprint. + + Args: + env: The environment instance. + x_bounds: Allowed x-position range in the environment frame [m]. + y_bounds: Allowed y-position range in the environment frame [m]. + asset_cfg: The deformable object entity. + + Returns: + Boolean tensor with shape ``(num_envs,)``. + """ + asset: DeformableObject = env.scene[asset_cfg.name] + nodal_pos = wp.to_torch(asset.data.nodal_pos_w) - env.scene.env_origins.unsqueeze(1) + outside_x = (nodal_pos[..., 0] < x_bounds[0]) | (nodal_pos[..., 0] > x_bounds[1]) + outside_y = (nodal_pos[..., 1] < y_bounds[0]) | (nodal_pos[..., 1] > y_bounds[1]) + return torch.any(outside_x | outside_y, dim=1) + + +def ee_below_minimum( + env: ManagerBasedRLEnv, + minimum_height: float, + ee_frame_cfg: SceneEntityCfg = SceneEntityCfg("ee_frame"), +) -> torch.Tensor: + """Termination signal when the end-effector falls below ``minimum_height`` [m]. + + Height is measured in the environment frame (``z`` of the EE position with the env + origin subtracted), so the threshold is independent of the environment's xy offset. + + Args: + env: The environment instance. + minimum_height: Minimum allowed EE height in the environment frame [m]. + ee_frame_cfg: The end-effector frame entity. + + Returns: + Boolean tensor with shape ``(num_envs,)``. + """ + ee_frame: FrameTransformer = env.scene[ee_frame_cfg.name] + ee_z = wp.to_torch(ee_frame.data.target_pos_w)[..., 0, 2] - env.scene.env_origins[:, 2] + return ee_z < minimum_height From 32f92a91a0297e5e61404b3a27a85ae84c8dbad8 Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Wed, 20 May 2026 01:52:11 +0000 Subject: [PATCH 28/31] tiled cam --- source/isaaclab/isaaclab/sim/simulation_cfg.py | 3 --- source/isaaclab/isaaclab/sim/simulation_context.py | 1 - .../isaaclab_newton/renderers/newton_warp_renderer.py | 4 +--- .../manager_based/manipulation/dexsuite/dexsuite_env_cfg.py | 1 - 4 files changed, 1 insertion(+), 8 deletions(-) diff --git a/source/isaaclab/isaaclab/sim/simulation_cfg.py b/source/isaaclab/isaaclab/sim/simulation_cfg.py index 695b1a64959a..2c71c724ad3d 100644 --- a/source/isaaclab/isaaclab/sim/simulation_cfg.py +++ b/source/isaaclab/isaaclab/sim/simulation_cfg.py @@ -335,6 +335,3 @@ class SimulationCfg: visualizer_cfgs: list[VisualizerCfg] | VisualizerCfg = [] """The visualizer configuration(s). Default is an empty list.""" - - default_visualizer_cfg: VisualizerCfg = VisualizerCfg() - """Default visualizer settings applied to backend configs created from explicit CLI visualizer requests.""" diff --git a/source/isaaclab/isaaclab/sim/simulation_context.py b/source/isaaclab/isaaclab/sim/simulation_context.py index 9d1dff664389..45f182c17689 100644 --- a/source/isaaclab/isaaclab/sim/simulation_context.py +++ b/source/isaaclab/isaaclab/sim/simulation_context.py @@ -11,7 +11,6 @@ import traceback from collections.abc import Iterator from contextlib import contextmanager -from dataclasses import fields from typing import TYPE_CHECKING, Any import toml diff --git a/source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py b/source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py index cfa5aa2614ad..ac54443971c9 100644 --- a/source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py +++ b/source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py @@ -118,9 +118,7 @@ def update(self, positions: ProxyArray, orientations: ProxyArray, intrinsics: Pr fov_radians_all = 2.0 * torch.atan(self.height / (2.0 * first_focal_length)) fov_warp = wp.from_torch(fov_radians_all, dtype=wp.float32) - self.camera_rays = self.newton_sensor.utils.compute_pinhole_camera_rays( - self.width, self.height, fov_warp - ) + self.camera_rays = self.newton_sensor.utils.compute_pinhole_camera_rays(self.width, self.height, fov_warp) @wp.kernel def _update_transforms( diff --git a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/dexsuite_env_cfg.py b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/dexsuite_env_cfg.py index 1fe37f62c81b..eeea53b48415 100644 --- a/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/dexsuite_env_cfg.py +++ b/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/dexsuite_env_cfg.py @@ -490,7 +490,6 @@ def __post_init__(self): """Post initialization.""" # general settings self.decimation = 2 # 50 Hz - self.sim.default_visualizer_cfg.tiled_cam_target_prim_path = "/World/envs/*/Object" # *single-goal setup self.commands.object_pose.resampling_time_range = (2.0, 3.0) From 58e89e74a7d2d9817a5f85fd600e86d762056bda Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Wed, 20 May 2026 02:28:09 +0000 Subject: [PATCH 29/31] review bot feedback --- docs/source/features/visualization.rst | 2 +- .../isaaclab/envs/utils/camera_view.py | 4 +- .../isaaclab/sim/simulation_context.py | 1 + .../isaaclab/visualizers/visualizer_cfg.py | 4 +- .../test/visualizers/test_visualizer.py | 36 ++++++++++ .../kit/kit_visualizer.py | 69 ++++++++++++++----- .../newton/newton_visualizer.py | 1 - 7 files changed, 94 insertions(+), 23 deletions(-) diff --git a/docs/source/features/visualization.rst b/docs/source/features/visualization.rst index cd5a324af8b7..4f950f932871 100644 --- a/docs/source/features/visualization.rst +++ b/docs/source/features/visualization.rst @@ -393,7 +393,7 @@ Newton Visualizer # Tiled camera view settings tiled_cam_view=True, # Enable non-interactive tiled camera image view - tiled_cam_num=57, # Number of generated camera tiles to display + tiled_cam_num=16, # Number of generated camera tiles to display tiled_cam_env_indices=None, # Optional explicit env ids to show in the tiled view tiled_cam_prim_path=None, # Existing Camera sensor prim path, e.g. "/World/envs/*/Camera" tiled_cam_eye=(4.0, -4.0, 3.0), # Eye offset for generated tiled cameras diff --git a/source/isaaclab/isaaclab/envs/utils/camera_view.py b/source/isaaclab/isaaclab/envs/utils/camera_view.py index bb57f0c8c4c3..7b7c093c3c8b 100644 --- a/source/isaaclab/isaaclab/envs/utils/camera_view.py +++ b/source/isaaclab/isaaclab/envs/utils/camera_view.py @@ -173,6 +173,8 @@ def compose_rgb_grid_tensor(rgb_batch: torch.Tensor) -> torch.Tensor: def compute_tile_resolution(window_width: int, window_height: int, num_tiles: int) -> tuple[int, int]: """Derive a conservative per-tile resolution from the visualizer window.""" + if window_width <= 0 or window_height <= 0: + raise ValueError(f"Window dimensions must be positive, got {window_width}x{window_height}.") cols = max(1, math.ceil(math.sqrt(max(1, num_tiles)))) rows = math.ceil(max(1, num_tiles) / cols) return max(1, int(window_width) // cols), max(1, int(window_height) // rows) @@ -255,7 +257,7 @@ def apply_camera_view_from_origins( origins = origins.to(device=device) eye_offset = torch.tensor(eye, dtype=torch.float32, device=device).unsqueeze(0) lookat_offset = torch.tensor(lookat, dtype=torch.float32, device=device).unsqueeze(0) - camera.set_world_poses_from_view(origins + eye_offset, origins + lookat_offset) + camera.set_world_poses_from_view(origins + eye_offset, origins + lookat_offset, env_ids=env_ids) camera._update_poses(None) diff --git a/source/isaaclab/isaaclab/sim/simulation_context.py b/source/isaaclab/isaaclab/sim/simulation_context.py index 45f182c17689..9d1dff664389 100644 --- a/source/isaaclab/isaaclab/sim/simulation_context.py +++ b/source/isaaclab/isaaclab/sim/simulation_context.py @@ -11,6 +11,7 @@ import traceback from collections.abc import Iterator from contextlib import contextmanager +from dataclasses import fields from typing import TYPE_CHECKING, Any import toml diff --git a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py index 1c441944591c..e6d84237e8ed 100644 --- a/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py +++ b/source/isaaclab/isaaclab/visualizers/visualizer_cfg.py @@ -40,10 +40,10 @@ class VisualizerCfg: lookat: tuple[float, float, float] = (0.0, 0.0, 0.0) """Interactive visualizer camera look-at target in world coordinates.""" - focal_length: float = 15.0 + focal_length: float = 12.0 """Camera focal length in millimeters for visualizer camera views.""" - tiled_cam_view: bool = True + tiled_cam_view: bool = False """Enable a non-interactive tiled camera image view.""" tiled_cam_num: int = 16 diff --git a/source/isaaclab/test/visualizers/test_visualizer.py b/source/isaaclab/test/visualizers/test_visualizer.py index 4b050431eb82..3cc8deed56b2 100644 --- a/source/isaaclab/test/visualizers/test_visualizer.py +++ b/source/isaaclab/test/visualizers/test_visualizer.py @@ -11,7 +11,9 @@ from types import SimpleNamespace import pytest +import torch +from isaaclab.envs.utils.camera_view import apply_camera_view_from_origins from isaaclab.visualizers.base_visualizer import BaseVisualizer from isaaclab.visualizers.visualizer import Visualizer from isaaclab.visualizers.visualizer_cfg import VisualizerCfg @@ -27,6 +29,13 @@ def test_create_visualizer_raises_for_base_cfg(): cfg.create_visualizer() +def test_visualizer_cfg_tiled_camera_view_is_opt_in(): + cfg = VisualizerCfg() + assert cfg.focal_length == 12.0 + assert cfg.tiled_cam_view is False + assert cfg.tiled_cam_num == 16 + + def test_create_visualizer_raises_for_unknown_type(): cfg = VisualizerCfg(visualizer_type="unknown-backend") with pytest.raises(ValueError, match="not registered"): @@ -90,6 +99,33 @@ def get_camera_transforms(self): return self._transforms +class _FakeCamera: + device = "cpu" + + def __init__(self): + self.set_world_poses_from_view_calls = [] + self.update_poses_calls = [] + + def set_world_poses_from_view(self, eyes, targets, env_ids=None): + self.set_world_poses_from_view_calls.append((eyes.clone(), targets.clone(), env_ids)) + + def _update_poses(self, dt): + self.update_poses_calls.append(dt) + + +def test_apply_camera_view_from_origins_forwards_env_ids(): + camera = _FakeCamera() + origins = torch.tensor([[1.0, 2.0, 3.0]]) + + apply_camera_view_from_origins(camera, origins, eye=(0.5, 0.0, 1.0), lookat=(0.0, 0.0, 0.0), env_ids=[2]) + + eyes, targets, env_ids = camera.set_world_poses_from_view_calls[0] + assert eyes.tolist() == [[1.5, 2.0, 4.0]] + assert targets.tolist() == [[1.0, 2.0, 3.0]] + assert env_ids == [2] + assert camera.update_poses_calls == [None] + + def test_compute_visualized_env_ids_cap_only_returns_none(): """Cap-only path: :meth:`_compute_visualized_env_ids` is ``None``. diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py index 2145af34be02..8750afe27932 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py @@ -76,6 +76,9 @@ def __init__(self, cfg: KitVisualizerCfg): self._camera_env_indices: list[int] = [] self._camera_is_owned = False self._generated_camera_prim_paths: list[str] = [] + self._generated_camera_xform_ops: dict[str, tuple[UsdGeom.XformOp, UsdGeom.XformOp]] = {} + self._generated_camera_pose_cache: dict[str, tuple[float, ...]] = {} + self._generated_camera_poses_dirty = False self._camera_image_provider = None self._camera_image_window = None self._camera_gpu_upload_tensor = None @@ -166,9 +169,11 @@ def close(self) -> None: return self._restore_env_visibility() if self._camera_sensor is not None and self._camera_is_owned: - self._camera_sensor.__del__() remove_generated_prims(self._generated_camera_prim_paths) self._camera_sensor = None + self._generated_camera_xform_ops.clear() + self._generated_camera_pose_cache.clear() + self._generated_camera_poses_dirty = False self._camera_image_provider = None self._camera_image_window = None self._simulation_app = None @@ -329,13 +334,13 @@ def _setup_camera_sensor_view(self, num_envs: int) -> None: """Resolve or create the Camera sensor backing non-interactive image views.""" if not self._uses_camera_sensor_view(): return + if self._runtime_headless: + return if not get_settings_manager().get("/isaaclab/cameras_enabled", False): raise RuntimeError( "[KitVisualizer] tiled_cam_view=True requires camera rendering support. " "Rerun with --enable_cameras, or disable tiled_cam_view for this visualizer config." ) - if self._runtime_headless: - return logger.debug( "[KitVisualizer] Setting up camera image view: tiled=%s source=%s num_envs=%s", self.cfg.tiled_cam_view, @@ -387,7 +392,7 @@ def _setup_camera_image_window(self) -> None: """Create a dockable Kit UI image panel for camera sensor RGB output.""" import omni.ui - title = self.cfg.viewport_name or "Visualizer Camera" + title = self.cfg.viewport_name or "Visualizer Tiled Camera" self._camera_image_provider = omni.ui.ByteImageProvider() self._camera_image_window = omni.ui.Window(title, width=self.cfg.window_width, height=self.cfg.window_height) with self._camera_image_window.frame: @@ -440,7 +445,9 @@ def _update_camera_image_panel(self, dt: float) -> None: return if self._camera_is_owned: self._update_owned_camera_poses() - self._sync_camera_pose_updates_to_kit() + if self._generated_camera_poses_dirty: + self._sync_camera_pose_updates_to_kit() + self._generated_camera_poses_dirty = False if self._camera_is_owned: self._camera_sensor.update(dt=dt, force_recompute=True) rgb = camera_rgb_batch(self._camera_sensor, self._camera_sensor_indices) @@ -562,21 +569,20 @@ def _set_generated_usd_camera_poses(self, eyes: torch.Tensor, targets: torch.Ten if 0 <= env_id < len(self._generated_camera_prim_paths) else f"/World/envs/env_{env_id}/VisualizerCamera" ) - self._set_usd_camera_pose(camera_path, eyes[local_idx], targets[local_idx]) + self._generated_camera_poses_dirty |= self._set_usd_camera_pose( + camera_path, eyes[local_idx], targets[local_idx] + ) - def _set_usd_camera_pose(self, camera_path: str, position, target) -> None: - """Apply eye/target camera pose directly to a USD camera prim.""" + def _set_usd_camera_pose(self, camera_path: str, position, target) -> bool: + """Apply eye/target camera pose directly to a USD camera prim. + + Returns: + ``True`` when authored values changed, otherwise ``False``. + """ # TODO: Remove this USD-side pose path once Fabric-backed camera transforms propagate reliably to Kit. usd_stage = self._scene_data_provider.usd_stage if self._scene_data_provider else None if usd_stage is None: - return - - camera = UsdGeom.Camera.Define(usd_stage, camera_path) - camera_xform = UsdGeom.Xformable(camera.GetPrim()) - camera_xform.ClearXformOpOrder() - # Generated visualizer cameras live under env prims, but eyes/targets are world-space. - # Reset the xform stack so Kit/Fabric sees the authored pose as a world pose. - camera_xform.SetResetXformStack(True) + return False eye = torch.as_tensor(position, dtype=torch.float32, device="cpu").reshape(1, 3) lookat = torch.as_tensor(target, dtype=torch.float32, device="cpu").reshape(1, 3) @@ -585,13 +591,40 @@ def _set_usd_camera_pose(self, camera_path: str, position, target) -> None: if torch.isnan(rotation_matrix).any(): raise ValueError("[KitVisualizer] Cannot set camera pose because eye and lookat are degenerate.") quat_xyzw = quat_from_matrix(rotation_matrix)[0] + pose_key = ( + float(eye[0, 0]), + float(eye[0, 1]), + float(eye[0, 2]), + float(quat_xyzw[0]), + float(quat_xyzw[1]), + float(quat_xyzw[2]), + float(quat_xyzw[3]), + ) + if self._generated_camera_pose_cache.get(camera_path) == pose_key: + return False + + if camera_path not in self._generated_camera_xform_ops: + camera = UsdGeom.Camera.Define(usd_stage, camera_path) + camera_xform = UsdGeom.Xformable(camera.GetPrim()) + camera_xform.ClearXformOpOrder() + # Generated visualizer cameras live under env prims, but eyes/targets are world-space. + # Reset the xform stack so Kit/Fabric sees the authored pose as a world pose. + camera_xform.SetResetXformStack(True) + translate_op = camera_xform.AddTranslateOp() + orient_op = camera_xform.AddOrientOp(UsdGeom.XformOp.PrecisionDouble) + self._generated_camera_xform_ops[camera_path] = (translate_op, orient_op) + else: + translate_op, orient_op = self._generated_camera_xform_ops[camera_path] + quat_gf = Gf.Quatd( float(quat_xyzw[3]), Gf.Vec3d(float(quat_xyzw[0]), float(quat_xyzw[1]), float(quat_xyzw[2])), ) - camera_xform.AddTranslateOp().Set(Gf.Vec3d(float(eye[0, 0]), float(eye[0, 1]), float(eye[0, 2]))) - camera_xform.AddOrientOp(UsdGeom.XformOp.PrecisionDouble).Set(quat_gf) + translate_op.Set(Gf.Vec3d(float(eye[0, 0]), float(eye[0, 1]), float(eye[0, 2]))) + orient_op.Set(quat_gf) + self._generated_camera_pose_cache[camera_path] = pose_key + return True def _apply_cfg_camera_pose_if_configured(self) -> None: """Apply configured camera pose from eye/lookat.""" diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py index 77fa17fa29d7..185a992cc1ed 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py @@ -486,7 +486,6 @@ def close(self) -> None: if self._viewer is not None: self._viewer = None if self._camera_sensor is not None and self._camera_is_owned: - self._camera_sensor.__del__() remove_generated_prims(self._generated_camera_prim_paths) self._camera_sensor = None self._is_closed = True From 6c8a5a82d396486c569d01c2731624a2122197dc Mon Sep 17 00:00:00 2001 From: Matthew Trepte Date: Wed, 20 May 2026 02:49:12 +0000 Subject: [PATCH 30/31] rm dup test --- source/isaaclab/changelog.d/mtrepte-tiled_viz.rst | 9 +++++++++ source/isaaclab/isaaclab/visualizers/base_visualizer.py | 7 +++++++ .../test/sim/test_simulation_context_visualizers.py | 5 ----- source/isaaclab_newton/changelog.d/mtrepte-tiled_viz.rst | 4 ++++ .../isaaclab_visualizers/kit/kit_visualizer.py | 5 +---- .../isaaclab_visualizers/newton/newton_visualizer.py | 5 +---- .../isaaclab_visualizers/rerun/rerun_visualizer.py | 5 +---- .../isaaclab_visualizers/viser/viser_visualizer.py | 5 +---- 8 files changed, 24 insertions(+), 21 deletions(-) create mode 100644 source/isaaclab/changelog.d/mtrepte-tiled_viz.rst create mode 100644 source/isaaclab_newton/changelog.d/mtrepte-tiled_viz.rst diff --git a/source/isaaclab/changelog.d/mtrepte-tiled_viz.rst b/source/isaaclab/changelog.d/mtrepte-tiled_viz.rst new file mode 100644 index 000000000000..d7a7dfe0f048 --- /dev/null +++ b/source/isaaclab/changelog.d/mtrepte-tiled_viz.rst @@ -0,0 +1,9 @@ +Added +^^^^^ + +* Added shared visualizer defaults for CLI-created visualizer configs. + +Fixed +^^^^^ + +* Fixed visualizer tiled camera defaults and camera pose helpers so tiled camera views remain opt-in and respect selected environment ids. diff --git a/source/isaaclab/isaaclab/visualizers/base_visualizer.py b/source/isaaclab/isaaclab/visualizers/base_visualizer.py index 9e41ddce3402..4d7accd97a05 100644 --- a/source/isaaclab/isaaclab/visualizers/base_visualizer.py +++ b/source/isaaclab/isaaclab/visualizers/base_visualizer.py @@ -53,6 +53,13 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: """ raise NotImplementedError + def _set_scene_data_provider(self, scene_data_provider: SceneDataProvider) -> SceneDataProvider: + """Store the scene data provider shared by all visualizer backends.""" + if scene_data_provider is None: + raise RuntimeError(f"{self.__class__.__name__} requires a scene_data_provider.") + self._scene_data_provider = scene_data_provider + return scene_data_provider + @abstractmethod def step(self, dt: float) -> None: """Update visualization for one step. diff --git a/source/isaaclab/test/sim/test_simulation_context_visualizers.py b/source/isaaclab/test/sim/test_simulation_context_visualizers.py index a597ba561cd5..094975033ab6 100644 --- a/source/isaaclab/test/sim/test_simulation_context_visualizers.py +++ b/source/isaaclab/test/sim/test_simulation_context_visualizers.py @@ -23,11 +23,6 @@ from isaaclab.visualizers.visualizer_cfg import VisualizerCfg -def test_web_visualizer_cfgs_do_not_open_browser_by_default(): - assert RerunVisualizerCfg().open_browser is False - assert ViserVisualizerCfg().open_browser is False - - def test_web_visualizer_cfgs_do_not_open_browser_by_default(): assert RerunVisualizerCfg().open_browser is False assert ViserVisualizerCfg().open_browser is False diff --git a/source/isaaclab_newton/changelog.d/mtrepte-tiled_viz.rst b/source/isaaclab_newton/changelog.d/mtrepte-tiled_viz.rst new file mode 100644 index 000000000000..96ea88dc9164 --- /dev/null +++ b/source/isaaclab_newton/changelog.d/mtrepte-tiled_viz.rst @@ -0,0 +1,4 @@ +Fixed +^^^^^ + +* Fixed Newton visualizer camera image views and state updates for PhysX-backed simulations. diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py index 8750afe27932..517951216330 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py @@ -62,7 +62,6 @@ def __init__(self, cfg: KitVisualizerCfg): self._is_initialized = False self._sim_time = 0.0 self._step_counter = 0 - self._scene_data_provider = None self._env_ids = None self._resolved_visible_env_ids: list[int] | None = None self._hidden_env_visibilities: dict[str, str] = {} @@ -96,9 +95,7 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: logger.debug("[KitVisualizer] initialize() called while already initialized.") return - if scene_data_provider is None: - raise RuntimeError("[KitVisualizer] Requires a scene_data_provider.") - self._scene_data_provider = scene_data_provider + scene_data_provider = self._set_scene_data_provider(scene_data_provider) usd_stage = scene_data_provider.usd_stage if usd_stage is None: raise RuntimeError("[KitVisualizer] USD stage not available from scene_data_provider.") diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py index 185a992cc1ed..4385cc7eec82 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py @@ -327,7 +327,6 @@ def __init__(self, cfg: NewtonVisualizerCfg): self._model = None self._state = None self._update_frequency = cfg.update_frequency - self._scene_data_provider = None self._last_camera_pose: tuple[tuple[float, float, float], tuple[float, float, float]] | None = None self._headless_no_viewer = False self._resolved_visible_env_ids: list[int] | None = None @@ -348,10 +347,8 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: if self._is_initialized: logger.debug("[NewtonVisualizer] initialize() called while already initialized.") return - if scene_data_provider is None: - raise RuntimeError("Newton visualizer requires a scene_data_provider.") - self._scene_data_provider = scene_data_provider + scene_data_provider = self._set_scene_data_provider(scene_data_provider) num_envs = scene_data_provider.num_envs metadata = {"num_envs": num_envs} self._env_ids = self._compute_visualized_env_ids() diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py index 751b14205698..c0fe97a02b1d 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py @@ -151,7 +151,6 @@ def __init__(self, cfg: RerunVisualizerCfg): self._step_counter = 0 self._model = None self._state = None - self._scene_data_provider = None self._last_camera_pose: tuple[tuple[float, float, float], tuple[float, float, float]] | None = None self._resolved_visible_env_ids: list[int] | None = None @@ -165,10 +164,8 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: if self._is_initialized: return - if scene_data_provider is None: - raise RuntimeError("Rerun visualizer requires a scene_data_provider.") - self._scene_data_provider = scene_data_provider + scene_data_provider = self._set_scene_data_provider(scene_data_provider) num_envs = scene_data_provider.num_envs self._env_ids = self._compute_visualized_env_ids() self._model = NewtonManager.get_model() diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py index 57282b3152db..293d26c570b1 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py @@ -130,7 +130,6 @@ def __init__(self, cfg: ViserVisualizerCfg): self._model: Any | None = None self._state = None self._sim_time = 0.0 - self._scene_data_provider = None self._active_record_path: str | None = None self._last_camera_pose: tuple[tuple[float, float, float], tuple[float, float, float]] | None = None self._pending_camera_pose: tuple[tuple[float, float, float], tuple[float, float, float]] | None = None @@ -148,10 +147,8 @@ def initialize(self, scene_data_provider: SceneDataProvider) -> None: if self._is_initialized: logger.debug("[ViserVisualizer] initialize() called while already initialized.") return - if scene_data_provider is None: - raise RuntimeError("Viser visualizer requires a scene_data_provider.") - self._scene_data_provider = scene_data_provider + scene_data_provider = self._set_scene_data_provider(scene_data_provider) num_envs = scene_data_provider.num_envs metadata = {"num_envs": num_envs} self._env_ids = self._compute_visualized_env_ids() From a36ab7ab56d196b2214c37cb986e10282a089425 Mon Sep 17 00:00:00 2001 From: Daniela Hase <116915287+daniela-hase@users.noreply.github.com> Date: Tue, 19 May 2026 20:54:58 -0700 Subject: [PATCH 31/31] Scene Data Provider Update (#5622) --- docs/source/api/index.rst | 1 + docs/source/api/lab/isaaclab.scene_data.rst | 33 +++++++ docs/source/features/visualization.rst | 2 +- .../multi_backend_architecture.rst | 9 +- .../core-concepts/scene_data_providers.rst | 10 +- .../daniela-move-scene-data.major.rst | 23 +++++ source/isaaclab/docs/CHANGELOG.rst | 12 +-- source/isaaclab/isaaclab/physics/__init__.pyi | 3 - .../isaaclab/physics/physics_manager.py | 2 +- .../isaaclab/physics/scene_data_backend.py | 61 ------------- .../isaaclab/isaaclab/scene_data/__init__.py | 22 +++++ .../isaaclab/isaaclab/scene_data/__init__.pyi | 13 +++ .../isaaclab/scene_data/scene_data_backend.py | 91 +++++++++++++++++++ .../scene_data_provider.py | 2 +- .../isaaclab/sim/simulation_context.py | 7 +- .../isaaclab/visualizers/base_visualizer.py | 2 +- ...test_newton_manager_visualization_state.py | 2 +- .../changelog.d/daniela-move-scene-data.rst | 14 +++ source/isaaclab_newton/docs/CHANGELOG.rst | 4 +- .../isaaclab_newton/physics/newton_manager.py | 36 ++++---- .../changelog.d/daniela-move-scene-data.rst | 7 ++ .../physics/ovphysx_manager.py | 3 +- .../changelog.d/daniela-move-scene-data.rst | 6 ++ source/isaaclab_physx/docs/CHANGELOG.rst | 2 +- .../isaaclab_physx/physics/physx_manager.py | 3 +- .../changelog.d/daniela-move-scene-data.rst | 11 +++ .../kit/kit_visualizer.py | 2 +- .../newton/newton_visualizer.py | 2 +- .../rerun/rerun_visualizer.py | 2 +- .../viser/viser_visualizer.py | 2 +- 30 files changed, 271 insertions(+), 118 deletions(-) create mode 100644 docs/source/api/lab/isaaclab.scene_data.rst create mode 100644 source/isaaclab/changelog.d/daniela-move-scene-data.major.rst delete mode 100644 source/isaaclab/isaaclab/physics/scene_data_backend.py create mode 100644 source/isaaclab/isaaclab/scene_data/__init__.py create mode 100644 source/isaaclab/isaaclab/scene_data/__init__.pyi create mode 100644 source/isaaclab/isaaclab/scene_data/scene_data_backend.py rename source/isaaclab/isaaclab/{scene => scene_data}/scene_data_provider.py (99%) create mode 100644 source/isaaclab_newton/changelog.d/daniela-move-scene-data.rst create mode 100644 source/isaaclab_ovphysx/changelog.d/daniela-move-scene-data.rst create mode 100644 source/isaaclab_physx/changelog.d/daniela-move-scene-data.rst create mode 100644 source/isaaclab_visualizers/changelog.d/daniela-move-scene-data.rst diff --git a/docs/source/api/index.rst b/docs/source/api/index.rst index 452cadfebcc4..2ff0cf6174be 100644 --- a/docs/source/api/index.rst +++ b/docs/source/api/index.rst @@ -25,6 +25,7 @@ The following modules are available in the ``isaaclab`` extension: physics renderers scene + scene_data sensors sim terrains diff --git a/docs/source/api/lab/isaaclab.scene_data.rst b/docs/source/api/lab/isaaclab.scene_data.rst new file mode 100644 index 000000000000..c36b7666861a --- /dev/null +++ b/docs/source/api/lab/isaaclab.scene_data.rst @@ -0,0 +1,33 @@ +isaaclab.scene_data +=================== + +.. automodule:: isaaclab.scene_data + + .. rubric:: Classes + + .. autosummary:: + + SceneDataProvider + SceneDataBackend + SceneDataFormat + +Scene Data Provider +------------------- + +.. autoclass:: SceneDataProvider + :members: + :undoc-members: + :show-inheritance: + +Scene Data Backend +------------------ + +.. autoclass:: SceneDataBackend + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: SceneDataFormat + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/features/visualization.rst b/docs/source/features/visualization.rst index c23d9d4b5fd2..91a6027e72c5 100644 --- a/docs/source/features/visualization.rst +++ b/docs/source/features/visualization.rst @@ -547,7 +547,7 @@ Currently, live plots are only available in the Kit Visualizer. **Viser Visualizer Renderer Requirement** The Viser visualizer requires a Newton model, which is provided automatically by -:class:`~isaaclab.scene.scene_data_provider.SceneDataProvider` regardless of the active physics +:class:`~isaaclab.scene_data.SceneDataProvider` regardless of the active physics backend or renderer. It is compatible with all rendering backends (RTX, Newton Warp, OVRTX). diff --git a/docs/source/overview/core-concepts/multi_backend_architecture.rst b/docs/source/overview/core-concepts/multi_backend_architecture.rst index 8a946b50023c..55c524b22c33 100644 --- a/docs/source/overview/core-concepts/multi_backend_architecture.rst +++ b/docs/source/overview/core-concepts/multi_backend_architecture.rst @@ -56,7 +56,7 @@ This pattern applies to all simulation components: - :class:`~isaaclab_physx.renderers.IsaacRtxRenderer` - :class:`~isaaclab_newton.renderers.NewtonWarpRenderer` * - Scene Data Backend - - :class:`~isaaclab.physics.SceneDataBackend` + - :class:`~isaaclab.scene_data.SceneDataBackend` - ``PhysxSceneDataBackend`` (in :mod:`isaaclab_physx.physics`) - ``NewtonSceneDataBackend`` (in :mod:`isaaclab_newton.physics`) * - Cloner @@ -272,14 +272,15 @@ the established conventions: **2. Implement the physics manager:** -The manager must expose a :class:`~isaaclab.physics.SceneDataBackend` so that -:class:`~isaaclab.scene.scene_data_provider.SceneDataProvider` can read your backend's body +The manager must expose a :class:`~isaaclab.scene_data.SceneDataBackend` so that +:class:`~isaaclab.scene_data.SceneDataProvider` can read your backend's body transforms in a Warp-native format that renderers and visualizers consume directly. .. code-block:: python # isaaclab_mybackend/physics/mybackend_manager.py - from isaaclab.physics import PhysicsManager, SceneDataBackend, SceneDataFormat + from isaaclab.physics import PhysicsManager + from isaaclab.scene_data import SceneDataBackend, SceneDataFormat class MyBackendSceneDataBackend(SceneDataBackend): diff --git a/docs/source/overview/core-concepts/scene_data_providers.rst b/docs/source/overview/core-concepts/scene_data_providers.rst index a279735efb31..9562610e8b02 100644 --- a/docs/source/overview/core-concepts/scene_data_providers.rst +++ b/docs/source/overview/core-concepts/scene_data_providers.rst @@ -1,7 +1,7 @@ Scene Data Provider =================== -The :class:`~isaaclab.scene.scene_data_provider.SceneDataProvider` bridges physics simulation +The :class:`~isaaclab.scene_data.SceneDataProvider` bridges physics simulation backends and the visualizers/renderers that consume scene data. It exposes a single Warp-native read path for body transforms regardless of which physics backend (PhysX or Newton) is active, so renderers and visualizers can stay backend-agnostic. @@ -12,7 +12,7 @@ Overview Isaac Lab supports multiple physics backends (PhysX and Newton) and multiple visualizers (Omniverse Kit, Newton, Rerun, Viser). Each combination needs scene data to flow from the physics engine into the renderer or visualizer. The :class:`SceneDataProvider` owns this flow: -the physics manager provides a :class:`~isaaclab.physics.SceneDataBackend` that wraps its +the physics manager provides a :class:`~isaaclab.scene_data.SceneDataBackend` that wraps its native tensor views, and the provider handles format conversion and re-mapping on top of it. .. code-block:: python @@ -28,9 +28,9 @@ Architecture The system has three layers: -1. :class:`~isaaclab.physics.SceneDataBackend` — small interface implemented by each physics +1. :class:`~isaaclab.scene_data.SceneDataBackend` — small interface implemented by each physics manager. It exposes the backend's transform array directly as one of the - :class:`~isaaclab.physics.SceneDataFormat` Warp structs, plus the per-transform prim paths + :class:`~isaaclab.scene_data.SceneDataFormat` Warp structs, plus the per-transform prim paths and total count. There is no per-frame "update" call — the property accessors return live views into the underlying tensor each time they're read. @@ -40,7 +40,7 @@ The system has three layers: - :attr:`SceneDataBackend.transform_count` — number of transforms. - :attr:`SceneDataBackend.transform_paths` — list of USD prim paths, one per transform. -2. :class:`~isaaclab.scene.scene_data_provider.SceneDataProvider` — wraps a backend and offers +2. :class:`~isaaclab.scene_data.SceneDataProvider` — wraps a backend and offers format conversion plus index re-mapping: - :meth:`SceneDataProvider.get_transforms` — write the backend's transforms into a diff --git a/source/isaaclab/changelog.d/daniela-move-scene-data.major.rst b/source/isaaclab/changelog.d/daniela-move-scene-data.major.rst new file mode 100644 index 000000000000..39c979e086c4 --- /dev/null +++ b/source/isaaclab/changelog.d/daniela-move-scene-data.major.rst @@ -0,0 +1,23 @@ +Added +^^^^^ + +* Added :mod:`isaaclab.scene_data` sub-package consolidating + :class:`~isaaclab.scene_data.SceneDataProvider`, + :class:`~isaaclab.scene_data.SceneDataBackend`, and + :class:`~isaaclab.scene_data.SceneDataFormat` in a single import location. + +Changed +^^^^^^^ + +* **Breaking:** Moved :class:`~isaaclab.scene_data.SceneDataProvider` from + :mod:`isaaclab.scene.scene_data_provider` and + :class:`~isaaclab.scene_data.SceneDataBackend` / + :class:`~isaaclab.scene_data.SceneDataFormat` from :mod:`isaaclab.physics` + to the new :mod:`isaaclab.scene_data` sub-package. Update imports:: + + # before + from isaaclab.scene.scene_data_provider import SceneDataProvider + from isaaclab.physics import SceneDataBackend, SceneDataFormat + + # after + from isaaclab.scene_data import SceneDataProvider, SceneDataBackend, SceneDataFormat diff --git a/source/isaaclab/docs/CHANGELOG.rst b/source/isaaclab/docs/CHANGELOG.rst index 93d4c67b9497..68028a87110e 100644 --- a/source/isaaclab/docs/CHANGELOG.rst +++ b/source/isaaclab/docs/CHANGELOG.rst @@ -272,9 +272,9 @@ Added ``JointTypeFloatingBase``, OCS2's ``generalizedCoordinatesNum = 6 + actuatedJointsNum``, iDynTree's ``getFreeFloatingMassMatrix`` returning ``(6 + dofs, 6 + dofs)``). -* Added :attr:`~isaaclab.scene.scene_data_provider.SceneDataProvider.usd_stage`, - :attr:`~isaaclab.scene.scene_data_provider.SceneDataProvider.num_envs`, and - :meth:`~isaaclab.scene.scene_data_provider.SceneDataProvider.get_camera_transforms` +* Added :attr:`~isaaclab.scene_data.SceneDataProvider.usd_stage`, + :attr:`~isaaclab.scene_data.SceneDataProvider.num_envs`, and + :meth:`~isaaclab.scene_data.SceneDataProvider.get_camera_transforms` so visualizers and renderers can pull stage-derived data through the same Warp-native provider that already exposes transforms. @@ -309,12 +309,12 @@ Changed COM-referenced form can read :attr:`body_com_jacobian_w`. * **Breaking:** :class:`~isaaclab.visualizers.base_visualizer.BaseVisualizer` subclasses now receive a - :class:`~isaaclab.scene.scene_data_provider.SceneDataProvider` in + :class:`~isaaclab.scene_data.SceneDataProvider` in :meth:`~isaaclab.visualizers.base_visualizer.BaseVisualizer.initialize` instead of the removed ``BaseSceneDataProvider``. Read environment count - from :attr:`~isaaclab.scene.scene_data_provider.SceneDataProvider.num_envs` + from :attr:`~isaaclab.scene_data.SceneDataProvider.num_envs` and call - :meth:`~isaaclab.scene.scene_data_provider.SceneDataProvider.get_camera_transforms` + :meth:`~isaaclab.scene_data.SceneDataProvider.get_camera_transforms` on the new provider; both replace the previous ``get_metadata()`` / ``get_camera_transforms()`` calls on the legacy interface. diff --git a/source/isaaclab/isaaclab/physics/__init__.pyi b/source/isaaclab/isaaclab/physics/__init__.pyi index d2ceced82eb2..b4f910866210 100644 --- a/source/isaaclab/isaaclab/physics/__init__.pyi +++ b/source/isaaclab/isaaclab/physics/__init__.pyi @@ -8,10 +8,7 @@ __all__ = [ "PhysicsEvent", "PhysicsManager", "PhysicsCfg", - "SceneDataBackend", - "SceneDataFormat", ] from .physics_manager import CallbackHandle, PhysicsEvent, PhysicsManager from .physics_manager_cfg import PhysicsCfg -from .scene_data_backend import SceneDataBackend, SceneDataFormat diff --git a/source/isaaclab/isaaclab/physics/physics_manager.py b/source/isaaclab/isaaclab/physics/physics_manager.py index e6abd8287139..658679d5db01 100644 --- a/source/isaaclab/isaaclab/physics/physics_manager.py +++ b/source/isaaclab/isaaclab/physics/physics_manager.py @@ -15,7 +15,7 @@ from typing import TYPE_CHECKING, Any, ClassVar if TYPE_CHECKING: - from isaaclab.physics.scene_data_backend import SceneDataBackend + from isaaclab.scene_data import SceneDataBackend from isaaclab.sim.simulation_context import SimulationContext logger = logging.getLogger(__name__) diff --git a/source/isaaclab/isaaclab/physics/scene_data_backend.py b/source/isaaclab/isaaclab/physics/scene_data_backend.py deleted file mode 100644 index d94bea9e76c9..000000000000 --- a/source/isaaclab/isaaclab/physics/scene_data_backend.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). -# All rights reserved. -# -# SPDX-License-Identifier: BSD-3-Clause - -"""Backend interface and data formats for the scene data provider. - -These types live in :mod:`isaaclab.physics` rather than -:mod:`isaaclab.scene.scene_data_provider` so that physics backends -(``isaaclab_physx``, ``isaaclab_newton``) can subclass -:class:`SceneDataBackend` without pulling :mod:`isaaclab.scene` into the -``AppLauncher`` pre-launch import chain. ``AppLauncher._create_app`` pops -``*lab*`` modules from ``sys.modules`` during Kit init and any submodule -imported during that window ends up orphaned from its parent's -``__dict__`` after restoration. -""" - -from __future__ import annotations - -import warp as wp - - -class SceneDataFormat: - @wp.struct - class Vec3_Quat: - positions: wp.array(dtype=wp.vec3f) = None - orientations: wp.array(dtype=wp.quatf) = None - - @wp.struct - class Vec3_Matrix33: - positions: wp.array(dtype=wp.vec3f) = None - orientations: wp.array(dtype=wp.mat33f) = None - - @wp.struct - class Transform: - transforms: wp.array(dtype=wp.transformf) = None - - @wp.struct - class Matrix44: - matrices: wp.array(dtype=wp.mat44f) = None - - -class SceneDataBackend: - @property - def transforms( - self, - ) -> ( - SceneDataFormat.Vec3_Quat | SceneDataFormat.Transform | SceneDataFormat.Matrix44 | SceneDataFormat.Vec3_Matrix33 - ): - """Return the sim backends transforms as one of the SceneDataFormat structs.""" - raise NotImplementedError - - @property - def transform_count(self) -> int: - """Return the number of transforms in the sim backend.""" - raise NotImplementedError - - @property - def transform_paths(self) -> list[str]: - """Return the paths for each transform.""" - raise NotImplementedError diff --git a/source/isaaclab/isaaclab/scene_data/__init__.py b/source/isaaclab/isaaclab/scene_data/__init__.py new file mode 100644 index 000000000000..9e3eaa88579f --- /dev/null +++ b/source/isaaclab/isaaclab/scene_data/__init__.py @@ -0,0 +1,22 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Sub-package containing the scene data provider and backend interface. + +The :class:`SceneDataProvider` bridges physics simulation backends and the +consumers that read scene transforms (renderers and visualizers). Physics +backends implement :class:`SceneDataBackend` to expose their current +transforms in one of the :class:`SceneDataFormat` Warp struct variants; +the provider converts and remaps them on demand for each consumer. + +This package is deliberately separate from :mod:`isaaclab.scene` so that +physics backends (``isaaclab_physx``, ``isaaclab_newton``) can subclass +:class:`SceneDataBackend` without pulling :mod:`isaaclab.scene` into the +``AppLauncher`` pre-launch import chain. +""" + +from isaaclab.utils.module import lazy_export + +lazy_export() diff --git a/source/isaaclab/isaaclab/scene_data/__init__.pyi b/source/isaaclab/isaaclab/scene_data/__init__.pyi new file mode 100644 index 000000000000..2e17d33cf195 --- /dev/null +++ b/source/isaaclab/isaaclab/scene_data/__init__.pyi @@ -0,0 +1,13 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +__all__ = [ + "SceneDataBackend", + "SceneDataFormat", + "SceneDataProvider", +] + +from .scene_data_backend import SceneDataBackend, SceneDataFormat +from .scene_data_provider import SceneDataProvider diff --git a/source/isaaclab/isaaclab/scene_data/scene_data_backend.py b/source/isaaclab/isaaclab/scene_data/scene_data_backend.py new file mode 100644 index 000000000000..183b08246848 --- /dev/null +++ b/source/isaaclab/isaaclab/scene_data/scene_data_backend.py @@ -0,0 +1,91 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Backend interface and data formats for the scene data provider. + +These types live in :mod:`isaaclab.scene_data` rather than +:mod:`isaaclab.scene` so that physics backends (``isaaclab_physx``, +``isaaclab_newton``) can subclass :class:`SceneDataBackend` without pulling +:mod:`isaaclab.scene` into the ``AppLauncher`` pre-launch import chain. +``AppLauncher._create_app`` pops ``*lab*`` modules from ``sys.modules`` +during Kit init and any submodule imported during that window ends up +orphaned from its parent's ``__dict__`` after restoration. +""" + +from __future__ import annotations + +import warp as wp + +# Under Sphinx ``autodoc_mock_imports``, ``wp.struct`` is a ``_MockObject`` +# that replaces the decorated class with another mock, hiding its docstring +# and fields from autodoc. Fall back to an identity decorator when warp is +# mocked so the documentation builds from the source classes directly. +if getattr(wp, "__sphinx_mock__", False): + + def wp_struct(cls): + return cls +else: + wp_struct = wp.struct + + +class SceneDataFormat: + """Warp struct variants describing the transform layouts that a + :class:`SceneDataBackend` may publish to consumers. + """ + + @wp_struct + class Vec3_Quat: + """Separate position and quaternion arrays.""" + + positions: wp.array(dtype=wp.vec3f) = None + """Per-transform positions [m].""" + + orientations: wp.array(dtype=wp.quatf) = None + """Per-transform orientations as quaternions.""" + + @wp_struct + class Vec3_Matrix33: + """Separate position and rotation-matrix arrays.""" + + positions: wp.array(dtype=wp.vec3f) = None + """Per-transform positions [m].""" + + orientations: wp.array(dtype=wp.mat33f) = None + """Per-transform orientations as 3x3 rotation matrices.""" + + @wp_struct + class Transform: + """Packed warp transforms (position + quaternion).""" + + transforms: wp.array(dtype=wp.transformf) = None + """Per-transform packed position + orientation transforms [m, -].""" + + @wp_struct + class Matrix44: + """Packed 4x4 homogeneous transform matrices.""" + + matrices: wp.array(dtype=wp.mat44f) = None + """Per-transform 4x4 homogeneous transform matrices [m].""" + + +class SceneDataBackend: + @property + def transforms( + self, + ) -> ( + SceneDataFormat.Vec3_Quat | SceneDataFormat.Transform | SceneDataFormat.Matrix44 | SceneDataFormat.Vec3_Matrix33 + ): + """Return the sim backends transforms as one of the SceneDataFormat structs.""" + raise NotImplementedError + + @property + def transform_count(self) -> int: + """Return the number of transforms in the sim backend.""" + raise NotImplementedError + + @property + def transform_paths(self) -> list[str]: + """Return the paths for each transform.""" + raise NotImplementedError diff --git a/source/isaaclab/isaaclab/scene/scene_data_provider.py b/source/isaaclab/isaaclab/scene_data/scene_data_provider.py similarity index 99% rename from source/isaaclab/isaaclab/scene/scene_data_provider.py rename to source/isaaclab/isaaclab/scene_data/scene_data_provider.py index a44700dd575c..723652339b46 100644 --- a/source/isaaclab/isaaclab/scene/scene_data_provider.py +++ b/source/isaaclab/isaaclab/scene_data/scene_data_provider.py @@ -13,7 +13,7 @@ import numpy as np import warp as wp -from isaaclab.physics.scene_data_backend import SceneDataBackend, SceneDataFormat +from .scene_data_backend import SceneDataBackend, SceneDataFormat if TYPE_CHECKING: from pxr import Usd diff --git a/source/isaaclab/isaaclab/sim/simulation_context.py b/source/isaaclab/isaaclab/sim/simulation_context.py index 9975cf13100d..19712bde96b8 100644 --- a/source/isaaclab/isaaclab/sim/simulation_context.py +++ b/source/isaaclab/isaaclab/sim/simulation_context.py @@ -29,7 +29,7 @@ resolve_scene_data_requirements, ) from isaaclab.renderers.render_context import RenderContext -from isaaclab.scene.scene_data_provider import SceneDataProvider +from isaaclab.scene_data import SceneDataProvider from isaaclab.sim.utils import create_new_stage from isaaclab.utils.string import clear_resolve_matching_names_cache from isaaclab.utils.version import has_kit @@ -772,11 +772,6 @@ def update_visualizers(self, dt: float, skip_app_pumping: bool = False) -> None: if any(viz.supports_markers() for viz in self._visualizers): self.vis_marker_registry.dispatch_callbacks() - # Marker callbacks update VisualizationMarkers state; visualizer step() - # consumes that state later in this method. - if any(viz.supports_markers() for viz in self._visualizers): - self.vis_marker_registry.dispatch_callbacks() - visualizers_to_remove = [] for viz in self._visualizers: try: diff --git a/source/isaaclab/isaaclab/visualizers/base_visualizer.py b/source/isaaclab/isaaclab/visualizers/base_visualizer.py index 9e41ddce3402..4084aaef559b 100644 --- a/source/isaaclab/isaaclab/visualizers/base_visualizer.py +++ b/source/isaaclab/isaaclab/visualizers/base_visualizer.py @@ -16,7 +16,7 @@ from urllib.parse import urlparse if TYPE_CHECKING: - from isaaclab.scene.scene_data_provider import SceneDataProvider + from isaaclab.scene_data import SceneDataProvider from .visualizer_cfg import VisualizerCfg diff --git a/source/isaaclab/test/sim/test_newton_manager_visualization_state.py b/source/isaaclab/test/sim/test_newton_manager_visualization_state.py index 7c3abf9f300a..d444799620f5 100644 --- a/source/isaaclab/test/sim/test_newton_manager_visualization_state.py +++ b/source/isaaclab/test/sim/test_newton_manager_visualization_state.py @@ -11,7 +11,7 @@ :meth:`NewtonManager._build_visualization_model_from_stage`), and :meth:`NewtonManager.update_visualization_state` must copy fresh transforms into ``_state_0.body_q`` via the new -:class:`~isaaclab.scene.scene_data_provider.SceneDataProvider`. +:class:`~isaaclab.scene_data.SceneDataProvider`. """ from __future__ import annotations diff --git a/source/isaaclab_newton/changelog.d/daniela-move-scene-data.rst b/source/isaaclab_newton/changelog.d/daniela-move-scene-data.rst new file mode 100644 index 000000000000..1c077dc3f68a --- /dev/null +++ b/source/isaaclab_newton/changelog.d/daniela-move-scene-data.rst @@ -0,0 +1,14 @@ +Changed +^^^^^^^ + +* Updated imports of :class:`~isaaclab.scene_data.SceneDataBackend` and + :class:`~isaaclab.scene_data.SceneDataFormat` to their new location in + :mod:`isaaclab.scene_data` (previously :mod:`isaaclab.physics`). + +Fixed +^^^^^ + +* Fixed :meth:`~isaaclab_newton.physics.NewtonManager.update_visualization_state` + retrieving the wrong simulation context. It now uses + :meth:`~isaaclab.sim.SimulationContext.instance` instead of the stale + ``PhysicsManager._sim`` reference. diff --git a/source/isaaclab_newton/docs/CHANGELOG.rst b/source/isaaclab_newton/docs/CHANGELOG.rst index 836ba4a2c133..03ceddc82005 100644 --- a/source/isaaclab_newton/docs/CHANGELOG.rst +++ b/source/isaaclab_newton/docs/CHANGELOG.rst @@ -116,7 +116,7 @@ Added USD stage (via :meth:`~isaaclab_newton.physics.NewtonManager.instantiate_builder_from_stage`) and refreshes ``state_0.body_q`` from rigid-body transforms supplied by the - :class:`~isaaclab.scene.scene_data_provider.SceneDataProvider` each render + :class:`~isaaclab.scene_data.SceneDataProvider` each render frame. Changed @@ -149,7 +149,7 @@ Removed (``NewtonSceneDataProvider``). Replace direct uses with :meth:`~isaaclab_newton.physics.NewtonManager.get_model` / :meth:`~isaaclab_newton.physics.NewtonManager.get_state` and the - Warp-native :class:`~isaaclab.scene.scene_data_provider.SceneDataProvider`. + Warp-native :class:`~isaaclab.scene_data.SceneDataProvider`. Fixed ^^^^^ diff --git a/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py b/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py index 73f1ac535049..f41afa85678b 100644 --- a/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py +++ b/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py @@ -33,7 +33,9 @@ from newton.sensors import SensorIMU as NewtonSensorIMU from newton.solvers import SolverBase, SolverKamino, SolverNotifyFlags -from isaaclab.physics import CallbackHandle, PhysicsEvent, PhysicsManager, SceneDataBackend, SceneDataFormat +from isaaclab.physics import CallbackHandle, PhysicsEvent, PhysicsManager +from isaaclab.scene_data import SceneDataBackend, SceneDataFormat +from isaaclab.sim import SimulationContext from isaaclab.sim.utils.newton_model_utils import replace_newton_shape_colors from isaaclab.sim.utils.stage import get_current_stage from isaaclab.utils import checked_apply @@ -264,8 +266,8 @@ class NewtonManager(PhysicsManager): # Visualization-only state used when the sim backend is PhysX. Populated # lazily in :meth:`_ensure_visualization_model` and updated each render # frame in :meth:`update_visualization_state`. - _visualization_scene_data: SceneDataFormat.Transform | None = None - _visualization_mapping: wp.array | None = None + _scene_data: SceneDataFormat.Transform | None = None + _scene_data_mapping: wp.array | None = None # Views list for assets to register their views _views: list = [] @@ -609,7 +611,7 @@ def close(cls) -> None: super().close() @classmethod - def get_scene_data_backend(cls) -> SceneDataBackend: + def get_scene_data_backend(cls) -> SceneDataBackend | None: """Return the SceneDataBackend for the SceneDataProvider.""" return cls._scene_data_backend @@ -683,8 +685,8 @@ def clear(cls): NewtonManager._transforms_dirty = False NewtonManager._particles_dirty = False NewtonManager._up_axis = "Z" - NewtonManager._visualization_scene_data = None - NewtonManager._visualization_mapping = None + NewtonManager._scene_data = None + NewtonManager._scene_data_mapping = None NewtonManager._model_changes = set() NewtonManager._scene_data_backend = None NewtonManager._cl_pending_sites = {} @@ -1573,7 +1575,7 @@ def _ensure_visualization_model(cls) -> None: :meth:`_build_visualization_model_from_stage` and finalizing the resulting :class:`~newton.ModelBuilder`. Per-frame body transforms are pushed into ``_state_0.body_q`` by :meth:`update_visualization_state` using the new - :class:`~isaaclab.scene.scene_data_provider.SceneDataProvider`. + :class:`~isaaclab.scene_data.SceneDataProvider`. """ if cls._model is not None and cls._state_0 is not None: @@ -1767,7 +1769,7 @@ def update_visualization_state(cls, scene_data_provider=None) -> None: already advanced by :meth:`step` / forward kinematics. PhysX sim backend: pull rigid-body transforms from the - :class:`~isaaclab.scene.scene_data_provider.SceneDataProvider` and write + :class:`~isaaclab.scene_data.SceneDataProvider` and write them into the shadow ``_state_0.body_q`` so Newton-native consumers (Newton renderer, Newton/Rerun/Viser visualizers, OVRTX renderer, Newton GL video) see fresh poses. @@ -1782,24 +1784,20 @@ def update_visualization_state(cls, scene_data_provider=None) -> None: return sdp = scene_data_provider if sdp is None: - sim = PhysicsManager._sim + sim = SimulationContext.instance() if sim is not None: sdp = sim.get_scene_data_provider() if sdp is None: return - if cls._visualization_scene_data is None: - cls._visualization_scene_data = SceneDataFormat.Transform() - if cls._visualization_mapping is None: + if cls._scene_data is None: + cls._scene_data = SceneDataFormat.Transform() + if cls._scene_data_mapping is None: body_paths = list(getattr(cls._model, "body_label", None) or []) - cls._visualization_mapping = sdp.create_mapping(body_paths) + cls._scene_data_mapping = sdp.create_mapping(body_paths) - cls._visualization_scene_data.transforms = cls._state_0.body_q - sdp.get_transforms( - cls._visualization_scene_data, - mapping=cls._visualization_mapping, - allow_passthrough=False, - ) + cls._scene_data.transforms = cls._state_0.body_q + sdp.get_transforms(cls._scene_data, mapping=cls._scene_data_mapping) @classmethod def get_state_1(cls) -> State: diff --git a/source/isaaclab_ovphysx/changelog.d/daniela-move-scene-data.rst b/source/isaaclab_ovphysx/changelog.d/daniela-move-scene-data.rst new file mode 100644 index 000000000000..3a830a73152a --- /dev/null +++ b/source/isaaclab_ovphysx/changelog.d/daniela-move-scene-data.rst @@ -0,0 +1,7 @@ +Changed +^^^^^^^ + +* Updated imports of :class:`~isaaclab.scene_data.SceneDataBackend` and + :class:`~isaaclab.scene_data.SceneDataFormat` in + :mod:`~isaaclab_ovphysx.physics.ovphysx_manager` to their new location in + :mod:`isaaclab.scene_data` (previously :mod:`isaaclab.physics`). diff --git a/source/isaaclab_ovphysx/isaaclab_ovphysx/physics/ovphysx_manager.py b/source/isaaclab_ovphysx/isaaclab_ovphysx/physics/ovphysx_manager.py index 729b06befa95..14eb03ddc5fc 100644 --- a/source/isaaclab_ovphysx/isaaclab_ovphysx/physics/ovphysx_manager.py +++ b/source/isaaclab_ovphysx/isaaclab_ovphysx/physics/ovphysx_manager.py @@ -24,7 +24,8 @@ from pxr import UsdPhysics -from isaaclab.physics import PhysicsEvent, PhysicsManager, SceneDataBackend, SceneDataFormat +from isaaclab.physics import PhysicsEvent, PhysicsManager +from isaaclab.scene_data import SceneDataBackend, SceneDataFormat if TYPE_CHECKING: from isaaclab.sim.simulation_context import SimulationContext diff --git a/source/isaaclab_physx/changelog.d/daniela-move-scene-data.rst b/source/isaaclab_physx/changelog.d/daniela-move-scene-data.rst new file mode 100644 index 000000000000..d2e0b7df99ad --- /dev/null +++ b/source/isaaclab_physx/changelog.d/daniela-move-scene-data.rst @@ -0,0 +1,6 @@ +Changed +^^^^^^^ + +* Updated imports of :class:`~isaaclab.scene_data.SceneDataBackend` and + :class:`~isaaclab.scene_data.SceneDataFormat` to their new location in + :mod:`isaaclab.scene_data` (previously :mod:`isaaclab.physics`). diff --git a/source/isaaclab_physx/docs/CHANGELOG.rst b/source/isaaclab_physx/docs/CHANGELOG.rst index 8cee80884415..416ad468b059 100644 --- a/source/isaaclab_physx/docs/CHANGELOG.rst +++ b/source/isaaclab_physx/docs/CHANGELOG.rst @@ -100,7 +100,7 @@ Removed * **Breaking:** Removed the ``isaaclab_physx.scene_data_providers`` package (``PhysxSceneDataProvider``). The Warp-native - :class:`~isaaclab.scene.scene_data_provider.SceneDataProvider` now exposes + :class:`~isaaclab.scene_data.SceneDataProvider` now exposes PhysX rigid-body transforms via :class:`~isaaclab_physx.physics.PhysxSceneDataBackend`, and the PhysX→Newton state sync used by Newton visualizers/renderers moved to diff --git a/source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py b/source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py index 00e9ed3007d6..df30c9e268a7 100644 --- a/source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py +++ b/source/isaaclab_physx/isaaclab_physx/physics/physx_manager.py @@ -32,7 +32,8 @@ from pxr import Sdf, Usd, UsdPhysics, UsdUtils import isaaclab.sim as sim_utils -from isaaclab.physics import CallbackHandle, PhysicsEvent, PhysicsManager, SceneDataBackend, SceneDataFormat +from isaaclab.physics import CallbackHandle, PhysicsEvent, PhysicsManager +from isaaclab.scene_data import SceneDataBackend, SceneDataFormat from isaaclab.utils.string import to_camel_case if TYPE_CHECKING: diff --git a/source/isaaclab_visualizers/changelog.d/daniela-move-scene-data.rst b/source/isaaclab_visualizers/changelog.d/daniela-move-scene-data.rst new file mode 100644 index 000000000000..ea709a9f96b3 --- /dev/null +++ b/source/isaaclab_visualizers/changelog.d/daniela-move-scene-data.rst @@ -0,0 +1,11 @@ +Fixed +^^^^^ + +* Updated ``configclass`` imports in :mod:`isaaclab_visualizers.kit`, + :mod:`isaaclab_visualizers.newton`, :mod:`isaaclab_visualizers.rerun`, and + :mod:`isaaclab_visualizers.viser` visualizer configs to import from + :mod:`isaaclab.utils.configclass` directly, matching the lazy-import layout + introduced in :mod:`isaaclab.utils`. +* Updated ``test_visualizer_cartpole_integration`` to read the tiled camera + RGB output via the ``.torch`` accessor, matching the Warp-backed camera + data API. diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py index 133c565ccbad..ec51d8b183d4 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/kit/kit_visualizer.py @@ -23,7 +23,7 @@ logger = logging.getLogger(__name__) if TYPE_CHECKING: - from isaaclab.scene.scene_data_provider import SceneDataProvider + from isaaclab.scene_data import SceneDataProvider _DEFAULT_VIEWPORT_NAME = "Visualizer Viewport" diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py index 565c16b0251b..43aa7d628e80 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py @@ -24,7 +24,7 @@ logger = logging.getLogger(__name__) if TYPE_CHECKING: - from isaaclab.scene.scene_data_provider import SceneDataProvider + from isaaclab.scene_data import SceneDataProvider class NewtonViewerGL(ViewerGL): diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py index cc55dcef7fa5..c83439a5b9f3 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py @@ -28,7 +28,7 @@ from .rerun_visualizer_cfg import RerunVisualizerCfg if TYPE_CHECKING: - from isaaclab.scene.scene_data_provider import SceneDataProvider + from isaaclab.scene_data import SceneDataProvider logger = logging.getLogger(__name__) diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py index 5848690263f1..10d117873c7d 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py @@ -28,7 +28,7 @@ logger = logging.getLogger(__name__) if TYPE_CHECKING: - from isaaclab.scene.scene_data_provider import SceneDataProvider + from isaaclab.scene_data import SceneDataProvider def _disable_viser_runtime_client_rebuild_if_bundled() -> None:
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�X7HB)L5U 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 -|F%8jyI? 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

s2F1r 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�gc`LuzLf)s_s5fAd_``fRum9)Z5`I! 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%0Au#qR{BoNHj8LZa#ga z-HRy{pol)*=}a0u+dtv`_R*H<2MViTs1XN1g_)BFo*(I*n|=a?wK%x zqUjQlMnVzKr65V~;rNz8&N#6O)AJfgaWhzYJPW=$17^qV21E0S2O`&Du@fZMy+L^4 zVUGoKBQlIBa{`6IS;bdSvPy&S2__@0OnD;FM)9I(<1FaCm-hKTHQ3?Hi(B7{bLY zPNLE+0m&S3a~*I`p%jZF?nVoyt|XGFKr%!AwHTLW)(NJEY4${Q#y$awCqj(ESGn8R zi^z(7$(xAP1Ge(;%*-{PrS~B7FVOr8y8R6sU(&3k3qC6WThDsHxGRq)8>G*XFsS5Z=oF?){IuVL^5MJ$e~ybTbGa*2a3!F`p{p~Vhu5QGg}@uQM! z9JRvp{n%&Z$(efhSKsy1N@?kncv68sUI zW`~g>q3g^pSeKMy!j_gtVDNZl;-#WMTCwa3r4?6NSggzr0JlyZ;UfEZ0>#OPS(LLU zx-hab(Yrb#Jh1f4qBVFHn1^Qu?P!|j&`*M10iSS@18vurUK~zlS&cdJg0e$x&aB+) z6_o%E7@`p$Q6V#AB?p|qk$W|{p_o|L`XNRYn=Lv|DU9fZ$0{fA+{-jj$KStG&1vpg zjtop5i%h?x0$r%IP>gb+CifB39=4XcQe;uwkXAmd*jFi!at;$!4tL zpz>U|8ZaMkmBfgtwEhrK0jr#Ke-&xmPil9T4zV+G%2`ku z&xVubuuxQGT9jZZktTsk_lh9IJGHWk(jll^y*s*kXL!k`<FX}vC2W|)VSl+-b60?h6G$wvECSV(VNF=uC^jiujYU)PpVMI@T+Pn8^Qp;IxjpK*W6nS_v3H>lr?U4aA#9)F{M)!cTqG2onu9qg!RXjzjSHqk_dJNY0{lnZdX1O zvjddsUbzh%@)UjnLJbD!`J%+9%^aZbXzufRCK*&$x0KTHdl+KbEaoo@<%GbQ1*o8@ zOCI7!PDZki>|t(@la;H;sN~eXhkOW%B$Lv87gH)wPP$i7hj7&-lwJar>vyWHyOu~H zobaIC>()-+)Dlxj#zrYE6nS+fVev|zA~Day0xI?;IKCL2Z&WkoPmnngvEV=;UcFxF zYQE)9yWClYgT2+uzqZz@LgH4*mNI` zuO((5^=>`QqU;Jl2ujXqja`&A&*mk(q7-6AAJ(RRbattXYwjVO8DFT2kY7BIY9s(*{P~neAKPZBn*47nM!6WknD(;H# zOJEp$%86oQ80|%R-&bVcd%jhY`6O{dy68t%Bwz!#C!9~|`$>AP(j-2ql!uv=`b=)? z>Q8b+-sP0a0{uox*2ksduNg8hQ=e6_BY?!11n)N87>*WfG^UDzZ`_Ze|AMW zcgHIZ)No?OOkY!&zG^BN=7ZAf4p%^qL(gCi@Bjc107*naRECrWIy|g`FJWw+L2!;C z9-w;o6uTnGIC%%dXnx29!r1m7{lePPEQh!nKJv_oEM5SEgHfJ_Jfva++BqN>i5%>? zdt52ue<0ofCQzBI4jBYbH}LoS1JA=~M3RNY>@4Qidn_=rwhL%+=v`PxVE}`;!NRUw z2xhpV9m&&3t24-vX+&G^#~OPP3>@jo9`Xwb#KaJ0FwmF9AF^~94QM)?z#FD04)B1w z3O8rcmo(3kuC3LfY`-#YP~unSb}ZP^9AHkPvpBB%r*eOt=GH8~j)$e~gepgLZRKv_ z^{MKTohSk%!m`EFqMP9~$b?1MGf|CM3N^8ECYR#2F`j4PEy?rR#tXMo9)?;nFLEqP zb#|HJVV*bbaz6A~6)nIhud-LJ(bw`S#cc*u7-E5_60`J2&+Z;SKbq|_3g5-meJniI zCR1v@uX8dg#oD7&hqKC=hT=0}`raQjOW)6?uU^$Xizqemq+17-Zu!Xc5|v)sI=>R9 zOqauH`hPO({@F17nO5Q_>%RypWdm4!R;Acc>2gcN|7_oJK9R11tJR*}rsP6(LIt?p z%o1>FUG`nY3s-ETgDDrqzOU|-UB;X0?Baomm936;QS{Yb%rg4ONpG_jN0Bj%R{AbvVSw>~k5m*-y zQND251oYC1O6V*0yWsvw+Xb9mtak!{8Q{)C&e^)bnfU@RxgCt^#3Oc^D z3B&H@>y4bA*S`TtCy5$teh> z+);WS1;kKGb~;l4$q4=}-Ddt0mrr20QMp)9uj|E&rWofPqeUy)Ru7eIJl9-yld>UmirDH1n z+>AYXqdDwV%f5T-5wcf^kbg6F+kHqSb3f0nuXJl!Wk6e(l#OLXN4V7p$zrOU4Ah>! zGBZo}ivYFrnij9$p;j3T_e~D*fm{RzCMjhCMJ0hMf z`%1P;KrT_IZr;{NH z4u~o}*Hs)*n0+UuOw<78ii!~hO;)bV_){Egu;&N^n5irDak2FaPoMRpZdh0{kqb+O zU|wO&bUuAqkV?F;n0zuT##L-z#g1W(QpypP=q65}!ZNguao2ok!Iix+5-Af*tSC~? zZFN;(9iokTm$>L#kO!;`9y2b2dvL{kgx@c1U|Nktj^c!~=m^$9f3UN`ipHX_na4kHwu68TpLTh`3+|b;h$DsI zsUwkF@F^h&My&fTm>kEpJt*bAHtAy&#jpN}7J2hTd5;Hqbdq4;mNT62e!xkr+H+0P zCs}CFdWbxX{l;VAzUVbRp4DrP<5Xpb7~YRrvj`p`iOldmSMX?oDC{G!s=mSEf%~G` zIL1{TW%rk7SC+M4{lk^kSJ3ozZ2bq_-XM_)cj)2!`a>w?sd@S*B$Tra#Z^@sn=hy9 zpPXKK3aHeX_cuC4aGw|Jc&k_Qh4q=1UN6jT7Lz0G ze0Vn(`>EoO`tcOcPB7ZuVwgl^lJ%3?JuWg7LyZiz*F=xfJ!OvpGIjl%twm)#I-kp? zbBqL~!y8_`#glI^8^ibGVv%R`%h-%R{#tGTGjz!XU(H;FTw9IYVRYmIyU}jQmgXV; zb~zX0vPtWbt0e`K>;%Ka?7m+d zcpIY19r(KpQH)Y}I{{3Uj@yksA}Dt3w9O30M2d73W0C%mz zztxo4k`}@qLEnE1K68XxaD9*mhsS+?ougJ9c zu;Z*Z%pG6R8x~9R>4hR5)6J|RP~p4scCUQF%{dXzsHjTVTeH>-yVC_{*%EoWw9ejj z&zN>s>^+lz<^-5-KL_YhF>De_cR0f_@7X=+g%nURm(*aY>PM z>{G8ZmiobIMK%;$;GXb6f0tUwRtT#sy2ET^W2+cdAYOO&$3=KX#me(8H4y&Xq@tjQ zz`MUHh3QvVd-QON|Fxbudj(E7-!YW4%bw2BdfQZ)^n518hCxPd`r@DpeP0ky>_Xh- z0yTZvJ4rvoDgNXJx`37{)-k;l4MO^@}C|RF>&Zq*GQJf5blh zzcEA5C|s>Dm{Y^dpPZuP3uTOI%p-m+30g#WAkVPbc#uHD4jl4!W>e71RWu z8lRvSF5PcM*pg!QS?R`2Qiqm9Ez<*9tV+&W_4fE+Rvtq|IiyvLqm!yCRw!l9EoW$S z@GyO0jv`0x(FTSWwM%>p(j->F$_mC+4mjtc10gO&R$w6uRIs3Y&TkyM7-ISfXH`fk zxpoXMJ@%l#2`_d9e+=Z?XHuVe4~0+M&^1OS8sqQ$j$}eZ+JyG433QuXx;qNMBqEbpKA6W+O&nQmLD)4|EKcBAAx$4;E4t@I^-&YM(UR7GDgGv?I zl9E-bl19#vQ_e@1bBc*;rmt54mHgZQl?pe;li75+Kbo#bU=W|@`eJM;m{R!BPKDrI=du+2j2S8qe5RXb12kND&z!}N@% zc*vxlV`&OhG=x&LOWo8o%Ehe)IfRZfVt}JgTZj@{icvmxJ*G}tMMz!ROHElKS$V+5 z1NXH+%S9KjE()U5XzY;I=+d%V7rL8ZQ58E3of3oTHkx|(wVrTAb=*HskAU+VtqUcKGpr+^$(4@*%o;pN&L z1C=Y>gl1;ogrSizUgq}*u?kfa|9MG~cS#&mfRR1`-6z_8kD$3SJ$ z>`f7i&w&$49{>^I)^ z>3dP5A%RH*#tChY=I3<2A|MjF1ENEfsv>VXE*#Xx_8(Z1h5>u%QcziL=rp?ym{<9LE@4DWJviBZg2In(uNN1^bK<9jPS0jG3_SO0mPV3U!Z-p~^EeYO=(G?tS4izj^_xv#wDiyBLS3XNeDTcRQiWyGu} zE{`R=URF$OhqZy(r<19HiY?Hio_G@B^2IR|;ggV1oFWpdOfUcntCdL^46bmWo?O8& zedZ8awg@3J3Nj#;iIg|?5i2Y2d@}T8i-+d-@o!k;q$B490Rw~=4pXKZWEJ?nJeuOH zqW)sfobd53+AtztYuS>SEi@DUSlkuHvSTivAT(k>UdCqZ##HXIprHEU;dRG`2*<}F zq*x_4iRprPuI}TLps4_u$a@5VD0ZUh$-{saPr4t*!#ufh>bdzt*MF3f_w(zk=^iw0 zJ@A*y+4^`7MZDyAQUe&>AJ(!nY?Fs0)A>W>$^Bzd0N~Zn#f-^$#xw&e__}#%KCjU?DZ(Ig(ugkOGDGib z%#P~}45I98M2LT@?N|1-t&du8!m|)@DI9;WSIsW^Z8h}aBO%J%-*8PvfYJ1bxsQsJ zj;|Y@OJUiX>|RAPfxjgTFLGE$&Bd(Sif>a$;MT7qg5n#Ia&y`H@njA?Zyj!B`aASQ zK2#2NxD^I4$teVz+%v{^Qf#?wkP|$?&p- z<_yh;cp}FaYIa+ASQDy?cMvOo5;eqJL80{v?yp5ooOAFI@fDR*sYQ1e(~T1zF@WeL zS1RnHgc1|%>>y_UZvaM~s7FSnGgyYk(&|o{-HA5ixjh0_;XPGeCh;!;V0=nNsrrs` z@JfjcjHcF#k?5sY(l_Y!VmN-Z_8k=h$)f8W&9T_X=#B{~FqQEhfy&5}@#9rTpu)ht z0ZG?_B70;KM(uN~#RKN@;Eu|K>nJ{2Z_YTpJS1|sy2aQeO%oD??4d00ZYJC zK-?qVc*U@El_^Q4>S+!#oxGEt$g<y6VP zYKToO8N^dOH&45l9fFe)Y+yLNt#}5z7aU(>kEXDgm>#f+?ZF>RW`|Q>TAa*}IN}Le z10#u8NQ4J0U6`7>GMXlg*{3TEp%g4ODxpLq z)~`KQPK~7ApoUxV49O0)AxOsMZIga&jTFdzt++vOir9VF zx@I=tel$@I=LDIPeYn>S*-SNw+z5UM%;itG_85&@7+SGQ?d7qj$xq}T%bv6Yh3}1d z4p|_!WZT%una49Zqow&$X2Ij<+9Z)v ziwXD$vfI<0G@?d1TEC`oDtoY5yj0|sn~2#y{2(Ifge6Y3Ea(aYw=f^#O~kq8c%Jz| z_OsY&nPwXp11As6+yx8V!X{|Y(1ng>rc+iSryM+56#I|!97@Sq6b;zNZO^ML4<2H= zp4;<}iz+bt()#Gxg9mS#&emPuYV7q)PHDB;emp6vbhD!J1fsO-N&54~>Cc+GiYUw3 zt@<N}611jBEW8?^REZgak{M zvIbAi5-{-t3cD`ao}h*J)6;1cyWmlii|7_}ImNXCyqpFfqr%wTWnuc-jQ8(GsX(KP zKJgK*q`VhNT~X|c&`5ra3n^KYxSlzrWQ?ME%k`XV{w+JLUG%ZeO;fjEwJL0HWkzMw*IF)E z`N3TeuEaWIBVb83E$zK9tJBg1Ss4Q}uB@4)VrXr#%7lb(S} zceL9ctXkC~feHdDv$kK#zL-eS1^0MW+m#P) z!J0~3`nLf5SZ+G&K*6C`@1|Mg;11v>Cw;?P=i?L2(8!U6%VCh8*8QpFp{~>U^ za=7qr<)x=zR+XBF{Zp<+T6W+5@?Bwo(h?>^Mo<><63nPLmLMWedl;@e2TXYTL^@4e zR&$JgXB#{L#*kZdxY*(Yjx$*ThQ=^$fJcH5%aBkH>7&e-omT_3fpJiU{6KlcKH_!@ zyFy)8`ymPV79t%m*Zv?qevpohI$3rhULY8llxp$M2h{PZ7HsW9O+Bw7e1n2 z%lm(ivb{=;!-tay>!VdHwpk*B5y|+;@{nXzLc%zkyRv$tJ6&dxx`KocA2wd*ny=gp z#`zQl?rZj~nTOy#@4-3w3_LThC(fp@$ z@c0BO`zOz%daIWRq^k^lCxgnFj;~-95lj7Ohn!Lijr7h!O9?9dCn=?F{i=O>9;=*n zcAcS=@>!%5fl41o)nBf5-|?GEuC(rI3 zJxcVl+Drev5M9#$!`XM?1=c9wG{b{+n?gkKF@QY;gr9u`LZyMxAkZ z>U=L{JAP394K|o#K^yXRq)@yHdB;U6wiRd9g{(qoF{Q-W?sjk9>)qO}?ly<$__82MvzxBt(-nDQgC#)) zpDs*b>5)e42DBGbOB&rtI>aq~$tfC!*G$KZ?ZbX>kX{-3@O=SqVD~~NmDPM%CGoTq zhOSCy>-cLaA2L$>gVA<3WlaeQz!IJl^C=E0&92>vMfSDsWJz(|Dh5ep>147{S=M;P z)sgah^y_{`O4LJzgozJO4(0%)3Gx!$T7j&=sYm$PRDXT;L-**O$ zcE8ac@HVIedn_4OERWG2F4*FQE@NmW79StAOt=`pzBTEyl{hClv_sGE22__Zz-D05 zN*9VpyK1z$>1ehpb86X_1~O|^Bk;qth{ccDDU5OYQFy>8)Fj~HftRo|H=A9j6xz2y zI|CejXgEWh-xQSzL1mCW0$Ox`-qNs|HUE5s0ABBa^V5Vg@^)O#YRGk z{l^0|7at;*a(}iwVQDY?V0O%KAr4Tn@pvr9A4EMd0RwCC$V#E4nD#K$h`Ghf2Uzyi zCzc&t^nLAnw*)a}k3x#qBCohEkN*VFgUknW@KKX2{2KwKn_Z2%unKv)n~T5jqAXu_ zxyB4{Bj+N|!}y>IukRO!6Mrx>GwL5>ff!;f5<@^N?$P3*q{pO4yo?nBp&FyAu{?po z*%LLA9TmBhlZk^(X}c1Msbj!P2-Lk+1XN0^7?FnPvOJ7#Vx7Syqq417qbuIB1(%dh zpok@+Ryg7!C$VQDoXB3aW3H*h{v+N`HiOM}8Pv)7nX|m)+4^EfMIjbju{81I0i_bP z4}0chDc(SaM5+2mjFj-UG?T(MX5bOgd3wWY!FuRC`wy)0>yV+B2kGP9>GRX+l7k~1 z1XS84rxc)ahE-}wm2>2jCl!_RB#zoHii<6;4l4cgL8W&dsMLCm3u3AAkr1U1P)U!x z|M2nOfBX|4{LtV1^#?!v-uM0WyWjJd?|j#v|H(Vw^Om>0`;XrG&Nsa2Po90lTa8{G zz47?|^M^o}^}+pRpvx+&F7k}cX!g0pV<=Z_EqU>yLMUF@ZLa=_yreK-Glv!Uo80fjt#3fk<(HDY;y&N}EBzpMl32TMv zPa_5OOB9NL>=%+hLavdlCE%N4&4keYzxjg|==W2hnv z2J6*OEdE(^D^UkW9>FT!5u89Jy*Fk>ys9gmvzg$bGc@f5N3)%P#F!-Ak1wTde2 zI0;b6>69F6i6qJ1Fw~V9BP-~<<_3$Ny9;PpMI!~uf0iT=!ijKb8u@#^P#(OGDaMG) zlX)0r;q0Rdz8DM9ZsS4Z`zQ1LXalnh2{%JxQQkCxW9NiJ5+FQm^y_<|#SAb<^D;>< zP&sw6vh66vs(?iAWG5CzJ9di=*gSrd4bTUm2001dPIF zcH)?%>Bd|cyp9MyO9Dt8F%5aDK+Ceou6W9&#x^y*+emmfQB{W_mgy602MSx~8-LL)g||Fkc?8msi`uTt>{O3^)(Q~LcuI=a2ypS=B@ zU-;sGeD-r+_|&IA`!ApTw}1SnfBE~5{nJPO?jJt%w;z4~2R{71zxtd1^XKn>*Ps2x z+yC^vZ~c=$OX%|YH@*GY*C%v&^Jzkt2d=stLUn;*Y?}UiS?o{d=|4|bU1qz_FkZr{ zsK`qcsg;fos&tCZ-!^59i!NUvux=3IJB&>+Mp@+L{lc7R%-Ir-(-$tUxE5z6F_n|N z*5cbK0W;A-oK0))ZrUi2sdgQ_r3?DJv3R498e%iVTX#|58)^9F=2ni#mop|P5)gMq zW~0b&XpeHk6%5BJz16I-So*(r<#ylp{Uvv%%`tVJ=AV zA)k^gSVem&;zP?0H3C5$W?E2h@ZNlqg)tS4MIv965M|@)-NY@Iwi;J%wyxZ48mrvt z+_>Al0d*y@N&|2L$JeaUNgQI)6#Bk=(|yHg2gMWS<~y23(NaonZysNY=)8Kp7-9i_ zc;0=fbNVbJty$?KZCwn{pxtYN@~*tknCpXU9FlEXwTy~~=6jfX=lF>>OtfSlL31h_ z%SfTXzGXrOdxtfILVA%bGmTunjgw6F`qMpr<9Zf1Px^r|%cuc}krwJC>Ew2OGvBW! zE7lELP#!;0I=(P_kMunHxDcaXu#kZFcf>6-pMX@_N7cQ%*&=BK*D1!YyKHq`i6SIq7ZEDX&JU5D=W`0 z^}N(r(G+_*yR01e-^D>V%BWOsy$dPEEnX|NGqK}q%*h=Bs7%}4QL}9X zl8&aKh^;-gv9FS#$f}BaqjV$PpT3%?1BA0O)uli4h&%%Q%+qskWDrC3%;+zoJ*>^3 zh~?=`Y-zN5=>U+J>Y^x1#qpD3mQl%*^sHgKH{ig08@dXlm0}d7A7d%geoUrR8go=L zGG4;)!$?*5QF(u{MNl~WqQ%AqZ(uA1UN4Wbuw*QqPz&^<12hoB|wMDsGmFubxWyDa8nTi0>G$t;@FT-FH1BVfbOD;|Ri=4nk8@v5$< zc(#yPeN?SWr11R9|w`a|!1-v{3Pp1*v@ zyZ-!--|>Im^wvK$&)6H^_UQSy+<*K>Cl6kKc>3JI@#DS2hwFp%pO_La&y-^$WD>;O zyP}i=PBvWYbT*Uc$F|5Ut6*vv0^xz$CTNqVL-Nc$vUMS z5)}0|BS4?=h88T(#HR;bxzk5!Z+Pv{ofRrd(c`>XmMoWgPENyji8M}|5!`neb16x! z%GWbo6=F*xZt%zGKC2ct>?OHm?^%QQGfaXg|Go5cv;5DkE`4!^G3qhX8wjbS9C_5n zJT)-|?%9u=K=&?&?a_Zgtwvw%WThoNK$ULcB5ShF*v2Ba8XM^(SMDZav0;1jgEbSb zT)EM>vbA&VR_oSwg_^I)PIs0*+7_f-VdF+;(o~*)-i}ZTD5VRLAx0a^+hCX_78rBx z+{F;jv{2j_EMob@6`MuyjL9RmP;QmME>PIBls3^N{>leT6l-azm1~7D69baAOkXpm zS!BT)F!-^;8smzxLz$1)f(k2!(Qn3djlCjnSQa8Zcf4*#c7L>t!N*SQSNe=eXF$u@ zl4<+AuLAt&U|}YGI+$VAUVD)%G6`cR9TCflX^sALZ9x>fe_mmAQ4e+j!4u?5RuXsn zeka^bQYZQvkB9q9pwd$(?_5Ri<0vL(H}T9YE0!|>s9^umc2{H@TS=4A+{()1E5S&L z?>l%bA&KmkvWcCOlaXjP;|5DC*4s2n-@Xe{2TF}`@?u9Nx`~-%k*owi7r(+t+`nvrbBD5Yw3hPjxyADRq`w)(|t3M^Q?bPT7JoL3Xg)oZ|U%7uH$DF7Q& zPP+cl$Y(||r{_j5X7sx*$QX)@=G>I@-r;G#n3?w_M;3qOp%uW4g;EBJrdWWUjxVk{ z4*BVr3y-@T-yBbkRXmR3bBD-^jBpR?yjFDR4Y9IgYkmPws$sp2L)?w(PHw+IuAd|bV}hTF{DY8I)Zdk-6`fL8Tg=8S54BS*Spq_WC8p}{@+lQw1n)mWAYS3`r4C%_ z?rP)&L7IIwn1#)Z)x(lXd+-p|%vT;T50h|=g-a&njbazl`^P+*0!BKqYzdT#I2CRf zDJ>4nf(1R~R6WH?-alN4C>}x&d3J@LJgGxsh=xrbt`vw@1DM@&s5O-p@+wqRv@0)H z7|-)47-ps1JmCb16bBVPX7A+n%l-TPUVm6s!^$!$o%+J#xyK(+_VpAw8LmXnI}^M5^C{uIJIzvy`j2WwMa1>AD7V=^ZrJQX?WL6WGBV4E*wn!5xfG1VN4J#^j5L``$uat1 zmD(UlxVhT3{x0w4LP>y^3f%Be%8k~AYumX>tLQ0?2z!{;!oU`wQV?d#oopFYE_kG* zbe9!#ko4(o2U#gyvlS{zz3&)5uJS2Zy6jyp7%a$VGj|+s`%FrcK@{~AgCG3NJ4_0( zG3=5yTXskwzF6Pz$@!vJn&l1Ne-PDSKoJFFXbICj-rI&_+pS*>-iF4qlPKl#jr8Eg zX8P*2?JGB$%&y$LS6Pd3Ykb#sRECMe6!{iEc{6^}V7!o`+p$4BeC!)CW- zNyTe-Fp0gfYAO>KS%p{6?gx{K;>RGm;lU)}pDE#_nxq#>Z*10|u11UWb4Z`UMP^u{ ztrZ)vA8Xh?lop>%VPt=;u3^_t3?cB%g2B)jHb3Yy+@dbERoQf`S^6>hmwRKzfx7WX zADr=6j^}d*GgCrvKpT-lf+fdEs|NzG0?G&xU~ zSA}SPr)xN52eK~dqfX0CFds|th|8qM=k_N9t^o{Y`Q-+D{ER)tnoQveq*2P$-`@5}UX<)x2oH5fY8^l9Nk%E&+^J$Rb;aI{G}-VK}IP!zGt ze|sJ~+4H%TBc)T=bZi(PGNA+)kLL+TQON~RVQ_znkc^Mh?`2b#ON)%J6un#Wj48h2 zNHcU}SgEuSTA=DExB{D*d%fJ&RhA2@2MqJ`3)EGKCDtYs*tPx~Gmgu{DEXzQi=xUx zJ1wUQ+%psJnG>?Xbalj5u{=o`>oK*6h-OSI1fP>9$9mBWD#vcqa&Ga?-!kKYw7nb2 zTy>dho&Dilg7eo&a!L(Uo}>}C zdyzNJjzwlf=@k;n*#(ul>FZoYC0dD%Q-;F<6qQ^SCRzXh5CBO;K~!&i^P9hY<#(_A z?)Sg{-~a!2zyJS!`^xWM{_XF6^YU+h{hOCx`k!C_;+HSI`14=9@YA3D=*K_#!4H4@ zz5o8fx4-kfZ+!DRU;WxQzWkN1ec_8=`S;I%>C>P2ym`hx@vk5I_&PMZGxnG7c-Q;h_RjY_ql~*J)K%~ma3X+d=5)IY7fOaiyhPdYGii!Egu+}3 zZxq9h%Eq9&w@s;pQG3FMM_KL>OM*R$R7T5XobAwLa#xrPNIRbG_Sne{wd+QR<)XLx z;+x&3*n$i3PHyzN-iV%J4Y4PdxYgxTd@UqedK>Z62r@BM!F#}Xr=v)t{B$F4q>xe) zJvv$xbp@M+aVxxA;X2bdqF={OL1jLog0^CCre{mSwPo*fn`;axq3){2sOvZwe}Qf=Rr~t5M!luuUg0WzhHW@b@ae2I zsPE7_rNq&$f*CXnZx7%G3xfFak{P;8LS_k*7|Efl+G1wP7d_N-S)kuC_mj4-MykO1H?+vxFCzl-`?t?|>H0XigYe;l(|;1(_Bt zvd;)A7B{cPy!ci|g_q#zS{Bh1t0I}EOT%<6xExr&`@6p7)cgtkyd*+Pa^;srv?dyJ7(C^F)0`p0^sMdlEYiB~F{&!@|1knyll z7CFbq>GMEjrC-MkQIlEE^&Ao!K*ii#%;(q*e1olO)J#|K#wutdFmo4DB0!FWG(@{f zO~kXDxleBZA$tZ(TFx7Ah#7};oljg{nQ@f89WT-46B(LUAy`LVuPIDlT5`1duO7cT zQtKD(Vf<2e*aKHoMMfnSR0>4}Wn!H{1{Ie|^xVG2A30Y~6IWk~$|U>8fGEHcji~5; zJQwc}CN$>qb8y81Dz=xBuC`I`%uouvUb|z#i7!CIj|Y|p_u8j(q2qVn6yEtQ)u*L)TX#rv%G3Uu~ zLSrpWv#;EXg(9pm-a{-!I;-qKC!Bj@o{}o$A_$DDP-*M|=}XH)E z;tkHd+ZenTc14V&P-9Gzny>r@rBcHUeiM+Xi8^E{eP&^exK#oVYfd}CS5G}sUm#W#lmQpf=A2mKVKBxj z>|Q=;;@!j#+~P%1B|mQ@VB>Z#vI@fVk13}l9;j^Xwcq#c zl-B^2v+l3p6H&^OBk@mBRL)MUL?R_?BWG6apW01)KB!c$0V>c_hC?t~{|jII(l1~7 zRXTqC@+-gj?e7w(q~n#}{qOI7|G%%K<9Gk-=<-T>L_(LJ|Kg>ezWDPOUU>0`KQh(j zyWji%x4-k_7eUr-&{e`tZk|$>HprL2vkCW0nO3 zB=vkq@!VF(1*|8I$3&7|e4>)BW@Jlf8;gozzqW2FJadEbnC69Rnn}4>JlJKK#_KNM zd)*b~`<+js5qKxcuUrji)UNMy&#zA+dM+I8QZ1id7o5O|wOzWxaK+DtU5no`O+zBgRx8`9ZnR5F*u|o2*tcRr{czr3fahAK8o!+zfwS0B+^)$fTR^)l zY}9Po+q-l#oi@ACAO}TLnMd%QVaRoaiV|8vX4&*@mW%bDyk=5vbtQvDK0aETO)BZ; zO`GfLJn-T#gPHVNd~QubiRNV=nmn^%?=v`pll&!R>rGd=daHBgW{bFT?RMwJz3S$@ z>NYm)aSZO+&f~O!(%}ZnzN+a?*Cy&)jK$*$-qB^wOpEO;4i;!VF7SgA3M215rA=7Z zYYK_jW=dMXjJ>DH;egWkBV136vo|qvpYF8X$!d~QC?bJHnPXAPzoNpUg0e0{HB@{0 zvz^W;ee44pTr&axxT_83F7<$Qi7`sr&`+h>Vu~cPX(6D}<3>u%MDlDLj6Zi;-YS~~ z8&pw9EN1SS?pg{3*k)?ObeHW|AQ_Z{4b=3d&M~_{pK1&d7xx4S$|=JMV(uuRsyvG_ zny$yQ-E<76>9UVlGPSG%sufY>mEbD9lcgOK49}zBYlN0nUa7D#47(`i3S-l8Z#TpiaLn{s}Zs&)2^oIry!ZJ;_~)06qzV|0+GbLTis!!-ETUafK`lGDB3_^QMd(_ zYZFk)$h~A}9A-ctA1UMH{^CMcL38n>ZOy`!w+Z(dj_1e9vg}iiuJn9R#ZkxE2B2bu z(;ZgIzUPhkUJ+lhMlbldszKycX83eg$^8XT>2r$Sk}W;_xNI)QzCox%AUzUG#b&c~ zPUtH1eZl3mHySe^zXpU@Apx4%(9>XWvXo!~T3}sUf3%F$6Yfu!{(F#9(hc?zv%*Ur zEodye%9%`+P>;vx0Y!NihEo7JQOs4;5RVrQDj4INdvO*^v6xO7+fL=-TphlAz86u}`i8w^}6u3eWb{XCn`ezOC;wS$w9=#dN+Jqmf*_l5vv(FvlcYW#_6mv57y)pSD#%k6kdOV~cv%pfyA_K|e zW$qNdpY3gE>q;Nj2(fT?#VlJ8WYV#B^gP{PyErnjC}D5DIJ(6stuRB+O5%eDZ|V;w zm4Qlc-0xTB`|A9Gib|^b|d6Q&h5+a=wo%HtDml-!a_-_1TH z>0gJQ;=qqu3t5@7FIEvf`MU0SKsKek{QIzZv4hNpN>fI>p zQWvAr>Z-2nX`Q-oO}B{wuvE2#F8&4zWC}&4OZypJVVmw%ixM*?Og1z*Por9+He=#m zFMr!UlTw^q;Foagy3-WirxYQyFmDtC_R$%T4=tP&KUMoZb`gSaTyV8S^^Kx< zo0;M(basI~e0QZmq5!&dJRDDeX*y{jgd=-cODPw`Q7&Ps#a(3HP^>ECdlFULe&*$1 zUM+nc2C$p01S&kPS!Z!|bGv_Q+j_tfrQB`~Za4aOTf@X8JH5$vXMC?cOrPFu4->tl zJDb>^H*~>PeXOY{V(!JkOu8pBX{PHiG+eO}nB2m+h^?{!D#$iv{=VggE2=SVS5Ddh z7~w?j3ywX<73gS6dljZj)@OF*)dqb80tszQRXCtU-j%ZPLt6#Za* z;m2B|DFYp4NYihMpjE0x`w+b=?eXXeA1y@0Fbc4;`X4? zs?vev{jk|VvIWRNKB30KQey}&>2}{;Uj4XDQ3X;AH5Q$LODTwpOsJ(anznzEj-B+8 z`zPiW6NKc}uZYnDGc0G%60pHEz@w6pY}AWINZjKa_Qn`MCQU{%I`87iRHOBM$R=2{ zTNoH)!4~B$&ZKZxNQc9}wQU`(x+p)0lgIo$)5C|>&=vVe$!{yXL zQN`!v;U&veU3(sm-}6WAN0f!)z{nFX5mi@aM)z~oQF(L>wBMg|I(>1##S-n7=rM_E zj^^+V&0>L3#lZ^Y=Wu-VdJ#zc&Z6&&cka6XY9FUEwwZ8au@FxfCShuY1yj@?%Em3~ zDikq1e9wAZ4bLxZ<^6=60plWtKgTvm&d@{N$?d?DDe@A$GV39*c+0RDW9n;(O5H4j z6HX>Yud#Z)gcAgqLm!1aR?U|Y0zTb^kg|6lvyw;r)Ltk-%l?Dqp$Q9f##&hXRV>QS zc5_)X-hb}n0$@B^QdD8;-F|2;rvU$=u!{+P`ozrfwQoCdYOdpf9;8TwLAd4frv4l2WRV=x?j@IxQ| zeuO!>G3~(@n;ryf$;K-2S_v?Uv^|g zfmKKi1c^Ld;>IoK2av-Jrs^AUQ67*JDU=`q8og@BA*&Mmi+5gknds7toXkdg>030fqwu15CBO;K~x*1+%rnK0am%) z7zCwk8>LLQyP}kPt>K+!x_{6BsAwq?H+DK>vWL%}nZ0uDA;G)QZ zu(r$mT`SMbtPNw7jZqhKh%E%9_-aonc6pYhwBRZHW4~7_PrSl-?nxU?qfGs6Of@_| zTKcj|1XN}5Yk8N~L@}3%)p+GUK1~fi6%~IF~-)5+VZA!}Q<@KxGLJSOT8( zE!d6paWGvCW_DDQL@Cxy%!YVRr_A_7pRQNCk=wxLIiRmS7Ft})$HFMQYP*Kv{#xep zF7QJi%ir!6TNGoFrgg-KjaLZF_<%OU@zuDa@JTE-qRVSAfiN{itus<9Ks$}lTfZ=T zbZ+`$1f^v(VpszGWJLU+kV5xYQ!6X|Xb($Q?FnYKMHiLzdUXXY?D%5uFwUdf4b%`f zu$Lm$#hQo*R!`i*!3P+$SW%d)Us;Dyp1vA*<|I^+k%=KaZAwNx@yLlH-9NNh6%0W_ zGM zJqSU8B-xzwp@jaCYf08xM(`}9hsgO~eZmqMf0Iv2s!iDfw18+paBhUekq$`7ES zE82c#>pE7L!ApS?oLClRszxtJE088E2ZFHs@+?azlaot|={HkkMV(-^W7mT#f|+<2 z{=?)fJ%a4urP)d&^tSsB-U!#$Se3^HJh*h$dz-_{H1mxVsvpeTP%hxmhXU)-hU~Ha zCmX;~-7-o_vg~tkJ zFn_$KR+w}@;pj??mMqwTrXz=TVEuZ?&3G%+0;NR3v4hI-d^f`PC)8nxe~UHkf~-6S z#1@t|M)qT9avnpI=rJn`h*uuC&qc%gk0SlDj?BIElSi=^$tcVE;L+ak>kl6MQGYN_ zl+r`rSAu}o_I1E&`M0n9_7w^*cBI?s zo`f#xOaJq$Uq7>PW`{)WSn_=#*M5&PDH{eA_xQ*-B8!r(M8p-h61khpl#}QLa|;<0 zDfgUtng#y=Y4BA=R$}dO%R~HEN`+^e&MO8*qMWzx^(sbSt}0JbA29JOheF;SyvCxr zs%)88*~-#Wc9#)FVvT;Q=_!%zppi^OMcRHNq>nQCzZJ~Wq()H#6+NcBl{iC28YXQDK0!3a+l{w5u|fkFiodX4f2X^3 zw`U)jQAsq?7^H*BNjeg)Y zxTa?cf3As@!OR=9G;%@*7boOZ$pt*HSbulSrN}({$i8FHLd0M=jiRZ0@Juo6IQ4k_ zJjPXmJs7EHfS%P}rV*JnYo%lh6cvGrM(7o8X{keO?kxfnZLJJ_&vBroNEQslmZfD~ zP;bRJI0?$Gq5HGm$X7w&3gbtk6s=EA{o=b!Vu(Frv=Q=%nf`rwa3)Yuyq?f9!&fKB zuFN2S7@zb;3EL7NO?sng-}uDtPqzot#c;MBCT^K5sxem@x%o%~m>twiICbCS-^Fp& zMLmRVV+^H`9Ju)?GY?C{hWch{)^fEDqVOCKj(AALToN*x9z>vA0dU?P? z>Iz@le$-Q;3qtn%d8StxV7IsJowvwmQ@%X~Z?Tr>K<#8j3!$?sMr zV~n^Q_&(#o_)Mn7tGbx7pg6GfmMzA|F})H97kl>mV{OuVjDF!GDh_*v)k-lQa)=Tv zjw^i1+2H%y3$@l7ODRC*NSy&Lq|Eo2q(8D;%Ic6B3uj^UHqa=r%#CNzF?6V^Ye7n3 zV14h=!bcm=EIbPH$Sp*Xm0G{3Q~7qveg5>O{v{5oEX#Ug-8kh+qq>H3i?OPQ-OYvY z2{SOTiiubb9~YEjpt5T_^lw=0KLAUZM+(Kn==(Y|=J*ur*V93z_uB5SGjI~0zQ*Xv z9|S5-^L@Pvs0{7@MhR3NKmX=8zwO;``r|)+>)YS`Cx7~$KmD`!{n>l}>b-yYfxmkH z-~P?te)Pj1{rJZ|{?GsTiBEmG?|u5M?rx#n9O zo@xk6i7k15ZX;6l)_RP+lnWlmmvnQr;Sqc$=C|=?5-nII@#`+9Yu24htT|?Zuj?%Y zp(0T3vyxYL^y->r^o#yt@jd?bABp**iCBv(7d;F3IQ|(Xq6>5cX=en zgZ>1Pba9VH0leHc)@JJPW7e`LPit|gQFbiFW`rDBJP~-fqNVJLnac7Cq+~8d{5@D- z+PBD-x}oq1T8S?qSkH{ghG$g#R31~oVm(Y>U4lwF5~XZ+CW%8FRK_?)iBI6~nl=+2 z^yijZ=~)RF6Db50uGhN(3?``llAzKZth!zw47mXJ7a0;#3is&&Br(WXX5T&K;-UE% zY3ZIRK8^u3dGTCwA6Wa*TwjkxuTMz4e=ygisMM+-~R z_l;8K)nwTlo6bY`7eXLR)!W~LT}y!_jMrYg;L}$be-GYR`lk$*EPZQb6^nbkFl8H< zHG@Gy!P6O_pQ14tsTiE3FHuX8m9q(Z&C4u&UWVmhmQ-Q#VPH{;6>s!IX|ej_2{oB6 z#4f8ksK#7`Y?#oQOl3@;*qK}k5NOC|utpu9TJUk%bE~kBq1u>) z!(0u|3B9h5Gz*#oMsAkF;Sn0-f7Fl$yqVF6s7ar(CVJS5jjMJfrV?_@7$}c_5_=50 zdyHQDjLb`n_!oLhbR1Y#+q<6NFOt=2$gBt&huvY7`@TF(!hID5-_bQylPc7J zA!HEcMwq9!K+Ca%$|;``hB5w0e!|IlR%rnzj3|6me&l_`A%l$k{4Pfv_?>*fLF6uC zdC-Lj;0mo`L;|N8b1q5kInEX=JSK7@)KYjcZE8kI9l7=Eq#SL;C<^44)yYV;V@K#) zqgCvLtL<8mM;$OcU9qcp99a}qc*)rn6DX|qiXq5j-$*%~aq1DreBWe>85qWgASHJo z&@a&m%83rB6VHZBq1s>GMr-rj$5?{6YQ@JPr|7jziBg>xtB3a=DATe&p^Ou?#fd;> zySe7eZ?L)RVFXuX>pFZ~sw+#s-vK6;?6c&ufAact^oJA99}X3xe=SfsFPc(PkxyPxg`nNv+ zM{j%MTi*VbKYr&Qzy00seAoZw>`j;?OV8`P0ZA!jIUJUR>8jrMuB^)C=AJkAti5`f z?wP@09qb@TfB-QdK~fYAE)pp&wk1DDYyRGI&ij5}R#gu` zDz1vzS-DmhdhU~F`{i$b=X<~Ws|j5``QeX#^T)sO+rRbOzx%uY?598d{XhJ}Klebzi z&~_Oqp~*^<9RdTMe;Y8+VRhg2g!`c_G^L{4+xHv`ePYcxgAX%UA*5t^$E{Pjc?{jY z>1?>#3iM~2S+o*!uyT8-1fSe*cK|efMrPl|idN1u__1?8|52y-nL`>82+@hY@3dTs z?7M!l&#VXuDnP~IiWSW5u$E=IxG#Ass=fT@9Q!blWj~1W07*j+oLjbrBcDht$d z3)VqV;@t5-PCAv((<{3VT#JhuVL%m6a>O73nH>bMe=$P*f05NtCjOV&ca(S8sWG zbbk$=wGzEpq~5$8M5o?j0 zf(>_zc{8l84JGOn!$u&S^yIlP@;< z7$q|;9A-hoRnM0Y%hh&ny4%^_VBYVgV>TKtN5jQrvZ<$s)A`YCc`{w5Qv_oSE90_Q zd^*Lq6%M3O>|hGTRgrlR4wEYOsrEP#+8iFR#A)frVmy-$e&8KP-$E}Qsb`|S%+u$U zi8V(d>09H*iy6uAPHE!mx~mN~o}-x2z%_{gnFX45hwl55n@*Ao$1A#$S*FlHB%#pj z#X2wPHfSfq^%E9MF|4Ay_mz9$l0AvV9y4(WD242KjDr9G5CBO;K~y%gNY!&1QlQcs z?RR?3s^97+Tp2Vg9+Y3WpwgpJ3X)5g{w@o67zRufhzCWeO^a2> z5Zj^RufE6hyJ7TFj>V}a5z(-zn7&!R4q6os^MG|6u}Ccb+D5p(->$;F)@Qng#8O#s zWe8A#rwe>iRtNx8#&DO_5ujrC1LIZn6KY^I{VFo!WLK^|iz0FuHg*s#w^A8<$VV`;Ix!;@D7V9h6g6V_2A!RxgQVd9KY92N;u9 z%rkbVkbRarj>S=W3Nw9;iUk#=O-v#%#lzD4xin0iMsA^ULJt+Rj^LSMcZIKt$_CRo z&S1VNSti=g!j#3xjd{lSP3?>dSdU|asW_|F!Cppa7kIJ#5P+d zo`*4pS>@Lgg~FjoL>2M`-ZOj4o97+{iD^ZJNqq0}`F2CsEAj~rBNqO+0C*Qu?{Q|? zKoo_A#-c3B`oh!)(&)z$|cys>pgBS08?!6Db@cu_% z`RI#Z`{Gx=`IWDI+f|NA%`3HvZ*OuV<+))5ak$#JFmP-_<^t|YL3I;R1#4cz9$s%yStHcx&5rP zf$eNV!cCucal=y6@gpk_dUwU33YC*vk0?;=+tlNODDd^FD_Y3-?mW1krHtYQgmG?1 zBb8BsRR&|qZspuQTtrY}j>Z9i)dj_LV^GRvr`i4BmV>R!d3@%8odi5UCd~++xIeIQ zBm8x)v?wkDXu;29&)r-NK$J2n3d5LFtKnglEX(i)H(iQB&c3yy!s3HVrNf?pZ{;^szyG+=h*D5PjA;}?NRS5EVA1h)dDzBS{N>!zpB91mX;23z z9a{);9Hi&1CcF(ID3ex}BNp;d`3qCSm~K2tcdyj$#dJRWWA54_4w)ASXu&eEDDOfr zMnFP07x(Ge#AW6yP!`7-VN0vYEhHAcFW!c{!NJOoQ%7avhMPoj4CFvPSb4$j(#yNN zrfiTUCN83AI8uF9EI+DfYQ=OOa&?mphax6M5uQ?)afH9>gNNE zsAy0HnR`087?XD)mj2iTlkTu%QkA~7pU(Kx zNqo<)Z`)V!i4lt~<9gebPnQp}>5J#_1Ny#jh-A#`MMALj{8-Jx0V-WP+Lof}!MkO& z0}L3m5UdSd08al16|%~hPO6Qv z91vA3uTrn5%&EvEI^{}}1K(j75@ zir-sr@tPi`vwuL#Db*D_@1GSVU8bveZLuNNC6CrI(Tf?63?%l)LlUAZS1#gP z$BL$S>oMmh$DWV()C8v4=}DWTP9XLreYRz!JH{T%aSabu9%^(|p>_k9Qur>mWW9wK z7HsZ1&Py!Cf=V&8lD>N+QAhxj58G-yw6k-6_G}NELd>xTXU2W)T;X!ZbbVgRAY1M) zFC$*k{iSQ*xmp=f_?6kjRTuTdQ-AUN7V3=oIBqICjG8qW*bH4fOkWJlhg9-H9D=aQ z{4fu&1f^_EvbdHKQVe`wuQz9J`h#%~r~c_|y=UkBdFSzG##F8al}kG+Zv&OLnY*q~ zilzyAFn{`l8F`K=%S=0E+( z@BH5H{@x$_^bh{UAN})x`6vJK&)>O{HaUor-;h%xs=n(vlO4^*lWI^Q6(Rd*^osDk z@)9xVv0pf#^4>^H&P$@XTUfau_1sWo1=Y!f9KSNZ{=2yYAP+Cz&6>k*UQgc&g1f-d ziqHOU!qkM1|1F(j(BgacY}s-LS3}R3aG(Jv?0N4r2r4Q81A!S)6I{5^pn5(H~;p3{8xYd-5>t`;rW*bvzN`m@w3kI{?p!_N9~(;_io;O zdi(B^dk-6(?qoEv7{+jfdL^2^D5uB(26LBn79%!bOtPfotBV8+8^C((3bW4Qh0^nb zsj~{bTMMp0c3DBj56g%2p)qx!vpt=da%~2sVLgp$NG-`3XnefwWnwFrp(eap$KY`6 zO=vZTQ`mjn29UJ9JD8#hPmy_nl>@G#)X+o5v5Ue3kYiE_dc9mc;VrG^*uJ)4_d4kG z)8TNE{@B}ZchlFVt~6;S0H$Rci{XkFg5^{_m;pm8osI zdF3?l(Z08A=_R>x;**ygvQ(Z#_Ap<5+`3mNLb$Ky>L;GR=Zs?pAz%-Bz|;md3H4@q z0yXR?U6?X<0yo!eb9OZu@3q1wa)mjE;P%g_BAHU|IFI#uJE1p9xlsIFN=wYAJZF-g zBtuY%La?xt#fE)QQ+ieDF-@Ow3i65i=M31mt?pvJ@mgWny4}bjI@8rS6SU3swV{4YN8S=E4xNxf++qnjU~eRri$EpSVfQ{R#R|IBSey-+L)fPMo~lDQW>$T zgby5qw)5+S)Kd(MTzVs0^X(yAM3)ryQZn|aaVu}0&_i{p-`O_c?>ys(yAQ#D7WwQE_?4tH!eR%kwSFuE;hfMTOJ#vUmyP?s?a+t3&uB zJBSbH{vtRTQkMJl_?B*CF!OA;kYy4YF||^z)Tbkzq27B^rQ^ZV>fy7lZPGV~`_;73 zsX3woN|~l(zpAlMKe6aM234A*4wFzAFOl_2x(ZoE4kfe$TS{b3@aSlg=p z`38Jv;Qh#DCO&$`03@b?SdZ|MQ|NfiDc2rZXUsfFM?V31x42U8;gh+)9cCo*8}E33 zaV#FXw1T0`9;R1}f5w&PSQt-FsPKn>^vD0t|NH;_-~ZSD`D;J<7o*c}R;#a6tFLws zzcD!e*5KsZgOl&{kH6Jjf83mY?#`3`KmP3P$4^_6$!0WK4JU{O4C}=Z<}R-x zUidoXl3%%7-yI=gKUo;DjHayof*Wcg5yQ=3}-XwRzxV>@8uEStzGa zT{(b9i+OsRx~HSb4?~m+7B3VNGgxn?uS74lED31onZN>`kr}Mj?HjS!x&FY<^84sM z?o!04uph5nimkXzS*67(MoXb!9R;SZe#d%&6L?4)VeG^(LzH4ibR3)Z0%(!%3#tgE z5@tx+6{smePBRBr_?Q%AkejCjWO;o*)P_7rzW`W7>H0Jce&)d{b+^z`D7^69fh0$! z9}Xi-zlx;0XH-_Sfkl%QKb0TSngy|97#Z}bKH4AS9G$IPjHWQG5{<@uObnPDa&lxe zr8u);Iz>9k!m6cg&t$4?7GvzORFomRyf9Rc70U%Ftrbkv z!|uhXjQ1KJ<9YK&3i`MXBlWI5ljVu*UUp`GOa+M*#vE02>cH!tA5y8XBKsuf-e3p(k(R`@9g0<&6q#~))^iE01yC4L_t&`pDber z9z4U*Mu{GjR-h4a?S!()obIyK`C@&s(&Kw40XH0_r8t}vjb85BGM;fA2CoWP{P{YU zgHcdg1hgDJUlc+L)D=5c=KFeMZZFgUZ^#d-&Jx*UZzOGB5Tu#+r0hLQD zE0@ZM%PX(T&<}n_My2od!~~U5O)?pHcRy+d(cqu{!#5GMjO>4m>#>f_RmsH+V@GBf zi>%Ab*Pnmy{V%?6S(mSU;mhBA#|H;np+b0}a5~)jbwA=O8avMkb&W7esd)FUIlgW_ z6sYhd^~Sw@mqFse8;p_gTRUC?=JEOl^#@={!zbrsfquWRkY&{+Tg&q2`XLMK?d0fu z*|L>AX*sZQ@1U%o+7bcuvy!*UA5b05H!oY}ge4(OM4wo3$86B@u3_YG?j2ZI#q^>E z4P&}l5x%E|EDv1X-H$yJ#Y>!y^iHz`ME$DiR=PcaN)`DKsw=x%x3r%?!k9!{H(|00 zDP||v2a9lW-|=09@*Px6{Xi3!DheKWUBP{qUj#WZOsSZP)5V@bDMXrBm%rbP4&sna zu;o&*`1m%1`wFfowGyK%fhFuC=6nhU>01d=9`5xYH3p9x{Rhvw5BIuH4~ERBJZp~j z+T(*N0myf}m_2FPu~K00Lm#_}IMN^cX0$l6ZeNsup^^(B zjS)Mt6dK7jS)PVx{ zV>S!VVz#A{<7=+X6APuRt3qHQNT_P87@A~aq{@}V22ordWDP*&?Ov6Izt-XZ6+2 z>f>tlwe+<$`+Q^gvOWL&;P{*M^Y0B4u^fM^T7UIPb^Pw`{k{EeJv|!Nha=5a067w? z08wBJo2tmmL=r5nOjkLYkI*yY_&i-pSHd5~j60f$Et+rVTQF1A%KaBxkX>&jrrr`m zegqgT*?V=EU}m8B3M-y0yOI!*85R7Hd9RM@i6GavrJoO>S1rzWj3R71Q_34mR zthv~#a(QJH6~=A`V^+ScVEwpR!S^*_yxs!zW-YTDjA#k7D|MN0(hs!+N%~%56?wzb zWt{X=yD%!~4r!5MsH5v6_eiQRzB0me{jloBsw2v~OkL@x1DH{fRjkywRkhkZImC=) z%*rJlrZAqhWcTX3zpF2BsyOC2^a#ob^C{R-$vZG=3}f=1J1};o^edx5yk>QU^|f`Q zK~j@nv|2NoERnD>B$l2)DPv0cLVdp6hL75=h`;-$%3;isFm{4=U{9Vz)0ZwB zjX0=MyJ|5#EEeTs%;X5NzzN!rwK~s?3e8>Ii%d`LV=PBBfK3;wgfdXUTdO$r!q7Z(`Ss$=%8@y}0@a0?oUsNXH?(i%dM>qNVfrII0`@>WuM%5_ zsvD+Vsz4D%_9Lm{!Tnr;oea05A zsWwvh-Q15HtzwZIiFuZ6{qls#+`t2?i1-P(M&W~rjwrFHm{>)BJW?Gn$^gzFOkGe3 z5hNJr$m?Yfj`7Wze!?fr+0#Ep4_j{dqbQ3A=O`1- z@|Xu60hc#tZ_-(H=o^qz7^CloqVjXhr(8dxvVBeW5{+B}5)M0FtFc^3()X?|sC2i* zmMegAI2DU(d$@&FOK}Kbg{=+viP+{u~H=SmID>_*0VaI~P3+P3v3vAUTx||YS zR!3nNd-KlD-8_eav(IKYtoYHs(}>RLIjzW(`*h*6x1PRx`x$S^yHFg6cu73kj)(94 zti4Qk_;!kz0XK6>aaf8he)!Xx1-j&CksD2qe-HtihUWMz-e`2{nOz9C1kSLTA0hKU- z-QC~Gs!rj{dZ<6TkdmV~OrBf{vFv1)(?AL$BI3HT6IsCapjoyO@8k|>WlJ)sAZ^0v z%bmwv$~ciWVXqh7<{pU!KagSkwC@JrS|=Ji=D)XiVf0R_ZO{J7D~bJJU{7QGHwwk z@fY9Ti^uW6ND(Q6kJJ2s^l#X~7_%reM-5bakaJ58r92VHKNcBQnGgDJpa6 zMUqoy*&C(@nQ9}Fv2R8ur~@c|W1s7HB%z3B(&>0QB})JG`tVEN`py6DPu_g|!|wXy z_UhyI(vDk?`~Tn{{OJ!lL1a56|A2PFrw0Phgg@B7qE&^Q5j&*-tj|QkQRl96NcOH z&^^q)kIYZaKINW|t_-8w zgb%2Ay^{tZ#EwK7gGsxup%vxqu?tz4z97XIhnR$77B6WmKo?|G>}GulEN=1Q<^F!V z+h|n>?xZ?s%M1n`rq^h7_7g||U=F+!%m?fZUD~izbsbT#3puv1J4BDfCrGdi6RWt! zBBvJlq%EQ_?FD2aVzE0W){+OT+iJUotkd(vJh(+1GE{s;T1wwV5&=xp&0}rd5>!)} z-3xp3R#d?0%syMfID7AdkMPoQMFr7}^-J8`idC-jG|maJ$1i{3eA zJPd%*XzaI)1<5s|9eT9vxxz4fy&AZJryL8n>L+gK@`(?-{bbDqOF|145Ko*dIC4M2 zeB{LA=~HMdlbKxubkS}g_E-)_8b3uO#&(J{enR6JM3~c<`$z{B8zToSIjTQk&V0^_ ztWq=-AC-xb>GH%4U>b<5y@HHd5CF!W0&iiXV9d7c<9GvA#QOle>`T#n&*f)3S?z+% zgp@)%S!|)ON*H`HULJcX!VxHiVR{yVox84b?4XjTCu@cEwIG@ISTG3Vy#mjD!|~ix z^%q!(hqAHJAib96O@T2h7yMw)m&I+C3gW+{@WNS1uBOaZTPCqt?ozw(hGMoJpXNEp z!xz+c<~S1VBsyB3yqCaZPVvWBMWc`PhU z{M?}Ovyv)Xf%t16Wl-_2 z`wC+`XhjeuzGd73Xc2)3Y#L57VgplAZtvmBMu{lV_FGa@W`p}u0Mb+71+=q1fcutD z5kra*P$|q{+~V;0_F~$lJg1Md?IQ0{o`Q!pPSw3RlcRHY5nX5+Y_DR??ydCVZOO6e z_|(?+{N4S}-Zt=yH%033W%`3s@$l9G&)n<9sQDnAW~KivKm}3;-^-xdpJHv~v4V3nTR12YWpTDX~UBcm_&ofKv8a zoOeu@CkNGZzcT@-OaLcShD-`>nYKM@VhqE0J@pecTrDM}4Af1`Gx!#Ifg^F=()1zI z?Ncs7wj>v16{6!*!1H^?$`id{B4b&(K`Q`;HMQg-kwbrq8sfQON#6qWz46ku9ZpHs zC>NeGRKv!w4%L`+#>qA?L~e4mv6+Yj6~P`6m@L zdJPlIKF(29Yz@WuXFAszBMdzuv?!f2;T(#=fFd-CsmyrZFGWT2$SgFIAQ=Xxt%G*)ohKVh%oPI3-6`$bsTRkOmJ&mvl|~>pN&u zdqIsAt^}x9ZvLRvZ~BJgfe)%EtYXftVH~{j0QnQ1_lr^pDwX9}_JJ|DO_w~wc*%aV zO5Y-@uq;b+jUF;7d|7}Bz((SeX3Mkn9K0vMzzQb`MxjBae%L)(;O812DGae6reN zh6zI;r(^UBLn=8OjC~hn#{I{&mRKxqZUG4f!0)rDMn*c)0*%cWZayZo$#_J)x91&DV9f-4^*0Ym|^q-9PBB)ouP2l8;EW^oQfxKF&hk&a zhU<)`uVQ6VQ5gstsu z{uZcEQDNoRu-`XOp*AuY&$IRGnqzkL%E35uOVG=Z9t2OkjHta#SMOlvyvcEQ&vLYt z`nVfyU^-KeJPFnq4|Y5slD^)0)b)hPo?FPci*gXIEhOM8_m4#lF*i@D9Yr#tW~!;p zmZIJ_efP7>YFpthVy~K!0lecG3 zp4?J22=C|GVvV9m1ap(v0aNm^G#^8dB9mf%9=F-sx%2EsU*zTh01yC4L_t)h$5gD3 z__4_;_pzE{h_XL;axhdVr9Czv>5LPfJUuXOv1>5t*zC@zs~mJDh9Z_lv0RE}<+=5q z=t6&l?~5}js3-1?%&b)nmK{=xZ=Nu7Pf%gGFh^IqP+-CY6({;2oUA<9sl4By<%9I) zec?Tu7G|n><$)0P3s)-ga{UC2!?@mePB;(W_t(iilfkT$Q|}A<(Axb%AOe%5yZ7tE zE(*lx)Z(CICx=XC5p;>r3uTtPMCo=luhv~~tguMc$K@Ftz*s<(PW8sqP5KYp=JeHQ zesVB8uU20k9Dk#``C4!D_1@t(y6dkrMz5OF&sk(;{V|TOR*TPf>x*>XSvm(wr*Q*2 zO~mL5U0y~b!&!P?3v52tOx0^gC7tfnZ1j={-bd$H$2P^m661x(+!>Bv4C+;2NyMX3 z1F&%Lfc1-IU#eQBUbK>^f_3>wt>9eKDxM7OyaSCfm6)!_-^JDnq}LCy4H<<58h=!O zan`apW{=h{?V^BWww6*Xk&@^o1Qt*zm6l3lDQ&lYDP-?A=ENZsm?4Fij+cf-GZ?&K z-TihVmP*F3syM=DMZqKnPuhx_9JtpDPA!249a*NR05-~6;g$;{tn-Spua4(Q04hk) z=)NY8_)N-(oAkx#%J34R@YPnm$zU|M|6os6<8*RA-AvMXe>#!1Rh%g*jE%nfBMB^E z6)f1`Z)(8bD__^4UkgsBLuwRWt943>n4~|>Pt#$zqFmF;%NtfY#m6aZoc-(6e|}t? zjvu+tYNOl>BNlU<>Dt;OuRoH<$xdpumomk~I|nzQkl2eH;4Rn|q?ZMg3Ye!)RKqi1 zteTun=cjq(WXWm8Q;v{~+(pLMHum5vo|kaeg5TpUwHZIqx8qs@yj)~8bLLurlsVN{flB>RIXD(uBt1AJW^DCT`_%?t;7*f>GuZx zD~PhqDkZ3l%mFr10GQTeQBCnw{_|9|p zLj4Hk1REv~m>O>oZ;IkzI;jr0bD~EEirfg;a8FmE-Zb*lW!Pvi&XG=_bI_dU{r7XM zzewBbOdZRWa;6n$_HFXUyHVtYo~|8Vuiwq(T}`K;sN~8zM(%DdYJ!6n%CLSsb=)@u z6JMNcE9;`%{H0Rh{dncKqVx=cRqVIy7VDE9cK0QFhm9L|ww03%A&s&?3}JUQP-`2< z;lv>XIs3;|u1f|TnI7^Oq;Wq>C{UcPs4>dJuY*B8Gseeu27%kR%#d~f>v zyOYyz)@NUzUwo}zU-U-lWsK9gpnNz(*u5q*&d>KOb7G&r_QtkPua2+Llv|KC{%9kQ z7NCb4-t08Yjl_=15W{>78^pd!bT`q;qV;+v<|iwa&w?4~*W!oYJ*Z5yZ(&eHVS{L7 z@`B@8F@HHL##IxrRmp-*fSO<%1J@-3#EC~(hB zNei^Fxwx39XG>T|kH&tmQR0(oV9T4*UJzeNN4q!d^euUhim+a@>bc&6F&1})HC1Qa zYsaL$>||XA`$_suv;S0&4OGA?n=uv=A+VUrfu+b9KA{AVq~C#2`tBN=n+wj@ zz#F!Zgu={8eK@WUC!iFpe;f{d_7Ua3r+MD7R`MP@?q(evW95SxbeO5kaF|Z|@d^3` z^%jlX``F@yf%Rz6!hGQb`S@C~lQTiZ?3g`Tc0eboEth{dR3sM?1n9-q>s9|{iFS^w zz+!YU^Tl+62cO9D{azDH4_G-=4~^wiXW`0qoMfXEE|2DfkYirPA5n~EQ5H;Os#2M< zPx^tJ2B^%e8O(-6YK_M$$HGCz7%^ncZe2{Kxa}%O>XkWBO5R|jUYFY`*p0aEfk>6^K2E{YW;e_U_E4(3*7hx zK;`x3{Qb#nqj1O;rF4T*wm_xR$>;(mp_X!mRj#9yqF?yuNy&E$hPkS!WKg-Xmr^tr zm!RTI()$^p5_6B~F2nI`JY8P_71hb$^AV2H(hPPTy<8Goz$dEgFNo!x%cZ~+7BH6L zjk=1X_*#5~-5dVL{aAe8o}6!xCE|`^hP$+W?cK=MuY-v1Gh{DX&{Jgl+0Na}Q63ZlU}Xm-laMrF|NaSJ{cu zXV!(?v*RXTHYm$OEQQBRPioBIelUOv?QlU#Tisk)pYbcRC9E{F?NWcB#u6<8vEG>N zVX$@GYq;b>S6F(F&t&u>9(d`n(@5h6)eKWw_WhRT`?`TU>X!GSOsrWHA~(vz1(Rzo zGdHhX%I#;tDynY74N#bM*FBIcE{JOV})HBMKKk{XCEsB>$$JuQz{skA1tiDxHIB*3PAA)N6%k=^!&Zg zpIp4zoV*;2XNZZ{U5=#;Ih;}tF#Bi>GZ=gOu$2+>BmZ6YAQ^d4l^ ze5tk=KSO?vwk|rs2q)g9!ck2iqL5+Kc!B ziZD=tN^{We%DaV|0ckj#(rd+Ia@R$VKgdDMp)Zj93JO!^h-_BlBp z+RLCtRHIjollDLn$CU&j3tV2B`EWQN3@x2vtYS2R965l*3U(kDphRmKIgpVB*3qLi zlb(=hBzlAyh#yb2WHJgHlDWAyVioEVd9+c4FKmn$)>W^ITr@;BRY0kQ>D79C#l2xC9=<;Xx0nH}j*erubzv-$NqYLY z&LyX;&c-hC*wG2 z(pUcIcYgOzfBFYM`L!Q?{==^wrdzIF4kyQ*;nBmV)!hgCJGUP0+*K_&hFbj_$1(19yEMR;RJr;G}wlcD}`f^?SQ{Su!s?-cl`?!^wjP;_vYWJ5XJAkx{Ys z;j(otW*nJLA?Ms>XuhHHVxZ=2c7@qqO2coTGxuc;G3{P=pHy&sRSz58Crv{XM(3Zk zhPFN5r2A{MM;gHzqs`Bwl^7S&^<$3Hw|Z<1UT}K^)~SV+fPqY^q0gRBb)oPAOBfa# z$2!HQT4JYqvrHx|^^$!{=CPLYA&U$c9yI~XgwabIh+quGV5H1=dL(WJ+t^g2?;e_u zb$L#vw0%&e+<9avFat2E7sKr_CNpN`v2)Q3uT4P0wqhp`yo@eSm+>NzNYVaD;SIC9 z!#l(rOIBCVQpFTFLloXjRr~ITeNGR3V}>n4)*08}@w5(YRGH5U*JkfeI7wVfP}`sW%Mrmnm0@_tYdD z&ZGpZRK6719c;V2a2rfR4qN5aBO^naoL9SX8$HE&ZbgC#G!xOthyjl#V;%q%AE)nO zUqwm!mVIleR6TZAz%AVa@Cgv5z2EBWw{@BQU`>WeDl0exE%+h;01yC4L_t)x?Z}SS zSDLw1jGQBjm)pI1#k?cXrA@u1;y{XejjJ3-2}_t^iGk~g`icGK;4rTjiiv6ZYUR|5 z`oS=qGVS%Jh^Z{7t01FdMGjVEoUVtVF<3 zGnnTbc!6OOX2*pVhn8tU*xEXRR~HOz2eN0L-2{#R1A1%24jU9CRq}?)LyRT;qP;iNzni{A)NRHpOR#KGY23228f7^xUxd( zSQ21F2q+ys)%FX_U6a*`C`DMM&^%O^6H*FiTWa-#k#iKj=eO{FPZ+ZaaH^M4Ow6_W zi#oTQ!M)}k1JPw%_Ja{o<{qq{7Rmd$h8kb80nEXYF?u~kBALU}*Kb~b^xHrD!C(Hx zU;Ol+{`8}-{l>{hpRC{i(fZAgR_}kZ`rwn*n;$LS`~K|3cZWwGH+shp9_&AU@c8xl zs}Ej({`mC0B@!&OfjO%bgO4!CMZ$h{{NDQP^>92VLUeH$QM9k2l%L=Ib?x}$6-C7^ zcRn3duCa${>aonIbhjzxZF%|}rvEfh>62B`<>7E-N&5Z}JCAG2s7w|GU@QtzML<#< zv*Lm)ErOQIpd#8C0aPLdUVw*k^Co@VDJ@y`Ki}ilrefN(Ya8rfX zlzA0+sP;>*R~cw2WfPCYz(SV0(L5jT!Id4>5PP=AOVS$LKhdJREM%meXTgEdLChR~ zx_swJ7cIt-dr5zlPsuR1aAWHpc&Jw5TmBw1K_O3%9DIje#u|uBKND1k-G*&%$D71r zkUk43hA6#Wu{2Tb)$Gk2 zRNzh8Y56iga zndc^DOg8B*F-XtzX7ma~$5%eQQFrd#qT9q<@DKi_1F$Kp>ti5UQGv6+qiles3wS0? z8Q%ZsPvWs4ZUd2+ zWRdA>fRDHF-38B_kiQprggYz7Rp?+zCyV5kXQTjJt49be11fsfED#>18;MCxbM zKrl5k;{+s#XeF-mAEVcl(cm}l`W?OIt8V#@3L%GidK$(cstn_Dkzlc0xOE&4vB%OM zM?*1vUnZyYhSgwfSZD{5Dx6s94zXuXyy?>e#=J_)%fZso?y^j*ANDX)NLn5DcX=`% zuCS&Tc^$|wW^*e=Vm@!ucYL>A#bVuI5tr|`SPs^c6AX>R0V*Tz-1nrMSb;K@Sft6= zxJtk74`u@_jgH1kH-MQuGO(~y?N!Ctib;Q8MHLv84N(eKO0Z*aG)0uWX7%kn_|7k3pEn?YOzC#g!h zTST6T`8jM|%&v@RfveZG#bc%s+{_d6cuA*u4F-~z z>k=18dc1(%5>qNH1iO%(uL!zCkyq_q&u)7=rVY>2^XDuIJ2`#*{a^mUpa1#)^rdh8 z*5T)Vy#C;m%?Cf;e3&l$c=6t^)feBHzxsaSmc!5e#^Fc5x%u#u^_w5GhNm}g-W^nf z_g{T*@%)3$@vFRTZ!hdQE6bQpnFCZ7w($6BefoNFc#(jlW5hr?B~!{(6uD&mdTXRU z8^b;oR4CnC11gz7f>m<6*LLOC+mFjP@THVbvwnrBLQv_W`^$Q|X66DI`hVaeXOi7t zmn>nIx#jH<{6d)#sJQ8?xtrAwA2UHc*Uok$aP9)WpEDsCWZXkQ+(Pqoc~SJBG0X$& ztu2kEZ2c-g#UH$R8Cs~)ghGO07M5idL3W8ATUB6XnBJkHX!r7I_PY(v-g_QBJGgdO z5^&%1_o3u$OFG$e6-?zt1<_?3Kj&=C3dyAi@$VER0lPkCU#Kew@k;W0jMlZn+ZOM) zjIV6P?#UI}p>O6TcrX4Us9;dCaZ8}GXDjdb_Or9=fwoh~ExeM~8=pKlh=7WR=5c#G zwGA~svik~D^5#jq=oGfW#|X;DA5}>V35Xm*5$5)z_FV&^mb=0Zcp=ftoyVO!_T7$! zQX2gy%>*aoXPrqp_NvKVXS}De`NYKimNuDd_OUmM z2C$CDS8{G1st9|%EJnfoyB;GGzV%gs573y{l&eMuyxf)-h(~*rORvTtC3TS za|}EKLqC{Gy|9#msxd9fkCt+gxd74~FSy>wM2hn9lMM!8YP^f7)=`)ca&QRVZ0#*!vv4I<3d%E=vWyf8k~0{fqyj*x2j}z=sOYNOY8*lVPP?HY zaLmN?lXSv2QL>r!S$0R=vO8dg9uhWfV7O*5o~M7chxM{I(v`A@;rev&(<78Hrqj5E zi8I3*`;T+Y)dO16SKD2gEGvv-Xx?$8ggwvyLNg&Y^x**;C|#{$;=c0!xa~hlUT#8BF%IeS_S98| z%*q>YcpN2mRN9$Uh$y<;@(D<0R62keS+)Qw`v}$_ghEm<3>{*2**6^7=yjlZdk?X= zV8XU82bDh6m2@PW=(#*%6$=CN6WU-48j#U_tRRYMDZcG!b}tX#8>rZ?&|=8{kaM0A zZDSUcPk%f8X=0Uhn5bf`vgU3*_F*QlFS7p2eph?0+I67R4Jgp@V@u1~hvc%+Ni!oH zjFEVcFy`u2lFOM975>&67Jq3v-N#j8)qaS`O1QjO1)-j?03LN8pZH1n1~tmEpN+WN z=#QzUET-qJ*XzWGS(fa^NZd7}-V!mDrE(?#28wqtG{Mfqze_-eL@c1xdXWCtiY)sG z^~44q^TT?nYAu=yz~%rouM0=JDWi` zC@DVSCgatOlsd~PbbOuavQR|8Qz{+^!1yR6TwCy5y?|PB9^sS7(y$P05)+VEq|x*{ zd$c^$;xqFaf1G)Dv#DDEMOG(IJ$%CQw;ASNNPu1)#&U&=)#mvZKKGSB_?_ST(NF$x z^Wl#YobXt^|H^xKd7FwtP-%avqCzdD z)5)yTxeO|zl=3S<T%pA%HFoZ_H@Uau?NUj?X)e8X{#yxgLH=@@NciCCr3QuujF zSi<_b)EF0X_VEG9R`z;)ICU3o^kKQmZCpw27`FOIJ3V|Oflm2&$e#AJxg+< z_z7GJgaqv6ZF~O-yz3%KFiZq{O550O^e^9f8quK3AXNM?w=8OI>%tH4nddsTo+5|r z?_P?v9K_!d^&G#YYb+rgg`xtbC_Rz2gyEVzhk^jTeM^y*s60LhZYcsP`$a}2-v%)f z2rqYvqG1oQ*o(aVjAQd;4?ajD90E)PSIT)u8o=QD+Q0pJ(m@;80meyM!T`&tROyu6)CNf?+@~j>u+fXl@!D#{_9D@R zbB(RMR5&Y~R9mV=+yG%1CPO@J@7pQ9C@(m%3CNy>0Zk@$ zS5RiPF>jKOsrv`^lXC6RORJ__@Mw!hrWydrvx6hG<+>Tn2lYTBgQN@D$#SoB(pfdE8dj8CnJZXhOrC2;51m9`{ZxFH{U}$C7E)G$y5RD5QT~W$qZIOD8hfJ{wpEv{o6-4Seytn~-Kxt7) z@g^@nLP@~V=z_`~^Y@WU8Tcv-tzrS*={Jz|$C{or2vnGw;0f&S!q%7t&dQNh^s}J~X{${_88`8n3;-v%mIdy3Ql@yf63k0N1T#!m7z+ZmO=E2yZUxF5$g z^~CxkK&5Y>GSlR|gGwQTlwL0Z$*f=gu{^F`nX@Z1TEBcXH9%7U01yC4L_t)O5$X1K zi@=H0k~r1gl3a8%E}~p*?usqQ9p8D(F06tXLP#OfaE(4=gXUHgM-dA-sDx}ncNsDs zEFWvf3}RH71$bn?)>gzkTgbAyQnG;?x=P3il8MJDrH=x!Vo)Wb9s3H|>qpmQ&2EM+ zmuq%{Qas#$nNP};JRZ|IXFnttJ)r8*irvP@^hef(;*5ieY+Y>YDpqCSq-x+(C+1fn+(b~xv3H(^t-WYAJxOUS<{{c0To!h zTDvL}vw|Wp1u#ZBR6uH>UW#t%!Fp3sEOXztm6e1g7ROH)D!@#-kZwRfSa+zMM>@y2n!;R) zsY%F|z_PWVpRKZLTCojYHUZ41QNS=fL1bA|SGVE4Od^x&(kEs>gCfAd(i_j+J_~ZR)6&3Ds4F!9oBzEh>*DOewt7G;z?dNsWy&e{!_ZPj02NZo6sOb-(A#_ft3Xu0B%~5^ z5~geLzel#rN#95(_ZUV*U}HU)GMBRA@Q8y7mm-y|8hTQN%Ow0g*IEmCLoaVr_|Uk` zvedS{A*T-k4FAtBoCRtF*<^^ku`NwpDBac=3Ut*j4i`{NV z>6A~geicXi+CQoUY%5G3hOtit6|u^tjxP<>cdIQA*d+-0SxNdBQMo*&a-HexlA@CS z@0g%6x&~CXOk5tOpY^T*m1zMg)ACyqRdAOr>sOIi$$u=nxqP%_Kl;IPKtJ`9G1y?%|2EIFwar4o;l@2(xb?7N>v7Zo1c&nkCxL$!>abCX}Mmx>kD3T!C>vShRtmwm3&zybTQXXe#yE zxvN4j$}BgvyW(7-@-d?bQ#|fdD&bsX!`qFUO2BKxrf~~9D$+&(GdIyh%xM&NcHuGc zjCOm>&zp+!2%-5W#wQP++DriA|$ebC-hk zoe9?-6)))vQJ^_2;R;iMZ^cHh!iXh7!|31}kIyp5+{PSzY|%|$b72PK^|*}>O=?Fain$`|?@e5Dwi2tYxE!a4uPl4IM$2+* zvO|2bfIiU6KC>U^l4VupVnh;!0bL=SrXI;K9lVBgj^4D>+_6?2)mSy<*?E;4sAjCd zLV47PW-z8Bn6T$rc!b?0u<(zAy3_NLTzHwNmXKrsHWPk&?aTBa7W3#6HcUmKL(KlA zHjCHP^pk;=8YfB_)XQ|DhU?md)A%Ff@2DrX(>t6vaaeH{4 zuXCel2lo&o=D|~K1zNb~@mvv$^@WeDTlgF8x;C<|jY8y1LKcYS_;C!stlWDhuN~{c zj-%(7xej(KGh7~{>=c5W7G%B(|5&YYl`|5HDu$IOab~b0<8vigWE(q+3a^}dq3E>m z0R5$>Uicj?pnpz>Sdp+lS2r<(D-7K0S%fR9Fb1=!*NfM$&UJaA$H!LVDFaAIsyshA zd;RL=M+sCu_vXtNufK8l`d8O)ez^JYlcUf7J9b4`*jz zoo?Qw-`?r`Xl{%3C_ARS>L1i;yn7*v}Dk_G{aD4BYRQ;B65-?M= ze(6EpiKGPGU+><@szPjZ6cdWkRppB-p^b0|Qd#ZRu9noB0W+T0&svIWFU2DhA&`V2 zu0#sIG>Mr?W8XTE(qdH8+G-6(WdEiXC$&&1Xvq`x2QG2!2Qc{Q_(61YA%5-W?$Hu< zxnH=<%I5?>Riy9Y}6?c8D~x8(T5OJMuie zLi2?n(+HR;>&3EI>Psw4UWBpS}6m z!toFCee-3d;SYA~4K{8+>Lm8S^|{(OqAa%`+HlHkkj$+I2X`K|?>+6_f7W~OtoLxQ z_vCl+2!WH4oAieByI!uQd#@fABO-i^58<-DJPBn7x1~8AcfP4lSnef6Ua2eH2o7Wh& z1s<7#_}z{)rE!QY-j-XVvv@*K!7WjeYf*TUO{$_@H+{wQ`y%ox?q8}wCN<~zcnw7o zJIwCoNqQVODIdl){Ls4Rbza<8w11BAXyYo~axx!GSIDI-dSf%{ zr1P-EC+S-WRBZWivgCua;LEvU9&?bzX#HF@#AC+pIgpa~=_?fp%)F;K=%o%L8ME@S z=g8~R1*GJM6`vZ36xFE}0*ly#)-RWFx-RVaUdq^)MuxGk64D9@B>H*nZ^Yt502Pu< zM?)nX8)KR#)^(zrbkM&{(Dh@VmH0Dx@T8S%`3sWaS zt{n$*Balt{@_HGo^zb#YO|xbD^oAmXdcfNwE6;l3%A3Bx9*I?E!!dAWY(-x7OGODT za`w8$p#@!YY;NFh6ulPw^^>^^@p^tv*+o(%A|g=>HdKqTcm7iP7^}iqQ|qo6B$lA; zrlonGMj0ZDV$3{UYtVhh7A#fw@}qsF)u%bR?umR+2Y2XMpQTRT)hR|)HW-wo<>^GE z!GsBt^fV6H-R%r%IaY14RwH@K3HMLzzK66$F)lx5sTWlge9PO1Pg%z0*@=sQmW7I{ zE@GpQxPmN)^onWb90t%&7`q<}w|ox8m4u0(sf$n6c_Y#%6n!94;BlUdj7dR&0aHH~ zB8q6`qA$yHEMDP78D!<7lNdiKI)(Lgh7+5M2$@WEdu^?NMzBh`cB2<3>U~y)kI8+Q zxeu_+wLPDOms-Q~7tE+wmoXh#>A_cL@}$j=W~)qj5{`pF01Kl|WUj?cfaPLH#+cD32^biPSHUS?*_qokP2 zkL&<&IpuZL9S}||pwhYO`}#Y9%2qU`Q-Vr4_}HoPQ6}ks=2m^Vi}ER;Qmm|8q7+T3 zd`dlWT((*IUcWySs0?eDQ${7Ilr3JfD|}KkeHEb6-=>rdD!~}LaA$oMWD>drKcQ!j zu+ZX8gUR(^m9T7GMwe|(rc`E_%a34vQ8m`c5tr?5;%pU-tXO&}U226*tBlv@ zrv||6IjFR9q}~%9+&C+#3qcxHni&9r>JyjXSD*W#IP@hSSLXh(oC z$JqQ_d?FbHCNV72SLrNA)n2G4&i!2zN-N)0IhyN$g2ly$>eO6g7fNyi%Es76L6l|G4AyA%M1C*p0giX9{87OExobe^ZD z@InZIV4_oJuu9*f$@;_^S!bKbjST%1-yLJpan7&`XH|yvf+b*#req75_8;|cw?zns zOqEQy3yCa^UFJZfO-91{)x#&{vTwOri}`h)i86%4l^E^&PL{eC4*%PBMAb*n4>8w>6g%GiuJ_(NY8UA1**#!!zsf~1Cu9&9_vpkaWaHt5|_I!9+P)W%d%kl>sSPTboCWU*TeL*R_Vde^3 zc6IjP8=AE-`=TvE?^r~@dWt~+NUxd`-sNGBMGM|jxzWvNsBXy18-wCqyr;GLZ>NxSC z<{;CB&GCE3r?1a0K0G=9!1h=UUmTshIy`=b`IK|)(}$LF=9KcBaH386&GW^PZKu?W zql6-;-l{|?R{|W_>GOncm zXB}aq2|#5PzOU&5yRTD1MnLF%zL%Vfq4N-tnybHXFE?$-%&f9f2kAbUi5p z--TL|`+sS*if>7=xve7OT~dMDv;uF6r@IbFwy*bx-2>h{)6MS08GpoSiWCY18~!3M zNj;a0a64}9GF>G-ATC&E)T8S5qsms>x7xb)cemIq%v1u8n19JYRobq$CSOXrkcx8i ze(R>st?ZI#JQkmuzP6f(HJF$UW1*kqj5{dzh3HtW`uPq;Ex40LB4F5k#S(i)0cbgB#e`kmGu{S>BnGGikOU(0Q@1abz-GSy=YjYPMbVg}U*9C0 zbgjUu1K*c#AojRe&;IC0qBmnpo;}5uQR%ld7MZJx8B>whi}Q{h?Lr>F=mj`oF>%F7 zM{YzKs~Aq8o2xfXC}KxEbRp=J?RF}Si1bB8q_L#7);oxLndKQUC*TzrIoDFZsr4{01`DcSkW9V z>svhsU|_(?%f8kcNGYo13;+;y7%CSIFyKH6ZeR^#6iL+6wIZ|(e6Cbk5ePV5!?lSh z3$iGO(Id<~cwWYt^a&sYffmM8Fyn}zi<-^cX13y~y!K?^98*A_BPu>KkEd{Zm_gbG zY{Q#vd{d=t^hzi3$1EjN9>&KpC!Q3Smx-tQJ_tj#M`3a57;3&cjUkcQ8ZBXSwuGJ3 z5lTr9%f)&n&K+iSuJ4dw>+&b`vw<;Aq_h-kk}O`Ui$J^+PI;SS?_xg9A$Z?-2{~x1 z#cO#QdHTAjIiBRAvvU=Wc|`$VL#-BpJkaTa(kXv9uz?X;^mT?S=O#v7U@7GSBcVV( zgyR>77hJe^^5L4~R!oH#==3rR` z7jHhX9LRlE3^x=Ap>65*-VNV*bPpAS@_U&s93RyQs7(2m)Y zcMB5m;;cr23e5<9>=fMqJ4G{cWLk>k%zpU;0sl0kAz-^!SQr!WcfK|3oV0gUOx!4n z!uV6l)yDL3p*y%4Oro+cw{^wMcJ4zIaPUP*@aplaG(ddB)|_VU1pN@Y^BORcnYz-StR=Rj)0)XDokKInf*Z^UDqZpk9#at)JRs8iIBmsDWI$I( zUCDMvUgeMf=Ss|1f{<#E^_C`5+P>y7BftQSrdm}G>ycC)!<(>s5meM_rM>wXVSpb+ z>t9U#Yind;{}AI}-r+P|9#3J(9T8m?J^wUFh8&F0dxf$6>&)BGz7=I+2(IYg_pVl1 zn5&s$N@azK`lU}y+LiRos2x}X^GoRLO9?V;E2cvXTt_PxK!wARc?go5^gVonj`}8i zWR4s{SqsMIV)o4XZss(qq7fi5AgQQsLGGX+LmQsOfuzz*Ktu!{5Qy&U;TClpP(K;t zZ(-g`zf0-q(yvPT9cT|w87x*2*mMTGOUrjlJa9i~2dH$6Pde-`wk9##Q9*Q`U16Gc zbUDQbRe%<(ElwBc!m_Vj9lahqh(}1$qk|aoNL8vm)xbw22Z-QH6k<0(M-S`Ek9_+$ zlukheU;Fg&%Y(%gOX`o5OJNR5)H78B2Sq3}SKoUV zkRCzO-9$z1^KAOK3!PLdlCl8F5wXPEz%X@>h&k9`<5n?;AD2)VFClp3)kvRnK@f|< z_aiaG>?k%^WKo-`iECJ^XN*l*e3p7{GaXYGF=9J~9y5+V5<-}$u~UDaO;m^ldW%n^ z94e$P57mjo$&teDDlEndq)x<-J@*1zS5y@fR6JQe_3^}*SaD$_*I@}2LZ%?G%ugbr z&V~3~PrRa2i^Gm*>Oo#+AgKX4k(`Msl~6)_`^90#9byyipFp5cdLG?b{I0vjTo}>V z<#9~1l%i1NVIqZ(7J>X4CK=>B|AWhhE6lM-Ejgca)gGY2-28S+vV30)ms6JfdTiS% zkW)H_3hk|Q%HP%W<&<(|klsNBk6G-~U#j`KBB5-3>(kr4t_i68JfL!wRR+a@KVt2% zKOE0I_Lf2AQnn|TNu#jzu9;Laf0IHZ_Iq>c*r?m&I?HliVdDrDfF1y8cd``Xy&4=#` zv6K5PQ4_N(_{tlERqV#yMv<1;sxx+yX~{!nDnx5nVfX!L=-T1H;=@)*D=a?FRxZE^ z2qfFRiis6SJywFe=O(xPYns09xCgA1M=*hM`%y=ek$3GuZW)2FkGR1t6f}eF17H9r z4_kL0w=FLJOj&v8Cx$4`TH`%n32}vg)P^Gu+eoR+>(v>z(CF3i;CoY8q9eRsWEB%q zaJ{TkDEn$c>qsb4!M>IEPZ+XS$Jc`0SuS*-{wrF)dLAH&W-WNPn&?4hfmmCSm3iDH z9;(9~`dFz4pKu{QJ!x;UPF%ryV#AE#EcgT-FzPC^pc+8SLI8t(6(bOEi!%)Fh#8hv zQ5h_N!F?s9_}2~}oQV{TrC>W{IL~8_Dm$JqDDMlEcADPuCBcfTg*}#i@y4-LKk=qr zef-aYUDV)4@z<#BPN!->^Ady+NFVXfXkrh;732p-_UY^z|1M+Laco~%0*0N(Mbs8| zY26vh?lsi@W3(F^sNi4uMM&XYopf@p(M}vmAYB;GMwU)qv!j_~DI8ki3WT8r`vZKv zo?T&O!k%JC<#UakL(#cx;S0*klI%G4V136I=ut+jpu8Hr*Z`)$$=K&sq{G0BRQJNJ zhDnxE=F$V&2mkQe(Me??5KOHP7rn&f>3>s^LQ$`=(kwJ?HH&q5$P|fW`uhnyutwk3 z+zJODC1RvMBD>FEeV?+1eMQn8fs+wp^g|<)^cH%aj5jBkeT9Ro&pKlrlzKV??LR{Q zX?HYl&$8nn8J|@ljp%oVxfG1~*n8wON&zZZsYkys2kFi4lweg#$W%Zw@;`cTg*-4& zS@24#1*&)rMP#osxnPK7)Sy=8WDGo@0@G&=@f3;q>53!n_8?egN&LWv6@+LDWkYM0 zR_(X;QVz#iY+k4CpjEW!%pV=Er{9pTU*NR<%{oLVO*38PcQIZmR(H?$7MV6=8$3_*@HsVNE~r5LEJPuUjW6OiZD12%_Lm?hSy!2s zug#Yt6-QZ>@bkQ&l$T3epnsLVS~}(Uz0LXiovKe}s1Q*3G}Bk>XZgOi$5q;w^Yo#p zl%P^=gUZ#6$~LIHomH;!eGycyiKg_o=TqKJDGn+K(+`G|x%$2)i$YPET`B%5L-Ve& zTn8$>;#(!CSnHPuJVFxL+dflI=b#5S?(AQVj)c-84uMmMIR}UyH?)+}h-pZ{%oOURx z1S)uvWV;>!TP2>%Iq;x-^bw0FP<;F>OEg7RgjLH5>)YS5`n&QY=nnh3000mGNkl9Uwd?N)Jm75O^Y{L;*dG!uIkSw!^0LO^CmmaF zd|Ex+>pwXViaZ0B?6t-)chx4L5K#av=_@1@$tkSxivBJR#=A`mlwvV@J7fdv)qbVz zjj(PjDuXG5w0ayES!*4oP&u}(Uo+ALV)RZRx$26Ml0{3S?=xms2rX@|;mUo%l_pl2 z-8{E~6_oTk1s<#_Mw_tvwbH*D=2M^ZdiP@09H$40kTGU{dn~}+~T zJ`}xS6i@ho=0(x?pq&=`y~dtznKL(!!NJ-eWQG@sOa)$2LJk`c#5pv?7VnbM*f~Gf zhfmDT0%y!)#1wkFr{6MDc{(iOrB zf+!PaOH>q=S!Ip~6t_e`rBldIT9}F83X~8O7T1woK}LU;2V1C9b(KZoo@4qJ0}sm6 z6DDm@q?Q4!jre<&&e74*#_Wv^CeCN$6LiR056Mcu1J`GyYEZo%B z+)&)~8=H7i^%A9Ek7eMk#67Aj2{t+%k_iP9R(=gx;bjts9pm5-wiQ>^DBY`*_ym__ z24l9~1s_bc7(`Xbs6c)hb<~DDP`O!82iuBSWmX==j6GH$M@nTRXz3dSn&c9&=UYH_ zV{ZnV2eeSZW3w>(ixaM-Ggn*kLHit19O=7g@k;dKGRsIMT`S8|fu-$b)G8n*Fxcei zSZFi^y%fa{rL}Cu?+#oZ3>5{$Zca-!SjC*ZWhvATw(mJoXsj1jAx(B)-Xko6tgh>EuoACH&EY&$T42*kp; zj#B;{m*itE7*9GB92(4=jLV*}A~sV)eX5ry{;H10nqe`(U{+ovwk$F*Wh0WhzU8}C3!no^O^_LZuuBPj^SmkQ9 zm#sYxhL=HQAW*R{6Nvm1egNmSU70bqXav@;>9*Y~t}pCirRgAF@1U~fm5R)I#6|9G zl@vz+-T4Hx5Lj~8v-tN9(zlq-&*ixe0G=4x%Yl&m?2Q7S8U?-RT>r*h*FA7e=8Xpu zF=7^GJD{?EBLoVx>%P0y(3N93#U5fSJXZk{P=TKM-sN5)lH^KXB`*Bub|r;U>N$<_ z2;RtsF5?Y8W{k8HR~8k+I~c6g_T^T@C5VeD_Pt2b(=C=z!9qu7)7mTY@ePlE0P1v` z3@W9?OT#I+F@y3<#i-W{9x#Pi`~|SWn07CghDGuouR#Hoh{$tVe#c4X&ZEw~rxhB9 zZC(DsUN0Sw_WOl?;-m7F7UlOl2}P#O-Yk91T8dpiLD`q!M1o1TZWSSu5>!AYQ)(%Y zDHQf_P^nuAg4>)T_Z&Njxf5OR;1f4}TPYBA1`D zF8)xS!taA>pr244q4~pH)$s7)cL zyo~ev5!NAVCl=fud3v;pO>hD_@fMV6FT`<@&ycOh@`HJ5WrawIM?vJ{niB?OT7wK# z1maoH>~R#>!}znZQ^v`%>w_~KqKOH}D0e2bpa)FZdf>+rPdZbM1y(NaCgx8Y)}sO4 zbalRgyTyEmertIX=hHLpvZo{QNgs%UyQ`aczu8TA!?SQESlmqI6N;7gAE}vy%gZhU zRE!pEai!-s82Iud3}CIQ=>v{7RAM#6V=oJyMBOlP#nqLGxCPhrtfaWc0bvB+WthI> z#NrA2VPI7&h9FgxOhn-^Hu^OH#%1k1j=O_w0~@Bl6b#jSKg1*|w)0X!mskh6%;zV($T}P&k(V4!UMjwWN~)SMu8;yMeTJ_Jq+tt-~p#vf-3+p$mOr7 z<`LcfzRa<#Mqq})G)!n3fcFg+I?5!B4D}+HZY|@WLXRBi zg%&>iQ9d*<$wVqmf{GIn%Zfc?Ctf&4syVI|fJtO^%y|9f0KMv!2|0q3?c;>UR6WbN z+hFRZweZeSR4FJ$jcvtz3Pv7JUz15X$fXobUsoxmosafaR(T7kT&*fDK&6p^8eP#KO8*IWryG>|gwUDix4;m7Q{%d@agZ(U!4 zN})gG5t7`sbtliN@XDJHI@`9ZgM9hk{<|~?-6Jy?nS54ALv5sCE;2O^hdzUTtp_bi z8Sz{>Ns?QVBh#PFUD9E|KLnLfdx}kx*i8YoC|3U^%|c5a~o z`KDXfLRTpXYU?%UVaKhd$lPemm`#qdDDJ{*$^jPD_$r{1_h*6xLQtZflSeIiWzbGh zd@N9L1K7KTH&R%CJR!ZH)R@Z2geTB6 zfN{{Br*t&p7bPTKrljS>2|wup@KB=BsUhyWU}J38Qz(qfE8pB7yEn{&_zaix3{UHj zOsZHMf1zOXz&QcQ6YKI)dR{@4-k9-{4YDitGv_DGe)lP}@|922yNWU^beO4YIC@Ns z)7NY|j@9`nFJ|0DZOCwTaUdn)E;t)r3{eba!z>7ySdu$|$G|ct22Ne-Dii8jRZi`r zR2Qt0ZqT#4%#cw5kbqSVTK$e$q3o4(8Po3ydn~fzk$Ri#?2pMb0o-U{>17ufv z%+}KhhU=B82cV*Auz%1@<2e~1X z-VE>tX@qm^sNjolt>Q3}v8f+TwhtI zS-^d2q5&BLO~iJJ>=}}c673#RALVbc9N7WDx7#D!GS+6iLLJJqsH*q`y~Y-~Rs$7> z4s9G}9k02RHHvxrQ#N`bZE_Kx+*>GObusq{3eWP3WfI#+nMc7Cr_k*|3~c7>=MZ6z ztr-l@g_T3QjSrrj`g*;e^mB(EZ_pR(&%#gTnGg_^CQZ)0G;GU@m0ezJD_)-EO_jOZ z$zWh(mS*fFW6ZZqmM(xKp~mY+0b({#3*FQDcjMMp^m{dtTGr5Mx%r(0+lV_ z*Raf@%yYF@@vZBJ9pAo8k5A$NDm?T4?5(HVZ7f3V%@A1Lz2nth+qn)Z7&21MJ6$3BqUw-D97KgN=2|Cg;LWrFCIS@;zBpLui$Wh}~ zqm->!1?CPkDrUuCO9mTm$oQs;2U?2h`fTwML^<%}Dq$%q`F(l8@t#~~_p~u@Ym5(Q zUDI;B&(wf+JTy|@JTNMD#*1% z`amo3s=`L31y_bhr1WLB?-B(^5Ic*b=1CiIQ&Xu)tx zSEa@<9gXIEShY^26Fd0O@Hi7(z)9^84m4vGyA70<5i7jXw-VCeeL;SS>2#M_nw{YR z@(k!8Rx2@q!7q;(`_}G<21OkcPp7aHK4%FMw=()@8t~F#XCBr=c|b)yV5$>{ z>!S*pPB3Qo`FZ>_4`o8?sl2C{MN;@&oK_!vOg>unhv{6|awh3(jVp9b_Tc*8% zD2&=;T@e~fC+ff4*9SK6(l(z}fj%=*1O-^aokxc0byVmh?sPe*(t@r+)x}I-DCy$N ze5Zmu;=A(jYmGuwNgP55!J_;G3-qWbw(t4`3U!!X=rHN+OzgkP!>MdtoPC5*uo@=r z1zaIH({UjsU2tO2d}mnew@zT>ka*2J&<@M?^5W^fpCxluUE3+70=HvLyy4gEMn{T9 ztd3qS4__|UFXpQkhBx?u@RZ#k%2>z%W~TG+v#>}7s7x@HVtXkHnsXQBL?vTIY=e;x zp#_fF$_E)e66514IVe9_oO*3B+X8asaSq(msfDCI5F&$$;RF~XudFx-Db!B!WR=^! zh=1&8*1@rw^n-%*F_X-4O7P${4A2W&GOb0sR{F^Ti?CL{Z(iw?JZEra!N*ya)&;%%BO-#@0!Z5%b?O5 z=(q+{u2ED*Mk=GBXDn$k=#RDlBkL+Tqf!9Lc4x5%>b5G3i=Z8>Up%qqkUVvjZFASY zFKl=gCF>hBWj(_I7ZA^Vl?yMbhqu-kTaGtHYj9cGTa4EK|CGIlb{koiv|G>ccS&XB zN+pUUTmtFg2pZWkJu~nBH23U?*!!FW$?Cqda&bXm5&+Fq=ZA2zrJ)(wOTCJL`3y5o z9aG0znRv-z@eEF7faJQ+bJSw2WPEX>T#byNyhbf2P%#O$zbH~7{8`0M&k>?%M)qlx z;$;M}T=Scizw2#=mNO1{8NVt1vhuU2m+XaiZK+eZsNTxoa{d-Rs=w>a%%@jAQWb%! z!;H!WX~chAmpUi^B*iU&B3qY#89OR(_=O({Dx?$#mHDgREr0Ur_vP!~mv1ks_gD4r z*Uj(b6C4-w_5xkQSId3|_DGasw=1Cn6cVk-^WZ-*<7jXKXc;N6u$wqH3m4X}1(({5 zQWQXEdVZme`E4!&me#RVq{KHF*WJygKW*$9?zZl4IhLZ;`FiI&Cp(pZ@TCB0nlbB4Y{*K1$eWVp6LqBTQng1_v1uU68S;*Am7qJZ(*--x=arh#rGn25tm>4JK{A025c(Rr=X3%a&zNG zeCt7?i-w)EG4jZ*6;z6y)J4k&>6la*II$8gEac`<_Y7e3QZjo1WCpJ)md6Z3>GZ0x zYB{%xZCHZbY_Z-rEGzYWIrE^WxI*_X9&q8CDr>RgR^~Al6KhSSjbF;rv-N(b#FKfY z=|-hiBA|jXMjcAut85lg~OQTk=o0?{PvN=!d!<3OLp8^p`6kyvth#{poJ3^k4V+mEC+*fk}!3H ztw~RJ+zxO_jp*~;9(2*qfQJB^YKD2GleWR78EW#vZ1DM^$PvF3xkNt4ICq%Jb zl>5KTsdf9E?ZfoT5meOf<(n#dDJRO}6Jz2Kv~ZIiutYhfZnv}9JWu5Sb? z$0koY7HUbhay?x`{KyZc(d5ZFU4EP&N(G?I(7%WgMrK7&uEoo7@C>m;*zm7Fmm)y_ zqi6I5EiQxju3}VgE!c^iWB1`uQY>V;O#kbpb3!aFUPX4lc>P!L?YOb(s{FoQzCP1f zuFi^vBTteq{h>QadU7R7w|#yw0A{KhO;aM4`@bf%;79lIMLDrz7AYz!UVr>5(@ucO zk5W0xrEy@E$6rIOm1Y({p^$yRfezqD|5L_NB|0o|+?Thm6$Xe=CdsG{HJ8;cbt|QRG zG?Pu2{s%Sv?{2zMyjclRh&s8acIwW@_=0iA&AGtN~y_895|sf_E!_|DK(~ z@NsF?o?9y`^^&QlYo$9!54Xt5#X2uLhx>r$3LEMEA_4(aI)Ms40#x$+{8FKdoqL%E zCSm5QW@cTt4Mi27S8pk;u-taR0T7ikaTl`TGQFiu>{)QhE;1QeTg)H<*H<{bVwXYN z8~oRNi}!1f_Fs8exo8a2wxd@V%kT>=>`!zRii99|TmeD;hP~ zj7wp<#1`L~w#VNjf%%}#X~0aFyTm8CKn$!Rkhtr_6^D@iZ0qp&RfC4n_cffn{)Mzk0@$$LTu< z>ESN>1ZBXa3`dTL)Z1+_(&2#u!jNmeqH63eLO7x704f|*c6UA~8BFp?HO>2nJoxB) zDQBS4rxym8geB}#S*fhs(>u4(zs%KnRtDdEQC#HqPx{>U9w4EF!W#sYI80jMY6|;> zJr*A%LwccNRt`r-q<&v;%i;cy1TzUz=8020KC-m`I|G$-M0o@%|4{$+6sRmGipn&m z@K&5#aR2u3k>Lca>>sOmVrEPouuUPGJN`00&nT$Mc zr*^M@$f!I6D&&wdn|^+|UzBvkMZaBMrwdKg0aHJT6uVO91z;dqL08oxC{wWm#G1JZ{h@fNMy3?FIU6ms=qW=X|K|e z5M?RA5QB7h=_pv-F!j^b=UW1|;lTg31yABSi%l&6PiU#brrfcIWkUBA)~* zc2NscncX%B>B0l%QTzP9wW%mD>Tq8r%7}47k6n3t)7l+e*6u~UC0!bJHWO*dmUK?H zO_y<2bKGxF>7kY$l>uLMRUMlud2*iDi{4he;|*#ZF1+mBzJ*fYnkovHH@99=ypJCU z)APA{xaQJ%C@s+m=F^aP4NC2+k!%-h^m&RNZaSac9E$ClJ`8nX?vkg@9d7Jb&}tzc z&_W>FD*eq=+$5>JdbkTMC1xUlF2*D?m4LO&oK|l%kb>J(P6!*M7gYv>XJ!k8>6b;D zu#}cHS;YePi;7E*bB?ayNGC6gIhU1TiK#C3=LC{r<*{b$x!btm2xb!)vx=;|2`TBX zXh?@Ifu6q^$sf2qY(nDnH`ymjIe}8>`#N?=i7?Njzao6(+yP;=;n@7h>Mgr8AFRpB zEWTfzX(d5gX#yMK!J`-SwA~poc`4-nC`&1$dh+q@^*>_90e-q1^&y?0>?>Z!GsNmlRsnU1o!-T^YHiS;cr;x1TaS` zEis4UMlU6B5R%y;mtR=LVxOMAufM8wLrO_JQISmG?|K|fnFuKvRGy5^PZn3EKyn5x z+4r@a)Dst=QbtXlP*)0vSlr_^UpSr+RnEJKomgzov2N{PJy<%31Eefxf7w+$T zz!4YXd2;^sETjg;!LiCR(b$KM@2)#I{n1qtZ2oM z&xleiKd^10W9{Mz{9UXb_TJ$wLB(8K?H)qPfV$yrd=_HGuAG*4ePg#Q@POka94cXY zB=5d^lHNUC>H640Db0A7j&#EZQphT!rt9mV4s{uaTZ|4d_7|fP&|$vW000mGNkliYpr@~#DBXFQdbaDVZt8%F4dH66sgDB2@@k+ zAJ^(1vq1Zx1*T2J0MYv8G5VFq^re$@ezcQ=?E>r@fKM#_4U9OpQm#OB6n4TPw$iVl z&(+TaFj&I8<9Os{danH91vIXz6(91(Es(~Bu%U5!#pt_*JHAYs2=T;AztO+&a}Pf4fwF3RI6O2E+ai6RrG zu6%|2>-*vK?VwbKh7O@Nd}o%%rz9W5d&uS6h4|4Eu1uFb!+x7sJ z=2=jAlu_|z`ZCILwxi-yvw!qB02NnAF5>F8+(HRkS7}UA71*n+ekco3baTCYd|5yB zNB#6B3V*L(X5nQXxWeFvy)dM-U%n}PP&r!?dV;i?xWQb9F2`S&!HlaiXL2SVxdKD{ z_=r;7{9}3H5tPz47HR_k#6Ua03VhuZ=c0!zB=T9&_vMGf(%Wfaag-*j4f$haPNtx8 zRXE?ANjykH+9^}@v~tA*Mi5_iFRu#=5hh;wOJ9-ysc^wDA}{ZjyT6!Yd7;^pIorLc zsQk>>i2)3%hf&^@&aol+-xj2lHy;o|xwfrFP)bb|#frRE{iP^{<0smQkFoY#E6mh; ztTcKQewGdclAh(l&`-F9Vs&2W-BgpxVc0%>v%wx z8ncNQQI-TMqK>-VcHMqA9JlF`J^f}I^ofBk)+!w?kDeB?mAqs+_?II%K`R|$n7@K` zElJ7-e~eO21M({49_*2>o6Q-xkJU&6mGoi;^jw)wEQ1R3;*dTb7%54Q)1zWIrt3Fo z6@wmfqfvPj5GV3!HKL+R4etfo^NCE2%zG(Ec*BnVuOcsv=C zm~5hWjHxU56cm*s5r}3;wj}epv=O=U-2a}(-Eh1gY;xqDt-j2zw8*Xy29(R+>r>5O(L+LK{&Tz$YI=JR76*)D55%m-P{ve~2>&kILlX6nMa_5rN&ryNB4C%u)Mk zHio?dxbs1Fue*Q{iYj))SmpjtLWt7yr*FsG@99o=g4sXGbw>dVKn3M#(_zPNXY(lp z6=v(1QAz(ieg1c~Y3K6=@nboml;vbEk!tR37yUPqHX1hM9h9a(xmEv0b3L}QTC{Id}{E{2JUKT~b zFC22pr}jqxsDM)3PBtrEd~V9hKqY^ghxKFsu_!^Frd%Y3lmY$Hb`{C>7i!<)GkO}{ z9)X1f{*!qlP$B9B^JFE5r19!wES!i~Fe^Eskuvzs?qjT%{QQou>LR}Dmti5J;vrD^ zK-U%@llColZ3)B7yM-QM;!qiy*Lq`S7*Wd4vUq)b8F$)*ypc)zXgA(3UjM#){lVr? z-lfClxr7Re(RGl^zl8(-hebAQ*CLdx7O z+)Zs*G`sjheC?KIsu)$&#^QS5r)UGKay9ViatdgH4Z>*g;>j!yMu6UsV~x3ber@8D znU+#0kHEy0palxX6mm{{kieBSYl}fdwpcGfBC*7dIP59Y68V;8W*7{E=*NY7NH1LH1bdk0itMp&y*L;Lqz{g}gC!cznQS!v zD9mVP#q>MRVbL(7EdJ$dvWIiXX1>ax5*iD25+{%i+QT+WtkynS81>Pp8pf8TokOzhMeGA3F8)Xbcbn8AI*no_M`|LEk)j*2d!yEpxcw)Y1kybe2=}w8#tg4 z$45-un1n+smQ+D%#V*rt|F|*p5)5GXf9&tnd8Phh7{*LI$*cC^ zz3TWQOg|ZR+&%)8@e!Xqxv!E>C^Z?ge!cX_$5c7VaN_w8-&)}%xQiQEUCGtJbJ@V` z3Ola*C&a=bMUzBchYf7z_A0MDo@8cnyKy#*St5MShl*uJ&yM?YV$@-Irgt33#K)6qijtQXYBnD#V{~XF*HR19Eib8Y7G|)^>SqaYR;T zNSI*rghMEXF_#HZURf-DrQ>4t?$XR%c3f@P;`ISWFAK_BIG!o_&Lxx)$8a6X@s@y3 z2SB9-X1M8#yrIL8hMGx?oa~KQ7)Png-eLYzU}IY-Uf#t3$;#1!POXTAzzua^2iI~v zdPne0jZ&`-TwA))<{152qKby+BbdILXd}CLOe~Yw3&>5xdfr3O(bD0{Tv)4)4PaDz z7i@?3M_DC9->F8J1$YdhJrn5$-${_>+gK@3wichb!Bga*6m8Oyt zRW>O%k1Ke>Ks`|EVR_eD`o`8FWb6K}0Wf3DS_Bn_2P$nrl4`^%J~A^zYDLo}JzKf9 zp}P2P4B{mbEa6Al$>ODMIsby+*Wh)}%=+V})Og9krjs@$^HNXIV<|-o342Y;w~Wh^ zIn(1LVwtUCd%o4lD*+5P-J3|K%oHP`eQV>%_%cinTbG29c1Ag+(t|8L2hX}NC@0|% z+u$#u1wr{_EEBvQu2113QjAnp~s`0b$`L8?pFt`sLh~e@U2M#Qku2{TX3;)H>rM@FV@Wj<@CnGgeV{<^iwfxhQI zBLnkCy8rIW|E`cup^&m#o`K3r)~_db|X==H+K;D6HiQ?6;I?}M!zsrlxt4itE$+?>0|9AS9S5Wj;kDq{|$pDZ)d-n zev=FP{2bqargzl%j)7owC_h=N`DbX-SCS4FedR>60p z3uq~cPbNBvxxB6`X)iP1KOwG^It2j`Zes58)54%tr04I_ zLC2TNAjdLQ64Cjn{pF4dG?_zwc7{^4XV0RoW9-nQ%vbyFpcglNtwY(^ap8n>_1u-u z*rQ_ckJ-wK!p>F?A{~jLl#a=G6&o1L$STUK1T_&+-4EaEHpVU$5$K+TlyO)H1h{Or+QTD)kX@6r6Lc-9Saj z!UYq_Cf)|NKS%r?{&5AMhQq22u2RY z@Q1^OfN<7*Z=$ z;$>e8XB9VzkziXi6cc+k+JdY$v8;mi6;KB(e`Ke`DjYmn#*`!n<|~(6)(xA-7T9pq znV=e>3wphz(NSch?!=tE()Kz*@fC)kFzMr&mVx^!sE20v7mpo86?A_gP~JsgU6b%P z9EDVp6@MpEJlu{&mgu*qe#fi}=z{C-V4FPbn9hNWpfyH59k{|*;YsFwm zD$0$NnL;YjSi8mC1t;Ky2f)LvhUXnr2sSRQSXqmmxn zZrtHcF06kd#_+2UVtL?1I@jWT+vFkqPJGhHky};n&DW4q_By@>v`}E#7b_z4L~W&e zfHw}DYWc!VNg&GS@t8rygc`N|ej_}fWsCgtCub!o(FjzGf+&U%ezg62FN~o0Lw%J63oDI0z&2e+cqv?4*J0dJ;h455J#WJiNe30D6j;UZnCZEG`K?lNor+NK z(qupJ>Zi%TOQsRmk&0?8+=GD4MSQu-O0t1vG30d?J^VF)eDS)NJ@Ht}k9mz`9tXON zmw_=-IWqiQNkw^+g`Jm$%`E&~p`!dOmzU3{z%%x6~vL>k5VU$uEu3!j-TPUIwgi@|nio##7htisr*yGwzWVkXK zF_K}np4k-(oY?IixH5!Qj7&0H7g(|$BBL@Zo?@X5M=i>83uTolg*SBJzVw;-4Fwa= zq7We{s*q0#zt+A?s6&vkeN* z$Ykd^4oWCBFGCy1CNyjHZtLnRkE8fyW$#ul$^`?p7ydVQt^5GRggz!OLL8?i?)j5F z@ApEjQLB-F3Wn@s_L0S3(Qd3fiEAAaK;*$g-bU@lJO9Fi7lmo4p5n~fd%f0Lpg$Cg zD7MDPo?rgi4zGqTISt6aHbx0qGT+f!(99xhEElW7Npp($YC91)E zq~QlCWv!8WJ3uK$JMf7a%wP`1G!re*r+cWw)VSReTGl8dUTf;neq5PGpzQhIXZ9b` zFqUp}c5Nfog)x0=b~YyI*@b<>s$xSIaf_#YGZ}v!9ru>PAyzNYr$T)%cSOiXKXbu9GAV!M^)I{lwDwcj=XPyAz#VELmn=pR5vTlQu8L+g{D00SSD5cWu))ob1^XgIi^%_!WT}X6m8BNaQqszY3a5_S?>N&$UY9E7n5A< zC{p;hpZ0x{JSlSTqL zUcR-;tQWqy{v($|aNQggz&WWx6~ZtkTf!dK3agL!^(+I7-tQ;FOw;y>2j18W$j|Rq zIasf1FaN^VIZxu@Z>c~o`&?(WA*2Qg3#9_1S4*kn=Rl*PrtD0X$u6&mzWDY1 z%VaV#PJo5%LrgkG^)P?tKWe$fza+BzOv`AT#OG5YyU&fq7+ZXull?hT{{|~APAQ6% zyqo{@CKreiR9;S+h!ck_r$^4gzk0v;m7T&Ls<#*GcNZHVN&`<nWZroLJoVrO6Jx@2*sR8%@M>rmm}&d-E%|e#y$^ z^~8ZIyog-l{cm0}GJNlqy90ELk(EsmwdbSR#0uUhVrvt=mV@+L-)Ps{OX(7`-`SNA zjA7pK)zPwlV~PsCi%Oe}VihG%@T2y*m{!Xt&BzRW)u<$z&_XL$KnNrnPSGol!aCZT zgZEZc#czbqWnZh#Xk}E?O3Y_mxuuQL6NyC?1)IE@vhs!lMplT99-7DUX4Qsl;_Hp* z^;*~sdajmL5;X-7+uCn;h$P2Mlh4Y=!;Yr07I<|J>v2s%wUYH0C*(k*pWVUTZ?%P zBqBbhyks`0athSJY^7rHG5Cm?mwSpf=Gw9buuln8Iz?G-qaUpH{0zFVxCQAMcZ{5j zNSSv&sKT&&x<(Hm+24HQnjyNsY!c+4MEDn~1kBm{{$r5Lx5&Gj+v z8K{H;vx|nYTMf3*59Z^EIynl*_W5NLnB@1^>j$W?C7CmhTiE%m40<6p93oA)-NSbp6dJhZw@CDZmMj8QgE=R_(@Rl zDHY?CKGBMeh7AR%oJ%E7GRb4>*FWv3lucqkFgI@!%B$?)dLf5W=n0byz5BQ>vt;r5 zI#Mn#${dTna8CJ!x4oWg9(qf<=hydZ+bD!46UMM2ZSP-|i-`p!WiF-6v;_Qkb?-bB zi5G?7P*xb{C`<1C%E9_#`u!@x^ku)9*AvHDV(vXFRgwIfbBo%g4>FoI6509}+L!!5 zmBXD~#qdjZc3o#U$=@S&B-y%58%a;`&YF?2E-!3DP~Nss>?1}|Xyr08dB1w~dj%oI z)RNb~S8qS8?YO}40YGwDzrU=1Gcu`py!*I$cTxRj;rRN)b@TgG{rlC1EyIaSu*}$B zEo}k?Skhe~C4aT*l$BqFU(0wSVTOGw@y1kC$z5I$Nzys#Q;TM2hdf;)9<$34dDL4) z0deF^)bS;glI^Q-#u1fY*a>OS5!{_9bg+zT)8B*>Ov8?CMwGyY#4YuZ{`XQMNlut( zV&zD&<-A|G*kCb?AuriJ{Q;$6`S3+Z(#tW{TFe8cZmt922indJDag|Bh`2!B(Mv+g zpZ2gFgD#0KD&OK*GGt;XwYNd~b}uc(i;3l7^SsK+tzST$Am<%QxCr`OqLe9>nX0hqw-2xB|0qjER86+_e{)i&REjt1)&R zp=UY4dan}RGCI%1yd;H9pMx1Vr%4xi#psGHKMn`3M!NfJPvKhwe7IQTWt>m%F-cLYp=S&fWT%;zOoazXu<%RBkkKB-<^yi6$lOI-p*?I#g`^>(sDK#1 zJ83M8nm|{PW`e-|yo{rG{@%~2y~~R$^*pUDwHJNXgp_VUn1MF3kfH*4#0yzI0ssII z07*naRA_{~aLCwr_+-^|D{Hk5T?Kct)T>sDgMk@)!`LaO9E&wscZ#hWY?wmQ9W<|| znmG3*BF(~zIn;lVUfgA&Vq)}t0awzabx>Mt;xMzy-rvq7n&L^VUn~804}X3(Z*Qa2 zI?()+&P9{o@y{paG>R{^-^b{ooL$FH>0^`^TP+x|#O!UxcpR-q@vcTf z&~0@ukJjM})e_Sy4xjf_326N~x*n3QxsP8Ns7ROHp>KFIm|ek-48AK6@<7#M>(yaL zB+>CiUt=fHRT^7VbRU=byF$k>=E;fqPwYF!2qV;zJNU#P?3jNHzA}!uYnjp26YD`l81)J6Z9(c-e$2W%Q3<3Qz&z+)$Mgeo!`{v&$0p z_kW)rlyBiV5LG}gP*-k0|GP^6&+Blq3^`@Fe9S86WnaRNXOr|##OQ5P<+*O+N1$?E z`E?eiuOEX-CXlIG@`Uy4`C{Tn)~_}nP4fs;dafH!K;`j!9ECO#_vbsy8sfgRd*v~f zIDUBf<~q;GtMBV7oM1(iyqH=q1|nmV#7#}5qN3CCB%OG3NGHm1t$Si$055%3hIp9SMr{v1u>l{b% zs;+pDpK?}SXR2TLO%BsXvL#sPCw<{nqLhFA`~j0Aa;DjjOBH8SigE`R!zA#w`A7DT z*)P|Od|Xq3RgC({WzL@{_mXaykn;1}>t9VCS*OE_gMVL9CrJ=uQpsh)4;>$_+e9ND zaO3xDh$oku_aE0EuIrBpOX%jBw-+<}c(t%u##uF9+gxM1r@8dG#&l22p}3HeDaEyv z0p-FNXT(7!G35l!!pn9KP$^ULqZfHa32)ApEV=er73IQ8!_#o()~pWUc#E2xuy(R@ zQ`XKGRCEIi@fueN!x*PdmXa@GOMa(tJU+`KSS82hO};oFgKm_PUPimUZ-zSqjC#M0 zmaxrsO|_>@|Ep~0)FzMY8ChYQFcU1n5_mWLXh@TYE5~5Q><~lQ7aNL+E3O^sNogLF zjg0{nUB6QLD^13M-MQWwEUn&8W69wy4NvvZex0unZi6Io~44tfEND(w+ zf`@~*gmF7XjledO!h}8UXOR~}0=!tn_(|C>ypM)r+I8Z+sFZ-UozQx z07mM{(!(cIW~w3_Z=k|!RyG>TRHo)G$VWyp>H5{|UNuuC9IxyNNcd^@6$%Dc0u|I1 zY%Hl_s?=tv1e0C3yp~nxu%WUq{jOJPX*gX*sgq=ei|K|U7komC}riL`Bjt=*S(~SJ^bho0^i{@-Lbo6uXW3D`J>_ zq$+abTPR2R63yAQC3P^w!nUrx7aYrArm`<*gIehq;_fjrDx_bK$*Ht~H7>AhFg%)G zm7xWe+NH4=Kb*i1<~6c?Mg?Y0O*uEI$O^V2VaL**`r-4zEJNSYN3R}!P!PZ9EROp- z*}d*HKcWt+Xyg4%AW7HadvEvR`~4{R`eKc`j8EKhKjaU5Dz3Q&Dn>iSxTGWq^#b4b zyW8}U=@EBunGUlBjqC+K3a)G;e=msg%HTunu6$C(FveAA&Fa~qOe2}o1$k`Cyt{T4 z`F)v6gN*xMk)a<#P6RKa@wbCiHeoa87kW=cSr`SNn>WR0d zqEb)%T6zCuf+d_unFQ$7O?(b2-6K#jETNU?^(AxO>G;>L7ys|)-~aXNhvEYLe9%2K z2bE<(c*$XmvtDC!h?#}VQyH_e*UQU==jCjCscBF#;HC;eMV_=@E_@rlpec{<$I}&Q zelO;F#bjd_7+vTQmft<=v9OWvrM>t`R2=k1?f6HZJv`PH^f=C-am&Ex_Ky@mC}9c!!@Q1M(xA2-TjM~zybl6a-J z$@|Kq0$NR}888^UvzYC%^zaNk*eA>DQRea~O%y84z_w-N(f4`>9l>R$S)y?m7pbGn zVD8T$fe0adAzrGHnz+zJthQoX1{}uQbab%mY;^1*<+9H0#ye07FtX~F8b}$~Pr!@~ zpbN4s`HZ`45YS(bLJ6KOYkH!4u})kGV&<-_vl2&BTNMP5QxHemMaNFPV27PZIpBDsGDBVhD3#o13o198#4tR}gmx4CV9^Z52Cyg%Yt;mBBDbWg_5=^dyYHv_KaRIw z6Sw#RJ!k3f*$gJP7%X1G6`rua_o`p{$0QU;Joy%~1Be9|^f$~={HY}nq`mjDW_`_G zEGj1kDi3mP(cfi~1sCNXq)XWG*`xMKz^idsqa~k|KXFi@;NwFm<#DGu6iGLqD270{ zAt`aOk#%801vVy|N`r4F_TYCBsv+bY1ByObI_3DOaIAer{^YyQ&+EX?M{UBkopOgM zm3!6=JE+85z2o2UWIxhq3DYDC^_EXS7o_TuPH9%lDn=gvDT^{el#)sQvG|MP%zst< zb>3F|X!Ei3ePs?gQ&cwV0F_7U_5W!*1<=9~mHN!|Wx15%HfJ9RDs%)ynEug#W4ZQN z22q}{gq`t8d`Wr#4>^PW2I>T^&Wn-9k1>!^q%tNH;ni5a*MVzpVoBau3~ca~#=NQ? zA(l6}bT|h>0+U|FJE>(rUQgFEw3~I0RvmU)0C7eD!-e!P;9}zp ztb(IwZe;F(6C_Of$h&)U*LKw&s{TkVWf>t8jyXaz@e*Dy+5(waFx@WVw}b(jmnfA0 zEwr|78iEX02kn{kCpB)-BnC)W7L2j*VG~wnRTxwWGguUP8K|rbB(S@pF5qCKVs(BC zP&x2%){;K@ez*a4~=fI${y3` z4ny)cc-dRVP9-i{Qj*j+J2#;_u4S4-0LoL<1P-R(L_* z#exb26YZ3*yV&j^qHrn2YjE2*ZJ|6vO|uXeJQ%EBh=t;^QWuLdYtWC<;hc&CXzSN>3O=WziDmLalPfs#ohR3Rz zwQW+RYutWQ@7ngb+uj}b5BqclGn_nE9UV|{Y<)9c80$|-pxMFn4inWdhz@$iXyy;xqHBk~NZfLrE%W;6&>uUMKI z)9Hrqw9M^!>y^%5qd;uM;rWF-$D-hCwQ16yM067yR|fQoR%ZD3SU{1?p&2)YfCC+Re+;*q3- z1 zkEH%FSb|)^LHGFDR-MK6k>P&dd35z-cZVjio3M&CgWJgz^gjm1n4<}((5p?D=C1pw zE%th1=II%W=j-tW6(1_ma^8VdC;EN{)%^Qr-ji$cNDQWY40X zFqI+`vT*G0!^UQPUxf!u$H_jr+`$#g2NtFwL;odj-p7QZoMT#X8ABm)^=6yRUEjD9 zA2TU#W|LfF%FIEaMfj0=gRly#x(-asxckdzRr1OT*on5W@5lSx@pZUiXK~3Y;SGag z^YGX0m;c$_d|s4t%KQl-W&R&1&X^v`7SX6}}-xz~9`f0w(b{9rnTFC&~$MQ~;AJ0TU~9J8{L z_$6LLBts(9{xbGkSZRgYv8eo#+bVxm9xZv{+n2dn*tbf&{5QMQiqR6)k^SV^m_qOo zmKfUo{J~m}`E;B#(M#oY$&&egu3^gV<(4o4&oA#5uYO;@hCwSaMq(15c&~7*%fq}| zA2)A5uB{{ZgnewuhYM@DNf+Gdesygv<;5C}SYQcz z!q7!BK}j+9Qt12QgghQ+425Lna!LsdA)AEribg4?mqF_&$b~O*JMf|0a}4$%%&>r% zNCt`HP!-FJOpMgSttHp-X!K=SmK}+d6<>LkYk~uEVtz56uGCMMv-iY2?LE6fAd22C zMexZgJ~4@8zZZxRFnmFB8&B#2%Z^)NZScub{c0RRRB zYpvI>+O2J;nEJta$B~I`eALhigBE(*ejN+wp_X|2uxCYBvE@`#L?iSrwKDgl6a@4& z1itdIa9F&kinJbr->#d;(~mLoxbbS^wc2|*Hn0C!;}UJ>-l~EBfo82Ry1BNDY9Nua zXHBr_=6wWF)--a@EA`=^H5Cr62vMx6*yWTRm+5^Wac6jtzW0M? zyP1z?0rA0&Qn=uTbIA#W8z$cLrR>TQ~qaV=@gSw zh!oON=1)?}6A-dI2bJYnCGkHEs{Cg}$;C3&&3R}#bb_4C`=tM}ELi_M#hHF~!kQ7#gqtauwhQvY_P2P9_(~xL?e>k8^A+;z)NUN=YBl#FajkPOCr88h3E@*UKRtv(=amiYJLx3|*Ez zK?NTNs1_cPCu~fVlb&1efC@X7)?_8XAg;_Iq$pvspra=PL;;Lj*-|MGi-nv=M4Aco~>_T$VE?MW=AA$X7*%UK8?IcuY6=AOth0G)MGw z>54yQQXFp>&^KpSx`C!IGSaw+`e3Jv>9g;NTY0`7O#oIld~ww zoXoZ7IPwb@{=E7&J)NJv&Gab~rf8z`aEljzZC&=+vIdvVv6WZ|VCJz`jRZ{jcTM8a!bl ziNmt>?bdsTxh~I97jl#aBE!BVR~V~@148h^R(4qo;6b$%pGYTc27!b^Ean}m9H=x# z>FmevT6G9(?NRceS50T?2b<+1(#~A#^6CPPGd+Xki4@&v^*B|VuKu~5g2QAG&#u(2 zZ^Qy)s})5oxUYj%kmy+Xm#j1ipNMW)M>{aG^-G_IToeGq=3+7l8qC;H)+~$jagIEM z0WhK_Is;Mp1vv$jA}vMS;`h@}i23K<26-wkajGM$r`YUUwikJWH|g)8nb=BwLJr6UpZB=L1goIrYM<_Z*djqbkW9oacK+z^{ig%i zV@G;$+oSY-m9_|_Ft1{;!ER!&B#!J#9+5m~^JKXBJlvqXOXKmj|0us1l_L9EuGcGP z(#cMCY$fuXB(vblC6(;hP5gjKN44&%fY|5T6*ND7DOrNPF9M1G^IJ}LoW4e0<=`b< z`x}j@L?zcQCRX5s+WTaCy4pt8;_q|`9%pWGxz7vqy(=4Db8KI`&3ee^0!yM1xL^zh zNc6p+(fBsQ#xB+;C3@sEJn3aC5r&p94$uD)3KCxAcP3K2kt`zq-w)b<{HtKIXj1mc z`OJG?i60I{-<4yTf(==bFaGw+{|viZm3=KN;cjxu{249f`K)|^%2`A4Gp)q%bp5wl zO0}x0iJbDZk1HotWb0bjI?AL9)Ri;wq^|2HK;~IB&h7Bpt3J1 zWdbmh@yFd1a*D$r)^=3>HKC5{Pzhdm^gO5Pol?Rg6-KYKW48{&JQvM^=9HK)kp8Qdh}8pUIg{ zEYUR_kdsdPNiq~lx4e&w;1Ma8mtj2fOuT@HtmR=;J9ZJQbMSbs8tRIK7kBJ*P+Ax-5$#Bt z*mRgj&D~wc7h^L z4V;9)m^?h%VT82X*2BIXk2I0jrnKzzsay^=puE`3QN0ym-3EqTyVnD1uReiTKD$o|(n@-e1V6*=Fy7gX#0i6Ug1OSd%HvIM zgDOag+|^WFl$`M`$aI<=*8SD-FoEUXGbHqY9cx8jjvkWXUdlb-M2d}so&3nR)F=id z>Ah}C1z#)(X3T>Tc3ywSRW%bA2i#_JF7Kx$iJ!tO{r5l-f(o+fMkCrw!6B>I?KgVB zLUlR#v^?EV*pB?BlCfgdF}THZ_TFr^V;R8b0GMs|o~5snOhKp+5gCU>qt}B6NuVNbY1?5K+iuvLhw1m5oHHrql*um2WR1S;BX;Z8 zSw2@XWJ8rOe4t_KG7|Wgy7*dIlpxv+g zqxT^D_f_0r$OIda>3?*s1bKss;Y|Vw+zH*|hz-gk>Sa%{l)~r;0xBcM88dq%$WdLG zEM9x&^?IM0tAeN=irv$6mI)dQ%8me4v47Nee-m8rVS3l%2^JRBw7m-dWvgvY6F@Mb6_evWq z?(r403@YyuyqQjweiR8*Fv2)i;ub@fm5-?KYn|xirgQsN7k8FfAfKq0xohdrxQwAj zZ-Qn(XuitjzZ%)}$DizV5>bj0VMmT^oeH&N?hulG!bUjriWEhEsqs4L}8Q$~vzu>R20WVB|z6tk+#7oupF$rm7>mAE1J57UgHW_)BZ_ zkfd7n9TQXvT;WzG8jYh|&UuC;!+tp)>uy|QhJ_I(s)g#NYh*-3NwG&(S~MV!w*z2f z3-4Cq6C9?fq#Ik@Z`=YE#fIv4wq4d)7#D?wUk<}vm0blEKg7K714!Ll7t!k6Nly|(NRW9p;iNFMUF2Li;9v*&%kWy zXS?@E%q`biT6*4qD^U~{BagIxAqwJocSb34O|XQKgZ9^Tp%0e$N=hlmPi{EEc*DRw za~k>*PrUf>qJ$TTFtj<@na9s8MCO-xl0FvebLrNoxQNavj?W>Igl$ZZZ!w}y+W-I% z07*naRCQ#QkRkRUzVD4;o`+G7@QsufFFqFI=}GMcXiZY-e1CcAXJ{&u{ATdH-TuiR z1v4kiu6X|BPYP3|Hw^#$s`BD|n`rcTmk8(K&u%;>8VNYC7`^ypQG&{%_&8rYMJZAF z^%STqpYwg44XR9_r6?wzE~rf5q%LmMfi57DI&_t@hT?yMlln|-$?mVSP5Oi=tsmpK zZ5&ie(PNT^pJY^~*_FpTD&@(=3FX@T578j(SL752l%4qT)m6a~dB7z1fCZ(*bp6?< za^n_8T09VvKBi~+e?Ptd*RLNr=J-N6jXYN}l~!iH({fp0jCX=@sqnXD*v879uCwVC zCyksKDZVKeN!}Lq#0A@&?LT_0@GNh+yv??;$U~H8nENR|fx4&IA@35};Vb)gEP}VaZNwK?uE~$)0R#8NaSE<{Tgqv&#OML z1fltX$0ExqL(wU`ofi%$UJ8p4+Jk~3o-beIq4beYI>VOY(o3{Tl|#J2DH1kcbw_=6 zqc}L#54&VCWHOA5&2`ULrHpM?%HkIj~n;OtU*x+ zrjna2$2LPa%remP6Y-L$V9I$FAPPNT98Tfjqk{@cmbr6+>rt_Xh$W%sNQyu-W%NY5 z1%4J-i>IQ31x5~=@Ll=*0JSnmZB;!m3I||!gc&}Sp?{LkWygfN>qTtVmiChuPh-e++SvYf?85B7-O$z zsMG!FPU{S47fWSJ@D#Lob$Eo9#KNPB_>HS8D~(h(oPg9WWVKQ2G>F8#{j9)xUyzh}US??_JUVLe@8JN)I0bq6MCx3RB6GHH7av-F88geAAU(T{h- zc#nwuDQ`q>Z%HZU`#L;q>FlzVj|bZ%MF7T5?%4Wu7qr8$3J}P;whU1eWqC+EK;v0c z)FNxo6rf^*EvA`pIX(f#RCU3bkw3vg{9W${>#)@N70qM4_EngQDcA0MjoIHZT+aLH zG^6UG91B`(5W92mO5 zmmF`iEVD1xAt}5_Z^1GnF7Aa%J832lgO5&j*qQ_<pj7othU5O|HmBB!zo`A}JnoE)OtDX|e zf3kiRRi^mo(=UTej3unY%5@AEE^>tZ*J38+9Ag3!uCf@S<@i~IPhJ!d zQf5)2)c8eSPQ1O6b_vUt69 zP$|Cf<=g9@-l-{=DSV!gtA~}>zry3V-OH60q)<2#&tv|}yT#j!L>=w>%f@%#yWg+d z_lTe*O8K}LKF$#@nOP1Vf%pv%OPH8E!b65G_jff3MXoRme=*y9xLT)=myRe`3w#z> zGOO%JH|)-Noj%SST|Un0i|b9gXReFdA;HPV+2-PUeL3IE3{*PaZ=eFsA*)=k((Pte zwds-R-GX=`5ylXuv13t>0V-aw>!I*)N!hV?BNhWHqtHc`EuqUQGAF((PYFd=@GRGA zW7|Y!!OjV039G!Am|(^fil^a^xH=!oArxvRa#eNa??Pd*98>FpQtUL~kP632fkmdQ zBczfegCbB-EH2`d0xhKzmOx-R#Aod`z8nX1nEqs}l3qh|;6H1GXM6}UYdHGhPwT?? zgJo1EW@lEkav^`ho?>whAw)XKkS$)DG2g$5s$#9n#~04?d;Hp=Un<+sEW8Gng|=`s z4Jw#Y?7lDFt#9VOxv`8lv+>?Pk|&IA7d>FO9jSwbL=`!9URNg#k=mA@1513oRoI(R>7CMcET}=oq3j>LYqDr)TS=}%|rF4j;kG7(iUaI9% zZ1>%~e~Afe$|H6pbZHSZN4XhYVTLPwOm3|hv>?g?+ZeMbJlbg}B|%8+KeFAcRsnJC zC_<@ZL*CFpB#{Fg(yqlSV{QPWHsTf7b^<760xBV!U>8zW3J#}qwjkN1%lhG_^D5yU zN{DU`r>;M>rokQ}c3*p6wtbpX4lv0AqbGs-ds60V34kQjIv7qwU^fi@JmWAW%nIyd< zii66gR8(}&rW{&1ulbs$Q&2jLqds4IY?_Ij(wgb34_rzAHwv) zt*iVpOw6(8jYY*t8pP%cg$kZ^xeAz3!v57qH5ALAmK%Yy@5}!gEdp9W$@`Vuj4xY> z<9o~-@NV}ihT);2!1VPoKQ$k%2>QSDA5J`%Tf#Vsf|6nc=m{zoh(~vD!WAPHkI4Ui zjZFNkO_XAAa%Bg-UdX}QaRHpTFv(^8{<8XTole+%gwm2|1SiBl+w_rNMLC6ruHcqn z72Ig>g&U#^@(=d5dIA!j!>jrEs>kKK`+{%)er(9k-YFNNbWExUT`a^h@_t^sN4(p| z?U|fhFYs|@8vV7%|!bTV6m=M{s z^A}2jS;8t*e)$T@R+h8qEOzlE3ddrAg%S;;G6WS05%DDywa5n&Gb7eM%qc z_zKu)hxEVnc0xt91Pk{DVCLyc{}Z16clx2Qh#^~3iGNU10lS5W$})ur1r+wAMy_ch3mRFA%!Zr(7nika=Fl?}`fQv(=c zsS1P8s+}tfVOPnRE#G)ORO^7zdzik9H|!%KFQ}jg32$$^`q~u}_T5mJarb zZCFg~i5(B?@3{o;xs*Y^ICVXHJ8%e73J5ik`Qg(1Qo0lEWZZt?KuXUT$y^O!1C#DD zfUUD;!uul}A3#z@{<0!1a7W^=&veu@`pv^~>M(ntA&%@c^hJz=Fo!mkmq4

=pD2^sJhxC40u(FOScEcj9dS_^BCV#!nwJe#*otvo`KLd8F&+ zp;K26c3eSG1ymIAsKAuhvF<*k6kKr3WYV4_s`S4gv-BI0U(6~7rL-HMGVpc5Utbqb zjG&S%stmK4UMnh>3`*&~GNM7nU=;)vu%>{Pg6Uq`lrsF}=B;Oacb*N5yg$Hv^8WDs z4~Fl5$m`yRidcq#Ri53v_4L{hh$LmeO@-E8l^e(J*D;KOf!|A)W+ z>remr?|%NfzyH~nKm74O{>|V2@~?jQi@*HqpMCcSKmD`6_}l;VXMg$0fBx<_KKZ{M zeefqEk3Lqfe0AW_<-WT&h8{fo#vlLjfBrwe`@O&X(T{&jd0&727yl&?uUO~|=Mbp; z2Tds%#mUW&CI0jRo1&C)<0llAxcre?G;UQ;-d7-kzr|-YgBr@I)v+~F-pZXG_=|bk zH?u!PH_hWQ8MS3_sF5ntkyUV@i=-FkVmzlfeXXVzp8b31hF4Ao zSIs693+4Z|#;e-Im){aId@s%9dkOQ*RSU@%+?gm@WFb?k7?|ww8z4=FAJ;w>#Vtkw z3+(S<_$pRwFiZe8LCLYWu8lvk1}ZASq+ZCDQCbQyOq2nyQJ4Ri zK_#R;QRM_7EzC0vEzPW-R5pbvt(ZJ2_#if}Gz&}cmswuDxV~Ze#&ugZY~0nfYH!uD zJ>^UHB$n)ow(JPjzY}iS8e9BcqHSlz;_Wr98=DrdtEpdBUB9#fN5x`Q{{g5_S^zq5 zeEtwpioaSGwr<+|?zSDf6}s#_aOBY8<42Eooa{J#x~r$V=lppH83Wa&ZvdS7x9;4%bN9a5 zqu;;Js>{QNkMGmW7^p7yhi{FrQ~&Dq^B1q2?Y(fOySMXH_wkdb4)e^|K5*)90;m4U zH`gs$_GVMdvf75$%9^HlMSVC{?GIIY0%|`IF+#?QU6?RNs!J)Ni!P=t63Jy@N;0drCWawMpad|o`{V0q4~aIoqpgL=0WS=mEAj}M<|A~VBZKLB zN*uvOwtxZ^9hWSO6xMHQmFJQRUqmNXRQN|2xNLC*OC1;hz{a5j$AuwrNd-G)4Fi!{ zX&%f3Y3ONzP%n|iBruIe$ncMn!mNy!f*m<-(}hwn-j_jw=A&dvij@tlG>H%l*m-0t zN;6mZc#HzRB*Hj`ew7#nh?$iThgaKOh*B^$7!$pCnoEZQlYa&^5PBmUj)&1z!CwHp zHq$G|%oATkNA$8tbVj-{s#3;0n4bIM_Y&5k41gqGjY&K)Cc)TJZ(6K7NmhZW5e3^D zA>{jDC8>Fzl^| zu6n3dVNg*AxgIjB%+GSp&vvTYxf%93>7{ehEprywW~W=H&0H{M?0B@Uyz;~(;J|rJnJ7}31t{5<<^6bZa?Iuj&D6s z#Dc6c0s~-I!BT&(_u8GV%eT}ei7uo*=1q0Y>T~s6x!ZgF;n^$qyDr}9JU`rdUY$04 z>ilqf&)~_k{q!k79a8J!t*(p1XRi!j8+`ca{cnHgPyY13e*dq2^rIguO8NBDPfwri zvIims&PaA?+1C_17R#Vlv7x18mH4v?7EPW4lu}S+SLEOgg1=NzN?D|i8{#9ievbWR zvjiLHVKwP2uMoD&bE60-1GRii&qckZOi)KVUSQ$8mLI3N@IemmqFxl5zY6GP$(V$C zMRjqC^qJ^#6+A(wcv0Mz-{c;FsA7bWfMKRrt41dm8%hhi@T*05G3hW74Jx(Te=l!Z z?PsN>N*|l$8kr)imUC)?W&o2BxR5c`Mqg2l{(vM_aOxUcP)OxK(>!_2Wj9Y=b8IZp zoA?$%7sIj=)$$8ZPtgf9J=GF5u%}7J!(^Ncg&NXfSXAK=vS#?HT!Rs69IXion`uXi z(Y3-fqU|xY(!r866&BR_$l_KCOWrXV*^#X1YiUgWn0hdbg2haqh{2{=jP*KVyPk?K z5nURXUVx(-wc%bn@(D{I#jCnCPTmquNp7vEX=`kLWA)0lD_5*7Z`qt!x;wFSPkhO4 zclqi$sd>|9&6}N)lV^#9nzkjD?yXq1ud?mkin>*Gb!|2EOUkhb(gZ54WTU6P7ey)Y z%Ei%mtYbjue(kWb~`dL?*Cot}@e)V7)ZYL@B(u zZzPXomib0Z7B+rJV~KV(V2wKHEm?pr)|`blQeD(uU+wsdZ9a=5VE2UG{&s z`UV7DARn2+ksm#N3hgcrq1|N!+FcK$(E9)Y5CBO;K~(My-MZ7qA!FB}mGW$F=jpEY zlc$azJE^w#yZ7wh@$T-;Teq*@xcRNOH?Dkh?a~#iTif2KYg$@Wr~Vk!-^E&mF75#A zp8#F#lrZK}A1bfVjtRe|qPi3r9(_p7D$v>s@(H##Qtohmsd{cfu@i94=Civ)-m-)O zjGFqk#YAj#n>fq3=`|9&Nrl$Fam_B@?tGoB;vG!ei4s3X5 z`-)X>H?}NKR5z7Hs}*9Y?^zLJc7eJux+)%B`x-=%ZW%zS1$hgy3oUlPFIb7etTf`K z3pb1bCvnDyNt&?A&K?aaTm~YYAsA`pi&-gYml(!t%!5&wFHr}Z$hC(AH}}-9LcVsK z>4M*0+e9v4l>=ju)ye8l2lmzqNbm|7DyW%}JRWqZm{H4K8ZW0I!?n4i$<_|kSWp8I zPke~=6DWBM3Z=j?PIg5#2%*O&ssV=QwQzt65XH%SLX&BB?4O~}SR!(Y4jaH3Gq_-Y zIz>$|NET&u5%4qgC5%?Xst_hU`s{WwGuYTs6LAQoAZ(bHM?N@wS8y7tF&b17x=<4B z^)Z{N@erW}H8qLpD{xh`Ha(78gTHfedIULUEN<+q@OU_K4z>>_Si0Lbqa)WJGagP`Z~1 zRMPVt3QI^o;fXJWD+O)^DyccP^gQAgXKIce2EY`M5>;sQD|z}0OCDKKk$7S@)1L=QAp<>WDNle>ZV{zC zAH4g4qLf?rKO{uC^XMCQ9({A<@i)~itcTyY1H=N8VEwnBT_1XM5kiVb&R)KK>ipoz z-o6g}n8&-WA3Jm9$f+yTae1<506sojbOzdc2jFYz?(g7)0QJ*8_0bfl^jx`feelu! zryqav$A9|W@BQFMKl;hfe)jX9{N%r_e0y!NJCtK7%SzHu{N^dI3=_9xm-zAv7fzc# zefo@9rFOrxI#zJ2qf|PA-y>9Q2rOiv2hT|ZEWwqa&x?{U;nVz-AJYgo3YMMYN_JEav46RjCDEsR?hGcBD{JGN2_9OT6i2EUnaYZwx-D zw(L?xLK9z9kfMVP>ZtrN!d#)_p_^qYjQU9$$iG34=%g&Yvu9fize3}G04loOrBNgu z_K<`GJh)#4C6oZ87jp<)Dw|bwka4Y!Hm1n6fHt=nB`um*>dYDu&Oxz#MORFUWkr_6 zHyhQNx{6*$t7#ugIWEK@%p-0jtGzx);)yA5trd;(Z44@Cjt4oVarx$TTURZ9!*27I zRj!J+?NY1~YTQ;}Po!p-&PmH(P~a-7-;`LkuYB3QXzP2nK)p8*tE_1$uOaNAa!I{F zT8V+o1@4zJf5>igfKK9Yeoez8F0-ft!i!TzzYtXH0xC9Hj3`JcHdg?aJVKz72do0! zg}G)>nMZf^vH3yC=M{RP*eDzr%S;{^%Pj`srGT9J_5yI~dx{}sOkHTi<&S%V<$-Wz zC{_(IWYvu|^{tI9%N8$Px%`c{-h6A_+Vz_^ZQj0Z`_A`v?cKZo(4oV}j-5E!(Rrq; z8|*F@Fe3RzU;n@m*j?_BQy+>cpNLccvD)iXKJqX+^>6m|Uj?WBm7a4K)Qfa$P?%DVb6;rNXZVDO0VoG-EE8G&hi?68Ev#{8iUu4fKw1MNLzycA; z`4$RFMr?z`;XLs9I27lU*!*fgq+UUN9qib9m( z+avex;jGsa#bU#^Zr`}kcMeVS>Py(N?cFu&HaEAesH|-cMyp&tn)Pz#sCUmRrg612 zL6npiR1__wsJp2kq*%Ryg!-4AJ5b?}<_$gOB*Q{D(?u9yWaG*&^|9HI;&zL!X{v#&kgjE!F92qlXhCCw8iEMF^pkA2`D;ISxW-41WpoJXgCbWo3$Zx{5n~ym} z01+u8%I)&(zt^Cme;U?&@-@I9XR-$!&)5BrHkZ*}?;y7iHb1hQf*&xMQb_Se#Evr?=xQ>-)Q6pf!W>*ZI* z(ma?V7R4vyr_3HdbuQ+BO;rDhS-0buB8|hHmk*t~3Tee%0xE8G?b_)5FrTg76{YbUxP5A7s;T{nBL-ygpB z;r(ad`qp>;_dowH-~aJXe){RpfAPKVe?L@Foo@q5$$DYXE5n@kg$c#kMV18_3novQ znw7KA?N9iERcK0qTQq!EBb+EUs0EJ-3nQpzpyI)96I4+!KDar=l_L$@;~uF62Ss@c z7RlH12pIx~w$aOCX$8}VAriNY8xqCT0g!+SiDV_qs@2&_f`h3zZGC zNnK|aqq+iP%L9X{M)=7Wz;| zJaJOnV}R%D8gdY69{n3G5Ao*m9swIpH9pn_J zVigmpu!mmz=}Rou5=tnx+7+mvlb%2&WKfFBpb^_BP;nUo%S!?$c>*E%2A^>GdeWkw z&NZ6Xa}5PXOD;Sx1~X&S!^qkKatqq@S#>EWc9Gqs#O}8^%bZ>)g7JqcLea`td2I!u zOY@S&OI9pjvHH!`Yu{eCann27w(Z!tbN4<%mlN%sou|9I&i0KZe(*q%%!7OP5M6HF9vB$9akKC0)$128UF|)0v8%hc6VT=4k)y}K zslV^=b`W0PTfcGpTWhhH66`JzGFD#Q6sxEYMXSN7?^7EfwKWM@9D!o1SFJ@B^@-H2 zx5NgY%j!~87WS4Uf|06tMSWFmb5rZ`CCgT=Ub6uZW#7T0$J-S(UA%Oqe*km69zS{d z?D?ZdkMG{SH!wJK`N~ywVMmUhcyHI9^&2;@Sh=RLWof*!LA|xj6)duNKuF1lAZ8F! zxKTbyNJ-IF8Cv*AW=@gasld3(8>&IqwLvL%+KtI3RS#}a!H6EavKzkhkX6!krJ7wo z1;?@6Q9U`PJK8A@i<5cXG%gk5=0tJBif`Dn|`l=5UC2HBjU&DKP-V3=EutMqFVD z7)B#-^oXIf&^HDzKx;AhuD3J-%(HkX+tbjlVF zaS5@QX0hm5qK!m(Yz)~+Cbd{OF|6l$c8gw+Uf=VgMOL~r? z6dNr7m3#-e=*QI7Wv zz&Z=JCRRyG9430frS1-x2f)lWm3A2b!$OsQQ%G_0>NN#_ogdU56;f0>K~Wht6crx& zA}xg$fy&(;wYWs(9vW28LqGWV>d=#Gl<{>N)4gco>;6Z#9*~Oi@%^XYQp`Z>fx3MP z>)z9kN03$2$^CbpUmJRK39`SSpB_9aXKx%kbN#UT>P}rfs@Cai>h6(K*N$NN^Pvv) zxH`B7C#btnwRx3;)F4!?@7(nV*N2}C-F<%V@rMteeDL8nKlz)#{bB#`kkuCkIi)lp z#q+w-QCH5ZV}E9mBPA_;;>1Z9@a0E1ses))APVA27$pc1A8`LoogNL)?FiyYBNnPq znlRc(!0^z7wh9``s>49AdH@*okRYP)3V`dK3@VL!x(vVuHrCAmITG4S`h$;6{IwdN zL@MFZvcpxfR@pc2K9U$~xH_0c?`3P=+svpZQ(DEkYF9jjPEk#c%}RDy#R2IpvnOyjY8+Q#V zqhAMd(h_1>iPdZTknT$w_aY61Ws^ub1r;=E|A}cvOF*TfRr_8f#fv;1(CjW+4P^v3 z0@dJ{8CAIO6c%jsoAoG~wyCI%KE0!O^kM5CWNQjbB05+sNZ&w$jrD=3q=7{#t(f|D z;HNo;=z7XnyR7t~080DAG8!;(foii#Oj6qcA`zHpUe zTO}9W5ZD+}40-fv-Yj0(TvgN7)cnS>wm0i*Tim|rB8z*G#aC$YS={B0fcnc&uTF-E zCs!yIuS--mCdi;t$xKBpE{H2&kBQ=#ncr9DayV@^yAyBCrWA`|Q-KFdO4YKkpWfv| zCp~yn>}F6gQ;N$>DF`Yqe*C4mX-PC9$2rN51D4E9#YK<6aW*b^FgdZ;ymF1e-vv@B z$W0wEGqwO5PGC3+8V^fz7g{N+x5(n(?z^T7?%Ql5BTOlHO z_4;Ki-)dd5s;PBFUE^YPVT!EEs~XjxcN~{GuB>UUhE%krEp2Zsdt;4y%GwQEwrmH` zI(+1~dgY$p^H;818yFl`i1O&slc!IgJ$v^4!-tP<-x-k_$1?{H9aYG*X5Bk&%b>wW=jymR;KV_q#Te3PI$6 zLrP&x{ta18C5*6L#tpa8d4ecHc5vD{({KH$GKIF?8doTZn7k-r4k@F_E49Ce%{`sW zqgJDx?W_g%tS%tu}&X0jfX|detLR4q?AA@AhZ!uOe|r6 zYYQ-0Im$}xWfrEF5bG68G9dzk5_>?MVP{leZVmqAQI-;c$t zsEAZz<#)v4H1V{Uu}7nq&eFAsi^I;~lQDVmJ)CQdj{^w79~{k$@s-Oz0{T1mMC9)$kD{@s#Viwf<->9}OM>78(I{T( zj1*b|c}1SA0!M~Ikz6YwN}56xpcFd}df5e6>@@U+paM2}SymtxtWTlQuheX7N|psl z#;y)hvaM-3c38O%ffI-6*r~WhkqMgVoy02Sp{HzOMJZ`{o)qw?s7HJ_`sGxVl9J<_ zpXHgC>6(}6o|oyFo9UU8;hwX=Gdtb$db;biRM)HXEHi*rN~X^)o<65|`s|`f)6>R` z8IQP(`6p&L8RGw;38NB-enNo;S z?mba_^6dv|J^!Nz&%gZ;mpV|yqF6S%j!TD5Uf$n+VgHHq>h{3Niw91qV|8$8fBVHlomY-^-#m2c+V+EI*K9r7 z{KnP|+mF1pY4_4M)^6Q-^u2v2zwxb4THjbz?1^TVa-U;HQlBGHia$NC`1QGSCrq3; zZru3P^elHE0lyJLQ;H{8jzeEMa<^vG@{`Ymr@R`=aJY+P7O_mgkJw>?6Jj{0)S$Tq zS1tPT>uH3nEW%M;vbiVqhC{c=l5Px5fud3k!)ZZgDv~C6@m&KLq=B)9MU`L$pMWZg zw5e2~v7Sc1)PwK{tf$~2^|eO6d0u#o=giF3l$Y3_-j;PaUJ(R zvPp*Dny61tm(e3q8QGvaE=AU8lH}q>(AxHH#DKZe>$tWn)!kLrrzF+NQ?J>k}0X z2bOoc5tVloH7bjF!)Q_R6tQtpi&B; z;&A&}QSq7-6_>#($%w+BqK{uPkqIj#be9B{xgwZoP%+L=)eDIwEoT(H(6Ld2Xhizz zt`bsR3bf+|g_k1YmMk*86j*R(%vo5XSkPN)R~H>rpVkwI2f`KMcnxY4E%oTsU#dvp zt@UrO-@Ngi9gHpq4j(#l{Md<(j#FJ{x_f)iUA%DV3ZYA1{~)l-?GZ%?=yrMh=<$3t1-TKXMy}jY>wePH1yK(LM&FeP2qd02wRti1acks|L#XOy-ySlo26ojbD7#hBH z_uhT=zR#Y&|NaLbs#|p#>Z;H;e_37j{)0!h?b!X++Kr2su2P>=ePp*UVzK)Ri=BCe zHqufua*7nFP`X!=mLiBqsl;g+1$hM)uP+t|SG&t9O%3ik&`D1LMPrI_&9$R(SCFqX0v*dOyt^9A`SOZVJ+T$S{S;b>>I&60Q$(> z+=5$V+H{(_HAvQgy*0@fI1(0QjM3QuvWd6q1D94#$hs5cF9BdIf(jLDBN{?zc1~uH z*j7YfMu-JI4eDE@(szG?j7dr5hNPCDz+#mT$cl=eW}gh&F$q(|BvA$xCxilPv4S)T zObQM{JCm1s<0T?ZJkX^^-*7?E__+|tG&ADE7nd8z`Dq1;ln3Wkp6b&j) zpPnug!;CAWKc-w2%R8E#@Pv^5>!EQC6V)E7}fl3F>eVrTXpc$}pGy{gB3XXntUqVrZaYf51_i$-Yd3Z^; zrVypvdCms<2Q>5r6JPh9eoIlx!{>kW=>0!>^uZ@;J^lcW)q@IP)CqSUel&FVeVqP! zc>elG_vPWv-u~lV*N&XJjG%JiK!>7{3kTX^DKgoA@}heD(5WkjP65Nfd|l_|Bd4xy z+jrU*ZBETDoj!B^?D^S+cJ;4l*c{sf^*Kcziz^VWtX}906QX38i|32|3kuySImI(y zdwtTBDdWdam^~-O>5an7QmEPoJ*Uui%1T9uD^q;oTCJt{8B}TnJdiyUs9jd&rt zGz6oK!B`Uoa0N&?0U*KKa2-yO)dsW$C0xr&h>kxN)j|-(@P3HG!&`r`dB8$QsH9^t zsT(bdk~>V|37gkx5)7#W!NzQn7giyF(LBS&_K_O#)=NW+&=I^lV+M&cxZp>X2B@&l zr&g*(YE7BN+E`yLF~_xLIN^{0X_6^3M20#>0ww|6#G}<7!`a{%U?b6l9u>oJPGrJC zy4?Agl=r3WD3NjVJiV1@=$xk^)%vy}}Sz1BCPNesh0CdL)hIh7oD#wMIf zQillGcyde^Wf}#Vl^j~Y@xe$b>UGdtkE^ORQN6gLc4<}p@|wn#wM{Fl82(p|C7mb7ilvwm+g*wr3yWQXE+LmM>ZNuUM3>5%rrL(ZO)bmXmabZ{a?R>B8`f>u z{LYr`+js2RwP(ND`X4@e;zWDrDFB9ED5kt}?fQ*=1tbGQw{G3Ob7ut5<-vo8NG~G1 zJb0*x;x8&XS&aH^_=ZF zcOD=~y;0wQdYjvK6d$RVefsqIb3~L6KKStY`yV{SbT6n$xp2AjR2OL}Yd36NzH)7S z)6!T)y)T%syFurq68Rr zY)-Vv$rQt7O}eD0W*fv6l6}JT6l1`Qnp?zBj@T2$!j^=#m>bp+7j}}Mc|*8Mir!74 zgf5P>O2RUui<7dpV$x!66pX+(#@|(#z4#(}a7dbIt8LWg&5N#yj z%gR$;cD?4AsH~!uNgAt$IwH9F8LU`N{Bt;x?e>>2yh5u zh6pK9Qw3zyDO|*pWBla@85h;DGsJ;lk_l##gG_`e*C}IQVJ)+mWhMQ&i0jmsR%{Oz z6nnElHi5A&v7JzO7yD1r*@%Lg392Y$N+DJu35DQvUwKVr@X>j}MJlpiwi>AG{P_3!2Eu1nl zM}f+u>1mUurB0kWZ^G2M6DH3dH{o+sEN1`!5CBO;K~&W-V<)`)3KPq?@e@43iVZuC z9_zXRbkRGgDCGzjLG~Rxi^E`MN|8s(H{tK<8F_Y7;w-N)a&_vB-t zluw>~@SP_geDdT&b@0jK_dg+4dGPe(kw+g5-+#aF_LHjv4=&!k+k5p6);6tv?!0oa^U{Hnm-Za%eeY2Brd=J2SMNy6 zDj7Rrx?1BWzdmXD{BaXs9Y1N-nQ>RZ? zd@^?IxY=`4toDH0U+xK1zyd_6Bv3Jk0s%wui3llmGWtcn6wb^gbp^O32J>3jX9CTP zA@Z((s8SEy0_aj-#=dz9IHoZ$CL$UhGsXDwE?>8ukTJvPKZzLTbtD1lUJUD?Elvvd z8)H;;^2A!xc$jpaMA$CkH=?~*X$i2IzS^gSj#w?b>J2N4DJ6{U_|@=W9DOfQHt`3d z5)7at0ix`Ip=&lfRXBuzEIpE#OiBeBcB%&KQxv;&3^eSaf`_ zdOO|g5|N%38lxK3x)|0IYZXmVo#n*_8@-RG?n`WDjPuwPU7@M+MIRejqInEhrHb0( zTL@Q>E?Ti41}KQYgJ^+9mA|oiWcDt8Mk9?3S}0QO5)Y63P>tIKUuDz zC4r^&u&}otgNa+IwZ_o3=ylW^)hzah%565g%i(l6AeGpLZI1#fu$WSC>CrC+6;GL2 zQE`~e^rIucMx}nak~}IxAO@&_ab>R2W;hB+5JLF;9K*Yk1Sw4+$8v6 z!}kjbVN6}nJFa>(P4`h$s3v?^9nS3&5~ zx~y&4suimMUDj{hym>3MQtn1{IdZhUz4OfJuHLh~=h5zR_3E`7pt=l0waW-4j6uj4 z*z{phO7i^&BX{rN*w~0VQ=L69IIN!3KQKsZU|>)^I5a#A_ydlahmRhi2Svfj`yYG& zeDV?8e)y5P`{e0!MMy(K!&k3fSMYPZy>svWL+T>mUcY6@^0(@nmq*L%{Gm#_Csbnd zEhI$+JoJ=5wn*}b^HcTL#Zpzl!XkSpR1u8SfRG};@3&L=3G6}3oqBHhq~ zcKyqH)KuMQOQp7O>$g zm$YK0F985!u@A?noY&K0#w3*nAxO>gGp-nU!6pTXC%a7aVwXWU&UwRSgGn=Dm57OI zQ20Ws5sy&FVwwQt(LM|wAjG98>VjfV_CiG+wgtJ?G#Ti^g`gsELgEQ6Eunx^l8xu* zTKN`0#hQ{;nv%tE0(Z03OF1$M6oeqdu)1OaS3NY-dnn^e9b_)_DqKmIaW4fnFb@V6 z6rhxpY#XL~LBLl!nNpxc#gz&(V8AB`Dlqp2PI}N%JgK>U#VQI^=4E+6Q2|i#;VPT6 zzz3j`3am17zGKGRl4-MxroCP?^|gX2vvMcDnmK8D`a}Sg`2Z?YX2Y8N+PH}`#*Uw& zh=pX9S6&%2{nghio8El)VAoOTe*{tG$eF&wT?$&jbAPP6|G0rDu<#sMsJ)QN3BAwBjyqLUs#1?7~15tdVK zp`1bn`g>2m_3-((6_Y49dHUgZjD@<2`q6y=l@EvSJ@3E$_}ZWXm3wEe-Z_17xa0g_ zdr#lduIu|dF6}zfyW>FDrd=oB-ge}T^?O@aZmDTmUD2@I7pYbMs?0BTre_z;oSia$ z;?yx?C(?QaZYPZyH)-sIsgtI?mYQAYEQ{F661mn8_~wyM=4YvA&7VAV+V}|*6h^)_ zdtQmf;|Y{|6_x}l)h+Rf2bV91runc|A?mb|T}<%YZ352t7$thu!x)(Eb4*}EJawKV zT4)Rmw#e~ju9k;(d9FGM$k-T}PxK_1Ut+t2Qbb+R3IiHB#4A#3Xbw90*XS@Yx69MV zk*C1>RvAK#DJ2YRRWR=hMHR4XW4{Uvh8bi6y({Eh;YW10JvU3T-a=zw0z$PsjfTX+ z{q!bZzV4f6OH7nWh8!A3FfqsV)|n=(KzY!HbtVC<(Zt4&X0ejA8)gd@Sgnsh1K=9t_hW z2A@D)mqfk!Sy2-{>FyM^nsDA1g{L(bY0gqSFC%a~;w2T01`C->SGbRb-~>U79WPkY z0s~zTIiQfE4Ho4Q6R2<%V{26M#9|x>wz#}zNqOy(c+C>Jo2W)I!B>kTt3beU6^DJr zsx+uz9x<^{O+>9jkUQmp2ZN2^Iuu^OOKVo_%%gNiwxcvNwU;jPzB zdb3Aml>bCLDteU5fDrMkXthK}!sHlEn~%MR28X2SbMlNavlmqtC`m-I1zE-Dbpd4n zOI^&|LMgymg_>K?tk39@i&`I zzqfnm?gRS{9zA@t9i94JU1!gM>T>DI_3JmmsXwI9Wn|iWfzM4c~GsXj}IjXWy$9AcH+MH!TpY|cl{D$LAXWOoLG z;TrYd3>TJQ&@X+-ltR1#?iBRX3xu%O+-}T7(X3I_!rR=anHg+4xfsh#O3;XcQN?P< zUsmD_z)+SesLkJ2^w5i6C5jVea7+)vs2IO*P&!W}ikFle7GwZR=zLORu#hBxlV)sB zFJ*&Ah!T^a4uPxKBM9g9yueLB7+JkE#&(LMWsyQiACYToH3fSvP)y5 z$!HE6bTb99oWh`@y)J&@6LmmI4BEJ2ksYwM!V}NX{Fs(6MBFfp-5Mdp2Ez%<+Rd+> z{TAMrGi&KTVmevtIrN>-kelIDkup$Y2#qdV)Cuzyl9Ip)W25H2JTV>;b1^DdDYU@F z<7C;u8&q&Q$uZ$=qKbX0NJE=GJ|l?c7BdbX6?xus%;$lBNF5;6^oDEa-LI8s*iXErIsF~g^6JI>~m6~lQ zR-v9pSm>VD?i8N;LQsLcFDQKUq%x=|XbGg`_~vD~=Vq$qoeMS<-`uRg>{4$ne%Np09HDEPN^c6sjuZvnVCI##sbt;Qb1RkGH1f%*T+wqHEtqQtc)E$8E|FH zxR+lUqi|*1_=yE2o;Tk)u($mRnpL2EColj8PL;b)zIp4x2LmHdF8AN- z=()ai_lf$pH4a}aGp|_vSM~LI88c?hpFC~Wgvm3;PncV zsMQfcB~n)=9u-8R8VNzJV^(3hsChBo*K1N9^az$utSrWO7@2B#JdCU(HDby)0=Q@v z%PYPS?K43!r-q#>kt)B~>$xV<2rt&2mTF`AOP@TtGQ|ih($6(U8W8eqeMjyT=8z+NHo?JA_5PxJFn0UlDZ7ON&E82s!qYj z5mRthnPHKy6Ysko@oLeCLa#}eA!B2dF+hK*>XB8NqdIpB6%v^o)5%?Qg{10eWl7*9 z!KN4Oej(2c_oBp&+c0;ekkzDtohtIB>Cb2`U_nr6iOCWriY)u|qgDzbh?xexjG-?; zk;ORK)k;CbFm7F6V}fJ&*==1^pU zpb{F@qhd39RLpP^kYO+x{UT0y!6>gmC5QbdNyplMLIk3Z4N%dqIqLBwKMX;O8Hz}0 z`P$Jk0hq7xx|nmuWP=Xql5N;sxSJB3`c5ia6m*IDf{9?HGMcC@uWBT8XZ{r5jqNOS+eqg%H}6c1gvbmdHU&#@C7d-omO{@$MT8@H`my}otH z8+DCK5>@J7&^4Zb+Gd4I?S4gK1&bVcNGvo7rf7{m^`cS_{5KBBTm;}g?a`xpj!u%bIIT-k#i;v*_=kW*9hAwh z=YurRr9JxG<)WU8YK*v3f)Y>69V`_2ML}nFPfW)m8+&~%gP6=Pr4m}8r|F1N(3=z{ zMM=wpd!k&SRgvYSI@e?Osx97p70LWbAH6a{9079s(w9J&uj?ut00K}F;g9U8{` zLvvqI-JL=w+eJkYiwh_uHu3WqA#6ADOq2is5CBO;K~$Pa;8P=+(8qBFCF;P?Pw1bE z5=W@W8Yn37W*0g$@-e3?Tj4}02qt+F(xngukC{yHt}uy()Dl!q08vs|EwQ3JVqrL8 zRw-2nKq;9eY5~uX@Irh-P8Asaa-h;;Bg+aZGbqB)-8?5bRnl{v>A9{n>`qB%M~Vwf zDePAPUrJgIxa!eZ?_p4Z)Gr(W%k`#YyHm0~H2npj0(oCfaMEXZ=D_He@3nOAYiXX> z(!7dQW~R7i%(KpzXP+@w5sPKo>|(_#lV8o6q*!Ifg2^+|5m)9;n4(x^CbG)3accLB zT`RAQ8LLtrR#SbKhSZNTr0pN3SAVabPpWq>OVrP0(V(g(E7zB2UW+O4NI zZa?pb5_(Aby8Y;zcOHK{f?5ht$}^BtG^jlL=zn|u4Yj`W3;tc)*6?;sWf@`uYygPA<984QOY5Mp{ z)5cGpK6(0U3P|RqEhs21wmNLN3kzR;eJ<)J6UU98Fm3v)DXAGYXPK|8GN5Rrtl9^w zO5ODlsDx|T*A8~}hCrm=&$t5I;zz{;xBE3T55_)tw5E{8!j+2@G>kdL)WAqDEA@q( zK8&~Q!bhZO5EX@59c#->Sz)8{e2w`oHqciKhg6c=L^z>Zr~siQQX@uo1{IIVe=q1# z>A|=&n$A)$1$WW1LiphgS7~BlUy2DuG?p+j>2s1wA=#Zmr7L7CA*ID{5Kgr@Jq)9h zP4UM`BVpXNf+@W^J&d;Cjc8M$(XS?nimnr@J$pX{S_~C}q>`YiJ5n;QILlbmiykTy zyGx^FBAfgnf)fSj-6=Hp zg^S~flR%}EKm}OEYIh>2n0i#)$)jJM(W76ZeD*IT!%5z#Fk+KhVm8(%jd;Cqe)g#3 z;w1RtC}dpnT!NFiIukiPsj!9bN?dZ%ImsS>nm%T>maae<ScKw#kTXyVt zZ_n<%2Phf&OjpnO3zx25yWT%AIDG5QJs_6H&(IhDK5Hf)>ESK{7(B+iH1UOU3Ny@y zA3c5c?EZs?3V!K?cxmI0LUGuVvnwDsJ zT_9ZH2_zie2y|20{FIDrbp*>oRq9{X>c0m#dK9O8Sm?X(mpk-u7ymjuE}#;lp7Laq zv^nD|MAJ~Zpp+QJo7BQO9)gI$WO!_a`1wyA_DHOuDZh-9J{bxAe^9<`8Fg1BH~3aSVKo`DKl zPH|Cre8~8^w5vpZglPm4MIef!$B7tjegPYsSyM{hqs#1^5ey(d3d=kj&$($B&_h<*h@!s)J*DR0 z_WGE4giM#H7zL3gP?7MisP0j5XvIbfD3k0ZCZCBBX23Wq5pbewQ8Q_Y1DZOEtp0_i z-hyIx-Xd2{p(B&0xk^*BOH#8+;5J(wmlAuBcp_?vF}{W8Ae`g^ex&307V-vg3o9qt zr9d+Ib~;CU=;<{Tz>uPnn!}S|=@dg;;KE1j)S80nUVNMm`Ctz0d2~p>iaP_wz})H7 zKo6!AxLr`-%fN}R1^M0u;7jr9$glK#zk-ugwyF4OfufS*Pss_)&koGX^vpv=Wln}q zfy$hWve$uC`~WJTs5oAo?*LGlXP-W|bULugqA9b0RVKfh2@BMf)QQvPO@tY+S>q?Y zsy5uX%mRxL%gX>QBFc0f4<(sJFes0_Viqf89AfTXaL=f-0^hI z5jlfNfuu-@6ctEO!IqVC&WWN#B}$Z-6l~x7=B=6e>!!X`RR^S>T&vf?>C+LiT|ew^ z?+u~G6cL8R0?aNR$FV0*fi-O+_QF#b@5aNMh=D$BOqWZ+&{@?a$Qu z>7CDhQk3$pO(_?D`2egb*FL&*4d=eDD@ytF@{P~Fz4gWSzg_&{_ph$}@y*Zw`u5j< z{eW`Hl^g$~SmmmC=w;~Z|5beQ&;JF8^4q`u_thJJy!Xka?IXuW{Nr6c{q0>n9bLWc zUA=9cJuU4$2im&#w|4J?)w{2)Pu=cStEFepf$lvAy7nCC+_S#}SNooQZF}~&!2vGC zHhWt-)l!_((%#+C-P_UK)85&+ci+Cr(W$f%A*3|VLJBbht1f7i$3ZL# z>7wmEL5rvyTw_ATERxo>W?5FWW8RqQcQn)Mlk77=Up`=rT*D2L6`k~@=ex-BL#baY zx*&yL9Z8JhiS?55#|`<;arBED>sR%-ltm*PpH<;5kxe}k#-?_vVkBog&1<35P}Rkb z^INc!R@X?;%8rpOe64k>2$D_ergTyi8#*Ylv;u=$l$2F90?w91BvS{aZfjW+hM}Y~ zG2%BjYVwe(%)HTMBHC>7To!p?I38eIw>M&!3kHaBSEC*VGwKT6R%N~m-yX^=3!5_T zMawkM>xry}C4C^ASmpFG)>S7^QP(U%**paeqvymlIZDhIEv6c@ub>5g8RV2gsTo5R zC7Vno9hOsUP|?A}wxXg~1y>Y|Ef%OanUZ_rtEBDqw}DED3m-LL^Z{VlP{GMv<~(UB zcDk1~hrj>h z`Bz?h^X+%u|M-*7E`Iej2>=^G8N{pL?U|MJ~;KVJI!@@Joa`TmEWy!rOKFTMQgKc0K>$*2Eu?)(!EVMOx1CmuX} z_X%isSwB3#v`NL3<=SdR{SS0`DGkLJ;5;EpNugf?>kf^nAfAyTzbyL=WItM?5gqN7 zk6I%xVnwKOvKB=QONy5?s}P(>Yf6Tvxp;tzeHRtYJa*@!nWv&I5en_q0l2auCJBYD zEYefS$3!(~h$j<^Hg&L8kf{(OfgA)I=pukhzQJXY8VNv2sL5BPdnD;iR_r)nOP#1n zTAB+^Sbj;$W;am|Ck$y&rY#o;;;dal#_g=puM(v;^S&Qcmx>K43{H~t#hsFAtq)Z6 zz>-*HOr49NtV~sp*y)d;ohMr4vDBW5_>{^9sO)$H6t5y86JZA;c8sJs7wa|)N?C^P zbdZ{qMRF)Gkp{If)^BGOQxnN5O3kq;h@M;)eVJiLrtIVloWvEezOHbxG!x5BhSNcX zC6fwEqKqZTB&eK3DXmLrB+6AMf+&n70VWgDM$9pcW)XY9l^%cCGZund02N1pid>A+ zih4ORrQnZ5DQ4D-62Rd3<6cPvQ&=)GnbC!hc#vi@{Y09UZcrf=1^FZcS_&7ZWN7qD zK?@k`!A74UMMWWsH>l7h52;^ZqvzDG{?TloFAc0RQt0&+dWIFNHmu(>sy1Htoq77n7vERh^2+;PzY2ASScZsZl~;8#*oVw2P}cs@cSJ7$ zFu*N8ydicJ4J`mF$S1srQa)Gf=XVfP-u?3GdocP1pz{7#zXGd#ef=X@AAfV>({FEn z{{3%X{`kkGpZ@&j=YL+l`p@qm>wo_5oB#TMz&ii$|L@vw|LdC{ zuf6=n2Z!#ye{v?$H!#%GKiKc_4h#+V4G#4Vc)I!qJ9-A%y82o=dJnYs>~HJY*V?nU zrCTlF6NPy|dY!xJ+q0IY6k!!bFD<+G?g!aq*KW1$+O>Q4?!Dc;1Hp-~ld7ik&1`X@ zP+3v?{*rEvuWyoIf|AH8+227$scqoxI=7l2nQ+V(oLSFT;XDGyCc?&Mr9pp|#@3WM z3L~a#)=5z@HI6m>>pnVFh*GQz)^NkUXzO>@7hnyhL4*`5wz3^@JFe zKeDG~$)1w3%2<{PRKi$gkBW8uigB*fP#HV&={dzrPXS^zQ@WDFU@NPGWvO61D#n-3 z@n0BCro|kX`w}0EvE{Jyp0tQH{AI1_R?X_1+HG&)cfP`)QkT)MCYw@-Ov*@!Xu00z z5*7G+KXjhpU=_|pp~8S$aBi}%qGgz{xZ_>t^8x|m; zz=94X1qo36GROY=su<`iMk=8`lQrtBEHyZ2n8pYNkhW_&e+*k(D0Z2{02ahNV|h!& zHxJSW6~9zmprWV3){S~IzfIl}<7t7XI^>?nLQq*Yes@t(Q2LtvW*vLI_AG z2Wv}sKn0EqT;N!fFk*U6eV)Y@1eN;2Mk0}p#o`K4Qi-IIQ)KSTMJeK>*Q^qSiLrDd z1HSQu29+JG;66i{J|vbtkDn>UGp3es3;9*xURbw962oKF^{(+S ziZezEvd6=`_GlS{jVmA7MLRp)`GYL8$ay{v$S4}IihHSdLRra7HRy+-g)zQhIv<04 zWSJI|WaLn4HkMO6n`E|@Ej7#arRL(s%KD*$hwr`j_^C5zAG`3x-=2H^rPp46>w^zJ z`Qpp3zWw(5pRWFL|O>@GKM{-%iK z>Mz%R`0>i+%imr6>Kg?uAAa=dyYGMa#+&cF^zv)ZKKIhoe|z@vC;tA}<4>JF`}nbw z4==7B0n>b8j=So}98aC!g?T7WQ9p`bf;P>KrPq)E01yC4L_t*KS3yrgnJfwnD(J7! zf24E4mtBJ8P#tiUjU~E*>8n22A%z)7) z!G(!2Q&A#54FxJ7j5MO6u8_2%P4+SAOEHMTLuFK?2yZ@|$g34imx!k5Ey1>xs6gP# zT4paEF#?Mi+%d~53Ed|HCuZJxvmnots7himdmYZ|mE$LA@~fyg8H3rh2cf}Tpas}xC!q{Ebgyg{}f z{#{q7jFSE}paUMmv(7}5g$Njy=&|K^dWV`}-XwhxQ!K-_5FDjTmgAiEYdq_soYYxw zkz?T z(_f&m2&h0T7{m}mHt~=@j*gT#90SZ?FC+$nc`^7)a3zVZ`UF7>C@KgrP^dzR3Kl+k zK}aEk3Q!7GsbmLWsl)yMMJzuwi-+Bq3Y z9=ZSA6VJc*!rK=WuDtr8!j;Rf5siHE1BH6M&P!N@Q;ff32aBHjg7u>i%a7Rkc!d`d z%e$ZdOw(WQeR=hLYz_a{@o!)J@cYFpe=1P9{PUmR{_;<#qQCyH zpKkp7%JqMJ|H~g2f4KSDyIi z_YHRT^tW~Owssgf1t{eJQA+o2SY5I@f%i~Rfhn)O3QG>`+OuyL4s^j%aB|l!5Kmg$ zeZIidjM`~bvxWIW1+#_?rK}TLl)=lsi9oW3!NHq2rbX(9+5~SPrQ9j$OZ`|Ays2_? ze2KY&D^sZOu~5}hUUb14`Bi{bSW%ICNj!lT$PFltY+@e^-7EGLwz=~9e@Bmd=#<%3lT%k`PdW&?M zKq;sCN>@u#V6P{;$UuTaXe$H^lo~AY!B~Sh#|)s$SlAH4>Bbx4_gK>s2hM{LlTfHJ z+u2P!3KyQNQo|#p6u_&qiG_jzbTF}4N?1`rS!UgBpl7cL&1IFO;Zcu@Y2Xoi$%goC zT;_P4DQs&g+Lpum6170`gc!3NQ3}Ep8Co{c>8_TZ4WryJ@~&v!*fQb53obIDlwkuT zMp2=YCN~VBYl}c7n=QrTPCVu)P;u}8L1l+KMV3)i_%3y&lgv0SP$|3J^QE};V?14S zSE-;!ML(9|4JnS!GA_V;^7Ow}h8~ky&hIMw?Y^AB(HYMdrjbgjh3R zgE~6QTw@Z(G@Q75UHt!HsZTei7)BEd=q+M&QP46r5eZJmCou|H?Pij>dZD^hn_FF6 z-QGNS_kAZGc<7PyPd)wYi!ZfBz40 zh`N)__u#qz^VV;F+`9Gqt>1pX_4^;{nC$p}{7D{tNCQ*nZ{GY(J?$^p?()MASHAi7 zyRR;N^XX?_eEjJb?|t~m>u}JUt0Kt)G#q7o8cE*B!uB^&ppAPZ$;F5f^sbO6L?V)^?C^%Y za-O`}w5c^#8F8D0kyU7FOLUB~_(RGx4;F!!E=Co5;3%4K2OG1};SIQbBNIVGxwB*BFR8OFStYS3r#<%!D(nv-j}1bCEPBuIyDNkfOn~$|HQo|7S|F(KN6@8{ z22|q2d#NHJC`Trwg;K=-gAb(lpdLt|0*?Y{Vc;>@S@IrO=x}pXnJL9ea7JjUI#OPl z$Qb3svD_?_*`<3P$&NxFgiuQVerZ}SgxOrv(fmX>8<M8Hb3jWIAk14uEgG$=t zPkNx4K7*i=MNn}mDvDJGF!-xafeIuPC%cAVb$Sp~VEQZG#-K7Sii+O_Ds6+nDy{vB zRZv>#5?w`17rXc4+IQFPz3ddfYxiI8+T|JyGynXT`e)a!J>9*Y*;u~0de8B*&pi6K z*PnXv{byeL^trb#zWB~1h&=w_@~iKQ&mOJz_Il%E%_={>34HR?Tc7>(HmslD{`}{6 zzPPFu41j&fGhiQJCq2Xy!vNT?q^o>*>DTwa{Q2F_e|+=f%P+n8>4j%rJ$&y2PP*Xn zjwmGAyKlc*2U=R&Iy$<#yLy4`KkLBe#$R*2~LT(UUSSkG~I1I*C7D5sGdcM-MpwI<$hV%`#%QL{t8^b*bBZMo|5y|HJ zrJQCJbr;1PH&9*C?e&(?pPOE4T+%`#VcMg@u5=vtDs!5sOtS%{9}Yg6VJ| zLsh}FBgnKWMe^&+3vyS$C zF{nt80AtLi$pv26uFP+io6=a%p<(iV(Cb21n$8pHW2tHI5JZ7xhQ3%%A-P6aWs6>d zoiGZMnrrbG%z8n?W84gVS&E9gmOd5}O5sJIf(H6>lAGymk4j0_4l{khE?4=BWt4Z9 zIl9KZEDMSa7*?FI?NqVO>kbw^Kj@oqSx`tek(guGc`jWU*KlH@!0zZ>84kL}!(8f3 z;7+VE#ucWs1#GZFz2aYvbU(_Z&NQ z=HbVmRG{+8>+ihx;b&i5y!`F=KVSV76_r21BL6R1{|#RGKNXPtcH_p)YuB#-{PWcx zul)So_dkAzl`PoG^37LYUsmhu%in)}`MYnw`$66N!;e2v<;pL=-uU&}4FxJ3j(qEP z2uFr$m+R`~KK%IeBlkRzFE6HYMmZrn3LeZ8rKA8<=5|y%>Z%ZH#EXq0t^_a%=QWc~ z93)lJb%bUFNc0Dp?(MFLGO~@tyfGAHtfXV@4ixvf20qSSP?I< z#v)dAo-&e*FU>8ohc3k}FFVy=imzSdj-;(f*sB;Z^Gg+5OpBTh9Gw$g)%KpK2~#dd z*{tH4gNjLgXEeRz7$@s86_(M6UL^dD@Fv0v5+V?vGzq@~S_YzYouVxhSw)IGA(oUB zFS4VU$`swS=qi$@**37hhn&((yf_)j1w&c?OnP(*gb~n2%yI}DhLKp9D}o%x#SDY8 ziRdR7H;mvRb6@0ck5XC}DKJAc9yX5SUQ9dc`h=IB3SXL02i`y&fh3}iA$pkKNt}Ht z>Io-P!xQjWFTBqL!wHttV~+}GCbZ~Y-((u%iNT|yj$tGWWR`#mPUG?L+p+FWEh$=Y!6)X$|i$mjOPoOjyC=HAi`~Bs<(L(P?0h}se zrcZQw6t1K?JShN`z7VGsca0zD3?QqFf~CHFNLZzxbQNY5_ZZmjy)EJtXJ~! z9T@EQ3=IzZJiZYHCqpBngeZgF;eL;z6i-jTr>l3My_>X@u6->Xd-u2D>=w|7{_OPe z^|}kz?p?bS2eo(isArv;31wqWC6TJ9GjrMe0<6NKT4;`6%$HUQXhbQ}m=+mAU}v^f zMOG0?*=9whzR5xg8dOMKkwmXe83SXnB`;xLtUxl0DWvDruY#^;u}8La5|=4L zS=F9(nHjTZz?NJoTQcQkrFCH^JtP#b72H*ly6TZ+7D_J04C19?bFI|G4wkxp!X<0m zOTvhESXNBm%L*CssYj)Z-HyEJZ>&l4!eDPA&#wJu(i zHL<4X&J&%o#VN*RZI>tW2~h#}+2iPx)i+PHLo-xZ;HW;!8bm+i7fYfF*U6J7o`=)i zy^YdyqTi3%_mfYCWQsZM(qWWh5}PwR32G)=Wz^GxeJN!+Zw-do;)zN9T4G~8jee179s-URrHx*_1O<*QZY9%sMH&Pu zETrJlpb~L`3XOreLck!KIGMFT<&HKLm&QVfB4P`f>2WLHw*yqX7La)5{7!|0x>js} zkyd-|RI%fU2~;L<{U=b_ne?SVg{QoRg9C2Iynd2F7qM6jBQnb!L4`-!08!*t?t%$1 zbh8YNOn|6@V_<>F*u+d?HkOO0%enGGZEk&e?Z}~{$L@dN;m6KD`OI@Kz46w&AAS7U zrLVvJ@ybuvZ`}O-_dm!_uWo<;{m+}XV1i3)UOlM*01yC4L_t&$#rHp4x%BnrFD_pC z_><4x`{1Lu-+_Y2*Is|~mDk>U`IXmUy{eAidiAw8Uwi%SH{N`QDk(quo5lQ+og^$^)1ud1hZaNp6xNDk=UHdhc<07q1Af_Q+tTp77 zmr@jKK1$CHMOb<&0*bJlQW8;xuSb~$KD7l%W$o+GY@n+HC0V%216Q2$D}e_#Q<>Z_ zdGk7z82v?1@tMb@0RmCU8zB5+kC0_jf?th53(*UIKRnlG{|KNW{hMVGTUdBuGBMe{ z(%`Op5-lf;d*V)NMy(asii*7?LoC527JXMEA^$}PCn__s;y92=YHTVAa54!%V#A3} z4Q98Akb_R;B0a=p3I}7l{KT66g2h8$IQ^wSg_#F67Q;UrL(BvMLBkq^fK?KtuBeMg zCX+s75*qO$6@~DF(M4#4Km`r@Num@ef<#e;I#no@*atDk2_`*| zS)4+fN)9b6*z*`j5BU|nIi-j(VOo(1?pBDSScK-8Uj550vQ|v#`(%#n5)!pam8yFt+j(Ud220Xs* zzQOj+p0@Tbb+5W+7ZxD?^{?v5|4Y|a($mm$sb_*Y4eW_UzfWPoc~Hme!Wm zw$}CzMJ%v7JL#acy}hNa{Xk2bx<(<*9&nQE!wfE%+5%zZPCx={*PcClTiV+d_>7JD zLz9!qP`DIz>P~VYkzPn<)LKks7t*=KOkVNHQm(k1FDp1fJ|Tm9eG3SrCeHPSh6-W^ zpp**fDs|LVfK>=uw(tT#7fj-U_%9$6nCV)_y)sul5(VKCGL~p(Je?`=Jkkb^Cv8#V ziZ(ychL{S=_22awbuikPPV%4It2B~Cb7d=qs;+W3ZSK-u&*hI~UoqV;;#t?7Dw5pA zT#+>%ciqOw#`i*1-~nY5g%;?@XY)PEDO?1pZ)w)6P{-1h1{>>hXsp*Y_F}$YDpD&; zO~8pkMMIIUXEDKFn!W()8mwQ5I2ew3sWJT^r5KBNvQaT* zkuobK<@DO`&TN1sj53ynb6i+JDdZ_uewyHF>eQ@tHnP*ySpz_&Sd)Gj&JS3nyGqu; zn4pd{+0R@46ZV{JalA5rMtLXLIYT3JG6|+9$Aoss!y~c6b?IcJw_4JMencjCbxR?U z7u~FJW4!?qQCBvRWKd%vZjn&0Id z&!}4ZK>rA1U|zqtRv1+L*vc{;Nko=g!5huHD{`UK?*Kb_;>FVWgzyI>fuRi|x(|6weKyk=(FTVEV)Bkw< ziN8Ja*o8A^&!2kmkyEE1QOtAysk6sVoxT5ovnL;X_<_@po_^@u!;d|oK(EzrhYSfEKQEK8w`*IEKa6_QY>y-`f? z5Isz@L*z?|@%UB^H*ky=5I(Ex1?4Z?>fvb6uj>wS+Q(CAeLPj=a*s@1T1WJs_Bnbu z6bD-c?uo1VIUyPt?}B^HI1}z}k!r{yFERo=mFZ(ag9;pzNFs7dlrJdB-}_;#h{wPr z_BTdCfr+sac}I_yQA96|sNvX^S*&{$2q!ej(12FhDMk{uT0&7#@I2b3O>aA#Fai$2 zi9An%4KqnoDHu<4lCQm?kKsx99?*H>P9mm}JhI9Zv`}NB(qzG9=&O;m=!(Vzane(i znZ!@0By}1fq=>7eX#_w8Kv6+siSrfGOet0@nAS-&N9kx|qc6GLEEOGP27Y=skR+uz zGLdca&|{uJU7`D2EZqnvYcuikM5GXy$&5`VM<)|%LCZT5h(3Vh$rLW27orGG7M@55 zmPB2nTnsHTzU7Z<@SxEzDryuUiJ}Wd5laXVXo5$)L?Mak0Ag8D^vxrgpaq4kDKOcE zjuZ|fj#9Uy2gkthIoVr3%r+IUqY$Wow|+u_M;e_iamgu$0kGkTG_=$Qs9Xg?z=i?< zD(V)3zsA*FDI5SxdHe~FU%`x`mm*Dnc>C5+K&$p44M_`&P;e*{YuKkSkPECK(h^XfjnbuImmb<#Y^?p???id%r4 z+Pgcu`g(hZ20dfLzKK8}JUNw|p3TpOE8$op>MX>Q%T8)FkzPw?H&VHcRCXnmSxRRY z)48Q=VI@~w&6QU3<+Va(qfp%_RX31Kpi!|pcd&|6UezXp2Rat;7(qpFWfO=6u0&#~ zqeX7c|3cLnLu37mdUepCsaBN)eqT>N2S8z!mElct#OUB9$kDij02! zCs2{FFFpE|);%h0N*S}fC;{VFf%67bv@eAo;!XzJ2{!$Af|)y&OZrJ~=@TH4_+yv( z-W^FiE(MXnLEpG3k<^gGU}JeytS|uN6IN9Em{nL*K}rdDe3)g7ZHfMg$T(3-vXm*# zRhw(e>qidXbMpQNA3pc^Q_nvC(i?BR^U=qje)aX`AFup$4fDSK{PUl;ZvFo2uh)P0 z@yeyI!A9~9L=3g?V3ojiEt*y`3%#XNJ%Tg@c}*&Ddl8w(rF zh4s0mt%cQtD;r1Fw(j0OeBY6KPTYI!ffFaso__ek!;e03_L0Yr-~Ujlx}^4J=ta@7 zR1Nu~dzC8H$w@EyU80I$*cX5b8O;$G$Yes}UIY(R{3jJ674!@Us2~nG+;Ui??K_%V z>XKbsW_>1AWk!+_FR&(~_ki}nXa|RlxK6mD&2hvjkiSLmVol$OHkt#p=q?crE#jLO zV8HiWu|<-oAsjtSRfr0EaOANrd`xRdD@0`4%>^n7x{PV0Xr9N|R7yHVnfv2;A`Z#H z%wIRh2D2qY=5VCE?mee z$DaaL4#;!84u31Gl9G8Wb*6L6Bym{!n8CB6xgLHL1?=!KvH@FQLn2AlCX zQiomGCGKJWl6eoSZ5MDzmM<)2%&|E#2daRa(0I2r9$-)oLHw-|pE@tkNc|($YhI@x2GS zaEV8JUpuZgT&)(j5VY*4WpT^iJFYucB9fiMKC_FL>#@7{?Ax>Vz}|f=3Le@!y1RS& z1_nIdp<$nIbVRL@F<$hePaXT!t-9AYJTmO{c?O3DVR_Y~`uhj_$(u6d8}g0}`^H8` z$Nm1vKyW%3oEe`8O-x3ork$DD)ND8x3Kw8S%AsgA6l;d#3lV22nple^H{I9RTOP6CuNcc?ml zu!gHTf2cBdkSIkADx`sIp@C$REGr6fC=g7sN4~PkUX=QJp}vlTRjYz7E96~K^pY(v zVL{3|bm%xk>0f=pQR7uAi6VHf8f3;-%^lPNL z6m)fC)u<>rVHI5S;vr$TJr7(}IJQ`Z$F#a)DqPqrk1iMSx-5y?e$kK*VMc{b_Ewe3 z3PpawlYnt$(X>PI!xy=#r97Xd?Bl9MJ~b`5&`21|JNieqb}+QqtfCl0n^@N98!p#L zMcI-~{6@uSEL&BPT=ZBJa0n)eRm2w07$YE5F=qEQCL%GqX#b2621#j1|chIJHJ z*hVi-`chM?D-0?iG{J%#Fc}Hk($zHO=7wci;pb70l^5Eo2&hz%M9uARLr_IWm_Se}-h9umvzqH~JfV_*_nJj|slR9CNCQ|??4Y04-$ z1k7bnp}8;S6D_0!Ji|d0Ql>@%Gh^f7AX-yGv7D2szs)%TGM@%-KgDKXK}z zLq|`nZXBh?#%yUWnXAV!Itk3y@tk@gUhzC#YUDTo01yC4L_t)Sj;E{1Y)t`DzC2&5 zuRu!j^1;T!cD}rj$~97GN1@nUD#IoqAz;A0EN$9M7gC|%Qt5Ym&LNI`%h$V&GJRncJ1#R%}U81;I%)5 z^8mQYga6Vm+g#=cJ(VQGNZPCd$2@+f9pTzkQ{17GL?sR@Y&q=V42d~b(5(*Uq7g5# z&X+AuOGSqS@H`d0u4(PycwC*6tGnT)*s+cl3l2M$UuBhlzX=ncMf^a)1}vB)3}{6p zt=Oajq|E!&*Tfj`^advUN{CO}`dN`m`X$c{oO#&AA~m!+_1B?F^h2^h29U9^${irb6*wXj2)agyLBo0Gn*>nP}}9Z|M%S;PjWml>?xw0IT2{P^e#!VRy!Al>}$1F%N`@S?7w}H0^>Tl_W-fSUViuiT6^*2zWr_c4z%w- z(B9J8+1b_G*YELqeWRm(zdslVOpK3DO-xLWk57+JOixbEOis>DO@(G=BD1rxP&ghA zJK;z&5=}*8=~z6YS0t7WM^fspa5NQ(rlZkJES85AFT|W;JW+9ybtl<~C!4Y4d@Q*b zORmI{>#@{kEPW`JJ{-&39n0JkOWzaE+#Ao{o5-I?7Vb}%PG(B?XUq5JDklro`wP|M zrP_VP>d|8LaIto{R6kOx9W2$hVF9HatTYaSJTiBPeDexdYV(I{3x^f7fKdb%9F&<@ z5KdrZYpcYAUO*S>UX0!XL_zBax$W2K0)!xXKo14a8focHxym=~&k!FKCL+Wd|5#InsrHpRu+Nup0=9r7;7f4hQ zbG*?g3RXY8j{LIA95pkr7hj-gN9(*c$1|(of-x}kVDR*(vKE$fef#Ij5tdaZj<;Uo8oun}cl8{Dn7MB6ORiDBfRK%`l(SQc|O za_H)M-H`(6ZTJ!>MA)pNsS9>&8= z6yTQON)~<(3xqveI1h&U)%+%wIg(Do(_e@vT$w_|!fqC1l_r~24$iF{nO{AcNaZ4t zsA7@?4}Ixg6bn@3%$-3c1`mt@s1y=eF{orgS}~#udRI(lF%pXyREk<@`3ox~W~|Ft zP*_FTd0rG1yGKRN^W+$uybWM(=a4(}sOWx`9i5MIekfp{A9RC?%fZ5m3Xg&H-w{-_ zFGa?^xK<^ALduMTLJEv~Rhp|yYljaWz5nEck34$*$!DH@;nmmQ{_x{Zzq<6z4?q6& z%e5O2+x6S;*I>}=m#@FM{K=Nq7>}BI5iQpC%xFt6iC(#48fErP1FoMO^gcWV3*L)JyJ&#GV1CB|Dw=EaM*x`+! zA~8y^y-wP0Z`vq}|3q*|_MNRkdN5kGqsq#jke7FxA{-R-YtLr=fCJ@WDJa~ z8fiV(qzAoNXW)!l{Or1-L_55r#)4AXp@CT4_C?ut(=NPZ;|(Qi(WX420Y(%d-FhAs zhm1icww;tE%)kVWNqDr@B~Vb(!+S~HS4b}{nh#gpkeQ@Hi3o-wj8{j4iuR}oNiwJi zgou8EPlyH;yyaj60ZaM3bWBV4&v5F%Oe{Yg%}qse6QOKy7Eokt(is_#4F@7#e;8Lp z9S?(Zoy_YozSaJaK#Z0Tl7bUHLKI95R*W&qpyJq*U33g8ho1+!h*(G@5w!$Bg(wAV z@#_3&h$6WVauVXLCtz^w=wUDOjIp3-BZ?vu@WhYD-Pgy40btTJ{RNT7_;7fY2{EFm zH;)aEgQL*=i0Tb3#Te-k9Pz@}O`)-#;3S2{m4rY`R^2K%AzZ;3u#|5qH#`BOU&9lb zk?A6WN&;uV^1jJ}H<P`bV?9zI4xU zy2qO$P@(v*P83zz`$KKL(`~&|2r3h;-NBZwv6jw}1D%RhyayDhv;(E!>PJ>VTxsdq z*V4U@Sml7tDncv(E$vJ!``dVFblKYi;GqG=ECV)s_p7B)1ZaeSWPeMCVv_cbp04ix z{{A7acVu)lKuj_@!6-5{9fmapYf}9<6`Gz7t2HwnnVE?qr8sJZ`8E-bBqC8J7PSA@w8fp;^`yt%zg3f z@mT)BSnk1C{!BdoP@?p3vUE0Gc{p1=ldGP|*B&g^A1pRbmYesN8~2qP_mt{K%MG;@ zt{eg_rEwV7HjZ)~!lnK%3_)dHfyyC~Sq@bIT3~^hzP5>q3Xg|TRXn%FH#UplRH639 zO(G2@lg5VjMbM*4HJwl_l+vWs<8_^1EWIdeXjZ|y8akesM}trYfHzaBu@{9(?y=DY z!>@QF&N0VgWg$gH>?IQEOYQYU9eS(?pNF9(rwbpeqK#l|mrnQ6rSdt12Y?d}0b@^# zP8G93B_~xa7AcXBMHfCUa@UFsdXWf2rIv-pDoujr>ny^E>3@X>!0IbHBa1fiG9N`# zaM(%Gq;FCO_-^S0Fe-vX-q<8T!<~P%S=CZZlT)gw1f6D?;DL>G69vW(*)%g$IFH1| zBVs_;sbcIYqJ#j5QC`&-;QL{91+@|Mz0`CZGf#X;SIk<8f|cp>5-RAaE&deiuGV=H zG(RR1ruVBoP;-xUd9^=V{%BD`>cf zcw)y%TcBbgiYO{&x4mAQ=}Q)<*yeJbBP=1sthBgdi`~~t0$88E1}?b0IMUT{Z)_?% zM#8*1y)O27<6XHu&X_$dK|K;?DJuQ}uY2@M4D{5jGUyF}BL!!@p!sn;1VTy(gp@?K zR)m1B?af2Sj@^Ie?4uW+`rC6az48VMDPLax_WP?>6`R~rh;se<&7Xey`J2n%efs&A z>fRS$diANl{X-$jy~j>%Y~4M-v{|Yyrt!Ma*B0L>Vk<3EY4(Kt==#s%I`9t&!jMgr&xsN3j)=XF=(c@v_S7A*9 z_lUYxQ09ds4w!mWINM4mGI6vqdRih0vY-@b`%Zp|xH00FZPbD1cVs6mmFM&*3JBIl zm#F-uU*~QK67U$96&b8$mYR$<6$O3@)cYZ6x#BptcK_O95oMjEtjDYD1S;J9G5X*{%1O}fV<(X6(+YduyS~QGjieFrPATX1LuT`M; z5E7*VF$rHWg?1FYb-ykHL)5Nsx9+v#*L0bEh3CQ0nquPAG7`d;roe-WrH!ahQG_61 z=yz8fk_pbF{8I_!5U6a$BZh-?n)3Hyj7QEG~rDK0f=l<5t18rR`ZC$PH-K`zn z?VY{dy#oV--eKS9n14JNoE#sYo|u@KKr)#I0*OomoG_LElh9JVU8;=u6hEMd}Wc#A8RYRCWU%YL|x%A zFdz^(KQBti!VX&tH=@Y!7|(#gOR#9;j!5SI$2^*awd5WrTNE_Xr7PIV zQeQ6UhI`)VFBcJ1M7F|3@m~!I7q-nRIHpzAjrAO^pdn#HLWhkOLF`dqz@89}!`cua z*{bp`-^BG+*l}UR76U5yYod29i{vjhpXkvw?M@NL%c={;8YWv=FBqkb_0__KQ;EIBzeKRyNf&I2cAzU`Qzoz&3L$N8^cf1T_&wDe5*Fxf7^ZlyW<$ zL?yIQMm%O~sM6M<=DziWyV_y2EkjvOFsNJ3z(wQY;0A@Wz}mkYm6@&fA5L zwz0n74I~3Q`c-U$zBbvr`&cAu%mNjYN32zp0Cv>}*)$)RRfrOpj84oXW}+GN%{Qxa z>nodgAG!DBsnd@={^T>yKL0YMdwuma2r1Vu>%2 zndcun|Ky32XSNUDx3IEZt}i2^6clMF1R5KzU;n5801yC4L_t(1J^?tv+LrM_wcwou za0O{;c}K0_WGoWT$9Sxtw#ZdE;AtpI^pc^kW=u-u$%|t4ot*wuj`1@ftKeXm4Kvc? zQL<;fWRGs@z2hlLTcS$)3i)B8Fq2cD{9NP^fDnhHmY|Q%1`n-B#FF%8SVkZNI)N7Y zXG&Vcu*yJK#NfZjlQ%|s1NpnMn$*LRhs#r55+ZDt9qmOaRaz_(IfX4fJP>7|i{CTD z149L+0_e#l(F({eFXL6#u3cXvqiE!5DVvEbTacmG!n-u*$oQ59P8P3#Qm}xM@P@LI zDHu&rN*HTT^iW)s=!hsq=I)|q+^zzUoUO;yzgHZ&R%4b+=?6kqSR`Eyrz)XDc_v<* zisr{dnZQg6)^y50og9T`5>`bRKhUr-G{!%Ie`8etNK9gNz@vCVc6M(-ODL9*!nwde zCTwjdp@St3-N`6I3Zjb~+o{CbIachOZ1Os2K*yZn z?!{2Ap%Jfd%s1*E^#}cdU?4aV9G@JYn4XxN5gG|=8iB>f(b-v$P1Ks<6-PvILg575 zhDb$0P@&Xe_HhC8qvoKc0)iEiM3+ifAcwKq=XkRCX0S@%h7QC2~id+|hXU zSUh(;o;wxKpLP_elpafz&ns3*lpjx39#^1}sh-Q#&lT#A6zZo7wG*ZK{R&h{jbo&% zl+vu$%@=gvJXNpY^#u_q z5DAux3@WCEMfbGe!&i&kVZY4PjqIeCGDq^O@O;-2D<0xY!OIqP`6GMl1w6DuqMtc$ zHP#oHj*wwW^I+ny$nztCaJpC3Oo-)i=!@L>+Vo;X9zIyahI*=ggLm!BZevYeM zBMNVVDuqJcG@qmQe73tSoEQ`3>YQ{eOVnT z_OX4<&g=CEnUp_T&@!OA9YMI5@&#tc#>3Dgh9Z_rUtj+Clh59G^POj(d*R&UPo8@4 z;loFdFRvY{&8_B2a|$qKBAM~&_}D~P;gHV{l%nX!!+a7Pgtx?vlfWw8fCs~o6{skr znT_V8y|IQHM9LJdVxDZ>mQxTmjF5sES!7Vrt0snQMiiO4(Ydr0vZ=@%SCwzY7s2(B z>Xv=XkgdcyOIb)*N}wx-sEK_PNnjfYO)e~`$Hon)kVIpqs$|Tm!mpb)FeZQ9ujKCzdDF~}iBCJ=`^-BGWgjIxsEI^#=W#iL|W ziFxMA=o@fGp-2k*Rgxvk(1J9Angg3&5=Gr5&#x|`3gZVKxK)&RepanmekPWal~uR1 zP61XtKNE-J5H!7%Limu`cyWdXuB=5rD&BCdNNzHc8xLoKvuVX1V`@!7h}P&toFD|D zB812>J2)~kI5It;mQSrIytNkGJ2Qx*QzS&-nkBorwXp7REX*Z|YWam#xIfVx!(7%R z80B4_6VqzKBx7d7f~?65lXnHx6`Bo0Il>X^yeKf>Z%J}cn7|KGQyj+0rZSQnrjFAD zB&e3eSp(t8F6xmaupz%n$~T#iiyfUWOb6UXPZ7gNEFz?UDMh^~zLsMmi76-<9uBMm zpJ*z>1{Lt1q$%}lcp^1AogbOXQvV~~n`VJ!JgM#-6i^wS$Wj8BHwecbzk(Bo62L%C zVVg=8J0El4st*fMH7D70QuE2oN;12aNJE5IDt9=UJCevAalngm zGFdpCD4an@}uDpa8ZyI4@H(0#i?$;+NPIB}D$}j~YmcM!d+|4SVYi z%f~{}0rAYLWtU%PC1D;Fn0X{s)Ep?ZN_-%&G(Vs;v07?q-e5Juv{EvtU>tJ;BD3&z zL#-ml4NHEpJmH*WUm*%=P|BP{EzjwW$W2{iQkUEu%&W{=ftZmC_B4TY-qU+LV`OX#FRoxFC?}}npzbTXKYvn8}7l%;^EnuXP@%1;~{`Aw;Yd3EGhCT6D zum1Y&cRzgo#l`pD|LEmcUVHlQ&pq_;WA`0@U}Nj(+~P)|x{%0JLb2?`Y$7lj9Sw$j z{%P;nly_v35Jlb?Hn+$S8X5XgPMHc$#nqNQl~?=wIrg}-xx5wyw5XG9n)df`DFE+4 z5Wh%?vagz*Cp0G(&otuMW{e}bN;Z&05mzEf9>`(WiZxM2MfJoaz#dSd2R4`_{RsKR zRmoohbm4MI=x?p3klAQC`c(VJv9?!FNaye7zhe1eaS(+lBu7-7>qh$nCj`%>WjB*{#V=6iC|Cn%4YBEQtZC>m6N zOakh2r4_UcPo!aiYXuU(Fc1ubziJd~1#}u@r2S$?vqiMty zT!Erm{iC^FU#fRF1x^)^Qxe@n35@@W(drn8bo9@*1FKB5_5@qI$6C8aTRMF$onB!T z53WI8fGhpCtv*-?DzsYKVYPPjwsrKicMWv)c>4N>Jwv0zKL6-g&>xr#j!#ca&Q4B+ zXsKhh#wTU~PJ~aGUM7K3CZ}en(GWi)G%}<3WQIkRX*xfP3n^tb6rZJWFQk-NP*)tJ z6a*DTEPyDPa3mc8O3B4y#3!Vv6u_P0fO|fkY&gj|C$-?D7M=96lLmK6B6|oaMXh@h z`TLWF2NQ+U$-+bF^7%~VLc01yx_Ti~dn!}Akg1-_)*j2%AIpQHqCn-ra`SX${!DfL zRHb>WGIy-fybmn(pr{;$F)#~M%8f%MsC3*aH58C+73*8r-bkK!P*+OLgH-QW!v06{ z#8bm#zDiZ`7-5W5Q=L0hncLN|hNx4)CP6+g3JeJ{ z`k9e5R-oh&A*3L!^T-2hTDU;N`ckg6s4g?TkT4Jhk7eUNs5C1)l*JLlB(1pJ_9(NU zaCrQ>_(=CzQo+JhP^r(TGsUe z9Tz1c%u-s`1a;O8v^1$;l)fuOwK*f9Fx}DCU&M{FHt6F_m!uD~qEem{pAV@h03=%) zX3D&nSjt*yu?ioF8*2-jnBql?o$cDp5lzVOZ*7I?#9ND6k!%lC(hOHcBbE1s1!do_|3r^+ z*?tli*tk1P3<%l9kv8!3-{}~kV%$U(51S4a?qks-UH~mtzls?Z3vg7i{D?xz#L&nj z2`Qr_q)f-B!)dio1=Bosq#QhQ;v_UVJ_Q{qufO@>N1tB2ct zlq(m%`uf9`5~VPN(XTT~%FN#d) zAYE91>(vV-%ZM>e^x{j7FwTZS$z!g8smD617=(hDL=rQNh_zj8;y9K@t&2&hSP>Pa zNQuMHMxv-A;W`%J!PwkXNyfJdJEywn2tlm?K8lZLl0n4}v#Ie}UD zHO|YniR)qz8BCG}61q#Q$Gb6E+WL<#%Gs4lQ5lwQM=vrAu?Nd6JU5GQ?W zPfzj0se!R9jDC&g`bIK6ich@B?xA>>hgau7L@l6{fiQteJBTX5ww^#+w_mYJYnKmn z6@@FELoFR@d1MXZ8i3W_&#R-qwR51YbD*Ppu&dA8H#p+)`iDoxNBxujz*HcpkOS~T zamY9TNr;^8dMOB*m;^0_;3NP>lqq#aou865Jsp~1IfW6$rW9Uy0sH0=RB#du2EgO#pAmb|)4yPidsKm07SPoVcZlke6%&Ei@idE(mtEk&pVlI|kjHgx-nQbR?*vTA8 zWbaPok2|>&PWE^re=1daBwaq2DxFJL9?#aE%GRIA*3RW>k19&Z)z0Q>XY#cNij4=# zbEnJAQ>FTSic-n|Do0UNIVvGw=vSEoMWtBXEL1lN)%ARpdmFL*L~ZQW08X0QMRt{- zlu?0we?bESy538T%>t);ZD1JJM!vk9EvYU05$?vdB2 z#5{S$RrfmT7j4{J9LZB&L?bemhWGMNR!%Bb^q?9aSIO=ID@)cF3?R;t1w|TEveIWy z&jwp?R9cX2?BbMFp(9R9gAFm-RG2!%o=%IfgepXM9;`tVUl>CyUGG$BPaAsPl#_`_ zx4F|^AAZqmWeF56k=6l*lO>$vqVH{)+uv#4NeUmi*qCn&uB<*q2&xZ;fKX7PnYVK|v&9 zK?n_Zg>5+nDTP+p#V|1%1HF29A zZrr+g>vwglj?pxK@tt?ye?fA+?!NEj+UAk^{Ho$nCtX(0iL+jjk-+Tm*tA!2Rers8uhC?udl9fQ2`+)tuq7R5{SisBEkjqFAh;W9zV%QC$v+LR(4CJ9kd zT<-INkydnxyNs48I05H(G&dQ^OomC3P)w2~_XSj&Lwg`g5U}vr$BS$JsQW^j!iNH3 zmw}}r?7jd^7lEBg@IQ^!xffqn=^GlAr<-g&aU5 z03?$}Fab*8yx`gCDKMG9)YcUH;0aX5Nk~DYkszYE*vXH$URFcb1 zdJ8}$dpME1J6SlH$emCtnLm{-J(>bgIj=w^12%d^Dd+R`^ZAB4cnIRZY7Z3Z50n~e zoh;TAqTGv?`nwgSROaq2H^Hw`1W-9#Tezn(ccf6;%!A2fEmv91l~;09;8<4s@|7Zr zCsd6>zInB!&J^cyV@(PMoQD|V3X#b&P%As&vc23!b4@cXcB~-%588WWs%yZf)y^b7MV}n9{sHDnv~AUJOu!d2qRjJ zo`xb3gGhitqHKIA)NR5%pre7&ZNDx}^Kv_{Nk)6(H>5H}4Dkd)%}oh4UN1|PhT^uO~ zG(zU`|FH8Oq#jBl=HM?`TUeQL5JBZ2_SqlI6l%a2VJ)X9P>F<%s1l1Xu0-z$Dq%Ac zMxf#VsKiqQDoF??wvvbqs1)x2DygD*oc$yM5`;0P4&{y`UXnqKeiaipY`iG? zfjiQN4M^-rCDx{wot-Ln1HC>Nlv%GqG^UfrV$XmPq702pifJBZy%eI%Q2KDbR%t9R zu52ATdi>-AXU|`F>L1U&h&7I%UHbalA5loT_1o_^ZvJ-lmuugBkF#DMeDvyTZ$16@ zXCHp#{Cy`*ZEoK^x3p2H%&Ff&D3+O+b^KsQLB~8G%7kZFD24e1;6x5+?h7czH>Uo7 zEtAk)ekq$$jCq4oTwyEbD@0&M!d+clGar!4PqY0w$w`kEIqR}LyKvoOrgVM zvCszkL`ESBg9_SI_-P$i`yTOCRckB>6k*~fZLES7kkpA@lXgf+-%4?zAxB6_007wW zaRLqQN@-ft*~Ar-=S%6vDJeHJ!DU=^PX+K%DS*e5;x>sJcL&XG&BpT6k?ce$9hgpx zO~ywj)Mhm1qX{T!($F{|!%N{I%l7T@n=>B1M!Xh&S?YLH8!*iDlr>VNhpG57_HVG) zJYqXH_}NB7)^qanBdbLCPx$;+qXtp_te?74`N+89@$xA{qVSHb%0P(2LUB0NqCC7YCiu zsm$0+PREOpOI|b_vk+iJQK5Eu{1J*B;Zww^L`9*9*ill1H@*q#a!DYxq(-N6Gz>20*fc zIti!;$OB7M??}Q0b<*Qx)M^oCZ`Np%ie{w>yc4AuVjSgyW|gfn*LQ4KwI>o)EJl2e z_G)nD1#Vu6E?9n1R6vx%?*v5^en{P}=AhSI*CS`8ny!OfmmjGCMq1}B>0<{WaM*E$ zN3e)bN;R56S|cM$0U5^hwA9$U!hvHf(X3e1LH+bBr)UBJ1B@k`n1zZ8PkhyEN}<5C zRf!?qppQu&ZrprtP$XAHO6AM)eO5T|Y71>7uq2|2^1Il_Qf2G>7X5W!ds=isq>l8} z0tK)NrI0nC+NIc7fhF{!!`V2GSR%PB-w4jIY2}C9@c4NtYl}dU#R|)~PnpOlL>z=& zKKoC$v7~ZM`}irCSjNCs)sG-b%x~$=M=LVeG{N%)R0uP-S<)k3D5{_z#Cu0+FLJy1k5lx@{0isGQQ}sQUr;tatK{R3RJeEF()Ga5}_0T6}N|; z1r|*yA%U5&g)!<%0+pDP7EsZ1U;ke?Bo-fN})2} zTv*@OzWd%|r!al^A1}S~`n&Ia@Wq#3ef#~7SAV^(_~f_W{Jo(J22hSe4`^3`fcD1pb$=71Z!gM$pE>C2V000mGNklI^7({z1 zo~aR9i0!@+XM26kDn()H5P&JWD#@uTMC>RWa7^{~c8Lm)gh@h|w3u_5iS(I-rQSq3 z8S`PxP!=G`&7u`#3a}&_=~K?(UVUD%!tb>6&IM0h6T_I0>a5%UJ<*@PVIvO>GNQ2*uLwp-BO^||E` z=8?i=FCmhl7Zo45l#sY4zD!l3?Xuvlw#YKW-0y)~b_x!*8sfRoUF;t`1~!pUyML^W zH%@m1m8kl0Y&wIY2Z|eHNyqKJ3}rc*cGwvYdwN$O7*m8!2BS_!_O<>*nhHv!KBg=FvA)}b4GA9i{1l`pJ{Xa2(ScT64g_Ru|7c%!a)K8 zZ)i$!w1i^&RS;B?__fxy#h?Nc zUuo)8@j~Gvf{H&$osUQySx{OMrVf+o zX+B^|fkCesw8&GSaMpo`B~pfVes4XLG=ge4R`*#!qPuxP|<7C~h; zjMHE0IFbzWN@*1ZQ@<3Pqz$D+v(Z>S=9FSiITkO+oJu@Vk0+Y(9FP0F_+rT)uv;&^TXcJXWYbRH&aW zH5H{iT$z8UJa-DBikl~D^Czow$I(@PxL7|_tQ{&;w+q#+QeExaj;d8|9xB$i3NZb( zj)}tnC$g5YejaAJ3N#f4J&%y|MXH6N6n3SfqrI_FsI6H5vqAvF-W3pAR!dd1pcrg0 zsK7piI%5V39XuXplEKIXf}>3U1Nd5Kg$KI8{tZEq8XuUHuo{;|qIzS@4|G96tng%! zr>QLwK$h4Of>dBv)Jv(0p z@c=3?jC7cs@f@?uFQlKGqmVU+$n+F|3gj;%0UE!J^r+}jF?!@S$#; zr2Mh6)jCfG734JhAoF6#!9_D^^tx893K&|z(=u-uN%KjSh;e}}F5FeGsBHOAwooG` zalk_#)s%uKZ}ZT@c?~MjJAeu*D$u5q$SP1txa5@EK*jZ=Ewk+CQE~ZC0(uxskACqe z*U)WaU{>64zm7BJ<`X-lct@cmM;rIq=k0z*14u#3WFmeQNgt+?M*$T%mhwlCGRThN zpH+xLP4iQsq}ri6X*A6@S5`NUDA+iC_OT03{{6+5Uw`Y}4{+A&yPvN9N_G?lDnDQS z_1ka1|NM)K3SeG-<&CHR{_H~!pTGC`gKJwy8VhUrQZt?^g`$~>84yyyF+Vz~5XHlB zUI7{OVoK?^5oKshU8AT*y`^+vK`p3n*ER6$Dk*a|7iVzQpr{00)ObN9EI0w)i8xMn znZ6WM8_38ZCUsJ2q>6+#^oT={5nKkYAlEy!G%7+!NenSt=PN9dkd2-l{P0*9@xpPh zIGI}%O*zF_qC~cM^25-(qG&8_GGPx%TE`G;oZxsX(miy=O2qV{(D)YMmc#)UBBpP| ziu2;G$)Z$?z%9T@G&>2QRVn|p$*Q7xDz6Mg>A)!2h&vM2WSkg7T6~}#zuXR43A0$K zA(Kve=~=0OYiLT7j~T?WVaD*xPO7oMNgG0T5RobCpnWsFsAwrd0&=gGK_pifxkYc* zF$ULd`5JG$%3!}HI^YcsmHOr0SdWtG$d%rWtag;I^a!ym!l;SAB0~4 zJn*+6l_nnDM#43VuJ|wA1h1)vV9A<2OW39q9My}mCSwfo4jlU}Gd(>g7}l8#sRf1K z27jMX5=_WTqoGU7JKAr93Q1glMV>L778{H5B$%KJoTTwEOap^JFk}+?mgq+^-_w=q zd1wrb${)c=KbeKpFYR3!PkF{-RHcHhdWRNd6OXc41yjEq3I0-)k{rPPM_AyhPYsNw zu~Q{2GhpdnU!qrm%5YpEN{=_zJrq@x(&Y(t4bFBCPWN~w2Zm>S{)m4fj=i^uDOk?* zY(j1h_IaxKaiMv(+GlUZYt zrxjM`abX5=h<)=qkC;=5XK0a`J~kVsc980al(;Hx;&m3{m2jiKePVRUjcf!e?NERQ=R33wvugbYh^>Lt- z>bY#?kzDnWV)I;S?tHO+mO!P{JX2_#EWtWntlx*C%CSoGUSX9ZrN)tBZL3h(P`A~& z!{}Gpf}r6l=qk{KqHv|QmPNNomQ|Eh3B>|>$-;0HCHcYy2qgR?sxHfP4H?sMGrf#< z5xBQdS%Hk>s`R38YA|DrJMv5B!F1wqV@*2gX)n9NCxw3?E{YSBcQoRMl2DT>L<$G5*a`c5R)8MuYrK?0T(BS!9aGOJXYYp5!0 zbEhN)D>pZ45)43E3f@b;6g}9sQIX@CgbipvNlACGG+hlRWhu5N&cT*a$w8K7ndWa> zbL-kaV_rx+EvB1gZcVMqJWr!hVwgb7CZ&hr`Av=|UOL!VKBS<6yHlblo7`dU%XsMR zxi4lFF|oKnB@&M(a|u&FZ#SsiY3@t1i8-E_$G{4Iv7Gd-z7(rRMHf5T=l!}MvQL}l zb(8&n?on}rjl1Dq0LB>c?~pboX8mF9OEDsh9{1u|uhA)*^@8-_2}mEFiDY8QQaax# z*OwNTH#ZO7bNv3(k34qanSZ?S%Ij}^@X;q1zxw+7AFllJD;A{O`u*Cq>p!B9^5I9H zzW&Br&pi9Wx${q+eDL9eNA6o(-741>Qu$gqo|_6$uGcKidQC8*+{PzHPGOtMm}fW; zoQx*HdOn}P0OGhbwMzqrWJy`mUs4ZW#;7iJT;}wrIPTOP@^8q5mAF%)WKBU`LeWJk z*G%Oa4%VR{ja1CA7EX@E!~}u_Qla7$>P~^6WObK<5d1NYkfT*Kc%yA+%>AC#Rshn$N_J@AwVf%ck|(0#xgG0VEi6>8L;t3wb9(PYWSmK`Cy%7 zhje4#1shWg^W{M=9{SQ_W?@@bkwGupjuO*5Z!u0-(Y@o2Hl6EnCW!!!>lrYUw;SXp zMA`kz&fXFt@Fg5=bC(Yi*Jr$1X3?&3<_d`bmWFt*1v!|M6{pXrOXa=t6=}c9kWK>= zN->OqRvc7L#Jf+Qg=f!FKG8rlz|b}DFOOe{V$1}T$!J;Jq?V04qI!vh<&xl{V&I8- z#!LVj{rZTVpqz1(EHMd!5Q8p3n)`xgdO=)z3I5e+o`H@OgcC%aC>?`-qR0f{gu3dz zXr>1b{TLLdAgECPBQGvd8FM^=q$fZg6?Ke$dK*+=r3S{*xKjP2NwxY$61~27?{KWg z8|@j2^mrqE!x4o&zCb)MnVOi*Oog)3;p|K}GZRithmzBXC{qeirlY8w0G~|YnzZB; zb+1rL?Ek0iy?f-m?!G_c$%7<75FiN>i`+skx9^mzPP@wVvc0i)QY6K$E=#s$$+Be2 zmTXJzvMkH8x)NNk@r^|(-(tE2_d8?tWvTCUy`1ed&=qogN%y76%bYYD(Na7 zfC{h*JHCPxPMD$ftBKaHrWdySu;T?CFYNj8svocU@p@3*j_N1P2?112M;K6vlPlHy zdaZR2Ddowe_e?f;KJ7i-=-f^^H?zKBm3w*rPIGW8?>~`tucqD0Ie^OLyn8Mn{t39qSW zlbAd|Gf!*X0tVh+m%L4#w^o322uqT-wV#Q9Y|Ytnai%JPj@I$=eMb``OdXn=mo*jM zg6wm&L##xwB-pVuf_1$%_%Sx!D6F!lf^3M81h0FlXD5t#p$4+0^a<8GPJA znSvx2nX6~nWJ|~3>09f9$it>Jw4%{y<9<(+B4-x`gCf$xtfn=sIMa3cd|c9T3z{W8 zzaz2rpPRM}UfRGPHh8QWj;FcTooAR5jPB=F&WCuR>^k@6@@AkKed?_qpd^`pK(5d+Y7r{{9bt{^0K)e)8$(Ujd~2 z+m~NI`1q60bZP$OpZxTxXI?OmKYRYl*50We45WaNQm~=<0tqSOS(F1R$}ENEiD6hs zu^TBeX#%#Zwvb7OP=rJe?hb~?M~^;YD{;@Sc4;vXK1Dc(`hPj=B1ST5=!zS?YNKC; ziedI{VXCAPf4 z8jJa5l^kNz7^5e8LDLuQ(}$R$4>6PiatgS=fK_5*l?k+gO~CCc#p|i~Yhtc?7;Iow zFn}Ga;4wFF>`3_7QGgut0_Ij4i=||_oUV9@IR!5%dInKyE2S!_D9eR# zsSqr#c#F$$miR{5Ih05#3#$Zh6i(ze=_GC|7oP*2U8u1v%OZ+6#P~7n2QyewV(V9l zSj8R}R6?r!VnmT>3V})q=2NJTxE^557oC7AiK85al%@fda*&t&yd1Q=uv-o~rJ(0U zt6scbj@GFV*4;}8R65(B znLy5DFYoWCogF}wCL>Cty&-bShItHlz#x3j7BA$M4es{EtL+3FUl6#b?RgoFM9&pA z7gI^GY^U(_iVb!TI5t2z7FvPSIY>_AV+WMxRdj`2exfhjf}@Zu%Si5pR$LfY@BuW; zq9J&^P~JjEmO899g)4x{3`mfzD`7ZUPj%iN=|cTlKtW{R>fm8$VG|d@Ps+0`8(dq= z6~;Cjqfr*;Iq|h&SOv2&WBp?yK4|)!qVr=TDw?flnPq6%1AC>8LQgHMG zDs)My;NV9`aw!A}!Y6x@GudfNt+UL`Z*qUJ&eBWRMz&(Mgb-Mo&?YW(T~08d`Tov; z8_Y0#53jjzUN(v(a2v8T`+8`1l6tf#k8Z>I_&jQvbEhFi!{Pf|Hn_i!d_vOG@ir{k zYXkC5OBPqK%b4VpJ9QT;1!E%S+<;6xKJt)muN+UE8O0 ziVsutB&YagH+fP%c*ZHkKGHh?DnS%gvoTOPxJmyV+bJTp5UYI8l|Bx%Zym_19K7E` zh(jrp59LqnK%}&IDX3_>mm9>NS{MhFg+nsB;(&@Mwrr)^Fea!(ZS3;G=(j^2L{5!i?jC zZ$A6{%MbtY(VzbG-mic2&TFs#;>DL~2CJv}5)a%8=DTZeji!s(@^&AzY`D)-#>PQ~o0l^!@}d{a*?Jp@*YJrl31#9j5HI<7MHJFfO3=`i0-vl@ zW)>sqwz9*2YSvO~4P8A$65|-9N*D&<;%_0GkY+0WEL9Tb1yoR1(QvsNALk#FlVNEI zlG797-4b~44j(IN>_j+3HUZ~?LzJg?b`K?aVigjHG=d4+Miui|;XGdfYsJ>218Mw2 z{W)=esVhvZN9Ij{koQtmHUuNdNQ5GB_|x`1qzt;K9Og^ocTH9?4V05|;;JtCdRP^~ zx0DcNnrBQZW4RPjU_|A>*U1?biJdG&=AOAyWEACq{B^_cDqE>1C9|huG1D+FT)7Ea%I9vkPle+Pr*H&y)Bz3 zEV(!UI3aKtTO0&>jx{vdg(0Y6-(X>m3_XXMWg{ zxq8kTQFNZ<9adKeNJi9}rJ!Q4>O>DWNf1#eslTr_E!jfZm8JPzt{K2sh)l}hTxow- zgCz_e1J2g79x`+nA&Dp;sgFfun3KT>3WExYB>FJn5K^~J5Sb9PbO}^eMO4A(>~EfK zWL+9|3~@`|dstEVcPV8&Ot0gQVHiX~5F6f5N{J6r%9ymGRmIx+CDtzi70IH&)&GI* zl!G=cL=@M`<=h^tEp}{Oa}Q@!+_6X=XVm;fl()`bV@yj2*{L1~OpL_iJty5<%Zf>9K47M7%Bcv>p7*UQcjtMCfGjd!ZWe$g2 z=4R$g;F6M&c-=+O9Y$J5;h;R~D%9nL%+N!J7*+sR+O)LD0tg3AfJRVD>6wf5QjnO7 zb&7(3_bE(0kwLha3r~m(CloFRO$IDmoO@v%0}Wt4T>2;GIXR(DmaPi40JpG)xF?lg z9jy|k{0V6?vhU9JEz(RVTSEO^V&9@RVTr=WV|()&xW`Wdf_p+m16sF0;l;9wd8E1eT&^OAuxPly91W&Q(_lnapt zJy;B8f!f2Ab_^S18XljVlkSl<_08#@zB=OgnUzIJq!JdD{0V+LlrvEdF|QLBHsP(F zP+@IG|2}X!h2AAe-iPGDI}D#&A|u)18S}Y5VGM%`o(Wv0CmR@rRmcX0@p_zq#C;Wz zRuEeBmoU3G4YRxAq>D4$fH*Tl> z7t;Ro_4eIb>t>^KFB?4LfXbbub0h8DY>ns?AZXB+J^Y4?0{aIrZ! zpSDjlnmdiwZqnXsv<#W-XT9SFP8zLEm{LJq1yzwv6j-(r4Az@_>IP20%C*74l_pud zHi=ItMvwi)tH>t|DrEga6=H)}1y}7QK8`J6+QgM>>9EeEbk(3G9mkGlqLsL)2JAIm zBbh#44#pUG!iBDhGcE5@;})B>$i%gpwm3O*VQFtB^J85t$a7zjtofyb38W zjkc3`QIcul{nkRZPOh_hb5yq}lr{TmTO)uR9E+sYNF4 zJ0&!zq!{nnrfYNxrBK*In}XZ6^4R88IQT(p^h0hs*3by`7t`00@?vQq;O~81=@GeQ zgT)rHhl!Vui_Wqh4@&Bn>yD6E$k{~_iYP5zyJ*R`+irA3We|p<9l;L?x?o^kOkz9I z^hFsJQFtgc!hS8T1?zAL@-~W5`@|TSRI%%mWUG@FV>bh{OYL5qs+W3V?g2xMrMC{1 zUtv_CbV`6*jRGXze<-AUC#aM`Q6W%?YeXr{M}dmXsJNBHVKu|UzAv_ZxuJOnNM;{9 za6bj*1yndRKbA9b0OpY!EV{cQ&!b9?4m;`w3$}sHy918Sn!8n093f@-hzcpo!BVMW zE~(>sCvC6xMmw9kryw+c^WIZWKlkI8fBK6zfA#x6{Q0l%V`%=X2j71CZ(l$7=F`u= z_~65j{`e;d&A;;MYtKIa;uE*-p1broEzPH`Vch7H!(@qs6c|Vu+i+z41o>nvj)EG? z;i=iFqYF!gh#Ijlr=e}RE$*bk;d#u{d`oswz=eg4!w_~4MarPmN}!{Sv`JF$ zLZ4NTV83vOSVgb+QCh(h$+o)m^AZ|KGQ>O}Lx4$rR5mbx-k?u6_an4432aD>@#7ZW zhVn6Ti^1nsxbN7D-3=2SWQ65J+%_pw85h7$Ghk4WWIWuTQbq-0DiAwKd77QZ7Nh2h zpUxMnGfNnVxAPzeE~bM?ZPeupU%r#}(IxNBfeI_3;B}|Dogpmv^@ntOI=>lyH!?u)ubj^#wx!w z*5Xe~qTRK4nTy&fE?M>^Lmz6bFrkZ+_*by8*Y~!q2jsR%000mGNkl}y(c?Zk(3*7L@WIN5DX6g9o7n6;(*&HEa0Z=|rY*d0GCv zq-u;?kGU<4^C!4JW8*ylD*T4aH}1RjYvvxRCR5OwvIajM&Ez7uSzEwBe-l7qgChtk z-wPG5O=>CzZyY*W1boF;f*BQ0E85hj$4M31TA{zy@Jf6FmBa8U7+Hbe3Td&PvMVI2 zAe^9^zh;P~+Ps=-)Be+jRkHs5tbY%7RNB{)&h@Y{}UE2{TE{vIoaP z1X^5B;kby%CQLzyC2kJhTDd58Vl(t|IPPMP&A>EAtj3ViLd%kl2&1!)vhz5$Rnn9p zNMV&dVhK&2(B(J@6bLXooskC6cXWmYA44@+yG$ldSr&l`y7+ih(WRG`_`y<{myzjA zffG%z^tMt_y13=cAPNRQXoRsn*k&A}+O84jYS@{EuY_YN{KEFprnSo%CmE4!$=JM| zv!pi--y(Wsi$xXcB6A)rf{zL*VhP(*)kQ@cA`>S`&m~}zY@sh??`LnG3o4YMUpwC4 zI9W@Yn0ogk*k@EZ<-p;p;(rfR#(iRB{R+a0E2n%1sGxFEdk9o&PMF?8g@uGU?JRch zch=|+K!wegP-&Qg#KB=lcSmK~RZbim7~dxSLFZ6NIT>sE(q1nL%~K);gp^{X;5ULw zUeEfS!Pffr$>S%_U%7hY?!Bj9`q58bqow)3{PiCnef;?sUw!@HTXR1D{EL75A@#YU+`jL6?rOVfjpFF#^x!>t;R+Ek&r7Pv?JTJ|!j3rX0B#ScP^2wac zJ3?rFcE0F`d8O8mtJs}|84R!5gu}6Klo4uezt)xtEZ&l*)f8D2;rcHLsF0k}37pYJ z4A~n@;1^;z{5RQ#z>< z)lXm&-l-C1rxq<*avVjc#vH zDxvGw$vM5~5A?!!n!IFO87xplkD;*sVx*N~3QiJh3H6#@g$2CAj#Xq(q+#KRpNF2% zljjWdU78YINTEgh0|El89~}oR#3v>}aJMIbc+9?s>_)S-#dxPIhp-GqyCq9l z(YwG@qmma|o+>a{ge}SGPlkBv$%^QEyI!KRhV<|&&6$a)`?74$)VRqpuz$X{&7!< zL}or}+kwq5@!t(<7r@<>1HzMk_WZcxMeTCfDg{k*%3-?%w-Hy&F|1NT z>sPssH!4CY;i6c-KwVk!7m!tiMwkJd15Lqf`WnmDmlmO$7-r^I9+95+6jusGF?u9pD)p_KTGDPH~l84cG4bvB8?a_`r#=P(-J;i=N8G0~)*)Tn<(PQ6N)S=3+ z5H{&o!^(P8-HK|vQT;gYJSOMk#<@7TXh5abx@{;0Vk+&sjqcNp-ZM$>=>!&3p2`MK z=fh{s$E16!(J@#8C+)#LeRFstA6!p67aQ#}22}F?#rEh5h$`g!>YPYGSJ};a;Qq?{ zC-Z^%c*3ws-GGOIgu#B=*#huD2UpVBYG6i%tX!y~Y)Tv07A`+RUd8r+NnV_G?rC=g zd}7@#uuC;Z7(&>lIH>`Up9NnRZA*hHRvMS(# z-N&HVsLRW3BGwJcb@@cnVb)QlS;b7tmbPZOfqtq%qx=c+04fihcbEFCT?wPh6}%0d zv-rUAbZ(mB;xC%-Am^Aw?RUp_S9V#eLG6n@V+=NXJZ939HTUkO{3%e(X5GoF;9gCC zn;dR5r-;d%OS-;!v`t|Ol8QKPX8q#Q@b*B;HSz+X$w+@tr5HdU<4 z%V{#;u!;ed6RrMM7{(Y)3C(#FrThno@<>oA38;iPqT+(ezwiDUAAFSi(fTz8Dq~p` zQc)g#zgsITdnuE4rT)&^C!c&~_~gO2|0~Woe*U)){{H(v z{`r@0{q~htUw`Jg7jN9WcjnyX&7Bk7!A8;?M%8A~uPv;EIO7OQ^OHwyBE^ZLkadj^ zi@S*rPt6{gSt^!mm0HhGN>uG~QoK%KahV;I?LwL|#0z(}a^aX`hiqV6_NBr4D#cOS zT4Y@2iZG`%3_|uYOF|AY7gUr$tT!yD?qF6_NNfQMS;`$;^eGaxmjp}8sx&ldFUfe+ zZ9oH|(qW#5r};&5yQbN9U^bu04Xe6nhY^$$rW6=F3DZK5tbnU`^?+F3bk)`UC zET(gw$Ju9`u#Po`G#yW*`4o>*=nHEx4-1mXrOvx`rLU+DC$pqz6 z%zH>iILGEkq)N>S6rSPC5C13NIj<*{8fdCK4)HI1h+0aBWP&uZz$^7|TMEIZ@C`I% zvRF1`V*Z8c`n5sW7)8lyoUT>UwQ9C*KE{o;us#aw!=Tm=Ys08Mip=dw&oiI`atg4D zAr>#9+wFp%6})T(K&6fumD)0V1W>^fq794`l|ZufWm!$}u(LQImlrQB76Uh(LJeR` zFa(L+#I*BRD7&CSGbu&4bojvVyn>3GS1F@}0vjnIAc_Yy#0)AWfs+ztRG{h0b9YpH zJ|2fvoG?9RRG^zUZU=EEh`IsqsH}$7^{BQT*Z1Q3@fdtxX8=P%yuK>9_Gmz*l3%X@ zrQE2bk5>$|bnZ2}PbK|llKyk4`G~XhsR0!leC*xGhfw^L4{o$qZ?#u%=KU*a=Umc0 zop#UW{R;>xr$A9bN=du>dGAEl-ER&~wpY)zM`v2YlUaYiX)e*$PUrnyLnd{ij?N|} z>2U~ho3s?Pb!`z{;K<-Eu?MRzSo}rPD>x##j@B<7XXJSh&c~A@tG($Ox9I2n#4xSi zE?H|Jnu6meR0YNl6VDj#%7ap2E_1~prgF-rlUI?i#n+TuI@CyXfuI6=CNQ%|!iWvE zuMu~#W}@~j0SQ%9R1A@gl#PU+LD)m^h9w&3`H?p^WyNJJ5&328HCXr8YRa?psy1x6 zmNWBea~yMoi(q^!*q$^{iDQQ}1c{Yv76k_l`@7xYzB;D3bqa+7yhN6vGHYTNWTh7@e!K67p1SwyLL4`Gw z(LU|m(_)@zj!Z2A9J93 zm@Q#b59d|p4^PiKLJBU;qt|P0Ib14L&Bb&G11Y2KU~6mll!1+FH*P=u%=16|(NBK< z^I!hE*lkpT2nI zy1|>_+IHR^RvPC2J{wEWKI~y=zA!Z-b}ve#OsLmuR)v(qNLJ7jM3Th;twB))RcTQ-m9Wf?sbkPk(SMvZWq8m-M^j$)Vrn@H#^a*L-oV zeb!ofD1C95L>ty~%IY-3ACnhJy^)Jl{&X_`XxM{m#3a+N(G<$rPf3nN++QWlR7_x4 zpB-glX%hjYd{`4NcBDo7%0jVVdG>Q1 zpuW2?513Ygi6Edc4354?u49VkL^$*x+11E-ehlSdSe9`;WAjRV)~?1wkQ(b>h+L1E zZmQ642{7{#j$_92Q~|>TKmNJ!W7H28=++8+;!Jtm=I3c>ivJ2J+=~3AkC_yEm_qWD zPS(hpjdVkk(F@xS<|%U@qu7X@V2DPuEA=HWD@Gl|9#LZ$C%m>yvj6}P07*naRHI6| zTFDHYtXBD~RgqOHi8<@$W87Gc5LbX$ssq2$qvMgT0+iB0X~j@VvQkc#i#0TTRScyp z72_q8QPoM&QU(70!QNg3>6pE~Ha0RQ1WjMuiz{4p4Q&3^omaeAA zQVIl4e7BFdREBJQiDy!R6)ps`EA<6h9Y}snN{;O3ngRQYujGMmL308OfV~Rkm&!F7V z5TvPN1VscE;udK}wg$6R*Aix5r&Jgx;`=%PNErX%xH-+k)9c}PL#9rehF*FyI4E|n z4lUVJbFtxy9*ZIPfNgbHkqt~DLu$Snvxl|&?9C$yg-}zJF%JFH%ahi!b$82>AcSZn zC2k=;=}}CDH5Pg*9Wx;!!d%UGSj8sYZ4Ce(toj{D;jd&xAhl1RGT7B8(V@&XKjEGm z$9LCMH0YvAOH-#*;0x{8P|8p%!nB^4ZD5kl$J(*su6qpUH2a%j7$HwY7^5E(QvO9m zIS49c1(h%iNEBH!b4PXE&GK!*k63x&b*jJJ5lvz9M=t9VW9E*aA z4zL^m7-#Tte9Vy;=HdItLi4;dzfhi|(7fL;m%O#K-|VcdZXVx1dH&+%Yq#&*fBuCZ z8Up#%+rRnapWgf6@BjErm*&5E@bM>~{`LL8zx%sC{Nl~Ge*E&QPd)Se^&58#+iYx~ z=nOXNS&x?HF*IL3I#)1Igp{&mDTTaVtfiD~|vo4?loYS_WhY=4TQn23KrYnBp7H-xu zpBrID1%feMPV>V!BTk_3`bv&k3Q-Ce!1$MlG$nwXL=ya&j5)S-Azs!bb6+aFSX9C9 z3pEpzZUDw|rj#hbgM*A9q2$FNg|+x%b(Z-={9PKu(2YVZlA_eRgs{bzC6;~p>GcF< zJm|LYiD)by=_(RjDUW3X=2&4c2b;J`%1H1$06b+CI=JRomJQ{VcqES;I$EHg^iPUY z%e|(blsrA%Pd_H7c)fGc&y(|be_396$!#l^*aNE~7AiDgBl#R`H0BX{itOwAY%hpO zrLlQ^QZ2>;-!^XHuC++!L=-JnmPq=Tls^~wEDdZ>tRhr9nLeQqjyEw=(tB)^nC>>x zYZD-GUk^OMzTnV)5MdFxeRTorsr2fk(6_9sw%Gqq88ywc3kK}$EU(p2{x5>?t z2L9b3f7gTyf$}Gde}#&~HvHcLLtRP#E~#>bOyWBVQltYcV)P2w12!uq(I6|u^QA^1 zH2+@p!p1O47*49$S~XvBa*X%Cs$SQa1-6tE}`}NMW|^cp7F>}#SDio$y1#H{o)Re;jTi#Stjp{p8^GY=zNL3=WMt)%2S5%U_E>O* zU7cWdl4X;JAc<;_L(&$lwr{nl-B>JStiS+NjL4HlSN}9%0<6+nZM4^HE`_#DXn2uu zMH4I{jF{i8a03jijY&!1ym%APPM$-v{m0eSQ@rt6&Q@sbLH1yM>8;*a^WOQPuj*L za}ODG?nu}av1^m!C=Oilnu>x7uS1&CT?al9(-$c%E~U^)J-V|XnbVPRm98yW7FQeR zRrV!(!VS)DCPWN!BGHIXjE5I-AQB~-1AH&PwNoWsTYl8HFmwH_DrD!oBKXq157cP8zoJD~;U1AuI zF5M&(B3Tp}53wdCOkdEIG^VkAX;LbQU_za|WMQgI$+`%U0CgWv+mV*BM&rZds*6Ym z)*>aJ;&c9FAyf#_Ood+HDlHrqAmQ%gdH!)b-%oiMTA&x#99i8o3!!U|`t3|>5bctWcBcShwV4(e#xG2KbfLiBZPxwcSB z3Sm>=WK_vk@fbwGlh8nIFKXy$lDR#I%!BbDv5WvwYLrVsAq8S8ebiFAno%i&meMK$ zs8F7sGAfXyr;jkAQVf?%(NfVI)K!YiDht3W1S%fsDtwk`Q>CO^_2$OnQgH#IDMhLC zvXxw#MUi<&zTqx8QV<35Ck!eQN3n-YU-G+%B?d@JJVMWoFFq)w$aV_!5#t(t-47dd z(E63*)MG~OuQ2Kms6^GZs0smJz7vOZyq)<PQA62cK4bCuzMLeNjfZ{KtnN%)9--wOEdH=1(2w+A(06wBj#W|Ew0FFq4NL^ zOK>KBoAN3W&ByC+NY!u9^(*qvD*fOeu3|d~B)W@2oMS+xy@A)wyF7B=-PBAA*>WiG zpwlQhU&w;;LwCwn$$IMKNm6)KB36)6LF9lxuUV0)C`!0h;*Y}oUax(=9730MaqhbB8^lb*#5+Kjh7Y}!*SL3z%fNIfw& zFK;+-0({cMtNG?&8*gu;bI-2A(`|XFQo2QGG2G)yWt>vshGLdk?DrzGFm?`-sm9hc z?>eiO`)rw`D&)13<}kFF8>{=2%XchZG>Wp{l&n16Xv#1P4Xw1L@|c!bTHJ0-zr@@N zhU|MAr}FMPfCS1Z;QPY)dDrxHFq-o3g37^|iie;Q#bMlt8;e-+cbXm*$s$dGGz-zVo}+ zfBxo6Kl;i2r=Gj=_!B43T-ew?-tKSI(w-k@5Sr&1N3?q#TbX1nWf3cqMHHH_iVB;) zrsroDyh?qLWE+**FsvdZR8>ELgRH{yj$xg-Md$^bV5GO0&nmHg(v+-ui#Jv9sen?D zWYqmd6X{2aE?5r4|A8FDM}tH>S&L1)js=LE`LP@Dq#esuMm>-%>V}JTY+Jf${+$cWi_g97*MG+_T$F!sDYr;pmQNguf*xqIJ*h~ zmG(V6cMPZ`gXbE9ryITdN&j9txS#g#roAV!{>|p-j$sw-CVBT)fB*mx07*naREBzD z7*QEqhHhd2m9qv^lJ<$LcRKH%&Uz=44ut8O!&6yrKgHs&MtiFclPTunc*h)bC0)x# z&;!OEun|Qmw6uz*9@DlhDNcC3e!Z^MSTy%8&0U#zu52eSVoB0k#p`BFDPTyQQsA_y zl1mR5O=@6ve?2vy(xwRxJJybtA=hKJ!ATe%t>57b;gE>!=epu+hU(uF$oZfvq^jz-)m z%Yn&q4b#(zdol(p<}n~jnC}IxQv@pO=Gi2yqQMN%5ReZy#@eoioOdTp3?IMK8EM<^ zt~~<*l|Ik52>FQaA?gRcCsc&Mwlon>ti`Lp39lRvN9P9!Cj<0-^~44yDhki4SliW@ z4U8JPc#ULa^YrH6R?NvR4?!Mx$^$5@!h|FHCp&E&d?fLN-vlC}<0R1_-AU7z`7H({ zo>DGAgE`(V#9A0HW`|JNzyAy5hY z02P(E?tscelu~^(oJ3;{Uj zNopUCYOPYxSS&=)KDEAh;Ne`L-(Vy+SAK}*3Mz1X?PgN7*m5j$Rd7l}RC zmoL?;4nW<9#@7r$0nvm$hb4bOna9@o9*gi>Vran}CqoKzZYg$jkZuxWtaD0#bt zG<*p*NU+|?)9d~T$A0WIhy2){0y+2^IJ6_yBWVJ#eaeJkRD-L~BK$@K6_W@c(GLHi zAPrmBrnn$0a(c;RM`fPM%Ea$?Oig0GWlhc^Xf&Us^^&6^eAKg$-1* zB(1=vq&d^L)1tO6`COlNb@(F@7ZI8$S4a+l1ZDy?`>D~3-h3%wFb`uJ9z9G$MEnNM zd8G58-^^D5=P1%6ME@7#*_G;2IVnZvA0k5?19K|)5l%9wfSb$U$}pxCSHu!z5}0+O zk90$|lmLflLx2;5B^?HpYR}y8E8TJoi4>4iV(cbH#}~6o*ed#zQK6<;X*Aajt6=XiCRNDdg`1CcGCN^~alI+&dCbn-$gwO1)PfpalKY3ovZ8-K2tvgnJFeTyVfMKwlitaP^?5+mIWstD6g0u^&W zvl0_myRT+2NX)mLWfl4?!UGJY02;6a%a z_(-}Ct)p;Wg-2AjRlI7+WTewD#yw-^{@(h@yt5i&dOpIkFLUFeRYw;>%B2#Xe?3p{ zVdYmSiVCsHcywN7QpU0`Zw>s_WI9XgwXsmmmj}*{M3cj&HZ-I{HuyW zN{QNsOHl1K>mrIamDvt}Lz7xSJU21DaBQJ$;G~+4VhVUz%xJ3VixdnVcEnVDz)i$p zi-M0e)D~b`#9YmDa>F4UOqifei^BKHFeL&Njf+%TnqA@CJUX>XNHWDBDf$hJtYlp6 zMU7z+HPOf8j!N!4hIdYI^xcArFJp_e<)ROhW?{~^6e-_=ud@i5nFc)-B8nCbS7@bi zn&-)Vrva9(H3Lbm^Ek8?O;eh)f9hq zNmuKow)D^H_KRMm+9;0ms+@ehH(@V)J>7x9W3O~|P-TLujTa3#C&{pyt=2Mg6}48+*31o1 zPCzypR#QVMhE;%AV#uWo5J(tQ6F4I zf{OXa0Vt)2J1Q%s25BjBDk~aOfx$=Ed^9(f3tarQTr>yx1VP1HF5t+cbQUA4Ae<~0 z1y^Lrks6H)(l|^@^9MIl90M4Gip|rP6k3WJnit5y;0mz{))2>DxuRMM3Mn-_ps0}h z%K(hepaL3;A96i0unN}`o6`-k^(%lg3@YnEWz!tLx*gU}Mvc=!?PO^9B)=ACSEA&6 zoL#QAZq?d%D$N_!=FJAw6F-x{;Nz{ddpqsjPP^CB?zOD{gaMTdGb(B4V&1!GK&3S> zta35!o=!R^)9%TnV{m0R>Fgz)T}Y?242^6io$Uk^l}%Dq(l$>$^5A2>O1MI)44PHp z6pDi@RPEL5@8(z&OriEnHd45ZxU)g^U%XyVzA{MO59|3z21zI@LMw;PZSxq+eq{Ek zHXX28x1Y`-lkwpZ^NE;j5lpf(COUh;L5k+n{Y*v0<<2Mq5Un6?q_zG`9@*arp zLUO?utQU%VAPk|G$a`6paezfOmm|0xGqPf6ERn&>#OGeGHb&$nf5K^vRtUXb9b5}Zy}03< zQ%M7ZD?lu5wuEUx9WNKK3bx2uIku}K#xkY}7U#o;$D2wI#>~ef$ z-iI|k870IkGyYMx<`XnMPd=Q(Si@xyg-rHz(MgFB{4E&Up5lFvPy*Q=RTyy#Fiu7Y zxA=!KXQCjXMlwQC8n$jkG(plE9M1`FEeG?+y%wK<0G;V zVlS^n)|hm!<9V!5)DO9Iw)uFKapon{FLX2^T8%JIS=+h@z~D07yqOE(A+VDbC%HOV zc_tbgg+#4`qct-&%g`@lG{t|2(1xt)2c=e~=S4D_(=S@p$CIN>%Ev&BIRaXfIT3us+cV2R zCW~bZoK(AUV*q3VKj(&n*O%skZyl~F(@`xmSALLz2I~%h$`G7g23HKdV+0ILiF0F{9Om9Q~F#}`|_NLK+|A!4a?abdk(Hmnl1;G@BnASM(J{HQgqE|1Js|Ht=3n7iu>*k{z$j~n>Ti+M86_cC-O~fjyfYL%jN>GG@ zOeP2u|9v+`{l`u@gFb#o85L8%5 zfsa9$`goxAEAB*N<; zp^s9nE5j))BT!a;OI%Opf*4#(SO_M=BP~vlQ-5)asrq_8&Lk)xz@11h%prL3orxx6 z_=l$Y=z1Nq9P647(aU!w1X@3AVhyov)8ejVc!fzzD!HILcqiokk^le@07*naRJGO| z)+wis1MoIR&;7#GV!SHNT_gM!w5`jnGsaAde9=U*@*H+Y-oWZx&ecfM@rI+5SP4j? zJ6mLmlZXr3%*HHj214+{oiZvyEE{A+!+Kw7^~K%--3F1fL+@422E0uL6(EGp2HBZt zS_H4)k9Tkv1+y-gCI{=+4#{6A-E7ijJuy$tDhkUcB=V5jlPOSfz3&EPQ9wvR6@?NS zge#g$AzNKb^Zkx00wRglF0V@W7v)c2z=Vh4bx{#w_iI>K45KS2x)i~uK#N+!IJZL$ z#gK+aon(&;UABgJqyvt7(gwC8_wUNm{Ba$gm)~%7yS?=jNwZH) z#6S{3|V;l85L{yA~-oDtm1+S{}xA3=3Eu!KxqExTXR1Yr{Dl2}&TnAT@ zUfx~rukLQ|!$8Utx9&gl>@ZrZFfA+;!Uw`xMzdiWo+fP6L z;=_OZ)1b>+zy9s3uf6{K4}N&-&i#v*ukW8YJ6hk%J8RXXYc8E(X?{5XuNSrtPXMAU z9-h`QM?8~w#wo>+%&~=XQ0-Kq?q-0LLVF;bOK}Wck}8R_awQ`a0oPNMl*4){sFj0y z+5G>Lxrp~uFUWjzqTHMyhBQc6YtvQ<=R*)z#0rE z7h{oGJj$F*F&af0eCMCaBNtJoLSrq?zMeIn5jZJL(RMhcoVhnv!WEOWc8}Nn+FGXC zi_W|d15ofrYHGQ1ciMU1oCe`wQgOvmE%+0~;zhCmi6`XOLcyf0h{6kr0utH*AUPu8 zPXQITHbDN|Wu!$S;{-PJeiAqxU9NGg#m35U6mq#rau5zgR>-}kn=m#rBj1`pf;7bm z2WeAjl2A(?jc2+B>qR^>e!I$}n(h3{_l~Ig+U!bKl24Ni>Uoe@1O~he+ zCl}hzf3}l%tj(cm23+j%R)uG)sP3@{(e?Q83>^xo$wEXmABs1*cR% zP6?L_Fjo)z^h+F_Us$4&;u41Di+E>|57SFYaw!3U3f)+-vPsA;FDDJJ$rIj1DTge) zNM40P_OzFx+w(N+=z@ypRrL_4_#&rBo}Meew42!Tld_+f|2qicc2wyCtHcIZ1~~XQ zl8nj;b71qaj-UeP0;E&2$1C}D7*uK9ZglS@*!Pw6p3O%uc}J)QN=pr~R%<#^UVVNTlHlgh8Xw7XL`Sc0HJzAv7o zhre$aR%vpuUgPx|nirKoGAcP)o(O3;K~6dhUU2g>5XqPubd`?*7`d8fg=CeLnGR1w z>cBjJ3aB^R8b+taFub1L8mrPmrGw~|GK(bpF_A*`#IpDfPB1L&8bPX_ z=T_EOQK7~yU5gjB#MwN-)~}9O-P(@&fx=79qlde!tMH(t+|b~D$JuVTrCyXi-%K5u zg}ytdKs#Ay4G={FD)azwuXWd?@=M26HhDw^H$W*B zLwR=+K)NFvl*i%AT}tq?rOIkVJb$VAcp(-}+cTvM-Tq<&4;Xxh{SzCG&(-cFcRJNx z+oGlHwcIIs&a3Q^q9VKXbdI-Z)qcQ@#$8;n@o|U#Kax}YF)U$Fq4P-Vmwy0MAOORl z(*6!mX)34)t2`Q1?2gKK7KMI!jD27HTXS|?<$yiRwS-OBJr=iy_%YW8cChKo=H?9^ zu#f^X^H4oZUN3V29o0MLZ+UODv$c2j6bz)?x%cF=Km744ufOr;Z{PXdU*3EFqkn$< z+2)FFVVrTCp&7>o ziKBd{oN{<-etKrP;5Vw~|F-d`i8?b+3x@3YLP{ycc?tNWS+3-UQha_4h$pqIw|o5D z#?Gm%H4LlmrIpIeYTvHC!v+OE zu!cBFi(s$HH2A>@^)M;Y!H!+7N@~+0d4zH%8mw1)(qY-4phUj;MARS3T3tmH#8G%n!niqpBoGdq zpirZ+1St)}Ua|v*F=~mmm(G!7Y&$@nq$cBK6GnYdDsk9V zmN|`uc>3mx-cpwFN}>*`Jf@M#67Aei2!u$>Ee)>6(>(W4982Typp6DL_|>{~YI-cR znD0elLzi%79gi7((Zxtt-qmxZ07hSQQo(7AisjwYuPf0Vq6N)|C`4kP-Bg2@FwX$C;d$p1W zY-Uc`YXGZ6?NC@Hi27kXjH>H#eH)6u>bsz;)K7%6}U+pmGvp^!o|8 zzjo8^UfSD5)7Msm-Cv-k;6x*Jd2vIr*uAXvi}VEXK~dlCsP2duB(RWLBDKZj;If~B zja$4`0`i4>Q7(av6lYbkw)ma+LV1JSU%D30dW(4&Ew|7fe9N*2$}%!J&s?!~@MX6g z@0mEhWcGxDB15rz9o#>$v+=g7Scr>!Vl6U^iSR<%5{bVHU{LHtMG|_3RVQsP6Oc{1 zlbf;*Y*x}^WaNIGlPdh=>$LgE5p%I=8OG3I6WX5<9NK7FdzDZD-f3-A)SnBjUp&3i zJaB9c@g@P!nDwixGy+)~!osAp3b~rGV;&FP1}Rknz>zl)$;uwFoN@7&oEGQN-8IH7 zisE4Gp0hesuiRxwFu*|Y17Zu63Q(g1i#f8C!p}yLeR)IheY4QQ{w{TSNr0YPzj!zW z0S1>GTf=>!mmOVSF~8MYKT*#*hA2GGPzp`a2k!ofGl$|@yvh!!d`CdVrc>a_6;vWr zR4}8`JP@W2$AF}%h@!cah+`^`$f(TfUW$b5$1*4E?9z31PD(K_GrtnTr9igIlOsqeWsa0cI>p>sYnMU0 z$Vwa*r_L*gj|bIs|K!>K`~Uhs|G)qD|9RuhUuUE9LF-Jpd9IY7E2L+Z8>bhm`^U<| z>BZ!+$)%~IOMcvJfIVYqz9PsMv}$5y3ILBXgGvMHedWuQuxpEk8cE^+*a*mimD8tt zqJfYx4D%-q+JY3D7NqBGumpx#5LEmI!w)Sp>f8#zOio{mo(HrVe8HflU|jy#QY46? z-ulS0itftWEA?2zBn%SA;9CC>H2e~%&=10Kz)=(r0usnqAUr5w(B(4Se(XpA3bo9Y zG+C97jX{guSK+k^8#S50AG%YfMXq0UlBIl@QzBLc5=AA_24tOTVb9o{vqpk%f_W-8 zJfkUpYR$64FK!~m4k@w}K;EYbxq9YEJ*WAl8;#-)N+=gg&wcO zg3nW$93NAkl%NGShB@mloUo^>d|gsU*<@@J6Q5Ip8KM-)<-4TSsT&|vIGI`qMWTTy zj=oOEVfZMRk?%|2H})U$d+2L|#kn7cQ&=GK-8&~qFoa*0$46jvQJyyoY&l*i)eAx98$tl^7*_LWFV-)6TPutUqOO$2H5rW8 z1ErKaIAuTff=bzkqic_Hqh9u^B}5c+N?zSF{|^}C0d^CE`>WE&F#Ra3tVPw$xV{tD zb_}S*jZ<;sG$d7$^VR%XoL-KSOO@ujw;R2CQ2f=qmG*8L zP{{|kvi=PS(_@kzMpSwivhKO8cNQ}$ClFLlrQH*lQQ1qn=Hm_xKBnDWNUC6}9!6AL zIH4P;+R+7cU1KUagNKc**jNa46R+boh`mkfO2b0V_vm38V&{Y-P{W?3_Y8JSp3Z9VKqq34C}JqC{%;B#Vja|c%P9%^8#t? zWRoQs{Jt=w4?{Gn8d*@$mM~VF#K)D$BqhCFZYW`HAhjUk2mu>b%N07*naRH(pg z2v4u{cAMNptdV+oNo|`;p*RZ5DX6G$Sy^v`%K+$f)&%V~So4uL5Th*HJORnjvf)mb zXii;67^Menufx_X{-!iTZ$m89tvuWnS`!zXh*~U9akhl@5CEHj%svuqaJ83YQJ73v zW3ixuzvLYitH6Mm(_Pzdk9IsP_zHqRwo}CS<@sZ2_=Bxq{~o2dzAy9Spw=L$_y$y} znY4Z#1eNGf85NDG@RSN~s$}1LxMG;=iO2R;sNL&eJ@Fy8hIn$`swk7T#miMx+%d<; z#Pl^bkb>32#}>VXm1xDQn=4%d73MGKU~O+}_te=7k6*ue_u1!O{PD{_{lzcee)o5O zeDD1aKKkd!UwrY^g9qOlQ2F#TT$=y$Uw-|YcYpTAFJAf~EX`lOa^v`^^Q)VC&F)$? z>3GmST$^8spnVuAWs0?wMUhkB9GZRvr5v7`J33b|;M8bsRI^b?UN2`Fg|)(wNs5ST|sS*x@9xA*`0zy0t3$MD>X#r)z@^+mB;ZP4mVhRBWDf5Nu%wlN0URY~OP{BHY z{79RV=wtbiQ0-EV`ay&vVg*{B4nu|nBHD);WjKvb6Y$WNAk5weP@0Hw+pg9Prqz!mu- zhfky+fm9>*)S1^n7&DEv(N<^EdG$q(;6p%V$-K9rC_DrzW2y+*!(ilw*!&=Q*)h6IYGs89@7ior^OjxSs$e~_Gw&EVa!Li)=wXl^D1`=9g0Zzn11d%Ht$F4F z4G+Tf%`i5U(#6w@s>8Up5!W~4+GeG`Q%#On8vB*T@oIjd*1S^9FIO^i9*+c6st78z zwtZa>1Q)H5S$r<9a{L7*0^fKYyc02;Eh<$60&|Ficl=aT&+3{&ODg{RAatkhpU8>E?%aC zL?8gbb~66n9gCWMF%G}2)6z&Hf(Zpb(zdPqvO2N$+8EiTreoq3iK*b`35_`p z$iB75l8EF$sJD&wPcVEh!}I7IYt!JP?6HUuYfm;4;fAU!+#=9bC)fsiy5Q=6-S!bv zLvf$#%K%icl9=nBg%$uvNXpZ#L6|;p>K!hQ2J=!0!aj$!3Mil6`^T)o}%Bys^S9~6$szAEwt>aLkwQe z>Rxy4c(u_i!xST!z9N{SN8i^0MDZT7etp*|Jwj12kYvDzdCee$kD9h!#L6o_bs=4IS8eAJ6*J12qW1U)*^x*8rVr--5b0Gl(Cowo` z!V8Iquow8~a@}0-l4oq13UCpA*e8aU_~r`O@B^tX6O<>RM2Z6{a*?fCN||rls$VYf zW%HHP6HizO!J0CvlS0wPUUO?rncSlzC%OwsB9hc+*>t{*m(0raq*fBM#!{rsjVT>m z*FBjT2Zz*2<&-0{_^a49xG<%EOvi{yYRqn6@1y#j$!iUI@WJv4uBgYuHj zr&Kz@l1|NtexhqC+?qhy8)6meg+mF&6LmFcT+QTr%GBH~w<`G7M;JfZT zPIA?+&zmX?Gm%1w0tx?-u@Vwc7DX~D0fpVC9J3Y!i3H`jn|RKW$&q;&Ih|RGAZ-%E z4@9D5P|ZiRCb+o_W}s98-~p?PhDi`h45ffzLYhfJyGdONxm7?v8N`GuFw|&1rALYi z%`0Klenc0YwS3h)2A3SWyuZ}wb1nsZUwv9qlDSwImJO;vEOEm;;aUt~dT1y{Q0WGc zQR#s1D{8aL3r-87DWTyL5LHlDX@MCG;u$H$Gs>9LC>v1mkyQ{^;G-wdQq~j0M<5mh z32_MkCPcACa`rsl$Mr?61y+cOr%l@@P<@-SqY^aIt0RG0-)l-f(m?O zQDqEN%qv7zsp6?XUIiM8F{2XHap%!|*XEn86Q$tVqgH-(u=Q&Y)z$%2>f6<1zm}b> zCMPP%$!d1KmS3(luhyE^D*5A4az1LDujE&2?OULtv>~aI4xY`1FXW@=@}VJ?yN%8h zVEqDCxs?xY=7Vcl@3H}vq;od!oj0IDtzT*PWZplU^-eWf`w&wxpn{4Dh$_$k1~(v` zVkm`Y=?P0V@$!9(PkR#%mkv{ydlTnSxWy}_PXyRlr%&OuiL119%~igEqA^gB#x3F; zxL7xr{&kp#AE~rL1VXN)tV<1JJTT8u6a4XZ6YrF(_6>y}mO-##j1w#ls5E5oQFH?y zS7|sZM`J9LBe@hZXm$9|j3Y$$JGzodw^yAJ#!VT5)G!HL^~z`?cMRCpW*wxLuS?*O z+HgOqC^SFMdWAT_xNIO7xZf6R=OJpR2RzG!$$@cfffnY)0&DLxZP6v z!n}&)Kz7;6)!!18V@Ko{ZP}81+I z+6&r;r)HKWj=Dk$jZ4lS9t--LmpXTJcEwzUSF<6mC32M(1=ayn>K%+;0757a00~rS z^Q1`=C^Uc=RGQUBXMO8TGP7G-6M ziDR*w^w`@DX zl8`i(BfMuXIrxR}D3l0?vn!P)FDb>X0P-FK(MbkypmeJr8ic5Kv673AVUiG81s?{Q ziMc1JgCES?ADSD8C9v9LfX93;N-kvmG9Tf0YhCX6f^K5?aZEK^h2J7j!TEU@jcs5q z1+Vr~b9WFSKim0#sBM~$Uf zHvixdnXsHfa8k31O1WyT_zNhfz@>kgbQQx15h*HiL{V8TEg**wpHSK!Zs8ZC}eVKj<6ZOERm( z5`%-Z<}<4Rs048z!t}ULAJ?}qpt2p+_bchyN_s9%&!M7nC2ByhzSg{+^q)$4_p7ZN zwbl)gQ-;suJ@+u6ax3ZHNT8wk@w9gp>WO<-((Z+{3#^iM&*p>kS?>&usN}=5S^pGL z%09tK19w!+LA#*2J_ap?3LBsV43!fDCmGU(Y$-yH1*0GvYU`@2Bdf)8E9Mh%R%I)3 zy*=BfVGY%2 zL-AT7iGSU)M8!j!lo_agj0i8cWz_jSNeqw4_opRu{HOB(GoRWyw_n7~zoh z#LlX$YG_CHAL)BvPwbnf4_MRp&^@BMP~3b+!X#Kg+}##a7QT(-ougM0Q-}Zn5CBO; zK~y3#>Fwpi11a#)4WA(P>~OdR?Rn-oX-9<)&8pxhZsQk4YZ)A9G|naTf>thef=L+w z3rEt(Y9tIVBS{Y_h zJ!D5dML}gt0S4ZG%Ae5Sqo!8i0p#+cS};Q|$2+Sh;D9nisTBZ~N-d*wN(6meB&P)b z1(Z^H1fPu86Emowvn+%YBB+Rv;>sz|O-yIJ-OD{rupaA)AC*Npb|4(@=1=VHsRM8_ z=fH`5%%O2h7~8#$E~1da11a+>;bO63E{?-mtC0`8qn)kY)2Gi}dHnjF`%gai(vMzw z{f%F~^X?!1{NDQ?{^OsYeGW_W4<39g?ZfYz-}?D4e)*%9UxoHz5K=C0?VaupHk0Nc ztTxTlE*7HMMQ;X{=2x(Ncxlplz2>coGM=G7JT*5pv$*2bDk-E(1Kbjk0$RYbQH+=ETQcw|R~lJS0{;MWU&qZA>u z5Wt{{QlyNEd$L3siaL8gQwC}B!uQoyf5h(ZI7M;F-cVlx=F7}wM0D{1A5 zaK3}D+7CNd_!&3^b2Vt*^~^7XumKi9vVTBW;51aG^3&#lycNz zPK^g+M_EOTTk`lK9XE4tL+9Y6!gt18pwD?zPB5L~^>*`-^DTUduim zSVayX3Sdk=iqbwAy<&)_4A}jJ0t+z=&G>e4 z7%YL@e9=$LF{}arV~8b4>6E1M3lDg`3J5A?%9@wd{T1PEV^}C~SY<_m@*K4n>z7C? z5Vc=`vx28EZV4;1mSP*dY=*u_zhw7U1oQJ?;-bkE+N38efrlC3S;5VF?JO>n2Mk$- zLB+3;C{)Jp3wf*pW;Sz=pO|+efPMNlmK66RgD5pvrpLj@ji|O|PFUNGlhbi>7D45b z0hKttU~be~H_{$>y=rX)mEQf-P)Zl39u25uLxU@ZRjwx8%jN*3^e)3Peg9(8J(qOP zWc~AL?@ZD;0gkW!sl0#6+-S76NJ}xG(&%ibUAzITvH_KGP)WR6$EGi&j&(44v0Ac5 zw@F38%@FJIVqJv{ROWV5^#{$g&?L($jVjV5e%%d?$mpT?zS!5qjl&o?fltYY4T$Nl ziM>kpLr87lko~AZsS<(;Sz-}e7w6g~{w@P16ivYn0F*v9bsCZzlUr*Ew|-F{n)z?7 zU&{yly)=gnE(ha!V)^t{nUchB&0JgeHYhmWXi+3%GeI3Gp)`vlo~X(M5>{7&D=n8s z*2mz4KXIKIk1qN#&VgzPL`quZrt9#qBYL_vwde%9K$88R!usFjv@bOY@ z+@lG|ZE7p#+cF2q&(r7RJsG!f@ACgA?LGWsE35r~|A~7!C&@{#)ArueXZG}x-cIiw z4n-6NyCNu95Jf>mks=5Jmis?`pS3<~t>>AXgWli$`P$~SGnq^#At%}Ie46{uy(RIL zE{|GrqDJ_HI+1s zU0_K}3y)q-d9gcgik!EFqr^O!t;j)9~oe?mR*Uo8~VN~72AZ*T4# zpS^JX`mOsZ{jo-aR^dd2{!;-QOuU`tAaHjl5nFhvqlVu!=J6Rg`7#^_mE! z(4OPzGfT_sF*~{4?}mQlT~Dl6sE5i97E+*}v>@r8R)8AB{dWT0B{RAU}3tr#v^^)TOkpbtoK>eS}H9n!1cX(uGAhD1=)14mQ0>&%a+*8 zd~=t_o><$QC!Rm*m2n^0LzHNT7eiKtVmO_zAE(Ve_OA!PU(@YlD zi;he(j!a4&4t^lBJ`Uy-vHq@1qTIsF0IwG~giwTxUM!)o?`zDgLLv+oe{Is6LWXG! z=st~+S-%8+>O+h_QiU;ZJ~AMUrT&Yu^_-{Irx(}y6q4b*3W+U9tvJxCI_z-|$fGMY zf|F9+z3-Snh0-Z#A|^S7C?!eraWl_y3W+KXP8_8qt-PAP9H?X%PByX#&=>g%zJhA3 zY@RRZ9i?PNNMSh1vwe#|1)Go9s<)Ejwe_rTE*2pr=i9*eM2ub;b$p5M3;kU|b1|n- zf?-E%G)`>E-cbtUN~EB|+Dl#%HH;_~Vl661rq7N(7x^6PQIaOsOb85C zaO+pOezw#&S8kjyH!qi)*8nP23Z*=7M~$QN?x6GD(+wJYbgc4>qZBODH~RPLz1xme znuEIzR8mq@ZZ&#W>)p%E{7tuU^cLBSC%YG!#5hpSRw!+?;_!xh z@R2)o7;#fAPudX09w$KvRz=*2Cf&Crw$4}Q01QYgem{}xiEBYhzHS9vn36_-JjRFUu5~8}w0doavi1!Ut+5!*z+9P}b@0r-NbjDF1P@Zs0^Dy$hL0DhPz7$k~ z%n2I3^u({xi*nKpQYg9t=|JUtS6yMqtMs?emnscl3GoBx>6u%ERSN&hfJ*M);R?$& z0jNX{h$@Jth&D12P-&U2aw1;u!}LMI{wp3rrWS*o$-*fB559fBxDVzkBPCfBN%Z z{_^o>pM6g4!~gu}-#+{N!@qp|&L98$>)-tLl~;fHy&t^z?AM>abN9i8%QtrR&-I3T z_0}jZw>I(>^?Gsp@Z1EV_;JZFk%Gzj=>^9#xwuhF-LF`9y(Gn71c7*(Xv}cMuwxR; zp@_??P+!ki*K*a>TxBI&Udfb~Gi6?Kq?wc%oorX-AukvD!VD~%Xf{KlDGPan0lNQ+ zZ}-x4JA=of*r??Gd;u->#Jc1-?XF5Sudl6H2?;WqIZ-vE2%Sd_V0m28o|@AxWB!N$ z6?M zpqxMjnY@<~@K=AW8^&e2#9D&Q6P1RtK?{~OVkI*=cydNmpu9YP{ra4dpnRhHBtj;Z zr9s++^$OW7fjDDX8s7>nTRH`88!PzYEUUM++>Xj1_$o=cmgZF`14E-LiG1QKApxq@ zEf|i3ilXga1q`Hs^v;MBLtoEX1r?v6Uq&}qd1<4vlB=&3+&>fdPmCs6-4%a{Ac2lD z@0|GccM??wN-ww(jay@Ig@6Z3C8CxHs0?rpAq8R!;`Q#vN^7f>j@|t$Evo+loQPIK zqDl{5vZ4qqLm&!8Txxxz5$^9IBN?LnoUoVsCwObQmsH*3Y?T|^)CNYZLIHXxDoJHP zc@=<4;y{I%q)_Zqp5EPFY(q}T^NI*5Dp8$9m5RG|-^fxWF|Ot*NzaZiI}}tnLr;_v zW7tG8Dr?ylPN!H2n8Ot?jMRTIoMbpaFS+?iP|3;iV^MuvNswAeET$4PeR{W_p9edAn-aOEPbUtCXo zmn4+#eRtG)PcpPTQSU!Z0eUL?dWw8s#3~Q!y}R|^Ek`No@E*CphId-S+XO0|OO4)T z3eyiRH~Qz@(HvY%2NxMs9FxHMwNKRx-GgTTkPa&TI&Ah1q^Fn?_B_#(>Rb;?BCJd7 zG}`WP7su|R)?I)e604BcYfo8aPi8}~cu1hK6MzcFSa#F~W()3{2xh>dW8){<6}HKg zG7@dX?>6!|a`W0Kr8>!$W>ob43?~F6?Xm2o_=JhpCAWjjJ--Ti(I#RYOVK)D$Sg9? zVivEi)(VGQfu`fu7=>L}N|BGtAk<*zTgWPP3!kWffEWxe(``q^V)W+tYMSp03oXHe zq#9?JK}O1!97@z=U>D`Wis<9iTdW$Swm$#>5CBO; zK~yt5sm!#3Her2m5?ByZfquf(Ue-3YW6;vFx-ADPlC76X9DFhPGuqQp%L5*@w1%!p zAejUe4VLTyoVao8z|)S5a-yDC?{CA7qXQpq8LRbc;@m4Ll1m|-)FafBb-Rmr( zFI}Bm>2IHF^|vE}6N$M;!Y5y%sPNk3|1z3#!u_S7k_f0I6)pP`P`h`kj+nYa;NLgM_*4XP6SK5v%y2D*8 z&ELNJ;F)K?@$K*a@F%bQ{PkbI@%A77_`!!CV`<(UfBXE4k3Rb3Pk(yvx4(V!XFvbt z4_|!w8_$2|i6@`EdgJcl@#VqhKDQ5d9b>T9%a-O1QI;oW9A^Tf7cmtJDKksUYf-g1 zs%r0z%!7#L(DesASF*a;Rg&pwh9m{dwKCTw_QgUkj$|en-J!xQUUYuI|AXX(TkI6C% zdAFiE0xCo!3?%LZKCV?ok=1P3VaXccBx`U|TFI2$%fr#nA3mw8P6S{f#qSeN(V6L%lE(du!f<``jmFoy31)fG682Mm2~p8SFR#5TY{KdmE?_2SWE2tukqO_D$aVQd z^gpzsp;NeE!FsQvFqW|OD`yM?en1w5oL&4@^-%G{>N}c(p%0#&n$HKB{+x#&DIb$B zi#7|x$3>wL`Y0HeT=Dh9DxifMECqm&I?aMYGx`RTw2zP3fN=PkYkFRtT2@sopG%&* zl?#@_w{A2o8?f)^*f)Ji$Ue6HlX)uXs;w2$jkuL9cJk#Oht}wzHKb~nhhO`UN7!UF zEQUu-99ix@?*0@?X$V(1@NlqEP92;$VgaZO=!9hxha!|tp@2%C3%G0n~`cVbvJ=s)opIxbgRDG-m#1jWF zMTAw#T=2ymUj!;i8x6(o#+X6HqAA2GDyppUCjIIr(2Ki2lG;#f%x)aEt*h zYdot0N`co)3}9;+SiF={*r0^~{YG?BMFFVf<9wd8^28*(NuT4SN2BUgQDWjsn)E<*?Bl2e-0Q0!iwYjn>xv8K{Js(1FQ=}xt^ zOJ*>3f7RRWik3ZK9H!@6w#5U+HZXB~xhr)#cBqVtvnVMf6uz;Eu}C&Y)i@>3>WA)- zw6}fg17<4g$cWK3Nuwla2{Ufn#wa04W5*`TGP9#av<*|9*nj-UBNr&$7k$`948vJA9nh&vTg`LH|&KRqaJaR7vF!qV*b@q)~)t=-We~bVjs@Ul5 zEmLV|{1NUlnQ#fi`ZkR+Sy-%FkXvUtOn(;H`oYe*V!4hfdIlA5`YL>hqLTYJtYUJ? zL%0%v3bP6+Dpk#;JRFoaAQAW12}&{R7aEGcVhfM|b$UVVUb>?)wfKn4$+X`2QK0hJ z1gOvfM454jvY4G)&Lfesk}0}t-$D_g`R&p6@!{Dk*KXdUrTOQ-^TQv#^z&c5{<}AS z|EG7~`}pHesMqUX|MT~M{OglXKYRa!j~u%E>h(8XdifOxGfzMJ976M3duQAItxBWs zuGb-?!0QD>QA+W)u2bqsJ8{e|th-;>?x$f1wKiIZWu~b}E;YKMpJ+G)DG|1QbvOso zb_YwG-I0c)AQo6M3>C<}XC*cx zbzYb>$xwi#%J*q{>leGWVA;w<0xIrU&sTAfR8purI9c8(Eo~ItvACYN!@atkDeJ5J za*a-wHb5zfqmwmY2X#WC?My6%X)X>*y9vkPt*M!!<^C>#%W$qK`uLzy#Ks(gvYqZ zz7$C2aNTe`w_2KCD=%hhtNG@7lxB!kRE#Y{S>>7pLKgM#$xbpMeE#F?M&uk13Ux+k7|LeJZf`l|aRzOS#8+dP=9{+o|;{?|!&NHQuBz*(N=76+U=Vg{D;GB}A31hUwQg3X({n3CN8V38f&H zBI=3;SF-ED79_>xNlRg)SFV`V3CO_Kr5-RjigqrzLb-W8=N4m!H0zndIuQ}EN*>lP zYXi%{)mDVA!b$oXvkJPuwD`-hN_oU`$`*Hj?U(8Y9Hu`aP^q3xYUhfL%MMgZ>Ge|c z8o^2b$y)dEO8bsG=UmFy8iS|mz59*9A&ImT>L8|;g9l)US*ef>J zl(cAmPc}dr`UDt?FljGQ6QBj7(U#uX`_`lbWbA_@tXG=lJbYSuU@2H;yx=1;<)yoKrQ*!OlXa0s5KIm$FEFKP*yxJ1AK zx=8NDosSRwt3yvaNYc0ErGkotRoFkqiIm>fdHBToJLl8hwrVM$6kR)+JWk9$DyaPX z?yvkKLB)$IrJ9#h0#HdjlLVpp$q^OXQNi&Q)D7!^fwk(^F>~oZx*ZFdV4h3JlH$Dcq~qD3Ma@$G+FA>-mKK4myZ=gV`#H?X7Xmy=hS3 ze299zEHu)UEqO*1OxNeN2MnSO)e4v4BI`yLeG#jeoQv@cVVEL!c9AS2VHo(YvVap+ zaaj$BCq^1YLBo0U@@{VIwHIo%mNaR?~R+cs#kd*i(oiE~8FDi`PIg&WcrF*1$Pzhc*Y&@ zeUTZgDXqi=Ds|mxhb>BFhaB@s#$ElhxeL3EoCCq3ltRY}_j=J0RLvsifKM2 zVnHSf<&+5yVM7;KCC>$4g{XuwVtuQ5z=|TLkW`XTOeGr;sJQ1TPm)W?SYUB*0z{#G zn246?3$f!9eEJRgDlw=q#W|=O0#uk)c8j$`I>`EUq1?PwYF;X*S1YX><@83mNy{pZ zQtG`Y>pl1K0fWl3>G-SZ@Tqk8WIBGjIea1=K0)=w<0sO=?bh&idwjPwyxAIFZ;fuG zLn{7ijjpzamt{(Y)*g9ErN^6(Jo|`f3Ol|I_}ml~g^jkXrZAZtq8d2m{f+ME811^0 z59!lhq=eCX3_l&41liSXdl@IGb+!N)_4Zg2_Gk~Yt#{T*8xp^d>_eS^+>MVEQ z;vVPQyl(DC73GRwvK-rW!xcIMyNF2#NeV4N+oxa+@$oIvM zIpiD*i7hhHVxVG1HCvjuEQ%1wV9#7wouSW!xL0-Xk>*WoA>LcOpu>1LQOgzqhFP$5>thdvt*wp?NsI{0W{(ck>ALF!F|N zpX+a*i;Goh;&S{@m;jZ@Ir(6m{(rfn^6wRuxR{jcrP@i0m*!Hut?QTF)A#@Y5CBO; zK~&_SN=tZ)W>h9Y1#qHbi-(qzZ~`)!@{9GFs;9&}Z#eSI{A#qse|=xeRkLyGe)CnD zB&1Mi{`}P&w;zAEFsf9JjTKm7QUPboD2&wsg>pM3JSKfm|> zTW|mIm#_W$#g|`s{#)OD@~LO9-Mo8z?#g)Q$URQE-pwZsTAG*8dgUMvhj+K}sl$B_9xq$RM4q_FJ z(M#rd#XGVz%1oewL$?u28bJ}q6!I7vG}pZN#NbJ6p>_qEq6XIG4%`J?6j5b9F9pN` zEs5_wmQcGui`_o6?4=KKi0Rb5i8CUwXoN=XXCk&RDMjW13n&HdvaJ6DzqX$N80eat zCSOYWXa2}m7rk;qU)vIYMp=BFeKQzN=JOU=k*+YS3sVI~)e;a+&JR8Y4C6byCTqvE zRye_fDDcSM2zMmJ#sex|6|64IzE(6^;y&56GCyqs72b0Ms5k&9;q!8ztb;~O%`k`1 z?f9DDaSS}HR#@trMU%;goz7EQos2FAi@pSL7qOWkE7O#)jm+ zmA1nYdiT~Q8jZEWm;>}$Ps~lnJ>W_iAOzJ#v-?HtJOV}P-UCLKFiP0-Abq8eT}X6) z@hv@R0Aoc(`J@f)mq0~Dm2AOXi#H8X3W>$&>q;#3e4(1jmDj1!D~9iDEfcM+I~1W~ zZC$Iq!GWxuy~@&k9h=C$OE=cHDBr+(~JsCUlKL(rY{DS*eZ#+m6(c& zJ)aabThAx%VTq$aDRywYou zTz0s!Q>+~%)qTe*mGn}jb%jcb(<=^Cm{o{aYTbK|Qfl4%weDROQl4-J&8Ij}89kMb zh*H{{?&NNBaHBQ4-PwAgHM*4!9GuX>ta7C@zSbUHrTQA>Eg)lwUf2DnQeR;rjSUb9th-zJ0U2F{xsnr*|kR-G0+JXwNJyN$7 zOjC{uV6tL;W5l?!RTVPXYHCD*Oje{_jOac@ESs{gg1C#o$*v_jEN4>JJceAC`#lqA z4%?LIR$lsPFI7(2R;D^=&8KBoWz{WH`Ym*f56i za3y1xHUx?1ZueCcApR`;J@-fGCGt-FW>s5yC@S?U z0F=VF2b?ofAQ>O&hzfRHj%m3?0{7>-V*0w+=!`ir&jEMHDZzyNBm2Jo|A0z3_y|yW zIEw-~C2ab71glijVpvZcWK<^Gy~N_R_+O{z<#zRhS+$qnOPPjeOg0}+O?thcqO4{s znW*kA$}5dwtG7GcIyyMMbouISc)fn~;!8jK`D^fcef04soSFZZ`~N4Oe*VD+AN}DE ze|-JdZ@m1|SHJtcAAa>~-?(${!G%lLcMs0>hP(Ba`;pzs6sjv5@xn?@2U18#nT(?> z8mM^F7qiOD-0DWYiULqJShpi<(Bk?m_^{#rg1H<30~JLZx1gdhA@J>a1sJL)cJL54 zrMZ}AAJH<5Sct48Vrfx6G`$J=^<1LV!M$5qWZa8w-F6hIgiKuI>}p~oMRV{i1WW8# zFVv}qYoiFu7AM_{c0e29^OC))B8fXMc|=()f=T3HM482joJV+-nvOV6EZ2g%QvpuU z3(DA%^mbzF@?zie98!kuYFZcB<(~7NE<$dx0ejO=bP*+o_cDW)A8ZMnW7ckrj0&#l z?Omn=TbI%Ty)PTdGCmx__c`S=E!KV(`E3*LXUpSi{uG*mnH)}YtYU7hGQVC0il|d7 z3!UV2keQiQ`;)gw@zc}KE@J{ z_=<*e7SY@#EA>-0jUu38iFpp(Pp&$$ewag2g1l!{WF zOT9>ukdc!j));9ig{)c3Dk7Cs+MAWuW>M&Zb&_farNc4Vy*fNd&o@@Ph^D9x1G`s0 zF{hd2PewMwQc_kSWNHj`EV8E~Dr^a3N`WX;q#m##=2IA5_%Ah@V^QU#?GN`u5i>|*6vrW)vXe?;?g4X@lFipLdE%1{^?fC4L@Cjl)D63nh$$uS4!(uX0V*qNx#iW& z@`^h)EI_}ihelJ7OQAk4o_!QJnMkKdq(mhY*-MdtN{)+(Su@dF*cg^Cu*@R+k3|If z!6b5E+QmHwx-sIiFJhH!4yG?^D9%SU$10KgD)HBZfsGF`#kHfN`#m*?O70idR#M%K z%R6ypuSCUPmrC{Xl&Vh&SIW)HmDcq-fy(`A=T5bAv(~v)@7=5S@4KTpe7rfhPciz@ z{nq9a>G*DgqAAxLt2j_;_AWKL7n=P`DGxrTgG&xnQW90p(ms9nh(Lwwzta9O0`%3^ zZmo@Dr`Fnru2RK{Jhv5NR;AIgbi1FHm!S`fk#u&n6idh5x!cO%L`_!PV&6i}#3Cr| zO$CN+@+@(evG5GcSHTfFwb(bsfsKxo5jRAR*fa789|6cA)-S(pvh9&WQ!Z-0((t_~ zJXBtzDZ^(X|{rF<(8bnUe#Bu|2R^O9Lw2<)u&|sd_eU*|{vuPYgs_nq}WJ z$tcLBSUP2>VVfYG(j9w&g{fwrB@}lfu?jRC5oE?Zbw9RoNfM@nWXAkBj4Okk^X?tv zq%4#YlbrI15&AEsl>a-ZxPz1QrKEfUR8G_o>mfjr7S--$85Q+_1sN4-91cVC(~JM* z1N0~ID#3sw)fO+p>%{{pYcmTQvr9CPvb3J8WJ>G#D!g8e_GWLizk6`;+{GKW?mY3# zv)}m6_kKi6^RH8A{=N4<{N&TmzW9Qc=0E@9A0PkqlXw68zAnwb^v&nL^Z0{juHLxo z2xqu;*zAl;wN5UseOYKeM3hq}9ADEAQWh6ia-~|IhMDPCY%hX+T+Lb}p;YB|WZ!Wk z3bg`XRu?2=F%=|LWYt7e4i2TTkb=QPzx&P~G$+vF>wxn?xJ5yQqVqYeCze#b?!L3F zt3ie?ik(;uR}F}=maj3QIMPU%Mpz^v{75EUTSX`*{+3WPVJwkXscSW!UB*RN4CRE* zc^5)%TyYm_G!{X)vWmZC$7TmQZSept+a-!vlFdY9LXgeC?mH6Iw0T0hM(mK!55bIy%t>V76YJ(;-iT*pH@!G*6D4Y? zvJ}c`6<(%-rx%Q_Fec4s(e%Z;F0tQT(U^z!hS`99AZP?pehrF*b0U5<#qNX4eNMgS ze5f5<#JFOi4g(&(F{==wONe?00}0V&g5ea&&@bdBKDy4ZARnTek{pJaS9H;D66so) z%NSNksGYMkr&;-t$w&hjzl&1DNB1QGj3x4{NNCcTOC=Ds8bAssk9R z{1Vj##8L>0gCU`0bdx^AODukzej=2T?V5iR%Sksi)pEo_%D+sZ~fiOesS?#&KmkuIwh&ed_*doKI>;#roND zdWl)(QnhmfS(IAuPPKcp*1bic6vrpSCt90Nwl*K6qsN=WdyZAwn~$g5_mvKBwnjIb z{mb>v1!9#R!t@tfgGG-<_AO*I`+y)RLLh3D*QJz!C|4&2AEg=0XH;}YB>2C!hW13*UM1+N^nd*5Nn7i{+W z{O|wx>nDGE@4XM+`u#hvz5bh*UVi0U-~Rs7Uw!V@od@SGT-)45Xuek{Hr%hVC5oeD zW>55bJ)DxCUJQ-}cTCSN=c8Jqy;W|EWCTF)1K@$ge4h^YTN&#p%p~N3sE;U%!Z&)V zK@i2Z>iNAaRg#xbzAVZMpHw();vlDtd6pKP@T9#J9Al`_^7@=P;SrgQEztk~5CBO; zK~zRMPtnSS(6nT+Kpj#-ElF64d`N~F)V)WGUm8Hb|dX~=Z;2Yf$6S{1m z5Ue1H6bz&&nUI#UkprKYt|CoeSen;v;!LI>tB%r2oJ_WOX=t9+6@e2b7KN7J5N}wy z;Et%`PO^EfCSw&PM3gW|kFW}Z3NafsIu={pG(bmO>L-;^><*;rYkL%?uOF40=gaBE zV&k~jI9qOAc8F5#T&;GlJ1j{@PdPGi#Ny8DgL{p^o#yak=$IW-V zjvs6d%PeX!p`*+yX9qUxN1*#?|6 z#J%QNMMC^gU@UO2ITm>`l9@q^*NG}&b!|=9)qpOds8|t}=r9nAc%SjWW=~SG|uWJp0X~8#AwyBhY$$RFKZxm{UVyjf|rQPk(*3tgarHfZ?-M#pT7FbH-7u(JAb6m{HLFO_K$!3>+k>k*Jq!7@ewV}|M|DSd+TREd+qx_ zdg*J=J%8`(ZX_Wc41?(didcYuMknl{dIbJVP!2} zZ}jUakIl1{E7E~^NFp3K=@&F=0tc5+3Xev+gJLQ?DFCF2#g;7rdMGO47Nk%LN{5SJ z5t;(l6}YpEWNhu;-AA%9>id!ldR=5UC1={(%zT@c ztudk+SQG%@joGENi`g6-Zy8DW{+S#E6dDymz0E#^mb;RSq1?8Qg{-WrT<23 zi&7o{!9rRIp<+r&0O5Om6En{$zE$uz2`efEvx=cEOqb)OVwin~Bvs9=x&PKhC^O8f z_%sW;93O{MDuI5LpXNKC(f()zQJ@_ukT?#BW><^yea^SfEUG;WTPpF5i-dfH8_pu+8lwpcG> zOTv3loL9+4bz+r54Wf#Bl@KLY-pBz|%3zf>M$y;hy1(-48=hV!5Jj9_Nmf;tNlHob!K;2~C8z3&f(rVG8Bt=w6$TYja`M5B z3gnc0ML@+-3LQ=IfKk%`h3S)0zgQW$qg352)pm>Z{iL>EtRIw`$K~`wsd=tMpmNcH zO1*cx*1c8h-mLfUG=}$^B%a)F^lvu?x0|E8&Eeg8|7N3qqqTXzyYp0M`$2p2UTb_i z9o=+Cdwi?2d80kP(d=KUcN~qJZPGsdIre=WH+yHf`0IRYc)r;^thF)nxXY21Bg)ga zck9#uMw^edmb+ebSC9u3i0AovcT(e%bl~oPKoNOIBV8FTRIk^r2Nmi99$~tKF5G)Q z*B&SmI2UNqdS4bIwn_{$G-py*Fo2cI`h0_>ksUmKJpdJJ022_B%t^4p z!frIp!=TZ48xZ22n2b?&RNFDB2L;ESQ0*~=WVc~LhwZThdoR#T;Du{y>sL690&YfxA0zAUH zVX=(?Y;2955hcvG1PhPKEmGTsbpEmARnQm)hLJZRprX*ih08q(&7b9I$U`mrVrba{ zs6bAkbi3mQ_dnA~CKB^s3Mz&FAgV}D@d;5SQbmP9g{)r<37kx3PC{FkvPwIo6xh8& zx7b&76lMNI4e@_Z6Qc0#^L`^`dZKz*5QVC}*3K+!%q?XXRtw7;$y%??0f>{Jo=dm&e&}5C#il!Q_A(fKr?;||Yn^i@+!S0T!6a94Crl<1g-0ncWb1qqIW3tu^`yt$( zjK;7D_mqbXWgh)l-d@GjG@CialQMV{)DLa1ip(L#7CVSMc;=b#=>h>cSw5P&(aI1a5P0(#es^uk@xAdnJAP{Kq*jNgkFe7&{j;bmb^6-v)3zt zmZHo)ss}8Cyb3pf(ZQB8-c+gNx$G++)e3Q)=Tm}%h^5KyxMm?~#XMB+aD`UttJ{ej zJ4LE0J}Nbi>1dv-v@cgX*Q=dttgBqDc5l>rx9a`d_1>*o?pNbiuNHKI%W zjx-CSWte)v03d@@Sz#qVFQ1m)ePmdRdg8=q#?wGQImK zBRrFuSj+I3X7OT_GSiwh9e4EsyE=8Em106K^Z+SA*1mDa<}TCON_iyT9fF79E*Ht zvu_#d^x+NL3soi#mK?TZFLD=!as)PG39IOx{f=aN(&8npU#KS@AGE??{aB$z9AAfF z@mFsXk$SX#o$axlVxR&!CHF9>ctnZ+KZDANSrt)KIJh4JRLV)EVd0Y`44;(KfKo!2 zS3ob5_c2hhVaH(4ae4w^9`m3=VvDp7&#leOZ*T!|ZgDkQ*(hd6NJ-1}UaPk?-Z?%z zd-=-sJC8s3>T}=x_78qUq4_s{_tu}@eeW+Hef${*QXHmy`nS(N`0y`(c;`>A|K^QX zUj4=QzW?Iao_qezy$23&cJ|M8hr3l8NJ%$x<>j@){8DCiapMg2dYy=)1TL?c$@XC+ zQkJvjYPZaP0%V+kM@tS3&2vpK|4K%~uMWWb{D}tW`<$WY(qX)bqa(4n5;?MRqB#?UPnc5H3REAwlB>vc$p!<7@ClfNGbd`_Qa&jHHrUWr zvRC0hndN1=Ir?L8az-eHg^?u@PgFmNt;H*#lz7%X4j;lN`iE`)W454+T%|2X+K=RC z@_hYN5LM?t%&UWlJBWSeM2%IPR4Hf;SIO$O&a9L}@Q{a9)kS-{a(-)4qVu^^x)*5* ziM9jdVM{;}B<2y9w}L74HRT-1kbR5)Hjh{F(KVxzg>a!rP=InWr7Z%mEdg@*KBbNT z01yC4L_t(=A3LnwYbqcR4ZY}CiVVEuWZ=brtgPht)c|Ic!j;f;9e*kqbEJ2X2bVA4 zso4$vTr4YZGbsWpcrVpq{Emv+$;1jcg`FIL8L}u052vN^tPrX$5RNnegLxB~I?=ry zd8kvk+hRVje&ubn5$Av+cjw3Eq!nO#QL{0@5@q<=A(3LcG1IbZ8JTCyGAcZuBL9NK z`o-PcOhky6Hx7KrUiA~u z1tuLiPlC}#;ffNxTNM&g7-=L!4?!m4210=ofgB+pR2#$h1%DTXPdMv>T0$@n-s;|w zz@~zh5!!~*u0jGH-I7XqZMIzq_HeUtJ?r3vGAd~sPIu z?Ow0d-Gm8y44=v>1^KO9hJ+i;q`QIxzTk@ za^w(&HXr-vTf>X(k$cmHM(?QB-Xlawx7^DrzjUlprSDii(s9*+3eVDGLcR(hX>C?g z_q{!$JG5jh#imj0s@<001La{{}Uvn%eX~rl$9%bxKYyZvO40g{F+Vj zNYk<8d~_m4hFRLuj(7$dqA}WI*%~q<>e2{}pI_iD4;V2VyVO^!6YLY}v z_)}=1fEdYsK5UgT45i4OSvwMs*;oXe4!12^6EQfasb8fD>Y$;<1FCW!U|Dj;4R(;iVuUK+Rx3Lg1K zDk_i4)BkTX^ug?-doN1TQ*b{?;$lLe(qt_qJrR!o&qulG-gg32fLi6V@UA=kd?t^E)_WXCg_k*AO^wrmY z{oA+y@JA6+ur&YK=O2Ca*FU{Wq4}Tv{Fjbop8LiNk3V?k^3_}W#}|2New?OtK~@T8DpMlTO4i!*a8`KVfDPenpfiKf*{?a-Z7sC4Z{*uC-)CY4+gcGnLf_hjHAL{e+0c!AZiIlR^#p35h4U%1Yk7q^QYK zro6D>P$XF(*@T>3%p{IX+)0@^gwE&J%LEup(X1J_mSua9*`o+DVPYw&Dk6Vi#;&%M zt3nAOyH3s9p7$9|FQ~66Z+*VdIwUbK z1Cg9*VN({w5Ituf7tYyHHn&zKTv?2Rcc&ZOF-;;&9@!OH4hc1!6;W4=VNOd8R}la+ zk-UefvfwLj#mdD!xmd4POBe$ES$W@uaKc4a+fR#?OR#53SS4$u0j`rWE=hZf%7F)m zGBYEODZpUQSvLwNELF0#qAHE8qu76r>4`Xv1=&@;Fbf$Q(nLH7Dt(bJ$Mzz9|E!ke zb~|5`iZlKqali9V>EN9%^w;%RWEl8+pP7A+rXkQj)H$^(nv;M3jEU(YbE8o7V{2K zplU9ymo}niQtdk+snG@B7Q@LVS-inb%2}cm2PY(_T&=V&RN5Eoy&KKp-E?%%Axb*B z-`ReuHGZ7W@3uD`pFG{${c3;j*}>kk{oQ9ej#UQNcES8WbX{I|;3U_uo~MRSb|#kW`+HuHk=V?Es2^tEvQWG4P$nwq@s+)-z6=kf zg3K}}s2=9_yo-tJ3^!|_J}6+WSZ zRTSKa-4*5#^bz-H)e+V&G=K@HKu(FhoZ{0dj{ub~RaE{D^~6yCDn$a7Vue8^2**z( z=~MqQtSSz4l`yrUVfsl>vHVFm{)p<~)3d8nqM|rN$;~@Np{4oKMxp9{?Nyt@&R}@ef8Vl{lU{;{rauj_s?Fqy0v@U?r&F{{X((1p5vwYmHe#CIC@HXL=a_W zVk2d0W^rx3&}j53jUj$_N;1COkrjDVZ}DOY9M$krCs=i?3{dkWn=0B_Y#0N*1@w~F zO~&w;7&$>WUeoY7&C0uj;3TeZL^aY(9G2wD1SdHeIiVcak+bfC191A)@?psy(qG+MNBxe9CViJtyM`L^Iw#Uxc-xP zF>=YPvYH2sp~|lnjj}97wq}Ujg2V&R35MK=97P|pSBKbX@eWDlmku44<~QmNPM9|$ zR2TzPq>M|4JKzL!?>MWu6uYRMGOb1wk|c<2@E(gK>?1RSA+@4DE=k*C%!Cv9qQIy{ zN)#b}l6eb@`wO=;oTcGKKUGreEK=P=ufoK%K#}m1Hixz3}4f0bc6vzIX1L=p$=F13iu9q0{Dl3u}FJgv$V|!q`6NP1d@n~ zqT`Q7`}|OOiqRv)wU|epH~q=LfzvYASFDwmGnHJ@a$G?bNhwS+FwbmqR}v+Xs}!%_ z&omthx*tR0wP+#G+YzimC7{J+}b6Ujsed0%KU25NNANX0UeR{i|dNOO!N% z$&?PV_|{NN^+tIIYe1UQUy_aN!VkVz8G#h`n?n#R0+cLLMaYlu%kk&Dbz$9TN&{NJYbo~feN$~ zY^NlSPxxix{&QdilauttO21g{70dlnbzH2DVqz8dpL?g=pmBOS8pmbweVr9hX;Ott~o4ejqkTcge&c>C)%5jx5oFHgPZiG!OiygUT5nGhbY~xdyY(+gR6}$^?kL5 z*ZMnmyIZ%@{v|5@>R)K|&ekbRf80PurGJ*!9uKRn-DyBur|_AzM!1*6V6_@-^DgiT!D zVKp{+q;8MH?^5q!M}=}Ps%*$Mi{|4s!QvMiw}XcgP{I2OT*>V_bp;QC5EglamlRYQ z;RXwW^`tp$>rf*?{CnCU95#LF1wA^HO)UY*aBUaIMI0^c8Utud#YB>A3+M;ZhXis` z1|;mIU?b8r79U%OzR}&0xe`pZu*O1oFt8rpT?xJ;=r65eK`}trK9;s`*|fa+!CD%K zw-A5m`!XhJWB+z$WF_%Kpn`hhAWR=- zR6;F<67xLcxH>(zer6%V?ZbsdhbT1TSYaWhn|8NGTStdyFJHcT>)sPjf9)GD{NM*a ze&yAl|K^R~zVptXKK#qafBWon3e96_{=<*n`Qy89yz%C%Kl|nPfAHehzwyGo$Dg`% z<;L#8+1_Zko(|m8%|u}$1&FfB8!0QN{EVX=3)V!8cCWel)l#w6OsNUS{SwvI;I_ux zy8)}DxP?L~MfZz#P-<=#8{@b(j0jg?@oFJw4`Si5`Hr-BF_X~jN!&E+SKf9`l9t5c zsidnxh6kQQA&ZxTlbGv+3)R(pMc_n_rA&Eo!^6qks=KU-fhcqC)in#jN8BJJLk%pI zfHBadBpd=PVXnZT=lCy*6=5l=*3|R=eh>()#D0pnw@fxd;4HGCTJQ(k;R?BniH8q1p)+P*YVFt*VJTkG< zA}wATCFk2Qr2>ftV55y;R#Thk<8$H#fej`QpDp1BWP4aPs1MGXw+i=^VH2c8_|D)Q z6K5>k5-J{$GTi-7&*ji$B~4r^2q*~oIe8%^3(C8%+nkCd`fMTZoK|sWTB0%;d{@W^ zl_#UFusQL+@$<|;(7-z~oW#>!`6*~0ac)YDVN$ELm={F_tzx_z!_u6@1OP@s8u!gSzh0Tu^?Lp{QnC!a zmPQoR6_(cmi3C{`eQSP-RTOnmijmS$isVx0A6*R77Xm8lg+^TIk(|=ns&U_!SihQM zfQq~LE7gW{)Q6SEm~A$jWz-o{!PiJ`X$&h(dN1TsO0aLSWJT zjERNR7`ffHQ+f&KPZ(hOQdO)Chq3%+r-SV&REdyOk~GoSj!8#0iFLV&xXGo)9onQ% zBsWjiFG{D>nOhv6RI{E^IHtnZuQdtH$1?Q@N+CI=u+B~@*-=@~dkKZ|@nDs#pHgur z8#xRDCT-%4ybH$yVn8fe)O$~Z_zO~*Nrrw zvIWyu6{atUEn-!IH_Tnli)vAWX2RKbua@ZS318FGjH9OlzRIt$2m|XB^bkio6$d;> zwvfNe;74nMy9lfYp?qfZ{4b!N-c+BiMXgo&igVCI}g~+>BlkJ92)q2K) z1WssL+ivQ*i+;o%Wa5Km%w0eD2n9yc^LqwDZNLllX=G8txxRUY`wM zd$4}BMhq}~PB|8C21DtvG#Bebu`+Ym8Y!rZeRgFs$AVdvV~OKK7a}LvfrErd=HX?B z{y%`qe>hIEO4d;dV)X81?j)#ac{rW&W%e+wC;smsm*PPs1OV>! zV!Ic5y|VM{_2STc+5Kj6zu{`FQFpMjvw!aV1JN{T~id{?0&b0%Kk|Q-CRr^vH{QPigXhf{F%Z!wmPuHXRYToe)JG*k_ ziuPA|X=hr|W?Pn1#Au|8AKu<0HYCU?k=np~QI&R5spt`S zw9NVinPQgug~{t>;9=SNP^{3xDE)pkp|ks79-1k&<7Hp$yrh# z-Y_8((MiJOdOkSGBFbX(i+CJ&<6~#maeY4(1othYG8OEj>!0n#a&}tx8G~zRR43bN zr2Hq~!d$u#pPHA*xMW4<<$cv@mUuUp{8xy>Bt0(fPYI~xY{}k-`-1@%epN2V zT+(v9LA!XGGp?j#fC_q(++|<6P8A?U!jC}(T8c(iihhQrhu$y-m98ix+|Z>vlAKhL z;7XVC`VLeEY!BCb)>gc-=5fUx4kx&q7!AcbOK)5Cw5Af}vl73U{i_H;& zC{@kVJ5Z@KhYT>pDnJyzMz`!cp$mfPRlasdd_{sNErkT1OWCx53U05oFh$Rxk_wzC zfnWj>i}jn*MBMRKE>9*R#kiMa-E!H~eH9DRSfs?)3R@hd-jh0Gokr1Qe2`<&y#AI% zU*z1o;51&ctr-=*4>s>5>A^Oz&L&^wb54Vj^ka>+*b3w(6dRRdSj1q!V3ydkD7kQh%pQg(%U*)Yjol$F-rM2 zpu&@mUlLaN_n>0Al%V+QgrdTrQahPZsZKb%zMNGaaeU>mhnME379O&D@raZuex70rFy^J-(ex;(zToSXleeN-~Qo?FaPWpumARUzyIUAf9BBq=l}TU|9tU} ze|_@lXCHj{(I4J<_t(Gq-A`ZnnJvvzX#U`Qr@vKg^rBL8BVS%#i+E{%qT1^r3FQ%p za%y^EacQGb>oimM3z(MXxo)c|adv;SxB^&*O3ai+33ytUFYwqr-=R%2^N67M8}VrB z>5^SDi=eRETdFzRwt_;P!f}0_{avJ-7@1Heo|oVqnXE%UQSoHeBZ`p;A|l?w6@=i0 zNhJQE+6X-KO9h>(NM@uQsi>0|YWnhv9Q>--x)K|jnD+K9kwE}33M#r&fvZ3bpNNA? zXGSFXu$*}1gkfWDtvVxl2HsIw()(Kz*OGJ=pIM>hlQYXDsVauWi!}}O4x^aM*91!_ zMK)MC#DZ{qPRqo2vt_MfRxvyi;Dp+!&@^S1C!QaXcnk*bRY{OgjnwS=5WLRD8+FXc z=fxGeoX&?OjF{)l`eSD{PHTo9kTYdlCA#$it~;%&%aryGgDW88*nQlq_P*L`RX9DV zHfV!Z9Om^wG9s2y2I4hW zqe@Gp-xvivlzi`nBaiZ(42>?)U6d&JhA#;6EyZFd(;H_j>%syHJvUWLtldl0Ma!tH z!oC>UY6>3-r@|8UcX1Mo`@VdQ-ZFv8;(95Yv>XZ0ngn08afPe9+Smi1aMXC0-Cx^$ z*|&+dVy2M+8n?L7m{>()@m*N6L=~Z<8+&d+WMwF9BmOQZEmAj(#d;_zoJ9#Axrf#- z-e7T$Fv9dou{N}-cS+Khk-0}bF_)BZEf^zBiv#qcqKKkWU`2%kDh^aOawYeY5G6Tvfw>sl%j#ZkybB;{hK|3maEI%GJdxs5T6$(~iJ_V*P_p;I5!{#HG5&M`51yl%B zh8$|&W_m$67_U9j{v)@6Nx2qaN&bY-N1zK5N!mEyBn3vRz_@g43w={HRD`tw zHbZXrvhe!0-e=6&61d2G0hido3_OrrZ*PI18cG_fitvX>M|Ct5BZGlFjGUwVN@wHI z*(F;mJN0&;uCN!brknH)zg52_b~4zvdfTY_#hY+_Pj3pA=`}RJC)z^K@2CJ&KoJ;T zZ29WpeQEl_l{)i-S+G>QFss<^gzCaipxN1`sYjAl_EOEM2en5qw<{*XFo)yI5fpWp4f?fmHxhw zhU}<+ z0d2Oh*@WR{y3SU@!6mg^{Auh&W>SnpmaL~M;mh$zn|~vdO$=RRTZ0qWTBelHh~8=$ zelRE}nqVn&Ju&hRiRDl5__)Y6+Za7hdPjO3M>zBAwV4$G8*3S!TJ+%=3#;HFUQk5# zSRxe-L^iR76Wfw!&r&SG4-K*#umOJX6`t}l>hWoaGipV9jElC6J)oJKVkt&yg2hdj zv*?J3P7;iXygafL1nVV|q4!zzs*5KmZ91M(B?paOaAyTRt4IN<0ChqImi%e2kht?K z1e4PXKF}hCUB)wuk|)78vlba6Fe4KyaD@R~rW9H&yHUygSnmp`Fj|o6B1((a3PTBz z(R#|IV5)_q`C~kYSii^~)?>~Qsv#gDci9-i3;ziQkJEuq47W&#MO%zzV@k;oRR9?P z01yC4L_t)9>x{jkl0%rDRFpcaC^g<+uTw*DRLy8TvAuM}l8?wARp7mLcj8{nl0B?w zYmcm%(A@jl23{hdlIM*Sd3ALyv%0$BUaqWeEU#_=RIr_5|S9cSd0;m|7PYsW@V zff%l=8O*&pq+7yjVA*K_A(({12ft%evm!NFo!<_XmU?@#Z4977O2XL29L3Eg8VihG z+ln_6qlY?RFSnzjH?_SKh7-5k9Rc1o~MPalpaR;)M}&jdBc?1nm$xWDCgVCb?<71*(Y%}TqowRI_wP_q3zgGITLiFz~QLj2_B{Aop!hl7$h8tib4{*_cD9D@-zn zt)b>v^eF|tuYJy?9Cf#jC1mfbiYa05_^Q8swn}nJtkINsl2T46DwA!+3@W0)n8xzI z3D*BRP^rWEl>~}P@+eSAPVTEboKYcAS$xcPRHlMZ3T-&9@zVSTA<9Z&iDw)$QQiHP zLTG+_@7%cy*KXc<{HbTY@%*>G>+s_jzx>skZ~fuD_docUL-P(&+{?dy@~OLpLi4Zx z=H;Kf`kn9o@T*_@=B>L=oWFE^d+%&#up>+Jxe|N5vNq#5&2f~|CZzc1xe%%-?#5G7 z^DC>lPHUK^<09se%iQ13(xU#|S zvMdj+Y#`S$BWuMW9=3{wB-Lf>~#{H)P^O$~I z3^=lNE!dp#bo8> ze$0;lZO0@rfkzjmUhbS-UW1CPuOM^cu5SB9nVjz;aF6ZzGA-E;$(L11-Am~v<_#92 z6w05ph+(wR*lb|PrGQfS-P^MH$Wi+Q!;Wx#c|-xU@E?yPL*FdWJc!hPk-EaZFOg7k zMHs-Oy_drq@-nIC3RSGRZ;%Zvk6`_ZRDR`kd%mDsCA4bdID~7y!ltjh9CV)B(5?Ec zw|FI(Zp0`GCFzA>;=FmlWLYJ#GGgCK%&H3+y}0blBK2AKXoVs`MR0{@A9YQ|Q3}nc z$mV0hgO7>KKE~Aj<^Iz+A{~}$Tc!F=slM%4rQAHIq{l3$9Fwk+;^b_-cg3-aLzHU! zoWqzp(@Ud&wLQ8+lPLoSA=f&aw>#t84N_4qwgy)yJ>Na&4hEG=ErOPF_4a`~2u`|3 z&E64Nzj_BX<`bGy;UpDWzuY-}>(W7+kB(AW{e!f(-)L_)+OpvYP+|8Mbdqhm%6GT3 zx+qBu!jcrmFp(N~5poM_DwTA=5tOl(5tEQYonS3dSL)VEJn7MrxfRZ$uskxXw)H_+ zMUlsZTg=8MIl7fY4;$Z3DB=xc8bslP9-$>)0SmR8jwFtzj%=jpkjohG zgSOv#+dI%K#}A={--=Qmij z-?hMr07*xiyCh??X*v>;A=(?Z-SVZtH1gOT$!ZFAB;^UvX3JiOZ=q^nv5M`UkZ77g zjhKG89W6rGl#(>M=We*E(viVOe6ojNGYQReE~RbYq05r#V9(1Y0%Q8=``h)^Awu>T zSJ}0;03NERj|;O|OV{hQ>G(iW_#AQ3yvkAF{yI_ibrf+rg+QfnBAxPZKqa`npsp=% z75k5%^6$d*QJer&iYGv2A~dgKDdk5+Q_4+lC=N0zdadsjl~XfI4iBt-7)$fZ8^!f} z)%}_&HwHd5f9=lwC!YEGH@@}#AO85Kum0jUZ~X2L9Gd_1Z=X3t`Nu#1_1WiN{Dqe0 z-~H|H-g@!oA!vmBZi!9)fq zO;jOhIqidw-RU7dahX3R_~D$`yqk(#V&vG?fC z2__U}|0J@a+v%kV%|yz(7yxF())mbJo~Q}t(HdOKMq6DhDuBr!HLs2=Q84(A{n$vs z)jZRS?WG`Ca#~m9@vKhio5FWN;2!X!PdnTv(G8FVjifI?oGDm{MWZ$0+GAEXQnCsy zIi5?;%2uQLwbXkS4yt5zoH47}lB~)aIj8{~fH@g(*Y+)ZY9X=YEN6lyD>{de5+pq= zagO(5X$9g#jE}hA1RKO%@;!#K1kI($=Pm+N7>;KsBD}$%kZ%T) zf2#cNl#dxvREl#dr|@t}F<0!+Hhg`61UxxCXm^4@MJ)d57AY!izN%}CFq~KAbaEB@ zD(SdfAC~D6M(pFF1PeLAIPOy8^%Xe8kU3#i>BA=msRT=roJDCv3DHr=c5u>Gk))gG z!XwAx+pMMV_+zn!Dli6{HlHA5&!FOd(iWSAVv4j(p3^CUDB191bmDGQ&BbtIil`OS*WkmzY^P~wc&@;MZBzdAq_Z;ggIi%;g z<10~CK}5cYo5hw_Bqd%>juj&EADZ=WL^UoDMFvM^~DChd0Ny zHWDd^_0EB2RA`gFis6(LUax&-m0fm!Q7FZMN~5#Oq6#T0y70ISmzO_~q<1IHA>3bl zjAdZaoeQhz@FHsz9Jn8#r&zXAM3Ki~7a7O|!i$p6SV1UQmTU>V zFd?Wpjh_6_3oJ|{dNR`LWEF_o;~<%%84`mZr0R9mF`Ph=z)5G*HdVssDe8?p8hvza zzS8u$9OTa8$&nq%BWDNz!glX z@O%m%OcVwoOx+J z`J}OyD{E+8s)vKz{4z&ql%wdF|8E?p=l<)|?5U~w)zy5h(yh9m+9gV)uo*fnl- z7Ic0QJzdNs-q}SXi4&ShJgZhNx!kd|iRPy@TcYD6zAP&-oIpG9%t~Tiz!tg?kCwdZ zhv8M(NbnMsR#WaWi}EKWY2(Gavtw$?9RF1LRkZnbxU*o^3V^e zJk7v^JpE~Pc};6N-;@?nb=(F0GMVC_rOZf`aKT%x7A(^e!>J|Ksc^wiF+e+)$@V7g z0vZ+%Y6o@HgZCMCDaZ#33bx|O3-^RwtY6G+Iy89y2 zmk9q|wCPKOSVj9ZtyvC~u1~K>en|djMOH4_dx`ZA0a|d!J>6%NTL5hI--T<(PBWoN zUd21dxZrC^%81Vxr7)<>ua@RkVy2X~Qp$kUk|7OsyKA_X#Z-2fG)PNfEZM51TNS)Z zeOO~qsWbsFoB9XrZO1&OsL<`rO_X|ZE~SgL`(h1-uD0e_`bmuf`WTV4R$>dcNDZ;J z80#86EM9zj2{6<1pi)MZMd#|N=8Kk6#0gf!HFt_8;%uQRWnXz3PRVk-J^+;xvkGB} z9oc|e+!a43RSM(*<04?36Kv#JsUUk;#I7*Tu`sT1D5W5vf{Y5AzNCye>!%*A<13@_ z70##>%^OBolBI`oFCC@W@I2&{EWaZoit-7+L}Z2egwiQlwtf{zNU0&IQb-zP1LFoT z4yd4~xSdq`No61z6;@Q-OLtULhYb!<&Q?h_x#Z3rrPSKziBh_ko4qR?T5`N4inD3} z01yC4L_t*7>|d*QF4VghSWCIy=w5V3vwx*EywM(APX|}h!DaG&^)EDg=i8&J-OcMX zpVFt+uLh0NJMuZ=%C93LmflgLcZfVa!t`nXkXWU?YlxEaL9Ei*p`*9Q-`i4Ktmk96 zzgSKgHF#@<8D@x?MV!~$mK)L9V$}YMND?%6aZ#9+bk(Jy%df@Tt576>H?p_b30uE> zg%{p8s2G_Mi@%iigjwq1sX1JG-{(o$a9MYodnOs)`}4NcdZF9a1Ii zb2W&;p!EwG81|K^00O$$N4ni2?~-)K45BFv!(&8N1XQ%`iq#By0&2lBg~S&G!zzX) zd)nYzn4G~RlME(1U~Kq* z#{2HI_TKgONHYBI^;1tjHPzMC!6WJY_1bGKyN>nHi;#i{<8$?^>|dcuMcWlI1E$6m z{@$D|`~5J=!a$6cs;TP;=!`Kh5!H-^Z`h2hsE5w*Nj-5;yQsLkrjVV9aUfY&#Mg}GghgY zF&#hx_`!GN)j4ZI)E*LDcY52SnxS0?Qrgkh`Kpe7h3A?nV9_})y70r32JkT7f7KgN z{4+s4OB&a-&slehx43vWr5o-w&zC+Bbk#Fr*hj+L;6BqX9~s|i#Ft4h8z)=vF*)~y ztR$RGvT-c*erT3);f6Q3Tf==^3TdnMoQvGlp(2SXMRQhi9|>7WE>@W0;jhP47aPIS z!7BA~plmUA(ac*?R^Bv{!-@Rz$9Vu+RG;rFxN4v?gI|w6|>@N^bSTvl4rC}`)1^g;a8xpH| z>pW~KHR-$$fA@|8EijUp&;sMim=VT|Hbw&2m^w+7@$OP}(xn}RMtQ{*?Nos+5OH2f zuCDYkKQ`cFc_eIsQiv-u_l49i#(xzpo){cbXXhhuC4+ua#ZFl0SDjLTF6m62VliQt zqEG>LwhAt@s2ybs5n)@{-=>XTRg|hjNGYHD%1YMr7<*T|v7RR&%d3KyLGMZpE_v~& z&@>MtO4St=VVJTGeOc$D#1oeTrKGK-+D+smLOFf-NUMER)DNr8-CAoO3B^0r#0*VYD4Xn}{2Ecf{9cX0ySU?fXdOam0UgO7lyHEFapX!Vi zRC<%gdy~gRLfL()KYg-0c{G4ZZ}KR*9mDZ^0xE$|#+O~$t^%b5KIx7xdXw!{Wav@Fc1=!*SqDoVQY=P%?M|8Tw^)09+k>`9 z9sL>42~Iok`7U#$X#AAbBYJd|KnUd;tC?5xiIzFq`5J*G#(MeMo|b(NFQ!*Sm;T%) zry4V*16?Nok`q%@PDi^J@Mc^py13hZcgEeGdq2iL@pX)JuTw@9V~*=0AL5$&K-W2q zAERA%!0WV?^)TjfTj*dp!Ce<-6ghQM4ZSozC*~wZI`SX^o!7#sRapIBH`Z$;u^HuU z%1by(rmC*^uI0zPP*OfUuWGSWVs@mQGxdw?5v6;fQ*wHN*)0~i5JIj-bj5@ZeD4C? zk^W*%`jLOe+gDWiA*JAi$>H64vs)HWNV7Cc=uU}@C=U@;DiNh5Gjm@Lchdh`MI}vR zP;pjySOfhvQ0d+WDv_e1vtEmvR8az=sE|@_hIM)Pr!M@@*gd>FyYuwpPks0!&wlDN z|NTENeC6xk{?7Mb`pM6J{hL?*_~*aA{yJv8*fjswzXfjj#mm3^-uHj>;#a@^{Ad5? zW6wVKqg0(HoIR~c#v=DvC$jlR){sVKExIH%GGSkgr-SopCY4&r#fTt{Ji_;(Z) zD`Gp*sjyzzN*e>HY?T_DrTSK-xm9j%6zgl-wbkuw9jCCu0I=q*<~{4^FaYO9G(VU_a&NAsZDy1(M>hdEud|!GWQDuj=esGSH?nLiyb}ei^`A9YnAmNJ{+@lTywVQ zLZ7oQ$WMQ0N7fErY6L@hR&FXZ!AW)8t` zwG6y174*dzkYFS%&jXmt-v^C#tu!CSs$9nI6|P@L*A+<%3byAkt88 zu~{$9YxUV!Z?1X)7#a7f_UU7(wsy_IGT;oDFb}G7DW^!kibQ*L%3*EY&@<&yvOj`2 zAS-V{8tkNXkqe-%NIvg&sqQvfG+K@>b+hyYmE0^N;s;pYHEI)egsp_n7h38eCCD>F+!) zZS>NbBHfN)l}ANOk@}Q#7*w!L!U?CXK_H5gjzCL5lyilXkr0cp$}sSWvWiT94U|cA zpv&Tk8$FF7<~p9!Q@GcZirClTDaOMCcY`!@Gl@A;Z0Jjqi=nZocZCyNeLH`+A8k6^ zk#bZ_EyO^th`##oA7kG&GO6 z6J114vz?CBk3{*rX3wxBWm?HKc-Mxz^1R^DvGz7{yb3Cc@&o3n2AW@dtTq)>W>l<* zQoQ;WAQm1d^@zJPoM`um>L`qAkG|nn+nw);*)&{ zhgM-3^+;;W@kzWN7pHVB{j6u!6nCDODaD#nCJ)}ClkHM^{0#cc1*yignybrCQ^lFD|COl;z-ejRDvBePy6q_7|bLW-K^TfOOMdUka7*qz7T z|Naks;*+2H?B~Dm!dJictsng8rC?5O3WwkoqqtO+?ZclUEb(qt#0`D zxIU^N=CZ;s>gqlcEk|suix47ilM==c%5H*pLwrOj@Y#11RH|Kqib9UeRre%S)6Dnb zBN9F-)#;U&2rptulqPIxq}2c+GC;CY@^mAUL^uzVgNH_`GI0oBV0(3mJxafaUa+b} zn{}B<-12&OrJ7uUNtHu3C`}}6YS($Kl{A749TnrrlrqdnV2X;(k8x$`mK9Sfv>txW zDo1WQheU{CgrnjKx4iSwss?EtaaeMka8U7*M8A0CIiZK7>)gLA8l<#$CZ{_Cslc({w@Dje(5^X*4M3wl?I zejTj)i+)XV4h!k&S8<1lO{0~3ER9MFui#+dCi^*iFaEOmSh=P|la>7Bd9|mPA~Mlp zMEY2qC2pz!Qi?>6vik2;`4M0UB;)RXErMhzq$)odY&lzeUs1J7+5gm7BV_5Oib%8o zsV&M4@jQ;6$Ex^J!vA+-3AUS794WpOOn==p{|Q+okjZT;Afl6=a>~+1Eg;J3c6~eP zW&tXsASLia0EUTgfmJx=RR^fRVX~h9^gs*0&wYVaq-90&zNUddnrL-ol?nuzy*dmk zH7RhEi&Pa?gjg(+7|78vGO6_>E{uIC&5>gXNFyMPiY--Kup!A{6Xy*pPU&-B{^CXS za0=AnsF)h@VeaQO@$x3s#yD(}dRz~iOf+HZC!(&1Wu=5TuPF5^Lvcz*>wLk2eKF;g z(v*_7loUnu*hXNnBg!_!790I47wLl!7c68ntEl=>t_27Q$ElX63&rZ^jY-6vf^%dm z8O9VR& zB^@&{nRXgwDtXOad0{|^JvYr`1}yi}lVrS@HmHhQaDvEZ*`BNGK4^QPyq zUHSg5DF9$0sxGwe$Ayn^q_V+}R!I)@fwJRXe0T47XJrSTke=6=#Dy?3amK7tvCfvs zsr%yBQD4sYLTgumfM?ocGmGW%xR|SenRY9d0 zfy#9tQ5!v2<$k55t7erLRM0fP&FJBk&1AhGrund-ud-?W@Z$XH$tRxv&__T1-1DFN z;#Xe$#&^E=!=L`_<=?*Y`#=BXZ*RQ$*4yv=>mTp@>y0=6{^!5G_R8;G{n^i7{@(Y0 z_?54IdHpNn&vmJ7o@1IA1-+We8ujRg`3Oet#Ui- zWNE)#=@!f3AGR*Pkjv`&FDU(>6_ZU6H{6wxFyCNe@rf@{NCZourGRP(Hl