Changelog

This page contains the summary of changes made throughout different releases of the MuJoCo-rs crate.

Versioning

This project uses semantic versioning. This means that any incompatible changes increase the major version (X.y.z). This also includes breaking changes that MuJoCo itself introduced, thus even an update of MuJoCo alone can increase the major version.

7.1.0 (MuJoCo 3.17.0)

Error handling

New features and improvements

Batch API new

  • Added MjBatch new (with its methods) and MjBatchConfig new. The former can be used for batching multiple simulation instances across multiple-threads through a unified API.

  • Added MjBatchRecordBuilder new, the builder of the output buffer of a state that MjBatch::rollout records after each step.

Viewer UI

  • The RGBA section and the headlight colors of the MjViewer UI now use color pickers instead of numeric fields, so colors can be chosen visually.

  • The contact override “Sol Imp” and “Friction” fields and the free camera elevation now accept only the values that MuJoCo applies.

  • The joint and actuator windows now show the unit of each value, when the viewer can derive it from the model.

  • Added a sensor window that shows the value of each sensor component, with its unit when the viewer can derive it. A value with a natural range (for example a quaternion component) also shows a bar.

  • The joint and actuator windows now align their values in a grid with separators, as the sensor window does.

  • The quaternion components of a free joint now use sliders, as those of a ball joint do. The joint window no longer shows the rotation angle of a limited ball joint, and it no longer normalizes the quaternion after an edit. MuJoCo normalizes the quaternion in qpos at the next step.

  • The actuator, joint, sensor and equality windows now have a search bar. The window shows only the elements whose name starts with the search text.

  • Added a state window that shows the values of the state inside MjData.

  • When a close of the viewer’s OS window is requested, an exit-confirmation modal (dialog), will be opened. Another exit request, while the modal is already open, will close the viewer immediately (well, the running flag will be falsified; the actual close happens after the MjViewer struct instance has been dropped).

Viewer

Build

  • The build script now gives the absolute path of the MuJoCo library directory to the build scripts of dependent crates, in the DEP_MUJOCO_LIB_DIR environment variable. For this, the crate now declares links = "mujoco", thus Cargo permits only one crate with this links value in a dependency graph.

New examples

Bug fixes

  • The viewer no longer panics on a drag after a ctrl-double-click over the sky. Additionally, the selection is now not cleared when double-clicking over the sky whilst ctrl or alt key is pressed.

  • The viewer UI panel no longer changes a model or visualization option that is outside the range of its field (for example “LS Iter” set to 0). The range now limits only the values that the user enters.

  • The “Iterations” field of the viewer UI now accepts 0.

  • The “Frustum” field now accepts values above 1.

  • The built-in windows and the camera tracking dialog no longer share their state with a user egui window or dialog that has the same title or ID.

  • Two elements whose names are equal in a joint or actuator window (for example a joint named Joint 3 and the unnamed joint 3) no longer open and close together.

Other changes

  • The actuator_inputs example is now Control windows. Now, the example also shows the sensor window and the equality window.

  • The crates now point to the LICENSE file instead of the MIT OR Apache-2.0 SPDX expression. The file adds a reservation of rights for text and data mining and machine learning on top of the MIT or Apache license. Read it before you use the crates.

7.0.0 (MuJoCo 3.17.0)

Breaking changes

Deprecations

  • Deprecated the add_* and add_*_with_class element adders of MjsFrame. Instead, add elements to the same body as the MjsFrame and call FrameChild::with_frame.

Error handling

New features and improvements

Model editing

Rendering

Simulation

New examples

  • Threaded C++ viewer — an extended example of the original C++ viewer example, which shows multi-threaded use.

Bug fixes

6.1.0 (MuJoCo 3.12.0)

Deprecations

Error handling

New features and improvements

Model editing

MjModel

MjData

Asset cache

  • Added wrappers around MuJoCo’s asset cache, which keeps assets loaded from a file or a virtual file system across recompilations, to prevent reprocessing:

    • MjCache new — the zero-sized handle, with internal mutability (MuJoCo has a global mutex). The prelude exports it.

Virtual file system

Utility functions

Bug fixes

Renderer

  • Offscreen rendering using EGL now falls back to other supported devices when the first device fails.

6.0.1 (MuJoCo 3.12.0)

New features and improvements

Bug fixes

  • Fixed #276 where 3D viewer crashed with models containing a flex.

  • The 3D viewer no longer raises a GL_INVALID_OPERATION on every frame, which egui reported in a debug build. The viewer now takes the color buffer of its window from glutin instead of MuJoCo’s own detection.

Other changes

6.0.0 (MuJoCo 3.12.0)

Breaking changes

Mutable body iteration is restricted to a tree walk

The items deprecated in 3.0.0 are removed

Minimum-supported Rust version (MSRV) raised to 1.95

  • egui 0.36 requires Rust 1.95, so the MSRV rose from 1.88 to 1.95. This also applies to any program that extends the 3D viewer’s UI.

egui upgraded to 0.36

UI callbacks receive an egui::Ui and the shared state

The signature-mismatch error variants are renamed

MuJoCo FFI upgraded to 3.12.0

  • MuJoCo-rs now builds against MuJoCo 3.12.0 FFI, which rolls up the upstream 3.10.0, 3.11.0 and 3.12.0 releases. This directly affects all the wrappers, separate changelog will not be provided. See MuJoCo’s changelog for this release: https://mujoco.readthedocs.io/en/3.12.0/changelog.html

MjvCamera::move_ no longer takes a scene

Stricter MjrContext accessor types

Unsafe mutation of MjModel actuator_gaintype

Unsafe geom creation on MjvScene

Renamed DcMotorConfig input field

  • The public field DcMotorConfig::input_mode is now DcMotorConfig::ctrlspec and the builder with_input_mode is now with_ctrlspec, following the renamed mjs_setToDCMotor parameter. The value is now a bitmask of MjtCtrlInput values instead of a single mode selector. MuJoCo 3.12.0 also moved the dcmotor controller gains from voltage space to torque space, so the same controller array now gives a different force. The old velocity mode’s integrated-velocity term is gone.

Relaxed MjData body_awake view mutability

  • The body views now hold awake as a PointerViewMut. Code that wrote through as_mut_slice must drop the call and the surrounding unsafe.

SpecItem::default returns an Option

Changed MjrContext set_buffer parameter type

Control-history methods take an actuator index

Redundant dimension parameters removed

MjData and the visual APIs bound the model handle by ModelType

  • The model-handle generic M is now bound by the unsafe marker traits ModelType new and ModelTypeMut new, instead of Deref<Target = MjModel> and DerefMut<Target = MjModel>. Every standard handle implements the markers, so only code that spells the bound or defines its own handle needs an edit, see Migrating to 6.0.0.

Three MjModel setters that drive a C index became unsafe

Drawing an MjvFigure became unsafe

Sixteen MjData arena accessors became unsafe

Two model view fields moved to the read-only list

Element handles are opaque types

  • Every Mjs* element handle is now an opaque, zero-sized struct instead of an alias for the matching mujoco_c struct; MjsBody and MjsDefault are examples. It exposes its FFI struct through ffi for reading and unsafe fn ffi_mut for writing, so code that hands a handle to a mujoco_c function needs an edit, see Migrating to 6.0.0. std::mem::swap on two handles used to exchange the contents of two elements; it now exchanges nothing.

  • MjSpec lost with_compiler, because a by-value builder cannot take an opaque handle. Reach the compiler options through MjSpec::compiler_mut, see Migrating to 6.0.0.

MjSpec is no longer Send

  • A model that attaches another model keeps the child specification shared between a spec and every clone of it, behind a reference count that MuJoCo does not synchronize.

MjsWrap lost side_site_mut

MjsPlugin no longer stands for the plugin that an element embeds

delete moved from SpecItem to SpecObject

  • SpecObject::delete new stays unsafe and is no longer deprecated. A default class and a tendon wrap are no SpecObject, so a deletion that MuJoCo cannot carry out is now a compile error instead of an error value. See Migrating to 6.0.0.

New dependencies

  • Added log as a mandatory dependency.

Deprecations

New features and improvements

Model and scene compatibility

Viewer improvements

  • The viewer’s UI side-panel now also opens and closes by a drag of its inner edge to and from the left screen-edge, so the panel no longer needs the keyboard. The X key still toggles it. The panel now slides with an animation when it opens or closes.

  • The “Joint” window now lists every joint, and each joint is a collapsible section like an actuator in the “Actuator” window. The window listed a slide joint and a hinge joint only before.

  • Each component of the “Joint” window now writes qpos, so the window moves the model. The window was read-only before. A limited joint gets the joint limit as its slider range. Without a limit, a slide joint spans -1 to 1 and a hinge joint spans -pi to pi, as in MuJoCo’s own viewer. A quaternion component spans -1 to 1, and the window normalizes the quaternion after an edit. A free joint holds no natural range, so each of its components takes a numeric input instead of a slider.

  • A limited ball joint now shows its rotation angle against the limit, because MuJoCo limits the angle of a ball joint and not one quaternion component. Every component of a ball joint reported “no limit” before, even when the joint was limited.

  • The “Actuator” window now holds one collapsible section for each actuator, with one slider for each control input of the actuator. A multi-input actuator labels each slider with the input name that MuJoCo reports for the actuator type, for example pos, vel and ff for a pid actuator. An actuator without control inputs shows “no inputs”. The window showed one flat slider for each control before, labelled <name>[<index>].

  • A defined ctrlrange now sets the range of an actuator slider, even when MuJoCo does not clamp the control to it. The slider spanned -1 to 1 unless actuator_ctrllimited was set before.

  • A press on the scene now takes the selection away from every viewer window.

Unified logging API

New compilation-warning accessors

New MjData methods

RNE into a caller buffer

  • Added MjData::rne_into new and MjData::try_rne_into new, wrapping mj_rne to write the inverse-dynamics force into a caller buffer. try_rne_into returns Err(MjDataError::BufferTooSmall) when result holds fewer than nv elements.

Cloneable renderer builder

Procedural flex creation

  • MjsBody:

    • add_flexcomp new (and the fallible try_add_flexcomp), wrapping mjs_makeFlex to procedurally create a flex with auto-generated bodies, joints, and optional equality constraints (the programmatic equivalent of the flexcomp element). The generation is configured through the new MjFlexcompConfig new builder.

New type aliases

Actuator input names

New servo actuator constructors

Relaxed mutability on fields that cannot cause a memory fault

New examples

  • Actuator inputs (now Control windows since MuJoCo-rs 7.1.0) — shows the actuator controls of the 3D viewer for every input flavour. The model also holds a limited ball joint, an unlimited ball joint and a free joint, for the sliders of the “Joint” window.

  • Logging — shows the three ways to receive a MuJoCo message: the default handler with its MjLogConfig, the logging hook next to env_logger (with the target that carries the topic), and a custom handler that reads the MjLogMessage.

Bug fixes

Safety

  • MjData’s MjData::set_state now rejects a history payload whose ring-buffer cursor falls outside 0..nsample, and leaves the history buffer unchanged. It reports the new MjDataError::InvalidHistoryCursor new.

  • The matrix helpers of mujoco_rs::wrappers::fun::utility now reject a dimension above i32::MAX and a size product that overflows, instead of passing a wrapped size to MuJoCo.

  • A view of a MjData field that MuJoCo allocates in the arena is now empty until the stage that allocates it, for every element id. Only efcadr on MjTendonDataView is affected.

Viewer

  • The viewer runs a forward step on the data it builds for a new model, so the first frame after a model load or swap no longer draws every geom collapsed at the origin.

  • The viewer no longer clamps ctrl to the range of its slider. While the “Actuator” window was open, it wrote the clamped control back on every frame, so a program that set a control outside the slider range lost that value.

  • MjViewer::render no longer rebuilds the window and the OpenGL context when it is called after the window closed.

  • The viewer can no longer panic on a double click that arrives in the same frame as a model swap through MjViewer::sync_data. The click is dropped while the scene still follows the previous model.

  • The viewer’s side panel and its windows can no longer panic, or write to the wrong actuator, joint or equality, when a model swap through sync_data arrives in the same frame.

  • A model swap no longer discards the change that a program makes to the new model’s opt, vis or stat before the next ViewerSharedState::sync_model.

  • The viewer no longer applies a pending camera tracking selection to a body of the previous model after a model swap. A selection that the “Track” dialog left open held a body id that addresses a different body in the new model, so the camera followed that one instead.

Model and data pairing

  • Closed a set of out-of-bounds accesses reachable from safe code in MjData::swap_model, MjData::try_swap_model, MjData::copy_to, MjData::copy_visual_to, MjData::copy_state_from_data and in every Info view method. All of them compared the model signature alone. The MjData methods now test MjModel::is_compatible_with_model, which also compares the per-element address tables; an Info view method compares the model layout it cached.

  • The viewer and the renderer no longer test the signature either. MjViewer::sync_data and MjViewer::sync_model test model compatibility, so a model that needs different buffers triggers a reload rather than a copy into the wrong sizes. The viewer’s asset-upload methods test the mesh, texture and heightfield memory alone, so they accept a model whose mjData sizes differ.

Ray casting

  • MjData::ray no longer ends the process for a direction shorter than mjMINVAL. It now reports no intersection, which is what MjData::multi_ray reports for the same direction.

  • multi_ray and MjData::try_multi_ray no longer report geom 0 for such a direction; the returned id is now None.

Visualization

  • Closed an out-of-bounds read reachable from safe code in MjvCamera::frame for a tracking camera: frame now validates the id against the model and panics on an out-of-range one.

  • Closed an out-of-bounds read reachable from safe code in MjvPerturb::start: start now validates flexselect against the model and panics on an out-of-range id.

  • Closed an out-of-bounds read reachable from safe code in MjvScene::render: render now panics when the context holds fewer skins than the scene.

  • Closed a heap buffer overflow reachable from safe code in MjvScene::update and MjvScene::find_selection: both now test the model with MjvScene::is_compatible_with_model instead of the signature, and panic when the model in the given MjData needs larger scene buffers.

  • MjvScene::update and MjvScene::find_selection now accept a model that mj_loadModel returned. mj_saveModel writes no compilation signature, so the previous signature test refused the saved copy of the very model the scene was created for.

  • MjvScene::create_geom now writes every field that mjv_initGeom leaves unwritten over uninitialized memory. category becomes mjCAT_DECOR, and a mjCAT_DECOR geom casts no shadow.

  • MjvScene::new now clears the geom, flex and skin buffers that mjv_makeScene allocates but never writes. A read before the first render returned uninitialized memory; it now returns zeros.

  • MjvFigure::set_at now treats a negative linepnt entry as an empty plot, instead of writing outside the plot’s data range.

  • sync_geoms now panics when the two scenes were created for models that are not compatible.

Rendering

  • MjrContext::read_pixels no longer wraps when the pixel count or the rgb byte count overflows usize on a 32-bit target; it reports InvalidViewport instead.

  • MjRenderer::new no longer truncates its width, height and max_user_geom arguments; they now saturate at u32::MAX.

Model editing

  • SpecItem::id now returns None for a default class.

  • SpecObject::delete now reports UnsupportedOperation for a frame and for the world body. MuJoCo reported success for both and left the specification corrupted.

  • Closed a crash reachable from safe code when naming a tendon wrap: SpecItem::set_name on a MjsWrap now reports UnsupportedOperation and with_name panics. A wrap carries no name of its own: name reports the wrapped object’s name.

  • Closed a heap over-read reachable from safe code in MjSpec::compile. It now rejects a texture whose nchannel, width or height is negative, and one whose nchannel * width * height product does not fit in an i32.

Utility functions

Other changes

  • The Basic model editing example now builds three swinging pendulum chains instead of overlapping balls, and walks the finished body tree to shade each link by its depth. The old model stood still and showed nothing.

  • Following the reworked FFI struct field-visibility rules, the fields of MjsOrientation (type_, axisangle, xyaxes, zaxis, euler) and of the raw mjsElement handle (elemtype, signature) are now pub again, classifying them as plain-data structs and allowing direct field access.

5.0.0 (MuJoCo 3.9.0)

Breaking changes

MuJoCo FFI upgraded to 3.9.0

(Potentially breaking) Changed raw pointer types of trait methods

  • Changed raw pointer types of trait methods. For example, element_pointer(&self) now returns *const mjsElement. This is a change that should not affect most users, unless they explicitly opted in to using the C model-editing API of MuJoCo directly, or implemented/depended on the old trait method signature in advanced extension code.

MjrContext::upload_texture parameter type changed

MjrContext::upload_texture is now fallible

  • MjrContext::upload_texture now returns Result<(), MjrContextError> instead of () and no longer panics. Pass an out-of-range ID to receive MjrContextError::IndexOutOfBounds { id, len } instead of a panic.

MjrContext::add_aux / set_aux error type changed

  • MjrContext::add_aux and MjrContext::set_aux previously returned Result<(), MjSceneError> (with MjSceneError::InvalidAuxBufferIndex); they now return Result<(), MjrContextError> (with MjrContextError::IndexOutOfBounds).

MjSceneError::InvalidAuxBufferIndex / InvalidViewport / BufferTooSmall removed

  • MjSceneError::InvalidAuxBufferIndex, MjSceneError::InvalidViewport, and MjSceneError::BufferTooSmall have been removed from MjSceneError. Code matching on these variants will fail to compile. Replace them with the corresponding MjrContextError variants (see migration guide).

MjrContext::read_pixels error type changed

  • MjrContext::read_pixels previously returned Result<…, MjSceneError> (with MjSceneError::InvalidViewport and MjSceneError::BufferTooSmall); it now returns Result<…, MjrContextError> (with MjrContextError::InvalidViewport and MjrContextError::BufferTooSmall).

MjModelError::InvalidIndex removed

  • MjModelError::InvalidIndex(usize, usize) has been removed from MjModelError. Code matching on this variant will fail to compile. Replace it with IndexOutOfBounds (see migration guide).

MjsTuple::set_objtype now takes ``&[MjtObj]`` and is fallible

  • MjsTuple::set_objtype now accepts &[MjtObj] instead of &[i32] and is a safe fn returning Result<(), MjEditError> instead of (). Out-of-range object types are rejected with MjEditError::InvalidParameter. See the migration guide.

MjsSensor::set_objtype / set_reftype are now fallible

  • MjsSensor::set_objtype and MjsSensor::set_reftype now return Result<(), MjEditError> instead of (), rejecting out-of-range object types with MjEditError::InvalidParameter. The builder counterparts with_objtype / with_reftype keep their signature but panic on a rejected value. See the migration guide.

MjsNumeric::set_size is now fallible

  • MjsNumeric::set_size now returns Result<(), MjEditError> instead of (), rejecting a negative size with MjEditError::InvalidParameter (a negative size triggers an out-of-bounds write in the model compiler). See the migration guide.

Some index/size vector setters are now ``unsafe``

  • MjsFlex::set_elemtexcoord, MjsSkin::set_face, MjsMesh::set_userfacetexcoord and MjsMesh::set_userfacenormal are now unsafe fn: each writes a value the model compiler or renderer later trusts as an unchecked array index, count, or memcpy length, and the correct constraint is cross-field (it cannot be checked from the setter alone). Each carries a # Safety section describing the caller’s obligation. See the migration guide.

MjData::history_mut is now an ``unsafe fn``

  • MjData::history_mut is now unsafe. The signature is otherwise unchanged; existing calls only need an unsafe block. The immutable history accessor remains safe. See the migration guide.

Mjs element handles and MjsDefault are no longer Send/Sync

  • The Mjs* element handles (MjsBody, MjsGeom, …) and MjsDefault are now !Send + !Sync; they previously carried a blanket unsafe impl Send/Sync. Each handle is only a raw pointer into one shared mjSpec/mjCModel arena, so moving or sharing one across threads was unsound (see Bug fixes). Code that did so will no longer compile — move the owning MjSpec instead, which remains Send (it is no longer Sync; see below).

MjData::add_contact is now an ``unsafe fn``

  • MjData::add_contact is now unsafe. It stores the caller-supplied MjContact verbatim, and MuJoCo later uses it without validation, so a malformed contact can cause undefined behavior (see Bug fixes). The signature is otherwise unchanged; existing calls only need an unsafe block.

MjData::reset_debug is now an ``unsafe fn``

  • MjData::reset_debug is now unsafe. The debug fill writes raw bytes into arrays whose accessors expose types with validity invariants (bool, fieldless enums), so reading them afterwards can be undefined behavior (see Bug fixes). The signature is otherwise unchanged; existing calls only need an unsafe block.

MjData::ray / ray_mesh now take &mut self

  • MjData::ray, MjData::ray_mesh, and MjData::try_ray_mesh now take &mut self (previously &self). Their bounding-volume traversal mutates data.bvh_active (see Bug fixes), so callers holding a shared &MjData need an exclusive borrow instead. ray_flex / ray_hfield are unaffected and remain &self; multi_ray already took &mut self.

MjvScene::find_selection now takes &mut MjData

  • MjvScene::find_selection now takes data: &mut MjData<M> (previously &MjData<M>). It calls mjv_select -> mj_ray, whose bounding-volume traversal mutates data.bvh_active (see Bug fixes), so an exclusive borrow is required. This mirrors MjvScene’s update (already &mut MjData) and the ray / ray_mesh change above.

MjSpec is no longer Sync

  • MjSpec is now Send but no longer Sync (see Bug fixes). Several &self methods – notably clone / try_clone – drive C-side writes to the spec, so sharing a &MjSpec across threads is unsound. Code that required MjSpec: Sync (for example placing a &MjSpec in a type that demands Sync) will no longer compile; transfer ownership instead, as MjSpec remains Send.

MjsTendon::wrap / wrap_mut no longer return Option

  • MjsTendon’s wrap and wrap_mut now return &MjsWrap / &mut MjsWrap (previously Option<&MjsWrap> / Option<&mut MjsWrap>) and panic if the index is out of bounds. The old signature documented None on an out-of-range index, but the underlying C mjs_getWrap aborts the process for such an index and never returns null, so the None arm was unreachable. Drop the .unwrap() on existing calls and ensure the index is in range (< wrap_num()), or use the new fallible try_wrap / try_wrap_mut.

Deprecations

  • Deprecated SpecItem::delete due to it relying on undefined behavior. Users are expected to use MjSpec::delete_element instead.

  • Deprecated MjsTendon’s try_wrap_site, try_wrap_geom, try_wrap_joint, and try_wrap_pulley — their MjEditError::AllocationFailed arm is unreachable and an allocation failure cannot be recovered soundly (MuJoCo aborts the process, or writes through the null pointer before returning). Use the panicking wrap_site / wrap_geom / wrap_joint / wrap_pulley instead. These methods may be undeprecated in the future if MuJoCo’s upstream C++ code is changed to return null recoverably.

Error handling

New error types:

  • MjrContextError: new error enum for MjrContext operations, with variants:

    • IndexOutOfBounds { id, len } — an index passed to a rendering-context operation is out of range (covers GPU asset uploads and aux buffer index checks).

    • InvalidViewport { width, height } — the viewport dimensions are invalid for pixel read-back.

    • BufferTooSmall { name, got, needed } — the caller-supplied buffer is too small to hold the read-back data.

New error variants in pre-existing enums:

  • MjViewerError: SignatureMismatch (model structure does not match the viewer’s passive model), IndexOutOfBounds { id, len } (asset ID out of range), ContextError(MjrContextError) (wraps a rendering-context error, e.g. from pixel read-back during screenshot capture).

  • RendererError: SignatureMismatch (model structure does not match the renderer’s current scene), ContextError(MjrContextError) (wraps a rendering-context error, e.g. an out-of-range asset ID).

  • MjEditError: InvalidParameter(String) (a value was rejected; the string describes the rejection). Returned by the new set_to_* actuator helpers (see New features and improvements) and by the now-validating setters MjsSensor::set_objtype / set_reftype, MjsTuple::set_objtype and MjsNumeric::set_size (see Breaking changes).

New features and improvements

Fallible tendon wrap accessors

  • Added MjsTendon::try_wrap and MjsTendon::try_wrap_mut, the fallible counterparts of wrap / wrap_mut. They return Result<&MjsWrap, MjEditError> and yield MjEditError::IndexOutOfBounds for an out-of-range index instead of panicking. A new MjEditError IndexOutOfBounds variant carries the offending index and length.

MjrContext GPU re-upload methods

Viewer GPU asset re-upload

The Rust viewer now supports re-uploading textures, meshes, and heightfields to the GPU at runtime, mirroring the behaviour of MuJoCo’s C++ Simulate viewer.

Renderer GPU asset re-upload

The Rust renderer now supports re-uploading textures, meshes, and heightfields to the GPU at runtime.

Model-editing and utility API additions

  • Added mju_sym2dense as a wrapper for MuJoCo’s sparse-to-dense conversion utility. It is an unsafe fn: the C routine indexes mat / colind / res from the caller-supplied sparse structure without bounds checks, and the only way to validate that structure would be to iterate the index arrays, so the consistency of rownnz / rowadr / colind is documented as a # Safety precondition instead.

  • Added model-editing convenience APIs:

    • timer for CTIMER values.

    • child and child_mut for named child lookup.

    • id for retrieving optional element IDs.

    • delete_element for deleting a spec element by its FFI pointer — the sound replacement for the deprecated SpecItem::delete.

  • Added the missing frame-finding methods to MjSpec: frame and frame_mut.

  • MjsActuator now exposes the full family of actuator-configuration helpers covering MuJoCo’s mjs_setToX C API: set_to_motor, set_to_position, set_to_int_velocity, set_to_velocity, set_to_damper, set_to_cylinder, set_to_muscle, set_to_adhesion, and set_to_dc_motor. Helpers whose C function takes nullable parameters take a dedicated Default-able config struct (PositionConfig, IntVelocityConfig, DcMotorConfig) in which the nullable parameters are Option fields. Each config can be built either with struct-update syntax or with chainable with_* builder methods (which take the inner value and wrap optionals in Some), so callers specify only the fields they need and unset optionals default to None. For example: actuator.set_to_dc_motor(DcMotorConfig::default().with_motorconst([1.0, 1.0]).with_resistance(1.0))?. Helpers whose parameters are all mandatory keep simple positional arguments (e.g. set_to_velocity(kv), set_to_cylinder(timeconst, bias, area, diameter), set_to_muscle(...)). Helpers that MuJoCo can reject return Result<(), MjEditError> (e.g. set_to_damper, set_to_muscle, set_to_dc_motor); those that cannot fail (set_to_motor, set_to_velocity, set_to_cylinder) return ().

New examples

  • Asset re-upload — demonstrates animated heightfield, texture, and mesh GPU re-uploads with active physics: a ball falls onto a rippling terrain and bounces within a smooth-step wall. Physics and rendering run on separate threads.

  • Renderer asset re-upload — demonstrates the same animated assets using the offscreen renderer: heightfield, texture, and mesh are mutated each frame and immediately re-uploaded before rendering to PNG frames.

Bug fixes

  • Fixed MjsTendon’s wrap / wrap_mut, which documented returning None for an out-of-bounds index but could never do so: the underlying C mjs_getWrap aborts the process for such an index and never returns null, so the None case was unreachable. The accessors now reject an out-of-range index in Rust — panicking, or returning MjEditError::IndexOutOfBounds via the new try_wrap / try_wrap_mut (see Breaking changes).

  • Fixed a model-editing potential undefined behavior in MjsBody, which occurred when yielded references of body’s items were not destroyed before the next entry to the iterator. This was problematic if individual references of yielded items were stored and later used — technically a undefined behavior due to reuse of the MjsBody inside the iterator. The MjsBody parent is now stored as a raw FFI pointer to prevent such aliasing.

  • Fixed an out-of-bounds read reachable from safe code in MjvCamera::frame: a fixed camera whose fixedcamid was outside the model’s camera range caused MuJoCo’s mjv_cameraFrame to read cam_xpos / cam_xmat out of bounds. frame now validates the id against the model and panics on an out-of-range fixed camera id instead.

  • Extended that same fixed-camera range check to MjvScene::update: an out-of-range fixedcamid no longer reaches mjv_cameraFrame (whose mjCAMERA_FIXED branch reads cam_xpos / cam_xmat before MuJoCo’s own range check). This also closes the same out-of-bounds read on the offscreen renderer and the viewer, which both update through this method.

  • Fixed an out-of-bounds read reachable from safe code in MjvScene::update (and update_with_catmask) when an active perturbation carried an out-of-range select body id. MuJoCo’s mjv_updateScene only guards select <= 0 (it has no upper-bound check) before indexing xpos / xmat / body_bvhnum by select, so a too-large id read out of bounds. update now validates select against the model’s nbody and panics on an out-of-range id.

  • Fixed an out-of-bounds read reachable from safe code in MjvPerturb::move_ when the public select field held an out-of-range body id. move_ now validates select against the model’s nbody and panics on an out-of-range id.

  • Closed an unsoundness in MjData::add_contact, which was safe but copied a caller-built MjContact into the data arena without validation. MuJoCo later uses the stored contact’s fields without bound checks (when building constraints and when reading forces), so a malformed contact could cause out-of-bounds access from safe code. add_contact is now an unsafe fn: the caller guarantees the contact is valid for the model. See the Breaking changes section and the migration guide.

  • Closed an unsoundness in MjData::reset_debug, which was safe but filled mjData arrays with raw debug_value bytes that safe accessors could then read back as invalid values for types with validity invariants (e.g. bool, fieldless enums) — undefined behavior for any debug_value other than 0 or 1. reset_debug is now an unsafe fn: the caller guarantees such accessors are not read before the next reset. See the Breaking changes section and the migration guide.

  • Fixed a thread-safety hole reachable from safe code in the model-editing handles. Every Mjs* element handle and MjsDefault carried a blanket unsafe impl Send/Sync, although each is only a raw pointer into one shared mjSpec/mjCModel arena. Together with the lending mutable iterators (which hand out several simultaneously-live &mut handles), safe code could move handles to different threads and call mutators such as SpecItem::set_name concurrently; mjs_setName reaches model-global state (mjCModel::CheckRepeat and the shared error buffer), so the calls raced on the one arena – undefined behavior. The handles (and MjsDefault) are now !Send + !Sync, so such a mutation can no longer cross a thread boundary; the owning MjSpec stays Send (it is now !Sync; see below).

  • Closed an unsoundness reachable from safe code in MjData::ray and MjData::ray_mesh, which took &self although their bounding-volume traversal (mju_rayTree) writes data.bvh_active when the model’s bvactive visualization flag is set. The MuJoCo C functions declare const mjData* yet mutate through it, so the &self signature let safe code drive shared-state mutation: with MjData<M>: Sync two threads could race on bvh_active, and a &[bool] borrowed from the safe bvh_active accessor could be mutated while still live. ray, ray_mesh, and try_ray_mesh now take &mut self. See the Breaking changes section and the migration guide.

  • Closed an unsoundness reachable from safe code in MjvScene::find_selection, which took a shared &MjData although it calls mjv_select -> mj_ray, whose bounding-volume traversal writes data.bvh_active (the same const mjData*-but-mutating path as ray above). The shared borrow let safe code drive that mutation: with MjData<M>: Sync two threads could race on bvh_active, and a &[bool] from the safe bvh_active accessor could be mutated while still live. find_selection now takes &mut MjData. See the Breaking changes section and the migration guide.

  • Closed an unsoundness reachable from safe code in MjSpec, which carried an unconditional unsafe impl Sync. clone / try_clone produce a faithful, independent copy, but the C++ copy constructor behind mj_copySpec is not strictly const on the source: for each actuator it clears two std::map keyframe-resolution caches (ForgetKeyframes). Those maps are empty for a normally-built spec, yet std::map::clear rewrites the tree header unconditionally, so two threads sharing a &MjSpec and cloning concurrently raced on those writes (no spec data is lost). MjSpec is now !Sync (it stays Send). See the Breaking changes section and the migration guide.

  • Closed an out-of-bounds write reachable from safe code in utility::mju_normalize when called with an empty slice. The wrapper now panics on an empty slice.

  • Closed an unsoundness reachable from safe code in MjsMesh::set_userfacenormal, which stored unvalidated face-normal indices the renderer later dereferences without a bound check. It is now an unsafe fn with a # Safety contract. See the Breaking changes section and the migration guide.

  • Closed a heap overflow reachable from safe code when compiling an MjSpec whose texture has a builtin pattern and nchannel < 3 (MuJoCo’s builtin generators write more bytes than such a buffer holds). MjSpec::compile now rejects this configuration with MjEditError::CompileFailed.

  • Closed an out-of-bounds read reachable from safe code in MjData::history_mut, which exposed a cursor the sensor/control read path trusts as an array index. It is now an unsafe fn; the immutable history accessor stays safe. See the Breaking changes section and the migration guide.

Other changes

  • Updated bool-typed wrappers to use MjtBool where MuJoCo uses mjtBool. This is not a Rust API breaking change because MjtBool is a type alias to mjtBool, and mjtBool is bool by default. Related struct fields and function parameters now avoid force-casting and use MuJoCo’s bool mapping directly. Fields that are MjtByte in MuJoCo remain byte-backed and are not part of the MjtBool migration.

  • Added missing mjt* type aliases for recently surfaced MuJoCo enums: MjtMeshBuiltin, MjtCTimer.

  • Added auxiliary exposed aliases: MjPreContact, MjfCollision, MjpResourceProvider.

  • SpecItem::element_pointer and SpecItem::element_mut_pointer are no longer marked unsafe.

  • MjData::set_state is no longer marked unsafe. MuJoCo 3.9.0 changed eq_active to mjtBool (C bool), so writes through mjSTATE_EQ_ACTIVE are booleanized on assignment and reading eq_active() afterwards can no longer cause undefined behavior. Existing unsafe { data.set_state(...) } call sites keep compiling and only produce an unused_unsafe warning.

4.0.1 (MuJoCo 3.8.0)

Bug fixes

  • When using the auto-download-mujoco feature, the build script now checks for existence of the library before downloading it. When the library exists, its download and extraction are skipped.

4.0.0 (MuJoCo 3.8.0)

Breaking changes

MuJoCo upgraded to 3.8.0

  • MuJoCo-rs 4.0.0 is based on MuJoCo 3.8.0, upgraded from 3.6.0. MuJoCo 3.8.0 introduces breaking changes to its C API (renamed and removed constants, new struct fields). Code relying on specific MuJoCo enum values or internal constants may need to be updated.

MjData::geom_distance now requires mutable access

Model-editing wrappers now expose polynomial stiffness and damping coefficients

  • MjsJoint: stiffness and damping now expose coefficient arrays instead of scalar f64 values.

  • MjsTendon: stiffness and damping now expose coefficient arrays instead of scalar f64 values.

MjsFlex::vertcollide removed

  • MjsFlex::vertcollide is no longer available. MuJoCo 3.7.0 removed the upstream mjsFlex::vertcollide field, so the wrapper no longer exposes it.

MjData::model_mut is now unsafe

  • Because the MjData::model_mut allows full model replacement in unsafe ways, it is now marked unsafe.

MjViewer::add_ui_callback closure now receives Box<MjModel>

  • The passive model stored inside the viewer changed from Arc<MjModel> to Box<MjModel>. The closure passed to MjViewer::add_ui_callback now receives &mut MjData<Box<MjModel>>.

Error handling

New features and improvements

  • Updated FFI bindings and wrappers to match MuJoCo 3.8.0 headers.

  • MjModel now exposes the new joint, dof, tendon, and actuator coefficient arrays and actuator-linkage fields added in newer MuJoCo releases. MjsActuator now exposes its damping coefficient array and armature field.

  • MjtLRMode is now exported for typed access to MjLROpt.mode.

  • In light of MjData::model_mut becoming unsafe, six new methods are created to allow safe access to model fields:

    Immutable getters:

    Mutable accessors:

MjViewer model parameter synchronization

Viewer camera tracking modal

  • The viewer UI now features an interactive camera tracking modal for selecting bodies to track. Select “Track” in the camera panel to open a modal with a scrollable grid of body buttons.

Other changes

  • Removed the stl_mesh example. It existed in MuJoCo-rs 3.0.1 due to MuJoCo 3.6.0 requiring a plugin to load STL meshes. MuJoCo 3.7.0+ fixes this, thus the example is no longer required.

  • Reduced default width of the viewer’s UI side panel to 200.0.

  • Physics options (integrator, cone, jacobian, solver) in the viewer UI display a yellow warning when model parameters (opt/vis/stat) are out of sync with their initial values, indicating that parameters have been modified in the viewer.

3.0.1 (MuJoCo 3.6.0)

New features and improvements

3.0.0 (MuJoCo 3.6.0)

Breaking changes

Methods now return Result instead of panicking

  • The following methods previously returned () and panicked on error. They now return Result directly:

    • MjData: reset_keyframe, set_state, copy_visual_to, copy_to

    • MjrContext: read_pixels

    • MjvFigure: push, set_at

    • MjRenderer: set_font_scale now returns Result<(), RendererError>; with_font_scale now returns Result<Self, RendererError>.

    Callers must now handle the Result (e.g. append .unwrap() or ?).

Default features changed

  • viewer, viewer-ui, renderer, and renderer-winit-fallback are no longer enabled by default. Enable them explicitly when needed (e.g. cargo add mujoco-rs --features "viewer-ui renderer-winit-fallback").

MjvScene is no longer generic

  • MjvScene is no longer generic over M. It is now a plain struct MjvScene without a type parameter.

    • MjvScene::new now takes model: M where M: Deref<Target = MjModel> (accepts Arc<MjModel>, &MjModel, etc.).

    • MjvScene::update, MjvScene::update_with_catmask, and MjvScene::find_selection now take a generic <M> parameter on the method rather than on the struct. They panic if data was created from a different model (signature mismatch).

    • MjvScene exposes a new signature() -> u64 method for the model signature the scene was built for.

    • MjvPerturb::start / move_ and MjvCamera::move_ now take &MjvScene without a type parameter.

    • MjvPerturb::start and MjvPerturb::apply no longer take a model: &MjModel parameter (the model is now obtained internally via data.model()).

    • vis_common::sync_geoms is now non-generic.

    • MjvScene now derives Send + Sync unconditionally.

MjRenderer is no longer generic

MjViewer, MjViewerBuilder, ViewerSharedState are no longer generic

  • MjViewer, MjViewerBuilder, and ViewerSharedState are no longer generic over M. Remove the <M> type parameter from all usage sites.

    • The viewer stores a passive Arc<MjModel> internally (analogous to the existing passive MjData copy). sync_data, sync_data_full, and build_passive retain <M: Deref<Target = MjModel>> as method-level generics, so the call sites are unchanged.

    • MjViewerBuilder::build_passive now accepts any M: Deref<Target = MjModel> (e.g. &MjModel, Arc<MjModel>, Rc<MjModel>).

    • The closure passed to MjViewer::add_ui_callback now receives &mut MjData<Arc<MjModel>> instead of &mut MjData<M>. Update callback signatures accordingly.

Viewer and renderer sync model automatically

Separate renderer sync and render

MuJoCo upgrade

  • Updated MuJoCo from 3.3.7 to 3.6.0. C FFI bindings and all generated wrappers have been regenerated. See Installation for the matching release.

Error handling: io::Error replaced with typed enums

All methods that previously returned io::Error or bare types now return typed Result variants. Six new error enums have been added to the error module (all #[non_exhaustive], re-exported from the prelude): MjDataError, MjSceneError, MjEditError, MjModelError, MjVfsError, GlInitError. Pre-existing RendererError and MjViewerError gained new variants. See Error handling below for the full method list.

Type and signature changes

  • get_mujoco_version has been renamed to ~~mujoco_rs::mujoco_version. The old name is deprecated.

  • MjData::get_state has been renamed to MjData::state. The old name is deprecated.

  • MjvFigure::new has been renamed to MjvFigure::new_boxed. The old name is deprecated.

  • MjData::copy_to and MjData::copy_visual_to now accept a destination of a different model type: destination: &mut MjData<N> where N: Deref<Target = MjModel>. Previously the source and destination had to share the same M.

  • set_name now returns Result<(), MjEditError> instead of (). Append ? to call sites. with_name still returns &mut Self but now panics on duplicate names.

  • SpecItem is now a sealed trait. External implementations are no longer permitted.

  • MjsOrientation::switch_quat no longer has a type parameter. Replace switch_quat::<[f64; 4]>() with switch_quat().

  • MjModel now implements the standard Clone trait. The old clone() returning Option<MjModel> has been removed; use model.clone() (panics on failure) or the new try_clone() for a fallible version.

  • MjModel::save(filename, buffer) removed. Use the new save_to_file and save_to_buffer methods.

  • MjModel::size returns usize (was i32).

  • MjModel::save_last_xml now accepts AsRef<Path> (was &str) and returns Err(MjModelError::InvalidUtf8Path) for non-UTF-8 paths (previously impossible to pass since &str is always UTF-8).

  • MjViewerCpp<M> (cpp-viewer feature) is no longer generic over M. Remove the type parameter from all usage sites. The launch_passive associated function retains <M: Deref<Target = MjModel>> as a method-level generic.

  • MjModel::state_size returns usize (was i32).

  • MjModel::name_to_id returns Option<usize> (was i32; -1 is now None).

  • MjModel::totalmass renamed from get_totalmass.

  • MjrContext::set_buffer renamed from mjr_set_buffer.

  • MjData:

  • MjModel: MjModel::id_to_name: id takes usize (was i32).

  • mju_ray_geom gained normal_out: Option<&mut [MjtNum; 3]>.

  • MjvPerturb::update_local_pos takes selection_xyz by &[MjtNum; 3] (was by value).

  • MjvPerturb::move_ no longer takes a separate model parameter; data changed from &mut MjData<M> to &MjData<M>.

  • MjvScene::find_selection returns SceneSelection (was a 5-tuple (i32, i32, i32, i32, [MjtNum; 3])). SceneSelection fields (body_id, geom_id, flex_id, skin_id) are Option<usize> (None = no selection). Use if let Some(id) = sel.body_id instead of if sel.body_id >= 0.

  • MjvCamera: new_fixed, new_tracking, track, fix now take usize (was u32). Remove as u32 casts at call sites.

  • TryFrom<i32> for MjtCamera now uses MjSceneError as its error type (was ()). Replace Err(()) matches with Err(MjSceneError::InvalidCameraType(_)).

  • MjvScene::update: the pertub parameter has been renamed to perturb (typo fix).

  • MjvGeom::set_label now returns Result<(), MjSceneError>.

  • MjViewer::render now returns Result<(), MjViewerError>.

  • MjModel: print / print_formatted now accept AsRef<Path> and return Result<(), MjModelError>.

  • MjModel: tuple_objtype accessor now returns &[MjtObj] instead of &[i32].

  • MjCameraModelView / MjCameraModelViewMut: camera field projection (type MjtProjection) replaces the old boolean orthographic field, matching MuJoCo’s cam_projection rename.

  • MjsTendon: limited and actfrclimited are now MjtLimited (tri-state: FALSE / TRUE / AUTO) instead of bool, matching the C mjtLimited semantics.

  • MjsTexture::set_data now requires T: bytemuck::NoUninit.

New unsafe requirements

  • MjrContext::new is now unsafe fn. A valid OpenGL context must be current on the calling thread.

  • MjData::set_state is now unsafe fn and returns Result<(), MjDataError>. When spec includes mjSTATE_EQ_ACTIVE, MuJoCo writes raw f64 bytes into the eq_active array without booleanization; calling eq_active() afterwards is UB until the state is re-validated (e.g. via mj_forward / mj_step).

  • MjViewerCpp::render is now unsafe fn, must be called from the main thread, no longer accepts update_timer, and returns Result<(), &'static str>.

  • MjViewerCpp::launch_passive is now unsafe fn. Model and data must remain alive and at a stable address for the lifetime of the viewer.

Thread-safety bound tightening

  • MjData<M>: Send / Sync now require M: Send / M: Sync. Previously the bounds were unconditional, making MjData<Rc<MjModel>> incorrectly Send + Sync. (MjvScene is now non-generic and unconditionally Send + Sync.)

  • MjViewerCpp::launch_passive now requires M: Send + Sync (previously only Deref<Target = MjModel> + Clone).

Mutable accessor restrictions

*_mut() methods on MjModel, MjData, and MjvScene array fields are now unsafe fn where unrestricted writes can corrupt MuJoCo’s internal state. Two categories of fields are affected:

  • Structural invariants — topology, address, and engine-computed arrays that must not be changed at runtime: 199 fields on MjModel, 43 on MjData, 12 on MjvScene.

  • Companion-index fields — type/mode fields whose values control which array a companion index (*id, *adr) indexes into; writing inconsistent values causes out-of-bounds access: 17 fields on MjModel (jnt_type, actuator_trntype, actuator_dyntype, eq_type, eq_objtype, wrap_type, wrap_prm, sensor_type, sensor_objtype, sensor_reftype, skin_matid, tendon_matid, tendon_treeid, body_plugin, actuator_plugin, geom_plugin, sensor_plugin), 4 on MjData (efc_type, iefc_type, tree_asleep, wrap_obj).

  • Null-terminated string buffers — concatenated c_char arrays where each entry is null-terminated; removing a '\\0' byte allows MuJoCo’s C string functions (and CStr::from_ptr) to read past the buffer boundary: 4 fields on MjModel (names, plugin_attr, text_data, paths).

info_with_view! generated ViewMut types expose the companion-index fields as PointerViewUnsafeMut; mutation requires as_mut_slice inside an unsafe block. See migration for full field lists and before/after examples.

Removed APIs

  • MjViewerCpp::__raw() removed (exposed internal C++ binding pointers).

  • MjvFigure: figure renamed to draw.

  • MjViewer::sync removed (deprecated in 2.2.0; use sync_data + render).

  • MjViewer::user_scn / user_scn_mut removed (deprecated in 1.3.0).

  • MjViewer::user_scene / user_scene_mut removed (deprecated in 2.2.0; access via ViewerSharedState::user_scene / user_scene_mut through viewer.state()).

  • Removed deprecated methods:

    Type

    Removed

    Replacement

    MjData

    warning_stats

    MjData::warning

    MjData

    timer_stats

    MjData::timer

    MjData

    Type aliases MjJointInfo, MjJointView, MjJointViewMut, MjGeomInfo, MjGeomView, MjGeomViewMut, MjActuatorInfo, MjActuatorView, MjActuatorViewMut

    Use the current MjJointData*, MjGeomData*, MjActuatorData* types.

    MjJointDataViewMut

    reset

    MjJointDataViewMut::zero

    MjModel

    name2id

    MjModel::name_to_id

    MjvCamera

    new

    new_free, new_fixed, new_tracking, or new_user

MjTendonDataInfo field removal

  • MjTendonDataInfo no longer exposes J_rownnz, J_rowadr, or J_colind; these fields moved upstream to mjModel in MuJoCo 3.6.0 and are now accessible via MjTendonModelInfo (i.e. model.tendon()).

Error handling

New error types in error (all #[non_exhaustive], re-exported from the prelude):

Methods returning Result types

Type

Methods

Error type

MjModel

from_xml, from_xml_vfs, from_xml_string, from_buffer, save_last_xml, save_to_file new, save_to_buffer new, print, print_formatted, try_extract_state new, try_extract_state_into new

MjModelError

MjData

add_contact, constraint_update, try_jac new, try_jac_body new, try_jac_body_com new, try_jac_subtree_com new, try_jac_geom new, try_jac_site new, try_angmom_mat new, try_object_velocity new, try_object_acceleration new, try_geom_distance new, try_local_to_global new, try_multi_ray new, print, print_formatted, init_ctrl_history new, init_sensor_history new, try_read_ctrl new, read_sensor_into new, try_read_sensor_fixed new, try_read_sensor new, try_swap_model new, reset_keyframe, copy_state_from_data new, apply_ft new, set_state, copy_visual_to, copy_to

MjDataError

MjVfs

add_file new, add_file_from new, add_from_file deprecated, add_from_buffer, delete_file

MjVfsError

MjSpec

from_xml, from_xml_vfs, from_xml_string, compile, save_xml, save_xml_string, try_add_default new, from_parse new, from_parse_vfs new

MjEditError

MjvScene / MjvGeom / MjvFigure / MjrContext

try_create_geom new, set_label, add_aux, set_aux, push, set_at, read_pixels

MjSceneError

MjRenderer

try_rgb new, try_depth new, save_rgb, save_depth, save_depth_raw

RendererError

MjViewer

render

MjViewerError

Methods marked new are new in 3.0.0; the rest existed in 2.x and previously returned bare types or io::Error.

New error variants in pre-existing enums:

  • RendererError: RgbDisabled, DepthDisabled, DimensionMismatch, ZeroDimension, IoError, SceneError, GlInitFailed.

  • MjViewerError: PainterInitError, GlInitFailed, SceneError.

The six new enums (all #[non_exhaustive]) have the following variants:

  • MjModelError: InvalidUtf8Path, LoadFailed, SaveFailed, AllocationFailed, StateSliceLengthMismatch, SpecNotSubset, BufferTooSmall, SignatureMismatch, VfsError.

  • MjDataError: IndexOutOfBounds, UnsupportedObjectType, AllocationFailed, BufferTooSmall, LengthMismatch, SignatureMismatch, NoHistoryBuffer, ContactBufferFull, InvalidUtf8Path.

  • MjVfsError: AlreadyExists, LoadFailed, NotFound, InvalidUtf8Path, BufferTooLarge, Unknown.

  • MjEditError: AllocationFailed, InvalidUtf8Path, ParseFailed, CompileFailed, SaveFailed, NotFound, AlreadyExists, UnsupportedOperation, DeleteFailed, XmlBufferTooSmall — returned by save_xml_string when the supplied buffer is too small; the required_size field carries the snprintf-style byte count (excluding NUL), so retry with required_size + 1.

  • MjSceneError: SceneFull, LabelTooLong, InvalidAuxBufferIndex, InvalidViewport, BufferTooSmall, FigureBufferFull, FigureIndexOutOfBounds, InvalidPlotIndex, NonAsciiLabel, InvalidCameraType.

  • GlInitError (requires viewer or renderer-winit-fallback feature): DisplayBuild, NoWindow, WindowHandle, ContextCreation, SurfaceAttributes, SurfaceCreation, MakeCurrent.

  • New try_ methods paired with existing infallible counterparts (infallible variants now delegate to try_.``expect()``):

    • MjData: try_new, try_clone, try_read_state_into, try_swap_model, try_read_ctrl, try_read_sensor_fixed, try_read_sensor, try_jac, try_jac_body, try_jac_body_com, try_jac_subtree_com, try_jac_geom, try_jac_site, try_angmom_mat, try_object_velocity, try_object_acceleration, try_geom_distance, try_local_to_global, try_multi_ray

    • MjModel: try_clone, try_make_data, try_extract_state, try_extract_state_into

    • MjSpec / MjsTendon / MjsBody: try_new, try_clone, try_add_frame, try_wrap_site, try_wrap_geom, try_wrap_joint, try_wrap_pulley, try_add_default, macro-generated try_add_*

    • MjRenderer: try_rgb, try_depth

    • MjvScene: try_create_geom

    • MjData: try_ray_flex new, try_ray_hfield new, try_ray_mesh new

    • MjvFigure: try_full new, try_empty new, try_pop_front new, try_pop_back new, cut_front new, cut_end new

  • New MjData methods that return Result directly (no separate try_ variant): copy_state_from_data, apply_ft.

New features and improvements

  • MjRenderer: PNG compression is now configurable via MjRendererBuilder::png_compression (builder) and MjRenderer::set_png_compression (runtime setter). Defaults to png::Compression::NoCompression. Affects MjRenderer::save_rgb and MjRenderer::save_depth. The png crate is re-exported as mujoco_rs::renderer::png for naming png::Compression.

  • MjModel: extract_state / extract_state_into, try_make_data.

  • MjData: swap_model new (validates model signature), model_mut new (-> &mut MjModel, available when M: DerefMut), forward_kinematics, apply_ft, copy_state_from_data, ray_flex / ray_mesh / ray_hfield, init_ctrl_history / init_sensor_history, read_ctrl / read_sensor / read_sensor_into / read_sensor_fixed, model_clone new (-> M, available when M: Clone).

  • MjSpec: from_parse / from_parse_vfs. from_parse, from_parse_vfs, and save_xml now accept AsRef<Path> (was &str); returns Err(MjEditError::InvalidUtf8Path) for non-UTF-8 paths. MjSpec and SpecItem are also re-exported from mujoco_rs::wrappers.

  • MjsTendon: wrap / wrap_mut / wrap_num.

  • MjsWrap: coef, divisor, side_site, side_site_mut.

  • MjVfs: add_file / add_file_from replace add_from_file (which is now deprecated). add_from_buffer and delete_file now accept AsRef<Path>; returns Err(MjVfsError::InvalidUtf8Path) for non-UTF-8 paths.

  • MjvScene: update_with_catmask (exposes catmask filter parameter).

  • MjvCamera: frame, frustum.

  • New SceneSelection struct (returned by find_selection); implements Default.

  • The vis_common module is now public (requires viewer or renderer feature), exposing sync_geoms, write_png, flip_image_vertically.

  • MjModel and MjData views expose additional fields from MuJoCo 3.6.0. New MjModel view types added for exclude, mesh, and skin. dof_bodyid and dof_treeid are now exposed in per-dof joint model view types (the fields existed in MuJoCo 3.3.7 but were not previously accessible via per-object view types).

  • MjsMaterial: MjsMaterial::set_texture / MjsMaterial::with_texture set a texture name by MjtTextureRole, correctly targeting the pre-sized slot the MuJoCo renderer reads (e.g. mjTEXROLE_RGB). The existing set_textures / append_textures methods are retained but should generally be avoided for role-indexed vectors.

  • MjsTexture: MjsTexture::set_cubefile / MjsTexture::with_cubefile set a cube-map face file by MjtCubeFace. The existing set_cubefiles / append_cubefiles methods are retained.

  • info_with_view! structs now have try_view / try_view_mut methods.

  • Trait additions: Clone for MjSpec; Default for MjVfs, MjSpec, MjOption, MjRendererBuilder, MjViewerBuilder; Send + Sync for MjVfs; FusedIterator for all model-editing iterators; Eq for PointerView / PointerViewMut; Drop for MjRenderer.

  • PointerView::new, PointerViewMut::new, MjrRectangle::new are now const fn.

  • MjModel and MjData now use NonNull<mjModel> / NonNull<mjData> internally, encoding the non-null invariant at the type level and enabling niche optimisation for Option<MjModel> / Option<MjData>.

  • MjData: contacts() is deprecated; use contact() instead.

MjViewer

  • Added support for the Depth rendering flag (mjRND_DEPTH).

  • Added a “Print camera” button that prints the camera as an MJCF element.

  • Added a “Screenshot” panel with: a Screenshot button (timestamped PNG), a Viewport only checkbox (captures before UI is drawn), and a Depth checkbox (16-bit grayscale depth PNG).

Bug fixes

  • MjvCamera new_fixed / new_tracking and MjvScene new: debug_assert! guards on user-supplied camera_id, tracking_id, and max_geom (checking they fit in i32) were silently skipped in release builds. Changed to assert! so the precondition is always enforced. The # Panics documentation now correctly states panics occur in all builds, not only debug builds.

  • MjModel: MjModel::save_last_xml: a _ => unreachable!() arm would panic if MuJoCo ever returned a value other than 0 or 1; changed to _ => Err(MjModelError::SaveFailed(...)) so any unexpected return code is surfaced as an error instead of aborting the process.

  • mju_is_bad: now tests != 0 instead of == 1.

  • MjRenderer:

    • save_depth: guards against division by zero when the depth range is zero.

    • rgb, rgb_flat, save_rgb, save_depth, save_depth_raw: images were output vertically flipped (OpenGL reads bottom-up); now corrected.

    • MjRendererBuilder: font_scale setting was silently ignored during build(); now applied.

  • PointerView and PointerViewMut PartialEq now compares both pointer and length (previously pointer only).

  • MjModel: key_mpos and key_mquat array accessors were missing; added.

  • MjViewer: mouse normalization now uses inner_size() instead of outer_size(), fixing cursor mapping with window decorations.

  • MjViewer now implements Drop to make the GL context current before cleanup (prevents resource leaks on some platforms).

  • MjViewerCpp::sync:

    • FFI declaration used bool for state_only (should be c_int); fixed.

    • Could be called after the viewer window was closed, causing UB; now returns early if not running.

  • MjViewerCpp::launch_passive: asserts the C++ simulate handle is non-null after allocation.

  • MjvPerturb::update_local_pos: now uses bounds-checked slice indexing instead of raw pointer arithmetic.

  • MjrContext: set_aux now has a mjNAUX-based index bounds check.

  • Fixed potential UB when parsing null string pointers from MuJoCo C++ wrappers.

  • Added strict runtime signature check to view creation (panics with model signature mismatch on mismatch).

  • ViewerSharedState: on model change, ViewerSharedState::sync_data / ViewerSharedState::sync_data_full no longer spuriously write back the new model’s default pose (qpos0) to the incoming data. Previously, models with free joints, ball joints, or joints with a non-zero ref attribute would have their simulation state silently reset to the default pose on the first sync after a model switch. Active perturbations are also cleared on model change to prevent stale body-index references from the previous model being applied.

Other changes

2.3.5 (MuJoCo 3.3.7)

  • Fixed #161: actuator views could crash when a non-muscle actuator followed a muscle actuator.

  • Internal: minor performance improvement in sync_geoms (early exit when no geoms are added).

2.3.4 (MuJoCo 3.3.7)

  • 3D viewer bug fixes and usability improvements:

    • Keyboard events are now ignored whenever an egui input widget has focus.

    • Fixed a logic error where pressing an unhandled mouse button would drop subsequent events.

    • Ctrl+C no longer toggles camera visualization (reserved for copy).

2.3.3 (MuJoCo 3.3.7)

  • Fixed #144: the viewer caused high stack usage, potentially problematic on Windows (1M stack limit).

2.3.2 (MuJoCo 3.3.7)

  • Small performance improvement by removing mutex contention during scene render.

2.3.1 (MuJoCo 3.3.7)

  • Bug fixes:

    • Added a NULL pointer check to MjData; allocation failure now panics instead of accepting a NULL pointer.

    • Fixed #139: MjrContext::read_pixels no longer allows buffers smaller than the viewport.

    • Fixed potential viewer crashes when the display is disconnected or reconfigured.

2.3.0 (MuJoCo 3.3.7)

  • 3D viewer:

    • Added add_ui_callback_detached for passive UI widgets.

    • Added with_ui_egui_ctx for scoped egui context access.

    • Replaced panicking mutex handling with automatic unpoison logic.

    • Performance optimizations.

  • MjModel and MjSpec: version check against the MuJoCo shared library on instantiation; panics on mismatch.

2.2.2 (MuJoCo 3.3.7)

2.2.1 (MuJoCo 3.3.7)

  • Fixed #119: borrow tracker tracked the wrong lifetime.

  • Reset more OpenGL state after egui draw (figures now render correctly).

2.2.0 (MuJoCo 3.3.7)

  • 3D renderer now uses EGL on Linux by default (true offscreen rendering). New feature flag renderer-winit-fallback for compatibility (enabled by default).

  • 3D viewer:

    • add_ui_callback for custom egui UI widgets.

    • Info menu (F2): smoothed FPS, simulation time, used memory.

    • Realtime warning (F4): shows realtime factor when sync time deviates ≥ 2%.

    • Separate sync and render: sync_data (deprecates sync) + render.

    • Added ViewerSharedState for multi-threaded viewer/simulation sync.

  • New MjData methods: copy_visual_to, copy_to, set_state, get_state, read_state_into.

  • New example: rust_viewer_threaded.

  • Deprecated: user_scene() / user_scene_mut() (replaced by ViewerSharedState::user_scene / user_scene_mut).

2.1.0 / 2.1.1 (MuJoCo 3.3.7)

  • Option to automatically download MuJoCo (auto-download-mujoco feature).

  • pkg-config support for Linux and macOS.

  • Optional egui UI for MjViewer (enabled by default).

2.0.1 (MuJoCo 3.3.7)

  • Fixed the renderer feature not enabling all needed crates.

2.0.0 (MuJoCo 3.3.7)

  • Breaking changes:

    • Updated MuJoCo to 3.3.7.

    • Model editing: items no longer wrapped (just aliased types); attributes made private; immutable iterators added; _mut() methods added.

    • Viewer / renderer: switched from GLFW to Winit + Glutin.

    • Wrapper of MuJoCo’s C++ 3D viewer: moved to mujoco_rs::cpp_viewer; build target changed to glfw libmujoco_simulate.

    • MjData and related types now generic over model handle (Deref<Target = MjModel>).

    • Various c_int → i32 / bool type replacements.

    • Removed mujoco_rs::wrappers::mj_interface.

  • Other: additional getters / setters / array slice methods for MjData, MjModel, MjvScene.

1.5.0 (MuJoCo 3.3.5)

1.4.2 (MuJoCo 3.3.5)

  • Fixed segfault when model specification is invalid (#65).

1.4.1 (MuJoCo 3.3.5)

  • Added missing MjSpec named accessors: geom, site, camera, light.

1.4.0 (MuJoCo 3.3.5)

  • MjModel: added views for key, tuple, texture, site, pair, numeric, material, light, hfield, equality.

  • Model editing support added (mujoco_rs::wrappers::mj_editing module).

  • MjRenderer / MjRendererBuilder improvements.

  • MjViewer: new key bindings and visualization toggles; visual-geom headroom increased from 100 to 2000.

  • fix added.

1.3.0 (MuJoCo 3.3.5)

  • Added mujoco_rs::renderer module with MjRenderer.

  • Deprecated MjvCamera::new(); replaced by new_free, new_fixed, new_tracking, new_user.

  • Added MjData::maxuse_stack, maxuse_threadstack, warning_stats, timer_stats, time, energy.

  • Added MjModel::signature, opt / opt_mut, vis / vis_mut, stat / stat_mut.

  • Added joint view attributes: qfrc_spring, qfrc_damper, qfrc_gravcomp, qfrc_fluid.

1.2.0 (MuJoCo 3.3.5)

  • Function wrappers for utility and derivative functions (mujoco_rs::wrappers::fun).

  • Completed virtual file system wrapper: add_from_file, delete_file, from_xml_vfs.

1.1.0 (MuJoCo 3.3.5)

1.0.1 (MuJoCo 3.3.5)

  • Bug fixes in Drop implementations.

1.0.0 (MuJoCo 3.3.5)

  • Breaking: all ffi_mut() methods now require unsafe.

  • Viewer: help overlay (F1), user scene, mouse perturbation.

0.4.3 (MuJoCo 3.3.5)

  • Removed unnecessary header files (smaller crate size).

0.4.2 (MuJoCo 3.3.5)

  • Renamed env vars: MUJOCO_DYNAMIC_LINK_LIB → MUJOCO_DYNAMIC_LINK_DIR, MUJOCO_STATIC_LINK_LIB → MUJOCO_STATIC_LINK_DIR.

0.4.1 (MuJoCo 3.3.5)

  • Fixed event handling.

0.4.0 (MuJoCo 3.3.5)

  • Renamed package to mujoco-rs.

0.3.0 (MuJoCo 3.3.5)

  • Initial public release.