- Parallelize asset prefetching (max 4 workers) with retry logic and on-screen error banner, replacing silent failures and cryptic WASM out-of-bounds crashes with explicit missing-file errors.
- Support binary Uint8Array/ArrayBuffer in C++ asset registry via typed_memory_view, preventing Embind string coercion from corrupting binary shader packages into ASCII text.
- Automatically glob .mat files in CMake instead of maintaining a manual list.
- Add missing reflection materials to prefetch arrays.
- Disable caching for index.html in web server so template/script updates apply on normal page reload without stale cache issues, the file is small so caching had a negligible upside.
- Add null resource and payload validation in ObjectManager::LoadMaterial before constructing Filament materials.
PiperOrigin-RevId: 963364509
Change-Id: I7de79df6a5cede4e54683415ee193f1c5a665286
This change refactors the Python API to use the term "plugins" instead of "handlers" for classes containing decorated handler methods. The term handler is still used for an annotated method of a plugin class that handles a specific message type. Also improved some documentation.
PiperOrigin-RevId: 962150099
Change-Id: I34a8cc410cd784b088605490cfa3baccea5c9e71
Startblock:
* // Put other blockers before this line to avoid churning.
* has lgtm
* is approved
* and then
* all comments are resolved
* and then
PiperOrigin-RevId: 961854282
Change-Id: I9d323a8ef9f36ffe69cc62d449849eac82dc6858
This change introduces parallel chunked downloading for large model files (.mjb) directly into WASM linear memory, enables model loading without full page reloads and reduces memory overhead.
Key changes:
- Implement a chunked model endpoint in the Python web server to support range requests.
- Add parallel chunked fetching in the frontend with retry logic and a single-fetch fallback.
- Increase initial WASM memory to 3 GB to accommodate large models and prevent heap fragmentation.
- Support in-place model reloading in the C++ client, including texture cache invalidation when the Filament context is recreated.
- Display a model download progress bar and model parsing/loading banner to the UI.
- Fixes model drag and drop (caused by typo in sessionId, corrected to session_id).
PiperOrigin-RevId: 960249236
Change-Id: Icac89e6a4ca099882aaf9b111744c1b6ab0cc6c1
This change removes the ViewerMode enum and instead infers whether to launch the Web Viewer or Native Viewer based on the graphics mode (gfx) setting. Specifically, setting gfx to "web" or "webgl" will now launch the Web Viewer (in future "webgpu" would also launch the Web Viewer).
Additionally, this unifies command-line flags across studio scripts and samples by replacing the --mjcf flag with --model (with positional argument fallback) and standardizing absl flags usage. --model is a better name since formats like .mjz and .mjb are also supported by this argument.
PiperOrigin-RevId: 959058940
Change-Id: If4e0ace664ddc6e45cb681d168679148e8c4901d
This change introduces a get_frame method to the viewer protocol, replacing the previous is_running check. This allows the viewer loop to skip rendering and lifecycle events when no active frame is ready (for example, when no browser is connected to the web viewer).
Additionally, this change:
- Adds a shutdown hook to ViewerHandle to wait for the viewer to cleanly release resources before exiting.
- Implements an ExitEvent handler in the viewer protocol to stop the run loop.
- Adds an http_port configuration option for the web viewer.
- Prevents double-closing in the viewer.
PiperOrigin-RevId: 954603868
Change-Id: Iaba7d131dc94191de51150464514dd10fa7834b0
Each pybind extension module holds its own copy of the ImGui/ImPlot globals (extension modules are loaded RTLD_LOCAL), so the viewer that owns the contexts must share the pointers explicitly. Without this, client plotting GUIs and user plotting scripts (e.g. the implot sample) crash on null context pointers.
PiperOrigin-RevId: 951917287
Change-Id: I639a173c2ae9f1af4feaf57aff7b5c81854b7393
This example demonstrates how to use Studio's `extra_geoms` feature to render semi-transparent, time-delayed copies of the model's geoms, creating a "ghost" effect.
PiperOrigin-RevId: 943672439
Change-Id: I8ae640672945b55dd25dd3d444f52d0b2fa7f0f8
ViewerApp is simplified, interacting with the endpoint and handlers through the Viewer instance. This change makes it possible to write simulation viewers without using the UI/UX provided by ViewerApp (which should be renamed StudioApp)
PiperOrigin-RevId: 943642660
Change-Id: Id8eb3d4e6a27ceda93833367fec4699fa6bc838e
When `from __future__ import annotations` is active or forward references are used as strings, type annotations are not directly class objects. This change uses `get_type_hints` to resolve these string annotations to their actual types, ensuring correct validation of message handler signatures.
PiperOrigin-RevId: 942608060
Change-Id: Ic2b214ee85e835616cc3a18ec2699eba6deebb6a
This change replaces fixed callback protocols (e.g., ViewerGuiHook, ViewerUpdateHook, SimEventHandler) with a general-purpose, priority-based message and event handling mechanism.
Key changes:
- Handler decorator and registry: Introduced the `@messages.handler(priority=...)` decorator and `HandlerRegistry` (`handler_registry.py`). Methods marked as handlers are automatically discovered and dispatched by priority (CRITICAL, USER, LIBRARY, INTERNAL) or method resolution order.
- Local lifecycle events: Added `ViewerAppInitEvent`, `BuildGuiEvent`, and `UpdateEvent` to `messages.py`. Custom GUI rendering and per-frame update logic can now be implemented as standard event handlers without needing separate interface protocols.
- Streamlined launch and app APIs: Replaced individual hook and handler arguments in `launch_passive`, `ViewerApp`, and `ViewerHandle` with unified `viewer_handlers` and `sim_handlers` lists.
- Module restructuring: Extracted simulation-side message handling and `ViewerHandle` from `sim_app.py` into a dedicated `viewer_handle.py` module, removing `sim_app.py`.
- Sample updates: Migrated existing examples (such as `implot.py`) to use the new handler pattern and lifecycle events.
PiperOrigin-RevId: 941729322
Change-Id: I93e7c0edf0a13a8dc854f9e2083451f7c25a1825
Deleted async examples since they add noise and are not particularly useful
PiperOrigin-RevId: 941117641
Change-Id: I4f621801f652e0b410a7acf905cda58143dc3fb0
Introduce a new header `gil.h` defining `MutexLockIfGilDisabled` to support thread-safety in both standard and free-threaded CPython builds.
Protect critical shared states and registries:
- Guard global Python callback pointers in `callbacks.cc` using a mutex. Move `gil_scoped_acquire` into local blocks around refcount modifications to prevent `longjmp` from bypassing destructors.
- Protect raw pointer maps in `structs_wrappers.cc` with static mutexes.
- Replace TOCTOU race in `mjcb_time` initialization with thread-safe `std::call_once`.
- Add synchronization to lazy indexer array cache initialization in `indexers.cc` and `indexer_xmacro.h`.
- Protect vector mutations in `StructListBase::PopulateUpTo` in `structs.h` with a mutex.
- Revert unnecessary atomic changes to threadpool counters.
- Declare free-threading compatibility by passing `pybind11::mod_gil_not_used()` to all extension modules.
Fixes#3259Fixes#3256Fixes#2978
PiperOrigin-RevId: 941101502
Change-Id: Iec4ce58afcbc75d4b0be6a9a21fc8a47854242e3
- `sim_app.py` provides a SimEventHook and a Handle class for the simulation process to interact with the viewer, including sending state snapshots and processing viewer events.
- `viewer_app.py` implements the main viewer application logic, handling user input, building the Dear ImGui GUI, and managing communication with the simulation process. It also includes hooks for custom viewer behavior.
PiperOrigin-RevId: 940994701
Change-Id: I82786872cd885b15454843b3a4d1160ff0b4be2b
The ViewerEndpoint can now send Snapshot messages to the SimEndpoint. The SimEndpoint has a new method, `get_viewer_snapshots`, to retrieve these. Thsi channel will be used for UI state needed for the simulation (e.g., `MjOption` and `StepControl`).
PiperOrigin-RevId: 940981799
Change-Id: Ib9bd6ad848cd5479bc49492a34fddd82569cb09f
StepControl methods SetSpeed, SetNoiseParameters, and SetPauseState now return a boolean indicating whether the state was actually changed. The GUI functions NoiseGui and StepControlGui also return a boolean reflecting if any parameters were modified. Noise parameters are now directly managed by StepControl rather than being stored in UxState. The StepControlEvent message has been expanded to include all step control parameters. Keyboard event handling has been slightly refactored to better align with event-based updates.
PiperOrigin-RevId: 940656342
Change-Id: I3701623a2fdcfc4c084b5db0ae19048669040de4
- `messages.py`: Defines base classes for `Message`, `Snapshot`, and `Event`, along with `SnapshotChannel` and `EventChannel` protocols. It also includes concrete message types for state, reset, model, perturbation, pause state, and exit events.
- `endpoints.py`: Provides `ViewerEndpoint` and `SimEndpoint` classes to manage message routing between the simulation and the viewer via the defined channels. A `make_endpoints` function is included to create both endpoints.
PiperOrigin-RevId: 939659912
Change-Id: I61a52a7d25431bc4ed38a184813882145eb421d1
Introduced a ViewerConfig dataclass to encapsulate common viewer window parameters. The NativeViewer now accepts a ViewerConfig and no longer requires a model at initialization. The Viewer protocol has been updated to include a close method (renamed from stop) and an upload_image method. Added checks for None models/data in studio event handling.
PiperOrigin-RevId: 939631378
Change-Id: Ibc07d0e775efa0d79ce5552ee52102e943a9b290
The `StepControl.advance` method now checks if the provided `model` or `data` Python objects are `None` before attempting to cast them to `MjModelWrapper` or `MjDataWrapper`, passing `nullptr` to the underlying C++ function if they are `None`.
This fixes an oversight in the python bindings, C++ code already supported null models.
PiperOrigin-RevId: 937243996
Change-Id: Ie67b8603517e7390fb54bfefbfed79ccf7b2bc5e