Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
d4dc70e
init
matthewtrepte Nov 8, 2025
d70b0d8
initial impl
matthewtrepte Nov 11, 2025
d5fcc05
add scene data provider abstraction layer
matthewtrepte Nov 12, 2025
a1e8ec2
bug fix
matthewtrepte Nov 12, 2025
c927485
clean
matthewtrepte Nov 12, 2025
1b24ba5
wip
matthewtrepte Nov 13, 2025
b67e00e
add rerun viz
matthewtrepte Nov 15, 2025
30116c0
clean
matthewtrepte Nov 15, 2025
95d9429
clean
matthewtrepte Nov 15, 2025
f1ca9db
try new newton
matthewtrepte Nov 19, 2025
a8c9d07
wip
matthewtrepte Nov 19, 2025
7ee435f
adding feats
matthewtrepte Nov 19, 2025
011c373
wip
matthewtrepte Nov 19, 2025
c0a3b1e
cleaning up
matthewtrepte Nov 19, 2025
6f4ff19
clean
matthewtrepte Nov 19, 2025
3d154aa
add OV ui auto enabling to live plots
matthewtrepte Nov 19, 2025
0f8dda3
fb draft
matthewtrepte Nov 21, 2025
e4aceab
wip
matthewtrepte Nov 21, 2025
0f99eaa
clean
matthewtrepte Nov 21, 2025
a5f8256
prepare for merge;
matthewtrepte Nov 21, 2025
ecb66af
clean before review
matthewtrepte Nov 21, 2025
7562067
default to no viz
matthewtrepte Nov 21, 2025
52ae2f3
Update simulation_cfg.py
matthewtrepte Nov 21, 2025
17aa1e5
Update simulation_cfg.py
matthewtrepte Nov 21, 2025
9b55035
Adds --visualizer flag and updates behavior with --headless; also add…
kellyguo11 Nov 26, 2025
2c511b0
updates numpy to >=2
kellyguo11 Nov 26, 2025
3f77749
Remove carb settings and make SimulationApp optional in AppLauncher
kellyguo11 Nov 26, 2025
e0a16ff
adds new settings manager
kellyguo11 Nov 26, 2025
b76aeea
Renames carb settings
kellyguo11 Nov 27, 2025
9080acb
Merge branch 'dev/newton' of github.com:isaac-sim/IsaacLab into newto…
kellyguo11 Nov 27, 2025
6ad47b0
Update source/isaaclab/isaaclab/visualizers/rerun_visualizer_cfg.py
kellyguo11 Nov 27, 2025
89f621d
Update source/isaaclab/isaaclab/sim/simulation_cfg.py
kellyguo11 Nov 27, 2025
8ec7a12
Update source/isaaclab/isaaclab/visualizers/rerun_visualizer.py
kellyguo11 Nov 27, 2025
ec34aa6
Update source/isaaclab/isaaclab/visualizers/ov_visualizer.py
kellyguo11 Nov 27, 2025
3c5c27b
Merge branch 'newton/visualizers' of github.com:kellyguo11/IsaacLab-p…
kellyguo11 Nov 27, 2025
6345e9c
Merge branch 'dev/newton' of github.com:isaac-sim/IsaacLab into decou…
kellyguo11 Dec 1, 2025
70a686c
Merge branch 'dev/newton' of github.com:isaac-sim/IsaacLab into decou…
kellyguo11 Dec 1, 2025
5de0d38
Merge branch 'dev/newton' of github.com:isaac-sim/IsaacLab into decou…
kellyguo11 Dec 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions source/isaaclab/isaaclab/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
* Ability to launch the simulation app with different configurations
* Run tests with the simulation app
* Settings manager for storing configuration in both Omniverse and standalone modes
"""

from .app_launcher import AppLauncher # noqa: F401, F403
from .settings_manager import SettingsManager, get_settings_manager, initialize_carb_settings # noqa: F401, F403
233 changes: 171 additions & 62 deletions source/isaaclab/isaaclab/app/app_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,20 @@
import sys
from typing import Any, Literal

import flatdict

# Import the settings manager for non-Omniverse mode
from .settings_manager import get_settings_manager, initialize_carb_settings

# Conditionally import SimulationApp only when needed (for omniverse visualizer)
# This allows running Isaac Lab with Rerun/Newton visualizers without the full Omniverse stack
_SIMULATION_APP_AVAILABLE = False
_SimulationApp = None

with contextlib.suppress(ModuleNotFoundError):
import isaacsim # noqa: F401

from isaacsim import SimulationApp
from isaacsim import SimulationApp as _SimulationApp
_SIMULATION_APP_AVAILABLE = True

# import logger
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -127,37 +137,66 @@ def __init__(self, launcher_args: argparse.Namespace | dict | None = None, **kwa
# Integrate env-vars and input keyword args into simulation app config
self._config_resolution(launcher_args)

# Internal: Override SimulationApp._start_app method to apply patches after app has started.
self.__patch_simulation_start_app(launcher_args)

# Create SimulationApp, passing the resolved self._config to it for initialization
self._create_app()
# Load IsaacSim extensions
self._load_extensions()
# Hide the stop button in the toolbar
self._hide_stop_button()
# Set settings from the given rendering mode
self._set_rendering_mode_settings(launcher_args)
# Set animation recording settings
self._set_animation_recording_settings(launcher_args)

# Hide play button callback if the timeline is stopped
import omni.timeline

self._hide_play_button_callback = (
omni.timeline.get_timeline_interface()
.get_timeline_event_stream()
.create_subscription_to_pop_by_type(
int(omni.timeline.TimelineEventType.STOP), lambda e: self._hide_play_button(True)
# Determine if SimulationApp (Omniverse) is needed
# Only launch SimulationApp if:
# 1. Omniverse visualizer is explicitly requested OR
# 2. No specific visualizers are requested but livestreaming is enabled OR
# 3. No specific visualizers are requested and GUI is needed (not headless)
self._omniverse_required = self._check_if_omniverse_required()

# Get the global settings manager
self._settings_manager = get_settings_manager()

if self._omniverse_required:
if not _SIMULATION_APP_AVAILABLE:
raise RuntimeError(
"SimulationApp is required for the requested configuration but isaacsim module is not available. "
"Please ensure Isaac Sim is properly installed."
)

print("[INFO][AppLauncher]: Omniverse mode - Launching SimulationApp...")

# Internal: Override SimulationApp._start_app method to apply patches after app has started.
self.__patch_simulation_start_app(launcher_args)

# Create SimulationApp, passing the resolved self._config to it for initialization
self._create_app()

# Initialize carb.settings integration in the settings manager
initialize_carb_settings()

# Load IsaacSim extensions
self._load_extensions()
# Hide the stop button in the toolbar
self._hide_stop_button()
# Set settings from the given rendering mode
self._set_rendering_mode_settings(launcher_args)
# Set animation recording settings
self._set_animation_recording_settings(launcher_args)

# Hide play button callback if the timeline is stopped
import omni.timeline

self._hide_play_button_callback = (
omni.timeline.get_timeline_interface()
.get_timeline_event_stream()
.create_subscription_to_pop_by_type(
int(omni.timeline.TimelineEventType.STOP), lambda e: self._hide_play_button(True)
)
)
)
self._unhide_play_button_callback = (
omni.timeline.get_timeline_interface()
.get_timeline_event_stream()
.create_subscription_to_pop_by_type(
int(omni.timeline.TimelineEventType.PLAY), lambda e: self._hide_play_button(False)
self._unhide_play_button_callback = (
omni.timeline.get_timeline_interface()
.get_timeline_event_stream()
.create_subscription_to_pop_by_type(
int(omni.timeline.TimelineEventType.PLAY), lambda e: self._hide_play_button(False)
)
)
)
else:
print("[INFO][AppLauncher]: Standalone mode - Running without SimulationApp (Rerun/Newton visualizers only)...")
self._app = None
# Store settings in the standalone settings manager
self._store_settings_standalone()

# Set up signal handlers for graceful shutdown
# -- during explicit `kill` commands
signal.signal(signal.SIGTERM, self._abort_signal_handle_callback)
Expand All @@ -170,12 +209,30 @@ def __init__(self, launcher_args: argparse.Namespace | dict | None = None, **kwa
"""

@property
def app(self) -> SimulationApp:
"""The launched SimulationApp."""
if self._app is not None:
return self._app
def app(self) -> Any:
"""The launched SimulationApp (or None if running in standalone mode).
Returns:
SimulationApp instance if Omniverse mode is active, None if running in standalone mode.
"""
if self._omniverse_required:
if self._app is not None:
return self._app
else:
raise RuntimeError("The `AppLauncher.app` member cannot be retrieved until the class is initialized.")
else:
raise RuntimeError("The `AppLauncher.app` member cannot be retrieved until the class is initialized.")
# Standalone mode - no SimulationApp
return None

@property
def visualizer(self) -> list[str] | None:
"""The visualizer backend(s) to use.
Returns:
List of visualizer backend names (e.g., ["rerun", "newton"]) or None if no visualizers specified.
Empty list means no visualizers should be initialized.
"""
return self._visualizer

@property
def visualizer(self) -> list[str] | None:
Expand Down Expand Up @@ -512,6 +569,56 @@ def _check_argparser_config_params(config: dict) -> None:
# Print out values which will be used
print(f"[INFO][AppLauncher]: The argument '{key}' will be used to configure the SimulationApp.")

def _check_if_omniverse_required(self) -> bool:
"""Check if SimulationApp (Omniverse) needs to be launched.
TODO: this will also need to check RTX renderer and kit app renderer in the future.
Returns:
True if SimulationApp is required, False if can run in standalone mode.
"""
# Omniverse is required if:
# 1. Omniverse visualizer is explicitly requested
# 2. Livestreaming is enabled (requires Omniverse)
# 3. Cameras are enabled (requires Omniverse for rendering)

# Omniverse visualizer explicitly requested
if self._visualizer is not None and "omniverse" in self._visualizer:
return True

# Livestreaming requires Omniverse
if self._livestream >= 1:
return True

# Cameras enabled requires Omniverse for RTX rendering
if self._enable_cameras:
return True

# Otherwise, we can run in standalone mode (headless with rerun/newton, or just headless)
# If no visualizer is specified and not headless, we still assume headless mode (standalone)
return False

def _store_settings_standalone(self):
"""Store settings in the standalone settings manager (non-Omniverse mode).
This replicates the settings that would normally be stored via carb.settings.
"""
# Store render settings
self._settings_manager.set_bool("/isaaclab/render/offscreen", self._offscreen_render)
self._settings_manager.set_bool("/isaaclab/render/active_viewport", getattr(self, "_render_viewport", False))
self._settings_manager.set_bool("/isaaclab/render/rtx_sensors", False)

# Store visualizer settings
if self._visualizer is not None:
self._settings_manager.set_string("/isaaclab/visualizer", ",".join(self._visualizer))
else:
self._settings_manager.set_string("/isaaclab/visualizer", "")

print(f"[INFO][AppLauncher]: Standalone settings stored:")
print(f" - visualizer: {self._visualizer}")
print(f" - offscreen_render: {self._offscreen_render}")
print(f" - headless: {self._headless}")

def _config_resolution(self, launcher_args: dict):
"""Resolve the input arguments and environment variables.
Expand Down Expand Up @@ -880,7 +987,7 @@ def _create_app(self):
if "--verbose" not in sys.argv and "--info" not in sys.argv:
sys.stdout = open(os.devnull, "w") # noqa: SIM115
# launch simulation app
self._app = SimulationApp(self._sim_app_config, experience=self._sim_experience_file)
self._app = _SimulationApp(self._sim_app_config, experience=self._sim_experience_file)
# enable sys stdout and stderr
sys.stdout = sys.__stdout__

Expand All @@ -904,41 +1011,41 @@ def _rendering_enabled(self) -> bool:

def _load_extensions(self):
"""Load correct extensions based on AppLauncher's resolved config member variables."""
# These have to be loaded after SimulationApp is initialized
import carb

# Retrieve carb settings for modification
carb_settings_iface = carb.settings.get_settings()

# set carb setting to indicate Isaac Lab's offscreen_render pipeline should be enabled
# These have to be loaded after SimulationApp is initialized (if in Omniverse mode)
# In standalone mode, we just store settings

# Store settings using the settings manager (works in both Omniverse and standalone mode)
# set setting to indicate Isaac Lab's offscreen_render pipeline should be enabled
# this flag is used by the SimulationContext class to enable the offscreen_render pipeline
# when the render() method is called.
carb_settings_iface.set_bool("/isaaclab/render/offscreen", self._offscreen_render)
self._settings_manager.set_bool("/isaaclab/render/offscreen", self._offscreen_render)

# set carb setting to indicate Isaac Lab's render_viewport pipeline should be enabled
# set setting to indicate Isaac Lab's render_viewport pipeline should be enabled
# this flag is used by the SimulationContext class to enable the render_viewport pipeline
# when the render() method is called.
carb_settings_iface.set_bool("/isaaclab/render/active_viewport", self._render_viewport)
self._settings_manager.set_bool("/isaaclab/render/active_viewport", self._render_viewport)

# set carb setting to indicate no RTX sensors are used
# set setting to indicate no RTX sensors are used
# this flag is set to True when an RTX-rendering related sensor is created
# for example: the `Camera` sensor class
carb_settings_iface.set_bool("/isaaclab/render/rtx_sensors", False)
self._settings_manager.set_bool("/isaaclab/render/rtx_sensors", False)

# set carb setting to store the visualizer backend(s) specified via command-line
# set setting to store the visualizer backend(s) specified via command-line
# this allows SimulationContext to filter visualizers based on the --visualizer flag
# Store as comma-separated string for carb settings compatibility
# Store as comma-separated string for settings compatibility
if self._visualizer is not None:
carb_settings_iface.set_string("/isaaclab/visualizer", ",".join(self._visualizer))
self._settings_manager.set_string("/isaaclab/visualizer", ",".join(self._visualizer))
else:
carb_settings_iface.set_string("/isaaclab/visualizer", "")
self._settings_manager.set_string("/isaaclab/visualizer", "")

# set fabric update flag to disable updating transforms when rendering is disabled
carb_settings_iface.set_bool("/physics/fabricUpdateTransformations", self._rendering_enabled())
# Only set Omniverse-specific settings if running in Omniverse mode
if self._settings_manager.is_omniverse_mode:
# set fabric update flag to disable updating transforms when rendering is disabled
self._settings_manager.set_bool("/physics/fabricUpdateTransformations", self._rendering_enabled())

# in theory, this should ensure that dt is consistent across time stepping, but this is not the case
# for now, we use the custom loop runner from Isaac Sim to achieve this
carb_settings_iface.set_bool("/app/player/useFixedTimeStepping", False)
# in theory, this should ensure that dt is consistent across time stepping, but this is not the case
# for now, we use the custom loop runner from Isaac Sim to achieve this
self._settings_manager.set_bool("/app/player/useFixedTimeStepping", False)

def _hide_stop_button(self):
"""Hide the stop button in the toolbar.
Expand Down Expand Up @@ -1003,8 +1110,9 @@ def _set_animation_recording_settings(self, launcher_args: dict) -> None:

def _interrupt_signal_handle_callback(self, signal, frame):
"""Handle the interrupt signal from the keyboard."""
# close the app
self._app.close()
# close the app (if running in Omniverse mode)
if self._app is not None:
self._app.close()
# raise the error for keyboard interrupt
raise KeyboardInterrupt

Expand All @@ -1027,8 +1135,9 @@ def _hide_play_button(self, flag):

def _abort_signal_handle_callback(self, signal, frame):
"""Handle the abort/segmentation/kill signals."""
# close the app
self._app.close()
# close the app (if running in Omniverse mode)
if self._app is not None:
self._app.close()

def __patch_simulation_start_app(self, launcher_args: dict):
if not launcher_args.get("enable_pinocchio", False):
Expand Down
Loading
Loading