diff --git a/doc/APIreference/APItypes.rst b/doc/APIreference/APItypes.rst index d275b011..f24b71b1 100644 --- a/doc/APIreference/APItypes.rst +++ b/doc/APIreference/APItypes.rst @@ -814,6 +814,17 @@ This structure contains everything needed to render the 3D scene in OpenGL. .. mujoco-include:: mjvScene +.. _mjvSceneState: + +mjvSceneState +~~~~~~~~~~~~~ + +This structure contains the portions of :ref:`mjModel` and :ref:`mjData` that are required for +various ``mjv_*`` functions. + +.. mujoco-include:: mjvScene + + .. _mjvFigure: mjvFigure diff --git a/doc/APIreference/functions.rst b/doc/APIreference/functions.rst index 40d1db35..c4973470 100644 --- a/doc/APIreference/functions.rst +++ b/doc/APIreference/functions.rst @@ -1332,6 +1332,15 @@ mjv_moveCamera Move camera with mouse; action is mjtMouse. +.. _mjv_moveCameraFromState: + +mjv_moveCameraFromState +~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjv_moveCameraFromState + +Move camera with mouse given a scene state; action is mjtMouse. + .. _mjv_movePerturb: mjv_movePerturb @@ -1341,6 +1350,15 @@ mjv_movePerturb Move perturb object with mouse; action is mjtMouse. +.. _mjv_movePerturbFromState: + +mjv_movePerturbFromState +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjv_movePerturbFromState + +Move perturb object with mouse given a scene state; action is mjtMouse. + .. _mjv_moveModel: mjv_moveModel @@ -1483,6 +1501,51 @@ mjv_updateScene Update entire scene given model state. +.. _mjv_updateSceneFromState: + +mjv_updateSceneFromState +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjv_updateSceneFromState + +Update entire scene from a scene state, return the number of new mjWARN_VGEOMFULL warnings. + +.. _mjv_defaultSceneState: + +mjv_defaultSceneState +~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjv_defaultSceneState + +Set default scene state. + +.. _mjv_makeSceneState: + +mjv_makeSceneState +~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjv_makeSceneState + +Allocate resources and initialize a scene state object. + +.. _mjv_freeSceneState: + +mjv_freeSceneState +~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjv_freeSceneState + +Free scene state. + +.. _mjv_updateSceneState: + +mjv_updateSceneState +~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjv_updateSceneState + +Update a scene state from model and data. + .. _mjv_addGeoms: mjv_addGeoms @@ -1572,6 +1635,15 @@ mjr_freeContext Free resources in custom OpenGL context, set to default. +.. _mjr_resizeOffscreen: + +mjr_resizeOffscreen +~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjr_resizeOffscreen + +Resize offscreen buffers. + .. _mjr_uploadTexture: mjr_uploadTexture diff --git a/doc/changelog.rst b/doc/changelog.rst index 2e5d7294..9a748139 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -35,6 +35,10 @@ Python bindings state concurrently with the internal ``mj_forward``, resulting in e.g. `MuJoCo stack overflow error `_ or `segmentation fault `_. +- The ``viewer.launch_passive`` function now returns a handle which can be used to interact with the viewer. The passive + viewer now also requires an explicit call to ``sync`` on its handle to pick up any update to the physics state. This + is to avoid race conditions that can result in visual artifacts. See :ref:`documentation` for details. +- The ``viewer.launch_repl`` function has been removed since its functionality is superceded by ``launch_passive``. - Added a small number of missing struct fields discovered through the new ``introspect`` metadata. Bug fixes @@ -102,7 +106,7 @@ Python bindings #. Added ``viewer.launch_passive`` which launches the interactive viewer in a passive, non-blocking mode. Calls to ``launch_passive`` return immediately, allowing user code to continue execution, with the viewer automatically reflecting any changes to the physics state. (Note that this functionality is currently in experimental/beta stage, - and is not yet described in our :ref:`viewer documentation`.) + and is not yet described in our :ref:`viewer documentation`.) #. Added the ``mjpython`` launcher for macOS, which is required for ``viewer.launch_passive`` to function there. #. Removed ``efc_`` fields from joint indexers. Since the introduction of arena memory, these fields now have dynamic sizes that change between time steps depending on the number of active constraints, breaking strict correspondence diff --git a/doc/includes/references.h b/doc/includes/references.h index 1a0d30d9..f4884fe6 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -673,7 +673,6 @@ struct mjVisual_ { // visualization options float realtime; // initial real-time factor (1: real time) int offwidth; // width of offscreen buffer int offheight; // height of offscreen buffer - int treedepth; // depth of the bounding volume hierarchy int ellipsoidinertia; // geom for inertia visualization (0: box, 1: ellipsoid) } global; @@ -1779,6 +1778,7 @@ struct mjvOption_ { // abstract visualization options mjtByte actuatorgroup[mjNGROUP]; // actuator visualization by group mjtByte skingroup[mjNGROUP]; // skin visualization by group mjtByte flags[mjNVISFLAG]; // visualization flags (indexed by mjtVisFlag) + int bvh_depth; // depth of the bounding volume hierarchy to be visualized }; typedef struct mjvOption_ mjvOption; struct mjvScene_ { // abstract scene passed to OpenGL renderer @@ -1865,6 +1865,219 @@ struct mjvFigure_ { // abstract 2D figure passed to OpenGL rendere float yaxisdata[2]; // range of y-axis in data units }; typedef struct mjvFigure_ mjvFigure; +struct mjvSceneState_ { + int nbuffer; // size of the buffer in bytes + void* buffer; // heap-allocated memory for all arrays in this struct + int maxgeom; // maximum number of mjvGeom supported by this state object + mjvScene plugincache; // scratch space for vis geoms inserted by plugins + + // fields in mjModel that are necessary to re-render a scene + struct { + int nu; + int na; + int nbody; + int nbvh; + int njnt; + int ngeom; + int nsite; + int ncam; + int nlight; + int nmesh; + int nskin; + int nskinvert; + int nskinface; + int nskinbone; + int nskinbonevert; + int nmat; + int neq; + int ntendon; + int nwrap; + int nsensor; + int nnames; + int nsensordata; + + mjOption opt; + mjVisual vis; + mjStatistic stat; + + int* body_parentid; + int* body_rootid; + int* body_weldid; + int* body_mocapid; + int* body_jntnum; + int* body_jntadr; + int* body_geomnum; + int* body_geomadr; + mjtNum* body_iquat; + mjtNum* body_mass; + mjtNum* body_inertia; + int* body_bvhadr; + int* body_bvhnum; + + int* bvh_depth; + int* bvh_child; + int* bvh_geomid; + mjtNum* bvh_aabb; + + int* jnt_type; + int* jnt_bodyid; + int* jnt_group; + + int* geom_type; + int* geom_bodyid; + int* geom_dataid; + int* geom_matid; + int* geom_group; + mjtNum* geom_size; + mjtNum* geom_aabb; + mjtNum* geom_rbound; + float* geom_rgba; + + int* site_type; + int* site_bodyid; + int* site_matid; + int* site_group; + mjtNum* site_size; + float* site_rgba; + + mjtNum* cam_fovy; + mjtNum* cam_ipd; + + mjtByte* light_directional; + mjtByte* light_castshadow; + mjtByte* light_active; + float* light_attenuation; + float* light_cutoff; + float* light_exponent; + float* light_ambient; + float* light_diffuse; + float* light_specular; + + int* mesh_texcoordadr; + int* mesh_graphadr; + + int* skin_matid; + int* skin_group; + float* skin_rgba; + float* skin_inflate; + int* skin_vertadr; + int* skin_vertnum; + int* skin_texcoordadr; + int* skin_faceadr; + int* skin_facenum; + int* skin_boneadr; + int* skin_bonenum; + float* skin_vert; + int* skin_face; + int* skin_bonevertadr; + int* skin_bonevertnum; + float* skin_bonebindpos; + float* skin_bonebindquat; + int* skin_bonebodyid; + int* skin_bonevertid; + float* skin_bonevertweight; + + int* mat_texid; + mjtByte* mat_texuniform; + float* mat_texrepeat; + float* mat_emission; + float* mat_specular; + float* mat_shininess; + float* mat_reflectance; + float* mat_rgba; + + int* eq_type; + int* eq_obj1id; + int* eq_obj2id; + mjtByte* eq_active; + mjtNum* eq_data; + + int* tendon_num; + int* tendon_matid; + int* tendon_group; + mjtByte* tendon_limited; + mjtNum* tendon_width; + mjtNum* tendon_range; + mjtNum* tendon_stiffness; + mjtNum* tendon_damping; + mjtNum* tendon_frictionloss; + mjtNum* tendon_lengthspring; + float* tendon_rgba; + + int* actuator_trntype; + int* actuator_dyntype; + int* actuator_trnid; + int* actuator_actadr; + int* actuator_actnum; + int* actuator_group; + mjtByte* actuator_ctrllimited; + mjtByte* actuator_actlimited; + mjtNum* actuator_ctrlrange; + mjtNum* actuator_actrange; + mjtNum* actuator_cranklength; + + int* sensor_type; + int* sensor_objid; + int* sensor_adr; + + int* name_bodyadr; + int* name_jntadr; + int* name_geomadr; + int* name_siteadr; + int* name_camadr; + int* name_lightadr; + int* name_eqadr; + int* name_tendonadr; + int* name_actuatoradr; + char* names; + } model; + + // fields in mjData that are necessary to re-render a scene + struct { + mjWarningStat warning[mjNWARNING]; + + int nefc; + int ncon; + + mjtNum time; + + mjtNum* act; + + mjtNum* ctrl; + mjtNum* xfrc_applied; + + mjtNum* sensordata; + + mjtNum* xpos; + mjtNum* xquat; + mjtNum* xmat; + mjtNum* xipos; + mjtNum* ximat; + mjtNum* xanchor; + mjtNum* xaxis; + mjtNum* geom_xpos; + mjtNum* geom_xmat; + mjtNum* site_xpos; + mjtNum* site_xmat; + mjtNum* cam_xpos; + mjtNum* cam_xmat; + mjtNum* light_xpos; + mjtNum* light_xdir; + + mjtNum* subtree_com; + + int* ten_wrapadr; + int* ten_wrapnum; + int* wrap_obj; + mjtNum* wrap_xpos; + + mjtByte* bvh_active; + + mjContact* contact; + mjtNum* efc_force; + } data; +}; +typedef struct mjvSceneState_ mjvSceneState; //----------------------------- MJAPI FUNCTIONS -------------------------------- void mj_defaultVFS(mjVFS* vfs); @@ -2020,8 +2233,14 @@ mjtNum mjv_frustumHeight(const mjvScene* scn); void mjv_alignToCamera(mjtNum res[3], const mjtNum vec[3], const mjtNum forward[3]); void mjv_moveCamera(const mjModel* m, int action, mjtNum reldx, mjtNum reldy, const mjvScene* scn, mjvCamera* cam); +void mjv_moveCameraFromState(const mjvSceneState* scnstate, int action, + mjtNum reldx, mjtNum reldy, + const mjvScene* scn, mjvCamera* cam); void mjv_movePerturb(const mjModel* m, const mjData* d, int action, mjtNum reldx, mjtNum reldy, const mjvScene* scn, mjvPerturb* pert); +void mjv_movePerturbFromState(const mjvSceneState* scnstate, int action, + mjtNum reldx, mjtNum reldy, + const mjvScene* scn, mjvPerturb* pert); void mjv_moveModel(const mjModel* m, int action, mjtNum reldx, mjtNum reldy, const mjtNum roomup[3], mjvScene* scn); void mjv_initPerturb(const mjModel* m, mjData* d, const mjvScene* scn, mjvPerturb* pert); @@ -2044,6 +2263,14 @@ void mjv_makeScene(const mjModel* m, mjvScene* scn, int maxgeom); void mjv_freeScene(mjvScene* scn); void mjv_updateScene(const mjModel* m, mjData* d, const mjvOption* opt, const mjvPerturb* pert, mjvCamera* cam, int catmask, mjvScene* scn); +int mjv_updateSceneFromState(const mjvSceneState* scnstate, const mjvOption* opt, + const mjvPerturb* pert, mjvCamera* cam, int catmask, + mjvScene* scn); +void mjv_defaultSceneState(mjvSceneState* scnstate); +void mjv_makeSceneState(const mjModel* m, const mjData* d, + mjvSceneState* scnstate, int maxgeom); +void mjv_freeSceneState(mjvSceneState* scnstate); +void mjv_updateSceneState(const mjModel* m, mjData* d, mjvSceneState* scnstate); void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* opt, const mjvPerturb* pert, int catmask, mjvScene* scn); void mjv_makeLights(const mjModel* m, mjData* d, mjvScene* scn); @@ -2054,6 +2281,7 @@ void mjr_makeContext(const mjModel* m, mjrContext* con, int fontscale); void mjr_changeFont(int fontscale, mjrContext* con); void mjr_addAux(int index, int width, int height, int samples, mjrContext* con); void mjr_freeContext(mjrContext* con); +void mjr_resizeOffscreen(int width, int height, mjrContext* con); void mjr_uploadTexture(const mjModel* m, const mjrContext* con, int texid); void mjr_uploadMesh(const mjModel* m, const mjrContext* con, int meshid); void mjr_uploadHField(const mjModel* m, const mjrContext* con, int hfieldid); diff --git a/doc/python.rst b/doc/python.rst index 844646a4..e90adaa2 100644 --- a/doc/python.rst +++ b/doc/python.rst @@ -117,19 +117,19 @@ As a reference, a working build configuration can be found in MuJoCo's Interactive viewer ================== -An interactive GUI viewer is available as part of the Python package. (This is the same viewer as the ``simulate`` -application that ships with the MuJoCo binary releases.) +An interactive GUI viewer is provided as part of the Python package in the ``mujoco.viewer`` module. This is the same +viewer as the ``simulate`` application that ships with the MuJoCo binary releases. Three distinct use cases are supported: -#. Launching as a standalone application: +#. As a **standalone application**: - ``python -m mujoco.viewer`` launches an empty visualization session, where a model can be loaded by drag-and-drop. - ``python -m mujoco.viewer --mjcf=/path/to/some/mjcf.xml`` launches a visualization session for the specified model file. -#. Launching from a Python program/script -- import the module via ``from mujoco import viewer`` and launch the GUI - using one of the following invocations: +#. As a **fully managed viewer** in a Python program/script, through the function ``viewer.launch``. This function + **blocks the user's script completely** to take care of running and timing a physics loop. - ``viewer.launch()`` launches an empty visualization session, where a model can be loaded by drag-and-drop. - ``viewer.launch(model)`` launches a visualization session for the given ``mjModel`` where the visualizer @@ -137,13 +137,79 @@ Three distinct use cases are supported: - ``viewer.launch(model, data)`` is the same as above, except that the visualizer operates directly on the given ``mjData`` instance -- upon exit the ``data`` object will have been modified. -#. Launching from an interactive Python session (aka REPL): when working interactively either in a ``python`` or - ``ipython`` shell, the visualizer can be launched in a "passive" mode via ``viewer.launch_repl(model, data)``, where - the user remains in full control of modifying or stepping the physics. In this mode, the user can interact with the - visualizer using the mouse and keyboard as usual, however the physics will be frozen unless the user explicitly calls - ``mj_step`` (or perform any other modification of the ``mjData`` or ``mjModel``) in the REPL terminal. Note that since - the visualizer does not modify ``mjData`` in this mode, mouse-drag perturbations will not work unless the user - explicitly handles incoming GUI perturbation events in the REPL session. +#. As a **passive viewer**, by calling ``viewer.launch_passive(model, data)``. This function **does not block**, + allowing the user script to continue execution. In this mode, the user's script is responsible for timing and + advancing the physics state, and mouse-drag perturbations will not work unless the user explicitly handles incoming + events. + + .. warning:: + On macOS, ``launch_passive`` requires that the user script is executed via a special ``mjpython`` launcher. + The ``mjpython`` command is installed as part of the ``mujoco`` package, and can be used as a drop-in replacement + for the usual ``python`` command and supports an identical set of command line flags and arguments. For example, + a script can be executed via ``mjpython my_script.py``, and an IPython shell can be launched via + ``mjpython -m IPython``. + + The ``launch_passive`` function returns a handle which can be used to interact with the viewer. It has the following + attributes: + + - ``scn``, ``cam``, ``opt``, and ``pert`` properties: correspond to :ref:`mjvScene`, :ref:`mjvCamera`, + :ref:`mjvOption`, and :ref:`mjvPerturb` structs, respectively. + + - ``lock()``: provides a mutex lock for the viewer as a context manager. Since the viewer operates its own + thread, user code must ensure that it is holding the viewer lock before modifying any physics or visualization + state. These include the ``mjModel`` and ``mjData`` instance passed to ``launch_passive``, and also the ``scn``, + ``cam``, ``opt``, and ``pert`` properties of the viewer handle. + + - ``sync()``: synchronizes state between ``mjModel``, ``mjData``, and GUI user inputs since the previous call to + ``sync``. In order to allow user scripts to make arbitrary modifications to ``mjModel`` and ``mjData`` without + needing to hold the viewer lock, the passive viewer does not access or modify these structs outside of ``sync`` + calls. + + User scripts must call ``sync`` in order for the viewer to reflect physics state changes. The ``sync`` function + also transfers user inputs from the GUI back into ``mjOption`` (inside ``mjModel``) and ``mjData``, including + enable/disable flags, control inputs, and mouse perturbations. + + - ``close()``: programmatically closes the viewer window. This method can be safely called without locking. + + - ``is_running()``: returns ``True`` if the viewer window is running and ``False`` if it is closed. + This method can be safely called without locking. + + The viewer handle can also be used as a context manager which calls ``close()`` automatically upon exit. A minimal + example of a user script that uses ``launch_passive`` might look like the following. (Note that example is a simple + illustrative example that does **not** necessarily keep the physics ticking at the correct wallclock rate.) + + .. code-block:: python + + import time + + import mujoco + import mujoco.viewer + + m = mujoco.MjModel.from_xml_path('/path/to/mjcf.xml') + d = mujoco.MjData(m) + + with mujoco.viewer.launch_passive(m, d) as viewer: + # Close the viewer automatically after 30 seconds. + start = time.time() + while viewer.is_running() and time.time() - start < 30: + step_start = time.time() + + # The mj_step call can be replaced with a user-defined function that evaluates + # a policy, applies a control signal, and steps an environment. + mujoco.mj_step(m, d) + # Example of modifying a viewer option: toggle contact points every second. + with viewer.lock(): + viewer.opt.flags[mujoco.mjtVisFlag.mjVIS_CONTACTPOINT] = int(d.time % 2) + + # Synchronize so that the viewer picks up changes to the physics state. + viewer.sync() + + # Rudimentary time keeping, doesn't attempt to catch up if physics stepping + # takes too long. + time_until_next_step = m.opt.timestep - (time.time() - step_start) + if time_until_next_step > 0: + time.sleep(time_until_next_step) + .. _PyUsage: diff --git a/include/mujoco/mjmodel.h b/include/mujoco/mjmodel.h index 87113392..3eb59be9 100644 --- a/include/mujoco/mjmodel.h +++ b/include/mujoco/mjmodel.h @@ -447,7 +447,6 @@ struct mjVisual_ { // visualization options float realtime; // initial real-time factor (1: real time) int offwidth; // width of offscreen buffer int offheight; // height of offscreen buffer - int treedepth; // depth of the bounding volume hierarchy int ellipsoidinertia; // geom for inertia visualization (0: box, 1: ellipsoid) } global; diff --git a/include/mujoco/mjvisualize.h b/include/mujoco/mjvisualize.h index 818e424d..7a035d79 100644 --- a/include/mujoco/mjvisualize.h +++ b/include/mujoco/mjvisualize.h @@ -15,6 +15,8 @@ #ifndef MUJOCO_MJVISUALIZE_H_ #define MUJOCO_MJVISUALIZE_H_ +#include +#include #include @@ -265,6 +267,7 @@ struct mjvOption_ { // abstract visualization options mjtByte actuatorgroup[mjNGROUP]; // actuator visualization by group mjtByte skingroup[mjNGROUP]; // skin visualization by group mjtByte flags[mjNVISFLAG]; // visualization flags (indexed by mjtVisFlag) + int bvh_depth; // depth of the bounding volume hierarchy to be visualized }; typedef struct mjvOption_ mjvOption; @@ -360,4 +363,221 @@ struct mjvFigure_ { // abstract 2D figure passed to OpenGL rendere }; typedef struct mjvFigure_ mjvFigure; + +//---------------------------------- mjvSceneState ------------------------------------------------- + +struct mjvSceneState_ { + int nbuffer; // size of the buffer in bytes + void* buffer; // heap-allocated memory for all arrays in this struct + int maxgeom; // maximum number of mjvGeom supported by this state object + mjvScene plugincache; // scratch space for vis geoms inserted by plugins + + // fields in mjModel that are necessary to re-render a scene + struct { + int nu; + int na; + int nbody; + int nbvh; + int njnt; + int ngeom; + int nsite; + int ncam; + int nlight; + int nmesh; + int nskin; + int nskinvert; + int nskinface; + int nskinbone; + int nskinbonevert; + int nmat; + int neq; + int ntendon; + int nwrap; + int nsensor; + int nnames; + int nsensordata; + + mjOption opt; + mjVisual vis; + mjStatistic stat; + + int* body_parentid; + int* body_rootid; + int* body_weldid; + int* body_mocapid; + int* body_jntnum; + int* body_jntadr; + int* body_geomnum; + int* body_geomadr; + mjtNum* body_iquat; + mjtNum* body_mass; + mjtNum* body_inertia; + int* body_bvhadr; + int* body_bvhnum; + + int* bvh_depth; + int* bvh_child; + int* bvh_geomid; + mjtNum* bvh_aabb; + + int* jnt_type; + int* jnt_bodyid; + int* jnt_group; + + int* geom_type; + int* geom_bodyid; + int* geom_dataid; + int* geom_matid; + int* geom_group; + mjtNum* geom_size; + mjtNum* geom_aabb; + mjtNum* geom_rbound; + float* geom_rgba; + + int* site_type; + int* site_bodyid; + int* site_matid; + int* site_group; + mjtNum* site_size; + float* site_rgba; + + mjtNum* cam_fovy; + mjtNum* cam_ipd; + + mjtByte* light_directional; + mjtByte* light_castshadow; + mjtByte* light_active; + float* light_attenuation; + float* light_cutoff; + float* light_exponent; + float* light_ambient; + float* light_diffuse; + float* light_specular; + + int* mesh_texcoordadr; + int* mesh_graphadr; + + int* skin_matid; + int* skin_group; + float* skin_rgba; + float* skin_inflate; + int* skin_vertadr; + int* skin_vertnum; + int* skin_texcoordadr; + int* skin_faceadr; + int* skin_facenum; + int* skin_boneadr; + int* skin_bonenum; + float* skin_vert; + int* skin_face; + int* skin_bonevertadr; + int* skin_bonevertnum; + float* skin_bonebindpos; + float* skin_bonebindquat; + int* skin_bonebodyid; + int* skin_bonevertid; + float* skin_bonevertweight; + + int* mat_texid; + mjtByte* mat_texuniform; + float* mat_texrepeat; + float* mat_emission; + float* mat_specular; + float* mat_shininess; + float* mat_reflectance; + float* mat_rgba; + + int* eq_type; + int* eq_obj1id; + int* eq_obj2id; + mjtByte* eq_active; + mjtNum* eq_data; + + int* tendon_num; + int* tendon_matid; + int* tendon_group; + mjtByte* tendon_limited; + mjtNum* tendon_width; + mjtNum* tendon_range; + mjtNum* tendon_stiffness; + mjtNum* tendon_damping; + mjtNum* tendon_frictionloss; + mjtNum* tendon_lengthspring; + float* tendon_rgba; + + int* actuator_trntype; + int* actuator_dyntype; + int* actuator_trnid; + int* actuator_actadr; + int* actuator_actnum; + int* actuator_group; + mjtByte* actuator_ctrllimited; + mjtByte* actuator_actlimited; + mjtNum* actuator_ctrlrange; + mjtNum* actuator_actrange; + mjtNum* actuator_cranklength; + + int* sensor_type; + int* sensor_objid; + int* sensor_adr; + + int* name_bodyadr; + int* name_jntadr; + int* name_geomadr; + int* name_siteadr; + int* name_camadr; + int* name_lightadr; + int* name_eqadr; + int* name_tendonadr; + int* name_actuatoradr; + char* names; + } model; + + // fields in mjData that are necessary to re-render a scene + struct { + mjWarningStat warning[mjNWARNING]; + + int nefc; + int ncon; + + mjtNum time; + + mjtNum* act; + + mjtNum* ctrl; + mjtNum* xfrc_applied; + + mjtNum* sensordata; + + mjtNum* xpos; + mjtNum* xquat; + mjtNum* xmat; + mjtNum* xipos; + mjtNum* ximat; + mjtNum* xanchor; + mjtNum* xaxis; + mjtNum* geom_xpos; + mjtNum* geom_xmat; + mjtNum* site_xpos; + mjtNum* site_xmat; + mjtNum* cam_xpos; + mjtNum* cam_xmat; + mjtNum* light_xpos; + mjtNum* light_xdir; + + mjtNum* subtree_com; + + int* ten_wrapadr; + int* ten_wrapnum; + int* wrap_obj; + mjtNum* wrap_xpos; + + mjtByte* bvh_active; + + mjContact* contact; + mjtNum* efc_force; + } data; +}; +typedef struct mjvSceneState_ mjvSceneState; + #endif // MUJOCO_MJVISUALIZE_H_ diff --git a/include/mujoco/mjxmacro.h b/include/mujoco/mjxmacro.h index 93575e96..43667ffc 100644 --- a/include/mujoco/mjxmacro.h +++ b/include/mujoco/mjxmacro.h @@ -62,71 +62,71 @@ // int fields of mjModel #define MJMODEL_INTS \ - X( nq ) \ - X( nv ) \ - X( nu ) \ - X( na ) \ - X( nbody ) \ - X( nbvh ) \ - X( njnt ) \ - X( ngeom ) \ - X( nsite ) \ - X( ncam ) \ - X( nlight ) \ - X( nmesh ) \ - X( nmeshvert ) \ - X( nmeshnormal ) \ - X( nmeshtexcoord ) \ - X( nmeshface ) \ - X( nmeshgraph ) \ - X( nskin ) \ - X( nskinvert ) \ - X( nskintexvert ) \ - X( nskinface ) \ - X( nskinbone ) \ - X( nskinbonevert ) \ - X( nhfield ) \ - X( nhfielddata ) \ - X( ntex ) \ - X( ntexdata ) \ - X( nmat ) \ - X( npair ) \ - X( nexclude ) \ - X( neq ) \ - X( ntendon ) \ - X( nwrap ) \ - X( nsensor ) \ - X( nnumeric ) \ - X( nnumericdata ) \ - X( ntext ) \ - X( ntextdata ) \ - X( ntuple ) \ - X( ntupledata ) \ - X( nkey ) \ - X( nmocap ) \ - X( nplugin ) \ - X( npluginattr ) \ - X( nuser_body ) \ - X( nuser_jnt ) \ - X( nuser_geom ) \ - X( nuser_site ) \ - X( nuser_cam ) \ - X( nuser_tendon ) \ - X( nuser_actuator ) \ - X( nuser_sensor ) \ - X( nnames ) \ - X( nnames_map ) \ - X( nM ) \ - X( nD ) \ - X( nB ) \ - X( nemax ) \ - X( njmax ) \ - X( nconmax ) \ - X( nstack ) \ - X( nuserdata ) \ - X( nsensordata ) \ - X( npluginstate ) \ - X( nbuffer ) + X ( nq ) \ + X ( nv ) \ + XMJV( nu ) \ + XMJV( na ) \ + XMJV( nbody ) \ + XMJV( nbvh ) \ + XMJV( njnt ) \ + XMJV( ngeom ) \ + XMJV( nsite ) \ + XMJV( ncam ) \ + XMJV( nlight ) \ + XMJV( nmesh ) \ + X ( nmeshvert ) \ + X ( nmeshnormal ) \ + X ( nmeshtexcoord ) \ + X ( nmeshface ) \ + X ( nmeshgraph ) \ + XMJV( nskin ) \ + XMJV( nskinvert ) \ + X ( nskintexvert ) \ + XMJV( nskinface ) \ + XMJV( nskinbone ) \ + XMJV( nskinbonevert ) \ + X ( nhfield ) \ + X ( nhfielddata ) \ + X ( ntex ) \ + X ( ntexdata ) \ + XMJV( nmat ) \ + X ( npair ) \ + X ( nexclude ) \ + XMJV( neq ) \ + XMJV( ntendon ) \ + XMJV( nwrap ) \ + XMJV( nsensor ) \ + X ( nnumeric ) \ + X ( nnumericdata ) \ + X ( ntext ) \ + X ( ntextdata ) \ + X ( ntuple ) \ + X ( ntupledata ) \ + X ( nkey ) \ + X ( nmocap ) \ + X ( nplugin ) \ + X ( npluginattr ) \ + X ( nuser_body ) \ + X ( nuser_jnt ) \ + X ( nuser_geom ) \ + X ( nuser_site ) \ + X ( nuser_cam ) \ + X ( nuser_tendon ) \ + X ( nuser_actuator ) \ + X ( nuser_sensor ) \ + XMJV( nnames ) \ + X ( nnames_map ) \ + X ( nM ) \ + X ( nD ) \ + X ( nB ) \ + X ( nemax ) \ + X ( njmax ) \ + X ( nconmax ) \ + X ( nstack ) \ + X ( nuserdata ) \ + XMJV( nsensordata ) \ + X ( npluginstate ) \ + X ( nbuffer ) // define symbols needed in MJMODEL_POINTERS (corresponding to number of columns) @@ -151,305 +151,307 @@ // pointer fields of mjModel +// XMJV means that the field is required to construct mjvScene +// (by default we define XMJV to be the same as X) #define MJMODEL_POINTERS \ - X( mjtNum, qpos0, nq, 1 ) \ - X( mjtNum, qpos_spring, nq, 1 ) \ - X( int, body_parentid, nbody, 1 ) \ - X( int, body_rootid, nbody, 1 ) \ - X( int, body_weldid, nbody, 1 ) \ - X( int, body_mocapid, nbody, 1 ) \ - X( int, body_jntnum, nbody, 1 ) \ - X( int, body_jntadr, nbody, 1 ) \ - X( int, body_dofnum, nbody, 1 ) \ - X( int, body_dofadr, nbody, 1 ) \ - X( int, body_geomnum, nbody, 1 ) \ - X( int, body_geomadr, nbody, 1 ) \ - X( mjtByte, body_simple, nbody, 1 ) \ - X( mjtByte, body_sameframe, nbody, 1 ) \ - X( mjtNum, body_pos, nbody, 3 ) \ - X( mjtNum, body_quat, nbody, 4 ) \ - X( mjtNum, body_ipos, nbody, 3 ) \ - X( mjtNum, body_iquat, nbody, 4 ) \ - X( mjtNum, body_mass, nbody, 1 ) \ - X( mjtNum, body_subtreemass, nbody, 1 ) \ - X( mjtNum, body_inertia, nbody, 3 ) \ - X( mjtNum, body_invweight0, nbody, 2 ) \ - X( mjtNum, body_gravcomp, nbody, 1 ) \ - X( mjtNum, body_user, nbody, MJ_M(nuser_body) ) \ - X( int, body_plugin, nbody, 1 ) \ - X( int, body_bvhadr, nbody, 1 ) \ - X( int, body_bvhnum, nbody, 1 ) \ - X( int, bvh_depth, nbvh, 1 ) \ - X( int, bvh_child, nbvh, 2 ) \ - X( int, bvh_geomid, nbvh, 1 ) \ - X( mjtNum, bvh_aabb, nbvh, 6 ) \ - X( int, jnt_type, njnt, 1 ) \ - X( int, jnt_qposadr, njnt, 1 ) \ - X( int, jnt_dofadr, njnt, 1 ) \ - X( int, jnt_bodyid, njnt, 1 ) \ - X( int, jnt_group, njnt, 1 ) \ - X( mjtByte, jnt_limited, njnt, 1 ) \ - X( mjtNum, jnt_solref, njnt, mjNREF ) \ - X( mjtNum, jnt_solimp, njnt, mjNIMP ) \ - X( mjtNum, jnt_pos, njnt, 3 ) \ - X( mjtNum, jnt_axis, njnt, 3 ) \ - X( mjtNum, jnt_stiffness, njnt, 1 ) \ - X( mjtNum, jnt_range, njnt, 2 ) \ - X( mjtNum, jnt_margin, njnt, 1 ) \ - X( mjtNum, jnt_user, njnt, MJ_M(nuser_jnt) ) \ - X( int, dof_bodyid, nv, 1 ) \ - X( int, dof_jntid, nv, 1 ) \ - X( int, dof_parentid, nv, 1 ) \ - X( int, dof_Madr, nv, 1 ) \ - X( int, dof_simplenum, nv, 1 ) \ - X( mjtNum, dof_solref, nv, mjNREF ) \ - X( mjtNum, dof_solimp, nv, mjNIMP ) \ - X( mjtNum, dof_frictionloss, nv, 1 ) \ - X( mjtNum, dof_armature, nv, 1 ) \ - X( mjtNum, dof_damping, nv, 1 ) \ - X( mjtNum, dof_invweight0, nv, 1 ) \ - X( mjtNum, dof_M0, nv, 1 ) \ - X( int, geom_type, ngeom, 1 ) \ - X( int, geom_contype, ngeom, 1 ) \ - X( int, geom_conaffinity, ngeom, 1 ) \ - X( int, geom_condim, ngeom, 1 ) \ - X( int, geom_bodyid, ngeom, 1 ) \ - X( int, geom_dataid, ngeom, 1 ) \ - X( int, geom_matid, ngeom, 1 ) \ - X( int, geom_group, ngeom, 1 ) \ - X( int, geom_priority, ngeom, 1 ) \ - X( mjtByte, geom_sameframe, ngeom, 1 ) \ - X( mjtNum, geom_solmix, ngeom, 1 ) \ - X( mjtNum, geom_solref, ngeom, mjNREF ) \ - X( mjtNum, geom_solimp, ngeom, mjNIMP ) \ - X( mjtNum, geom_size, ngeom, 3 ) \ - X( mjtNum, geom_aabb, ngeom, 6 ) \ - X( mjtNum, geom_rbound, ngeom, 1 ) \ - X( mjtNum, geom_pos, ngeom, 3 ) \ - X( mjtNum, geom_quat, ngeom, 4 ) \ - X( mjtNum, geom_friction, ngeom, 3 ) \ - X( mjtNum, geom_margin, ngeom, 1 ) \ - X( mjtNum, geom_gap, ngeom, 1 ) \ - X( mjtNum, geom_fluid, ngeom, mjNFLUID ) \ - X( mjtNum, geom_user, ngeom, MJ_M(nuser_geom) ) \ - X( float, geom_rgba, ngeom, 4 ) \ - X( int, site_type, nsite, 1 ) \ - X( int, site_bodyid, nsite, 1 ) \ - X( int, site_matid, nsite, 1 ) \ - X( int, site_group, nsite, 1 ) \ - X( mjtByte, site_sameframe, nsite, 1 ) \ - X( mjtNum, site_size, nsite, 3 ) \ - X( mjtNum, site_pos, nsite, 3 ) \ - X( mjtNum, site_quat, nsite, 4 ) \ - X( mjtNum, site_user, nsite, MJ_M(nuser_site) ) \ - X( float, site_rgba, nsite, 4 ) \ - X( int, cam_mode, ncam, 1 ) \ - X( int, cam_bodyid, ncam, 1 ) \ - X( int, cam_targetbodyid, ncam, 1 ) \ - X( mjtNum, cam_pos, ncam, 3 ) \ - X( mjtNum, cam_quat, ncam, 4 ) \ - X( mjtNum, cam_poscom0, ncam, 3 ) \ - X( mjtNum, cam_pos0, ncam, 3 ) \ - X( mjtNum, cam_mat0, ncam, 9 ) \ - X( mjtNum, cam_fovy, ncam, 1 ) \ - X( mjtNum, cam_ipd, ncam, 1 ) \ - X( mjtNum, cam_user, ncam, MJ_M(nuser_cam) ) \ - X( int, light_mode, nlight, 1 ) \ - X( int, light_bodyid, nlight, 1 ) \ - X( int, light_targetbodyid, nlight, 1 ) \ - X( mjtByte, light_directional, nlight, 1 ) \ - X( mjtByte, light_castshadow, nlight, 1 ) \ - X( mjtByte, light_active, nlight, 1 ) \ - X( mjtNum, light_pos, nlight, 3 ) \ - X( mjtNum, light_dir, nlight, 3 ) \ - X( mjtNum, light_poscom0, nlight, 3 ) \ - X( mjtNum, light_pos0, nlight, 3 ) \ - X( mjtNum, light_dir0, nlight, 3 ) \ - X( float, light_attenuation, nlight, 3 ) \ - X( float, light_cutoff, nlight, 1 ) \ - X( float, light_exponent, nlight, 1 ) \ - X( float, light_ambient, nlight, 3 ) \ - X( float, light_diffuse, nlight, 3 ) \ - X( float, light_specular, nlight, 3 ) \ - X( int, mesh_vertadr, nmesh, 1 ) \ - X( int, mesh_vertnum, nmesh, 1 ) \ - X( int, mesh_normaladr, nmesh, 1 ) \ - X( int, mesh_normalnum, nmesh, 1 ) \ - X( int, mesh_texcoordadr, nmesh, 1 ) \ - X( int, mesh_texcoordnum, nmesh, 1 ) \ - X( int, mesh_faceadr, nmesh, 1 ) \ - X( int, mesh_facenum, nmesh, 1 ) \ - X( int, mesh_graphadr, nmesh, 1 ) \ - X( float, mesh_vert, nmeshvert, 3 ) \ - X( float, mesh_normal, nmeshnormal, 3 ) \ - X( float, mesh_texcoord, nmeshtexcoord, 2 ) \ - X( int, mesh_face, nmeshface, 3 ) \ - X( int, mesh_facenormal, nmeshface, 3 ) \ - X( int, mesh_facetexcoord, nmeshface, 3 ) \ - X( int, mesh_graph, nmeshgraph, 1 ) \ - X( int, skin_matid, nskin, 1 ) \ - X( int, skin_group, nskin, 1 ) \ - X( float, skin_rgba, nskin, 4 ) \ - X( float, skin_inflate, nskin, 1 ) \ - X( int, skin_vertadr, nskin, 1 ) \ - X( int, skin_vertnum, nskin, 1 ) \ - X( int, skin_texcoordadr, nskin, 1 ) \ - X( int, skin_faceadr, nskin, 1 ) \ - X( int, skin_facenum, nskin, 1 ) \ - X( int, skin_boneadr, nskin, 1 ) \ - X( int, skin_bonenum, nskin, 1 ) \ - X( float, skin_vert, nskinvert, 3 ) \ - X( float, skin_texcoord, nskintexvert, 2 ) \ - X( int, skin_face, nskinface, 3 ) \ - X( int, skin_bonevertadr, nskinbone, 1 ) \ - X( int, skin_bonevertnum, nskinbone, 1 ) \ - X( float, skin_bonebindpos, nskinbone, 3 ) \ - X( float, skin_bonebindquat, nskinbone, 4 ) \ - X( int, skin_bonebodyid, nskinbone, 1 ) \ - X( int, skin_bonevertid, nskinbonevert, 1 ) \ - X( float, skin_bonevertweight, nskinbonevert, 1 ) \ - X( mjtNum, hfield_size, nhfield, 4 ) \ - X( int, hfield_nrow, nhfield, 1 ) \ - X( int, hfield_ncol, nhfield, 1 ) \ - X( int, hfield_adr, nhfield, 1 ) \ - X( float, hfield_data, nhfielddata, 1 ) \ - X( int, tex_type, ntex, 1 ) \ - X( int, tex_height, ntex, 1 ) \ - X( int, tex_width, ntex, 1 ) \ - X( int, tex_adr, ntex, 1 ) \ - X( mjtByte, tex_rgb, ntexdata, 1 ) \ - X( int, mat_texid, nmat, 1 ) \ - X( mjtByte, mat_texuniform, nmat, 1 ) \ - X( float, mat_texrepeat, nmat, 2 ) \ - X( float, mat_emission, nmat, 1 ) \ - X( float, mat_specular, nmat, 1 ) \ - X( float, mat_shininess, nmat, 1 ) \ - X( float, mat_reflectance, nmat, 1 ) \ - X( float, mat_rgba, nmat, 4 ) \ - X( int, pair_dim, npair, 1 ) \ - X( int, pair_geom1, npair, 1 ) \ - X( int, pair_geom2, npair, 1 ) \ - X( int, pair_signature, npair, 1 ) \ - X( mjtNum, pair_solref, npair, mjNREF ) \ - X( mjtNum, pair_solimp, npair, mjNIMP ) \ - X( mjtNum, pair_margin, npair, 1 ) \ - X( mjtNum, pair_gap, npair, 1 ) \ - X( mjtNum, pair_friction, npair, 5 ) \ - X( int, exclude_signature, nexclude, 1 ) \ - X( int, eq_type, neq, 1 ) \ - X( int, eq_obj1id, neq, 1 ) \ - X( int, eq_obj2id, neq, 1 ) \ - X( mjtByte, eq_active, neq, 1 ) \ - X( mjtNum, eq_solref, neq, mjNREF ) \ - X( mjtNum, eq_solimp, neq, mjNIMP ) \ - X( mjtNum, eq_data, neq, mjNEQDATA ) \ - X( int, tendon_adr, ntendon, 1 ) \ - X( int, tendon_num, ntendon, 1 ) \ - X( int, tendon_matid, ntendon, 1 ) \ - X( int, tendon_group, ntendon, 1 ) \ - X( mjtByte, tendon_limited, ntendon, 1 ) \ - X( mjtNum, tendon_width, ntendon, 1 ) \ - X( mjtNum, tendon_solref_lim, ntendon, mjNREF ) \ - X( mjtNum, tendon_solimp_lim, ntendon, mjNIMP ) \ - X( mjtNum, tendon_solref_fri, ntendon, mjNREF ) \ - X( mjtNum, tendon_solimp_fri, ntendon, mjNIMP ) \ - X( mjtNum, tendon_range, ntendon, 2 ) \ - X( mjtNum, tendon_margin, ntendon, 1 ) \ - X( mjtNum, tendon_stiffness, ntendon, 1 ) \ - X( mjtNum, tendon_damping, ntendon, 1 ) \ - X( mjtNum, tendon_frictionloss, ntendon, 1 ) \ - X( mjtNum, tendon_lengthspring, ntendon, 2 ) \ - X( mjtNum, tendon_length0, ntendon, 1 ) \ - X( mjtNum, tendon_invweight0, ntendon, 1 ) \ - X( mjtNum, tendon_user, ntendon, MJ_M(nuser_tendon) ) \ - X( float, tendon_rgba, ntendon, 4 ) \ - X( int, wrap_type, nwrap, 1 ) \ - X( int, wrap_objid, nwrap, 1 ) \ - X( mjtNum, wrap_prm, nwrap, 1 ) \ - X( int, actuator_trntype, nu, 1 ) \ - X( int, actuator_dyntype, nu, 1 ) \ - X( int, actuator_gaintype, nu, 1 ) \ - X( int, actuator_biastype, nu, 1 ) \ - X( int, actuator_trnid, nu, 2 ) \ - X( int, actuator_actadr, nu, 1 ) \ - X( int, actuator_actnum, nu, 1 ) \ - X( int, actuator_group, nu, 1 ) \ - X( mjtByte, actuator_ctrllimited, nu, 1 ) \ - X( mjtByte, actuator_forcelimited, nu, 1 ) \ - X( mjtByte, actuator_actlimited, nu, 1 ) \ - X( mjtNum, actuator_dynprm, nu, mjNDYN ) \ - X( mjtNum, actuator_gainprm, nu, mjNGAIN ) \ - X( mjtNum, actuator_biasprm, nu, mjNBIAS ) \ - X( mjtNum, actuator_ctrlrange, nu, 2 ) \ - X( mjtNum, actuator_forcerange, nu, 2 ) \ - X( mjtNum, actuator_actrange, nu, 2 ) \ - X( mjtNum, actuator_gear, nu, 6 ) \ - X( mjtNum, actuator_cranklength, nu, 1 ) \ - X( mjtNum, actuator_acc0, nu, 1 ) \ - X( mjtNum, actuator_length0, nu, 1 ) \ - X( mjtNum, actuator_lengthrange, nu, 2 ) \ - X( mjtNum, actuator_user, nu, MJ_M(nuser_actuator) ) \ - X( int, actuator_plugin, nu, 1 ) \ - X( int, sensor_type, nsensor, 1 ) \ - X( int, sensor_datatype, nsensor, 1 ) \ - X( int, sensor_needstage, nsensor, 1 ) \ - X( int, sensor_objtype, nsensor, 1 ) \ - X( int, sensor_objid, nsensor, 1 ) \ - X( int, sensor_reftype, nsensor, 1 ) \ - X( int, sensor_refid, nsensor, 1 ) \ - X( int, sensor_dim, nsensor, 1 ) \ - X( int, sensor_adr, nsensor, 1 ) \ - X( mjtNum, sensor_cutoff, nsensor, 1 ) \ - X( mjtNum, sensor_noise, nsensor, 1 ) \ - X( mjtNum, sensor_user, nsensor, MJ_M(nuser_sensor) ) \ - X( int, sensor_plugin, nsensor, 1 ) \ - X( int, plugin, nplugin, 1 ) \ - X( int, plugin_stateadr, nplugin, 1 ) \ - X( int, plugin_statenum, nplugin, 1 ) \ - X( char, plugin_attr, npluginattr, 1 ) \ - X( int, plugin_attradr, nplugin, 1 ) \ - X( int, numeric_adr, nnumeric, 1 ) \ - X( int, numeric_size, nnumeric, 1 ) \ - X( mjtNum, numeric_data, nnumericdata, 1 ) \ - X( int, text_adr, ntext, 1 ) \ - X( int, text_size, ntext, 1 ) \ - X( char, text_data, ntextdata, 1 ) \ - X( int, tuple_adr, ntuple, 1 ) \ - X( int, tuple_size, ntuple, 1 ) \ - X( int, tuple_objtype, ntupledata, 1 ) \ - X( int, tuple_objid, ntupledata, 1 ) \ - X( mjtNum, tuple_objprm, ntupledata, 1 ) \ - X( mjtNum, key_time, nkey, 1 ) \ - X( mjtNum, key_qpos, nkey, MJ_M(nq) ) \ - X( mjtNum, key_qvel, nkey, MJ_M(nv) ) \ - X( mjtNum, key_act, nkey, MJ_M(na) ) \ - X( mjtNum, key_mpos, nkey, MJ_M(nmocap)*3 ) \ - X( mjtNum, key_mquat, nkey, MJ_M(nmocap)*4 ) \ - X( mjtNum, key_ctrl, nkey, MJ_M(nu) ) \ - X( int, name_bodyadr, nbody, 1 ) \ - X( int, name_jntadr, njnt, 1 ) \ - X( int, name_geomadr, ngeom, 1 ) \ - X( int, name_siteadr, nsite, 1 ) \ - X( int, name_camadr, ncam, 1 ) \ - X( int, name_lightadr, nlight, 1 ) \ - X( int, name_meshadr, nmesh, 1 ) \ - X( int, name_skinadr, nskin, 1 ) \ - X( int, name_hfieldadr, nhfield, 1 ) \ - X( int, name_texadr, ntex, 1 ) \ - X( int, name_matadr, nmat, 1 ) \ - X( int, name_pairadr, npair, 1 ) \ - X( int, name_excludeadr, nexclude, 1 ) \ - X( int, name_eqadr, neq, 1 ) \ - X( int, name_tendonadr, ntendon, 1 ) \ - X( int, name_actuatoradr, nu, 1 ) \ - X( int, name_sensoradr, nsensor, 1 ) \ - X( int, name_numericadr, nnumeric, 1 ) \ - X( int, name_textadr, ntext, 1 ) \ - X( int, name_tupleadr, ntuple, 1 ) \ - X( int, name_keyadr, nkey, 1 ) \ - X( int, name_pluginadr, nplugin, 1 ) \ - X( char, names, nnames, 1 ) \ - X( int, names_map, nnames_map, 1 ) \ + X ( mjtNum, qpos0, nq, 1 ) \ + X ( mjtNum, qpos_spring, nq, 1 ) \ + XMJV( int, body_parentid, nbody, 1 ) \ + XMJV( int, body_rootid, nbody, 1 ) \ + XMJV( int, body_weldid, nbody, 1 ) \ + XMJV( int, body_mocapid, nbody, 1 ) \ + XMJV( int, body_jntnum, nbody, 1 ) \ + XMJV( int, body_jntadr, nbody, 1 ) \ + X ( int, body_dofnum, nbody, 1 ) \ + X ( int, body_dofadr, nbody, 1 ) \ + XMJV( int, body_geomnum, nbody, 1 ) \ + XMJV( int, body_geomadr, nbody, 1 ) \ + X ( mjtByte, body_simple, nbody, 1 ) \ + X ( mjtByte, body_sameframe, nbody, 1 ) \ + X ( mjtNum, body_pos, nbody, 3 ) \ + X ( mjtNum, body_quat, nbody, 4 ) \ + X ( mjtNum, body_ipos, nbody, 3 ) \ + XMJV( mjtNum, body_iquat, nbody, 4 ) \ + XMJV( mjtNum, body_mass, nbody, 1 ) \ + X ( mjtNum, body_subtreemass, nbody, 1 ) \ + XMJV( mjtNum, body_inertia, nbody, 3 ) \ + X ( mjtNum, body_invweight0, nbody, 2 ) \ + X ( mjtNum, body_gravcomp, nbody, 1 ) \ + X ( mjtNum, body_user, nbody, MJ_M(nuser_body) ) \ + X ( int, body_plugin, nbody, 1 ) \ + XMJV( int, body_bvhadr, nbody, 1 ) \ + XMJV( int, body_bvhnum, nbody, 1 ) \ + XMJV( int, bvh_depth, nbvh, 1 ) \ + XMJV( int, bvh_child, nbvh, 2 ) \ + XMJV( int, bvh_geomid, nbvh, 1 ) \ + XMJV( mjtNum, bvh_aabb, nbvh, 6 ) \ + XMJV( int, jnt_type, njnt, 1 ) \ + X ( int, jnt_qposadr, njnt, 1 ) \ + X ( int, jnt_dofadr, njnt, 1 ) \ + XMJV( int, jnt_bodyid, njnt, 1 ) \ + XMJV( int, jnt_group, njnt, 1 ) \ + X ( mjtByte, jnt_limited, njnt, 1 ) \ + X ( mjtNum, jnt_solref, njnt, mjNREF ) \ + X ( mjtNum, jnt_solimp, njnt, mjNIMP ) \ + X ( mjtNum, jnt_pos, njnt, 3 ) \ + X ( mjtNum, jnt_axis, njnt, 3 ) \ + X ( mjtNum, jnt_stiffness, njnt, 1 ) \ + X ( mjtNum, jnt_range, njnt, 2 ) \ + X ( mjtNum, jnt_margin, njnt, 1 ) \ + X ( mjtNum, jnt_user, njnt, MJ_M(nuser_jnt) ) \ + X ( int, dof_bodyid, nv, 1 ) \ + X ( int, dof_jntid, nv, 1 ) \ + X ( int, dof_parentid, nv, 1 ) \ + X ( int, dof_Madr, nv, 1 ) \ + X ( int, dof_simplenum, nv, 1 ) \ + X ( mjtNum, dof_solref, nv, mjNREF ) \ + X ( mjtNum, dof_solimp, nv, mjNIMP ) \ + X ( mjtNum, dof_frictionloss, nv, 1 ) \ + X ( mjtNum, dof_armature, nv, 1 ) \ + X ( mjtNum, dof_damping, nv, 1 ) \ + X ( mjtNum, dof_invweight0, nv, 1 ) \ + X ( mjtNum, dof_M0, nv, 1 ) \ + XMJV( int, geom_type, ngeom, 1 ) \ + X ( int, geom_contype, ngeom, 1 ) \ + X ( int, geom_conaffinity, ngeom, 1 ) \ + X ( int, geom_condim, ngeom, 1 ) \ + XMJV( int, geom_bodyid, ngeom, 1 ) \ + XMJV( int, geom_dataid, ngeom, 1 ) \ + XMJV( int, geom_matid, ngeom, 1 ) \ + XMJV( int, geom_group, ngeom, 1 ) \ + X ( int, geom_priority, ngeom, 1 ) \ + X ( mjtByte, geom_sameframe, ngeom, 1 ) \ + X ( mjtNum, geom_solmix, ngeom, 1 ) \ + X ( mjtNum, geom_solref, ngeom, mjNREF ) \ + X ( mjtNum, geom_solimp, ngeom, mjNIMP ) \ + XMJV( mjtNum, geom_size, ngeom, 3 ) \ + XMJV( mjtNum, geom_aabb, ngeom, 6 ) \ + XMJV( mjtNum, geom_rbound, ngeom, 1 ) \ + X ( mjtNum, geom_pos, ngeom, 3 ) \ + X ( mjtNum, geom_quat, ngeom, 4 ) \ + X ( mjtNum, geom_friction, ngeom, 3 ) \ + X ( mjtNum, geom_margin, ngeom, 1 ) \ + X ( mjtNum, geom_gap, ngeom, 1 ) \ + X ( mjtNum, geom_fluid, ngeom, mjNFLUID ) \ + X ( mjtNum, geom_user, ngeom, MJ_M(nuser_geom) ) \ + XMJV( float, geom_rgba, ngeom, 4 ) \ + XMJV( int, site_type, nsite, 1 ) \ + XMJV( int, site_bodyid, nsite, 1 ) \ + XMJV( int, site_matid, nsite, 1 ) \ + XMJV( int, site_group, nsite, 1 ) \ + X ( mjtByte, site_sameframe, nsite, 1 ) \ + XMJV( mjtNum, site_size, nsite, 3 ) \ + X ( mjtNum, site_pos, nsite, 3 ) \ + X ( mjtNum, site_quat, nsite, 4 ) \ + X ( mjtNum, site_user, nsite, MJ_M(nuser_site) ) \ + XMJV( float, site_rgba, nsite, 4 ) \ + X ( int, cam_mode, ncam, 1 ) \ + X ( int, cam_bodyid, ncam, 1 ) \ + X ( int, cam_targetbodyid, ncam, 1 ) \ + X ( mjtNum, cam_pos, ncam, 3 ) \ + X ( mjtNum, cam_quat, ncam, 4 ) \ + X ( mjtNum, cam_poscom0, ncam, 3 ) \ + X ( mjtNum, cam_pos0, ncam, 3 ) \ + X ( mjtNum, cam_mat0, ncam, 9 ) \ + XMJV( mjtNum, cam_fovy, ncam, 1 ) \ + XMJV( mjtNum, cam_ipd, ncam, 1 ) \ + X ( mjtNum, cam_user, ncam, MJ_M(nuser_cam) ) \ + X ( int, light_mode, nlight, 1 ) \ + X ( int, light_bodyid, nlight, 1 ) \ + X ( int, light_targetbodyid, nlight, 1 ) \ + XMJV( mjtByte, light_directional, nlight, 1 ) \ + XMJV( mjtByte, light_castshadow, nlight, 1 ) \ + XMJV( mjtByte, light_active, nlight, 1 ) \ + X ( mjtNum, light_pos, nlight, 3 ) \ + X ( mjtNum, light_dir, nlight, 3 ) \ + X ( mjtNum, light_poscom0, nlight, 3 ) \ + X ( mjtNum, light_pos0, nlight, 3 ) \ + X ( mjtNum, light_dir0, nlight, 3 ) \ + XMJV( float, light_attenuation, nlight, 3 ) \ + XMJV( float, light_cutoff, nlight, 1 ) \ + XMJV( float, light_exponent, nlight, 1 ) \ + XMJV( float, light_ambient, nlight, 3 ) \ + XMJV( float, light_diffuse, nlight, 3 ) \ + XMJV( float, light_specular, nlight, 3 ) \ + X ( int, mesh_vertadr, nmesh, 1 ) \ + X ( int, mesh_vertnum, nmesh, 1 ) \ + X ( int, mesh_normaladr, nmesh, 1 ) \ + X ( int, mesh_normalnum, nmesh, 1 ) \ + XMJV( int, mesh_texcoordadr, nmesh, 1 ) \ + X ( int, mesh_texcoordnum, nmesh, 1 ) \ + X ( int, mesh_faceadr, nmesh, 1 ) \ + X ( int, mesh_facenum, nmesh, 1 ) \ + XMJV( int, mesh_graphadr, nmesh, 1 ) \ + X ( float, mesh_vert, nmeshvert, 3 ) \ + X ( float, mesh_normal, nmeshnormal, 3 ) \ + X ( float, mesh_texcoord, nmeshtexcoord, 2 ) \ + X ( int, mesh_face, nmeshface, 3 ) \ + X ( int, mesh_facenormal, nmeshface, 3 ) \ + X ( int, mesh_facetexcoord, nmeshface, 3 ) \ + X ( int, mesh_graph, nmeshgraph, 1 ) \ + XMJV( int, skin_matid, nskin, 1 ) \ + XMJV( int, skin_group, nskin, 1 ) \ + XMJV( float, skin_rgba, nskin, 4 ) \ + XMJV( float, skin_inflate, nskin, 1 ) \ + XMJV( int, skin_vertadr, nskin, 1 ) \ + XMJV( int, skin_vertnum, nskin, 1 ) \ + XMJV( int, skin_texcoordadr, nskin, 1 ) \ + XMJV( int, skin_faceadr, nskin, 1 ) \ + XMJV( int, skin_facenum, nskin, 1 ) \ + XMJV( int, skin_boneadr, nskin, 1 ) \ + XMJV( int, skin_bonenum, nskin, 1 ) \ + XMJV( float, skin_vert, nskinvert, 3 ) \ + X ( float, skin_texcoord, nskintexvert, 2 ) \ + XMJV( int, skin_face, nskinface, 3 ) \ + XMJV( int, skin_bonevertadr, nskinbone, 1 ) \ + XMJV( int, skin_bonevertnum, nskinbone, 1 ) \ + XMJV( float, skin_bonebindpos, nskinbone, 3 ) \ + XMJV( float, skin_bonebindquat, nskinbone, 4 ) \ + XMJV( int, skin_bonebodyid, nskinbone, 1 ) \ + XMJV( int, skin_bonevertid, nskinbonevert, 1 ) \ + XMJV( float, skin_bonevertweight, nskinbonevert, 1 ) \ + X ( mjtNum, hfield_size, nhfield, 4 ) \ + X ( int, hfield_nrow, nhfield, 1 ) \ + X ( int, hfield_ncol, nhfield, 1 ) \ + X ( int, hfield_adr, nhfield, 1 ) \ + X ( float, hfield_data, nhfielddata, 1 ) \ + X ( int, tex_type, ntex, 1 ) \ + X ( int, tex_height, ntex, 1 ) \ + X ( int, tex_width, ntex, 1 ) \ + X ( int, tex_adr, ntex, 1 ) \ + X ( mjtByte, tex_rgb, ntexdata, 1 ) \ + XMJV( int, mat_texid, nmat, 1 ) \ + XMJV( mjtByte, mat_texuniform, nmat, 1 ) \ + XMJV( float, mat_texrepeat, nmat, 2 ) \ + XMJV( float, mat_emission, nmat, 1 ) \ + XMJV( float, mat_specular, nmat, 1 ) \ + XMJV( float, mat_shininess, nmat, 1 ) \ + XMJV( float, mat_reflectance, nmat, 1 ) \ + XMJV( float, mat_rgba, nmat, 4 ) \ + X ( int, pair_dim, npair, 1 ) \ + X ( int, pair_geom1, npair, 1 ) \ + X ( int, pair_geom2, npair, 1 ) \ + X ( int, pair_signature, npair, 1 ) \ + X ( mjtNum, pair_solref, npair, mjNREF ) \ + X ( mjtNum, pair_solimp, npair, mjNIMP ) \ + X ( mjtNum, pair_margin, npair, 1 ) \ + X ( mjtNum, pair_gap, npair, 1 ) \ + X ( mjtNum, pair_friction, npair, 5 ) \ + X ( int, exclude_signature, nexclude, 1 ) \ + XMJV( int, eq_type, neq, 1 ) \ + XMJV( int, eq_obj1id, neq, 1 ) \ + XMJV( int, eq_obj2id, neq, 1 ) \ + XMJV( mjtByte, eq_active, neq, 1 ) \ + X ( mjtNum, eq_solref, neq, mjNREF ) \ + X ( mjtNum, eq_solimp, neq, mjNIMP ) \ + XMJV( mjtNum, eq_data, neq, mjNEQDATA ) \ + X ( int, tendon_adr, ntendon, 1 ) \ + XMJV( int, tendon_num, ntendon, 1 ) \ + XMJV( int, tendon_matid, ntendon, 1 ) \ + XMJV( int, tendon_group, ntendon, 1 ) \ + XMJV( mjtByte, tendon_limited, ntendon, 1 ) \ + XMJV( mjtNum, tendon_width, ntendon, 1 ) \ + X ( mjtNum, tendon_solref_lim, ntendon, mjNREF ) \ + X ( mjtNum, tendon_solimp_lim, ntendon, mjNIMP ) \ + X ( mjtNum, tendon_solref_fri, ntendon, mjNREF ) \ + X ( mjtNum, tendon_solimp_fri, ntendon, mjNIMP ) \ + XMJV( mjtNum, tendon_range, ntendon, 2 ) \ + X ( mjtNum, tendon_margin, ntendon, 1 ) \ + XMJV( mjtNum, tendon_stiffness, ntendon, 1 ) \ + XMJV( mjtNum, tendon_damping, ntendon, 1 ) \ + XMJV( mjtNum, tendon_frictionloss, ntendon, 1 ) \ + XMJV( mjtNum, tendon_lengthspring, ntendon, 2 ) \ + X ( mjtNum, tendon_length0, ntendon, 1 ) \ + X ( mjtNum, tendon_invweight0, ntendon, 1 ) \ + X ( mjtNum, tendon_user, ntendon, MJ_M(nuser_tendon) ) \ + XMJV( float, tendon_rgba, ntendon, 4 ) \ + X ( int, wrap_type, nwrap, 1 ) \ + X ( int, wrap_objid, nwrap, 1 ) \ + X ( mjtNum, wrap_prm, nwrap, 1 ) \ + XMJV( int, actuator_trntype, nu, 1 ) \ + XMJV( int, actuator_dyntype, nu, 1 ) \ + X ( int, actuator_gaintype, nu, 1 ) \ + X ( int, actuator_biastype, nu, 1 ) \ + XMJV( int, actuator_trnid, nu, 2 ) \ + XMJV( int, actuator_actadr, nu, 1 ) \ + XMJV( int, actuator_actnum, nu, 1 ) \ + XMJV( int, actuator_group, nu, 1 ) \ + XMJV( mjtByte, actuator_ctrllimited, nu, 1 ) \ + X ( mjtByte, actuator_forcelimited, nu, 1 ) \ + XMJV( mjtByte, actuator_actlimited, nu, 1 ) \ + X ( mjtNum, actuator_dynprm, nu, mjNDYN ) \ + X ( mjtNum, actuator_gainprm, nu, mjNGAIN ) \ + X ( mjtNum, actuator_biasprm, nu, mjNBIAS ) \ + XMJV( mjtNum, actuator_ctrlrange, nu, 2 ) \ + X ( mjtNum, actuator_forcerange, nu, 2 ) \ + XMJV( mjtNum, actuator_actrange, nu, 2 ) \ + X ( mjtNum, actuator_gear, nu, 6 ) \ + XMJV( mjtNum, actuator_cranklength, nu, 1 ) \ + X ( mjtNum, actuator_acc0, nu, 1 ) \ + X ( mjtNum, actuator_length0, nu, 1 ) \ + X ( mjtNum, actuator_lengthrange, nu, 2 ) \ + X ( mjtNum, actuator_user, nu, MJ_M(nuser_actuator) ) \ + X ( int, actuator_plugin, nu, 1 ) \ + XMJV( int, sensor_type, nsensor, 1 ) \ + X ( int, sensor_datatype, nsensor, 1 ) \ + X ( int, sensor_needstage, nsensor, 1 ) \ + X ( int, sensor_objtype, nsensor, 1 ) \ + XMJV( int, sensor_objid, nsensor, 1 ) \ + X ( int, sensor_reftype, nsensor, 1 ) \ + X ( int, sensor_refid, nsensor, 1 ) \ + X ( int, sensor_dim, nsensor, 1 ) \ + XMJV( int, sensor_adr, nsensor, 1 ) \ + X ( mjtNum, sensor_cutoff, nsensor, 1 ) \ + X ( mjtNum, sensor_noise, nsensor, 1 ) \ + X ( mjtNum, sensor_user, nsensor, MJ_M(nuser_sensor) ) \ + X ( int, sensor_plugin, nsensor, 1 ) \ + X ( int, plugin, nplugin, 1 ) \ + X ( int, plugin_stateadr, nplugin, 1 ) \ + X ( int, plugin_statenum, nplugin, 1 ) \ + X ( char, plugin_attr, npluginattr, 1 ) \ + X ( int, plugin_attradr, nplugin, 1 ) \ + X ( int, numeric_adr, nnumeric, 1 ) \ + X ( int, numeric_size, nnumeric, 1 ) \ + X ( mjtNum, numeric_data, nnumericdata, 1 ) \ + X ( int, text_adr, ntext, 1 ) \ + X ( int, text_size, ntext, 1 ) \ + X ( char, text_data, ntextdata, 1 ) \ + X ( int, tuple_adr, ntuple, 1 ) \ + X ( int, tuple_size, ntuple, 1 ) \ + X ( int, tuple_objtype, ntupledata, 1 ) \ + X ( int, tuple_objid, ntupledata, 1 ) \ + X ( mjtNum, tuple_objprm, ntupledata, 1 ) \ + X ( mjtNum, key_time, nkey, 1 ) \ + X ( mjtNum, key_qpos, nkey, MJ_M(nq) ) \ + X ( mjtNum, key_qvel, nkey, MJ_M(nv) ) \ + X ( mjtNum, key_act, nkey, MJ_M(na) ) \ + X ( mjtNum, key_mpos, nkey, MJ_M(nmocap)*3 ) \ + X ( mjtNum, key_mquat, nkey, MJ_M(nmocap)*4 ) \ + X ( mjtNum, key_ctrl, nkey, MJ_M(nu) ) \ + XMJV( int, name_bodyadr, nbody, 1 ) \ + XMJV( int, name_jntadr, njnt, 1 ) \ + XMJV( int, name_geomadr, ngeom, 1 ) \ + XMJV( int, name_siteadr, nsite, 1 ) \ + XMJV( int, name_camadr, ncam, 1 ) \ + XMJV( int, name_lightadr, nlight, 1 ) \ + X ( int, name_meshadr, nmesh, 1 ) \ + X ( int, name_skinadr, nskin, 1 ) \ + X ( int, name_hfieldadr, nhfield, 1 ) \ + X ( int, name_texadr, ntex, 1 ) \ + X ( int, name_matadr, nmat, 1 ) \ + X ( int, name_pairadr, npair, 1 ) \ + X ( int, name_excludeadr, nexclude, 1 ) \ + XMJV( int, name_eqadr, neq, 1 ) \ + XMJV( int, name_tendonadr, ntendon, 1 ) \ + XMJV( int, name_actuatoradr, nu, 1 ) \ + X ( int, name_sensoradr, nsensor, 1 ) \ + X ( int, name_numericadr, nnumeric, 1 ) \ + X ( int, name_textadr, ntext, 1 ) \ + X ( int, name_tupleadr, ntuple, 1 ) \ + X ( int, name_keyadr, nkey, 1 ) \ + X ( int, name_pluginadr, nplugin, 1 ) \ + XMJV( char, names, nnames, 1 ) \ + X ( int, names_map, nnames_map, 1 ) \ //-------------------------------- mjData ---------------------------------------------------------- @@ -459,85 +461,87 @@ // pointer fields of mjData -#define MJDATA_POINTERS \ - X( mjtNum, qpos, nq, 1 ) \ - X( mjtNum, qvel, nv, 1 ) \ - X( mjtNum, act, na, 1 ) \ - X( mjtNum, qacc_warmstart, nv, 1 ) \ - X( mjtNum, plugin_state, npluginstate, 1 ) \ - X( mjtNum, ctrl, nu, 1 ) \ - X( mjtNum, qfrc_applied, nv, 1 ) \ - X( mjtNum, xfrc_applied, nbody, 6 ) \ - X( mjtNum, mocap_pos, nmocap, 3 ) \ - X( mjtNum, mocap_quat, nmocap, 4 ) \ - X( mjtNum, qacc, nv, 1 ) \ - X( mjtNum, act_dot, na, 1 ) \ - X( mjtNum, userdata, nuserdata, 1 ) \ - X( mjtNum, sensordata, nsensordata, 1 ) \ - X( int, plugin, nplugin, 1 ) \ - X( uintptr_t, plugin_data, nplugin, 1 ) \ - X( mjtNum, xpos, nbody, 3 ) \ - X( mjtNum, xquat, nbody, 4 ) \ - X( mjtNum, xmat, nbody, 9 ) \ - X( mjtNum, xipos, nbody, 3 ) \ - X( mjtNum, ximat, nbody, 9 ) \ - X( mjtNum, xanchor, njnt, 3 ) \ - X( mjtNum, xaxis, njnt, 3 ) \ - X( mjtNum, geom_xpos, ngeom, 3 ) \ - X( mjtNum, geom_xmat, ngeom, 9 ) \ - X( mjtNum, site_xpos, nsite, 3 ) \ - X( mjtNum, site_xmat, nsite, 9 ) \ - X( mjtNum, cam_xpos, ncam, 3 ) \ - X( mjtNum, cam_xmat, ncam, 9 ) \ - X( mjtNum, light_xpos, nlight, 3 ) \ - X( mjtNum, light_xdir, nlight, 3 ) \ - X( mjtNum, subtree_com, nbody, 3 ) \ - X( mjtNum, cdof, nv, 6 ) \ - X( mjtNum, cinert, nbody, 10 ) \ - X( int, ten_wrapadr, ntendon, 1 ) \ - X( int, ten_wrapnum, ntendon, 1 ) \ - X( int, ten_J_rownnz, ntendon, 1 ) \ - X( int, ten_J_rowadr, ntendon, 1 ) \ - X( int, ten_J_colind, ntendon, MJ_M(nv) ) \ - X( mjtNum, ten_length, ntendon, 1 ) \ - X( mjtNum, ten_J, ntendon, MJ_M(nv) ) \ - X( int, wrap_obj, nwrap, 2 ) \ - X( mjtNum, wrap_xpos, nwrap, 6 ) \ - X( mjtNum, actuator_length, nu, 1 ) \ - X( mjtNum, actuator_moment, nu, MJ_M(nv) ) \ - X( mjtNum, crb, nbody, 10 ) \ - X( mjtNum, qM, nM, 1 ) \ - X( mjtNum, qLD, nM, 1 ) \ - X( mjtNum, qLDiagInv, nv, 1 ) \ - X( mjtNum, qLDiagSqrtInv, nv, 1 ) \ - X( mjtByte, bvh_active, nbvh, 1 ) \ - X( mjtNum, ten_velocity, ntendon, 1 ) \ - X( mjtNum, actuator_velocity, nu, 1 ) \ - X( mjtNum, cvel, nbody, 6 ) \ - X( mjtNum, cdof_dot, nv, 6 ) \ - X( mjtNum, qfrc_bias, nv, 1 ) \ - X( mjtNum, qfrc_passive, nv, 1 ) \ - X( mjtNum, subtree_linvel, nbody, 3 ) \ - X( mjtNum, subtree_angmom, nbody, 3 ) \ - X( mjtNum, qH, nM, 1 ) \ - X( mjtNum, qHDiagInv, nv, 1 ) \ - X( int, D_rownnz, nv, 1 ) \ - X( int, D_rowadr, nv, 1 ) \ - X( int, D_colind, nD, 1 ) \ - X( int, B_rownnz, nbody, 1 ) \ - X( int, B_rowadr, nbody, 1 ) \ - X( int, B_colind, nB, 1 ) \ - X( mjtNum, qDeriv, nD, 1 ) \ - X( mjtNum, qLU, nD, 1 ) \ - X( mjtNum, actuator_force, nu, 1 ) \ - X( mjtNum, qfrc_actuator, nv, 1 ) \ - X( mjtNum, qfrc_smooth, nv, 1 ) \ - X( mjtNum, qacc_smooth, nv, 1 ) \ - X( mjtNum, qfrc_constraint, nv, 1 ) \ - X( mjtNum, qfrc_inverse, nv, 1 ) \ - X( mjtNum, cacc, nbody, 6 ) \ - X( mjtNum, cfrc_int, nbody, 6 ) \ - X( mjtNum, cfrc_ext, nbody, 6 ) +// XMJV means that the field is required to construct mjvScene +// (by default we define XMJV to be the same as X) +#define MJDATA_POINTERS \ + X ( mjtNum, qpos, nq, 1 ) \ + X ( mjtNum, qvel, nv, 1 ) \ + XMJV( mjtNum, act, na, 1 ) \ + X ( mjtNum, qacc_warmstart, nv, 1 ) \ + X ( mjtNum, plugin_state, npluginstate, 1 ) \ + XMJV( mjtNum, ctrl, nu, 1 ) \ + X ( mjtNum, qfrc_applied, nv, 1 ) \ + XMJV( mjtNum, xfrc_applied, nbody, 6 ) \ + X ( mjtNum, mocap_pos, nmocap, 3 ) \ + X ( mjtNum, mocap_quat, nmocap, 4 ) \ + X ( mjtNum, qacc, nv, 1 ) \ + X ( mjtNum, act_dot, na, 1 ) \ + X ( mjtNum, userdata, nuserdata, 1 ) \ + XMJV( mjtNum, sensordata, nsensordata, 1 ) \ + X ( int, plugin, nplugin, 1 ) \ + X ( uintptr_t, plugin_data, nplugin, 1 ) \ + XMJV( mjtNum, xpos, nbody, 3 ) \ + XMJV( mjtNum, xquat, nbody, 4 ) \ + XMJV( mjtNum, xmat, nbody, 9 ) \ + XMJV( mjtNum, xipos, nbody, 3 ) \ + XMJV( mjtNum, ximat, nbody, 9 ) \ + XMJV( mjtNum, xanchor, njnt, 3 ) \ + XMJV( mjtNum, xaxis, njnt, 3 ) \ + XMJV( mjtNum, geom_xpos, ngeom, 3 ) \ + XMJV( mjtNum, geom_xmat, ngeom, 9 ) \ + XMJV( mjtNum, site_xpos, nsite, 3 ) \ + XMJV( mjtNum, site_xmat, nsite, 9 ) \ + XMJV( mjtNum, cam_xpos, ncam, 3 ) \ + XMJV( mjtNum, cam_xmat, ncam, 9 ) \ + XMJV( mjtNum, light_xpos, nlight, 3 ) \ + XMJV( mjtNum, light_xdir, nlight, 3 ) \ + XMJV( mjtNum, subtree_com, nbody, 3 ) \ + X ( mjtNum, cdof, nv, 6 ) \ + X ( mjtNum, cinert, nbody, 10 ) \ + XMJV( int, ten_wrapadr, ntendon, 1 ) \ + XMJV( int, ten_wrapnum, ntendon, 1 ) \ + X ( int, ten_J_rownnz, ntendon, 1 ) \ + X ( int, ten_J_rowadr, ntendon, 1 ) \ + X ( int, ten_J_colind, ntendon, MJ_M(nv) ) \ + X ( mjtNum, ten_length, ntendon, 1 ) \ + X ( mjtNum, ten_J, ntendon, MJ_M(nv) ) \ + XMJV( int, wrap_obj, nwrap, 2 ) \ + XMJV( mjtNum, wrap_xpos, nwrap, 6 ) \ + X ( mjtNum, actuator_length, nu, 1 ) \ + X ( mjtNum, actuator_moment, nu, MJ_M(nv) ) \ + X ( mjtNum, crb, nbody, 10 ) \ + X ( mjtNum, qM, nM, 1 ) \ + X ( mjtNum, qLD, nM, 1 ) \ + X ( mjtNum, qLDiagInv, nv, 1 ) \ + X ( mjtNum, qLDiagSqrtInv, nv, 1 ) \ + XMJV( mjtByte, bvh_active, nbvh, 1 ) \ + X ( mjtNum, ten_velocity, ntendon, 1 ) \ + X ( mjtNum, actuator_velocity, nu, 1 ) \ + X ( mjtNum, cvel, nbody, 6 ) \ + X ( mjtNum, cdof_dot, nv, 6 ) \ + X ( mjtNum, qfrc_bias, nv, 1 ) \ + X ( mjtNum, qfrc_passive, nv, 1 ) \ + X ( mjtNum, subtree_linvel, nbody, 3 ) \ + X ( mjtNum, subtree_angmom, nbody, 3 ) \ + X ( mjtNum, qH, nM, 1 ) \ + X ( mjtNum, qHDiagInv, nv, 1 ) \ + X ( int, D_rownnz, nv, 1 ) \ + X ( int, D_rowadr, nv, 1 ) \ + X ( int, D_colind, nD, 1 ) \ + X ( int, B_rownnz, nbody, 1 ) \ + X ( int, B_rowadr, nbody, 1 ) \ + X ( int, B_colind, nB, 1 ) \ + X ( mjtNum, qDeriv, nD, 1 ) \ + X ( mjtNum, qLU, nD, 1 ) \ + X ( mjtNum, actuator_force, nu, 1 ) \ + X ( mjtNum, qfrc_actuator, nv, 1 ) \ + X ( mjtNum, qfrc_smooth, nv, 1 ) \ + X ( mjtNum, qacc_smooth, nv, 1 ) \ + X ( mjtNum, qfrc_constraint, nv, 1 ) \ + X ( mjtNum, qfrc_inverse, nv, 1 ) \ + X ( mjtNum, cacc, nbody, 6 ) \ + X ( mjtNum, cfrc_int, nbody, 6 ) \ + X ( mjtNum, cfrc_ext, nbody, 6 ) // macro for annotating that an array size in an X macro is a member of mjData @@ -623,4 +627,9 @@ X( mjtNum, energy, 2, 1 ) +// alias XMJV to be the same as X +// to obtain only X macros for fields that are relevant for mjvScene creation, +// redefine X to expand to nothing, and XMJV to do what's required +#define XMJV X + #endif // MUJOCO_MJXMACRO_H_ diff --git a/include/mujoco/mujoco.h b/include/mujoco/mujoco.h index b73500d6..0259b3e1 100644 --- a/include/mujoco/mujoco.h +++ b/include/mujoco/mujoco.h @@ -546,10 +546,20 @@ MJAPI void mjv_alignToCamera(mjtNum res[3], const mjtNum vec[3], const mjtNum fo MJAPI void mjv_moveCamera(const mjModel* m, int action, mjtNum reldx, mjtNum reldy, const mjvScene* scn, mjvCamera* cam); +// Move camera with mouse given a scene state; action is mjtMouse. +MJAPI void mjv_moveCameraFromState(const mjvSceneState* scnstate, int action, + mjtNum reldx, mjtNum reldy, + const mjvScene* scn, mjvCamera* cam); + // Move perturb object with mouse; action is mjtMouse. MJAPI void mjv_movePerturb(const mjModel* m, const mjData* d, int action, mjtNum reldx, mjtNum reldy, const mjvScene* scn, mjvPerturb* pert); +// Move perturb object with mouse given a scene state; action is mjtMouse. +MJAPI void mjv_movePerturbFromState(const mjvSceneState* scnstate, int action, + mjtNum reldx, mjtNum reldy, + const mjvScene* scn, mjvPerturb* pert); + // Move model with mouse; action is mjtMouse. MJAPI void mjv_moveModel(const mjModel* m, int action, mjtNum reldx, mjtNum reldy, const mjtNum roomup[3], mjvScene* scn); @@ -606,6 +616,24 @@ MJAPI void mjv_freeScene(mjvScene* scn); MJAPI void mjv_updateScene(const mjModel* m, mjData* d, const mjvOption* opt, const mjvPerturb* pert, mjvCamera* cam, int catmask, mjvScene* scn); +// Update entire scene from a scene state, return the number of new mjWARN_VGEOMFULL warnings. +MJAPI int mjv_updateSceneFromState(const mjvSceneState* scnstate, const mjvOption* opt, + const mjvPerturb* pert, mjvCamera* cam, int catmask, + mjvScene* scn); + +// Set default scene state. +MJAPI void mjv_defaultSceneState(mjvSceneState* scnstate); + +// Allocate resources and initialize a scene state object. +MJAPI void mjv_makeSceneState(const mjModel* m, const mjData* d, + mjvSceneState* scnstate, int maxgeom); + +// Free scene state. +MJAPI void mjv_freeSceneState(mjvSceneState* scnstate); + +// Update a scene state from model and data. +MJAPI void mjv_updateSceneState(const mjModel* m, mjData* d, mjvSceneState* scnstate); + // Add geoms from selected categories. MJAPI void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* opt, const mjvPerturb* pert, int catmask, mjvScene* scn); @@ -637,6 +665,9 @@ MJAPI void mjr_addAux(int index, int width, int height, int samples, mjrContext* // Free resources in custom OpenGL context, set to default. MJAPI void mjr_freeContext(mjrContext* con); +// Resize offscreen buffers. +MJAPI void mjr_resizeOffscreen(int width, int height, mjrContext* con); + // Upload texture to GPU, overwriting previous upload if any. MJAPI void mjr_uploadTexture(const mjModel* m, const mjrContext* con, int texid); diff --git a/introspect/functions.py b/introspect/functions.py index ee1e1205..437c1b63 100644 --- a/introspect/functions.py +++ b/introspect/functions.py @@ -3276,6 +3276,44 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ), doc='Move camera with mouse; action is mjtMouse.', )), + ('mjv_moveCameraFromState', + FunctionDecl( + name='mjv_moveCameraFromState', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='scnstate', + type=PointerType( + inner_type=ValueType(name='mjvSceneState', is_const=True), + ), + ), + FunctionParameterDecl( + name='action', + type=ValueType(name='int'), + ), + FunctionParameterDecl( + name='reldx', + type=ValueType(name='mjtNum'), + ), + FunctionParameterDecl( + name='reldy', + type=ValueType(name='mjtNum'), + ), + FunctionParameterDecl( + name='scn', + type=PointerType( + inner_type=ValueType(name='mjvScene', is_const=True), + ), + ), + FunctionParameterDecl( + name='cam', + type=PointerType( + inner_type=ValueType(name='mjvCamera'), + ), + ), + ), + doc='Move camera with mouse given a scene state; action is mjtMouse.', + )), ('mjv_movePerturb', FunctionDecl( name='mjv_movePerturb', @@ -3320,6 +3358,44 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ), doc='Move perturb object with mouse; action is mjtMouse.', )), + ('mjv_movePerturbFromState', + FunctionDecl( + name='mjv_movePerturbFromState', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='scnstate', + type=PointerType( + inner_type=ValueType(name='mjvSceneState', is_const=True), + ), + ), + FunctionParameterDecl( + name='action', + type=ValueType(name='int'), + ), + FunctionParameterDecl( + name='reldx', + type=ValueType(name='mjtNum'), + ), + FunctionParameterDecl( + name='reldy', + type=ValueType(name='mjtNum'), + ), + FunctionParameterDecl( + name='scn', + type=PointerType( + inner_type=ValueType(name='mjvScene', is_const=True), + ), + ), + FunctionParameterDecl( + name='pert', + type=PointerType( + inner_type=ValueType(name='mjvPerturb'), + ), + ), + ), + doc='Move perturb object with mouse given a scene state; action is mjtMouse.', # pylint: disable=line-too-long + )), ('mjv_moveModel', FunctionDecl( name='mjv_moveModel', @@ -3752,6 +3828,132 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ), doc='Update entire scene given model state.', )), + ('mjv_updateSceneFromState', + FunctionDecl( + name='mjv_updateSceneFromState', + return_type=ValueType(name='int'), + parameters=( + FunctionParameterDecl( + name='scnstate', + type=PointerType( + inner_type=ValueType(name='mjvSceneState', is_const=True), + ), + ), + FunctionParameterDecl( + name='opt', + type=PointerType( + inner_type=ValueType(name='mjvOption', is_const=True), + ), + ), + FunctionParameterDecl( + name='pert', + type=PointerType( + inner_type=ValueType(name='mjvPerturb', is_const=True), + ), + ), + FunctionParameterDecl( + name='cam', + type=PointerType( + inner_type=ValueType(name='mjvCamera'), + ), + ), + FunctionParameterDecl( + name='catmask', + type=ValueType(name='int'), + ), + FunctionParameterDecl( + name='scn', + type=PointerType( + inner_type=ValueType(name='mjvScene'), + ), + ), + ), + doc='Update entire scene from a scene state, return the number of new mjWARN_VGEOMFULL warnings.', # pylint: disable=line-too-long + )), + ('mjv_defaultSceneState', + FunctionDecl( + name='mjv_defaultSceneState', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='scnstate', + type=PointerType( + inner_type=ValueType(name='mjvSceneState'), + ), + ), + ), + doc='Set default scene state.', + )), + ('mjv_makeSceneState', + FunctionDecl( + name='mjv_makeSceneState', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='m', + type=PointerType( + inner_type=ValueType(name='mjModel', is_const=True), + ), + ), + FunctionParameterDecl( + name='d', + type=PointerType( + inner_type=ValueType(name='mjData', is_const=True), + ), + ), + FunctionParameterDecl( + name='scnstate', + type=PointerType( + inner_type=ValueType(name='mjvSceneState'), + ), + ), + FunctionParameterDecl( + name='maxgeom', + type=ValueType(name='int'), + ), + ), + doc='Allocate resources and initialize a scene state object.', + )), + ('mjv_freeSceneState', + FunctionDecl( + name='mjv_freeSceneState', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='scnstate', + type=PointerType( + inner_type=ValueType(name='mjvSceneState'), + ), + ), + ), + doc='Free scene state.', + )), + ('mjv_updateSceneState', + FunctionDecl( + name='mjv_updateSceneState', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='m', + type=PointerType( + inner_type=ValueType(name='mjModel', is_const=True), + ), + ), + FunctionParameterDecl( + name='d', + type=PointerType( + inner_type=ValueType(name='mjData'), + ), + ), + FunctionParameterDecl( + name='scnstate', + type=PointerType( + inner_type=ValueType(name='mjvSceneState'), + ), + ), + ), + doc='Update a scene state from model and data.', + )), ('mjv_addGeoms', FunctionDecl( name='mjv_addGeoms', @@ -3978,6 +4180,28 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ), doc='Free resources in custom OpenGL context, set to default.', )), + ('mjr_resizeOffscreen', + FunctionDecl( + name='mjr_resizeOffscreen', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='width', + type=ValueType(name='int'), + ), + FunctionParameterDecl( + name='height', + type=ValueType(name='int'), + ), + FunctionParameterDecl( + name='con', + type=PointerType( + inner_type=ValueType(name='mjrContext'), + ), + ), + ), + doc='Resize offscreen buffers.', + )), ('mjr_uploadTexture', FunctionDecl( name='mjr_uploadTexture', diff --git a/introspect/structs.py b/introspect/structs.py index 16a0ee7c..ed21de7a 100644 --- a/introspect/structs.py +++ b/introspect/structs.py @@ -319,11 +319,6 @@ STRUCTS: Mapping[str, StructDecl] = dict([ type=ValueType(name='int'), doc='height of offscreen buffer', ), - StructFieldDecl( - name='treedepth', - type=ValueType(name='int'), - doc='depth of the bounding volume hierarchy', - ), StructFieldDecl( name='ellipsoidinertia', type=ValueType(name='int'), @@ -4767,6 +4762,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([ ), doc='visualization flags (indexed by mjtVisFlag)', ), + StructFieldDecl( + name='bvh_depth', + type=ValueType(name='int'), + doc='depth of the bounding volume hierarchy to be visualized', + ), ), )), ('mjvScene', @@ -5151,6 +5151,1200 @@ STRUCTS: Mapping[str, StructDecl] = dict([ ), ), )), + ('mjvSceneState', + StructDecl( + name='mjvSceneState', + declname='struct mjvSceneState_', + fields=( + StructFieldDecl( + name='nbuffer', + type=ValueType(name='int'), + doc='size of the buffer in bytes', + ), + StructFieldDecl( + name='buffer', + type=PointerType( + inner_type=ValueType(name='void'), + ), + doc='heap-allocated memory for all arrays in this struct', + ), + StructFieldDecl( + name='maxgeom', + type=ValueType(name='int'), + doc='maximum number of mjvGeom supported by this state object', + ), + StructFieldDecl( + name='plugincache', + type=ValueType(name='mjvScene'), + doc='scratch space for vis geoms inserted by plugins', + ), + StructFieldDecl( + name='model', + type=AnonymousStructDecl( + fields=( + StructFieldDecl( + name='nu', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='na', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='nbody', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='nbvh', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='njnt', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='ngeom', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='nsite', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='ncam', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='nlight', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='nmesh', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='nskin', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='nskinvert', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='nskinface', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='nskinbone', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='nskinbonevert', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='nmat', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='neq', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='ntendon', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='nwrap', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='nsensor', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='nnames', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='nsensordata', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='opt', + type=ValueType(name='mjOption'), + doc='', + ), + StructFieldDecl( + name='vis', + type=ValueType(name='mjVisual'), + doc='', + ), + StructFieldDecl( + name='stat', + type=ValueType(name='mjStatistic'), + doc='', + ), + StructFieldDecl( + name='body_parentid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='body_rootid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='body_weldid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='body_mocapid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='body_jntnum', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='body_jntadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='body_geomnum', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='body_geomadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='body_iquat', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='body_mass', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='body_inertia', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='body_bvhadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='body_bvhnum', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='bvh_depth', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='bvh_child', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='bvh_geomid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='bvh_aabb', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='jnt_type', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='jnt_bodyid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='jnt_group', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='geom_type', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='geom_bodyid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='geom_dataid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='geom_matid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='geom_group', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='geom_size', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='geom_aabb', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='geom_rbound', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='geom_rgba', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='site_type', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='site_bodyid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='site_matid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='site_group', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='site_size', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='site_rgba', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='cam_fovy', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='cam_ipd', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='light_directional', + type=PointerType( + inner_type=ValueType(name='mjtByte'), + ), + doc='', + ), + StructFieldDecl( + name='light_castshadow', + type=PointerType( + inner_type=ValueType(name='mjtByte'), + ), + doc='', + ), + StructFieldDecl( + name='light_active', + type=PointerType( + inner_type=ValueType(name='mjtByte'), + ), + doc='', + ), + StructFieldDecl( + name='light_attenuation', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='light_cutoff', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='light_exponent', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='light_ambient', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='light_diffuse', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='light_specular', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='mesh_texcoordadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='mesh_graphadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='skin_matid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='skin_group', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='skin_rgba', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='skin_inflate', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='skin_vertadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='skin_vertnum', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='skin_texcoordadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='skin_faceadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='skin_facenum', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='skin_boneadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='skin_bonenum', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='skin_vert', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='skin_face', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='skin_bonevertadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='skin_bonevertnum', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='skin_bonebindpos', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='skin_bonebindquat', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='skin_bonebodyid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='skin_bonevertid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='skin_bonevertweight', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='mat_texid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='mat_texuniform', + type=PointerType( + inner_type=ValueType(name='mjtByte'), + ), + doc='', + ), + StructFieldDecl( + name='mat_texrepeat', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='mat_emission', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='mat_specular', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='mat_shininess', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='mat_reflectance', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='mat_rgba', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='eq_type', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='eq_obj1id', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='eq_obj2id', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='eq_active', + type=PointerType( + inner_type=ValueType(name='mjtByte'), + ), + doc='', + ), + StructFieldDecl( + name='eq_data', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='tendon_num', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='tendon_matid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='tendon_group', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='tendon_limited', + type=PointerType( + inner_type=ValueType(name='mjtByte'), + ), + doc='', + ), + StructFieldDecl( + name='tendon_width', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='tendon_range', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='tendon_stiffness', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='tendon_damping', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='tendon_frictionloss', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='tendon_lengthspring', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='tendon_rgba', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='', + ), + StructFieldDecl( + name='actuator_trntype', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='actuator_dyntype', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='actuator_trnid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='actuator_actadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='actuator_actnum', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='actuator_group', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='actuator_ctrllimited', + type=PointerType( + inner_type=ValueType(name='mjtByte'), + ), + doc='', + ), + StructFieldDecl( + name='actuator_actlimited', + type=PointerType( + inner_type=ValueType(name='mjtByte'), + ), + doc='', + ), + StructFieldDecl( + name='actuator_ctrlrange', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='actuator_actrange', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='actuator_cranklength', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='sensor_type', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='sensor_objid', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='sensor_adr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='name_bodyadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='name_jntadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='name_geomadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='name_siteadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='name_camadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='name_lightadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='name_eqadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='name_tendonadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='name_actuatoradr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='names', + type=PointerType( + inner_type=ValueType(name='char'), + ), + doc='', + ), + ), + ), + doc='', + ), + StructFieldDecl( + name='data', + type=AnonymousStructDecl( + fields=( + StructFieldDecl( + name='warning', + type=ArrayType( + inner_type=ValueType(name='mjWarningStat'), + extents=(8,), + ), + doc='', + ), + StructFieldDecl( + name='nefc', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='ncon', + type=ValueType(name='int'), + doc='', + ), + StructFieldDecl( + name='time', + type=ValueType(name='mjtNum'), + doc='', + ), + StructFieldDecl( + name='act', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='ctrl', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='xfrc_applied', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='sensordata', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='xpos', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='xquat', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='xmat', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='xipos', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='ximat', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='xanchor', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='xaxis', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='geom_xpos', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='geom_xmat', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='site_xpos', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='site_xmat', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='cam_xpos', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='cam_xmat', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='light_xpos', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='light_xdir', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='subtree_com', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='ten_wrapadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='ten_wrapnum', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='wrap_obj', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='wrap_xpos', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='bvh_active', + type=PointerType( + inner_type=ValueType(name='mjtByte'), + ), + doc='', + ), + StructFieldDecl( + name='contact', + type=PointerType( + inner_type=ValueType(name='mjContact'), + ), + doc='', + ), + StructFieldDecl( + name='efc_force', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + ), + ), + doc='', + ), + ), + )), ('mjrRect', StructDecl( name='mjrRect', diff --git a/python/mujoco/mjpython/mjpython.mm b/python/mujoco/mjpython/mjpython.mm index 4d55d9b3..97a42311 100644 --- a/python/mujoco/mjpython/mjpython.mm +++ b/python/mujoco/mjpython/mjpython.mm @@ -52,24 +52,28 @@ struct { #define CPYTHON_FN(fname) decltype(&::fname) fname #if PY_MINOR_VERSION >= 8 - CPYTHON_FN(Py_InitializeFromConfig); - CPYTHON_FN(Py_RunMain); + // go/keep-sorted start CPYTHON_FN(PyConfig_Clear); CPYTHON_FN(PyConfig_InitPythonConfig); CPYTHON_FN(PyConfig_SetBytesArgv); + CPYTHON_FN(Py_InitializeFromConfig); + CPYTHON_FN(Py_RunMain); + // go/keep-sorted end #else + // go/keep-sorted start + CPYTHON_FN(PyMem_RawFree); CPYTHON_FN(Py_DecodeLocale); CPYTHON_FN(Py_Initialize); CPYTHON_FN(Py_Main); - CPYTHON_FN(PyMem_RawFree); CPYTHON_FN(Py_SetProgramName); + // go/keep-sorted end #endif // go/keep-sorted start - CPYTHON_FN(Py_FinalizeEx); CPYTHON_FN(PyGILState_Ensure); CPYTHON_FN(PyGILState_Release); CPYTHON_FN(PyRun_SimpleStringFlags); + CPYTHON_FN(Py_FinalizeEx); // go/keep-sorted end #undef CPYTHON_FN @@ -131,16 +135,16 @@ class _MjPythonImpl(mujoco.viewer._MjPythonBase): def __init__(self): self._cond = threading.Condition() - self._model_data = None + self._task = None self._termination = self.__class__.NOT_TERMINATED self._busy = False - def launch_on_ui_thread(self, model, data): + def launch_on_ui_thread(self, model, data, handle_return): with self._cond: - if self._busy or self._model_data is not None: + if self._busy or self._task is not None: raise RuntimeError('another MuJoCo viewer is already open') else: - self._model_data = (model, data) + self._task = (model, data, handle_return) self._cond.notify() def terminate(self): @@ -153,17 +157,17 @@ class _MjPythonImpl(mujoco.viewer._MjPythonBase): def get(self): with self._cond: self._cond.wait_for( - lambda: self._model_data is not None or self._termination) + lambda: self._task is not None or self._termination) if self._termination: if self._termination == self.__class__.TERMINATION_REQUESTED: self._termination = self.__class__.TERMINATION_ACCEPTED return None - model_data = self._model_data + task = self._task self._busy = True - self._model_data = None - return model_data + self._task = None + return task def done(self): with self._cond: @@ -257,24 +261,28 @@ int main(int argc, char** argv) { } #if PY_MINOR_VERSION >= 8 - CPYTHON_INITFN(Py_InitializeFromConfig); - CPYTHON_INITFN(Py_RunMain); + // go/keep-sorted start CPYTHON_INITFN(PyConfig_Clear); CPYTHON_INITFN(PyConfig_InitPythonConfig); CPYTHON_INITFN(PyConfig_SetBytesArgv); + CPYTHON_INITFN(Py_InitializeFromConfig); + CPYTHON_INITFN(Py_RunMain); + // go/keep-sorted end #else + // go/keep-sorted start + CPYTHON_INITFN(PyMem_RawFree); CPYTHON_INITFN(Py_DecodeLocale); CPYTHON_INITFN(Py_Initialize); CPYTHON_INITFN(Py_Main); - CPYTHON_INITFN(PyMem_RawFree); CPYTHON_INITFN(Py_SetProgramName); + // go/keep-sorted end #endif // go/keep-sorted start - CPYTHON_INITFN(Py_FinalizeEx); CPYTHON_INITFN(PyGILState_Ensure); CPYTHON_INITFN(PyGILState_Release); CPYTHON_INITFN(PyRun_SimpleStringFlags); + CPYTHON_INITFN(Py_FinalizeEx); // go/keep-sorted end #undef CPYTHON_INITFN @@ -327,17 +335,18 @@ with cond: while True: try: # Wait for an incoming payload. - payload = mujoco.viewer._MJPYTHON.get() + task = mujoco.viewer._MJPYTHON.get() # None means that we are exiting. - if payload is None: + if task is None: glfw.terminate() break # Otherwise, launch the viewer. - model, data = payload + model, data, handle_return = task ctypes.CDLL(None).mjpython_show_dock_icon() - mujoco.viewer._launch_internal(model, data, run_physics_thread=False) + mujoco.viewer._launch_internal( + model, data, run_physics_thread=False, handle_return=handle_return) ctypes.CDLL(None).mjpython_hide_dock_icon() finally: diff --git a/python/mujoco/render.cc b/python/mujoco/render.cc index e6a27983..622bbb7a 100644 --- a/python/mujoco/render.cc +++ b/python/mujoco/render.cc @@ -250,6 +250,7 @@ PYBIND11_MODULE(_render, pymodule) { Def(pymodule); Def(pymodule); // Skipped: mjr_freeContext (have MjrContext.__del__) + Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); diff --git a/python/mujoco/simulate.cc b/python/mujoco/simulate.cc index 1b43f167..b2373da7 100644 --- a/python/mujoco/simulate.cc +++ b/python/mujoco/simulate.cc @@ -12,29 +12,76 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include +#include #include #include #include +#include #include #include #include #include "structs.h" +#include #include +#include namespace mujoco::python { namespace { +namespace py = ::pybind11; + template constexpr inline std::size_t sizeof_arr(const T(&arr)[N]) { return sizeof(arr); } -PYBIND11_MODULE(_simulate, pymodule) { - namespace py = ::pybind11; - using SimulateMutex = decltype(mujoco::Simulate::mtx); +class SimulateWrapper : public mujoco::Simulate { + public: + SimulateWrapper(std::unique_ptr platform_ui_adapter, + py::object scn, py::object cam, + py::object opt, py::object pert, bool fully_managed) + : Simulate(std::move(platform_ui_adapter), + scn.cast().get(), + cam.cast().get(), + opt.cast().get(), + pert.cast().get(), + fully_managed), + m_(py::none()), + d_(py::none()), + scn_(scn), + cam_(cam), + opt_(opt), + pert_(pert) {} - py::class_(pymodule, "SimulateMutex") + void Load(py::object m, py::object d, const std::string& path) { + mjModel* m_raw = m.cast().get(); + mjData* d_raw = d.cast().get(); + { + py::gil_scoped_release no_gil; + Simulate::Load(m_raw, d_raw, path.c_str()); + } + m_ = m; + d_ = d; + m_raw_ = m_raw; + d_raw_ = d_raw; + } + + private: + // Hold references to keep these Python objects alive for as long as the + // simulate object. + py::object m_; + py::object d_; + py::object scn_; + py::object cam_; + py::object opt_; + py::object pert_; + + mjModel* m_raw_ = nullptr; + mjData* d_raw_ = nullptr; +}; + +PYBIND11_MODULE(_simulate, pymodule) { + py::class_(pymodule, "Mutex") .def( "__enter__", [](SimulateMutex& mtx) { mtx.lock(); }, py::call_guard()) @@ -45,36 +92,29 @@ PYBIND11_MODULE(_simulate, pymodule) { }, py::call_guard()); - py::class_(pymodule, "Simulate") - .def(py::init([]() { - return std::make_unique( - std::make_unique()); + py::class_(pymodule, "Simulate") + .def_readonly_static("MAX_GEOM", &mujoco::Simulate::kMaxGeom) + .def(py::init([](py::object scn, py::object cam, py::object opt, + py::object pert, bool fully_managed) { + return std::make_unique( + std::make_unique(), scn, cam, opt, pert, + fully_managed); })) - .def( - "render_loop", - [](mujoco::Simulate& simulate) { simulate.RenderLoop(); }, - py::call_guard()) - .def( - "load", - [](mujoco::Simulate& simulate, MjModelWrapper& m, MjDataWrapper& d, - const std::string& path) { - simulate.Load(m.get(), d.get(), path.c_str()); - }, - py::call_guard()) - .def("apply_pose_perturbations", - &mujoco::Simulate::ApplyPosePerturbations, - py::call_guard()) - .def("apply_force_perturbations", - &mujoco::Simulate::ApplyForcePerturbations, + .def("load", &SimulateWrapper::Load) + .def("sync", &mujoco::Simulate::Sync, py::call_guard()) + .def( + "render_loop", + [](SimulateWrapper& simulate) { simulate.RenderLoop(); }, + py::call_guard()) .def( "lock", - [](mujoco::Simulate& simulate) -> SimulateMutex& { + [](SimulateWrapper& simulate) -> SimulateMutex& { return simulate.mtx; }, py::call_guard(), - py::return_value_policy::reference) + py::return_value_policy::reference_internal) .def_readonly("ctrl_noise_std", &mujoco::Simulate::ctrl_noise_std, py::call_guard()) .def_readonly("ctrl_noise_rate", &mujoco::Simulate::ctrl_noise_rate, @@ -96,54 +136,52 @@ PYBIND11_MODULE(_simulate, pymodule) { .def_property( "exitrequest", - [](mujoco::Simulate& simulate) { - return simulate.exitrequest.load(); - }, - [](mujoco::Simulate& simulate, bool exitrequest) { + [](SimulateWrapper& simulate) { return simulate.exitrequest.load(); }, + [](SimulateWrapper& simulate, int exitrequest) { simulate.exitrequest.store(exitrequest); }, py::call_guard()) .def_property_readonly( "uiloadrequest", - [](mujoco::Simulate& simulate) { + [](SimulateWrapper& simulate) { return simulate.uiloadrequest.load(); }, py::call_guard()) .def( "uiloadrequest_decrement", - [](mujoco::Simulate& simulate) { + [](SimulateWrapper& simulate) { simulate.uiloadrequest.fetch_sub(1); }, py::call_guard()) .def_property( "droploadrequest", - [](mujoco::Simulate& simulate) { + [](SimulateWrapper& simulate) { return simulate.droploadrequest.load(); }, - [](mujoco::Simulate& simulate, bool droploadrequest) { + [](SimulateWrapper& simulate, bool droploadrequest) { simulate.droploadrequest.store(droploadrequest); }, py::call_guard()) .def_property_readonly( "dropfilename", - [](mujoco::Simulate& simulate) -> std::string { + [](SimulateWrapper& simulate) -> std::string { return simulate.dropfilename; }, py::call_guard()) .def_property_readonly( "filename", - [](mujoco::Simulate& simulate) -> std::string { + [](SimulateWrapper& simulate) -> std::string { return simulate.filename; }, py::call_guard()) .def_property( "load_error", - [](mujoco::Simulate& simulate) -> std::string { + [](SimulateWrapper& simulate) -> std::string { return simulate.load_error; }, - [](mujoco::Simulate& simulate, const std::string& error) { + [](SimulateWrapper& simulate, const std::string& error) { const auto max_length = sizeof_arr(simulate.load_error); std::strncpy(simulate.load_error, error.c_str(), max_length - 1); simulate.load_error[max_length - 1] = '\0'; diff --git a/python/mujoco/structs.cc b/python/mujoco/structs.cc index f490b64b..cc6675fc 100644 --- a/python/mujoco/structs.cc +++ b/python/mujoco/structs.cc @@ -1305,7 +1305,6 @@ PYBIND11_MODULE(_structs, m) { X(realtime); X(offwidth); X(offheight); - X(treedepth); X(ellipsoidinertia); #undef X @@ -2134,6 +2133,7 @@ This is useful for example when the MJB is not available as a file on disk.)")); }) X(label); X(frame); + X(bvh_depth); #undef X #define X(var) DefinePyArray(mjvOption, #var, &MjvOptionWrapper::var) diff --git a/python/mujoco/viewer.py b/python/mujoco/viewer.py index e6ce21e4..375c01bd 100644 --- a/python/mujoco/viewer.py +++ b/python/mujoco/viewer.py @@ -16,14 +16,15 @@ import abc import atexit -import code -import inspect +import contextlib import math import os +import queue import sys import threading import time from typing import Callable, Optional, Tuple, Union +import weakref import glfw import mujoco @@ -56,7 +57,70 @@ LoaderType = Callable[[], Tuple[mujoco.MjModel, mujoco.MjData]] _LoaderWithPathType = Callable[[], Tuple[mujoco.MjModel, mujoco.MjData, str]] _InternalLoaderType = Union[LoaderType, _LoaderWithPathType] -Simulate = _simulate.Simulate +_Simulate = _simulate.Simulate + + +class Handle: + """A handle for interacting with a MuJoCo viewer.""" + + def __init__( + self, + sim: _Simulate, + scn: mujoco.MjvScene, + cam: mujoco.MjvCamera, + opt: mujoco.MjvOption, + pert: mujoco.MjvPerturb, + ): + self._sim = weakref.ref(sim) + self._scn = scn + self._cam = cam + self._opt = opt + self._pert = pert + + @property + def scn(self): + return self._scn + + @property + def cam(self): + return self._cam + + @property + def opt(self): + return self._opt + + @property + def perturb(self): + return self._pert + + def close(self): + sim = self._sim() + if sim is not None: + sim.exitrequest = 1 + + def is_running(self) -> bool: + sim = self._sim() + if sim is not None: + return sim.exitrequest < 2 + return False + + def lock(self): + sim = self._sim() + if sim is not None: + return sim.lock() + return contextlib.nullcontext() + + def sync(self): + sim = self._sim() + if sim is not None: + with sim.lock(): + sim.sync() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() # Abstract base dispatcher class for systems that require UI calls to be made @@ -83,7 +147,8 @@ def _file_loader(path: str) -> _LoaderWithPathType: def _reload( - simulate: Simulate, loader: _InternalLoaderType + simulate: _Simulate, loader: _InternalLoaderType, + notify_loaded: Optional[Callable[[], None]] = None ) -> Optional[Tuple[mujoco.MjModel, mujoco.MjData]]: """Internal function for reloading a model in the viewer.""" try: @@ -102,10 +167,13 @@ def _reload( path = load_tuple[2] if len(load_tuple) == 3 else '' simulate.load(m, d, path) + if notify_loaded: + notify_loaded() + return m, d -def _physics_loop(simulate: Simulate, loader: Optional[_InternalLoaderType]): +def _physics_loop(simulate: _Simulate, loader: Optional[_InternalLoaderType]): """Physics loop for the GUI, to be run in a separate thread.""" m: mujoco.MjModel = None d: mujoco.MjData = None @@ -181,11 +249,6 @@ def _physics_loop(simulate: Simulate, loader: Optional[_InternalLoaderType]): syncsim = d.time simulate.speed_changed = False - # Clear old perturbations, apply new. - d.xfrc_applied[:, :] = 0 - simulate.apply_pose_perturbations(0) # Move mocap bodies only. - simulate.apply_force_perturbations() - # Run single step, let next iteration deal with timing. mujoco.mj_step(m, d) @@ -203,11 +266,6 @@ def _physics_loop(simulate: Simulate, loader: Optional[_InternalLoaderType]): simulate.measured_slowdown = elapsedcpu / elapsedsim measured = True - # Clear old perturbations, apply new. - d.xfrc_applied[:, :] = 0 - simulate.applyposepertubations(0) # Move mocap bodies only. - simulate.applyforceperturbations() - # Call mj_step. mujoco.mj_step(m, d) @@ -215,19 +273,19 @@ def _physics_loop(simulate: Simulate, loader: Optional[_InternalLoaderType]): if d.time < prevsim: break else: # simulate.run is False: GUI is paused. - # Apply pose perturbation. - simulate.applyposepertubations(1) # Move mocap and dynamic bodies. # Run mj_forward, to update rendering and joint sliders. mujoco.mj_forward(m, d) -def _launch_internal(model: Optional[mujoco.MjModel] = None, - data: Optional[mujoco.MjData] = None, - *, - run_physics_thread: bool = True, - loader: Optional[_InternalLoaderType] = None, - simulate: Optional[Simulate] = None) -> None: +def _launch_internal( + model: Optional[mujoco.MjModel] = None, + data: Optional[mujoco.MjData] = None, + *, + run_physics_thread: bool, + loader: Optional[_InternalLoaderType] = None, + handle_return: Optional['queue.Queue[Handle]'] = None, +) -> None: """Internal API, so that the public API has more readable type annotations.""" if model is None and data is not None: raise ValueError('mjData is specified but mjModel is not') @@ -236,6 +294,8 @@ def _launch_internal(model: Optional[mujoco.MjModel] = None, 'mjData should not be specified when an mjModel loader is used') elif loader is not None and model is not None: raise ValueError('model and loader are both specified') + elif run_physics_thread and handle_return is not None: + raise ValueError('run_physics_thread and handle_return are both specified') if loader is None and model is not None: @@ -246,9 +306,14 @@ def _launch_internal(model: Optional[mujoco.MjModel] = None, loader = _loader - # The simulate object encapsulates the UI. - if simulate is None: - simulate = Simulate() + if model and not run_physics_thread: + scn = mujoco.MjvScene(model, _Simulate.MAX_GEOM) + else: + scn = mujoco.MjvScene() + cam = mujoco.MjvCamera() + opt = mujoco.MjvOption() + pert = mujoco.MjvPerturb() + simulate = _Simulate(scn, cam, opt, pert, run_physics_thread) # Initialize GLFW if not using mjpython. if _MJPYTHON is None: @@ -256,13 +321,18 @@ def _launch_internal(model: Optional[mujoco.MjModel] = None, raise mujoco.FatalError('could not initialize GLFW') atexit.register(glfw.terminate) + notify_loaded = None + if handle_return: + notify_loaded = ( + lambda: handle_return.put_nowait(Handle(simulate, scn, cam, opt, pert))) + side_thread = None if run_physics_thread: side_thread = threading.Thread( target=_physics_loop, args=(simulate, loader)) else: side_thread = threading.Thread( - target=_reload, args=(simulate, loader)) + target=_reload, args=(simulate, loader, notify_loaded)) def make_exit_requester(simulate): def exit_requester(): @@ -281,18 +351,15 @@ def _launch_internal(model: Optional[mujoco.MjModel] = None, def launch(model: Optional[mujoco.MjModel] = None, data: Optional[mujoco.MjData] = None, *, - run_physics_thread: bool = True, loader: Optional[LoaderType] = None) -> None: """Launches the Simulate GUI.""" - if not run_physics_thread: - mujoco.mj_forward(model, data) _launch_internal( - model, data, run_physics_thread=run_physics_thread, loader=loader) + model, data, run_physics_thread=True, loader=loader) def launch_from_path(path: str) -> None: """Launches the Simulate GUI from file path.""" - _launch_internal(loader=_file_loader(path)) + _launch_internal(run_physics_thread=True, loader=_file_loader(path)) def launch_passive(model: mujoco.MjModel, data: mujoco.MjData) -> None: @@ -303,12 +370,13 @@ def launch_passive(model: mujoco.MjModel, data: mujoco.MjData) -> None: raise ValueError(f'`data` is not a mujoco.MjData: got {data!r}') mujoco.mj_forward(model, data) + handle_return = queue.Queue(1) if sys.platform != 'darwin': thread = threading.Thread( target=_launch_internal, args=(model, data), - kwargs=dict(run_physics_thread=False), + kwargs=dict(run_physics_thread=False, handle_return=handle_return), ) thread.daemon = True thread.start() @@ -316,85 +384,10 @@ def launch_passive(model: mujoco.MjModel, data: mujoco.MjData) -> None: if not isinstance(_MJPYTHON, _MjPythonBase): raise RuntimeError( '`launch_passive` requires that the Python script be run under ' - '`mjpython`') - _MJPYTHON.launch_on_ui_thread(model, data) + '`mjpython` on macOS') + _MJPYTHON.launch_on_ui_thread(model, data, handle_return) - -def launch_repl(model: mujoco.MjModel, data: mujoco.MjData) -> None: - """Launches the Simulate GUI in REPL mode.""" - ipython_shell = None - try: - import IPython # pylint: disable=g-import-not-at-top - ipython_shell = IPython.get_ipython() - ipython_is_terminal_interactive_shell = isinstance( - ipython_shell, - IPython.terminal.interactiveshell.TerminalInteractiveShell) - except ImportError: - ipython_is_terminal_interactive_shell = False - - simulate = Simulate() - viewer_is_running = True - - def start_shell(global_variables): - if ipython_is_terminal_interactive_shell: - ipython_shell.execution_count += 1 - - # A SQLite connection can only be used on the same thread that opened it. - # We cache the existing connection and reopen on the current thread. - old_db = ipython_shell.history_manager.db - ipython_shell.history_manager.init_db() - ipython_shell.history_manager.new_session() - - try: - # Replicate IPython main loop without exiting on keyboard interrupt, - # unless the viewer window has already been closed. - # (https://github.com/ipython/ipython/blob/8.9.0/IPython/terminal/interactiveshell.py#L701) - while viewer_is_running and ipython_shell.keep_running: - print(ipython_shell.separate_in, end='') - try: - c = ipython_shell.prompt_for_code() - except EOFError: - if not ipython_shell.confirm_exit or ipython_shell.ask_yes_no( - 'Do you really want to exit ([y]/n)?', 'y', 'n'): - ipython_shell.ask_exit() - if not ipython_shell.keep_running and simulate is not None: - simulate.exitrequest = True - else: - if c: - ipython_shell.run_cell(c, store_history=True) - finally: - # Close the temporary history DB connection and restore the old one. - ipython_shell.history_manager.end_session() - ipython_shell.history_manager.db.close() - ipython_shell.history_manager.db = old_db - ipython_shell.execution_count -= 1 - else: - code.InteractiveConsole(locals=global_variables).interact() - - # End IPython history session on the main thread. We will need to open - # a new session in the REPL thread. - if ipython_is_terminal_interactive_shell: - ipython_shell.history_manager.end_session() - - try: - # Continue the IPython REPL session in a separate thread. - repl_thread = threading.Thread( - target=start_shell, args=(inspect.stack()[1][0].f_globals,)) - repl_thread.start() - - # Launch the viewer on the main thread. - mujoco.mj_forward(model, data) - _launch_internal( - model, data, run_physics_thread=False, simulate=simulate) - simulate = None - - # Wait until the REPL thread quits, then restore the IPython history - # DB session on the main thread. - viewer_is_running = False - repl_thread.join() - finally: - if ipython_is_terminal_interactive_shell: - ipython_shell.history_manager.new_session() + return handle_return.get() if __name__ == '__main__': diff --git a/simulate/main.cc b/simulate/main.cc index b02daf9c..513c09a5 100644 --- a/simulate/main.cc +++ b/simulate/main.cc @@ -313,7 +313,7 @@ void PhysicsLoop(mj::Simulate& sim) { { // lock the sim mutex - const std::lock_guard lock(sim.mtx); + const std::unique_lock lock(sim.mtx); // run only if model is present if (m) { @@ -356,11 +356,6 @@ void PhysicsLoop(mj::Simulate& sim) { syncSim = d->time; sim.speed_changed = false; - // clear old perturbations, apply new - mju_zero(d->xfrc_applied, 6*m->nbody); - sim.ApplyPosePerturbations(0); // move mocap bodies only - sim.ApplyForcePerturbations(); - // run single step, let next iteration deal with timing mj_step(m, d); } @@ -382,11 +377,6 @@ void PhysicsLoop(mj::Simulate& sim) { measured = true; } - // clear old perturbations, apply new - mju_zero(d->xfrc_applied, 6*m->nbody); - sim.ApplyPosePerturbations(0); // move mocap bodies only - sim.ApplyForcePerturbations(); - // call mj_step mj_step(m, d); @@ -400,9 +390,6 @@ void PhysicsLoop(mj::Simulate& sim) { // paused else { - // apply pose perturbation - sim.ApplyPosePerturbations(1); // move mocap and dynamic bodies - // run mj_forward, to update rendering and joint sliders mj_forward(m, d); } @@ -468,9 +455,23 @@ int main(int argc, const char** argv) { // scan for libraries in the plugin directory to load additional plugins scanPluginLibraries(); + mjvScene scn; + mjv_defaultScene(&scn); + + mjvCamera cam; + mjv_defaultCamera(&cam); + + mjvOption opt; + mjv_defaultOption(&opt); + + mjvPerturb pert; + mjv_defaultPerturb(&pert); + // simulate object encapsulates the UI auto sim = std::make_unique( - std::make_unique()); + std::make_unique(), + &scn, &cam, &opt, &pert, /* fully_managed = */ true + ); const char* filename = nullptr; if (argc > 1) { diff --git a/simulate/platform_ui_adapter.cc b/simulate/platform_ui_adapter.cc index 0eacf072..37f24a33 100644 --- a/simulate/platform_ui_adapter.cc +++ b/simulate/platform_ui_adapter.cc @@ -35,6 +35,10 @@ bool PlatformUIAdapter::RefreshMjrContext(const mjModel* m, int fontscale) { return false; } +bool PlatformUIAdapter::EnsureContextSize() { + return false; +} + void PlatformUIAdapter::OnFilesDrop(int count, const char** paths) { state_.type = mjEVENT_FILESDROP; state_.dropcount = count; diff --git a/simulate/platform_ui_adapter.h b/simulate/platform_ui_adapter.h index 7a63dd0b..8332c457 100644 --- a/simulate/platform_ui_adapter.h +++ b/simulate/platform_ui_adapter.h @@ -41,6 +41,8 @@ class PlatformUIAdapter { // Optionally overrideable function to (re)create an mjrContext for an mjModel virtual bool RefreshMjrContext(const mjModel* m, int fontscale); + virtual bool EnsureContextSize(); + // Pure virtual functions to be implemented by individual adapters virtual std::pair GetCursorPosition() const = 0; virtual double GetDisplayPixelsPerInch() const = 0; diff --git a/simulate/simulate.cc b/simulate/simulate.cc index dc000288..34c2c3c1 100644 --- a/simulate/simulate.cc +++ b/simulate/simulate.cc @@ -19,13 +19,13 @@ #include #include #include -#include #include #include -#include +#include #include #include "lodepng.h" +#include #include #include #include @@ -52,9 +52,53 @@ namespace mju = ::mujoco::sample_util; using Seconds = std::chrono::duration; using Milliseconds = std::chrono::duration; +template +inline bool ScalarsDiffer(const T& a, const T& b) { + return a != b; +} + +template +inline bool ArraysDiffer(const T (&a)[N], const T (&b)[N]) { + for (int i = 0; i < N; ++i) { + if (a[i] != b[i]) { + return true; + } + } + return false; +} + +template +inline bool Differ(const T& a, const T& b) { + if constexpr (std::is_array_v) { + return ArraysDiffer(a, b); + } else { + return ScalarsDiffer(a, b); + } +} + +template +inline void CopyScalar(T& dst, const T& src) { + dst = src; +} + +template +inline void CopyArray(T (&dst)[N], const T (&src)[N]) { + for (int i = 0; i < N; ++i) { + dst[i] = src[i]; + } +} + +template +inline void Copy(T& dst, const T& src) { + if constexpr (std::is_array_v) { + CopyArray(dst, src); + } else { + CopyScalar(dst, src); + } +} + //------------------------------------------- global ----------------------------------------------- -const int maxgeom = 20000; // preallocated geom array in mjvScene const double zoom_increment = 0.02; // ratio of one click-wheel zoom increment to vertical extent // section ids @@ -231,17 +275,17 @@ void InitializeProfiler(mj::Simulate* sim) { } // update profiler figures -void UpdateProfiler(mj::Simulate* sim) { +void UpdateProfiler(mj::Simulate* sim, const mjModel* m, const mjData* d) { // update constraint figure - sim->figconstraint.linepnt[0] = mjMIN(mjMIN(sim->d->solver_iter, mjNSOLVER), mjMAXLINEPNT); + sim->figconstraint.linepnt[0] = mjMIN(mjMIN(d->solver_iter, mjNSOLVER), mjMAXLINEPNT); for (int i=1; i<5; i++) { sim->figconstraint.linepnt[i] = sim->figconstraint.linepnt[0]; } - if (sim->m->opt.solver==mjSOL_PGS) { + if (m->opt.solver==mjSOL_PGS) { sim->figconstraint.linepnt[3] = 0; sim->figconstraint.linepnt[4] = 0; } - if (sim->m->opt.solver==mjSOL_CG) { + if (m->opt.solver==mjSOL_CG) { sim->figconstraint.linepnt[4] = 0; } for (int i=0; ifigconstraint.linepnt[0]; i++) { @@ -253,19 +297,19 @@ void UpdateProfiler(mj::Simulate* sim) { sim->figconstraint.linedata[4][2*i] = i; // y - sim->figconstraint.linedata[0][2*i+1] = sim->d->nefc; - sim->figconstraint.linedata[1][2*i+1] = sim->d->solver[i].nactive; - sim->figconstraint.linedata[2][2*i+1] = sim->d->solver[i].nchange; - sim->figconstraint.linedata[3][2*i+1] = sim->d->solver[i].neval; - sim->figconstraint.linedata[4][2*i+1] = sim->d->solver[i].nupdate; + sim->figconstraint.linedata[0][2*i+1] = d->nefc; + sim->figconstraint.linedata[1][2*i+1] = d->solver[i].nactive; + sim->figconstraint.linedata[2][2*i+1] = d->solver[i].nchange; + sim->figconstraint.linedata[3][2*i+1] = d->solver[i].neval; + sim->figconstraint.linedata[4][2*i+1] = d->solver[i].nupdate; } // update cost figure - sim->figcost.linepnt[0] = mjMIN(mjMIN(sim->d->solver_iter, mjNSOLVER), mjMAXLINEPNT); + sim->figcost.linepnt[0] = mjMIN(mjMIN(d->solver_iter, mjNSOLVER), mjMAXLINEPNT); for (int i=1; i<3; i++) { sim->figcost.linepnt[i] = sim->figcost.linepnt[0]; } - if (sim->m->opt.solver==mjSOL_PGS) { + if (m->opt.solver==mjSOL_PGS) { sim->figcost.linepnt[1] = 0; sim->figcost.linepnt[2] = 0; } @@ -277,25 +321,25 @@ void UpdateProfiler(mj::Simulate* sim) { sim->figcost.linedata[2][2*i] = i; // y - sim->figcost.linedata[0][2*i+1] = mju_log10(mju_max(mjMINVAL, sim->d->solver[i].improvement)); - sim->figcost.linedata[1][2*i+1] = mju_log10(mju_max(mjMINVAL, sim->d->solver[i].gradient)); - sim->figcost.linedata[2][2*i+1] = mju_log10(mju_max(mjMINVAL, sim->d->solver[i].lineslope)); + sim->figcost.linedata[0][2*i+1] = mju_log10(mju_max(mjMINVAL, d->solver[i].improvement)); + sim->figcost.linedata[1][2*i+1] = mju_log10(mju_max(mjMINVAL, d->solver[i].gradient)); + sim->figcost.linedata[2][2*i+1] = mju_log10(mju_max(mjMINVAL, d->solver[i].lineslope)); } // get timers: total, collision, prepare, solve, other - mjtNum total = sim->d->timer[mjTIMER_STEP].duration; - int number = sim->d->timer[mjTIMER_STEP].number; + mjtNum total = d->timer[mjTIMER_STEP].duration; + int number = d->timer[mjTIMER_STEP].number; if (!number) { - total = sim->d->timer[mjTIMER_FORWARD].duration; - number = sim->d->timer[mjTIMER_FORWARD].number; + total = d->timer[mjTIMER_FORWARD].duration; + number = d->timer[mjTIMER_FORWARD].number; } number = mjMAX(1, number); float tdata[5] = { static_cast(total/number), - static_cast(sim->d->timer[mjTIMER_POS_COLLISION].duration/number), - static_cast(sim->d->timer[mjTIMER_POS_MAKE].duration/number) + - static_cast(sim->d->timer[mjTIMER_POS_PROJECT].duration/number), - static_cast(sim->d->timer[mjTIMER_CONSTRAINT].duration/number), + static_cast(d->timer[mjTIMER_POS_COLLISION].duration/number), + static_cast(d->timer[mjTIMER_POS_MAKE].duration/number) + + static_cast(d->timer[mjTIMER_POS_PROJECT].duration/number), + static_cast(d->timer[mjTIMER_CONSTRAINT].duration/number), 0 }; tdata[4] = tdata[0] - tdata[1] - tdata[2] - tdata[3]; @@ -315,12 +359,12 @@ void UpdateProfiler(mj::Simulate* sim) { // get sizes: nv, nbody, nefc, sqrt(nnz), ncont, iter float sdata[6] = { - static_cast(sim->m->nv), - static_cast(sim->m->nbody), - static_cast(sim->d->nefc), - static_cast(mju_sqrt(sim->d->solver_nnz)), - static_cast(sim->d->ncon), - static_cast(sim->d->solver_iter) + static_cast(m->nv), + static_cast(m->nbody), + static_cast(d->nefc), + static_cast(mju_sqrt(d->solver_nnz)), + static_cast(d->ncon), + static_cast(d->solver_iter) }; // update figsize @@ -386,8 +430,7 @@ void InitializeSensor(mj::Simulate* sim) { } // update sensor figure -void UpdateSensor(mj::Simulate* sim) { - mjModel* m = sim->m; +void UpdateSensor(mj::Simulate* sim, const mjModel* m, const mjData* d) { mjvFigure& figsensor = sim->figsensor; static const int maxline = 10; @@ -427,7 +470,7 @@ void UpdateSensor(mj::Simulate* sim) { // y figsensor.linedata[lineid][2*p+4*i+1] = 0; - figsensor.linedata[lineid][2*p+4*i+3] = sim->d->sensordata[adr+i]/cutoff; + figsensor.linedata[lineid][2*p+4*i+3] = d->sensordata[adr+i]/cutoff; } // update linepnt @@ -451,12 +494,9 @@ void ShowSensor(mj::Simulate* sim, mjrRect rect) { } // prepare info text -void UpdateInfoText(mj::Simulate* sim, +void UpdateInfoText(mj::Simulate* sim, const mjModel* m, const mjData* d, char (&title)[mj::Simulate::kMaxFilenameLength], - char (&content)[mj::Simulate::kMaxFilenameLength], - double interval) { - mjModel* m = sim->m; - mjData* d = sim->d; + char (&content)[mj::Simulate::kMaxFilenameLength]) { char tmp[20]; // compute solver error @@ -470,12 +510,20 @@ void UpdateInfoText(mj::Simulate* sim, } solerr = mju_log10(mju_max(mjMINVAL, solerr)); + // format FPS text + char fps[10]; + if (sim->fps_ < 1) { + mju::sprintf_arr(fps, "%0.1f ", sim->fps_); + } else { + mju::sprintf_arr(fps, "%.0f ", sim->fps_); + } + // prepare info text mju::strcpy_arr(title, "Time\nSize\nPruning\nCPU\nSolver \nFPS\nMemory"); int broad_pruning = d->nbodypair_broad ? (100.0*d->nbodypair_narrow)/d->nbodypair_broad : 0; int mid_pruning = d->ngeompair_mid ? (100.0*d->nbodypair_narrow)/d->ngeompair_mid : 0; mju::sprintf_arr(content, - "%-9.3f\n%d (%d con)\nb: %d%% m: %d%%\n%.3f\n%.1f (%d it)\n%.0f\n%.2g of %s", + "%-9.3f\n%d (%d con)\nb: %d%% m: %d%%\n%.3f\n%.1f (%d it)\n%s\n%.2g of %s", d->time, d->nefc, d->ncon, broad_pruning, mid_pruning, @@ -483,7 +531,7 @@ void UpdateInfoText(mj::Simulate* sim, d->timer[mjTIMER_STEP].duration / mjMAX(1, d->timer[mjTIMER_STEP].number) : d->timer[mjTIMER_FORWARD].duration / mjMAX(1, d->timer[mjTIMER_FORWARD].number), solerr, d->solver_iter, - 1/interval, + fps, d->maxuse_arena/(double)(d->nstack * sizeof(mjtNum)), mju_writeNumBytes(d->nstack * sizeof(mjtNum))); @@ -512,20 +560,20 @@ void PrintField(char (&str)[mjMAXUINAME], void* ptr) { } // update watch -void UpdateWatch(mj::Simulate* sim) { +void UpdateWatch(mj::Simulate* sim, const mjModel* m, const mjData* d) { // clear sim->ui0.sect[SECT_WATCH].item[2].multi.nelem = 1; mju::strcpy_arr(sim->ui0.sect[SECT_WATCH].item[2].multi.name[0], "invalid field"); // prepare symbols needed by xmacro - MJDATA_POINTERS_PREAMBLE(sim->m); + MJDATA_POINTERS_PREAMBLE(m); // find specified field in mjData arrays, update value #define X(TYPE, NAME, NR, NC) \ if (!mju::strcmp_arr(#NAME, sim->field) && \ !mju::strcmp_arr(#TYPE, "mjtNum")) { \ - if (sim->index >= 0 && sim->index < sim->m->NR * NC) { \ - PrintField(sim->ui0.sect[SECT_WATCH].item[2].multi.name[0], sim->d->NAME + sim->index); \ + if (sim->index >= 0 && sim->index < m->NR * NC) { \ + PrintField(sim->ui0.sect[SECT_WATCH].item[2].multi.name[0],d->NAME + sim->index); \ } else { \ mju::strcpy_arr(sim->ui0.sect[SECT_WATCH].item[2].multi.name[0], "invalid index"); \ } \ @@ -541,31 +589,30 @@ void UpdateWatch(mj::Simulate* sim) { // make physics section of UI void MakePhysicsSection(mj::Simulate* sim, int oldstate) { - mjOption& opt = sim->m->opt; - + mjOption* opt = sim->fully_managed_ ? &sim->m_->opt : &sim->scnstate_.model.opt; mjuiDef defPhysics[] = { {mjITEM_SECTION, "Physics", oldstate, nullptr, "AP"}, - {mjITEM_SELECT, "Integrator", 2, &(opt.integrator), "Euler\nRK4\nimplicit\nimplicitfast"}, - {mjITEM_SELECT, "Collision", 2, &(opt.collision), "All\nPair\nDynamic"}, - {mjITEM_SELECT, "Cone", 2, &(opt.cone), "Pyramidal\nElliptic"}, - {mjITEM_SELECT, "Jacobian", 2, &(opt.jacobian), "Dense\nSparse\nAuto"}, - {mjITEM_SELECT, "Solver", 2, &(opt.solver), "PGS\nCG\nNewton"}, + {mjITEM_SELECT, "Integrator", 2, &(opt->integrator), "Euler\nRK4\nimplicit\nimplicitfast"}, + {mjITEM_SELECT, "Collision", 2, &(opt->collision), "All\nPair\nDynamic"}, + {mjITEM_SELECT, "Cone", 2, &(opt->cone), "Pyramidal\nElliptic"}, + {mjITEM_SELECT, "Jacobian", 2, &(opt->jacobian), "Dense\nSparse\nAuto"}, + {mjITEM_SELECT, "Solver", 2, &(opt->solver), "PGS\nCG\nNewton"}, {mjITEM_SEPARATOR, "Algorithmic Parameters", 1}, - {mjITEM_EDITNUM, "Timestep", 2, &(opt.timestep), "1 0 1"}, - {mjITEM_EDITINT, "Iterations", 2, &(opt.iterations), "1 0 1000"}, - {mjITEM_EDITNUM, "Tolerance", 2, &(opt.tolerance), "1 0 1"}, - {mjITEM_EDITINT, "Noslip Iter", 2, &(opt.noslip_iterations), "1 0 1000"}, - {mjITEM_EDITNUM, "Noslip Tol", 2, &(opt.noslip_tolerance), "1 0 1"}, - {mjITEM_EDITINT, "MRR Iter", 2, &(opt.mpr_iterations), "1 0 1000"}, - {mjITEM_EDITNUM, "MPR Tol", 2, &(opt.mpr_tolerance), "1 0 1"}, - {mjITEM_EDITNUM, "API Rate", 2, &(opt.apirate), "1 0 1000"}, + {mjITEM_EDITNUM, "Timestep", 2, &(opt->timestep), "1 0 1"}, + {mjITEM_EDITINT, "Iterations", 2, &(opt->iterations), "1 0 1000"}, + {mjITEM_EDITNUM, "Tolerance", 2, &(opt->tolerance), "1 0 1"}, + {mjITEM_EDITINT, "Noslip Iter", 2, &(opt->noslip_iterations), "1 0 1000"}, + {mjITEM_EDITNUM, "Noslip Tol", 2, &(opt->noslip_tolerance), "1 0 1"}, + {mjITEM_EDITINT, "MRR Iter", 2, &(opt->mpr_iterations), "1 0 1000"}, + {mjITEM_EDITNUM, "MPR Tol", 2, &(opt->mpr_tolerance), "1 0 1"}, + {mjITEM_EDITNUM, "API Rate", 2, &(opt->apirate), "1 0 1000"}, {mjITEM_SEPARATOR, "Physical Parameters", 1}, - {mjITEM_EDITNUM, "Gravity", 2, opt.gravity, "3"}, - {mjITEM_EDITNUM, "Wind", 2, opt.wind, "3"}, - {mjITEM_EDITNUM, "Magnetic", 2, opt.magnetic, "3"}, - {mjITEM_EDITNUM, "Density", 2, &(opt.density), "1"}, - {mjITEM_EDITNUM, "Viscosity", 2, &(opt.viscosity), "1"}, - {mjITEM_EDITNUM, "Imp Ratio", 2, &(opt.impratio), "1"}, + {mjITEM_EDITNUM, "Gravity", 2, opt->gravity, "3"}, + {mjITEM_EDITNUM, "Wind", 2, opt->wind, "3"}, + {mjITEM_EDITNUM, "Magnetic", 2, opt->magnetic, "3"}, + {mjITEM_EDITNUM, "Density", 2, &(opt->density), "1"}, + {mjITEM_EDITNUM, "Viscosity", 2, &(opt->viscosity), "1"}, + {mjITEM_EDITNUM, "Imp Ratio", 2, &(opt->impratio), "1"}, {mjITEM_SEPARATOR, "Disable Flags", 1}, {mjITEM_END} }; @@ -575,9 +622,9 @@ void MakePhysicsSection(mj::Simulate* sim, int oldstate) { }; mjuiDef defOverride[] = { {mjITEM_SEPARATOR, "Contact Override", 1}, - {mjITEM_EDITNUM, "Margin", 2, &(opt.o_margin), "1"}, - {mjITEM_EDITNUM, "Sol Imp", 2, &(opt.o_solimp), "5"}, - {mjITEM_EDITNUM, "Sol Ref", 2, &(opt.o_solref), "2"}, + {mjITEM_EDITNUM, "Margin", 2, &(opt->o_margin), "1"}, + {mjITEM_EDITNUM, "Sol Imp", 2, &(opt->o_solimp), "5"}, + {mjITEM_EDITNUM, "Sol Ref", 2, &(opt->o_solref), "2"}, {mjITEM_END} }; @@ -608,7 +655,7 @@ void MakePhysicsSection(mj::Simulate* sim, int oldstate) { // make rendering section of UI -void MakeRenderingSection(mj::Simulate* sim, int oldstate) { +void MakeRenderingSection(mj::Simulate* sim, const mjModel* m, int oldstate) { mjuiDef defRendering[] = { { mjITEM_SECTION, @@ -661,11 +708,11 @@ void MakeRenderingSection(mj::Simulate* sim, int oldstate) { }; // add model cameras, up to UI limit - for (int i=0; im->ncam, mjMAXUIMULTI-2); i++) { + for (int i=0; incam, mjMAXUIMULTI-2); i++) { // prepare name char camname[mjMAXUITEXT] = "\n"; - if (sim->m->names[sim->m->name_camadr[i]]) { - mju::strcat_arr(camname, sim->m->names+sim->m->name_camadr[i]); + if (m->names[m->name_camadr[i]]) { + mju::strcat_arr(camname, m->names+m->name_camadr[i]); } else { mju::sprintf_arr(camname, "\nCamera %d", i); } @@ -708,9 +755,10 @@ void MakeRenderingSection(mj::Simulate* sim, int oldstate) { } // create tree slider - sim->m->vis.global.treedepth = 0; - mjuiDef defTree[] = {{mjITEM_SLIDERINT, "Tree depth", 2, &sim->m->vis.global.treedepth, "-1 15"}, - {mjITEM_END}}; + mjuiDef defTree[] = { + {mjITEM_SLIDERINT, "Tree depth", 2, &sim->opt.bvh_depth, "-1 15"}, + {mjITEM_END} + }; mjui_add(&sim->ui0, defTree); mjui_add(&sim->ui0, defOpenGL); @@ -730,51 +778,50 @@ void MakeRenderingSection(mj::Simulate* sim, int oldstate) { // make group section of UI void MakeGroupSection(mj::Simulate* sim, int oldstate) { - mjvOption& vopt = sim->opt; mjuiDef defGroup[] = { {mjITEM_SECTION, "Group enable", oldstate, nullptr, "AG"}, {mjITEM_SEPARATOR, "Geom groups", 1}, - {mjITEM_CHECKBYTE, "Geom 0", 2, vopt.geomgroup, " 0"}, - {mjITEM_CHECKBYTE, "Geom 1", 2, vopt.geomgroup+1, " 1"}, - {mjITEM_CHECKBYTE, "Geom 2", 2, vopt.geomgroup+2, " 2"}, - {mjITEM_CHECKBYTE, "Geom 3", 2, vopt.geomgroup+3, " 3"}, - {mjITEM_CHECKBYTE, "Geom 4", 2, vopt.geomgroup+4, " 4"}, - {mjITEM_CHECKBYTE, "Geom 5", 2, vopt.geomgroup+5, " 5"}, + {mjITEM_CHECKBYTE, "Geom 0", 2, sim->opt.geomgroup, " 0"}, + {mjITEM_CHECKBYTE, "Geom 1", 2, sim->opt.geomgroup+1, " 1"}, + {mjITEM_CHECKBYTE, "Geom 2", 2, sim->opt.geomgroup+2, " 2"}, + {mjITEM_CHECKBYTE, "Geom 3", 2, sim->opt.geomgroup+3, " 3"}, + {mjITEM_CHECKBYTE, "Geom 4", 2, sim->opt.geomgroup+4, " 4"}, + {mjITEM_CHECKBYTE, "Geom 5", 2, sim->opt.geomgroup+5, " 5"}, {mjITEM_SEPARATOR, "Site groups", 1}, - {mjITEM_CHECKBYTE, "Site 0", 2, vopt.sitegroup, "S0"}, - {mjITEM_CHECKBYTE, "Site 1", 2, vopt.sitegroup+1, "S1"}, - {mjITEM_CHECKBYTE, "Site 2", 2, vopt.sitegroup+2, "S2"}, - {mjITEM_CHECKBYTE, "Site 3", 2, vopt.sitegroup+3, "S3"}, - {mjITEM_CHECKBYTE, "Site 4", 2, vopt.sitegroup+4, "S4"}, - {mjITEM_CHECKBYTE, "Site 5", 2, vopt.sitegroup+5, "S5"}, + {mjITEM_CHECKBYTE, "Site 0", 2, sim->opt.sitegroup, "S0"}, + {mjITEM_CHECKBYTE, "Site 1", 2, sim->opt.sitegroup+1, "S1"}, + {mjITEM_CHECKBYTE, "Site 2", 2, sim->opt.sitegroup+2, "S2"}, + {mjITEM_CHECKBYTE, "Site 3", 2, sim->opt.sitegroup+3, "S3"}, + {mjITEM_CHECKBYTE, "Site 4", 2, sim->opt.sitegroup+4, "S4"}, + {mjITEM_CHECKBYTE, "Site 5", 2, sim->opt.sitegroup+5, "S5"}, {mjITEM_SEPARATOR, "Joint groups", 1}, - {mjITEM_CHECKBYTE, "Joint 0", 2, vopt.jointgroup, ""}, - {mjITEM_CHECKBYTE, "Joint 1", 2, vopt.jointgroup+1, ""}, - {mjITEM_CHECKBYTE, "Joint 2", 2, vopt.jointgroup+2, ""}, - {mjITEM_CHECKBYTE, "Joint 3", 2, vopt.jointgroup+3, ""}, - {mjITEM_CHECKBYTE, "Joint 4", 2, vopt.jointgroup+4, ""}, - {mjITEM_CHECKBYTE, "Joint 5", 2, vopt.jointgroup+5, ""}, + {mjITEM_CHECKBYTE, "Joint 0", 2, sim->opt.jointgroup, ""}, + {mjITEM_CHECKBYTE, "Joint 1", 2, sim->opt.jointgroup+1, ""}, + {mjITEM_CHECKBYTE, "Joint 2", 2, sim->opt.jointgroup+2, ""}, + {mjITEM_CHECKBYTE, "Joint 3", 2, sim->opt.jointgroup+3, ""}, + {mjITEM_CHECKBYTE, "Joint 4", 2, sim->opt.jointgroup+4, ""}, + {mjITEM_CHECKBYTE, "Joint 5", 2, sim->opt.jointgroup+5, ""}, {mjITEM_SEPARATOR, "Tendon groups", 1}, - {mjITEM_CHECKBYTE, "Tendon 0", 2, vopt.tendongroup, ""}, - {mjITEM_CHECKBYTE, "Tendon 1", 2, vopt.tendongroup+1, ""}, - {mjITEM_CHECKBYTE, "Tendon 2", 2, vopt.tendongroup+2, ""}, - {mjITEM_CHECKBYTE, "Tendon 3", 2, vopt.tendongroup+3, ""}, - {mjITEM_CHECKBYTE, "Tendon 4", 2, vopt.tendongroup+4, ""}, - {mjITEM_CHECKBYTE, "Tendon 5", 2, vopt.tendongroup+5, ""}, + {mjITEM_CHECKBYTE, "Tendon 0", 2, sim->opt.tendongroup, ""}, + {mjITEM_CHECKBYTE, "Tendon 1", 2, sim->opt.tendongroup+1, ""}, + {mjITEM_CHECKBYTE, "Tendon 2", 2, sim->opt.tendongroup+2, ""}, + {mjITEM_CHECKBYTE, "Tendon 3", 2, sim->opt.tendongroup+3, ""}, + {mjITEM_CHECKBYTE, "Tendon 4", 2, sim->opt.tendongroup+4, ""}, + {mjITEM_CHECKBYTE, "Tendon 5", 2, sim->opt.tendongroup+5, ""}, {mjITEM_SEPARATOR, "Actuator groups", 1}, - {mjITEM_CHECKBYTE, "Actuator 0", 2, vopt.actuatorgroup, ""}, - {mjITEM_CHECKBYTE, "Actuator 1", 2, vopt.actuatorgroup+1, ""}, - {mjITEM_CHECKBYTE, "Actuator 2", 2, vopt.actuatorgroup+2, ""}, - {mjITEM_CHECKBYTE, "Actuator 3", 2, vopt.actuatorgroup+3, ""}, - {mjITEM_CHECKBYTE, "Actuator 4", 2, vopt.actuatorgroup+4, ""}, - {mjITEM_CHECKBYTE, "Actuator 5", 2, vopt.actuatorgroup+5, ""}, + {mjITEM_CHECKBYTE, "Actuator 0", 2, sim->opt.actuatorgroup, ""}, + {mjITEM_CHECKBYTE, "Actuator 1", 2, sim->opt.actuatorgroup+1, ""}, + {mjITEM_CHECKBYTE, "Actuator 2", 2, sim->opt.actuatorgroup+2, ""}, + {mjITEM_CHECKBYTE, "Actuator 3", 2, sim->opt.actuatorgroup+3, ""}, + {mjITEM_CHECKBYTE, "Actuator 4", 2, sim->opt.actuatorgroup+4, ""}, + {mjITEM_CHECKBYTE, "Actuator 5", 2, sim->opt.actuatorgroup+5, ""}, {mjITEM_SEPARATOR, "Skin groups", 1}, - {mjITEM_CHECKBYTE, "Skin 0", 2, vopt.skingroup, ""}, - {mjITEM_CHECKBYTE, "Skin 1", 2, vopt.skingroup+1, ""}, - {mjITEM_CHECKBYTE, "Skin 2", 2, vopt.skingroup+2, ""}, - {mjITEM_CHECKBYTE, "Skin 3", 2, vopt.skingroup+3, ""}, - {mjITEM_CHECKBYTE, "Skin 4", 2, vopt.skingroup+4, ""}, - {mjITEM_CHECKBYTE, "Skin 5", 2, vopt.skingroup+5, ""}, + {mjITEM_CHECKBYTE, "Skin 0", 2, sim->opt.skingroup, ""}, + {mjITEM_CHECKBYTE, "Skin 1", 2, sim->opt.skingroup+1, ""}, + {mjITEM_CHECKBYTE, "Skin 2", 2, sim->opt.skingroup+2, ""}, + {mjITEM_CHECKBYTE, "Skin 3", 2, sim->opt.skingroup+3, ""}, + {mjITEM_CHECKBYTE, "Skin 4", 2, sim->opt.skingroup+4, ""}, + {mjITEM_CHECKBYTE, "Skin 5", 2, sim->opt.skingroup+5, ""}, {mjITEM_END} }; @@ -784,7 +831,6 @@ void MakeGroupSection(mj::Simulate* sim, int oldstate) { // make joint section of UI void MakeJointSection(mj::Simulate* sim, int oldstate) { - mjuiDef defJoint[] = { {mjITEM_SECTION, "Joint", oldstate, nullptr, "AJ"}, {mjITEM_END} @@ -800,26 +846,30 @@ void MakeJointSection(mj::Simulate* sim, int oldstate) { // add scalar joints, exit if UI limit reached int itemcnt = 0; - for (int i=0; im->njnt && itemcntm->jnt_type[i]==mjJNT_HINGE || sim->m->jnt_type[i]==mjJNT_SLIDE)) { + for (int i=0; i < sim->jnt_type_.size() && itemcntjnt_type_[i]==mjJNT_HINGE || sim->jnt_type_[i]==mjJNT_SLIDE)) { // skip if joint group is disabled - if (!sim->opt.jointgroup[mjMAX(0, mjMIN(mjNGROUP-1, sim->m->jnt_group[i]))]) { + if (!sim->opt.jointgroup[mjMAX(0, mjMIN(mjNGROUP-1, sim->jnt_group_[i]))]) { continue; } // set data and name - defSlider[0].pdata = sim->d->qpos + sim->m->jnt_qposadr[i]; - if (sim->m->names[sim->m->name_jntadr[i]]) { - mju::strcpy_arr(defSlider[0].name, sim->m->names+sim->m->name_jntadr[i]); + if (sim->fully_managed_) { + defSlider[0].pdata = &sim->d_->qpos[sim->m_->jnt_qposadr[i]]; + } else { + defSlider[0].pdata = &sim->qpos_[sim->jnt_qposadr_[i]]; + } + if (!sim->jnt_names_[i].empty()) { + mju::strcpy_arr(defSlider[0].name, sim->jnt_names_[i].c_str()); } else { mju::sprintf_arr(defSlider[0].name, "joint %d", i); } // set range - if (sim->m->jnt_limited[i]) + if (sim->jnt_range_[i].has_value()) mju::sprintf_arr(defSlider[0].other, "%.4g %.4g", - sim->m->jnt_range[2*i], sim->m->jnt_range[2*i+1]); - else if (sim->m->jnt_type[i]==mjJNT_SLIDE) { + sim->jnt_range_[i]->first, sim->jnt_range_[i]->second); + else if (sim->jnt_type_[i]==mjJNT_SLIDE) { mju::strcpy_arr(defSlider[0].other, "-1 1"); } else { mju::strcpy_arr(defSlider[0].other, "-3.1416 3.1416"); @@ -849,24 +899,28 @@ void MakeControlSection(mj::Simulate* sim, int oldstate) { // add controls, exit if UI limit reached (Clear button already added) int itemcnt = 1; - for (int i=0; im->nu && itemcntactuator_ctrlrange_.size() && itemcntopt.actuatorgroup[mjMAX(0, mjMIN(mjNGROUP-1, sim->m->actuator_group[i]))]) { + if (!sim->opt.actuatorgroup[mjMAX(0, mjMIN(mjNGROUP-1, sim->actuator_group_[i]))]) { continue; } // set data and name - defSlider[0].pdata = sim->d->ctrl + i; - if (sim->m->names[sim->m->name_actuatoradr[i]]) { - mju::strcpy_arr(defSlider[0].name, sim->m->names+sim->m->name_actuatoradr[i]); + if (sim->fully_managed_) { + defSlider[0].pdata = &sim->d_->ctrl[i]; + } else { + defSlider[0].pdata = &sim->ctrl_[i]; + } + if (!sim->actuator_names_[i].empty()) { + mju::strcpy_arr(defSlider[0].name, sim->actuator_names_[i].c_str()); } else { mju::sprintf_arr(defSlider[0].name, "control %d", i); } // set range - if (sim->m->actuator_ctrllimited[i]) + if (sim->actuator_ctrlrange_[i].has_value()) mju::sprintf_arr(defSlider[0].other, "%.4g %.4g", - sim->m->actuator_ctrlrange[2*i], sim->m->actuator_ctrlrange[2*i+1]); + sim->actuator_ctrlrange_[i]->first, sim->actuator_ctrlrange_[i]->second); else { mju::strcpy_arr(defSlider[0].other, "-1 1"); } @@ -878,7 +932,7 @@ void MakeControlSection(mj::Simulate* sim, int oldstate) { } // make model-dependent UI sections -void MakeUiSections(mj::Simulate* sim) { +void MakeUiSections(mj::Simulate* sim, const mjModel* m, const mjData* d) { // get section open-close state, UI 0 int oldstate0[NSECT0]; for (int i=0; im, &sim->cam); + mjv_defaultFreeCamera(m, &sim->cam); } // copy qpos to clipboard as key -void CopyKey(mj::Simulate* sim) { +void CopyPose(mj::Simulate* sim, const mjModel* m, const mjData* d) { char clipboard[5000] = ""); @@ -940,7 +994,7 @@ mjtNum Timer() { } // clear all times -void ClearTimeres(mjData* d) { +void ClearTimers(mjData* d) { for (int i=0; itimer[i].duration = 0; d->timer[i].number = 0; @@ -975,13 +1029,13 @@ void CopyCamera(mj::Simulate* sim) { } // update UI 0 when MuJoCo structures change (except for joint sliders) -void UpdateSettings(mj::Simulate* sim) { +void UpdateSettings(mj::Simulate* sim, const mjModel* m) { // physics flags for (int i=0; idisable[i] = ((sim->m->opt.disableflags & (1<disable[i] = ((m->opt.disableflags & (1<enable[i] = ((sim->m->opt.enableflags & (1<enable[i] = ((m->opt.enableflags & (1<ui0, &sim->uistate, &sim->platform_ui->mjr_context()); + sim->pending_.full_ui_update = true; } // Compute suitable font scale. @@ -1031,13 +1085,16 @@ int UiPredicate(int category, void* userdata) { switch (category) { case 2: // require model - return (sim->m != nullptr); + return sim->m_ || !sim->fully_managed_; case 3: // require model and nkey - return (sim->m && sim->m->nkey); + return sim->fully_managed_ && sim->nkey_; case 4: // require model and paused - return (sim->m && !sim->run); + return sim->m_ && !sim->run; + + case 5: // require model and fully managed mode + return sim->fully_managed_ && sim->m_; default: return 1; @@ -1081,9 +1138,6 @@ void UiModify(mjUI* ui, mjuiState* state, mjrContext* con) { // handle UI event void UiEvent(mjuiState* state) { mj::Simulate* sim = static_cast(state->userdata); - mjModel* m = sim->m; - mjData* d = sim->d; - char err[200]; // call UI 0 if event is directed to it if ((state->dragrect==sim->ui0.rectid) || @@ -1096,29 +1150,19 @@ void UiEvent(mjuiState* state) { if (it && it->sectionid==SECT_FILE) { switch (it->itemid) { case 0: // Save xml - { - const std::string path = GetSavePath("mjmodel.xml"); - if (!path.empty() && !mj_saveLastXML(path.c_str(), m, err, 200)) { - std::printf("Save XML error: %s", err); - } - } + sim->pending_.save_xml = GetSavePath("mjmodel.xml"); break; case 1: // Save mjb - { - const std::string path = GetSavePath("mjmodel.mjb"); - if (!path.empty()) { - mj_saveModel(m, path.c_str(), nullptr, 0); - } - } + sim->pending_.save_mjb = GetSavePath("mjmodel.mjb"); break; case 2: // Print model - mj_printModel(m, "MJMODEL.TXT"); + sim->pending_.print_model = GetSavePath("MJMODEL.TXT"); break; case 3: // Print data - mj_printData(m, d, "MJDATA.TXT"); + sim->pending_.print_data = GetSavePath("MJDATA.TXT"); break; case 4: // Quit @@ -1166,13 +1210,7 @@ void UiEvent(mjuiState* state) { else if (it && it->sectionid==SECT_SIMULATION) { switch (it->itemid) { case 1: // Reset - if (m) { - mj_resetData(m, d); - mj_forward(m, d); - UpdateProfiler(sim); - UpdateSensor(sim); - UpdateSettings(sim); - } + sim->pending_.reset = true; break; case 2: // Reload @@ -1180,60 +1218,40 @@ void UiEvent(mjuiState* state) { break; case 3: // Align - AlignAndScaleView(sim); - UpdateSettings(sim); + sim->pending_.align = true; break; case 4: // Copy pose - CopyKey(sim); + sim->pending_.copy_pose = true; break; case 5: // Adjust key case 6: // Load key - { - int i = sim->key; - d->time = m->key_time[i]; - mju_copy(d->qpos, m->key_qpos + i * m->nq, m->nq); - mju_copy(d->qvel, m->key_qvel + i * m->nv, m->nv); - mju_copy(d->act, m->key_act + i * m->na, m->na); - mju_copy(d->mocap_pos, m->key_mpos + i * 3 * m->nmocap, 3 * m->nmocap); - mju_copy(d->mocap_quat, m->key_mquat + i * 4 * m->nmocap, - 4 * m->nmocap); - mju_copy(d->ctrl, m->key_ctrl + i * m->nu, m->nu); - mj_forward(m, d); - UpdateProfiler(sim); - UpdateSensor(sim); - UpdateSettings(sim); - } break; + sim->pending_.load_key = true; + break; case 7: // Save key - { - int i = sim->key; - m->key_time[i] = d->time; - mju_copy(m->key_qpos+i*m->nq, d->qpos, m->nq); - mju_copy(m->key_qvel+i*m->nv, d->qvel, m->nv); - mju_copy(m->key_act+i*m->na, d->act, m->na); - mju_copy(m->key_mpos+i*3*m->nmocap, d->mocap_pos, 3*m->nmocap); - mju_copy(m->key_mquat+i*4*m->nmocap, d->mocap_quat, 4*m->nmocap); - mju_copy(m->key_ctrl+i*m->nu, d->ctrl, m->nu); - } break; + sim->pending_.save_key = true; + break; } } // physics section - else if (it && it->sectionid==SECT_PHYSICS) { + else if (it && it->sectionid==SECT_PHYSICS && sim->m_) { + mjOption* opt = sim->fully_managed_ ? &sim->m_->opt : &sim->scnstate_.model.opt; + // update disable flags in mjOption - m->opt.disableflags = 0; + opt->disableflags = 0; for (int i=0; idisable[i]) { - m->opt.disableflags |= (1<disableflags |= (1<opt.enableflags = 0; + opt->enableflags = 0; for (int i=0; ienable[i]) { - m->opt.enableflags |= (1<enableflags |= (1<sectionid==SECT_CONTROL) { // clear controls if (it->itemid==0) { - mju_zero(d->ctrl, m->nu); - mjui_update(SECT_CONTROL, -1, &sim->ui1, &sim->uistate, &sim->platform_ui->mjr_context()); + sim->pending_.zero_ctrl = true; } } @@ -1314,7 +1331,7 @@ void UiEvent(mjuiState* state) { if (state->type==mjEVENT_KEY && state->key!=0) { switch (state->key) { case ' ': // Mode - if (m) { + if (sim->fully_managed_ && sim->m_) { sim->run = 1 - sim->run; sim->pert.active = 0; mjui_update(-1, -1, &sim->ui0, state, &sim->platform_ui->mjr_context()); @@ -1322,18 +1339,18 @@ void UiEvent(mjuiState* state) { break; case mjKEY_RIGHT: // step forward - if (m && !sim->run) { - ClearTimeres(d); - mj_step(m, d); - UpdateProfiler(sim); - UpdateSensor(sim); - UpdateSettings(sim); + if (sim->fully_managed_ && sim->m_ && !sim->run) { + ClearTimers(sim->d_); + mj_step(sim->m_, sim->d_); + UpdateProfiler(sim, sim->m_, sim->d_); + UpdateSensor(sim, sim->m_, sim->d_); + UpdateSettings(sim, sim->m_); } break; case mjKEY_PAGE_UP: // select parent body - if (m && sim->pert.select>0) { - sim->pert.select = m->body_parentid[sim->pert.select]; + if ((sim->m_ || !sim->fully_managed_) && sim->pert.select > 0) { + sim->pert.select = sim->body_parentid_[sim->pert.select]; sim->pert.skinselect = -1; // stop perturbation if world reached @@ -1345,10 +1362,10 @@ void UiEvent(mjuiState* state) { break; case ']': // cycle up fixed cameras - if (m && m->ncam) { + if ((sim->m_ || !sim->fully_managed_) && sim->ncam_) { sim->cam.type = mjCAMERA_FIXED; - // simulate->camera = {0 or 1} are reserved for the free and tracking cameras - if (sim->camera < 2 || sim->camera == 2 + m->ncam-1) { + // camera = {0 or 1} are reserved for the free and tracking cameras + if (sim->camera < 2 || sim->camera == 2 + sim->ncam_ - 1) { sim->camera = 2; } else { sim->camera += 1; @@ -1359,11 +1376,11 @@ void UiEvent(mjuiState* state) { break; case '[': // cycle down fixed cameras - if (m && m->ncam) { + if ((sim->m_ || !sim->fully_managed_) && sim->ncam_) { sim->cam.type = mjCAMERA_FIXED; - // settings.camera = {0 or 1} are reserved for the free and tracking cameras + // camera = {0 or 1} are reserved for the free and tracking cameras if (sim->camera <= 2) { - sim->camera = 2 + m->ncam-1; + sim->camera = 2 + sim->ncam_-1; } else { sim->camera -= 1; } @@ -1373,14 +1390,14 @@ void UiEvent(mjuiState* state) { break; case mjKEY_F6: // cycle frame visualisation - if (m) { + if (sim->m_ || !sim->fully_managed_) { sim->opt.frame = (sim->opt.frame + 1) % mjNFRAME; mjui_update(SECT_RENDERING, -1, &sim->ui0, &sim->uistate, &sim->platform_ui->mjr_context()); } break; case mjKEY_F7: // cycle label visualisation - if (m) { + if (sim->m_ || !sim->fully_managed_) { sim->opt.label = (sim->opt.label + 1) % mjNLABEL; mjui_update(SECT_RENDERING, -1, &sim->ui0, &sim->uistate, &sim->platform_ui->mjr_context()); } @@ -1393,7 +1410,7 @@ void UiEvent(mjuiState* state) { break; case '-': // slow down - { + if (sim->fully_managed_) { int numclicks = sizeof(sim->percentRealTime) / sizeof(sim->percentRealTime[0]); if (sim->real_time_index < numclicks-1 && !state->shift) { sim->real_time_index++; @@ -1403,7 +1420,7 @@ void UiEvent(mjuiState* state) { break; case '=': // speed up - if (sim->real_time_index > 0 && !state->shift) { + if (sim->fully_managed_ && sim->real_time_index > 0 && !state->shift) { sim->real_time_index--; sim->speed_changed = true; } @@ -1414,109 +1431,56 @@ void UiEvent(mjuiState* state) { } // 3D scroll - if (state->type==mjEVENT_SCROLL && state->mouserect==3 && m) { + if (state->type==mjEVENT_SCROLL && state->mouserect==3) { // emulate vertical mouse motion = 2% of window height - mjv_moveCamera(m, mjMOUSE_ZOOM, 0, -zoom_increment*state->sy, &sim->scn, &sim->cam); - + if (sim->fully_managed_) { + mjv_moveCamera(sim->m_, mjMOUSE_ZOOM, 0, -zoom_increment*state->sy, &sim->scn, &sim->cam); + } else { + mjv_moveCameraFromState( + &sim->scnstate_, mjMOUSE_ZOOM, 0, -zoom_increment*state->sy, &sim->scn, &sim->cam); + } return; } // 3D press - if (state->type==mjEVENT_PRESS && state->mouserect==3 && m) { + if (state->type==mjEVENT_PRESS && state->mouserect==3) { // set perturbation int newperturb = 0; - if (state->control && sim->pert.select>0) { + if (state->control && sim->pert.select>0 && (sim->m_ || !sim->fully_managed_)) { // right: translate; left: rotate if (state->right) { newperturb = mjPERT_TRANSLATE; } else if (state->left) { newperturb = mjPERT_ROTATE; } - - // perturbation onset: reset reference if (newperturb && !sim->pert.active) { - mjv_initPerturb(m, d, &sim->scn, &sim->pert); + sim->pending_.newperturb = newperturb; } } - sim->pert.active = newperturb; // handle double-click - if (state->doubleclick) { - // determine selection mode - int selmode; - if (state->button==mjBUTTON_LEFT) { - selmode = 1; - } else if (state->control) { - selmode = 3; - } else { - selmode = 2; - } - - // find geom and 3D click point, get corresponding body - mjrRect r = state->rect[3]; - mjtNum selpnt[3]; - int selgeom, selskin; - int selbody = mjv_select(m, d, &sim->opt, - static_cast(r.width)/r.height, - (state->x - r.left)/r.width, - (state->y - r.bottom)/r.height, - &sim->scn, selpnt, &selgeom, &selskin); - - // set lookat point, start tracking is requested - if (selmode==2 || selmode==3) { - // copy selpnt if anything clicked - if (selbody>=0) { - mju_copy3(sim->cam.lookat, selpnt); - } - - // switch to tracking camera if dynamic body clicked - if (selmode==3 && selbody>0) { - // mujoco camera - sim->cam.type = mjCAMERA_TRACKING; - sim->cam.trackbodyid = selbody; - sim->cam.fixedcamid = -1; - - // UI camera - sim->camera = 1; - mjui_update(SECT_RENDERING, -1, &sim->ui0, &sim->uistate, - &sim->platform_ui->mjr_context()); - } - } - - // set body selection - else { - if (selbody>=0) { - // record selection - sim->pert.select = selbody; - sim->pert.skinselect = selskin; - - // compute localpos - mjtNum tmp[3]; - mju_sub3(tmp, selpnt, d->xpos+3*sim->pert.select); - mju_mulMatTVec(sim->pert.localpos, d->xmat+9*sim->pert.select, tmp, 3, 3); - } else { - sim->pert.select = 0; - sim->pert.skinselect = -1; - } - } + if (state->doubleclick && (sim->m_ || !sim->fully_managed_)) { + sim->pending_.select = true; + std::memcpy(&sim->pending_.select_state, state, sizeof(sim->pending_.select_state)); // stop perturbation on select sim->pert.active = 0; + sim->pending_.newperturb = 0; } return; } // 3D release - if (state->type==mjEVENT_RELEASE && state->dragrect==3 && m) { + if (state->type==mjEVENT_RELEASE && state->dragrect==3 && (sim->m_ || !sim->fully_managed_)) { // stop perturbation sim->pert.active = 0; - + sim->pending_.newperturb = 0; return; } // 3D move - if (state->type==mjEVENT_MOVE && state->dragrect==3 && m) { + if (state->type==mjEVENT_MOVE && state->dragrect==3 && (sim->m_ || !sim->fully_managed_)) { // determine action based on mouse button mjtMouse action; if (state->right) { @@ -1529,18 +1493,32 @@ void UiEvent(mjuiState* state) { // move perturb or camera mjrRect r = state->rect[3]; - if (sim->pert.active) - mjv_movePerturb(m, d, action, state->dx/r.height, -state->dy/r.height, - &sim->scn, &sim->pert); - else - mjv_moveCamera(m, action, state->dx/r.height, -state->dy/r.height, - &sim->scn, &sim->cam); - + if (sim->pert.active) { + if (sim->fully_managed_) { + mjv_movePerturb( + sim->m_, sim->d_, action, state->dx / r.height, -state->dy / r.height, + &sim->scn, &sim->pert); + } else { + mjv_movePerturbFromState( + &sim->scnstate_, action, state->dx / r.height, -state->dy / r.height, + &sim->scn, &sim->pert); + } + } else { + if (sim->fully_managed_) { + mjv_moveCamera( + sim->m_, action, state->dx / r.height, -state->dy / r.height, + &sim->scn, &sim->cam); + } else { + mjv_moveCameraFromState( + &sim->scnstate_, action, state->dx / r.height, -state->dy / r.height, + &sim->scn, &sim->cam); + } + } return; } // Dropped files - if (state->type == mjEVENT_FILESDROP && state->dropcount > 0) { + if (state->type == mjEVENT_FILESDROP && state->dropcount > 0 && sim->fully_managed_) { while (sim->droploadrequest.load()) {} mju::strcpy_arr(sim->dropfilename, state->droppaths[0]); sim->droploadrequest.store(true); @@ -1558,21 +1536,300 @@ void UiEvent(mjuiState* state) { namespace mujoco { namespace mju = ::mujoco::sample_util; -Simulate::Simulate(std::unique_ptr platform_ui) - : platform_ui(std::move(platform_ui)), - uistate(this->platform_ui->state()) {} - -//------------------------------------ apply pose perturbations ------------------------------------ -void Simulate::ApplyPosePerturbations(int flg_paused) { - if (this->m != nullptr) { - mjv_applyPerturbPose(this->m, this->d, &this->pert, flg_paused); // move mocap bodies only - } +Simulate::Simulate(std::unique_ptr platform_ui, + mjvScene* scn, mjvCamera* cam, + mjvOption* opt, mjvPerturb* pert, + bool fully_managed) + : fully_managed_(fully_managed), + scn(*scn), + cam(*cam), + opt(*opt), + pert(*pert), + platform_ui(std::move(platform_ui)), + uistate(this->platform_ui->state()) { + mjv_defaultSceneState(&scnstate_); } -//----------------------------------- apply force perturbations ------------------------------------ -void Simulate::ApplyForcePerturbations() { - if (this->m != nullptr) { - mjv_applyPerturbForce(this->m, this->d, &this->pert); +void Simulate::Sync() { + if (!m_) { + return; + } + + bool update_profiler = this->profiler && (this->run || !this->m_); + bool update_sensor = this->sensor && (this->run || !this->m_); + bool update_settings = false; + + for (int i = 0; i < m_->njnt; ++i) { + std::optional> range; + if (m_->jnt_limited[i]) { + range.emplace(m_->jnt_range[2*i], m_->jnt_range[2*i + 1]); + } + if (jnt_range_[i] != range) { + pending_.ui_update_joint = true; + jnt_range_[i].swap(range); + } + } + + for (int i = 0; i < m_->nu; ++i) { + std::optional> range; + if (m_->actuator_ctrllimited[i]) { + range.emplace(m_->actuator_ctrlrange[2*i], m_->actuator_ctrlrange[2*i + 1]); + } + if (actuator_ctrlrange_[i] != range) { + pending_.ui_update_ctrl = true; + actuator_ctrlrange_[i].swap(range); + } + } + + for (int i = 0; i < m_->nq; ++i) { + if (qpos_[i] != qpos_prev_[i]) { + d_->qpos[i] = qpos_[i]; + } else { + qpos_[i] = d_->qpos[i]; + } + if (qpos_prev_[i] != qpos_[i]) { + pending_.ui_update_joint = true; + qpos_prev_[i] = qpos_[i]; + } + } + + for (int i = 0; i < m_->nu; ++i) { + if (ctrl_[i] != ctrl_prev_[i]) { + d_->ctrl[i] = ctrl_[i]; + } else { + ctrl_[i] = d_->ctrl[i]; + } + if (ctrl_prev_[i] != ctrl_[i]) { + pending_.ui_update_ctrl = true; + ctrl_prev_[i] = ctrl_[i]; + } + } + + if (!fully_managed_) { + // synchronize m_->opt with changes made via the UI + #define X(name) \ + if (Differ(scnstate_.model.opt.name, mjopt_prev_.name)) { \ + pending_.ui_update_physics = true; \ + Copy(m_->opt.name, scnstate_.model.opt.name); \ + } + + X(timestep); + X(apirate); + X(impratio); + X(tolerance); + X(noslip_tolerance); + X(mpr_tolerance); + X(gravity); + X(wind); + X(magnetic); + X(density); + X(viscosity); + X(o_margin); + X(o_solref); + X(o_solimp); + X(integrator); + X(collision); + X(cone); + X(jacobian); + X(solver); + X(iterations); + X(noslip_iterations); + X(mpr_iterations); + X(disableflags); + X(enableflags); + + #undef X + + // synchronize number of mjWARN_VGEOMFULL warnings + if (scnstate_.data.warning[mjWARN_VGEOMFULL].number > warn_vgeomfull_prev_) { + d_->warning[mjWARN_VGEOMFULL].number += + scnstate_.data.warning[mjWARN_VGEOMFULL].number - warn_vgeomfull_prev_; + } + } + + if (pending_.save_xml) { + char err[200]; + if (!pending_.save_xml->empty() && !mj_saveLastXML(pending_.save_xml->c_str(), m_, err, 200)) { + std::printf("Save XML error: %s", err); + } + pending_.save_xml = std::nullopt; + } + + if (pending_.save_mjb) { + if (!pending_.save_mjb->empty()) { + mj_saveModel(m_, pending_.save_mjb->c_str(), nullptr, 0); + } + pending_.save_mjb = std::nullopt; + } + + if (pending_.print_model) { + if (!pending_.print_model->empty()) { + mj_printModel(m_, pending_.print_model->c_str()); + } + pending_.print_model = std::nullopt; + } + + if (pending_.print_data) { + if (!pending_.print_data->empty()) { + mj_printData(m_, d_, pending_.print_data->c_str()); + } + pending_.print_data = std::nullopt; + } + + if (pending_.reset) { + mj_resetData(m_, d_); + mj_forward(m_, d_); + update_profiler = true; + update_sensor = true; + update_settings = true; + pending_.reset = false; + } + + if (pending_.align) { + AlignAndScaleView(this, m_); + update_settings = true; + pending_.align = false; + } + + if (pending_.copy_pose) { + CopyPose(this, m_, d_); + pending_.copy_pose = false; + } + + if (pending_.load_key) { + int i = this->key; + d_->time = m_->key_time[i]; + mju_copy(d_->qpos, m_->key_qpos + i*m_->nq, m_->nq); + mju_copy(d_->qvel, m_->key_qvel + i*m_->nv, m_->nv); + mju_copy(d_->act, m_->key_act + i*m_->na, m_->na); + mju_copy(d_->mocap_pos, m_->key_mpos + i*3*m_->nmocap, 3*m_->nmocap); + mju_copy(d_->mocap_quat, m_->key_mquat + i*4*m_->nmocap, + 4*m_->nmocap); + mju_copy(d_->ctrl, m_->key_ctrl + i*m_->nu, m_->nu); + mj_forward(m_, d_); + update_profiler = true; + update_sensor = true; + update_settings = true; + pending_.load_key = false; + } + + if (pending_.save_key) { + int i = this->key; + m_->key_time[i] = d_->time; + mju_copy(m_->key_qpos + i*m_->nq, d_->qpos, m_->nq); + mju_copy(m_->key_qvel + i*m_->nv, d_->qvel, m_->nv); + mju_copy(m_->key_act + i*m_->na, d_->act, m_->na); + mju_copy(m_->key_mpos + i*3*m_->nmocap, d_->mocap_pos, 3*m_->nmocap); + mju_copy(m_->key_mquat + i*4*m_->nmocap, d_->mocap_quat, 4*m_->nmocap); + mju_copy(m_->key_ctrl + i*m_->nu, d_->ctrl, m_->nu); + pending_.save_key = false; + } + + if (pending_.zero_ctrl) { + mju_zero(d_->ctrl, m_->nu); + pending_.zero_ctrl = false; + } + + // perturbation onset: reset reference + if (pending_.newperturb) { + mjv_initPerturb(m_, d_, &this->scn, &this->pert); + this->pert.active = pending_.newperturb; + pending_.newperturb = 0; + } + + if (pending_.select) { + // determine selection mode + int selmode; + if (pending_.select_state.button==mjBUTTON_LEFT) { + selmode = 1; + } else if (pending_.select_state.control) { + selmode = 3; + } else { + selmode = 2; + } + + // find geom and 3D click point, get corresponding body + mjrRect r = pending_.select_state.rect[3]; + mjtNum selpnt[3]; + int selgeom, selskin; + int selbody = mjv_select(m_, d_, &this->opt, + static_cast(r.width) / r.height, + (pending_.select_state.x - r.left) / r.width, + (pending_.select_state.y - r.bottom) / r.height, + &this->scn, selpnt, &selgeom, &selskin); + + // set lookat point, start tracking is requested + if (selmode==2 || selmode==3) { + // copy selpnt if anything clicked + if (selbody>=0) { + mju_copy3(this->cam.lookat, selpnt); + } + + // switch to tracking camera if dynamic body clicked + if (selmode==3 && selbody>0) { + // mujoco camera + this->cam.type = mjCAMERA_TRACKING; + this->cam.trackbodyid = selbody; + this->cam.fixedcamid = -1; + + // UI camera + this->camera = 1; + mjui_update(SECT_RENDERING, -1, &this->ui0, &pending_.select_state, + &this->platform_ui->mjr_context()); + } + } + + // set body selection + else { + if (selbody>=0) { + // record selection + this->pert.select = selbody; + this->pert.skinselect = selskin; + + // compute localpos + mjtNum tmp[3]; + mju_sub3(tmp, selpnt, d_->xpos + 3*this->pert.select); + mju_mulMatTVec(this->pert.localpos, d_->xmat + 9*this->pert.select, tmp, 3, 3); + } else { + this->pert.select = 0; + this->pert.skinselect = -1; + } + } + pending_.select = false; + } + + // update scene + if (fully_managed_) { + mjv_updateScene(m_, d_, &this->opt, &this->pert, &this->cam, mjCAT_ALL, &this->scn); + } else { + mjv_updateSceneState(m_, d_, &scnstate_); + mjopt_prev_ = scnstate_.model.opt; + warn_vgeomfull_prev_ = scnstate_.data.warning[mjWARN_VGEOMFULL].number; + } + + // update watch + if (this->ui0_enable && this->ui0.sect[SECT_WATCH].state) { + UpdateWatch(this, m_, d_); + } + + // update info text + if (this->info) { + UpdateInfoText(this, m_, d_, this->info_title, this->info_content); + } + if (update_profiler) { UpdateProfiler(this, m_, d_); } + if (update_sensor) { UpdateSensor(this, m_, d_); } + if (update_settings) { UpdateSettings(this, m_); } + + // clear timers once profiler info has been copied + ClearTimers(d_); + + if (this->run || !this->fully_managed_) { + // clear old perturbations, apply new + mju_zero(d_->xfrc_applied, 6*m_->nbody); + mjv_applyPerturbPose(m_, d_, &this->pert, 0); // mocap bodies only + mjv_applyPerturbForce(m_, d_, &this->pert); + } else { + mjv_applyPerturbPose(m_, d_, &this->pert, 1); // mocap and dynamic bodies } } @@ -1580,12 +1837,12 @@ void Simulate::ApplyForcePerturbations() { void Simulate::Load(mjModel* m, mjData* d, const char* displayed_filename) { - this->mnew = m; - this->dnew = d; + this->mnew_ = m; + this->dnew_ = d; mju::strcpy_arr(this->filename, displayed_filename); { - std::unique_lock lock(mtx); + MutexLock lock(mtx); this->loadrequest = 2; // Wait for the render thread to be done loading @@ -1597,12 +1854,87 @@ void Simulate::Load(mjModel* m, //------------------------------------- load mjb or xml model -------------------------------------- void Simulate::LoadOnRenderThread() { - this->m = this->mnew; - this->d = this->dnew; + this->m_ = this->mnew_; + this->d_ = this->dnew_; + + ncam_ = this->mnew_->ncam; + nkey_ = this->mnew_->nkey; + body_parentid_.resize(this->mnew_->nbody); + std::memcpy(body_parentid_.data(), this->mnew_->body_parentid, + sizeof(this->mnew_->body_parentid[0]) * this->mnew_->nbody); + + jnt_type_.resize(this->mnew_->njnt); + std::memcpy(jnt_type_.data(), this->mnew_->jnt_type, + sizeof(this->mnew_->jnt_type[0]) * this->mnew_->njnt); + + jnt_group_.resize(this->mnew_->njnt); + std::memcpy(jnt_group_.data(), this->mnew_->jnt_group, + sizeof(this->mnew_->jnt_group[0]) * this->mnew_->njnt); + + jnt_qposadr_.resize(this->mnew_->njnt); + std::memcpy(jnt_qposadr_.data(), this->mnew_->jnt_qposadr, + sizeof(this->mnew_->jnt_qposadr[0]) * this->mnew_->njnt); + + jnt_range_.clear(); + jnt_range_.reserve(this->mnew_->njnt); + for (int i = 0; i < this->mnew_->njnt; ++i) { + if (this->mnew_->jnt_limited[i]) { + jnt_range_.push_back( + std::make_pair(this->mnew_->jnt_range[2 * i], this->mnew_->jnt_range[2 * i + 1])); + } else { + jnt_range_.push_back(std::nullopt); + } + } + + jnt_names_.clear(); + jnt_names_.reserve(this->mnew_->njnt); + for (int i = 0; i < this->mnew_->njnt; ++i) { + jnt_names_.emplace_back(this->mnew_->names + this->mnew_->name_jntadr[i]); + } + + actuator_group_.resize(this->mnew_->nu); + std::memcpy(actuator_group_.data(), this->mnew_->actuator_group, + sizeof(this->mnew_->actuator_group[0]) * this->mnew_->nu); + + actuator_ctrlrange_.clear(); + actuator_ctrlrange_.reserve(this->mnew_->nu); + for (int i = 0; i < this->mnew_->nu; ++i) { + if (this->mnew_->actuator_ctrllimited[i]) { + actuator_ctrlrange_.push_back(std::make_pair( + this->mnew_->actuator_ctrlrange[2 * i], this->mnew_->actuator_ctrlrange[2 * i + 1])); + } else { + actuator_ctrlrange_.push_back(std::nullopt); + } + } + + actuator_names_.clear(); + actuator_names_.reserve(this->mnew_->nu); + for (int i = 0; i < this->mnew_->nu; ++i) { + actuator_names_.emplace_back(this->mnew_->names + this->mnew_->name_actuatoradr[i]); + } + + qpos_.resize(this->mnew_->nq); + std::memcpy(qpos_.data(), this->dnew_->qpos, sizeof(this->dnew_->qpos[0]) * this->mnew_->nq); + qpos_prev_ = qpos_; + + ctrl_.resize(this->mnew_->nu); + std::memcpy(ctrl_.data(), this->dnew_->ctrl, sizeof(this->dnew_->ctrl[0]) * this->mnew_->nu); + ctrl_prev_ = ctrl_; // re-create scene and context - mjv_makeScene(this->m, &this->scn, maxgeom); - this->platform_ui->RefreshMjrContext(this->m, 50*(this->font+1)); + if (this->fully_managed_) { + mjv_makeScene(this->mnew_, &this->scn, kMaxGeom); + } else { + mjopt_prev_ = mnew_->opt; + opt_prev_ = opt; + cam_prev_ = cam; + warn_vgeomfull_prev_ = dnew_->warning[mjWARN_VGEOMFULL].number; + mjv_makeSceneState(this->mnew_, this->dnew_, &this->scnstate_, kMaxGeom); + } + + this->platform_ui->RefreshMjrContext(this->mnew_, 50*(this->font+1)); + UiModify(&this->ui0, &this->uistate, &this->platform_ui->mjr_context()); + UiModify(&this->ui1, &this->uistate, &this->platform_ui->mjr_context()); if (!this->platform_ui->IsGPUAccelerated()) { this->scn.flags[mjRND_SHADOW] = 0; @@ -1617,32 +1949,37 @@ void Simulate::LoadOnRenderThread() { // align and scale view unless reloading the same file if (this->filename[0] && mju::strcmp_arr(this->filename, this->previous_filename)) { - AlignAndScaleView(this); + AlignAndScaleView(this, this->mnew_); mju::strcpy_arr(this->previous_filename, this->filename); } // update scene - mjv_updateScene(this->m, this->d, &this->opt, &this->pert, &this->cam, mjCAT_ALL, &this->scn); + if (fully_managed_) { + mjv_updateScene(this->mnew_, this->dnew_, + &this->opt, &this->pert, &this->cam, mjCAT_ALL, &this->scn); + } else { + mjv_updateSceneState(this->mnew_, this->dnew_, &this->scnstate_); + } // set window title to model name - if (this->m->names) { + if (this->mnew_->names) { char title[200] = "MuJoCo : "; - mju::strcat_arr(title, this->m->names); + mju::strcat_arr(title, this->mnew_->names); platform_ui->SetWindowTitle(title); } // set keyframe range and divisions this->ui0.sect[SECT_SIMULATION].item[5].slider.range[0] = 0; - this->ui0.sect[SECT_SIMULATION].item[5].slider.range[1] = mjMAX(0, this->m->nkey - 1); - this->ui0.sect[SECT_SIMULATION].item[5].slider.divisions = mjMAX(1, this->m->nkey - 1); + this->ui0.sect[SECT_SIMULATION].item[5].slider.range[1] = mjMAX(0, this->mnew_->nkey - 1); + this->ui0.sect[SECT_SIMULATION].item[5].slider.divisions = mjMAX(1, this->mnew_->nkey - 1); // rebuild UI sections - MakeUiSections(this); + MakeUiSections(this, this->mnew_, this->dnew_); // full ui update UiModify(&this->ui0, &this->uistate, &this->platform_ui->mjr_context()); UiModify(&this->ui1, &this->uistate, &this->platform_ui->mjr_context()); - UpdateSettings(this); + UpdateSettings(this, this->mnew_); // clear request this->loadrequest = 0; @@ -1651,7 +1988,7 @@ void Simulate::LoadOnRenderThread() { // set real time index int numclicks = sizeof(this->percentRealTime) / sizeof(this->percentRealTime[0]); float min_error = 1e6; - float desired = mju_log(100*this->m->vis.global.realtime); + float desired = mju_log(100*this->mnew_->vis.global.realtime); for (int click=0; clickpercentRealTime[click]) - desired); if (error < min_error) { @@ -1659,69 +1996,18 @@ void Simulate::LoadOnRenderThread() { this->real_time_index = click; } } + + this->mnew_ = nullptr; + this->dnew_ = nullptr; } //------------------------------------------- rendering -------------------------------------------- - -// prepare to render -void Simulate::PrepareScene() { - // data for FPS calculation - static std::chrono::time_point lastupdatetm; - - // update interval, save update time - auto tmnow = Clock::now(); - double interval = Seconds(tmnow - lastupdatetm).count(); - interval = mjMIN(1, mjMAX(0.0001, interval)); - lastupdatetm = tmnow; - - // no model: nothing to do - if (!this->m) { - return; - } - - // update scene - mjv_updateScene(this->m, this->d, &this->opt, &this->pert, &this->cam, mjCAT_ALL, &this->scn); - - // update watch - if (this->ui0_enable && this->ui0.sect[SECT_WATCH].state) { - UpdateWatch(this); - mjui_update(SECT_WATCH, -1, &this->ui0, &this->uistate, &this->platform_ui->mjr_context()); - } - - // update joint - if (this->ui1_enable && this->ui1.sect[SECT_JOINT].state) { - mjui_update(SECT_JOINT, -1, &this->ui1, &this->uistate, &this->platform_ui->mjr_context()); - } - - // update info text - if (this->info) { - UpdateInfoText(this, this->info_title, this->info_content, interval); - } - - // update control - if (this->ui1_enable && this->ui1.sect[SECT_CONTROL].state) { - mjui_update(SECT_CONTROL, -1, &this->ui1, &this->uistate, &this->platform_ui->mjr_context()); - } - - // update profiler - if (this->profiler && this->run) { - UpdateProfiler(this); - } - - // update sensor - if (this->sensor && this->run) { - UpdateSensor(this); - } - - // clear timers once profiler info has been copied - ClearTimeres(this->d); -} - // render the ui to the window void Simulate::Render() { - if (this->platform_ui->RefreshMjrContext(this->m, 50*(this->font+1))) { + // update rendering context buffer size if required + if (this->platform_ui->EnsureContextSize()) { UiModify(&this->ui0, &this->uistate, &this->platform_ui->mjr_context()); UiModify(&this->ui1, &this->uistate, &this->platform_ui->mjr_context()); } @@ -1734,7 +2020,7 @@ void Simulate::Render() { } // no model - if (!this->m) { + if (this->fully_managed_ && !this->m_) { // blank screen mjr_rectangle(rect, 0.2f, 0.3f, 0.4f, 1); @@ -1770,6 +2056,67 @@ void Simulate::Render() { return; } + // update UI sections from last sync + if (pending_.full_ui_update) { + mjui_update(-1, -1, &this->ui0, &this->uistate, &this->platform_ui->mjr_context()); + mjui_update(-1, -1, &this->ui1, &this->uistate, &this->platform_ui->mjr_context()); + pending_.full_ui_update = false; + pending_.ui_update_physics = false; + pending_.ui_update_joint = false; + pending_.ui_update_ctrl = false; + } else { + if (this->ui0_enable && this->ui0.sect[SECT_WATCH].state) { + mjui_update(SECT_WATCH, -1, &this->ui0, &this->uistate, &this->platform_ui->mjr_context()); + } + + if (pending_.ui_update_physics) { + if (this->ui0_enable && this->ui0.sect[SECT_PHYSICS].state) { + mjui_update(SECT_PHYSICS, -1, &this->ui0, &this->uistate, &this->platform_ui->mjr_context()); + } + pending_.ui_update_physics = false; + } + + if (!fully_managed_) { + if (this->ui0_enable && this->ui0.sect[SECT_RENDERING].state && + (cam_prev_.type != cam.type || + cam_prev_.fixedcamid != cam.fixedcamid || + cam_prev_.trackbodyid != cam.trackbodyid || + opt_prev_.label != opt.label || opt_prev_.frame != opt.frame || + Differ(opt_prev_.flags, opt.flags))) { + mjui_update(SECT_RENDERING, -1, &this->ui0, &this->uistate, + &this->platform_ui->mjr_context()); + } + + if (this->ui0_enable && this->ui0.sect[SECT_RENDERING].state && + (Differ(opt_prev_.geomgroup, opt.geomgroup) || + Differ(opt_prev_.sitegroup, opt.sitegroup) || + Differ(opt_prev_.jointgroup, opt.jointgroup) || + Differ(opt_prev_.tendongroup, opt.tendongroup) || + Differ(opt_prev_.actuatorgroup, opt.actuatorgroup) || + Differ(opt_prev_.skingroup, opt.skingroup))) { + mjui_update(SECT_GROUP, -1, &this->ui0, &this->uistate, + &this->platform_ui->mjr_context()); + } + + opt_prev_ = opt; + cam_prev_ = cam; + } + + if (pending_.ui_update_joint) { + if (this->ui1_enable && this->ui1.sect[SECT_JOINT].state) { + mjui_update(SECT_JOINT, -1, &this->ui1, &this->uistate, &this->platform_ui->mjr_context()); + } + pending_.ui_update_joint = false; + } + + if (pending_.ui_update_ctrl) { + if (this->ui1_enable && this->ui1.sect[SECT_CONTROL].state) { + mjui_update(SECT_CONTROL, -1, &this->ui1, &this->uistate, &this->platform_ui->mjr_context()); + } + pending_.ui_update_ctrl = false; + } + } + // render scene mjr_render(rect, &this->scn, &this->platform_ui->mjr_context()); @@ -1897,8 +2244,11 @@ void Simulate::RenderLoop() { InitializeSensor(this); // make empty scene - mjv_defaultScene(&this->scn); - mjv_makeScene(nullptr, &this->scn, maxgeom); + if (fully_managed_) { + mjv_defaultScene(&this->scn); + mjv_makeScene(nullptr, &this->scn, kMaxGeom); + } + if (!this->platform_ui->IsGPUAccelerated()) { this->scn.flags[mjRND_SHADOW] = 0; this->scn.flags[mjRND_REFLECTION] = 0; @@ -1951,10 +2301,13 @@ void Simulate::RenderLoop() { // set VSync to initial value this->platform_ui->SetVSync(this->vsync); + frames_ = 0; + last_fps_update_ = mj::Simulate::Clock::now(); + // run event loop while (!this->platform_ui->ShouldCloseWindow() && !this->exitrequest.load()) { { - const std::lock_guard lock(this->mtx); + const MutexLock lock(this->mtx); // load model (not on first pass, to show "loading" label) if (this->loadrequest==1) { @@ -1966,16 +2319,33 @@ void Simulate::RenderLoop() { // poll and handle events this->platform_ui->PollEvents(); - // prepare to render - this->PrepareScene(); - } // std::lock_guard (unblocks simulation thread) + // update scene, doing a full sync if in fully managed mode + if (this->fully_managed_) { + Sync(); + } else { + scnstate_.data.warning[mjWARN_VGEOMFULL].number += mjv_updateSceneFromState( + &scnstate_, &this->opt, &this->pert, &this->cam, mjCAT_ALL, &this->scn); + } + } // MutexLock (unblocks simulation thread) // render while simulation is running this->Render(); + + // update FPS stat, at most 5 times per second + auto now = mj::Simulate::Clock::now(); + double interval = Seconds(now - last_fps_update_).count(); + ++frames_; + if (interval > 0.2) { + last_fps_update_ = now; + fps_ = frames_ / interval; + frames_ = 0; + } } - this->exitrequest.store(true); + if (fully_managed_){ + mjv_freeScene(&this->scn); + } - mjv_freeScene(&this->scn); + this->exitrequest.store(2); } } // namespace mujoco diff --git a/simulate/simulate.h b/simulate/simulate.h index f0cfc7ec..0ba1027b 100644 --- a/simulate/simulate.h +++ b/simulate/simulate.h @@ -20,15 +20,24 @@ #include #include #include +#include #include -#include +#include +#include +#include #include #include "platform_ui_adapter.h" namespace mujoco { -//-------------------------------- global ----------------------------------------------- +// The viewer itself doesn't require a reentrant mutex, however we use it in +// order to provide a Python sync API that doesn't require separate locking +// (since sync is by far the most common operation), but that also won't +// deadlock if called when a lock is already held by the user script on the +// same thread. +class SimulateMutex : public std::recursive_mutex {}; +using MutexLock = std::unique_lock; // Simulate states not contained in MuJoCo structures class Simulate { @@ -36,14 +45,17 @@ class Simulate { using Clock = std::chrono::steady_clock; static_assert(std::ratio_less_equal_v); + static constexpr int kMaxGeom = 20000; + // create object and initialize the simulate ui - Simulate(std::unique_ptr platform_ui_adapter); + Simulate( + std::unique_ptr platform_ui_adapter, + mjvScene* scn, mjvCamera* cam, + mjvOption* opt, mjvPerturb* pert, bool fully_managed); - // Apply UI pose perturbations to model and data - void ApplyPosePerturbations(int flg_paused); - - // Apply UI force perturbations to model and data - void ApplyForcePerturbations(); + // Synchronize mjModel and mjData state with UI inputs, and update + // visualization. + void Sync(); // Request that the Simulate UI thread render a new model // optionally delete the old model and data when done @@ -53,9 +65,6 @@ class Simulate { // load mjb or xml model that has been requested by load() void LoadOnRenderThread(); - // prepare to render - void PrepareScene(); - // render the ui to the window void Render(); @@ -65,14 +74,71 @@ class Simulate { // constants static constexpr int kMaxFilenameLength = 1000; - // model and data to be visualized - mjModel* mnew = nullptr; - mjData* dnew = nullptr; + // whether the viewer is operating in fully managed mode, where it can assume + // that it has exclusive access to mjModel, mjData, and various mjv objects + bool fully_managed_ = true; - mjModel* m = nullptr; - mjData* d = nullptr; - std::mutex mtx; - std::condition_variable cond_loadrequest; + // model and data to be visualized + mjModel* mnew_ = nullptr; + mjData* dnew_ = nullptr; + + mjModel* m_ = nullptr; + mjData* d_ = nullptr; + + int ncam_ = 0; + int nkey_ = 0; + + std::vector body_parentid_; + + std::vector jnt_type_; + std::vector jnt_group_; + std::vector jnt_qposadr_; + std::vector>> jnt_range_; + std::vector jnt_names_; + + std::vector actuator_group_; + std::vector>> actuator_ctrlrange_; + std::vector actuator_names_; + + // mjModel and mjData fields that can be modified by the user through the GUI + std::vector qpos_; + std::vector qpos_prev_; + std::vector ctrl_; + std::vector ctrl_prev_; + + mjvSceneState scnstate_; + mjOption mjopt_prev_; + mjvOption opt_prev_; + mjvCamera cam_prev_; + int warn_vgeomfull_prev_; + + // pending GUI-driven actions, to be applied at the next call to Sync + struct { + std::optional save_xml; + std::optional save_mjb; + std::optional print_model; + std::optional print_data; + bool reset; + bool align; + bool copy_pose; + bool load_key; + bool save_key; + bool zero_ctrl; + int newperturb; + bool select; + mjuiState select_state; + bool full_ui_update; + bool ui_update_physics; + bool ui_update_joint; + bool ui_update_ctrl; + } pending_ = {}; + + SimulateMutex mtx; + std::condition_variable_any cond_loadrequest; + + int frames_ = 0; + std::chrono::time_point last_fps_update_; + double fps_ = 0; // options int spacing = 0; @@ -140,10 +206,10 @@ class Simulate { int camera = 0; // abstract visualization - mjvScene scn = {}; - mjvCamera cam = {}; - mjvOption opt = {}; - mjvPerturb pert = {}; + mjvScene& scn; + mjvCamera& cam; + mjvOption& opt; + mjvPerturb& pert; mjvFigure figconstraint = {}; mjvFigure figcost = {}; mjvFigure figtimer = {}; @@ -186,16 +252,16 @@ class Simulate { // simulation section of UI const mjuiDef def_simulation[12] = { {mjITEM_SECTION, "Simulation", 1, nullptr, "AS"}, - {mjITEM_RADIO, "", 2, &this->run, "Pause\nRun"}, + {mjITEM_RADIO, "", 5, &this->run, "Pause\nRun"}, {mjITEM_BUTTON, "Reset", 2, nullptr, " #259"}, - {mjITEM_BUTTON, "Reload", 2, nullptr, "CL"}, + {mjITEM_BUTTON, "Reload", 5, nullptr, "CL"}, {mjITEM_BUTTON, "Align", 2, nullptr, "CA"}, {mjITEM_BUTTON, "Copy pose", 2, nullptr, "CC"}, {mjITEM_SLIDERINT, "Key", 3, &this->key, "0 0"}, {mjITEM_BUTTON, "Load key", 3}, {mjITEM_BUTTON, "Save key", 3}, - {mjITEM_SLIDERNUM, "Noise scale", 2, &this->ctrl_noise_std, "0 2"}, - {mjITEM_SLIDERNUM, "Noise rate", 2, &this->ctrl_noise_rate, "0 2"}, + {mjITEM_SLIDERNUM, "Noise scale", 5, &this->ctrl_noise_std, "0 2"}, + {mjITEM_SLIDERNUM, "Noise rate", 5, &this->ctrl_noise_rate, "0 2"}, {mjITEM_END} }; diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index 031d834e..3dfa1468 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -77,6 +77,8 @@ set(MUJOCO_ENGINE_SRCS engine_vis_init.h engine_vis_interact.c engine_vis_interact.h + engine_vis_state.c + engine_vis_state.h engine_vis_visualize.c engine_vis_visualize.h ) diff --git a/src/engine/engine_io.c b/src/engine/engine_io.c index 0c488262..1cb806cf 100644 --- a/src/engine/engine_io.c +++ b/src/engine/engine_io.c @@ -145,7 +145,6 @@ void mj_defaultVisual(mjVisual* vis) { vis->global.offwidth = 640; vis->global.offheight = 480; vis->global.realtime = 1.0; - vis->global.treedepth = 1; vis->global.ellipsoidinertia = 0; // rendering quality diff --git a/src/engine/engine_vis_init.c b/src/engine/engine_vis_init.c index 8028cd81..2b0db455 100644 --- a/src/engine/engine_vis_init.c +++ b/src/engine/engine_vis_init.c @@ -226,6 +226,8 @@ void mjv_defaultOption(mjvOption* vopt) { for (int i=0; iflags[i] = (mjVISSTRING[i][1][0]=='1'); } + + vopt->bvh_depth = 1; } diff --git a/src/engine/engine_vis_state.c b/src/engine/engine_vis_state.c new file mode 100644 index 00000000..2e0d62e9 --- /dev/null +++ b/src/engine/engine_vis_state.c @@ -0,0 +1,339 @@ +// Copyright 2023 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "engine/engine_vis_state.h" + +#include + +#include +#include +#include +#include +#include +#include "engine/engine_core_constraint.h" +#include "engine/engine_macro.h" +#include "engine/engine_plugin.h" +#include "engine/engine_support.h" +#include "engine/engine_util_errmem.h" +#include "engine/engine_vis_init.h" +#include "engine/engine_vis_interact.h" +#include "engine/engine_vis_visualize.h" + +// this source file needs to treat XMJV differently from other X macros +#undef XMJV + + + +// round size up to multiples of 64-byte cache lines +static inline size_t roundUpToCacheLine(size_t n) { + return 64 * ((n / 64) + (n % 64 ? 1 : 0)); +} + + + +// set default scene +void mjv_defaultSceneState(mjvSceneState* scnstate) { + memset(scnstate, 0, sizeof(mjvSceneState)); + mjv_defaultScene(&scnstate->plugincache); +} + + + +// allocate and init scene state +void mjv_makeSceneState(const mjModel* m, const mjData* d, mjvSceneState* scnstate, int maxgeom) { + mjv_freeScene(&scnstate->plugincache); + mju_free(scnstate->buffer); + +#ifdef MEMORY_SANITIZER + __msan_allocated_memory(scnstate, sizeof(mjvSceneState)); + mjv_defaultScene(&scnstate->plugincache); +#endif + + scnstate->nbuffer = 0; + scnstate->maxgeom = maxgeom; + +#define X(var) +#define XMJV(var) scnstate->model.var = m->var; + MJMODEL_INTS +#undef XMJV +#undef X + +#define X(dtype, var, dim0, dim1) +#define XMJV(dtype, var, dim0, dim1) \ + scnstate->nbuffer += roundUpToCacheLine(sizeof(dtype) * m->dim0 * dim1); + MJMODEL_POINTERS +#undef XMJV +#undef X + +#define X(dtype, var, dim0, dim1) +#define XMJV(dtype, var, dim0, dim1) \ + scnstate->nbuffer += roundUpToCacheLine(sizeof(dtype) * m->dim0 * dim1); + MJDATA_POINTERS +#undef XMJV +#undef X + + int condimmax = mj_isPyramidal(m) ? 10 : 6; + scnstate->nbuffer += roundUpToCacheLine(sizeof(mjContact) * maxgeom); + scnstate->nbuffer += roundUpToCacheLine(sizeof(mjtNum) * maxgeom * condimmax); + + scnstate->buffer = mju_malloc(scnstate->nbuffer); + + char* ptr = scnstate->buffer; + +#define X(dtype, var, dim0, dim1) +#define XMJV(dtype, var, dim0, dim1) \ + scnstate->model.var = (dtype*)ptr; \ + ptr += roundUpToCacheLine(sizeof(dtype) * m->dim0 * dim1); + MJMODEL_POINTERS +#undef XMJV +#undef X + +#define X(dtype, var, dim0, dim1) +#define XMJV(dtype, var, dim0, dim1) \ + scnstate->data.var = (dtype*)ptr; \ + ptr += roundUpToCacheLine(sizeof(dtype) * m->dim0 * dim1); + MJDATA_POINTERS +#undef XMJV +#undef X + + scnstate->data.contact = (mjContact*)ptr; + ptr += roundUpToCacheLine(sizeof(mjContact) * scnstate->maxgeom); + + scnstate->data.efc_force = (mjtNum*)ptr; + ptr += roundUpToCacheLine(sizeof(mjtNum) * scnstate->maxgeom * condimmax); + + // should not occur + if (ptr - (char*)scnstate->buffer != scnstate->nbuffer) { + mju_error("Unexpected error: mjvSceneState buffer is not fully used"); + } + + mjv_makeScene(m, &scnstate->plugincache, maxgeom); +} + + + +// free scene state +void mjv_freeSceneState(mjvSceneState* scnstate) { + mjv_freeScene(&scnstate->plugincache); + mju_free(scnstate->buffer); + mjv_defaultSceneState(scnstate); +} + + + +// shallow copy scene state into model and data for use with mjv functions +void mjv_assignFromSceneState(const mjvSceneState* scnstate, mjModel* m, mjData* d) { + if (m) { + memset(m, 0, sizeof(mjModel)); + +#ifdef MEMORY_SANITIZER + // Tell msan to treat the entire buffer as uninitialized + __msan_allocated_memory(m, sizeof(mjModel)); +#endif + +#define X(var) +#define XMJV(var) m->var = scnstate->model.var; + MJMODEL_INTS +#undef XMJV +#undef X + + m->opt = scnstate->model.opt; + m->vis = scnstate->model.vis; + m->stat = scnstate->model.stat; + +#define X(dtype, var, dim0, dim1) +#define XMJV(dtype, var, dim0, dim1) m->var = scnstate->model.var; + MJMODEL_POINTERS +#undef XMJV +#undef X + } + + if (d) { + memset(d, 0, sizeof(mjData)); + +#ifdef MEMORY_SANITIZER + // Tell msan to treat the entire buffer as uninitialized + __msan_allocated_memory(d, sizeof(mjData)); +#endif + + memcpy(d->warning, scnstate->data.warning, sizeof(d->warning)); + d->nefc = scnstate->data.nefc; + d->ncon = scnstate->data.ncon; + d->time = scnstate->data.time; + + #define X(dtype, var, dim0, dim1) + #define XMJV(dtype, var, dim0, dim1) d->var = scnstate->data.var; + MJDATA_POINTERS + #undef XMJV + #undef X + + d->contact = scnstate->data.contact; + d->efc_force = scnstate->data.efc_force; + } +} + + + +// update entire scene from a scene state, return the number of new mjWARN_VGEOMFULL warnings +int mjv_updateSceneFromState(const mjvSceneState* scnstate, const mjvOption* opt, + const mjvPerturb* pert, mjvCamera* cam, int catmask, mjvScene* scn) { + // shallow-copy scnstate pointers into mjModel and mjData + mjModel m; + mjData d; + mjv_assignFromSceneState(scnstate, &m, &d); + + // save the number of mjWARN_VGEOMFULL warnings before the scene update + int warning_start = d.warning[mjWARN_VGEOMFULL].number; + + // copy mjvGeoms added by plugins + int nplugingeom = scnstate->plugincache.ngeom; + if (nplugingeom > scn->maxgeom) { + mj_warning(&d, mjWARN_VGEOMFULL, scn->maxgeom); + scn->ngeom = scn->maxgeom; + } else { + scn->ngeom = nplugingeom; + } + memcpy(scn->geoms, scnstate->plugincache.geoms, sizeof(mjvGeom) * scn->ngeom); + + // add all categories + mjv_addGeoms(&m, &d, opt, pert, catmask, scn); + + // add lights + mjv_makeLights(&m, &d, scn); + + // update camera + mjv_updateCamera(&m, &d, cam, scn); + + // update skins + if (opt->flags[mjVIS_SKIN]) { + mjv_updateActiveSkin(&m, &d, scn, opt); + } + + // return the number of new mjWARN_VGEOMFULL warnings generated + return d.warning[mjWARN_VGEOMFULL].number - warning_start; +} + + + +// update a scene state from model and data +void mjv_updateSceneState(const mjModel* m, mjData* d, mjvSceneState* scnstate) { + // Check that mjModel sizes haven't changed. +#define X(var) +#define XMJV(var) \ + if (scnstate->model.var != m->var) { \ + mju_error("m->%s changed", #var); \ + } + MJMODEL_INTS +#undef XMJV +#undef X + + // Update plugin visualization cache. + scnstate->plugincache.ngeom = 0; + if (m->nplugin) { + const int nslot = mjp_pluginCount(); + // iterate over plugins, call visualize if defined + for (int i=0; inplugin; i++) { + const int slot = m->plugin[i]; + const mjpPlugin* plugin = mjp_getPluginAtSlotUnsafe(slot, nslot); + if (!plugin) { + mju_error("invalid plugin slot: %d", slot); + } + if (plugin->visualize) { + plugin->visualize(m, d, &scnstate->plugincache, i); + } + } + } + + // Copy variable-sized arrays in mjModel. +#define X(dtype, var, dim0, dim1) +#define XMJV(dtype, var, dim0, dim1) \ + memcpy(scnstate->model.var, m->var, sizeof(dtype) * m->dim0 * dim1); + MJMODEL_POINTERS +#undef XMJV +#undef X + + scnstate->model.opt = m->opt; + scnstate->model.vis = m->vis; + scnstate->model.stat = m->stat; + + // Copy mjData variables. + memcpy(scnstate->data.warning, d->warning, sizeof(d->warning)); + scnstate->data.time = d->time; + + // Copy variable-sized arrays in mjData. +#define X(dtype, var, dim0, dim1) +#define XMJV(dtype, var, dim0, dim1) \ + memcpy(scnstate->data.var, d->var, sizeof(dtype) * m->dim0 * dim1); + MJDATA_POINTERS +#undef XMJV +#undef X + + // Copy contacts. + { + if (d->ncon > scnstate->maxgeom) { + mj_warning(d, mjWARN_VGEOMFULL, scnstate->maxgeom); + scnstate->data.ncon = scnstate->maxgeom; + } else { + scnstate->data.ncon = d->ncon; + } + memcpy(scnstate->data.contact, d->contact, sizeof(mjContact) * scnstate->data.ncon); + } + + // Copy only the entries in efc_force that correspond to contacts. + { + scnstate->data.nefc = 0; + for (int i = 0; i < scnstate->data.ncon; ++i) { + const mjContact* con = &d->contact[i]; + scnstate->data.nefc += con->dim; + } + + int efc_address = 0; + int ispyramid = mj_isPyramidal(m); + for (int i = 0; i < scnstate->data.ncon; ++i) { + mjContact* con = &scnstate->data.contact[i]; + int dim = con->dim; + if (ispyramid && dim > 1){ + dim = 2*(dim - 1); + } + for (int j = 0; j < dim; ++j) { + scnstate->data.efc_force[efc_address + j] = d->efc_force[con->efc_address + j]; + } + con->efc_address = efc_address; + efc_address += dim; + } + } +} + + + +// move camera with mouse given a scene state; action is mjtMouse +MJAPI void mjv_moveCameraFromState(const mjvSceneState* scnstate, int action, + mjtNum reldx, mjtNum reldy, + const mjvScene* scn, mjvCamera* cam) { + mjModel m; + mjv_assignFromSceneState(scnstate, &m, NULL); + mjv_moveCamera(&m, action, reldx, reldy, scn, cam); +} + + + +// move perturb object with mouse given a scene state; action is mjtMouse +MJAPI void mjv_movePerturbFromState(const mjvSceneState* scnstate, int action, + mjtNum reldx, mjtNum reldy, + const mjvScene* scn, mjvPerturb* pert) { + mjModel m; + mjData d; + mjv_assignFromSceneState(scnstate, &m, &d); + mjv_movePerturb(&m, &d, action, reldx, reldy, scn, pert); +} diff --git a/src/engine/engine_vis_state.h b/src/engine/engine_vis_state.h new file mode 100644 index 00000000..61e7cddb --- /dev/null +++ b/src/engine/engine_vis_state.h @@ -0,0 +1,62 @@ +// Copyright 2023 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MUJOCO_SRC_ENGINE_ENGINE_VIS_STATE_H_ +#define MUJOCO_SRC_ENGINE_ENGINE_VIS_STATE_H_ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif +// set default scene state +MJAPI void mjv_defaultSceneState(mjvSceneState* scnstate); + +// allocate and init scene state +MJAPI void mjv_makeSceneState(const mjModel* m, const mjData* d, + mjvSceneState* scnstate, int maxgeom); + +// free scene state +MJAPI void mjv_freeSceneState(mjvSceneState* scnstate); + +// shallow copy scene state into model and data for use with mjv functions +void mjv_assignFromSceneState(const mjvSceneState* scnstate, mjModel* m, mjData* d); + +// update entire scene from a scene state, return the number of new mjWARN_VGEOMFULL warnings +MJAPI int mjv_updateSceneFromState(const mjvSceneState* scnstate, const mjvOption* opt, + const mjvPerturb* pert, mjvCamera* cam, int catmask, + mjvScene* scn); + +// update a scene state from model and data +MJAPI void mjv_updateSceneState(const mjModel* m, mjData* d, mjvSceneState* scnstate); + +// move camera with mouse given a scene state; action is mjtMouse +MJAPI void mjv_moveCameraFromState(const mjvSceneState* scnstate, int action, + mjtNum reldx, mjtNum reldy, + const mjvScene* scn, mjvCamera* cam); + +// move perturb object with mouse given a scene state; action is mjtMouse +MJAPI void mjv_movePerturbFromState(const mjvSceneState* scnstate, int action, + mjtNum reldx, mjtNum reldy, + const mjvScene* scn, mjvPerturb* pert); + +#ifdef __cplusplus +} +#endif + +#endif // MUJOCO_SRC_ENGINE_ENGINE_VIS_STATE_H_ diff --git a/src/engine/engine_vis_visualize.c b/src/engine/engine_vis_visualize.c index b317eafd..a1ce9082 100644 --- a/src/engine/engine_vis_visualize.c +++ b/src/engine/engine_vis_visualize.c @@ -460,7 +460,7 @@ static int bodycategory(const mjModel* m, int bodyid) { // add abstract geoms void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, - const mjvPerturb* pert, int catmask, mjvScene* scn) { + const mjvPerturb* pert, int catmask, mjvScene* scn) { int objtype, category; mjtNum sz[3], mat[9], selpos[3]; mjtNum catenary[3*mjNCATENARY]; @@ -527,8 +527,8 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, for (int i = 0; i < m->nbvh; i++) { int isleaf = m->bvh_child[2*i]==-1 && m->bvh_child[2*i+1]==-1; if (scn->ngeom >= scn->maxgeom) break; - if (m->bvh_depth[i] != m->vis.global.treedepth) { - if (!isleaf || m->bvh_depth[i] > m->vis.global.treedepth) { + if (m->bvh_depth[i] != vopt->bvh_depth) { + if (!isleaf || m->bvh_depth[i] > vopt->bvh_depth) { continue; } } @@ -2057,22 +2057,10 @@ void mjv_updateActiveSkin(const mjModel* m, mjData* d, mjvScene* scn, const mjvO // update entire scene void mjv_updateScene(const mjModel* m, mjData* d, const mjvOption* opt, const mjvPerturb* pert, mjvCamera* cam, int catmask, mjvScene* scn) { - // clear geoms and add all categories + // clear geoms scn->ngeom = 0; - mjv_addGeoms(m, d, opt, pert, catmask, scn); - // add lights - mjv_makeLights(m, d, scn); - - // update camera - mjv_updateCamera(m, d, cam, scn); - - // update skins - if (opt->flags[mjVIS_SKIN]) { - mjv_updateActiveSkin(m, d, scn, opt); - } - - // update plugin + // trigger plugin visualization hooks if (m->nplugin) { const int nslot = mjp_pluginCount(); // iterate over plugins, call visualize if defined @@ -2087,6 +2075,20 @@ void mjv_updateScene(const mjModel* m, mjData* d, const mjvOption* opt, } } } + + // add all categories + mjv_addGeoms(m, d, opt, pert, catmask, scn); + + // add lights + mjv_makeLights(m, d, scn); + + // update camera + mjv_updateCamera(m, d, cam, scn); + + // update skins + if (opt->flags[mjVIS_SKIN]) { + mjv_updateActiveSkin(m, d, scn, opt); + } } diff --git a/src/render/render_context.c b/src/render/render_context.c index 8f2dff12..8be0f433 100644 --- a/src/render/render_context.c +++ b/src/render/render_context.c @@ -1798,3 +1798,48 @@ void mjr_freeContext(mjrContext* con) { con->windowStereo = windowStereo; con->windowDoublebuffer = windowDoublebuffer; } + + + +// resize offscreen buffers +MJAPI void mjr_resizeOffscreen(int width, int height, mjrContext* con) { + if (con->offWidth == width && con->offHeight == height) { + return; + } + + con->offWidth = width; + con->offHeight = height; + + if (!width || !height) { + return; + } + + if (!con->offFBO) { + makeOff(con); + return; + } + + glBindRenderbuffer(GL_RENDERBUFFER, con->offColor); + if (con->offSamples) { + glRenderbufferStorageMultisample(GL_RENDERBUFFER, con->offSamples, GL_RGBA8, + con->offWidth, con->offHeight); + } else { + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, con->offWidth, con->offHeight); + } + + glBindRenderbuffer(GL_RENDERBUFFER, con->offDepthStencil); + if (con->offSamples) { + glRenderbufferStorageMultisample(GL_RENDERBUFFER, con->offSamples, GL_DEPTH24_STENCIL8, + con->offWidth, con->offHeight); + } else { + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, con->offWidth, con->offHeight); + } + + if (con->offSamples) { + glBindRenderbuffer(GL_RENDERBUFFER, con->offColor_r); + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, con->offWidth, con->offHeight); + + glBindRenderbuffer(GL_RENDERBUFFER, con->offDepthStencil_r); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, con->offWidth, con->offHeight); + } +} diff --git a/src/render/render_context.h b/src/render/render_context.h index f8025ecc..6e29c096 100644 --- a/src/render/render_context.h +++ b/src/render/render_context.h @@ -53,6 +53,9 @@ MJAPI void mjr_addAux(int index, int width, int height, int samples, mjrContext* // free resources in custom OpenGL context, set to default MJAPI void mjr_freeContext(mjrContext* con); +// resize offscreen renderbuffer +MJAPI void mjr_resizeOffscreen(int offwidth, int offheight, mjrContext* con); + // (re) upload texture to GPU MJAPI void mjr_uploadTexture(const mjModel* m, const mjrContext* con, int texid); diff --git a/test/engine/CMakeLists.txt b/test/engine/CMakeLists.txt index d43a6fe7..fd3f7732 100644 --- a/test/engine/CMakeLists.txt +++ b/test/engine/CMakeLists.txt @@ -78,3 +78,6 @@ target_link_libraries(engine_util_spatial_test fixture gmock) mujoco_test(engine_vfs_test) target_link_libraries(engine_vfs_test fixture gmock) + +mujoco_test(engine_vis_state_test) +target_link_libraries(engine_vis_state_test fixture gmock) diff --git a/test/engine/engine_vis_state_test.cc b/test/engine/engine_vis_state_test.cc new file mode 100644 index 00000000..a1b4f182 --- /dev/null +++ b/test/engine/engine_vis_state_test.cc @@ -0,0 +1,116 @@ +// Copyright 2023 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include +#include +#include +#include +#include "test/fixture.h" + +namespace mujoco { +namespace { + +using ::testing::NotNull; +using MjvSceneStateTest = MujocoTest; + +constexpr int kMaxGeom = 10000; + +TEST_F(MjvSceneStateTest, CanUpdateFromState) { + constexpr char path[] = "engine/testdata/hammock/hammock.xml"; + const std::string xml_path = GetTestDataFilePath(path); + mjModel* model = mj_loadXML(xml_path.c_str(), nullptr, 0, 0); + ASSERT_THAT(model, NotNull()); + mjData* data = mj_makeData(model); + + while (data->time < 2) { + mj_step(model, data); + } + ASSERT_GT(data->ncon, 10); + + mjvScene scn1; + mjv_defaultScene(&scn1); + mjv_makeScene(model, &scn1, kMaxGeom); + + mjvOption opt; + mjv_defaultOption(&opt); + + mjvPerturb pert; + mjv_defaultPerturb(&pert); + + mjvCamera cam; + mjv_defaultFreeCamera(model, &cam); + + // Enable all flags to exercise all code paths + for (int i = 0; i < mjNVISFLAG; ++i) { + opt.flags[i] = 1; + } + + mjv_updateScene(model, data, &opt, &pert, &cam, mjCAT_ALL, &scn1); + EXPECT_GT(scn1.ngeom, 0); + EXPECT_GT(scn1.nskin, 0); + EXPECT_GT(scn1.nlight, 0); + + mjvSceneState scnstate; + mjv_defaultSceneState(&scnstate); + mjv_makeSceneState(model, data, &scnstate, kMaxGeom); + mjv_updateSceneState(model, data, &scnstate); + + mjvScene scn2; + mjv_defaultScene(&scn2); + mjv_makeScene(model, &scn2, kMaxGeom); + mjv_updateSceneFromState(&scnstate, &opt, &pert, &cam, mjCAT_ALL, &scn2); + + EXPECT_EQ(scn1.ngeom, scn2.ngeom); + for (int i = 0; i < scn1.ngeom; ++i) { + EXPECT_EQ(std::memcmp(&scn1.geoms[i], &scn2.geoms[i], sizeof(mjvGeom)), 0); + } + // NB: scn->geomorder is a scratch space for use by mjr_render, so we don't + // need to compare them here. + + EXPECT_LE(scn1.nskin, scn2.nskin); + EXPECT_EQ(std::memcmp(scn1.skinfacenum, scn2.skinfacenum, + sizeof(*scn2.skinfacenum) * scn2.nskin), + 0); + EXPECT_EQ(std::memcmp(scn1.skinvertadr, scn2.skinvertadr, + sizeof(*scn2.skinvertadr) * scn2.nskin), + 0); + EXPECT_EQ(std::memcmp(scn1.skinvertnum, scn2.skinvertnum, + sizeof(*scn2.skinvertnum) * scn2.nskin), + 0); + EXPECT_EQ(std::memcmp(scn1.skinvert, scn2.skinvert, + sizeof(*scn2.skinvert) * scn2.nskin), + 0); + EXPECT_EQ(std::memcmp(scn1.skinnormal, scn2.skinnormal, + sizeof(*scn2.skinnormal) * scn2.nskin), + 0); + + auto scn1_cmp_begin = reinterpret_cast(&scn1.nlight); + auto scn2_cmp_begin = reinterpret_cast(&scn2.nlight); + auto cmp_bytes = + sizeof(mjvScene) - (scn2_cmp_begin - reinterpret_cast(&scn2)); + EXPECT_EQ(std::memcmp(scn1_cmp_begin, scn2_cmp_begin, cmp_bytes), 0); + + mjv_freeScene(&scn1); + mjv_freeScene(&scn2); + mjv_freeSceneState(&scnstate); + + mj_deleteData(data); + mj_deleteModel(model); +} + +} // namespace +} // namespace mujoco diff --git a/test/engine/testdata/hammock/hammock.xml b/test/engine/testdata/hammock/hammock.xml new file mode 100644 index 00000000..28e69ff5 --- /dev/null +++ b/test/engine/testdata/hammock/hammock.xml @@ -0,0 +1,63 @@ + + + + + + diff --git a/test/engine/testdata/hammock/humanoid_body.xml b/test/engine/testdata/hammock/humanoid_body.xml new file mode 100644 index 00000000..1254082a --- /dev/null +++ b/test/engine/testdata/hammock/humanoid_body.xml @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index f371f737..a76e6231 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -1771,7 +1771,6 @@ public unsafe struct global { public float realtime; public int offwidth; public int offheight; - public int treedepth; public int ellipsoidinertia; } @@ -2576,6 +2575,7 @@ public unsafe struct mjvOption_ { public fixed byte actuatorgroup[6]; public fixed byte skingroup[6]; public fixed byte flags[24]; + public int bvh_depth; } [StructLayout(LayoutKind.Sequential)] @@ -2738,6 +2738,203 @@ public unsafe struct mjvFigure_ { public fixed int yaxispixel[2]; public fixed float xaxisdata[2]; public fixed float yaxisdata[2]; +} + +[StructLayout(LayoutKind.Sequential)] +public unsafe struct model { + public int nu; + public int na; + public int nbody; + public int nbvh; + public int njnt; + public int ngeom; + public int nsite; + public int ncam; + public int nlight; + public int nmesh; + public int nskin; + public int nskinvert; + public int nskinface; + public int nskinbone; + public int nskinbonevert; + public int nmat; + public int neq; + public int ntendon; + public int nwrap; + public int nsensor; + public int nnames; + public int nsensordata; + public mjOption_ opt; + public mjVisual_ vis; + public mjStatistic_ stat; + public int* body_parentid; + public int* body_rootid; + public int* body_weldid; + public int* body_mocapid; + public int* body_jntnum; + public int* body_jntadr; + public int* body_geomnum; + public int* body_geomadr; + public double* body_iquat; + public double* body_mass; + public double* body_inertia; + public int* body_bvhadr; + public int* body_bvhnum; + public int* bvh_depth; + public int* bvh_child; + public int* bvh_geomid; + public double* bvh_aabb; + public int* jnt_type; + public int* jnt_bodyid; + public int* jnt_group; + public int* geom_type; + public int* geom_bodyid; + public int* geom_dataid; + public int* geom_matid; + public int* geom_group; + public double* geom_size; + public double* geom_aabb; + public double* geom_rbound; + public float* geom_rgba; + public int* site_type; + public int* site_bodyid; + public int* site_matid; + public int* site_group; + public double* site_size; + public float* site_rgba; + public double* cam_fovy; + public double* cam_ipd; + public byte* light_directional; + public byte* light_castshadow; + public byte* light_active; + public float* light_attenuation; + public float* light_cutoff; + public float* light_exponent; + public float* light_ambient; + public float* light_diffuse; + public float* light_specular; + public int* mesh_texcoordadr; + public int* mesh_graphadr; + public int* skin_matid; + public int* skin_group; + public float* skin_rgba; + public float* skin_inflate; + public int* skin_vertadr; + public int* skin_vertnum; + public int* skin_texcoordadr; + public int* skin_faceadr; + public int* skin_facenum; + public int* skin_boneadr; + public int* skin_bonenum; + public float* skin_vert; + public int* skin_face; + public int* skin_bonevertadr; + public int* skin_bonevertnum; + public float* skin_bonebindpos; + public float* skin_bonebindquat; + public int* skin_bonebodyid; + public int* skin_bonevertid; + public float* skin_bonevertweight; + public int* mat_texid; + public byte* mat_texuniform; + public float* mat_texrepeat; + public float* mat_emission; + public float* mat_specular; + public float* mat_shininess; + public float* mat_reflectance; + public float* mat_rgba; + public int* eq_type; + public int* eq_obj1id; + public int* eq_obj2id; + public byte* eq_active; + public double* eq_data; + public int* tendon_num; + public int* tendon_matid; + public int* tendon_group; + public byte* tendon_limited; + public double* tendon_width; + public double* tendon_range; + public double* tendon_stiffness; + public double* tendon_damping; + public double* tendon_frictionloss; + public double* tendon_lengthspring; + public float* tendon_rgba; + public int* actuator_trntype; + public int* actuator_dyntype; + public int* actuator_trnid; + public int* actuator_actadr; + public int* actuator_actnum; + public int* actuator_group; + public byte* actuator_ctrllimited; + public byte* actuator_actlimited; + public double* actuator_ctrlrange; + public double* actuator_actrange; + public double* actuator_cranklength; + public int* sensor_type; + public int* sensor_objid; + public int* sensor_adr; + public int* name_bodyadr; + public int* name_jntadr; + public int* name_geomadr; + public int* name_siteadr; + public int* name_camadr; + public int* name_lightadr; + public int* name_eqadr; + public int* name_tendonadr; + public int* name_actuatoradr; + public char* names; +} + +[StructLayout(LayoutKind.Sequential)] +public unsafe struct data { + public mjWarningStat_ warning0; + public mjWarningStat_ warning1; + public mjWarningStat_ warning2; + public mjWarningStat_ warning3; + public mjWarningStat_ warning4; + public mjWarningStat_ warning5; + public mjWarningStat_ warning6; + public mjWarningStat_ warning7; + public int nefc; + public int ncon; + public double time; + public double* act; + public double* ctrl; + public double* xfrc_applied; + public double* sensordata; + public double* xpos; + public double* xquat; + public double* xmat; + public double* xipos; + public double* ximat; + public double* xanchor; + public double* xaxis; + public double* geom_xpos; + public double* geom_xmat; + public double* site_xpos; + public double* site_xmat; + public double* cam_xpos; + public double* cam_xmat; + public double* light_xpos; + public double* light_xdir; + public double* subtree_com; + public int* ten_wrapadr; + public int* ten_wrapnum; + public int* wrap_obj; + public double* wrap_xpos; + public byte* bvh_active; + public mjContact_* contact; + public double* efc_force; +} + +[StructLayout(LayoutKind.Sequential)] +public unsafe struct mjvSceneState_ { + public int nbuffer; + public void* buffer; + public int maxgeom; + public mjvScene_ plugincache; + public model model; + public data data; }public struct mjuiItem_ {}public struct mjfItemEnable {} // ----------------------------Function declarations---------------------------- @@ -3128,9 +3325,15 @@ public static unsafe extern void mjv_alignToCamera(double* res, double* vec, dou [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] public static unsafe extern void mjv_moveCamera(mjModel_* m, int action, double reldx, double reldy, mjvScene_* scn, mjvCamera_* cam); +[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] +public static unsafe extern void mjv_moveCameraFromState(mjvSceneState_* scnstate, int action, double reldx, double reldy, mjvScene_* scn, mjvCamera_* cam); + [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] public static unsafe extern void mjv_movePerturb(mjModel_* m, mjData_* d, int action, double reldx, double reldy, mjvScene_* scn, mjvPerturb_* pert); +[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] +public static unsafe extern void mjv_movePerturbFromState(mjvSceneState_* scnstate, int action, double reldx, double reldy, mjvScene_* scn, mjvPerturb_* pert); + [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] public static unsafe extern void mjv_moveModel(mjModel_* m, int action, double reldx, double reldy, double* roomup, mjvScene_* scn); @@ -3173,6 +3376,21 @@ public static unsafe extern void mjv_freeScene(mjvScene_* scn); [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] public static unsafe extern void mjv_updateScene(mjModel_* m, mjData_* d, mjvOption_* opt, mjvPerturb_* pert, mjvCamera_* cam, int catmask, mjvScene_* scn); +[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] +public static unsafe extern int mjv_updateSceneFromState(mjvSceneState_* scnstate, mjvOption_* opt, mjvPerturb_* pert, mjvCamera_* cam, int catmask, mjvScene_* scn); + +[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] +public static unsafe extern void mjv_defaultSceneState(mjvSceneState_* scnstate); + +[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] +public static unsafe extern void mjv_makeSceneState(mjModel_* m, mjData_* d, mjvSceneState_* scnstate, int maxgeom); + +[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] +public static unsafe extern void mjv_freeSceneState(mjvSceneState_* scnstate); + +[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] +public static unsafe extern void mjv_updateSceneState(mjModel_* m, mjData_* d, mjvSceneState_* scnstate); + [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] public static unsafe extern void mjv_addGeoms(mjModel_* m, mjData_* d, mjvOption_* opt, mjvPerturb_* pert, int catmask, mjvScene_* scn); @@ -3200,6 +3418,9 @@ public static unsafe extern void mjr_addAux(int index, int width, int height, in [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] public static unsafe extern void mjr_freeContext(mjrContext_* con); +[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] +public static unsafe extern void mjr_resizeOffscreen(int width, int height, mjrContext_* con); + [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] public static unsafe extern void mjr_uploadTexture(mjModel_* m, mjrContext_* con, int texid);