Minor updates to documentation across multiple sections.

PiperOrigin-RevId: 898652783
Change-Id: Idb671910c18ed3406722b8a1307f97ae9a194139
This commit is contained in:
Yuval Tassa
2026-04-12 13:59:58 -07:00
committed by Copybara-Service
parent c08d181d52
commit 239aa1f856
11 changed files with 322 additions and 333 deletions
+3 -3
View File
@@ -3,7 +3,7 @@
Extensions
----------
This section describes MuJoCo's mechanisms for user-authored extensions. At present, extensibility is provided by
This section describes MuJoCo's mechanisms for user-authored extensions. At present, extensibility is provided
via :ref:`engine plugins<exPlugin>` and :ref:`resource providers<exProvider>`.
.. _exPlugin:
@@ -11,7 +11,7 @@ via :ref:`engine plugins<exPlugin>` and :ref:`resource providers<exProvider>`.
Engine plugins
~~~~~~~~~~~~~~
Engine plugins, introduced in MuJoCo 2.3.0, allow user-defined logic to be inserted into various parts of MuJoCo's
Engine plugins allow user-defined logic to be inserted into various parts of MuJoCo's
computational pipeline. For example, custom sensor and actuator types can be implemented as plugins. Plugin features are
referenced in the XML content of an MJCF model, allowing MJCF to remain an abstract physical description of
a system even if the simulation requirements extend beyond MuJoCo's built-in capabilities.
@@ -462,6 +462,6 @@ Now we can write assets as strings in our MJCF files:
<asset>
<texture name="grid" file="grid.png" type="2d"/>
<mesh content-type="model/obj" file="data:model/obj;base65,I215IG9iamVjdA0KdiAxIDAgMA0KdiAwIDEgMA0KdiAwIDAgMQ=="/>
<mesh content-type="model/obj" file="data:model/obj;base64,I215IG9iamVjdA0KdiAxIDAgMA0KdiAwIDEgMA0KdiAwIDAgMQ=="/>
...
</asset>
+8 -13
View File
@@ -31,9 +31,9 @@ OpenGL renderer
state-of-the-art rendering engines (and can be replaced with such an engine if desired) but nevertheless it provides
efficient and informative 3D rendering.
Thread
The Threading framework (new in MuJoCo 3.0) is written in C++ and exposed in C. It provides a ThreadPool interface
to process Tasks asynchronously. To enable use in MuJoCo, create a ThreadPool and assign it to the thread_pool field
in mjData.
The threading framework is written in C++ and exposed in C. It provides a :ref:`mjThreadPool<mjThreadPool>` interface
to process tasks asynchronously. To enable use in MuJoCo, create a thread pool and assign it to the
``mjData.threadpool`` field.
UI framework
The UI framework is written in C. UI elements are rendered in OpenGL. It has its own event
mechanism and abstract hooks for keyboard and mouse input. The code samples use it with GLFW, but it can also be used
@@ -80,7 +80,7 @@ working development environment. We provide a cross-platform `CMake
applications independently of the MuJoCo library itself.
On macOS, the DMG disk image contains ``MuJoCo.app``, which you can double-click to launch the ``simulate`` GUI. You can
also drag ``MuJoCo.app`` into the ``/Application`` on your system, as you would to install any other app. As well as the
also drag ``MuJoCo.app`` into the ``/Applications`` on your system, as you would to install any other app. As well as the
``MuJoCo.app`` `Application Bundle <https://developer.apple.com/go/?id=bundle-
structure>`__, the DMG includes the ``mujoco.framework`` subdirectory containing the MuJoCo dynamic library and all of
its public headers. If you are using Xcode, you can import it as a framework dependency on your project. (This also
@@ -94,7 +94,7 @@ Building from source
To build MuJoCo from source, you will need CMake and a working C++17 compiler installed. The steps are:
#. Clone the ``mujoco`` repository: ``git clone https://github.com/deepmind/mujoco.git``
#. Clone the ``mujoco`` repository: ``git clone https://github.com/google-deepmind/mujoco.git``
#. Create a new build directory and ``cd`` into it.
#. Run :shell:`cmake $PATH_TO_CLONED_REPO` to configure the build.
#. Run ``cmake --build .`` to build.
@@ -109,7 +109,7 @@ section of the documentation.
Additionally, the CMake setup also implements an installation phase which will copy and organize the output files to a
target directory.
5. Select the directory: :shell:`cmake $PATH_TO_CLONED_REPO -DCMAKE_INSTALL_PREFIX=<my_install_dir>`
#. Select the directory: :shell:`cmake $PATH_TO_CLONED_REPO -DCMAKE_INSTALL_PREFIX=<my_install_dir>`
#. After building, install with ``cmake --install .``
#. If desired, proceed to building the Python bindings - see
:ref:`PyBuild`.
@@ -132,7 +132,7 @@ Building the docs
If you wish to build the documentation locally, for example to test pull-requests that improve it, do:
1. Clone the ``mujoco`` repository: ``git clone https://github.com/deepmind/mujoco.git``
1. Clone the ``mujoco`` repository: ``git clone https://github.com/google-deepmind/mujoco.git``
2. Go to the ``doc/`` directory: ``cd mujoco/doc``
3. Install the dependencies: ``pip install -r requirements.txt``
|br| Note that the MuJoCo Warp API documentation is autogenerated and requires additional dependencies.
@@ -238,7 +238,7 @@ to which the symbol belongs. First we list the prefixes corresponding to type de
``mjui``
Data structure related to UI framework, for example :ref:`mjuiSection`.
``mjs``
Data structure related :doc:`procedural model editing <modeledit>`, for example :ref:`mjsJoint`.
Data structure related to :doc:`procedural model editing <modeledit>`, for example :ref:`mjsJoint`.
Next we list the prefixes corresponding to function definitions. Note that function prefixes always end with underscore.
@@ -280,11 +280,6 @@ thread. On Windows and macOS, there is a canonical OpenGL library provided by th
currently supports GLX for rendering to an X11 window, OSMesa for headless software rendering, and EGL for hardware
accelerated headless rendering.
Before version 2.1.4, MuJoCo used GLEW rather than GLAD to manage OpenGL symbols, which required linking against
different GLEW libraries at build time depending on the GL implementation used. In order to avoid having manage OpenGL
dependency when no rendering was required, "nogl" builds of the library was made available. Since OpenGL symbols are
now lazily resolved at runtime after the switch to GLAD, the "nogl" libraries are no longer provided.
.. toctree::
:hidden:
+12 -18
View File
@@ -1,14 +1,8 @@
Model Editing
-------------
.. admonition:: New API
:class: note
The API described below is new but feature complete. It is recommended for general use, but latent bugs are still
possible. Please report any issues on GitHub.
As of MuJoCo 3.2.0, it is possible to create and modify models using the :ref:`mjSpec` struct and related API.
This datastructure is in one-to-one correspondence with MJCF and indeed, MuJoCo's own XML parsers (both MJCF and URDF)
It is possible to create and modify models using the :ref:`mjSpec` struct and related API.
This data structure is in one-to-one correspondence with MJCF and indeed, MuJoCo's own XML parsers (both MJCF and URDF)
use this API when loading a model.
@@ -17,16 +11,16 @@ use this API when loading a model.
Overview
~~~~~~~~
The new API augments the traditional workflow of creating and editing models using XML files, breaking up the *parse* and
The API augments the traditional workflow of creating and editing models using XML files, breaking up the *parse* and
*compile* steps. As summarized in the :ref:`Overview chapter<Instance>`, the traditional workflow is:
1. Create an XML model description file (MJCF or URDF) and associated assets. |br|
2. Call :ref:`mj_loadXML`, obtain an :ref:`mjModel` instance.
The new workflow using :ref:`mjSpec` is:
The workflow using :ref:`mjSpec` is:
1. Create an empty :ref:`mjSpec` using :ref:`mj_makeSpec` or parse an existing XML file using :ref:`mj_parseXML`.
2. Programmatically edit the :ref:`mjSpec` datastructure by adding, modifying and removing elements.
2. Programmatically edit the :ref:`mjSpec` data structure by adding, modifying, and removing elements.
3. Compile the :ref:`mjSpec` to an :ref:`mjModel` instance using :ref:`mj_compile`.
After compilation, the :ref:`mjSpec` remains editable, so steps 2 and 3 are interchangeable.
@@ -40,9 +34,9 @@ Usage
Here we describe the C API for procedural model editing, but it is also exposed in the :ref:`Python
bindings<PyModelEdit>`. Advanced users can refer to `user_api_test.cc
<https://github.com/google-deepmind/mujoco/blob/main/test/user/user_api_test.cc>`__ and the MJCF parser in
`xml_native_reader.cc <https://github.com/google-deepmind/mujoco/blob/main/src/xml/xml_native_reader.cc>`__ for more
usage examples. After creating a new :ref:`mjSpec` or parsing an existing XML file to an :ref:`mjSpec`, procedural
editing corresponds to setting attributes. For example, in order to change the timestep, one can do:
`xml_native_reader.cc <https://github.com/google-deepmind/mujoco/blob/main/src/xml/xml_native_reader.cc>`__ for
more usage examples. After creating a new :ref:`mjSpec` or parsing an existing XML file to an :ref:`mjSpec`,
procedural editing corresponds to setting attributes. For example, in order to change the timestep, one can do:
.. code-block:: C
@@ -56,7 +50,7 @@ In C one uses the provided :ref:`getters<AttributeGetters>` and :ref:`setters<At
.. code-block:: C
mjs_setString(model->modelname, "my_model");
mjs_setString(spec->modelname, "my_model");
In C++, one can use vectors and strings directly:
@@ -76,7 +70,7 @@ Loading a spec from XML can be done as follows:
Model elements
^^^^^^^^^^^^^^
Model elements corresponding to MJCF are exposed to the user as C structs with the ``mjs`` prefix, the definitions are
Model elements corresponding to MJCF are exposed to the user as C structs with the ``mjs`` prefix. The definitions are
listed under the :ref:`Model Editing<tySpecStructure>` section of the struct reference. For example, an MJCF
:ref:`geom<body-geom>` corresponds to an :ref:`mjsGeom`.
@@ -120,7 +114,7 @@ Attachment
^^^^^^^^^^
This framework introduces a powerful new feature: attaching and deleting model subtrees. This feature is already used to
power the :ref:`attach<body-attach>` an :ref:`replicate<replicate>` meta-elements in MJCF. Attachment allows the user to
power the :ref:`attach<body-attach>` and :ref:`replicate<replicate>` meta-elements in MJCF. Attachment allows the user to
move or copy a subtree from one model into another, while also copying or moving related referenced assets and
referencing elements from outside the kinematic tree (e.g., actuators and sensors). Similarly, deleting a subtree will
remove all associated elements from the model. The default behavior ("shallow copy") is to move the child into the
@@ -199,7 +193,7 @@ already initialized elements.
.. admonition:: Possible future change
:class: note
The behaviour described above, where defaults are only applied at initialization, is a remnant of the old, XML-only
The behavior described above, where defaults are only applied at initialization, is a remnant of the old, XML-only
loading pipeline. A future API change could allow defaults to be changed and applied after initialization. If you
think this feature is important to you, please let us know on GitHub.
+13 -13
View File
@@ -13,7 +13,7 @@ initialized by the corresponding API functions. These are very elaborate data st
structures, preallocated data arrays for all intermediate results, as well as an :ref:`internal stack <siStack>`. Our
strategy is to allocate all necessary heap memory at the beginning of the simulation, and free it after the simulation
is done, so that we never have to call the C memory allocation and deallocation functions during the simulation. This is
done for speed, avoidance of memory fragmentation, future GPU portability, and ease of managing the state of the entire
done for speed, avoidance of memory fragmentation, GPU portability, and ease of managing the state of the entire
simulator during a reset. It also means however that the maximal variable-memory allocation given by the :at:`memory`
attribute in the :ref:`size <size>` MJCF element, which affects the allocation of :ref:`mjData`, must be set to a
sufficiently large value. If this maximal size is exceeded during simulation, it is not increased dynamically, but
@@ -331,6 +331,8 @@ Auxiliary Controls: ``qfrc_applied`` and ``xfrc_applied``
| Note that the effects of ``qfrc_applied`` and ``xfrc_applied`` can be recreated by appropriate actuator
definitions.
.. _siMocap:
MoCap poses: ``mocap_pos`` and ``mocap_quat``
``mjData.mocap_pos`` and ``mjData.mocap_quat`` are special optional kinematic states :ref:`described here<CMocap>`,
which allow the user to set the positions and orientations of static bodies in real-time, for example when streaming
@@ -560,14 +562,12 @@ external force computed by inverse dynamics.
Multi-threading
~~~~~~~~~~~~~~~
When MuJoCo is used for simulation as explained in the :ref:`simulation loop <siSimulation>` section, it runs in a
single thread. We have experimented with multi-threading parts of the simulation pipeline that are computationally
expensive and amenable to parallel processing, and have concluded that the speedup is not worth using up the extra
processor cores. This is because MuJoCo is already fast compared to the overhead of launching and synchronizing
multiple threads within the same time step. If users start working with large simulations involving many floating
bodies, we may eventually implement within-step multi-threading, but for now this use case is not common.
MuJoCo has experimental support for within-step multi-threading. When a :ref:`mjThreadPool` is assigned to
``mjData.threadpool``, parts of the simulation pipeline — such as collision detection and constraint solving across
:ref:`islands<siSleep>` — can be distributed across worker threads. Note that within-step threading currently has
significant memory overhead and is still a work in progress.
Rather than speed up a single simulation, we prefer to use multi-threading to speed up sampling operations that are
The more common and well-supported use of multi-threading is to speed up sampling operations that are
common in more advanced applications. Simulation is inherently serial over time (the output of one mj_step is the
input to the next), while in sampling many calls to either forward or inverse dynamics can be executed in parallel
since there are no dependencies among them, except perhaps for a common initial state.
@@ -784,7 +784,7 @@ difference between row-major and column-major formats.
When possible, MuJoCo exploits sparsity. This can make all the difference between O(N) and O(N^3) scaling. The inertia
matrix ``mjData.qM`` and its LTDL factorization ``mjData.qLD`` are always represented as sparse. ``qM`` uses a custom
indexing format designed for matrices that correspond to tree topology, while ``qLD`` uses the standard CSR format.
``qM`` will be migrated to CSR in and upcoming change. The functions :ref:`mj_factorM`, :ref:`mj_solveM`,
``qM`` will be migrated to CSR in an upcoming change. The functions :ref:`mj_factorM`, :ref:`mj_solveM`,
:ref:`mj_solveM2` and :ref:`mj_mulM` are used for sparse factorization, substitution and matrix-vector multiplication.
The user can also convert these matrices to dense format with the function :ref:`mj_fullM` although MuJoCo never does
that internally.
@@ -994,7 +994,7 @@ in MJCF which are sufficient for most models, and allow the user to adjust them
the simulator runs out of dynamic memory at runtime it will trigger an error. When such errors are triggered, the user
should increase :at:`memory`. The field ``mjData.maxuse_arena`` is designed to help with this adjustment. It keeps track
of the maximum arena use since the last reset. So one strategy is to make very large allocation, then monitor
``mjData.maxuse_memory`` statistics during typical simulations, and use it to reduce the allocation.
``mjData.maxuse_arena`` statistics during typical simulations, and use it to reduce the allocation.
The kinetic and potential energy are computed and stored in ``mjData.energy`` when the corresponding flag in
``mjModel.opt.enableflags`` is set. This can be used as another diagnostic. In general, simulation instability is
@@ -1061,7 +1061,7 @@ non-convex mesh collisions, or to replace some of the convex collision functions
beyond the ones provided by MuJoCo. The global 2D array :ref:`mjCOLLISIONFUNC` contains the collision function pointer
for each pair of geom types (in the upper-left triangle). To replace them, simply set these pointers to your
functions. The collision function type is :ref:`mjfCollision`. When user collision functions detect contacts, they
should construct an mjvContact structure for each contact and then call the function :ref:`mj_addContact` to add that
should construct an :ref:`mjContact` structure for each contact and then call the function :ref:`mj_addContact` to add that
contact to ``mjData.contact``. The reference documentation of mj_addContact explains which fields of mjContact must be
filled in by custom collision functions. Note that the functions we are talking about here correspond to near-phase
collisions, and are called only after the list of candidate geom pairs has been constructed by the internal
@@ -1176,7 +1176,7 @@ which are initialized asleep. These can be placed in mid-air or in deep collisio
Notes
^^^^^
.. admonition:: New feature
.. admonition:: Subject to change
:class: warning
Sleeping is a new feature (Nov 2025) that is subject to change and may have latent bugs.
@@ -1270,7 +1270,7 @@ Notes
The RK4 integrator is not currently supported, due to the subtleties of waking inside the sub-steps.
**Latent bugs**
Sleeping is a new feature (Nov 2025) and may have latent bugs. These bugs may generally come in two varieties:
Sleeping may have latent bugs. These bugs may generally come in two varieties:
- Quantities which could be skipped are instead recomputed. The only observable effect of such a bug would be that
the simulation is slower than it could be. This type of bug can only be diagnosed with detailed profiling.
+39 -54
View File
@@ -7,23 +7,23 @@ MuJoCo has a native 3D visualizer. Its use is illustrated in the :ref:`simulate.
the simpler :ref:`basic.cc <saBasic>` code sample. While it is not a full-featured rendering engine, it is a
convenient, efficient and reasonably good-looking visualizer that facilitates research and development. It renders not
only the simulation state but also decorative elements such as contact points and forces, equivalent inertia boxes,
convex hulls, kinematic trees, constraint violations, spatial frames and text labels; these can provide insight into
convex hulls, kinematic trees, constraint violations, spatial frames, and text labels; these can provide insight into
the physics simulation and help fine-tune the model.
The visualizer is tightly integrated with the simulator and supports both onscreen and offscreen rendering, as
illustrated in the :ref:`record.cc <saRecord>` code sample. This makes it suitable for synthetic computer vision and
machine learning applications, especially in cloud environments. VR integration is also available as of MuJoCo version
1.40, facilitating applications that utilize new head-mounted displays such as Oculus Rift and HTC Vive.
machine learning applications, especially in cloud environments. VR integration is also available, facilitating
applications that utilize head-mounted displays.
Visualization in MuJoCo is a two-stage process:
Abstract visualization and interaction
This stage populates the :ref:`mjvScene` data structure with a list of geometric objects, lights, cameras and
This stage populates the :ref:`mjvScene` data structure with a list of geometric objects, lights, cameras, and
everything else needed to produce a 3D rendering. It also provides abstract keyboard and mouse hooks for user
interaction. The relevant data structure and function names have the prefix ``mjv``.
OpenGL rendering
This stage takes the mjvScene data structure populated in the abstract visualization stage, and renders it. It also
provides basic 2d drawing and framebuffer access, so that most applications would not need to call OpenGL directly.
provides basic 2D drawing and framebuffer access, so that most applications would not need to call OpenGL directly.
The relevant data structure and function names have the prefix ``mjr``.
There are several reasons for this separation. First, the two stages are conceptually different and separating them is
@@ -105,8 +105,8 @@ Abstract visualization and interaction
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This stage populates the :ref:`mjvScene` data structure with a list of geometric objects,
lights, cameras and everything else needed to produce a 3D rendering. It also provides abstract keyboard and mouse hooks
for user interaction.
lights, cameras, and everything else needed to produce a 3D rendering. It also provides abstract keyboard and mouse
hooks for user interaction.
.. _viCamera:
@@ -125,14 +125,14 @@ are defined by the enum mjtCamera:
mjCAMERA_FREE
This is the most commonly used abstract camera. It can be freely moved with the mouse. It has a lookat point,
distance to the lookat point, azimuth and elevation; twist around the line of sight is not allowed. The function
:ref:`mjv_moveCamera` is a mouse hook for controlling all these camera properties interactively with the mouse. When
:ref:`simulate.cc <saSimulate>` first starts, it uses the free camera.
distance to the lookat point, azimuth, and elevation; twist around the line of sight is not allowed. The function
:ref:`mjv_moveCamera` is a mouse hook for controlling all these camera properties interactively with the mouse.
When :ref:`simulate.cc <saSimulate>` first starts, it uses the free camera.
mjCAMERA_TRACKING
This is similar to the free camera, except the lookat point is no longer a free parameter but instead is coupled to
the MuJoCo body whose id is given by mjvCamera.trackbodyid. At each update, the lookat point is set to the center of
mass of the kinematic subtree rooted at the specified body. There is also some filtering which produces smooth camera
motion. The distance, azimuth and elevation are controlled by the user and are not modified automatically. This is
motion. The distance, azimuth, and elevation are controlled by the user and are not modified automatically. This is
useful for tracking a body as it moves around, without turning the camera. To switch from the free to the tracking
camera in :ref:`simulate.cc <saSimulate>`, hold Ctrl and right-double-click on the body of interest. Press Esc to go
back to the free camera.
@@ -171,7 +171,7 @@ because it needs information about the camera and viewport.
The function mjv_select returns the index of the geom at the specified window coordinates, or -1 if there is no geom
at those coordinates. The 3D position is also returned. See the code sample :ref:`simulate.cc <saSimulate>` for an
example of how to use this function. Internally, mjv_select calls the engine-level function :ref:`mj_ray` which in turn
calls the per-geom functions :ref:`mj_rayMesh`, :ref:`mj_rayHfield` and :ref:`mju_rayGeom`. The user can implement
calls the per-geom functions :ref:`mj_rayMesh`, :ref:`mj_rayHfield`, and :ref:`mju_rayGeom`. The user can implement
custom selection mechanisms by calling these functions directly. In a VR application for example, it would make sense to
use the hand-held controller as a "laser pointer" that can select objects.
@@ -184,9 +184,8 @@ Interactive perturbations have proven very useful in exploring the model dynamic
systems. The user is free to implement any perturbation mechanism of their choice by setting ``mjData.qfrc_applied`` or
``mjData.xfrc_applied`` to suitable forces (in generalized and Cartesian coordinates respectively).
Prior to MuJoCo version 1.40, user code had to maintain a collection of objects in order to implement perturbations.
All these objects are now grouped into the data structure :ref:`mjvPerturb`. Its use is illustrated in
:ref:`simulate.cc <saSimulate>`.
All objects needed to implement interactive perturbations are grouped into the data structure :ref:`mjvPerturb`.
Its use is illustrated in :ref:`simulate.cc <saSimulate>`.
The idea is to select a MuJoCo body of interest, and provide a reference pose (i.e., a 3D position and quaternion
orientation) for that body. These are stored in mjPerturb.refpos/quat. The function :ref:`mjv_movePerturb` is a mouse
hook for controlling the reference pose with the mouse. The function :ref:`mjv_initPerturb` is used to set the
@@ -275,40 +274,26 @@ Since we have introduced two spaces, namely model space and room space, we need
which spatial quantities are defined with respect to which spatial frame. Everything accessible by the simulator lives
in the model space. The room space is only accessible by the visualizer. The only quantities defined in room space are
the mjvGLCamera parameters. The functions :ref:`mjv_room2model`, :ref:`mjv_model2room`, :ref:`mjv_cameraInModel`,
:ref:`mjv_cameraInRoom` perform the necessary transformations, and are needed for VR applications.
and :ref:`mjv_cameraInRoom` perform the necessary transformations, and are needed for VR applications.
We now outline the procedure for hooking up head tracking to MuJoCo's visualizer in a VR application. A code sample
illustrating this will soon be posted. We assume that a tracking device provides in real-time the positions of the two
eyes (usually generated by tracking the position and orientation of the head and assuming a user-specific ipd), as
well as the forward and up camera directions. We copy these data directly into the two mjvGLCameras, which are in
mjvScene.camera[n] where n=0 is the left eye and n=1 is the right eye. Note that the forward direction is normal to
the projection surface, and not necessarily aligned with the gaze direction; indeed the gaze direction is unknown
(unless we also have an eye-tracking device) and does not affect the rendering.
While MuJoCo does not provide a built-in VR application, it provides data structures and functions to support VR
integration in user code.
We must also set the mjvGLCamera frustum. How this is done depends on the nature of the VR system. For head-mounted
displays such as the Oculus Rift and HTC Vive, the projection surface moves with the head, and so the frustum is fixed
and provided by the SDK. In this case we simply copy it into mjvGLCamera, averaging the left and right edges to
compute the frustum_center parameter. Alternatively, the projection surface can be a monitor which is stationary in
the room (which is the case in the zSpace system). For such systems we must compute the frustum at each frame, by
taking into account the spatial relations between the monitor and the eyes/cameras. This assumes that the monitor is
also tracked. The natural approach here is to define the monitor as the center of the room coordinate frame, and track
the head relative to it. In the zSpace system this is done by embedding the motion capture cameras in the monitor
itself.
**Head tracking and cameras**
In a typical VR application, a tracking device provides the positions and orientations of the user's eyes in
real-time. These data can be copied directly into the two ``mjvGLCamera`` structures in ``mjvScene.camera[n]``
(where ``n=0`` is the left eye and ``n=1`` is the right eye). The ``mjvGLCamera`` frustum parameters must also be
set according to the physical characteristics of the tracked display.
Apart from tracking the head and using the correct perspective projection, VR applications typically involve hand-held
spatial controllers that must be mapped to the motion of simulated objects or otherwise interact with the simulation.
The pose of these controllers is recorded by the motion capture system in room space. The transformation functions we
provide (mjv_room2model in particular) can be used to map to model space. Once we have the pose of the controller in
model space, we can use a MuJoCo mocap body (defined in the model) to insert the controller in the simulation. This is
precisely why mocap bodies were introduced in MuJoCo. Such bodies are treated as fixed from the viewpoint of physics,
yet the user is expected to move them programmatically at each simulation step. They can interact with the simulation
through contacts, or better yet, through soft equality constraints to regular bodies which in turn make contacts. The
latter approach is illustrated in the MPL models available on the Forum. It provides effective dynamic filtering and
avoids contacts involving bodies that behave as if they are infinitely heavy (which is what a fixed body is). Note
that the time-varying positions and orientations of the mocap bodies are stored in ``mjData.mocap_pos/quat``, as opposed
to storing them in mjModel. This is because mjModel is supposed to remain constant. The fixed mocap body pose stored
in mjModel is only used at initialization and reset, when user code has not yet had a chance to update
mjData.mocap_pos/quat.
**Controllers and mocap bodies**
Hand-held spatial controllers are also tracked in room space. The function :ref:`mjv_room2model` can map these
poses to model space. Once in model space, the controller poses can be used to update the position of MuJoCo
*mocap bodies*. Mocap bodies are treated as fixed from the viewpoint of physics, yet the user is expected to move
them programmatically at each simulation step. They can interact with the simulation through contacts, or better
yet, through soft equality constraints to regular bodies which in turn make contacts. This provides effective
dynamic filtering and avoids contacts involving bodies that behave as if they are infinitely heavy. The
time-varying positions and orientations of the mocap bodies are stored in ``mjData.mocap_pos`` and
``mjData.mocap_quat``.
.. _Rendering:
@@ -316,14 +301,14 @@ OpenGL Rendering
~~~~~~~~~~~~~~~~
This stage takes the mjvScene data structure populated in the abstract visualization stage, and renders it. It also
provides basic 2d drawing and framebuffer access, so that most applications would not need to call OpenGL directly.
provides basic 2D drawing and framebuffer access, so that most applications would not need to call OpenGL directly.
.. _reContext:
Context and GPU resources
'''''''''''''''''''''''''
The first step in the rendering process is create the model-specific GPU context :ref:`mjrContext`. This is done by
The first step in the rendering process is to create the model-specific GPU context :ref:`mjrContext`. This is done by
first clearing the data structure with the function :ref:`mjr_defaultContext`, and then calling the function
:ref:`mjr_makeContext`. This was already illustrated earlier; the relevant code is:
@@ -371,7 +356,7 @@ mjrContext.currentBuffer which changes whenever the active buffer changes. Some
because the user can upload modified resources with the functions :ref:`mjr_uploadTexture`, :ref:`mjr_uploadMesh`,
:ref:`mjr_uploadHField`. This can be used to achieve dynamic effects such as inserting a video feed into the
rendering, or modulating a terrain map. Such modifications affect the resources residing on the GPU, but their OpenGL
names are reused, thus the change is not actually visible in mjrContext.
names are reused; thus, the change is not actually visible in mjrContext.
The user should **never** make changes to mjrContext directly. MuJoCo's renderer assumes that only it can manage
mjrContext. In fact this kind of object would normally be opaque and its internal structure would not be exposed to
@@ -434,7 +419,7 @@ be obtained with the function :ref:`mjr_maxViewport`. Note that while the offscr
window buffer size changes whenever the user resizes or maximizes the window. Therefore user code should not assume
fixed viewport size. In the code sample :ref:`simulate.cc <saSimulate>` we use a callback which is triggered whenever
the window size changes, while in :ref:`basic.cc <saBasic>` we simply check the window size every time we render. On
certain scaled displays (only on OSX it seems) the window size and framebuffer size can be different. So if you are
certain scaled displays (notably on MacOS) the window size and framebuffer size can be different. So if you are
getting the size with GLFW functions, use glfwGetFramebufferSize rather than glfwGetWindowSize. On the other hand,
mouse coordinates are returned by the operating system in window rather than framebuffer units; thus the mouse
interaction functions discussed earlier should use glfwGetWindowSize to obtain the window height needed to normalize
@@ -468,10 +453,10 @@ mjSTEREO_SIDEBYSIDE
side. In principle users can cross their eyes and see stereo on a regular monitor, but the goal here is to show it in
a stereoscopic device. Most head-mounted displays support this stereo mode.
In addition to the main mjr_render function, we provide several functions for "decorating" the image. These are 2d
rendering functions and include :ref:`mjr_overlay`, :ref:`mjr_text`, :ref:`mjr_rectangle`, :ref:`mjr_figure`. The user
can draw additional decorations with their own OpenGL code. This should be done after mjr_render, because mjr_render
clears the viewport.
In addition to the main mjr_render function, we provide several functions for "decorating" the image. These are 2D
rendering functions and include :ref:`mjr_overlay`, :ref:`mjr_text`, :ref:`mjr_rectangle`, and :ref:`mjr_figure`. The
user can draw additional decorations with their own OpenGL code. This should be done after mjr_render, because
mjr_render clears the viewport.
We also provide the functions :ref:`mjr_finish` and :ref:`mjr_getError` for explicit synchronization with the GPU and
for OpenGL error checking. They simply call glFinish and glGetError internally. This together with the basic 2d