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 enum MjBatchError new.
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
qposat 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
MjViewerstruct instance has been dropped).
Viewer
Added methods to MjViewer:
MjViewer::request_close new closes the viewer after the exit confirmation.
MjViewer::force_close new closes the viewer without the exit confirmation.
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_DIRenvironment variable. For this, the crate now declareslinks = "mujoco", thus Cargo permits only one crate with thislinksvalue in a dependency graph.
New examples
Batched simulation — runs many simulations in parallel using MjBatch.
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 3and the unnamed joint 3) no longer open and close together.
Other changes
The
actuator_inputsexample is now Control windows. Now, the example also shows the sensor window and the equality window.The crates now point to the
LICENSEfile instead of theMIT OR Apache-2.0SPDX 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
MuJoCo-rs now builds against MuJoCo 3.17.0 FFI, which rolls up the upstream 3.13.0, 3.14.0, 3.15.0, 3.16.0 and 3.17.0 releases. This directly affects all the wrappers. A separate changelog will not be provided. See MuJoCo’s changelog for this release: https://mujoco.readthedocs.io/en/3.17.0/changelog.html
treenumis now marked as unsafe to mutate (wrong values can cause out-of-bound reads). Consequently,the
treenumfield of MjTendonModelViewMut is now PointerViewUnsafeMut;MjModel::tendon_treenum_mut is now an
unsafe fn.
The
pluginfield of MjActuatorModelViewMut and MjBodyModelViewMut is now PointerViewUnsafeMut.MjModel::eq_data_mut, MjModel::flex_stiffness_mut and MjModel::flex_bending_mut are now
unsafe fn, and thedatafield of MjEqualityModelViewMut is now PointerViewUnsafeMut. A wrong value in a flex strain equality or an interpolated flex shell made a step read out of bounds.MjModel::actuator_biastype_mut, MjModel::actuator_dynprm_mut, MjModel::actuator_gainprm_mut, MjModel::body_simple_mut, MjModel::flex_rigid_mut and MjModel::flex_centered_mut are now
unsafe fn. Thebiastype,dynprmandgainprmfields of MjActuatorModelViewMut and thesimplefield of MjBodyModelViewMut are now PointerViewUnsafeMut. A value that disagreed with the rest of the model made a step access memory out of bounds.MjModel::tuple_objtype_mut is now an
unsafe fn. The relatedobjtypefield of MjTupleModelViewMut is now PointerViewUnsafeMut.MjModel::hfield_size_mut is now an
unsafe fn, and thesizefield of MjHfieldModelViewMut is now PointerViewUnsafeMut.MjModel::site_type_mut is now an
unsafe fn, and ther#typefield of MjSiteModelViewMut is now PointerViewUnsafeMut. A wrong site type made a sensor or the scene update read out of bounds.MjModel::paths_mut is now an
unsafe fn. A path without its terminating zero made MuJoCo read past the end of the buffer.(runtime-breaking) The
zeromethod of a mutable view (e.g. MjActuatorModelViewMut::zero) no longer zeroes the fields that are now PointerViewUnsafeMut. Write these fields through the unsafe view if you need them zeroed.MjData::efc_diag_a, MjData::efc_kbip, MjData::efc_d, MjData::efc_r, MjData::iefc_d, MjData::iefc_r, MjData::efc_y, MjData::efc_ar, MjData::efm_c and MjData::efm_l are now
unsafe fn, and so are their_mutvariants. A read taken before the pipeline stage that computes the array returns uninitialized memory, andefc_yandefc_arstay uninitialized under some solver settings. Run that stage first, see Migrating to 7.0.0.Removed
append_cubefilesandappend_texturesfrom MjsTexture and MjsMaterial, respectively.cubefilesandtexturesrequire a fixed number of elements. Use MjsTexture::set_cubefile and MjsMaterial::set_texture, respectively, to set the elements.Removed
add_frame,try_add_frame,add_body,try_add_bodyandadd_body_with_classfrom MjsFrame. A frame that a mutable frame iterator yielded could add another frame. The same iterator would then end up yielding the newly added frame again, resulting in two active mutable borrows (aliasing!). Use MjsBody::add_frame_in new and FrameChild::with_frame new instead.MjsDefault no longer implements SpecItem, because renaming a default class caused undefined behavior. It implements only the new SpecElement new, which is now the supertrait of
SpecItem.The member accessors of MjsDefault (MjsDefault::joint, MjsDefault::geom, MjsDefault::actuator and the others, with their
_mutvariants) now return handles marked with InDefault new, for example&mut MjsJoint<InDefault>(MjsJoint). These handles keep the field accessors, MjsJoint::info and the actuatorset_to_*methods, but no longer implement SpecElement, SpecItem, SpecObject, FrameChild, Attach or UserValued, because their methods crashed on a default-class member. The handles of these kinds gain a type parameter that defaults to InTree new, thus writingMjsJointgives the same result as before.(runtime-breaking) MjData::contact_force now panics when
contact_id >= ncon. Before, it returned[0; 6]. If you want to catch out-of-bounds errors, use MjData::try_contact_force new.(runtime-breaking) MjModel::is_compatible_with_model and the
Infoviews now compare every size and every integer table that no safe accessor writes, apart from the names, the paths, the buffer size, the mesh faces and hulls, the bounding volume and octree nodes and the values derived from floats, so they refuse more models, for example a model in which a geom uses a different material. MjModel::is_asset_compatible_with_model compares the mesh, height field and texture part of them. Before, a swapped model could make a step or a plugin read or write out of bounds.(runtime-breaking) MjvScene::is_compatible_with_model and MjvScene::is_compatible_with_scene now refuse more models, and MjvScene::update panics on each of them. A scene or a user scene that was filled from an accepted, but different, model could read or write out of bounds. MjRenderer rebuilds its scenes on its own.
MjDataError::IncompatibleModel and MjModelError::IncompatibleModel now carry the MjModelLayout new of both models instead of their signatures.
MjDataError::UnsupportedObjectType now carries an MjtObjBase new instead of an
i32.Removed
MjvScene::signature(MjvScene) and themodel_signaturemethod of everyInfotype. Read MjModel::signature on the model instead.Model and specification fields and functions that accept only real geom types, real object types or real texture roles now use MjtGeomBase new, MjtObjBase new and MjtTextureRoleBase new, which hold only the variants below
mjNGEOMTYPES,mjNOBJECTandmjNTEXROLE, respectively.MjtGeomBaseandMjtObjBaseconvert into the full enums with.into().MjsSensor::set_objtype, MjsSensor::set_reftype and MjsTuple::set_objtype no longer return a
Result, and MjsSensor::with_objtype and MjsSensor::with_reftype no longer panic, because the type now rejects the invalid object types.MjViewer::launch_passive, MjViewerBuilder::build_passive, MjRenderer::new, MjRendererBuilder::build and MjvScene::new now take the model as
&MjModel(MjModel) instead of a generic model type, because they only need the reference at construction.MjViewerCpp now owns the simulation data, meaning another thread can step it while the main thread renders:
MjViewerCpp::launch_passive takes the MjData by value and no longer takes a
modelparameter.MjViewerCppis now generic over a model type that implements ModelTypeMut, for exampleMjViewerCpp<Box<MjModel>>.Access the data through MjViewerCpp::state new, which returns the shared MjCppViewerSharedState new.
Removed
user_scn_mut. Push the user geoms into MjCppViewerSharedState::user_geoms_mut new, which is anunsafe fn, and call MjViewerCpp::sync_user_geoms new.MjViewerCpp::render is no longer an
unsafe fn, and MjViewerCpp::sync now returnstruewhile the viewer is open.MjViewerCppno longer implementsSendandSync. To step the simulation in another thread, send a clone of the shared state to that thread.
MjData::read_ctrl and MjData::try_read_ctrl now return a
Cow<[MjtNum]>(MjtNum) with one value per control of the actuator, instead of a singleMjtNum, because an actuator can now have more than one control. Index the result to get a value.
Deprecations
Deprecated the
add_*andadd_*_with_classelement adders of MjsFrame. Instead, add elements to the same body as theMjsFrameand call FrameChild::with_frame.
Error handling
New variant MjEditError::FrameParentMismatch new.
New variant MjModelError::AssetLoadFailed new holds MuJoCo’s error message when an asset of an existing model fails to load.
New variant MjEditError::OperationFailed new holds MuJoCo’s error message when a spec operation other than compilation fails.
MjsBody::try_add_flexcomp now returns MjEditError::InvalidParameter with MuJoCo’s error message when MuJoCo fails to create the flex, for example for a missing mesh file. Before, it returned MjEditError::AllocationFailed and lost the message.
The
Displayoutput of MjModelError, GlInitError, RendererError and MjViewerError no longer includes the inner error, because a reporter that walkssource()printed it twice. Walksource()to show the cause.MjData::print, MjData::print_formatted, MjModel::print and MjModel::print_formatted now return the new variants MjDataError::IoError new and MjModelError::IoError new when the file cannot be opened. Before, they returned
Ok(())and wrote nothing.MjSpec::compile no longer checks texture values before it calls MuJoCo. Before, it returned an error for a builtin texture with
nchannelbelow 3, a negative texture size or a texture size that overflowsi32. MuJoCo does not check every spec value, so give only valid values.
New features and improvements
Model editing
Added the FrameChild new trait and its methods, for setting the parent frame of body items/elements.
SpecItem::set_name on a MjsWrap now makes the wrap target the object with the new name. It returned MjEditError::UnsupportedOperation before.
SpecObject::delete now deletes a frame and its subtree.
-
MjsBody::add_frame_in new for adding a nested frame.
MjsBody::adopt_inertial new and MjsBody::adopt_inertial_with_vfs new compute the inertial of a body from its geoms and write it into the spec.
-
MjSpec::fuse_static new and MjSpec::fuse_static_with_vfs new fuse the static bodies with their parents (mjs_fuseStatic).
MjSpec::discard_visual new and MjSpec::discard_visual_with_vfs new discard the elements which are only visual.
MjSpec::dedup_assets new removes duplicate assets and points their users to the first asset of each group.
Rendering
-
MjrContext::is_compatible_with_model new reports whether a context can serve another model.
MjrContext::is_asset_compatible_with_model new reports whether a context can take an asset upload from another model.
Simulation
-
A fallible method, MjData::try_contact_force new, has been created for handling out-of-bound errors.
MjData::swap_model_unchecked new swaps in a model that the compatibility check refuses.
MjData::is_point_inside_site new and MjData::try_is_point_inside_site new report whether a point is inside a site (mj_insideSite).
MjData::read_ctrl_into new, MjData::read_ctrl_fixed new and MjData::try_read_ctrl_fixed new read the controls of an actuator into a buffer or a fixed-size array, without an allocation.
-
MjModel::layout new returns the layout that the compatibility checks compare.
MjModel::to_embed_assets new returns a copy of the model that embeds all of its assets.
MjModel::load_texture new decodes a texture to raw bytes or borrows the model data when the texture is embedded.
New examples
Threaded C++ viewer — an extended example of the original C++ viewer example, which shows multi-threaded use.
Bug fixes
MjsMaterial::set_textures and MjsTexture::set_cubefiles now fill the remaining needed parts of the vector with empty entries. A shorter length of vectors caused out-of-bound reads.
MjData::contact_force now checks the ID before the call. The cast to
i32could also wrap an ID of2^32or more into an in-bound, but wrong, contact index, thus the method returned the force of another contact.MjRenderer now checks its rendering context: MjRenderer::sync_data rebuilds the context, and the
update_*_frommethods return RendererError::IncompatibleModel, when the context cannot serve the model. Before, an upload could read out of bounds.MjvScene::render now panics when the MjrContext was created from a model with a different MjModelLayout than the scene. Before, a scene from a model with more materials or textures read out of bounds.
MjActuatorModelViewMut::zero and MjBodyModelViewMut::zero no longer set
pluginto0, which bound the actuator or body to plugin instance 0 instead of no plugin (-1).Fixed potential race condition when using separate MjRenderer instances in different threads. MjrContext and related wrappers now use a static mutex to prevent parallel access to the same data (
context_countin MuJoCo C code).MjViewer::render now makes the viewer’s OpenGL context current again after the user UI callbacks. A callback that made another context current caused the viewer to draw into that context.
MjViewer and the winit fallback of MjRenderer now release their OpenGL context and drop it, with its surface, before they close the window. Before, the window closed first, while the context still used it.
Fixed a hole in MjrContext::overlay, which allowed non-ASCII input to be passed into the function, causing possible out-of-bound reads. The function now asserts ASCII input and panics on non-ASCII or DEL input.
The vector and matrix functions of
mujoco_rs::wrappers::fun::utility(for example utility::mju_dot) now panic when a slice or a matrix has more thani32::MAXelements. Before, the length wrapped and MuJoCo used a wrong range.MjSpec::save_xml_string now clamps
buffer_sizetoi32::MAX. Before, a larger size wrapped, and MuJoCo could write past the buffer.MjData::ray and MjData::multi_ray now panic for a
bodyexcludeabovei32::MAX, which wrapped into another body ID before.multi_rayand MjData::try_multi_ray also panic for more thani32::MAXrays.MjModel::id_to_name now panics for an
idabovei32::MAX. Before, the ID wrapped, and the method could return the name of another element.MjvCamera::new_fixed, MjvCamera::new_tracking, MjvCamera::fix and MjvCamera::track now panic for an ID above
i32::MAXin all builds. Before, the ID wrapped into another camera or body ID.MjrContext::add_aux and MjrContext::resize_offscreen now panic for a
widthorheightabovei32::MAX, andadd_auxclampssamples. Before, the sizes wrapped into negative values.MjsBody::try_add_flexcomp now returns MjEditError::InvalidParameter for a
countwith more thani32::MAXpoints, or for an invalidcellcountwith a trilinear or quadraticdof. Before, MuJoCo wrote out of bounds.MjvCamera::move_ now panics for a move or turn action on a camera type other than free, tracking or fixed. Before, MuJoCo stopped the process.
MjvFigure::push, MjvFigure::full, MjvFigure::empty, MjvFigure::pop_front, MjvFigure::pop_back and the
try_variants of the last four now panic for a negativelinepnt, and MjvFigure::draw panics for a negativelegendoffsetwhenflg_legendis set. Before,drawread out of bounds.MjvScene::update and MjvScene::update_with_catmask now panic for a negative or
i32::MAXvis.quality.numslices. Before, MuJoCo wrote out of bounds.MjModel::state_size, the state methods that use it, and MjData::copy_state_from_data now panic for a
specwith an unknown bit. Before, MuJoCo stopped the process.MjSpec::world_body and MjSpec::world_body_mut now return the world body even after renames. Before, they would panic when the world body was renamed.
MjModel::max_contacts and MjModel::try_max_contacts now return
0for a negativesdf_initpoints. Before, the value wrapped due to an overflow.MjData::efc_j and MjData::efc_j_mut no longer expose uninitialized entries past
nefc * nvunder a dense Jacobian. The slice can now be shorter thannJ.
6.1.0 (MuJoCo 3.12.0)¶
Deprecations
Deprecated MjsFrame’s childclass, set_childclass and with_childclass. Use SpecItem::default, SpecItem::set_default and SpecItem::with_default instead, which modify the same field, but also fail when no class with the given name exists.
Error handling
New variants:
New features and improvements
Model editing
Added traits:
The first trait defines Attach::attach_by_deep_copy and Attach::attach_by_reference, which allow elements to be attached to
Self(implementor ofAttach) based on theAttachTotrait. For example, implementations ofAttachTo<MjsFrame>allow the implementor elements to be attached to MjsFrame.The prelude exports both traits.
Added the UserValued new trait, which stores an arbitrary Rust value on a specification element under a string key:
The prelude exports the trait.
Thread support:
Added support for sending MjSpec to other threads by wrapping it into SendableSpec new, which implements Send. A
SendableSpeccan be created fromMjSpecby calling theunsafemethod MjSpec::into_sendable new, or the equivalent SendableSpec::new new. The wrapped specification can be obtained by calling SendableSpec::take new.
New methods:
MjSpec::compile_with_vfs new for compiling a model with assets loaded on a virtual filesystem;
MjSpec::encode new for encoding a specification to a file with the encoder registered for the content type;
MjSpec::encode_with_vfs new for reading the encoder’s assets through a virtual filesystem;
Class-aware element constructors: the macro-generated
add_*_with_classmethods on MjSpec, MjsBody and MjsFrame, such as MjsBody::add_geom_with_class new.
Added setters for the MjSpec size attributes, which were read-only until now:
MjSpec::set_memory new, MjSpec::set_nuserdata new and MjSpec::set_nkey new;
MjSpec::set_nuser_body new, MjSpec::set_nuser_jnt new, MjSpec::set_nuser_geom new, MjSpec::set_nuser_site new, MjSpec::set_nuser_cam new, MjSpec::set_nuser_tendon new, MjSpec::set_nuser_actuator new and MjSpec::set_nuser_sensor new.
MjModel
New methods:
MjModel::encode new for encoding a compiled model to a file with the encoder registered for the content type;
MjModel::encode_with_vfs new for reading the encoder’s assets through a virtual filesystem;
MjModel::from_mjb new;
MjData
New methods:
MjData::set_const new — available only when
MinMjData<M>implements ModelTypeMut;MjData::save_keyframe new for copying the current state into a model keyframe — also only available when
MimplementsModelTypeMut;MjData::jac_subtree_com_into new and MjData::angmom_mat_into new.
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
Added MjVfs::contains_buffer new.
Utility functions
Added mju_euler_2_quat new.
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
MjrContext::set_window_doublebuffer new overrides the color-buffer choice that mjr_makeContext derives from
GL_DOUBLEBUFFER.
Bug fixes
Fixed #276 where 3D viewer crashed with models containing a flex.
The 3D viewer no longer raises a
GL_INVALID_OPERATIONon 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
The Rust viewer and threaded Rust viewer examples now show a ball that bounces on a trampoline.
6.0.0 (MuJoCo 3.12.0)¶
Breaking changes
Mutable body iteration is restricted to a tree walk
Patched undefined-behavior cases by making MjSpec no longer carry
body_iter_mut, and MjsBody::body_iter_mut no longer taking therecurseargument. This now makes them yield only their direct children. Reach a body through MjSpec::world_body_mut and walk down. Immutable iteration is unchanged, and MjsBody::body_iter keepsrecurse. See Migrating to 6.0.0.
The items deprecated in 3.0.0 are removed
mujoco_rs::get_mujoco_version(mujoco_version), MjVfs’sadd_from_file(add_file and add_file_from),MjRenderer::sync(MjRenderer),MjvFigure::new(MjvFigure), MjData’scontactsandget_state, andMjsSkin::set_vertidandMjsSkin::set_vertweight(MjsSkin) are gone. See Migrating to 6.0.0.
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
The 3D viewer now builds against
egui0.36, which it re-exports asmujoco_rs::viewer::egui. Custom UI code must be ported to the neweguiAPI, see Migrating to 6.0.0. For the upstream changes, see egui’s changelog: https://github.com/emilk/egui/blob/0.36.1/CHANGELOG.md
UI callbacks receive an egui::Ui and the shared state
The closure passed to MjViewer::add_ui_callback now receives
&mut egui::Uiand&Arc<Mutex<ViewerSharedState>>(ViewerSharedState), instead of&egui::Context(egui::Context) and&mut MjData<Box<MjModel>>(MjData, MjModel).MjViewer::add_ui_callback_detachedis removed asadd_ui_callbacknow handles both cases.
The signature-mismatch error variants are renamed
SignatureMismatchbecomesIncompatibleModelin MjDataError::IncompatibleModel, MjModelError::IncompatibleModel, MjViewerError::IncompatibleModel and RendererError::IncompatibleModel. The guard behind each of them now tests the whole model memory layout instead of the incomplete signature only.
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
MjvCamera::move_ lost its
scene: &MjvScene(MjvScene) parameter, following the upstream removal of the unneeded mjvScene argument from mjv_moveCamera.
Stricter MjrContext accessor types
gl_initialized, window_available, window_stereo and window_doublebuffer now read as
boolinstead ofi32; MuJoCo stores only 0 or 1 in them.font_scale and read_depth_map now read as MjtFontScale and MjtDepthMap instead of
i32.
Unsafe mutation of MjModel actuator_gaintype
actuator_gaintype_mut is now an
unsafe fn; the actuator views are affected the same way, so a write through a view needs as_mut_slice insideunsafe.
Unsafe geom creation on MjvScene
MjvScene::create_geom and MjvScene::try_create_geom are now
unsafe fn.
Renamed DcMotorConfig input field
The public field
DcMotorConfig::input_modeis now DcMotorConfig::ctrlspec and the builderwith_input_modeis 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 thedcmotorcontroller gains from voltage space to torque space, so the samecontrollerarray 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
awakeas a PointerViewMut. Code that wrote throughas_mut_slicemust drop the call and the surroundingunsafe.
SpecItem::default returns an Option
SpecItem::default now returns
Option<&MjsDefault>(MjsDefault). Handle theNonecase.
Changed MjrContext set_buffer parameter type
MjrContext::set_buffer now accepts
framebuffer: MjtFramebuffer(MjtFramebuffer) instead ofi32.
Control-history methods take an actuator index
MjData::read_ctrl, MjData::try_read_ctrl and MjData::init_ctrl_history now bound
idbynactuatorinstead ofnu.
Redundant dimension parameters removed
utility::mju_eye, utility::mju_symmetrize, utility::mju_sqr_mat_td and utility::mju_sym2dense each dropped a dimension that the slices they already take determine. Drop the argument at the call site, see Migrating to 6.0.0.
MjData and the visual APIs bound the model handle by ModelType
The model-handle generic
Mis now bound by theunsafemarker traits ModelType new and ModelTypeMut new, instead ofDeref<Target = MjModel>andDerefMut<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
MjModel::tex_type_mut, MjModel::sensor_intprm_mut and MjModel::flex_elemlayer_mut are now
unsafe fn. Wrap the call in anunsafeblock and uphold the C invariant, see Migrating to 6.0.0.
Drawing an MjvFigure became unsafe
MjvFigure::draw is now an
unsafe fn. Thelinepntbound is a caller precondition, see Migrating to 6.0.0.
Sixteen MjData arena accessors became unsafe
The accessors below are now
unsafe fn, together with their_mutcounterparts:MjData::efc_state, MjData::efc_force, MjData::efc_b, MjData::efc_vel, MjData::efc_aref, MjData::iefc_state, MjData::iefc_force, MjData::iefc_aref, MjData::iacc, MjData::iacc_smooth, MjData::ifrc_smooth, MjData::ifrc_constraint, MjData::efc_j_rownnz, MjData::efc_j_rowadr, MjData::efc_j_rowsuper, MjData::efc_j_colind
A read taken before the pipeline stage that computes the array returns uninitialized memory. Run that stage first, see Migrating to 6.0.0.
Two model view fields moved to the read-only list
intprmon MjSensorModelViewMut andtypeon MjTextureModelViewMut are now PointerViewUnsafeMut, matching the array accessors MjModel::sensor_intprm_mut and MjModel::tex_type_mut. Writing one now needsas_mut_sliceand explicitunsafe, see Migrating to 6.0.0.zerono longer touches either field.
Element handles are opaque types
Every
Mjs*element handle is now an opaque, zero-sized struct instead of an alias for the matchingmujoco_cstruct; MjsBody and MjsDefault are examples. It exposes its FFI struct throughffifor reading andunsafe fn ffi_mutfor writing, so code that hands a handle to amujoco_cfunction needs an edit, see Migrating to 6.0.0.std::mem::swapon 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
The shared MjsWrap::side_site stays. Mutable references to sites are available through MjSpec::site_mut, see Migrating to 6.0.0.
MjsPlugin no longer stands for the plugin that an element embeds
The
pluginandplugin_mutaccessors of a body, geom, mesh, actuator and sensor now hand out MjsPluginReference new. See Migrating to 6.0.0.
delete moved from SpecItem to
SpecObject
SpecObject::delete new stays
unsafeand is no longer deprecated. A default class and a tendon wrap are noSpecObject, 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
Deprecated MjSpec’s delete_element. Call
SpecObject::deleteon the element handle instead.Deprecated MjModel’s try_clone — its MjModelError::AllocationFailed arm is unreachable. Use
cloneinstead.Deprecated MjData’s try_clone — its MjDataError::AllocationFailed arm is unreachable. Use
cloneinstead.Deprecated MjSpec’s try_new, try_add_frame on MjsBody and MjsFrame, and every macro-generated
try_add_*element constructor on MjSpec, MjsBody and MjsFrame — their MjEditError::AllocationFailed arm is unreachable. Usenewand the panickingadd_*methods instead. try_add_default and try_add_flexcomp keep theirResult: MuJoCo does report a duplicate class name and a failed flex build.
New features and improvements
Model and scene compatibility
MjModel::is_compatible_with_model new reports whether another MjModel can take the place of this one in an MjData or in a cached
Info.MjModel::is_asset_compatible_with_model new is the weaker test the asset-upload methods need: it compares the mesh, texture and heightfield memory alone.
MjvScene::is_compatible_with_model new and MjvScene::is_compatible_with_scene new report whether a model, or a second MjvScene, matches the buffers this scene holds. Use them to decide whether a scene needs to be rebuilt before an update or a merge.
Every
Infogained anupdate_layoutmethod, for example MjBodyDataInfo::update_layout new. Callupdate_layoutonce against the new MjData or MjModel after a model swap. It reports the same error as a view method when the new model is not compatible.
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
Xkey 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,velandfffor apidactuator. An actuator without control inputs shows “no inputs”. The window showed one flat slider for each control before, labelled<name>[<index>].A defined
ctrlrangenow sets the range of an actuator slider, even when MuJoCo does not clamp the control to it. The slider spanned -1 to 1 unlessactuator_ctrllimitedwas set before.A press on the scene now takes the selection away from every viewer window.
Unified logging API
Added a new logging chapter. Access it at Logging for full information.
MuJoCo logging wrapper:
Added MjLogConfig new and MjLogMessage new aliases (with MjtLogLevel new and MjtLogTopic new) for the unified logging API of MuJoCo 3.10.0.
Added the free logging MuJoCo wrapper functions log_config new (mju_getLogConfig), set_log_config new (mju_setLogConfig), log_message new (mju_message), log_info new (mju_info), log_warning new (mju_warning) and log_error new (mju_error).
Standard logging integration
Added the new top-level logging module, which sends MuJoCo’s messages to the
logcrate, so they reach the same backend as the rest of the application. install_logging_hook new installs a hook forlogrouting. set_log_handler new registers a user handler.MuJoCo-rs now reports its own events to the
logcrate as well, on theDebug,Info,WarnandErrorlevels, with the emitting module path as the target.
New compilation-warning accessors
MjSpec gained two compilation-warning accessors, wrapping mjs_numWarnings and mjs_getWarning:
num_warnings new and warning new.
New MjData methods
-
full_m new, converting the sparse inertia matrix into a dense
nv x nvmatrix; wraps mj_fullM. Returns Err(MjDataError::BufferTooSmall) whendstis shorter thannv * nv.set_threadpool new, creating a worker thread pool with
nthreadthreads for parallel simulation; wraps mju_threadpool.
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_intoreturnsErr(MjDataError::BufferTooSmall)whenresultholds fewer thannvelements.
Cloneable renderer builder
MjRendererBuilder now derives
Clone, so a configured builder can be duplicated before build.
Procedural flex creation
-
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 theflexcompelement). The generation is configured through the new MjFlexcompConfig new builder.
New type aliases
Added MjrCamera new as a type alias for MjvGLCamera, matching MuJoCo’s own mjrCamera alias convention.
Actuator input names
-
actuator_input_name new, wrapping mj_actuatorInputName, gives the name of one control input of an actuator. It returns
Nonewhen the actuator type defines no input names.
New servo actuator constructors
-
set_to_pid new, wrapping mjs_setToPID, makes the actuator a PID controller on a single force output. The controller is configured through the new PidConfig new builder.
set_to_orientation new, wrapping mjs_setToOrientation, makes the actuator a geodesic PD orientation servo on a ball joint or a site. It is configured through the new OrientationConfig new builder.
Relaxed mutability on fields that cannot cause a memory fault
Mutating MjModel::paths and MjrContext::texture_type no longer needs
unsafe. A write can at worst give a wrong asset path or wrong rendering, never a memory fault.
New examples
Actuator inputs (now
Control windowssince 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 theMjLogMessage.
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::MAXand 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
efcadron 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
ctrlto 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_dataarrives in the same frame.A model swap no longer discards the change that a program makes to the new model’s
opt,visorstatbefore 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
Infoview method. All of them compared the model signature alone. TheMjDatamethods now testMjModel::is_compatible_with_model, which also compares the per-element address tables; anInfoview method compares the model layout it cached.The viewer and the renderer no longer test the signature either.
MjViewer::sync_dataandMjViewer::sync_modeltest 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 whosemjDatasizes 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_rayand MjData::try_multi_ray no longer report geom0for such a direction; the returned id is nowNone.
Visualization
Closed an out-of-bounds read reachable from safe code in MjvCamera::frame for a tracking camera:
framenow 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:
startnow validatesflexselectagainst the model and panics on an out-of-range id.Closed an out-of-bounds read reachable from safe code in MjvScene::render:
rendernow 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_modelinstead of the signature, and panic when the model in the given MjData needs larger scene buffers.MjvScene::updateandMjvScene::find_selectionnow accept a model that mj_loadModel returned.mj_saveModelwrites 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.
categorybecomes mjCAT_DECOR, and amjCAT_DECORgeom 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
linepntentry 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
rgbbyte count overflowsusizeon a 32-bit target; it reports InvalidViewport instead.MjRenderer::new no longer truncates its
width,heightandmax_user_geomarguments; they now saturate atu32::MAX.
Model editing
SpecItem::id now returns
Nonefor 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
UnsupportedOperationand 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,widthorheightis negative, and one whosenchannel * width * heightproduct does not fit in ani32.
Utility functions
Closed a division by zero and a non-terminating loop reachable from safe code in utility::mju_halton: the wrapper now panics unless
baseis 2 or more.Closed a heap buffer overflow reachable from safe code in utility::mju_zero, utility::mju_copy, utility::mju_normalize, utility::mju_mul_mat_t_vec, utility::mju_eye, utility::mju_mul_mat_mat, utility::mju_mul_mat_t_mat, utility::mju_sqr_mat_td, utility::mju_sym2dense and utility::mju_symmetrize. They now panic on a destination above
i32::MAXelements.Closed a heap buffer overflow reachable from safe code in MjsHfield::set_userdata and MjsTexture::set_data. They now panic on a slice above
i32::MAXelements.MjvScene::new now checks
max_geomin a release build as well. The check was adebug_assert!, so a release build passed a truncated capacity to MuJoCo and allocated a smaller scene than the caller asked for.
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 nowpubagain, 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
MuJoCo-rs now builds against MuJoCo 3.9.0 FFI. This is a breaking dependency-level change for consumers tied to older MuJoCo C ABI details. See MuJoCo’s changelog for this release: https://mujoco.readthedocs.io/en/3.9.0/changelog.html
(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 now accepts
texture_id: usizeinstead oftexid: u32.
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 receiveMjrContextError::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>(withMjSceneError::InvalidAuxBufferIndex); they now returnResult<(), MjrContextError>(withMjrContextError::IndexOutOfBounds).
MjSceneError::InvalidAuxBufferIndex / InvalidViewport / BufferTooSmall removed
MjSceneError::InvalidAuxBufferIndex,MjSceneError::InvalidViewport, andMjSceneError::BufferTooSmallhave 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>(withMjSceneError::InvalidViewportandMjSceneError::BufferTooSmall); it now returnsResult<…, MjrContextError>(withMjrContextError::InvalidViewportandMjrContextError::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 safefnreturningResult<(), MjEditError>instead of(). Out-of-range object types are rejected withMjEditError::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 withMjEditError::InvalidParameter. The builder counterpartswith_objtype/with_reftypekeep 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 withMjEditError::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_userfacetexcoordandMjsMesh::set_userfacenormalare nowunsafe fn: each writes a value the model compiler or renderer later trusts as an unchecked array index, count, ormemcpylength, and the correct constraint is cross-field (it cannot be checked from the setter alone). Each carries a# Safetysection 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 anunsafeblock. The immutablehistoryaccessor remains safe. See the migration guide.
Mjs element handles and MjsDefault are no longer Send/Sync
The
Mjs*element handles (MjsBody,MjsGeom, …) andMjsDefaultare now!Send + !Sync; they previously carried a blanketunsafe impl Send/Sync. Each handle is only a raw pointer into one sharedmjSpec/mjCModelarena, 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 remainsSend(it is no longerSync; see below).
MjData::add_contact is now an ``unsafe fn``
MjData::add_contact is now
unsafe. It stores the caller-suppliedMjContactverbatim, 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 anunsafeblock.
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 anunsafeblock.
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 mutatesdata.bvh_active(see Bug fixes), so callers holding a shared&MjDataneed an exclusive borrow instead.ray_flex/ray_hfieldare unaffected and remain&self;multi_rayalready took&mut self.
MjvScene::find_selection now takes &mut MjData
MjvScene::find_selection now takes
data: &mut MjData<M>(previously&MjData<M>). It callsmjv_select->mj_ray, whose bounding-volume traversal mutatesdata.bvh_active(see Bug fixes), so an exclusive borrow is required. This mirrors MjvScene’supdate(already&mut MjData) and theray/ray_meshchange above.
MjSpec is no longer Sync
MjSpec is now
Sendbut no longerSync(see Bug fixes). Several&selfmethods – notablyclone/try_clone– drive C-side writes to the spec, so sharing a&MjSpecacross threads is unsound. Code that requiredMjSpec: Sync(for example placing a&MjSpecin a type that demandsSync) will no longer compile; transfer ownership instead, asMjSpecremainsSend.
MjsTendon::wrap / wrap_mut no longer return Option
MjsTendon’s
wrapandwrap_mutnow return&MjsWrap/&mut MjsWrap(previouslyOption<&MjsWrap>/Option<&mut MjsWrap>) and panic if the index is out of bounds. The old signature documentedNoneon an out-of-range index, but the underlying Cmjs_getWrapaborts the process for such an index and never returns null, so theNonearm was unreachable. Drop the.unwrap()on existing calls and ensure the index is in range (< wrap_num()), or use the new fallibletry_wrap/try_wrap_mut.
Deprecations
Deprecated
SpecItem::deletedue 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, andtry_wrap_pulley— theirMjEditError::AllocationFailedarm is unreachable and an allocation failure cannot be recovered soundly (MuJoCo aborts the process, or writes through the null pointer before returning). Use the panickingwrap_site/wrap_geom/wrap_joint/wrap_pulleyinstead. 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 newset_to_*actuator helpers (see New features and improvements) and by the now-validating settersMjsSensor::set_objtype/set_reftype,MjsTuple::set_objtypeandMjsNumeric::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 returnResult<&MjsWrap, MjEditError>and yieldMjEditError::IndexOutOfBoundsfor an out-of-range index instead of panicking. A new MjEditErrorIndexOutOfBoundsvariant carries the offending index and length.
MjrContext GPU re-upload methods
MjrContext::upload_texture now takes
&selfinstead of&mut self, relaxing the mutability requirement.MjrContext now provides two additional GPU asset re-upload methods:
upload_mesh re-uploads a mesh to the GPU by mesh ID.
upload_hfield re-uploads a height field to the GPU by height-field ID.
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.
The singular methods MjViewer::update_texture_from, MjViewer::update_mesh_from, and MjViewer::update_hfield_from copy and mark one asset at a time. All methods return
Result<(), MjViewerError>.The plural methods MjViewer::update_textures_from, MjViewer::update_meshes_from, and MjViewer::update_hfields_from copy and mark all assets of that type. All methods return
Result<(), MjViewerError>.For multi-threaded programs the same singular and plural methods are available on the shared-state guard: ViewerSharedState::update_texture_from, ViewerSharedState::update_mesh_from, ViewerSharedState::update_hfield_from, ViewerSharedState::update_textures_from, ViewerSharedState::update_meshes_from, and ViewerSharedState::update_hfields_from.
Renderer GPU asset re-upload
The Rust renderer now supports re-uploading textures, meshes, and heightfields to the GPU at runtime.
The singular methods MjRenderer::update_texture_from, MjRenderer::update_mesh_from, and MjRenderer::update_hfield_from upload one asset immediately. All methods return
Result<(), RendererError>.The plural methods MjRenderer::update_textures_from, MjRenderer::update_meshes_from, and MjRenderer::update_hfields_from upload all assets of that type immediately. All methods return
Result<(), RendererError>.
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 indexesmat/colind/resfrom 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 ofrownnz/rowadr/colindis documented as a# Safetyprecondition instead.Added model-editing convenience APIs:
timer for CTIMER values.
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_setToXC 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, andset_to_dc_motor. Helpers whose C function takes nullable parameters take a dedicatedDefault-able config struct (PositionConfig,IntVelocityConfig,DcMotorConfig) in which the nullable parameters areOptionfields. Each config can be built either with struct-update syntax or with chainablewith_*builder methods (which take the inner value and wrap optionals inSome), so callers specify only the fields they need and unset optionals default toNone. 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 returnResult<(), 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 returningNonefor an out-of-bounds index but could never do so: the underlying Cmjs_getWrapaborts the process for such an index and never returns null, so theNonecase was unreachable. The accessors now reject an out-of-range index in Rust — panicking, or returningMjEditError::IndexOutOfBoundsvia the newtry_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
MjsBodyinside the iterator. TheMjsBodyparent 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
fixedcamidwas outside the model’s camera range caused MuJoCo’smjv_cameraFrameto readcam_xpos/cam_xmatout of bounds.framenow 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
fixedcamidno longer reachesmjv_cameraFrame(whosemjCAMERA_FIXEDbranch readscam_xpos/cam_xmatbefore 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-rangeselectbody id. MuJoCo’smjv_updateSceneonly guardsselect <= 0(it has no upper-bound check) before indexingxpos/xmat/body_bvhnumbyselect, so a too-large id read out of bounds.updatenow validatesselectagainst the model’snbodyand panics on an out-of-range id.Fixed an out-of-bounds read reachable from safe code in MjvPerturb::move_ when the public
selectfield held an out-of-range body id.move_now validatesselectagainst the model’snbodyand panics on an out-of-range id.Closed an unsoundness in MjData::add_contact, which was safe but copied a caller-built
MjContactinto 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_contactis now anunsafe 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
mjDataarrays with rawdebug_valuebytes that safe accessors could then read back as invalid values for types with validity invariants (e.g.bool, fieldless enums) — undefined behavior for anydebug_valueother than 0 or 1.reset_debugis now anunsafe 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 andMjsDefaultcarried a blanketunsafe impl Send/Sync, although each is only a raw pointer into one sharedmjSpec/mjCModelarena. Together with the lending mutable iterators (which hand out several simultaneously-live&muthandles), safe code could move handles to different threads and call mutators such as SpecItem::set_name concurrently;mjs_setNamereaches model-global state (mjCModel::CheckRepeatand the shared error buffer), so the calls raced on the one arena – undefined behavior. The handles (andMjsDefault) are now!Send + !Sync, so such a mutation can no longer cross a thread boundary; the owning MjSpec staysSend(it is now!Sync; see below).Closed an unsoundness reachable from safe code in MjData::ray and MjData::ray_mesh, which took
&selfalthough their bounding-volume traversal (mju_rayTree) writesdata.bvh_activewhen the model’sbvactivevisualization flag is set. The MuJoCo C functions declareconst mjData*yet mutate through it, so the&selfsignature let safe code drive shared-state mutation: withMjData<M>: Synctwo threads could race onbvh_active, and a&[bool]borrowed from the safebvh_activeaccessor could be mutated while still live.ray,ray_mesh, andtry_ray_meshnow 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
&MjDataalthough it callsmjv_select->mj_ray, whose bounding-volume traversal writesdata.bvh_active(the sameconst mjData*-but-mutating path asrayabove). The shared borrow let safe code drive that mutation: withMjData<M>: Synctwo threads could race onbvh_active, and a&[bool]from the safebvh_activeaccessor could be mutated while still live.find_selectionnow 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_cloneproduce a faithful, independent copy, but the C++ copy constructor behindmj_copySpecis not strictlyconston the source: for each actuator it clears twostd::mapkeyframe-resolution caches (ForgetKeyframes). Those maps are empty for a normally-built spec, yetstd::map::clearrewrites the tree header unconditionally, so two threads sharing a&MjSpecand cloning concurrently raced on those writes (no spec data is lost). MjSpec is now!Sync(it staysSend). 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 anunsafe fnwith a# Safetycontract. 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 withMjEditError::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 immutablehistoryaccessor stays safe. See the Breaking changes section and the migration guide.
Other changes
Updated bool-typed wrappers to use
MjtBoolwhere MuJoCo usesmjtBool. This is not a Rust API breaking change becauseMjtBoolis a type alias tomjtBool, andmjtBoolisboolby default. Related struct fields and function parameters now avoid force-casting and use MuJoCo’s bool mapping directly. Fields that areMjtBytein MuJoCo remain byte-backed and are not part of theMjtBoolmigration.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 changedeq_activetomjtBool(Cbool), so writes throughmjSTATE_EQ_ACTIVEare booleanized on assignment and readingeq_active()afterwards can no longer cause undefined behavior. Existingunsafe { data.set_state(...) }call sites keep compiling and only produce anunused_unsafewarning.
4.0.1 (MuJoCo 3.8.0)¶
Bug fixes
When using the
auto-download-mujocofeature, 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
MjData::geom_distance and MjData::try_geom_distance now take
&mut selfinstead of&self. This follows MuJoCo 3.7.0, wheremj_geomDistancemutatesmjDatainternally.
Model-editing wrappers now expose polynomial stiffness and damping coefficients
MjsJoint:
stiffnessanddampingnow expose coefficient arrays instead of scalarf64values.MjsTendon:
stiffnessanddampingnow expose coefficient arrays instead of scalarf64values.
MjsFlex::vertcollide removed
MjsFlex::vertcollideis no longer available. MuJoCo 3.7.0 removed the upstreammjsFlex::vertcollidefield, 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>toBox<MjModel>. The closure passed to MjViewer::add_ui_callback now receives&mut MjData<Box<MjModel>>.
Error handling
Added a new variant for handling invalid indices in MjModel::try_max_contacts:
MjModelError::InvalidIndex.
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
dampingcoefficient array andarmaturefield.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:
model_vis, and
Mutable accessors:
MjViewer model parameter synchronization
ViewerSharedState now provides methods to sync model parameters:
sync_model syncs all model parameters (physics options, visualization, and statistics) bidirectionally, detecting model changes via signature comparison.
sync_model_vis, and
allow syncing model physics options, visualization parameters, and statistics individually. Each performs a bidirectional three-way merge between the viewer’s passive model and the supplied struct, without requiring
unsafeaccess.Corresponding getter methods last_opt_sync_time, last_vis_sync_time, and last_stat_sync_time return when each parameter type was last synchronized, enabling UI warnings for out-of-sync parameters.
MjViewer now provides proxy methods (sync_model, sync_model_opt, sync_model_vis, sync_model_stat) that delegate to the corresponding ViewerSharedState methods, enabling convenient parameter synchronization without manual lock management.
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_meshexample. 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
mujoco_rs::wrappers::mj_plugin: load_plugin_library new and load_all_plugin_libraries new load a single or all MuJoCo plugin shared libraries. load_all_plugin_libraries accepts an optional MjPluginLibraryLoadCallback new function pointer invoked for each loaded library.Added
stl_mesh.rsnew – demonstrates loading plugin libraries and loading a model with an STL mesh asset.
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 returnResultdirectly:MjData:
reset_keyframe,set_state,copy_visual_to,copy_toMjrContext:
read_pixelsMjvFigure:
push,set_atMjRenderer:
set_font_scalenow returnsResult<(), RendererError>;with_font_scalenow returnsResult<Self, RendererError>.
Callers must now handle the
Result(e.g. append.unwrap()or?).
Default features changed
viewer,viewer-ui,renderer, andrenderer-winit-fallbackare 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 plainstruct MjvScenewithout a type parameter.MjvScene::newnow takesmodel: MwhereM: Deref<Target = MjModel>(acceptsArc<MjModel>,&MjModel, etc.).MjvScene::update,MjvScene::update_with_catmask, andMjvScene::find_selectionnow take a generic<M>parameter on the method rather than on the struct. They panic ifdatawas created from a different model (signature mismatch).MjvSceneexposes a newsignature() -> u64method for the model signature the scene was built for.MjvPerturb::start/move_andMjvCamera::move_now take&MjvScenewithout a type parameter.MjvPerturb::startandMjvPerturb::applyno longer take amodel: &MjModelparameter (the model is now obtained internally viadata.model()).vis_common::sync_geomsis now non-generic.MjvScene now derives
Send + Syncunconditionally.
MjRenderer is no longer generic
MjRenderer and MjRendererBuilder are no longer generic over
M. Remove the<M>type parameter from all usage sites.MjRenderer::sync_data and MjRenderer::new retain
<M: Deref<Target = MjModel>>as method-level generics; call sites are unchanged.MjRendererBuilder no longer requires
M: Clone; anyM: Deref<Target = MjModel>is accepted.syncis deprecated; usesync_data+renderinstead.
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 passiveMjDatacopy).sync_data,sync_data_full, andbuild_passiveretain<M: Deref<Target = MjModel>>as method-level generics, so the call sites are unchanged.MjViewerBuilder::build_passivenow accepts anyM: Deref<Target = MjModel>(e.g.&MjModel,Arc<MjModel>,Rc<MjModel>).The closure passed to
MjViewer::add_ui_callbacknow receives&mut MjData<Arc<MjModel>>instead of&mut MjData<M>. Update callback signatures accordingly.
Viewer and renderer sync model automatically
MjViewer::sync_data / ViewerSharedState::sync_data and MjRenderer::sync_data now detect when
datawas created from a different model than the viewer/renderer was initialized with. Instead of panicking or returning an error, both automatically recreate the scene(s) for the new model and update their internal model reference.
Separate renderer sync and render
MjRenderer: new MjRenderer::sync_data method updates the scene without rendering. Call MjRenderer::render separately afterwards. This replaces the combined
syncmethod, which is now deprecated.
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>whereN: Deref<Target = MjModel>. Previously the source and destination had to share the sameM.set_name now returns
Result<(), MjEditError>instead of(). Append?to call sites. with_name still returns&mut Selfbut now panics on duplicate names.SpecItem is now a sealed trait. External implementations are no longer permitted.
MjsOrientation::switch_quatno longer has a type parameter. Replaceswitch_quat::<[f64; 4]>()withswitch_quat().MjModel now implements the standard
Clonetrait. The oldclone()returningOption<MjModel>has been removed; usemodel.clone()(panics on failure) or the newtry_clone()for a fallible version.MjModel::save(filename, buffer)removed. Use the newsave_to_fileandsave_to_buffermethods.MjModel::size returns
usize(wasi32).MjModel::save_last_xml now accepts
AsRef<Path>(was&str) and returnsErr(MjModelError::InvalidUtf8Path)for non-UTF-8 paths (previously impossible to pass since&stris always UTF-8).MjViewerCpp<M>(cpp-viewerfeature) is no longer generic overM. Remove the type parameter from all usage sites. Thelaunch_passiveassociated function retains<M: Deref<Target = MjModel>>as a method-level generic.MjModel::state_size returns
usize(wasi32).MjModel::name_to_id returns
Option<usize>(wasi32;-1is nowNone).MjModel::totalmass renamed from
get_totalmass.MjrContext::set_buffer renamed from
mjr_set_buffer.-
MjData::reset_keyframe now takes
key: usize(wasi32) and returnsResult<(), MjDataError>. Out-of-range keys that previously silently fell back to a plain reset now return an error.MjData::runge_kutta now takes
n: u32(wasi32) and panics ifn < 1(previously passed negative or zero values silently to C).MjData::maxuse_threadstackreturns&[MjtSize; mjMAXTHREAD](was&[MjtSize]).MjData::jac, MjData::jac_body, MjData::jac_body_com, MjData::jac_subtree_com, MjData::jac_geom, MjData::jac_site, MjData::angmom_mat, MjData::object_velocity, MjData::object_acceleration, MjData::geom_distance, MjData::local_to_global: object-id / body-id / geom-id / site-id parameters now take
usize(wasi32). The>= 0guard is implicit in the type; passbody_id as usizeat call sites.MjData::ray, MjData::multi_ray:
bodyexcludechanged fromi32toOption<usize>. Replace-1withNoneandbody_idwithSome(body_id as usize).MjData::ray now returns
(Option<usize>, MjtNum)instead of(i32, MjtNum). The returned geom id isNonewhen the ray misses all geometry (previously-1).MjData::multi_ray now returns
(Vec<Option<usize>>, Vec<MjtNum>)instead of(Vec<i32>, Vec<MjtNum>)(usetry_multi_rayfor aResult-returning alternative). Each element of the geom-id vector isNonewhen the corresponding ray misses all geometry (previously-1).MjData::ray gained a new
normal_out: Option<&mut [MjtNum; 3]>parameter. PassNoneto preserve previous behaviour.MjData::multi_ray gained
normals_outand now panics on invalid input (usetry_multi_rayfor a fallible alternative). PassNonefor the new parameter.MjData::jac_subtree_com no longer accepts
jacp: bool(the Jacobian is always computed); now panics on invalid input (usetry_jac_subtree_comfor a fallible alternative).MjData::print and MjData::print_formatted now accept
AsRef<Path>and returnResult.
MjModel: MjModel::id_to_name:
idtakesusize(wasi32).mju_ray_geom gained
normal_out: Option<&mut [MjtNum; 3]>.MjvPerturb::update_local_pos takes
selection_xyzby&[MjtNum; 3](was by value).MjvPerturb::move_ no longer takes a separate
modelparameter;datachanged from&mut MjData<M>to&MjData<M>.MjvScene::find_selection returns SceneSelection (was a 5-tuple
(i32, i32, i32, i32, [MjtNum; 3])).SceneSelectionfields (body_id,geom_id,flex_id,skin_id) areOption<usize>(None= no selection). Useif let Some(id) = sel.body_idinstead ofif sel.body_id >= 0.MjvCamera:
new_fixed,new_tracking,track,fixnow takeusize(wasu32). Removeas u32casts at call sites.TryFrom<i32> for MjtCameranow usesMjSceneErroras its error type (was()). ReplaceErr(())matches withErr(MjSceneError::InvalidCameraType(_)).MjvScene::update: the
pertubparameter has been renamed toperturb(typo fix).MjvGeom::set_label now returns
Result<(), MjSceneError>.MjViewer::render now returns
Result<(), MjViewerError>.MjModel:
print/print_formattednow acceptAsRef<Path>and returnResult<(), MjModelError>.MjModel:
tuple_objtypeaccessor now returns&[MjtObj]instead of&[i32].MjCameraModelView/MjCameraModelViewMut: camera fieldprojection(typeMjtProjection) replaces the old booleanorthographicfield, matching MuJoCo’scam_projectionrename.MjsTendon:
limitedandactfrclimitedare nowMjtLimited(tri-state:FALSE/TRUE/AUTO) instead ofbool, matching the CmjtLimitedsemantics.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 fnand returnsResult<(), MjDataError>. WhenspecincludesmjSTATE_EQ_ACTIVE, MuJoCo writes rawf64bytes into theeq_activearray without booleanization; callingeq_active()afterwards is UB until the state is re-validated (e.g. viamj_forward/mj_step).MjViewerCpp::render is now
unsafe fn, must be called from the main thread, no longer acceptsupdate_timer, and returnsResult<(), &'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/Syncnow requireM: Send/M: Sync. Previously the bounds were unconditional, makingMjData<Rc<MjModel>>incorrectlySend + Sync. (MjvSceneis now non-generic and unconditionallySend + Sync.)MjViewerCpp::launch_passive now requires
M: Send + Sync(previously onlyDeref<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_chararrays where each entry is null-terminated; removing a'\\0'byte allows MuJoCo’s C string functions (andCStr::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:
figurerenamed todraw.MjViewer::syncremoved (deprecated in 2.2.0; usesync_data+render).MjViewer::user_scn/user_scn_mutremoved (deprecated in 1.3.0).MjViewer::user_scene/user_scene_mutremoved (deprecated in 2.2.0; access viaViewerSharedState::user_scene/user_scene_mutthroughviewer.state()).Removed deprecated methods:
Type
Removed
Replacement
warning_statstimer_statsType aliases
MjJointInfo,MjJointView,MjJointViewMut,MjGeomInfo,MjGeomView,MjGeomViewMut,MjActuatorInfo,MjActuatorView,MjActuatorViewMutUse the current
MjJointData*,MjGeomData*,MjActuatorData*types.resetname2idnewnew_free,new_fixed,new_tracking, ornew_user
MjTendonDataInfo field removal
MjTendonDataInfono longer exposesJ_rownnz,J_rowadr, orJ_colind; these fields moved upstream tomjModelin MuJoCo 3.6.0 and are now accessible viaMjTendonModelInfo(i.e.model.tendon()).
Error handling
New error types in error
(all #[non_exhaustive], re-exported from the prelude):
Type |
Methods |
Error type |
|---|---|---|
|
||
|
||
|
||
|
||
MjvScene / MjvGeom / MjvFigure / MjrContext |
|
|
|
||
|
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 bysave_xml_stringwhen the supplied buffer is too small; therequired_sizefield carries thesnprintf-style byte count (excluding NUL), so retry withrequired_size + 1.MjSceneError:
SceneFull,LabelTooLong,InvalidAuxBufferIndex,InvalidViewport,BufferTooSmall,FigureBufferFull,FigureIndexOutOfBounds,InvalidPlotIndex,NonAsciiLabel,InvalidCameraType.GlInitError (requires
viewerorrenderer-winit-fallbackfeature):DisplayBuild,NoWindow,WindowHandle,ContextCreation,SurfaceAttributes,SurfaceCreation,MakeCurrent.New
try_methods paired with existing infallible counterparts (infallible variants now delegate totry_.``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_rayMjModel:
try_clone,try_make_data,try_extract_state,try_extract_state_intoMjSpec / 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-generatedtry_add_*MjRenderer:
try_rgb,try_depthMjvScene:
try_create_geomMjData:
try_ray_flexnew,try_ray_hfieldnew,try_ray_meshnewMjvFigure:
try_fullnew,try_emptynew,try_pop_frontnew,try_pop_backnew,cut_frontnew,cut_endnew
New MjData methods that return
Resultdirectly (no separatetry_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. Thepngcrate is re-exported asmujoco_rs::renderer::pngfor namingpng::Compression.MjModel:
extract_state/extract_state_into,try_make_data.MjData:
swap_modelnew (validates model signature),model_mutnew (-> &mut MjModel, available whenM: 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_clonenew (-> M, available whenM: Clone).MjSpec:
from_parse/from_parse_vfs.from_parse,from_parse_vfs, andsave_xmlnow acceptAsRef<Path>(was&str); returnsErr(MjEditError::InvalidUtf8Path)for non-UTF-8 paths.MjSpecandSpecItemare 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_fromreplaceadd_from_file(which is now deprecated).add_from_bufferanddelete_filenow acceptAsRef<Path>; returnsErr(MjVfsError::InvalidUtf8Path)for non-UTF-8 paths.MjvScene:
update_with_catmask(exposescatmaskfilter parameter).MjvCamera:
frame,frustum.New SceneSelection struct (returned by
find_selection); implementsDefault.The vis_common module is now public (requires
viewerorrendererfeature), exposingsync_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, andskin.dof_bodyidanddof_treeidare 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 existingset_textures/append_texturesmethods 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 existingset_cubefiles/append_cubefilesmethods are retained.info_with_view!structs now havetry_view/try_view_mutmethods.Trait additions:
Clonefor MjSpec;Defaultfor MjVfs, MjSpec,MjOption,MjRendererBuilder,MjViewerBuilder;Send+Syncfor MjVfs;FusedIteratorfor all model-editing iterators;EqforPointerView/PointerViewMut;DropforMjRenderer.PointerView::new,PointerViewMut::new,MjrRectangle::neware nowconst fn.MjModel and MjData now use
NonNull<mjModel>/NonNull<mjData>internally, encoding the non-null invariant at the type level and enabling niche optimisation forOption<MjModel>/Option<MjData>.MjData:
contacts()is deprecated; usecontact()instead.
MjViewer
Added support for the
Depthrendering 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_trackingand MjvScenenew:debug_assert!guards on user-suppliedcamera_id,tracking_id, andmax_geom(checking they fit ini32) were silently skipped in release builds. Changed toassert!so the precondition is always enforced. The# Panicsdocumentation 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 than0or1; 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
!= 0instead of== 1.-
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_scalesetting was silently ignored duringbuild(); now applied.
PointerView and PointerViewMut
PartialEqnow compares both pointer and length (previously pointer only).MjModel:
key_mposandkey_mquatarray accessors were missing; added.MjViewer: mouse normalization now uses
inner_size()instead ofouter_size(), fixing cursor mapping with window decorations.MjViewer now implements
Dropto make the GL context current before cleanup (prevents resource leaks on some platforms).-
FFI declaration used
boolforstate_only(should bec_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_auxnow has amjNAUX-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 mismatchon 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 incomingdata. Previously, models with free joints, ball joints, or joints with a non-zerorefattribute 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
Fixed security issue:
model_signaturefield on all*Infostructs (e.g.MjJointDataInfo) was public, allowing the signature to be overwritten to bypass the bounds guard intry_view/try_view_mut, which could cause out-of-bounds raw-pointer arithmetic (UB). The field is now private; use themodel_signature()getter instead.Fixed RendererError
Displayimpl: the four wrapper variants (GlutinError,GlInitFailed,IoError,SceneError) were discarding the inner error message, making error output non-actionable (e.g."scene error"instead of the actual cause). They now include the inner error: e.g."scene error: …".EventLoopErrorin bothRendererErrorandMjViewerErrorhad the same issue and was also fixed.Updated enum type aliases to match MuJoCo 3.6.0 definitions.
Added examples: simulation/tippe_top.rs, simulation/chaotic_pendulum.rs, visualization/renderer/contact_forces.rs, model_editing/multi_legged_creatures.rs, model_editing/procedural_tree.rs, testing/miri_test.rs, visualization/viewer/model_switch.rs, simulation/model_parameters.rs.
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)¶
2.3.0 (MuJoCo 3.3.7)¶
-
Added
add_ui_callback_detachedfor passive UI widgets.Added
with_ui_egui_ctxfor 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-fallbackfor compatibility (enabled by default).-
add_ui_callbackfor 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(deprecatessync) +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 byViewerSharedState::user_scene/user_scene_mut).
2.1.0 / 2.1.1 (MuJoCo 3.3.7)¶
Option to automatically download MuJoCo (
auto-download-mujocofeature).pkg-config support for Linux and macOS.
Optional egui UI for MjViewer (enabled by default).
2.0.1 (MuJoCo 3.3.7)¶
Fixed the
rendererfeature 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 toglfw libmujoco_simulate.MjData and related types now generic over model handle (
Deref<Target = MjModel>).Various
c_int→i32/booltype 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)¶
MjvScene:
pop_geommethod added.Model editing: iterators added to MjSpec and
MjsBody.
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_editingmodule).MjRenderer /
MjRendererBuilderimprovements.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 bynew_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)¶
Fixed data-race bug #18.
Fixed MjGeomDataView / MjGeomDataViewMut pointing to wrong address (#17).
Fixed spurious scene-buffer warning on some models (#19).
Added mujoco_rs::wrappers::mj_primitive module.
More MjData views: actuator, body, camera, geom, joint, light, sensor, site, tendon.
More MjModel views: actuator, body, camera, geom, joint, sensor, tendon.
1.0.1 (MuJoCo 3.3.5)¶
Bug fixes in
Dropimplementations.
1.0.0 (MuJoCo 3.3.5)¶
Breaking: all
ffi_mut()methods now requireunsafe.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.