diff --git a/docs/source/overview/core-concepts/visualization.rst b/docs/source/overview/core-concepts/visualization.rst index 088687b511d9..5e9ec8a4945c 100644 --- a/docs/source/overview/core-concepts/visualization.rst +++ b/docs/source/overview/core-concepts/visualization.rst @@ -758,6 +758,15 @@ the num of environments can be overwritten and decreased using ``--num_envs``: The FPS control in the Rerun visualizer UI may not affect the visualization frame rate in all configurations. +**Rerun Visualizer Play/Pause Timeline Controls** + +The native Play/Pause timeline controls in the Rerun visualizer UI do not work while visualizing a live +simulation or training run. They are hidden by default, but Rerun's dock panel UI can still be used to reveal +them; when revealed, clicking them has no effect. Use Isaac Lab's own **Pause Rendering** / **Reset Episode** +controls (ImGui sidebar, under **IsaacLab Controls**) to control the live view instead. The timeline controls +are only meaningful when replaying a saved ``.rrd`` recording (see above). + + **Newton Contact Visualization** Newton's native ``Show Contacts`` view can show all contacts from the Newton physics contact buffer. When running diff --git a/source/isaaclab_visualizers/changelog.d/fix-newton-gl-pause-rendering-space-sync.rst b/source/isaaclab_visualizers/changelog.d/fix-newton-gl-pause-rendering-space-sync.rst new file mode 100644 index 000000000000..9980e4ac52e0 --- /dev/null +++ b/source/isaaclab_visualizers/changelog.d/fix-newton-gl-pause-rendering-space-sync.rst @@ -0,0 +1,9 @@ +Fixed +^^^^^ + +* Fixed the Newton GL visualizer's "Pause Rendering" button not reflecting the paused + state after pressing :kbd:`Space`. Both controls now toggle the same underlying flag, so + the button label and :meth:`~isaaclab_visualizers.newton.newton_visualizer.NewtonViewerGL.is_rendering_paused` + stay in sync regardless of whether rendering was paused via the button or the keyboard shortcut. + Also clarified the on-screen control hint from "Space - Pause/Resume" to "Space - Pause/Resume + Rendering". diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py index c5430cb2832d..8fb2643cf373 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py @@ -467,7 +467,7 @@ def _render_left_panel(_g=gui): imgui.text("Ctrl + Middle Click - Dolly") imgui.text("Scroll - Dolly") imgui.text("Ctrl + Scroll - FOV zoom") - imgui.text("Space - Pause/Resume") + imgui.text("Space - Pause/Resume Rendering") imgui.text(". - Step one frame (when paused)") imgui.text("H - Toggle UI") imgui.text("F - Frame camera around model") @@ -487,11 +487,13 @@ def _render_training_controls(self, imgui): # Pause/Resume Rendering is not exposed on RTX: stopping end_frame() would # freeze the imgui, and OVRTX naturally converges when the scene is paused. + # ``self._paused`` (not a separately tracked flag) is the single source of truth here + # because the Newton viewer's own Space key handler toggles it directly, bypassing this + # button; mirroring a separate flag would let the button state drift out of sync with Space. if not isinstance(self, NewtonViewerRTX): - rendering_label = "Resume Rendering" if self._paused_rendering else "Pause Rendering" + rendering_label = "Resume Rendering" if self._paused else "Pause Rendering" if imgui.button(rendering_label): - self._paused_rendering = not self._paused_rendering - self._paused = self._paused_rendering + self._paused = not self._paused if imgui.button("Reset Episode"): self._reset_requested = True @@ -733,7 +735,6 @@ def __init__(self, *args, metadata: dict | None = None, update_frequency: int = """ super().__init__(*args, **kwargs) self._paused_training = False - self._paused_rendering = False self._reset_requested = False self._metadata = metadata or {} self._update_frequency = update_frequency @@ -763,8 +764,12 @@ def is_training_paused(self) -> bool: return self._paused_training def is_rendering_paused(self) -> bool: - """Return whether rendering is paused by viewer controls.""" - return self._paused_rendering + """Return whether rendering is paused by viewer controls. + + Mirrors ``self._paused`` directly since the Newton viewer's Space key handler toggles it + in-place, outside the Isaac Lab "Pause Rendering" button. + """ + return self._paused def on_key_press(self, symbol, modifiers): """Forward key presses unless UI is currently capturing input.""" diff --git a/source/isaaclab_visualizers/test/test_newton_adapter.py b/source/isaaclab_visualizers/test/test_newton_adapter.py index 209a9aadc1f1..0611350a173c 100644 --- a/source/isaaclab_visualizers/test/test_newton_adapter.py +++ b/source/isaaclab_visualizers/test/test_newton_adapter.py @@ -292,6 +292,54 @@ def test_newton_viewer_camera_speed_setter_validates(monkeypatch): viewer.camera_speed = -1.0 +class _FakeTrainingControlsImgui: + """Minimal imgui double that drives ``_render_training_controls`` by label.""" + + def __init__(self, clicked_label: str | None = None): + self._clicked_label = clicked_label + + def button(self, label): + return label == self._clicked_label + + def text(self, _text): + pass + + 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 + + +def test_newton_gl_viewer_rendering_pause_state_stays_in_sync_with_space_key(): + """Space toggles ``_paused`` directly (Newton's own key handler); the "Pause Rendering" + button and ``is_rendering_paused()`` must reflect that instead of a separately tracked flag, + or the UI desyncs from the actual paused state that gates rendering. + """ + viewer = NewtonViewerGL.__new__(NewtonViewerGL) + viewer._paused = False + viewer._paused_training = False + viewer._reset_requested = False + viewer._update_frequency = 1 + + assert viewer.is_rendering_paused() is False + + # Simulate Newton's own Space key handler (newton/_src/viewer/viewer_gui.py), which + # toggles ``_paused`` directly and bypasses the Isaac Lab "Pause Rendering" button. + viewer._paused = not viewer._paused + + assert viewer.is_rendering_paused() is True + viewer._render_training_controls(_FakeTrainingControlsImgui()) # must not raise: no click + + # The button must read the post-Space state and toggle it back correctly. + resume_click = _FakeTrainingControlsImgui(clicked_label="Resume Rendering") + viewer._render_training_controls(resume_click) + assert viewer.is_rendering_paused() is False + + def test_newton_viewer_particle_color_override(monkeypatch): from newton.viewer import ViewerGL