Merge branch 'deepmind:main' into usd-integration

This commit is contained in:
Abhishek Joshi
2023-09-06 14:04:36 -05:00
committed by GitHub
122 changed files with 4019 additions and 1524 deletions
+2
View File
@@ -59,6 +59,7 @@ set(MUJOCO_HEADERS
include/mujoco/mjmodel.h include/mujoco/mjmodel.h
include/mujoco/mjplugin.h include/mujoco/mjplugin.h
include/mujoco/mjrender.h include/mujoco/mjrender.h
include/mujoco/mjthread.h
include/mujoco/mjtnum.h include/mujoco/mjtnum.h
include/mujoco/mjui.h include/mujoco/mjui.h
include/mujoco/mjvisualize.h include/mujoco/mjvisualize.h
@@ -88,6 +89,7 @@ add_subdirectory(src/engine)
add_subdirectory(src/user) add_subdirectory(src/user)
add_subdirectory(src/xml) add_subdirectory(src/xml)
add_subdirectory(src/render) add_subdirectory(src/render)
add_subdirectory(src/thread)
add_subdirectory(src/ui) add_subdirectory(src/ui)
target_compile_definitions(mujoco PRIVATE _GNU_SOURCE CCD_STATIC_DEFINE MUJOCO_DLL_EXPORTS -DMC_IMPLEM_ENABLE) target_compile_definitions(mujoco PRIVATE _GNU_SOURCE CCD_STATIC_DEFINE MUJOCO_DLL_EXPORTS -DMC_IMPLEM_ENABLE)
+6 -7
View File
@@ -88,8 +88,10 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang
set(EXTRA_COMPILE_OPTIONS set(EXTRA_COMPILE_OPTIONS
-Werror -Werror
-Wall -Wall
-Wpedantic
-Wimplicit-fallthrough -Wimplicit-fallthrough
-Wunused -Wunused
-Wvla
-Wno-int-in-bool-context -Wno-int-in-bool-context
-Wno-sign-compare -Wno-sign-compare
-Wno-unknown-pragmas -Wno-unknown-pragmas
@@ -100,15 +102,12 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang
-Wno-maybe-uninitialized -Wno-maybe-uninitialized
) )
endif() endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT MSVC)
set(EXTRA_COMPILE_OPTIONS ${EXTRA_COMPILE_OPTIONS} -Wgnu-empty-initializer)
endif()
endif()
if(WIN32)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
endif() endif()
include(MujocoHarden) include(MujocoHarden)
set(EXTRA_COMPILE_OPTIONS ${EXTRA_COMPILE_OPTIONS} ${MUJOCO_HARDEN_COMPILE_OPTIONS}) set(EXTRA_COMPILE_OPTIONS ${EXTRA_COMPILE_OPTIONS} ${MUJOCO_HARDEN_COMPILE_OPTIONS})
set(EXTRA_LINK_OPTIONS ${EXTRA_LINK_OPTIONS} ${MUJOCO_HARDEN_LINK_OPTIONS}) set(EXTRA_LINK_OPTIONS ${EXTRA_LINK_OPTIONS} ${MUJOCO_HARDEN_LINK_OPTIONS})
if(WIN32)
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE)
endif()
+1 -33
View File
@@ -32,6 +32,7 @@ API function can be classified as:
- :ref:`Derivatives<Derivatives-api>`. - :ref:`Derivatives<Derivatives-api>`.
- :ref:`Plugin<Plugins-api>` related functions. - :ref:`Plugin<Plugins-api>` related functions.
- :ref:`Macros<Macros>`. - :ref:`Macros<Macros>`.
- :ref:`Thread<Thread>` related functions.
.. TODO(b/273075045): Better category-label namespacing. .. TODO(b/273075045): Better category-label namespacing.
@@ -42,39 +43,6 @@ API function can be classified as:
Macros Macros
^^^^^^ ^^^^^^
.. _mjMARKSTACK:
mjMARKSTACK
~~~~~~~~~~~
.. code-block:: C
#define mjMARKSTACK int _mark = d->pstack;
This macro is helpful when using the MuJoCo stack in custom computations. It works together with the next macro and the
:ref:`mj_stackAlloc` family of functions, and assumes that mjData\* d is defined. The use pattern is this:
.. code-block:: C
mjMARKSTACK;
mjtNum* temp = mj_stackAllocNum(d, 100);
// ... use temp as needed
mjFREESTACK;
.. _mjFREESTACK:
mjFREESTACK
~~~~~~~~~~~
.. code-block:: C
#define mjFREESTACK d->pstack = _mark;
Reset the MuJoCo stack pointer to the variable \_mark, normally saved by mjMARKSTACK.
.. _mjDISABLED: .. _mjDISABLED:
mjDISABLED mjDISABLED
+33
View File
@@ -712,7 +712,26 @@ Options for configuring the automatic :ref:`actuator length-range computation<CL
.. mujoco-include:: mjLROpt .. mujoco-include:: mjLROpt
.. _mjTask:
mjTask
~~~~~~
This is a representation of a task to be run asynchronously inside of an :ref:`mjThreadPool` . It is created in the
:ref:`mju_threadPoolEnqueue` method of the :ref:`mjThreadPool` and is used to join the task at completion.
.. mujoco-include:: mjTask
.. _mjThreadPool:
mjThreadPool
~~~~~~~~~~~~
This is the data structure of the threadpool. It can only be constructed programmatically, and does not
have an analog in MJCF. In order to enable multi-threaded calculations, a pointer to an existing :ref:`mjThreadPool`
should be assigned to the ``mjData.threadpool``.
.. mujoco-include:: mjThreadPool
.. _tyStatStructure: .. _tyStatStructure:
@@ -1155,6 +1174,20 @@ mjfGetResourceDir
This callback is for returning the directory of a resource, by setting dir to the directory string with ndir being size This callback is for returning the directory of a resource, by setting dir to the directory string with ndir being size
of directory string. of directory string.
.. _mjfResourceModified:
mjfResourceModified
~~~~~~~~~~~~~~~~~~~
.. code-block:: C
typedef int (*mjfResourceModified)(const mjResource* resource);
This callback is for checking if a resource was modified since it was last read.
Returns positive value if the resource was modified since last open, 0 if resource was not modified,
and negative value if inconclusive.
.. _tyNotes: .. _tyNotes:
Notes Notes
+61 -1
View File
@@ -1232,6 +1232,25 @@ mj_resetDataKeyframe
Reset data, set fields from specified keyframe. Reset data, set fields from specified keyframe.
.. _mj_markStack:
mj_markStack
~~~~~~~~~~~~
.. mujoco-include:: mj_markStack
Mark a new frame on the :ref:`mjData` stack.
.. _mj_freeStack:
mj_freeStack
~~~~~~~~~~~~
.. mujoco-include:: mj_freeStack
Free the current :ref:`mjData` stack frame. All pointers returned by mj_stackAlloc since the last call
to mj_markStack must no longer be used afterwards.
.. _mj_stackAlloc: .. _mj_stackAlloc:
mj_stackAlloc mj_stackAlloc
@@ -1239,7 +1258,8 @@ mj_stackAlloc
.. mujoco-include:: mj_stackAlloc .. mujoco-include:: mj_stackAlloc
Allocate a specific number of bytes on :ref:`mjData` stack. Call mju_error on stack overflow. Allocate a number of bytes on :ref:`mjData` stack at a specific alignment.
Call mju_error on stack overflow.
.. _mj_stackAllocNum: .. _mj_stackAllocNum:
@@ -3413,3 +3433,43 @@ mjp_getResourceProviderAtSlot
Look up a resource provider by slot number returned by mjp_registerResourceProvider. Look up a resource provider by slot number returned by mjp_registerResourceProvider.
If invalid slot number, return NULL. If invalid slot number, return NULL.
.. _Thread:
Thread
^^^^^^
.. _mju_threadPoolCreate:
mju_threadPoolCreate
~~~~~~~~~~~~~~~~~~~~
.. mujoco-include:: mju_threadPoolCreate
Creates a thread pool with the specified number of threads running.
.. _mju_threadPoolEnqueue:
mju_threadPoolEnqueue
~~~~~~~~~~~~~~~~~~~~~
.. mujoco-include:: mju_threadPoolEnqueue
Enqueues a task in a thread pool.
.. _mju_taskJoin:
mju_taskJoin
~~~~~~~~~~~~
.. mujoco-include:: mju_taskJoin
Waits for a task to complete.
.. _mju_threadPoolDestroy:
mju_threadPoolDestroy
~~~~~~~~~~~~~~~~~~~~~
.. mujoco-include:: mju_threadPoolDestroy
Destroys a thread pool.
+17
View File
@@ -1972,6 +1972,18 @@ adjust it properly through the XML.
improvement between two iterations. For CG and Newton, it is applied to the smaller of the cost improvement and the improvement between two iterations. For CG and Newton, it is applied to the smaller of the cost improvement and the
gradient norm. Set the tolerance to 0 to disable early termination. gradient norm. Set the tolerance to 0 to disable early termination.
.. _option-ls_iterations:
:at:`ls_iterations`: :at-val:`int, "50"`
Maximum number of linesearch iterations performed by CG/Newton constraint solvers. Ensures that at most
:ref:`iterations<option-iterations>` times :ref:`ls_iterations<option-ls_iterations>` linesearch iterations are
performed during each constraint solve.
.. _option-ls_tolerance:
:at:`ls_tolerance`: :at-val:`real, "0.01"`
Tolerance threshold used for early termination of the linesearch algorithm.
.. _option-noslip_iterations: .. _option-noslip_iterations:
:at:`noslip_iterations`: :at-val:`int, "0"` :at:`noslip_iterations`: :at-val:`int, "0"`
@@ -3319,6 +3331,11 @@ coordinates results in compiler error. See :ref:`CComposite` in the modeling gui
:at:`vertex`: :at-val:`real(3*nvert), optional` :at:`vertex`: :at-val:`real(3*nvert), optional`
Vertex 3D positions in global coordinates (cable only). Vertex 3D positions in global coordinates (cable only).
.. _body-composite-face:
:at:`face`: :at-val:`real(3*nvert), optional`
Face connectivity of the vertices (shell only).
.. _body-composite-initial: .. _body-composite-initial:
:at:`initial`: :at-val:`[free, ball, none], "0"` :at:`initial`: :at-val:`[free, ball, none], "0"`
+10 -6
View File
@@ -218,15 +218,17 @@
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`timestep<option-timestep>` | :ref:`apirate<option-apirate>` | :ref:`impratio<option-impratio>` | :ref:`tolerance<option-tolerance>` | | | | | | :ref:`timestep<option-timestep>` | :ref:`apirate<option-apirate>` | :ref:`impratio<option-impratio>` | :ref:`tolerance<option-tolerance>` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`noslip_tolerance<option-noslip_tolerance>` | :ref:`mpr_tolerance<option-mpr_tolerance>` | :ref:`gravity<option-gravity>` | :ref:`wind<option-wind>` | | | | | | :ref:`ls_tolerance<option-ls_tolerance>` | :ref:`noslip_tolerance<option-noslip_tolerance>` | :ref:`mpr_tolerance<option-mpr_tolerance>` | :ref:`gravity<option-gravity>` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`magnetic<option-magnetic>` | :ref:`density<option-density>` | :ref:`viscosity<option-viscosity>` | :ref:`o_margin<option-o_margin>` | | | | | | :ref:`wind<option-wind>` | :ref:`magnetic<option-magnetic>` | :ref:`density<option-density>` | :ref:`viscosity<option-viscosity>` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`o_solref<option-o_solref>` | :ref:`o_solimp<option-o_solimp>` | :ref:`integrator<option-integrator>` | :ref:`collision<option-collision>` | | | | | | :ref:`o_margin<option-o_margin>` | :ref:`o_solref<option-o_solref>` | :ref:`o_solimp<option-o_solimp>` | :ref:`integrator<option-integrator>` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`cone<option-cone>` | :ref:`jacobian<option-jacobian>` | :ref:`solver<option-solver>` | :ref:`iterations<option-iterations>` | | | | | | :ref:`collision<option-collision>` | :ref:`cone<option-cone>` | :ref:`jacobian<option-jacobian>` | :ref:`solver<option-solver>` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`noslip_iterations<option-noslip_iterations>` | :ref:`mpr_iterations<option-mpr_iterations>` | :ref:`sdf_iterations<option-sdf_iterations>` | :ref:`sdf_initpoints<option-sdf_initpoints>` | | | | | | :ref:`iterations<option-iterations>` | :ref:`ls_iterations<option-ls_iterations>` | :ref:`noslip_iterations<option-noslip_iterations>` | :ref:`mpr_iterations<option-mpr_iterations>` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`sdf_iterations<option-sdf_iterations>` | :ref:`sdf_initpoints<option-sdf_initpoints>` | | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_| option |br| |_| |L| | | .. table:: | | |_| option |br| |_| |L| | | .. table:: |
@@ -390,7 +392,9 @@
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`offset<body-composite-offset>` | :ref:`flatinertia<body-composite-flatinertia>` | :ref:`solrefsmooth<body-composite-solrefsmooth>` | :ref:`solimpsmooth<body-composite-solimpsmooth>` | | | | | | :ref:`offset<body-composite-offset>` | :ref:`flatinertia<body-composite-flatinertia>` | :ref:`solrefsmooth<body-composite-solrefsmooth>` | :ref:`solimpsmooth<body-composite-solimpsmooth>` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`vertex<body-composite-vertex>` | :ref:`initial<body-composite-initial>` | :ref:`curve<body-composite-curve>` | :ref:`size<body-composite-size>` | | | | | | :ref:`vertex<body-composite-vertex>` | :ref:`face<body-composite-face>` | :ref:`initial<body-composite-initial>` | :ref:`curve<body-composite-curve>` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`size<body-composite-size>` | | | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_2| composite |br| |_2| |L| | | .. table:: | | |_2| composite |br| |_2| |L| | | .. table:: |
+46 -25
View File
@@ -5,55 +5,76 @@ Changelog
Upcoming version (not yet released) Upcoming version (not yet released)
----------------------------------- -----------------------------------
General New features
^^^^^^^ ^^^^^^^^^^^^
.. youtube:: Vc1tq0fFvQA
:align: right
:width: 240px
1. Added constraint island discovery in :ref:`mj_island`. Constraint islands are disjoint sets of constraints
and degrees-of-freedom that do not interact. In a future release the constraint solver will be refactored to
exploit the disjoint structure. Island discovery can be activated using a new :ref:`enable flag<option-flag-island>`
which will be removed after the refactor. If island discovery is enabled, geoms, contacts and
tendons will be colored according to the corresponding island, see video.
.. youtube:: QewlEqIZi1o .. youtube:: QewlEqIZi1o
:align: right :align: right
:width: 240px :width: 240px
1. Added new signed distance field (SDF) collision primitive. SDFs can take any shape and are not constrained to be 2. Added new signed distance field (SDF) collision primitive. SDFs can take any shape and are not constrained to be
convex. Collision points are found by minimizing the maximum of the two colliding SDFs via gradient descent. convex. Collision points are found by minimizing the maximum of the two colliding SDFs via gradient descent.
- Added new SDF plugin for defining implicit geometries. The plugin must define methods computing an SDF and its - Added new SDF plugin for defining implicit geometries. The plugin must define methods computing an SDF and its
gradient at query points See the :ref:`documentation<exWriting>` for more details. gradient at query points See the :ref:`documentation<exWriting>` for more details.
.. youtube:: Vc1tq0fFvQA 3. Added :ref:`mjThreadPool` and :ref:`mjTask` which allow for multi-threaded operations within the MuJoCo engine
:align: right pipeline.
:width: 240px
#. Added constraint island discovery in :ref:`mj_island`. Constraint islands are disjoint sets of constraints General
and degrees-of-freedom that do not interact. In a future release the constraint solver will be refactored to ^^^^^^^
exploit the disjoint structure. Island discovery can be activated using a new :ref:`enable flag<option-flag-island>`
which will be removed after the refactor. If island discovery is enabled, geoms, contacts and .. admonition:: Breaking API changes
tendons will be colored according to the corresponding island, see video. :class: attention
#. Added a new :ref:`dyntype<actuator-general-dyntype>`, ``filterexact``, which updates first-order filter states with
4. Removed the macros ``mjMARKSTACK`` and ``mjFREESTACK``.
**Migration:** These macros have been replaced by new functions :ref:`mj_markStack` and
:ref:`mj_freeStack`. These functions manage the :ref:`mjData stack<siStack>` in a fully encapsulated way (i.e.,
without introducing a local variable at the call site).
5. Changed the function :ref:`mj_stackAlloc` to allocate an arbitrary number of bytes, rather than in multiples of
``sizeof(mjtNum)``, and added an additional argument for specifying the alignment of the returned pointer.
**Migration:** The functionality for allocating ``mjtNum`` arrays is available via :ref:`mj_stackAllocNum`.
6. Renamed the ``nstack`` field in :ref:`mjModel` and :ref:`mjData` to ``narena``. Changed ``narena``, ``pstack``,
and ``maxuse_stack`` to count number of bytes rather than number of :ref:`mjtNum` |-| s.
7. Added a new :ref:`dyntype<actuator-general-dyntype>`, ``filterexact``, which updates first-order filter states with
the exact formula rather than with Euler integration. the exact formula rather than with Euler integration.
#. Added an actuator attribute, :ref:`actearly<actuator-general-actearly>`, which uses semi-implicit integration for 8. Added an actuator attribute, :ref:`actearly<actuator-general-actearly>`, which uses semi-implicit integration for
actuator forces: using the next step's actuator state to compute the current actuator forces at the current timestep. actuator forces: using the next step's actuator state to compute the current actuator forces at the current timestep.
#. Renamed ``actuatorforcerange`` and ``actuatorforcelimited``, introduced in the previous version to 9. Renamed ``actuatorforcerange`` and ``actuatorforcelimited``, introduced in the previous version to
:ref:`actuatorfrcrange<body-joint-actuatorfrcrange>` and :ref:`actuatorfrcrange<body-joint-actuatorfrcrange>` and
:ref:`actuatorfrclimited<body-joint-actuatorfrclimited>`, respectively. :ref:`actuatorfrclimited<body-joint-actuatorfrclimited>`, respectively.
#. Added the flag :ref:`eulerdamp<option-flag-eulerdamp>`, which disables implicit integration of joint damping in the 10. Added the flag :ref:`eulerdamp<option-flag-eulerdamp>`, which disables implicit integration of joint damping in the
Euler integrator. See the :ref:`Numerical Integration<geIntegration>` section for more details. Euler integrator. See the :ref:`Numerical Integration<geIntegration>` section for more details.
#. Added the flag :ref:`invdiscrete<option-flag-invdiscrete>`, which enables discrete-time inverse dynamics for all 11. Added the flag :ref:`invdiscrete<option-flag-invdiscrete>`, which enables discrete-time inverse dynamics for all
:ref:`integrators<option-integrator>` other than ``RK4``. See the flag documentation for more details. :ref:`integrators<option-integrator>` other than ``RK4``. See the flag documentation for more details.
#. Changed the function ``mj_stackAlloc`` to allocate an arbitrary number of bytes, rather than in multiples of 12. Added :ref:`ls_iterations<option-ls_iterations>` and :ref:`ls_iterations<option-ls_tolerance>` options for adjusting
``sizeof(mjtNum)``, and add an additional argument for specifying the alignment of the returned pointer. The existing linesearch stopping criteria in CG and Newton solvers. This can be useful for performance tuning.
functionality of allocating ``mjtNum`` arrays is still available through the new function ``mj_stackAllocNum``.
#. Renamed the ``nstack`` field in ``mjModel`` and ``mjData`` to ``narena``. Changed ``narena``, ``pstack``, and
``maxuse_stack`` to count number of bytes rather than number of ``mjtNum``s.
Python bindings Python bindings
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
10. Fixed `#870 <https://github.com/deepmind/mujoco/issues/870>`__ where calling ``update_scene`` with an invalid 13. Fixed `#870 <https://github.com/deepmind/mujoco/issues/870>`__ where calling ``update_scene`` with an invalid
camera name used the default camera. camera name used the default camera.
Bug fixes Bug fixes
^^^^^^^^^ ^^^^^^^^^
11. Fixed a bug that was causing the geom margins to be ignored during the midphase. 14. Fixed a bug that was causing the geom margins to be ignored during the midphase.
Version 2.3.7 (July 20, 2023) Version 2.3.7 (July 20, 2023)
+50 -36
View File
@@ -130,6 +130,7 @@ struct mjData_ {
// stack pointer // stack pointer
size_t pstack; // first available mjtNum address in stack size_t pstack; // first available mjtNum address in stack
size_t pbase; // value of pstack when mj_markStack was last called
// arena pointer // arena pointer
size_t parena; // first available byte in arena size_t parena; // first available byte in arena
@@ -374,6 +375,9 @@ struct mjData_ {
mjtNum* efc_b; // linear cost term: J*qacc_smooth - aref (nefc x 1) mjtNum* efc_b; // linear cost term: J*qacc_smooth - aref (nefc x 1)
mjtNum* efc_force; // constraint force in constraint space (nefc x 1) mjtNum* efc_force; // constraint force in constraint space (nefc x 1)
int* efc_state; // constraint state (mjtConstraintState) (nefc x 1) int* efc_state; // constraint state (mjtConstraintState) (nefc x 1)
// ThreadPool for multithreaded operations
uintptr_t threadpool;
}; };
typedef struct mjData_ mjData; typedef struct mjData_ mjData;
typedef enum mjtDisableBit_ { // disable default feature bitflags typedef enum mjtDisableBit_ { // disable default feature bitflags
@@ -655,28 +659,13 @@ struct mjLROpt_ { // options for mj_setLengthRange()
mjtNum tolrange; // convergence tolerance (relative to range) mjtNum tolrange; // convergence tolerance (relative to range)
}; };
typedef struct mjLROpt_ mjLROpt; typedef struct mjLROpt_ mjLROpt;
struct mjVFS_ { // virtual file system for loading from memory struct mjVFS_ { // virtual file system for loading from memory
int nfile; // number of files present int nfile; // number of files present
char filename[mjMAXVFS][mjMAXVFSNAME]; // file name without path char filename[mjMAXVFS][mjMAXVFSNAME]; // file name without path
int filesize[mjMAXVFS]; // file size in bytes size_t filesize[mjMAXVFS]; // file size in bytes
void* filedata[mjMAXVFS]; // buffer with file data void* filedata[mjMAXVFS]; // buffer with file data
}; };
typedef struct mjVFS_ mjVFS; typedef struct mjVFS_ mjVFS;
struct mjResource_ {
char* name; // name of resource (filename, etc)
void* data; // opaque data pointer
const void* provider_data; // opaque resource provider data
// reading callback from resource provider
int (*read)(struct mjResource_* resource, const void** buffer);
// closing callback from resource provider
void (*close)(struct mjResource_* resource);
// getdir callback from resource provider
void (*getdir)(struct mjResource_* resource, const char** dir, int* ndir);
};
typedef struct mjResource_ mjResource;
struct mjOption_ { // physics options struct mjOption_ { // physics options
// timing parameters // timing parameters
mjtNum timestep; // timestep mjtNum timestep; // timestep
@@ -685,6 +674,7 @@ struct mjOption_ { // physics options
// solver parameters // solver parameters
mjtNum impratio; // ratio of friction-to-normal contact impedance mjtNum impratio; // ratio of friction-to-normal contact impedance
mjtNum tolerance; // main solver tolerance mjtNum tolerance; // main solver tolerance
mjtNum ls_tolerance; // CG/Newton linesearch tolerance
mjtNum noslip_tolerance; // noslip solver tolerance mjtNum noslip_tolerance; // noslip solver tolerance
mjtNum mpr_tolerance; // MPR solver tolerance mjtNum mpr_tolerance; // MPR solver tolerance
@@ -707,6 +697,7 @@ struct mjOption_ { // physics options
int jacobian; // type of Jacobian (mjtJacobian) int jacobian; // type of Jacobian (mjtJacobian)
int solver; // solver algorithm (mjtSolver) int solver; // solver algorithm (mjtSolver)
int iterations; // maximum number of main solver iterations int iterations; // maximum number of main solver iterations
int ls_iterations; // maximum number of CG/Newton linesearch iterations
int noslip_iterations; // maximum number of noslip solver iterations int noslip_iterations; // maximum number of noslip solver iterations
int mpr_iterations; // maximum number of MPR solver iterations int mpr_iterations; // maximum number of MPR solver iterations
int disableflags; // bit flags for disabling standard features int disableflags; // bit flags for disabling standard features
@@ -1262,15 +1253,22 @@ struct mjModel_ {
int* names_map; // internal hash map of names (nnames_map x 1) int* names_map; // internal hash map of names (nnames_map x 1)
}; };
typedef struct mjModel_ mjModel; typedef struct mjModel_ mjModel;
struct mjpResourceProvider_ { struct mjResource_ {
const char* prefix; // prefix for match against a resource name char* name; // name of resource (filename, etc)
mjfOpenResource open; // opening callback void* data; // opaque data pointer
mjfReadResource read; // reading callback const struct mjpResourceProvider* provider; // pointer to the provider
mjfCloseResource close; // closing callback
mjfGetResourceDir getdir; // getdir callback (optional)
void* data; // opaque data pointer (resource invariant)
}; };
typedef struct mjpResourceProvider_ mjpResourceProvider; typedef struct mjResource_ mjResource;
struct mjpResourceProvider {
const char* prefix; // prefix for match against a resource name
mjfOpenResource open; // opening callback
mjfReadResource read; // reading callback
mjfCloseResource close; // closing callback
mjfGetResourceDir getdir; // get directory callback (optional)
mjfResourceModified modified; // resource modified callback (optional)
void* data; // opaque data pointer (resource invariant)
};
typedef struct mjpResourceProvider mjpResourceProvider;
typedef enum mjtPluginCapabilityBit_ { typedef enum mjtPluginCapabilityBit_ {
mjPLUGIN_ACTUATOR = 1<<0, // actuator forces mjPLUGIN_ACTUATOR = 1<<0, // actuator forces
mjPLUGIN_SENSOR = 1<<1, // sensor measurements mjPLUGIN_SENSOR = 1<<1, // sensor measurements
@@ -1443,6 +1441,14 @@ struct mjrContext_ { // custom OpenGL context
int readPixelFormat; // default color pixel format for mjr_readPixels int readPixelFormat; // default color pixel format for mjr_readPixels
}; };
typedef struct mjrContext_ mjrContext; typedef struct mjrContext_ mjrContext;
struct mjTask_ {
char buffer[24];
};
typedef struct mjTask_ mjTask;
struct mjThreadPool_ {
char buffer[6208];
};
typedef struct mjThreadPool_ mjThreadPool;
typedef enum mjtButton_ { // mouse button typedef enum mjtButton_ { // mouse button
mjBUTTON_NONE = 0, // no button mjBUTTON_NONE = 0, // no button
mjBUTTON_LEFT, // left button mjBUTTON_LEFT, // left button
@@ -2213,6 +2219,8 @@ mjData* mj_copyData(mjData* dest, const mjModel* m, const mjData* src);
void mj_resetData(const mjModel* m, mjData* d); void mj_resetData(const mjModel* m, mjData* d);
void mj_resetDataDebug(const mjModel* m, mjData* d, unsigned char debug_value); void mj_resetDataDebug(const mjModel* m, mjData* d, unsigned char debug_value);
void mj_resetDataKeyframe(const mjModel* m, mjData* d, int key); void mj_resetDataKeyframe(const mjModel* m, mjData* d, int key);
void mj_markStack(mjData* d);
void mj_freeStack(mjData* d);
void* mj_stackAlloc(mjData* d, size_t bytes, size_t alignment); void* mj_stackAlloc(mjData* d, size_t bytes, size_t alignment);
mjtNum* mj_stackAllocNum(mjData* d, int size); mjtNum* mj_stackAllocNum(mjData* d, int size);
int* mj_stackAllocInt(mjData* d, int size); int* mj_stackAllocInt(mjData* d, int size);
@@ -2276,8 +2284,8 @@ int mj_addContact(const mjModel* m, mjData* d, const mjContact* con);
int mj_isPyramidal(const mjModel* m); int mj_isPyramidal(const mjModel* m);
int mj_isSparse(const mjModel* m); int mj_isSparse(const mjModel* m);
int mj_isDual(const mjModel* m); int mj_isDual(const mjModel* m);
void mj_mulJacVec(const mjModel* m, mjData* d, mjtNum* res, const mjtNum* vec); void mj_mulJacVec(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec);
void mj_mulJacTVec(const mjModel* m, mjData* d, mjtNum* res, const mjtNum* vec); void mj_mulJacTVec(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec);
void mj_jac(const mjModel* m, const mjData* d, mjtNum* jacp, mjtNum* jacr, void mj_jac(const mjModel* m, const mjData* d, mjtNum* jacp, mjtNum* jacr,
const mjtNum point[3], int body); const mjtNum point[3], int body);
void mj_jacBody(const mjModel* m, const mjData* d, mjtNum* jacp, mjtNum* jacr, int body); void mj_jacBody(const mjModel* m, const mjData* d, mjtNum* jacp, mjtNum* jacr, int body);
@@ -2312,7 +2320,7 @@ const char* mj_getPluginConfig(const mjModel* m, int plugin_id, const char* attr
void mj_loadPluginLibrary(const char* path); void mj_loadPluginLibrary(const char* path);
void mj_loadAllPluginLibraries(const char* directory, mjfPluginLibraryLoadCallback callback); void mj_loadAllPluginLibraries(const char* directory, mjfPluginLibraryLoadCallback callback);
int mj_version(void); int mj_version(void);
const char* mj_versionString(); const char* mj_versionString(void);
void mj_multiRay(const mjModel* m, mjData* d, const mjtNum pnt[3], const mjtNum* vec, void mj_multiRay(const mjModel* m, mjData* d, const mjtNum pnt[3], const mjtNum* vec,
const mjtByte* geomgroup, mjtByte flg_static, int bodyexclude, const mjtByte* geomgroup, mjtByte flg_static, int bodyexclude,
int* geomid, mjtNum* dist, int nray, mjtNum cutoff); int* geomid, mjtNum* dist, int nray, mjtNum cutoff);
@@ -2450,7 +2458,7 @@ void mju_addTo3(mjtNum res[3], const mjtNum vec[3]);
void mju_subFrom3(mjtNum res[3], const mjtNum vec[3]); void mju_subFrom3(mjtNum res[3], const mjtNum vec[3]);
void mju_addToScl3(mjtNum res[3], const mjtNum vec[3], mjtNum scl); void mju_addToScl3(mjtNum res[3], const mjtNum vec[3], mjtNum scl);
void mju_addScl3(mjtNum res[3], const mjtNum vec1[3], const mjtNum vec2[3], mjtNum scl); void mju_addScl3(mjtNum res[3], const mjtNum vec1[3], const mjtNum vec2[3], mjtNum scl);
mjtNum mju_normalize3(mjtNum res[3]); mjtNum mju_normalize3(mjtNum vec[3]);
mjtNum mju_norm3(const mjtNum vec[3]); mjtNum mju_norm3(const mjtNum vec[3]);
mjtNum mju_dot3(const mjtNum vec1[3], const mjtNum vec2[3]); mjtNum mju_dot3(const mjtNum vec1[3], const mjtNum vec2[3]);
mjtNum mju_dist3(const mjtNum pos1[3], const mjtNum pos2[3]); mjtNum mju_dist3(const mjtNum pos1[3], const mjtNum pos2[3]);
@@ -2460,10 +2468,10 @@ void mju_cross(mjtNum res[3], const mjtNum a[3], const mjtNum b[3]);
void mju_zero4(mjtNum res[4]); void mju_zero4(mjtNum res[4]);
void mju_unit4(mjtNum res[4]); void mju_unit4(mjtNum res[4]);
void mju_copy4(mjtNum res[4], const mjtNum data[4]); void mju_copy4(mjtNum res[4], const mjtNum data[4]);
mjtNum mju_normalize4(mjtNum res[4]); mjtNum mju_normalize4(mjtNum vec[4]);
void mju_zero(mjtNum* res, int n); void mju_zero(mjtNum* res, int n);
void mju_fill(mjtNum* res, mjtNum val, int n); void mju_fill(mjtNum* res, mjtNum val, int n);
void mju_copy(mjtNum* res, const mjtNum* data, int n); void mju_copy(mjtNum* res, const mjtNum* vec, int n);
mjtNum mju_sum(const mjtNum* vec, int n); mjtNum mju_sum(const mjtNum* vec, int n);
mjtNum mju_L1(const mjtNum* vec, int n); mjtNum mju_L1(const mjtNum* vec, int n);
void mju_scl(mjtNum* res, const mjtNum* vec, mjtNum scl, int n); void mju_scl(mjtNum* res, const mjtNum* vec, mjtNum scl, int n);
@@ -2569,12 +2577,18 @@ void mjd_quatIntegrate(const mjtNum vel[3], mjtNum scale,
mjtNum Dquat[9], mjtNum Dvel[9], mjtNum Dscale[3]); mjtNum Dquat[9], mjtNum Dvel[9], mjtNum Dscale[3]);
void mjp_defaultPlugin(mjpPlugin* plugin); void mjp_defaultPlugin(mjpPlugin* plugin);
int mjp_registerPlugin(const mjpPlugin* plugin); int mjp_registerPlugin(const mjpPlugin* plugin);
int mjp_pluginCount(); int mjp_pluginCount(void);
const mjpPlugin* mjp_getPlugin(const char* name, int* slot); const mjpPlugin* mjp_getPlugin(const char* name, int* slot);
const mjpPlugin* mjp_getPluginAtSlot(int slot); const mjpPlugin* mjp_getPluginAtSlot(int slot);
void mjp_defaultResourceProvider(mjpResourceProvider* provider); void mjp_defaultResourceProvider(mjpResourceProvider* provider);
int mjp_registerResourceProvider(const mjpResourceProvider* provider); int mjp_registerResourceProvider(const mjpResourceProvider* provider);
int mjp_resourceProviderCount(); int mjp_resourceProviderCount(void);
const mjpResourceProvider* mjp_getResourceProvider(const char* resource_name); const mjpResourceProvider* mjp_getResourceProvider(const char* resource_name);
const mjpResourceProvider* mjp_getResourceProviderAtSlot(int slot); const mjpResourceProvider* mjp_getResourceProviderAtSlot(int slot);
mjThreadPool* mju_threadPoolCreate(size_t number_of_threads);
void mju_threadPoolEnqueue(
ThreadPool* thread_pool, mjTask* task, void*(start_routine)(void*),
id* args);
void mju_taskJoin(mjTask* task);
void mju_threadPoolDestroy(mjThreadPool* thread_pool);
// NOLINTEND // NOLINTEND
+227 -174
View File
@@ -239,8 +239,7 @@ The function :ref:`mj_step` is the top-level function which advances the simulat
of course is just a passive dynamical system. Things get more interesting when the user specifies controls or applies of course is just a passive dynamical system. Things get more interesting when the user specifies controls or applies
forces and starts interacting with the system. forces and starts interacting with the system.
Next we provide a more elaborate example illustrating several features of MJCF. Next we provide a more elaborate example illustrating several features of MJCF. Consider the following
`example.xml <_static/example.xml>`__: `example.xml <_static/example.xml>`__:
.. code:: xml .. code:: xml
@@ -332,18 +331,24 @@ XML file, default values are used. The options are designed such that the user c
simulation time step. Within a time step however none of the options should be changed. simulation time step. Within a time step however none of the options should be changed.
``mjOption`` ``mjOption``
This structure contains all options that affect the physics simulation. It is used to select algorithms and set their ^^^^^^^^^^^^
parameters, enable and disable different portions of the simulation pipeline, and adjust system-level physical
properties such as gravity. This structure contains all options that affect the physics simulation. It is used to select algorithms and set their
parameters, enable and disable different portions of the simulation pipeline, and adjust system-level physical
properties such as gravity.
``mjVisual`` ``mjVisual``
This structure contains all visualization options. There are additional OpenGL rendering options, but these are ^^^^^^^^^^^^
session-dependent and are not part of the model.
This structure contains all visualization options. There are additional OpenGL rendering options, but these are
session-dependent and are not part of the model.
``mjStatistic`` ``mjStatistic``
This structure contains statistics about the model which are computed by the compiler: average body mass, spatial ^^^^^^^^^^^^^^^
extent of the model etc. It is included for information purposes, and also because the visualizer uses it for
automatic scaling. This structure contains statistics about the model which are computed by the compiler: average body mass, spatial
extent of the model etc. It is included for information purposes, and also because the visualizer uses it for
automatic scaling.
.. _Assets: .. _Assets:
@@ -357,51 +362,61 @@ purpose of including an asset is to reference it, and referencing can only be do
undefined. undefined.
Mesh Mesh
MuJoCo can load triangulated meshes from OBJ files and binary STL. Software such as `MeshLab ^^^^
<https://www.meshlab.net/>`__ can be used to convert from other formats. While any collection of triangles can be
loaded and visualized as a mesh, the collision detector works with the convex hull. There are compile-time options MuJoCo can load triangulated meshes from OBJ files and binary STL. Software such as `MeshLab
for scaling the mesh, as well as fitting a primitive geometric shape to it. The mesh can also be used to <https://www.meshlab.net/>`__ can be used to convert from other formats. While any collection of triangles can be
automatically infer inertial properties -- by treating it as a union of triangular pyramids and combining their loaded and visualized as a mesh, the collision detector works with the convex hull. There are compile-time options
masses and inertias. Note that meshes have no color, instead the mesh is colored using the material properties of the for scaling the mesh, as well as fitting a primitive geometric shape to it. The mesh can also be used to
referencing geom. In contrast, all spatial properties are determined by the mesh data. MuJoCo supports both OBJ and a automatically infer inertial properties -- by treating it as a union of triangular pyramids and combining their
custom binary file format for normals and texture coordinates. Meshes can also be embedded directly in the XML. masses and inertias. Note that meshes have no color, instead the mesh is colored using the material properties of the
referencing geom. In contrast, all spatial properties are determined by the mesh data. MuJoCo supports both OBJ and a
custom binary file format for normals and texture coordinates. Meshes can also be embedded directly in the XML.
Skin Skin
Skinned meshes (or skins) are meshes whose shape can deform at runtime. Their vertices are attached to rigid bodies ^^^^
(called bones in this context) and each vertex can belong to multiple bones, resulting in smooth deformations of the
skin. Skins are purely visualization objects and do not affect the physics, but nevertheless they can enhance visual Skinned meshes (or skins) are meshes whose shape can deform at runtime. Their vertices are attached to rigid bodies
realism significantly. Skins can be loaded from custom binary files, or embedded directly in the XML, similar to (called bones in this context) and each vertex can belong to multiple bones, resulting in smooth deformations of the
meshes. When generating composite flexible objects automatically, the model compiler also generates skins for these skin. Skins are purely visualization objects and do not affect the physics, but nevertheless they can enhance visual
objects. realism significantly. Skins can be loaded from custom binary files, or embedded directly in the XML, similar to
meshes. When generating composite flexible objects automatically, the model compiler also generates skins for these
objects.
Height field Height field
Height fields can be loaded from PNG files (converted to gray-scale internally) or from files in a custom binary ^^^^^^^^^^^^
format described later. A height field is a rectangular grid of elevation data. The compiler normalizes the data to
the range [0-1]. The actual spatial extent of the height field is then determined by the size parameters of the Height fields can be loaded from PNG files (converted to gray-scale internally) or from files in a custom binary
referencing geom. Height fields can only be referenced from geoms that are attached to the world body. For rendering format described later. A height field is a rectangular grid of elevation data. The compiler normalizes the data to
and collision detection purposes, the grid rectangles are automatically triangulated, thus the height field is the range [0-1]. The actual spatial extent of the height field is then determined by the size parameters of the
treated as a union of triangular prisms. Collision detection with such a composite object can in principle generate a referencing geom. Height fields can only be referenced from geoms that are attached to the world body. For rendering
large number of contact points for a single geom pair. If that happens, only the first 64 contact points are kept. and collision detection purposes, the grid rectangles are automatically triangulated, thus the height field is
The rationale is that height fields should be used to model terrain maps whose spatial features are large compared to treated as a union of triangular prisms. Collision detection with such a composite object can in principle generate a
the other objects in the simulation, so the number of contacts will be small for well-designed models. large number of contact points for a single geom pair. If that happens, only the first 64 contact points are kept.
The rationale is that height fields should be used to model terrain maps whose spatial features are large compared to
the other objects in the simulation, so the number of contacts will be small for well-designed models.
Texture Texture
Textures can be loaded from PNG files or synthesized by the compiler based on user-defined procedural parameters. ^^^^^^^
There is also the option to leave the texture empty at model creation time and change it later at runtime -- so as to
render video in a MuJoCo simulation, or create other dynamic effects. The visualizer supports two types of texture Textures can be loaded from PNG files or synthesized by the compiler based on user-defined procedural parameters.
mapping: 2D and cube. 2D mapping is useful for planes and height fields. Cube mapping is useful for "shrink-wrapping" There is also the option to leave the texture empty at model creation time and change it later at runtime -- so as to
textures around 3D objects without having to specify texture coordinates. It is also used to create a skybox. The six render video in a MuJoCo simulation, or create other dynamic effects. The visualizer supports two types of texture
sides of a cube maps can be loaded from separate image files, or from one composite image file, or generated by mapping: 2D and cube. 2D mapping is useful for planes and height fields. Cube mapping is useful for "shrink-wrapping"
repeating the same image. Unlike all other assets which are referenced directly from model elements, textures can textures around 3D objects without having to specify texture coordinates. It is also used to create a skybox. The six
only be referenced from another asset (namely material) which is then referenced from model elements. sides of a cube maps can be loaded from separate image files, or from one composite image file, or generated by
repeating the same image. Unlike all other assets which are referenced directly from model elements, textures can
only be referenced from another asset (namely material) which is then referenced from model elements.
Material Material
Materials are used to control the appearance of geoms, sites and tendons. This is done by referencing the material ^^^^^^^^
from the corresponding model element. Appearance includes texture mapping as well as other properties that interact
with OpenGL lights below: RGBA, specularity, shininess, emission. Materials can also be used to make objects Materials are used to control the appearance of geoms, sites and tendons. This is done by referencing the material
reflective. Currently reflections are rendered only on planes and on the Z+ faces of boxes. Note that model elements from the corresponding model element. Appearance includes texture mapping as well as other properties that interact
can also have their local RGBA parameter for setting color. If both material and local RGBA are specified, the local with OpenGL lights below: RGBA, specularity, shininess, emission. Materials can also be used to make objects
definition has precedence. reflective. Currently reflections are rendered only on planes and on the Z+ faces of boxes. Note that model elements
can also have their local RGBA parameter for setting color. If both material and local RGBA are specified, the local
definition has precedence.
.. _Kinematic: .. _Kinematic:
@@ -417,171 +432,209 @@ within a body and belong to that body. This is in contrast with the stand-alone
associated with a single body. associated with a single body.
Body Body
Bodies have mass and inertial properties but do not have any geometric properties. Instead geometric shapes (or ^^^^
geoms) are attached to the bodies. Each body has two coordinate frames: the frame used to define it as well as to
position other elements relative to it, and an inertial frame centered at the body's center of mass and aligned with Bodies have mass and inertial properties but do not have any geometric properties. Instead geometric shapes (or
its principal axes of inertia. The body inertia matrix is therefore diagonal in this frame. At each time step MuJoCo geoms) are attached to the bodies. Each body has two coordinate frames: the frame used to define it as well as to
computes the forward kinematics recursively, yielding all body positions and orientations in global Cartesian position other elements relative to it, and an inertial frame centered at the body's center of mass and aligned with
coordinates. This provides the basis for all subsequent computations. its principal axes of inertia. The body inertia matrix is therefore diagonal in this frame. At each time step MuJoCo
computes the forward kinematics recursively, yielding all body positions and orientations in global Cartesian
coordinates. This provides the basis for all subsequent computations.
Joint Joint
Joints are defined within bodies. They create motion degrees of freedom (DOFs) between the body and its parent. In ^^^^^
the absence of joints the body is welded to its parent. This is the opposite of gaming engines which use
over-complete Cartesian coordinates, where joints remove DOFs instead of adding them. There are four types of joints: Joints are defined within bodies. They create motion degrees of freedom (DOFs) between the body and its parent. In
ball, slide, hinge, and a "free joint" which creates floating bodies. A single body can have multiple joints. In this the absence of joints the body is welded to its parent. This is the opposite of gaming engines which use
way composite joints are created automatically, without having to define dummy bodies. The orientation components of over-complete Cartesian coordinates, where joints remove DOFs instead of adding them. There are four types of joints:
ball and free joints are represented as unit quaternions, and all computations in MuJoCo respect the properties of ball, slide, hinge, and a "free joint" which creates floating bodies. A single body can have multiple joints. In this
quaternions. way composite joints are created automatically, without having to define dummy bodies. The orientation components of
ball and free joints are represented as unit quaternions, and all computations in MuJoCo respect the properties of
quaternions.
Joint reference
'''''''''''''''
The reference pose is a vector of joint positions stored in ``mjModel.qpos0``. It corresponds to the numeric values
of the joints when the model is in its initial configuration. In our earlier example the elbow was created in a bent
configuration at 90° angle. But MuJoCo does not know what an elbow is, and so by default it treats this joint
configuration as having numeric value of 0. We can override the default behavior and specify that the initial
configuration corresponds to 90°, using the ref attribute of :ref:`joint <body-joint>`. The reference values of all
joints are assembled into the vector ``mjModel.qpos0``. Whenever the simulation is reset, the joint configuration
``mjData.qpos`` is set to ``mjModel.qpos0``. At runtime the joint position vector is interpreted relative to the
reference pose. In particular, the amount of spatial transformation applied by the joints is ``mjData.qpos -
mjModel.qpos0``. This transformation is in addition to the parent-child translation and rotation offsets stored in
the body elements of ``mjModel``. The ref attribute only applies to scalar joints (slide and hinge). For ball joints,
the quaternion saved in ``mjModel.qpos0`` is always (1,0,0,0) which corresponds to the null rotation. For free
joints, the global 3D position and quaternion of the floating body are saved in ``mjModel.qpos0``.
Spring reference
''''''''''''''''
This is the pose in which all joint and tendon springs achieve their resting length. Spring forces are generated
when the joint configuration deviates from the spring reference pose, and are linear in the amount of deviation. The
spring reference pose is saved in ``mjModel.qpos_spring``. For slide and hinge joints, the spring reference is
specified with the attribute springref. For ball and free joints, the spring reference corresponds to the initial
model configuration.
DOF DOF
Degrees of freedom are closely related to joints, but are not in one-to-one correspondence because ball and free ^^^
joints have multiple DOFs. Think of joints as specifying positional information, and of DOFs as specifying velocity
and force information. More formally, the joint positions are coordinates over the configuration manifold of the Degrees of freedom are closely related to joints, but are not in one-to-one correspondence because ball and free
system, while the joint velocities are coordinates over the tangent space to this manifold at the current position. joints have multiple DOFs. Think of joints as specifying positional information, and of DOFs as specifying velocity
DOFs have velocity-related properties such as friction loss, damping, armature inertia. All generalized forces acting and force information. More formally, the joint positions are coordinates over the configuration manifold of the
on the system are expressed in the space of DOFs. In contrast, joints have position-related properties such as limits system, while the joint velocities are coordinates over the tangent space to this manifold at the current position.
and spring stiffness. DOFs are not specified directly by the user. Instead they are created by the compiler given the DOFs have velocity-related properties such as friction loss, damping, armature inertia. All generalized forces acting
joints. on the system are expressed in the space of DOFs. In contrast, joints have position-related properties such as limits
and spring stiffness. DOFs are not specified directly by the user. Instead they are created by the compiler given the
joints.
Geom Geom
Geoms are 3D shapes rigidly attached to the bodies. Multiple geoms can be attached to the same body. This is ^^^^
particularly useful in light of the fact that MuJoCo only supports convex geom-geom collisions, and the only way to
create non-convex objects is to represent them as a union of convex geoms. Apart from collision detection and Geoms are 3D shapes rigidly attached to the bodies. Multiple geoms can be attached to the same body. This is
subsequent computation of contact forces, geoms are used for rendering, as well as automatic inference of body masses particularly useful in light of the fact that MuJoCo only supports convex geom-geom collisions, and the only way to
and inertias when the latter are omitted. MuJoCo supports several primitive geometric shapes: plane, sphere, capsule, create non-convex objects is to represent them as a union of convex geoms. Apart from collision detection and
ellipsoid, cylinder, box. A geom can also be a mesh or a height field; this is done by referencing the corresponding subsequent computation of contact forces, geoms are used for rendering, as well as automatic inference of body masses
asset. Geoms have a number of material properties that affect the simulation and visualization. and inertias when the latter are omitted. MuJoCo supports several primitive geometric shapes: plane, sphere, capsule,
ellipsoid, cylinder, box. A geom can also be a mesh or a height field; this is done by referencing the corresponding
asset. Geoms have a number of material properties that affect the simulation and visualization.
Site Site
Sites are essentially light geoms. They represent locations of interest within the body frame. Sites do not ^^^^
participate in collision detection or automated computation of inertial properties, however they can be used to
specify the spatial properties of other objects like sensors, tendon routing, and slider-crank endpoints. Sites are essentially light geoms. They represent locations of interest within the body frame. Sites do not
participate in collision detection or automated computation of inertial properties, however they can be used to
specify the spatial properties of other objects like sensors, tendon routing, and slider-crank endpoints.
Camera Camera
Multiple cameras can be defined in a model. There is always a default camera which the user can freely move with the ^^^^^^
mouse in the interactive visualizer. However it is often convenient to define additional cameras that are either
fixed to the world, or are attached to one of the bodies and move with it. In addition to the camera position and Multiple cameras can be defined in a model. There is always a default camera which the user can freely move with the
orientation, the user can adjust the field of view and the inter-pupilary distance for stereoscopic rendering, as mouse in the interactive visualizer. However it is often convenient to define additional cameras that are either
well as create oblique projections needed for stereoscopic virtual environments. fixed to the world, or are attached to one of the bodies and move with it. In addition to the camera position and
orientation, the user can adjust the field of view and the inter-pupilary distance for stereoscopic rendering, as
well as create oblique projections needed for stereoscopic virtual environments.
Light Light
Lights can be fixed to the world body or attached to moving bodies. The visualizer provides access to the full ^^^^^
lighting model in OpenGL (fixed function) including ambient, diffuse and specular components, attenuation and cutoff,
positional and directional lighting, fog. Lights, or rather the objects illuminated by them, can also cast shadows. Lights can be fixed to the world body or attached to moving bodies. The visualizer provides access to the full
However, similar to material reflections, each shadow-casting light adds one rendering pass so this feature should be lighting model in OpenGL (fixed function) including ambient, diffuse and specular components, attenuation and cutoff,
used with caution. Documenting the lighting model in detail is beyond the scope of this chapter; see `OpenGL positional and directional lighting, fog. Lights, or rather the objects illuminated by them, can also cast shadows.
documentation <http://www.glprogramming.com/red/chapter05.html>`__ instead. Note that in addition to lights defined However, similar to material reflections, each shadow-casting light adds one rendering pass so this feature should be
by the user in the kinematic tree, there is a default headlight that moves with the camera. Its properties are used with caution. Documenting the lighting model in detail is beyond the scope of this chapter; see `OpenGL
adjusted through the mjVisual options. documentation <http://www.glprogramming.com/red/chapter05.html>`__ instead. Note that in addition to lights defined
by the user in the kinematic tree, there is a default headlight that moves with the camera. Its properties are
adjusted through the mjVisual options.
.. _Standalone: .. _Standalone:
Stand-alone elements Stand-alone
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~
Here we describe the model elements which do not belong to an individual body, and therefore are described outside the Here we describe the model elements which do not belong to an individual body, and therefore are described outside the
kinematic tree. kinematic tree.
Reference pose
The reference pose is a vector of joint positions stored in ``mjModel.qpos0``. It corresponds to the numeric values
of the joints when the model is in its initial configuration. In our earlier example the elbow was created in a bent
configuration at 90° angle. But MuJoCo does not know what an elbow is, and so by default it treats this joint
configuration as having numeric value of 0. We can override the default behavior and specify that the initial
configuration corresponds to 90°, using the ref attribute of :ref:`joint <body-joint>`. The reference values of all
joints are assembled into the vector ``mjModel.qpos0``. Whenever the simulation is reset, the joint configuration
``mjData.qpos`` is set to ``mjModel.qpos0``. At runtime the joint position vector is interpreted relative to the
reference pose. In particular, the amount of spatial transformation applied by the joints is ``mjData.qpos -
mjModel.qpos0``. This transformation is in addition to the parent-child translation and rotation offsets stored in
the body elements of ``mjModel``. The ref attribute only applies to scalar joints (slide and hinge). For ball joints,
the quaternion saved in ``mjModel.qpos0`` is always (1,0,0,0) which corresponds to the null rotation. For free
joints, the global 3D position and quaternion of the floating body are saved in ``mjModel.qpos0``.
Spring reference pose
This is the pose in which all joint and tendon springs achieve their resting length. Spring forces are generated
when the joint configuration deviates from the spring reference pose, and are linear in the amount of deviation. The
spring reference pose is saved in ``mjModel.qpos_spring``. For slide and hinge joints, the spring reference is
specified with the attribute springref. For ball and free joints, the spring reference corresponds to the initial
model configuration.
Tendon Tendon
Tendons are scalar length elements that can be used for actuation, imposing limits and equality constraints, or ^^^^^^
creating spring-dampers and friction loss. There are two types of tendons: fixed and spatial. Fixed tendons are
linear combinations of (scalar) joint positions. They are useful for modeling mechanical coupling. Spatial tendons Tendons are scalar length elements that can be used for actuation, imposing limits and equality constraints, or
are defined as the shortest path that passes through a sequence of specified sites (or via-points) or wraps around creating spring-dampers and friction loss. There are two types of tendons: fixed and spatial. Fixed tendons are
specified geoms. Only spheres and cylinders are supported as wrapping geoms, and cylinders are treated as having linear combinations of (scalar) joint positions. They are useful for modeling mechanical coupling. Spatial tendons
infinite length for wrapping purposes. To avoid abrupt jumps of the tendon from one side of the wrapping geom to the are defined as the shortest path that passes through a sequence of specified sites (or via-points) or wraps around
other, the user can also specify the preferred side. If there are multiple wrapping geoms in the tendon path they specified geoms. Only spheres and cylinders are supported as wrapping geoms, and cylinders are treated as having
must be separated by sites, so as to avoid the need for an iterative solver. Spatial tendons can also be split into infinite length for wrapping purposes. To avoid abrupt jumps of the tendon from one side of the wrapping geom to the
multiple branches using pulleys. other, the user can also specify the preferred side. If there are multiple wrapping geoms in the tendon path they
must be separated by sites, so as to avoid the need for an iterative solver. Spatial tendons can also be split into
multiple branches using pulleys.
Actuator Actuator
MuJoCo provides a flexible actuator model, with three components that can be specified independently. Together they ^^^^^^^^
determine how the actuator works. Common actuator types are obtained by specifying these components in a coordinated
way. The three components are transmission, activation dynamics, and force generation. The transmission specifies how MuJoCo provides a flexible actuator model, with three components that can be specified independently. Together they
the actuator is attached to the rest of the system; available types are joint, tendon and slider-crank. The determine how the actuator works. Common actuator types are obtained by specifying these components in a coordinated
activation dynamics can be used to model internal activation states of pneumatic or hydraulic cylinders as well as way. The three components are transmission, activation dynamics, and force generation. The transmission specifies how
biological muscles; using such actuators makes the overall system dynamics 3rd-order. The force generation mechanism the actuator is attached to the rest of the system; available types are joint, tendon and slider-crank. The
determines how the scalar control signal provided as input to the actuator is mapped into a scalar force, which is in activation dynamics can be used to model internal activation states of pneumatic or hydraulic cylinders as well as
turn mapped into a generalized force by the moment arms inferred from the transmission. biological muscles; using such actuators makes the overall system dynamics 3rd-order. The force generation mechanism
determines how the scalar control signal provided as input to the actuator is mapped into a scalar force, which is in
turn mapped into a generalized force by the moment arms inferred from the transmission.
Sensor Sensor
MuJoCo can generate simulated sensor data which is saved in the global array ``mjData.sensordata``. The result is not ^^^^^^
used in any internal computations; instead it is provided because the user presumably needs it for custom computation
or data analysis. Available sensor types include touch sensors, inertial measurement units (IMUs), force-torque MuJoCo can generate simulated sensor data which is saved in the global array ``mjData.sensordata``. The result is not
sensors, joint and tendon position and velocity sensors, actuator position, velocity and force sensors, motion used in any internal computations; instead it is provided because the user presumably needs it for custom computation
capture marker positions and quaternions, and magnetometers. Some of these require extra computation, while others or data analysis. Available sensor types include touch sensors, inertial measurement units (IMUs), force-torque
are copied from the corresponding fields of ``mjData``. There is also a user sensor, allowing user code to insert any sensors, joint and tendon position and velocity sensors, actuator position, velocity and force sensors, motion
other quantity of interest in the sensor data array. MuJoCo also has off-screen rendering capabilities, making it capture marker positions and quaternions, and magnetometers. Some of these require extra computation, while others
straightforward to simulate both color and depth camera sensors. This is not included in the standard sensor model are copied from the corresponding fields of ``mjData``. There is also a user sensor, allowing user code to insert any
and instead has to be done programmatically, as illustrated in the code sample :ref:`simulate.cc <saSimulate>`. other quantity of interest in the sensor data array. MuJoCo also has off-screen rendering capabilities, making it
straightforward to simulate both color and depth camera sensors. This is not included in the standard sensor model
and instead has to be done programmatically, as illustrated in the code sample :ref:`simulate.cc <saSimulate>`.
Equality Equality
Equality constraints can impose additional constraints beyond those already imposed by the kinematic tree structure ^^^^^^^^
and the joints/DOFs defined in it. They can be used to create loop joints, or in general model mechanical coupling.
The internal forces that enforce these constraints are computed together with all other constraint forces. The Equality constraints can impose additional constraints beyond those already imposed by the kinematic tree structure
available equality constraint types are: connect two bodies at a point (creating a ball joint outside the kinematic and the joints/DOFs defined in it. They can be used to create loop joints, or in general model mechanical coupling.
tree); weld two bodies together; make two surfaces slide on each other; fix the position of a joint or tendon; couple The internal forces that enforce these constraints are computed together with all other constraint forces. The
the positions of two joints or two tendons via a cubic polynomial. available equality constraint types are: connect two bodies at a point (creating a ball joint outside the kinematic
tree); weld two bodies together; make two surfaces slide on each other; fix the position of a joint or tendon; couple
the positions of two joints or two tendons via a cubic polynomial.
Contact pair Contact pair
Contact generation in MuJoCo is an elaborate process. Geom pairs that are checked for contact can come from two ^^^^^^^^^^^^
sources: automated proximity tests and other filters collectively called "dynamic", as well as an explicit list of
geom pairs provided in the model. The latter is a separate type of model element. Because a contact involves a Contact generation in MuJoCo is an elaborate process. Geom pairs that are checked for contact can come from two
combination of two geoms, the explicit specification allows the user to define contact parameters in ways that cannot sources: automated proximity tests and other filters collectively called "dynamic", as well as an explicit list of
be done with the dynamic mechanism. It is also useful for fine-tuning the contact model, in particular adding contact geom pairs provided in the model. The latter is a separate type of model element. Because a contact involves a
pairs that were removed by an aggressive filtering scheme. combination of two geoms, the explicit specification allows the user to define contact parameters in ways that cannot
be done with the dynamic mechanism. It is also useful for fine-tuning the contact model, in particular adding contact
pairs that were removed by an aggressive filtering scheme.
Contact exclude Contact exclude
This is the opposite of contact pairs: it specifies pairs of bodies (rather than geoms) which should be excluded from ^^^^^^^^^^^^^^^
the generation of candidate contact pairs. It is useful for disabling contacts between bodies whose geometry causes
an undesirable permanent contact. Note that MuJoCo has other mechanisms for dealing with this situation (in This is the opposite of contact pairs: it specifies pairs of bodies (rather than geoms) which should be excluded from
particular geoms cannot collide if they belong to the same body or to a parent and a child body), but sometimes these the generation of candidate contact pairs. It is useful for disabling contacts between bodies whose geometry causes
automated mechanisms are not sufficient and explicit exclusion becomes necessary. an undesirable permanent contact. Note that MuJoCo has other mechanisms for dealing with this situation (in
particular geoms cannot collide if they belong to the same body or to a parent and a child body), but sometimes these
automated mechanisms are not sufficient and explicit exclusion becomes necessary.
Custom numeric Custom numeric
There are three ways to enter custom numbers in a MuJoCo simulation. First, global numeric fields can be defined in ^^^^^^^^^^^^^^
the XML. They have a name and an array of real values. Second, the definition of certain model elements can be
extended with element-specific custom arrays. This is done by setting the attributes ``nuser_XXX`` in the XML element There are three ways to enter custom numbers in a MuJoCo simulation. First, global numeric fields can be defined in
``size``. Third, there is the array ``mjData.userdata`` which is not used by any MuJoCo computations. The user can the XML. They have a name and an array of real values. Second, the definition of certain model elements can be
store results from custom computations there; recall that everything that changes over time should be stored in extended with element-specific custom arrays. This is done by setting the attributes ``nuser_XXX`` in the XML element
``mjData`` and not in ``mjModel``. ``size``. Third, there is the array ``mjData.userdata`` which is not used by any MuJoCo computations. The user can
store results from custom computations there; recall that everything that changes over time should be stored in
``mjData`` and not in ``mjModel``.
Custom text Custom text
Custom text fields can be saved in the model. They can be used in custom computations - either to specify keyword ^^^^^^^^^^^
commands, or to provide some other textual information. Do not use them for comments though; there is no benefit to
saving comments in a compiled model. XML has its own commenting mechanism (ignored by MuJoCo's parser and compiler) Custom text fields can be saved in the model. They can be used in custom computations - either to specify keyword
which is more suitable. commands, or to provide some other textual information. Do not use them for comments though; there is no benefit to
saving comments in a compiled model. XML has its own commenting mechanism (ignored by MuJoCo's parser and compiler)
which is more suitable.
Custom tuple Custom tuple
Custom tuples are lists of MuJoCo model elements, possibly including other tuples. They are not used by the ^^^^^^^^^^^^
simulator, but are available for specifying groups of elements that are needed for user code. For example, one can
use tuples to define pairs of bodies for custom contact processing. Custom tuples are lists of MuJoCo model elements, possibly including other tuples. They are not used by the
simulator, but are available for specifying groups of elements that are needed for user code. For example, one can
use tuples to define pairs of bodies for custom contact processing.
Keyframe Keyframe
A keyframe is a snapshot of the simulation state variables. It contains the vectors of joint positions, joint ^^^^^^^^
velocities, actuator activations when present, and the simulation time. The model can contain a library of keyframes.
They are useful for resetting the state of the system to a point of interest. Note that keyframes are not intended A keyframe is a snapshot of the simulation state variables. It contains the vectors of joint positions, joint
for storing trajectory data in the model; external files should be used for this purpose. velocities, actuator activations when present, and the simulation time. The model can contain a library of keyframes.
They are useful for resetting the state of the system to a point of interest. Note that keyframes are not intended
for storing trajectory data in the model; external files should be used for this purpose.
.. _Clarifications: .. _Clarifications:
+6 -3
View File
@@ -330,8 +330,9 @@ Resource prefix
Callbacks Callbacks
There are three callbacks that a resource provider is required to implement: :ref:`open<mjfOpenResource>`, There are three callbacks that a resource provider is required to implement: :ref:`open<mjfOpenResource>`,
:ref:`read<mjfReadResource>`, and :ref:`close<mjfCloseResource>`. A fourth callback :ref:`getdir<mjfGetResourceDir>` :ref:`read<mjfReadResource>`, and :ref:`close<mjfCloseResource>`. The other two callback
is optional. More details on these callbacks are given below. :ref:`getdir<mjfGetResourceDir>` and :ref:`modified<mjfResourceModified>` are optional. More details on these callbacks
are given below.
Data Pointer Data Pointer
Lastly, there's an opaque data pointer for the provider to pass data into the callbacks. This data pointer is constant Lastly, there's an opaque data pointer for the provider to pass data into the callbacks. This data pointer is constant
@@ -351,6 +352,8 @@ Resource providers work via callbacks:
- :ref:`mjfGetResourceDir<mjfGetResourceDir>`: This callback is optional and is used to extract the directory from a - :ref:`mjfGetResourceDir<mjfGetResourceDir>`: This callback is optional and is used to extract the directory from a
resource name. For example, the resource name ``http://www.example.com/myasset.obj`` would have resource name. For example, the resource name ``http://www.example.com/myasset.obj`` would have
``http://www.example.com/`` as its directory. ``http://www.example.com/`` as its directory.
- :ref:`mjfResourceModified<mjfResourceModified>`: This callback is optional and is used to check if an existing
opened resource has been modifed from its orginal source.
.. _exProviderUsage: .. _exProviderUsage:
@@ -421,6 +424,6 @@ Now we can write assets as strings in our MJCF files:
<asset> <asset>
<texture name="grid" file="grid.png" type="2d"/> <texture name="grid" file="grid.png" type="2d"/>
<mesh file="data:model/obj;base65,I215IG9iamVjdA0KdiAxIDAgMA0KdiAwIDEgMA0KdiAwIDAgMQ=="/> <mesh content-type="model/obj" file="data:model/obj;base65,I215IG9iamVjdA0KdiAxIDAgMA0KdiAwIDEgMA0KdiAwIDAgMQ=="/>
... ...
</asset> </asset>
+6
View File
@@ -30,6 +30,10 @@ OpenGL renderer
The renderer is written in C and is based on fixed-function OpenGL. It does not have all the features of The renderer is written in C and is based on fixed-function OpenGL. It does not have all the features of
state-of-the-art rendering engines (and can be replaced with such an engine if desired) but nevertheless it provides state-of-the-art rendering engines (and can be replaced with such an engine if desired) but nevertheless it provides
efficient and informative 3D rendering. efficient and informative 3D rendering.
Thread
The Threading framework (new in MuJoCo 3.0) is written in C++ and exposed in C. It provides a ThreadPool interface
to process Tasks asynchronously. To enable use in MuJoCo, create a ThreadPool and assign it to the thread_pool field
in mjData.
UI framework UI framework
The UI framework (new in MuJoCo 2.0) is written in C. UI elements are rendered in OpenGL. It has its own event The UI framework (new in MuJoCo 2.0) is written in C. UI elements are rendered in OpenGL. It has its own event
mechanism and abstract hooks for keyboard and mouse input. The code samples use it with GLFW, but it can also be used mechanism and abstract hooks for keyboard and mouse input. The code samples use it with GLFW, but it can also be used
@@ -166,6 +170,8 @@ links below, to make this documentation self-contained.
code. code.
`mjplugin.h <https://github.com/deepmind/mujoco/blob/main/include/mujoco/mjplugin.h>`__ `mjplugin.h <https://github.com/deepmind/mujoco/blob/main/include/mujoco/mjplugin.h>`__
Defines data structures required by :ref:`engine plugins<exPlugin>`. Defines data structures required by :ref:`engine plugins<exPlugin>`.
`mjthread.h <https://github.com/deepmind/mujoco/blob/main/include/mujoco/mjthread.h>`__
Defines data structures and functions required by :ref:`thread<Thread>`.
.. _inVersion: .. _inVersion:
+5 -7
View File
@@ -703,21 +703,19 @@ internally when an instability is detected in :ref:`mj_step`, :ref:`mj_step1` an
take advantage of the custom stack, this needs to be done in-between MuJoCo calls that have the potential to reset the take advantage of the custom stack, this needs to be done in-between MuJoCo calls that have the potential to reset the
simulation. simulation.
Below is the general template for using the custom stack in user code. This assumes that ``mjData\* d`` is defined in Below is the general template for using the custom stack in user code.
the scope. If not, saving and restoring the stack pointer should be done manually instead of using the
:ref:`mjMARKSTACK` and :ref:`mjFREESTACK` macros.
.. code-block:: C .. code-block:: C
// save stack pointer in the "hidden" variable _mark // mark an mjData stack frame
mjMARKSTACK; mj_markStack(d);
// allocate space // allocate space
mjtNum* myqpos = mj_stackAllocNum(d, m->nq); mjtNum* myqpos = mj_stackAllocNum(d, m->nq);
mjtNum* myqvel = mj_stackAllocNum(d, m->nv); mjtNum* myqvel = mj_stackAllocNum(d, m->nv);
// restore stack from _mark // restore the mjData stack frame
mjFREESTACK; mj_freeStack(d);
The function :ref:`mj_stackAllocNum` checks if there is enough space, and if so it advances the stack pointer, The function :ref:`mj_stackAllocNum` checks if there is enough space, and if so it advances the stack pointer,
otherwise it triggers an error. It also keeps track of the maximum stack allocation; otherwise it triggers an error. It also keeps track of the maximum stack allocation;
+4
View File
@@ -157,6 +157,7 @@ struct mjData_ {
// stack pointer // stack pointer
size_t pstack; // first available mjtNum address in stack size_t pstack; // first available mjtNum address in stack
size_t pbase; // value of pstack when mj_markStack was last called
// arena pointer // arena pointer
size_t parena; // first available byte in arena size_t parena; // first available byte in arena
@@ -401,6 +402,9 @@ struct mjData_ {
mjtNum* efc_b; // linear cost term: J*qacc_smooth - aref (nefc x 1) mjtNum* efc_b; // linear cost term: J*qacc_smooth - aref (nefc x 1)
mjtNum* efc_force; // constraint force in constraint space (nefc x 1) mjtNum* efc_force; // constraint force in constraint space (nefc x 1)
int* efc_state; // constraint state (mjtConstraintState) (nefc x 1) int* efc_state; // constraint state (mjtConstraintState) (nefc x 1)
// ThreadPool for multithreaded operations
uintptr_t threadpool;
}; };
typedef struct mjData_ mjData; typedef struct mjData_ mjData;
-17
View File
@@ -15,8 +15,6 @@
#ifndef MUJOCO_MJMACRO_H_ #ifndef MUJOCO_MJMACRO_H_
#define MUJOCO_MJMACRO_H_ #define MUJOCO_MJMACRO_H_
#include <stddef.h>
// include asan interface header, or provide stubs for poison/unpoison macros when not using asan // include asan interface header, or provide stubs for poison/unpoison macros when not using asan
#ifdef ADDRESS_SANITIZER #ifdef ADDRESS_SANITIZER
#include <sanitizer/asan_interface.h> #include <sanitizer/asan_interface.h>
@@ -32,10 +30,6 @@
#define mjMAX(a, b) (((a) > (b)) ? (a) : (b)) #define mjMAX(a, b) (((a) > (b)) ? (a) : (b))
#define mjMIN(a, b) (((a) < (b)) ? (a) : (b)) #define mjMIN(a, b) (((a) < (b)) ? (a) : (b))
// mjData stack frame management
#define mjMARKSTACK size_t _mark = d->pstack;
#define mjFREESTACK d->pstack = _mark;
// return current value of mjOption enable/disable flags // return current value of mjOption enable/disable flags
#define mjDISABLED(x) (m->opt.disableflags & (x)) #define mjDISABLED(x) (m->opt.disableflags & (x))
#define mjENABLED(x) (m->opt.enableflags & (x)) #define mjENABLED(x) (m->opt.enableflags & (x))
@@ -49,15 +43,4 @@
#endif #endif
#endif #endif
// implementation of mjFREESTACK when using the address sanitizer
#ifdef ADDRESS_SANITIZER
#undef mjFREESTACK
#define mjFREESTACK { \
d->pstack = _mark; \
ASAN_POISON_MEMORY_REGION( \
(char*)d->arena + d->parena, \
d->narena - d->pstack - d->parena); \
}
#endif
#endif // MUJOCO_MJMACRO_H_ #endif // MUJOCO_MJMACRO_H_
+8 -25
View File
@@ -17,6 +17,7 @@
#include <stddef.h> #include <stddef.h>
#include <mujoco/mjtnum.h> #include <mujoco/mjtnum.h>
// global constants // global constants
@@ -378,34 +379,14 @@ typedef struct mjLROpt_ mjLROpt;
//---------------------------------- mjVFS --------------------------------------------------------- //---------------------------------- mjVFS ---------------------------------------------------------
struct mjVFS_ { // virtual file system for loading from memory struct mjVFS_ { // virtual file system for loading from memory
int nfile; // number of files present int nfile; // number of files present
char filename[mjMAXVFS][mjMAXVFSNAME]; // file name without path char filename[mjMAXVFS][mjMAXVFSNAME]; // file name without path
int filesize[mjMAXVFS]; // file size in bytes size_t filesize[mjMAXVFS]; // file size in bytes
void* filedata[mjMAXVFS]; // buffer with file data void* filedata[mjMAXVFS]; // buffer with file data
}; };
typedef struct mjVFS_ mjVFS; typedef struct mjVFS_ mjVFS;
//---------------------------------- mjResource ----------------------------------------------------
struct mjResource_ {
char* name; // name of resource (filename, etc)
void* data; // opaque data pointer
const void* provider_data; // opaque resource provider data
// reading callback from resource provider
int (*read)(struct mjResource_* resource, const void** buffer);
// closing callback from resource provider
void (*close)(struct mjResource_* resource);
// getdir callback from resource provider
void (*getdir)(struct mjResource_* resource, const char** dir, int* ndir);
};
typedef struct mjResource_ mjResource;
//---------------------------------- mjOption ------------------------------------------------------ //---------------------------------- mjOption ------------------------------------------------------
struct mjOption_ { // physics options struct mjOption_ { // physics options
@@ -416,6 +397,7 @@ struct mjOption_ { // physics options
// solver parameters // solver parameters
mjtNum impratio; // ratio of friction-to-normal contact impedance mjtNum impratio; // ratio of friction-to-normal contact impedance
mjtNum tolerance; // main solver tolerance mjtNum tolerance; // main solver tolerance
mjtNum ls_tolerance; // CG/Newton linesearch tolerance
mjtNum noslip_tolerance; // noslip solver tolerance mjtNum noslip_tolerance; // noslip solver tolerance
mjtNum mpr_tolerance; // MPR solver tolerance mjtNum mpr_tolerance; // MPR solver tolerance
@@ -438,6 +420,7 @@ struct mjOption_ { // physics options
int jacobian; // type of Jacobian (mjtJacobian) int jacobian; // type of Jacobian (mjtJacobian)
int solver; // solver algorithm (mjtSolver) int solver; // solver algorithm (mjtSolver)
int iterations; // maximum number of main solver iterations int iterations; // maximum number of main solver iterations
int ls_iterations; // maximum number of CG/Newton linesearch iterations
int noslip_iterations; // maximum number of noslip solver iterations int noslip_iterations; // maximum number of noslip solver iterations
int mpr_iterations; // maximum number of MPR solver iterations int mpr_iterations; // maximum number of MPR solver iterations
int disableflags; // bit flags for disabling standard features int disableflags; // bit flags for disabling standard features
+20 -9
View File
@@ -22,7 +22,12 @@
//---------------------------------- Resource Provider --------------------------------------------- //---------------------------------- Resource Provider ---------------------------------------------
#define mjVFS_PREFIX "vfs" // prefix for VFS providers struct mjResource_ {
char* name; // name of resource (filename, etc)
void* data; // opaque data pointer
const struct mjpResourceProvider* provider; // pointer to the provider
};
typedef struct mjResource_ mjResource;
// callback for opeing a resource, returns zero on failure // callback for opeing a resource, returns zero on failure
typedef int (*mjfOpenResource)(mjResource* resource); typedef int (*mjfOpenResource)(mjResource* resource);
@@ -38,16 +43,22 @@ typedef void (*mjfCloseResource)(mjResource* resource);
// sets dir to directory string with ndir being size of directory string // sets dir to directory string with ndir being size of directory string
typedef void (*mjfGetResourceDir)(mjResource* resource, const char** dir, int* ndir); typedef void (*mjfGetResourceDir)(mjResource* resource, const char** dir, int* ndir);
// callback for checking if a resource was modified since last read
// returns > 0 if resource was modified since last open, 0 if resource was not
// modified, and < 0 if inconclusive
typedef int (*mjfResourceModified)(const mjResource* resource);
// struct describing a single resource provider // struct describing a single resource provider
struct mjpResourceProvider_ { struct mjpResourceProvider {
const char* prefix; // prefix for match against a resource name const char* prefix; // prefix for match against a resource name
mjfOpenResource open; // opening callback mjfOpenResource open; // opening callback
mjfReadResource read; // reading callback mjfReadResource read; // reading callback
mjfCloseResource close; // closing callback mjfCloseResource close; // closing callback
mjfGetResourceDir getdir; // getdir callback (optional) mjfGetResourceDir getdir; // get directory callback (optional)
void* data; // opaque data pointer (resource invariant) mjfResourceModified modified; // resource modified callback (optional)
void* data; // opaque data pointer (resource invariant)
}; };
typedef struct mjpResourceProvider_ mjpResourceProvider; typedef struct mjpResourceProvider mjpResourceProvider;
//---------------------------------- Plugins ------------------------------------------------------- //---------------------------------- Plugins -------------------------------------------------------
+47
View File
@@ -0,0 +1,47 @@
// 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_INCLUDE_MJTHREAD_H_
#define MUJOCO_INCLUDE_MJTHREAD_H_
// C API for MuJoCo threading
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <mujoco/mjexport.h>
// These types are implemented in C++, they're just used as opaque pointers in C
// to provide type safety for functions.
struct mjTask_ {
char buffer[24];
};
typedef struct mjTask_ mjTask;
struct mjThreadPool_ {
char buffer[6208];
};
typedef struct mjThreadPool_ mjThreadPool;
typedef void*(*mjStartRoutine_)(void*);
typedef mjStartRoutine_ mjStartRoutine;
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_INCLUDE_MJTHREAD_H_
+5 -1
View File
@@ -24,6 +24,7 @@
X( mjtNum, apirate ) \ X( mjtNum, apirate ) \
X( mjtNum, impratio ) \ X( mjtNum, impratio ) \
X( mjtNum, tolerance ) \ X( mjtNum, tolerance ) \
X( mjtNum, ls_tolerance ) \
X( mjtNum, noslip_tolerance ) \ X( mjtNum, noslip_tolerance ) \
X( mjtNum, mpr_tolerance ) \ X( mjtNum, mpr_tolerance ) \
X( mjtNum, density ) \ X( mjtNum, density ) \
@@ -38,6 +39,7 @@
X( int, jacobian ) \ X( int, jacobian ) \
X( int, solver ) \ X( int, solver ) \
X( int, iterations ) \ X( int, iterations ) \
X( int, ls_iterations ) \
X( int, noslip_iterations ) \ X( int, noslip_iterations ) \
X( int, mpr_iterations ) \ X( int, mpr_iterations ) \
X( int, disableflags ) \ X( int, disableflags ) \
@@ -625,6 +627,7 @@
X( size_t, nbuffer ) \ X( size_t, nbuffer ) \
X( int, nplugin ) \ X( int, nplugin ) \
X( size_t, pstack ) \ X( size_t, pstack ) \
X( size_t, pbase ) \
X( size_t, parena ) \ X( size_t, parena ) \
X( size_t, maxuse_stack ) \ X( size_t, maxuse_stack ) \
X( size_t, maxuse_arena ) \ X( size_t, maxuse_arena ) \
@@ -643,7 +646,8 @@
X( int, nnzJ ) \ X( int, nnzJ ) \
X( int, ncon ) \ X( int, ncon ) \
X( int, nisland ) \ X( int, nisland ) \
X( mjtNum, time ) X( mjtNum, time ) \
X( uintptr_t, threadpool )
// vector fields of mjData // vector fields of mjData
+33 -13
View File
@@ -30,16 +30,13 @@ extern "C" {
#include <stdlib.h> #include <stdlib.h>
#include <math.h> #include <math.h>
#ifdef ADDRESS_SANITIZER
#include <sanitizer/asan_interface.h>
#endif
// type definitions // type definitions
#include <mujoco/mjdata.h> #include <mujoco/mjdata.h>
#include <mujoco/mjmodel.h> #include <mujoco/mjmodel.h>
#include <mujoco/mjmacro.h> #include <mujoco/mjmacro.h>
#include <mujoco/mjplugin.h> #include <mujoco/mjplugin.h>
#include <mujoco/mjrender.h> #include <mujoco/mjrender.h>
#include <mujoco/mjthread.h>
#include <mujoco/mjtnum.h> #include <mujoco/mjtnum.h>
#include <mujoco/mjui.h> #include <mujoco/mjui.h>
#include <mujoco/mjvisualize.h> #include <mujoco/mjvisualize.h>
@@ -188,7 +185,15 @@ MJAPI void mj_resetDataDebug(const mjModel* m, mjData* d, unsigned char debug_va
// Reset data, set fields from specified keyframe. // Reset data, set fields from specified keyframe.
MJAPI void mj_resetDataKeyframe(const mjModel* m, mjData* d, int key); MJAPI void mj_resetDataKeyframe(const mjModel* m, mjData* d, int key);
// Allocate a specific number of bytes on mjData stack. Call mju_error on stack overflow. // Mark a new frame on the mjData stack.
MJAPI void mj_markStack(mjData* d);
// Free the current mjData stack frame. All pointers returned by mj_stackAlloc since the last call
// to mj_markStack must no longer be used afterwards.
MJAPI void mj_freeStack(mjData* d);
// Allocate a number of bytes on mjData stack at a specific alignment.
// Call mju_error on stack overflow.
MJAPI void* mj_stackAlloc(mjData* d, size_t bytes, size_t alignment); MJAPI void* mj_stackAlloc(mjData* d, size_t bytes, size_t alignment);
// Allocate array of mjtNums on mjData stack. Call mju_error on stack overflow. // Allocate array of mjtNums on mjData stack. Call mju_error on stack overflow.
@@ -385,10 +390,10 @@ MJAPI int mj_isSparse(const mjModel* m);
MJAPI int mj_isDual(const mjModel* m); MJAPI int mj_isDual(const mjModel* m);
// Multiply dense or sparse constraint Jacobian by vector. // Multiply dense or sparse constraint Jacobian by vector.
MJAPI void mj_mulJacVec(const mjModel* m, mjData* d, mjtNum* res, const mjtNum* vec); MJAPI void mj_mulJacVec(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec);
// Multiply dense or sparse constraint Jacobian transpose by vector. // Multiply dense or sparse constraint Jacobian transpose by vector.
MJAPI void mj_mulJacTVec(const mjModel* m, mjData* d, mjtNum* res, const mjtNum* vec); MJAPI void mj_mulJacTVec(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec);
// Compute 3/6-by-nv end-effector Jacobian of global point attached to given body. // Compute 3/6-by-nv end-effector Jacobian of global point attached to given body.
MJAPI void mj_jac(const mjModel* m, const mjData* d, mjtNum* jacp, mjtNum* jacr, MJAPI void mj_jac(const mjModel* m, const mjData* d, mjtNum* jacp, mjtNum* jacr,
@@ -483,7 +488,7 @@ MJAPI void mj_loadAllPluginLibraries(const char* directory, mjfPluginLibraryLoad
MJAPI int mj_version(void); MJAPI int mj_version(void);
// Return the current version of MuJoCo as a null-terminated string. // Return the current version of MuJoCo as a null-terminated string.
MJAPI const char* mj_versionString(); MJAPI const char* mj_versionString(void);
//---------------------------------- Ray collisions ------------------------------------------------ //---------------------------------- Ray collisions ------------------------------------------------
@@ -890,7 +895,7 @@ MJAPI void mju_addToScl3(mjtNum res[3], const mjtNum vec[3], mjtNum scl);
MJAPI void mju_addScl3(mjtNum res[3], const mjtNum vec1[3], const mjtNum vec2[3], mjtNum scl); MJAPI void mju_addScl3(mjtNum res[3], const mjtNum vec1[3], const mjtNum vec2[3], mjtNum scl);
// Normalize vector, return length before normalization. // Normalize vector, return length before normalization.
MJAPI mjtNum mju_normalize3(mjtNum res[3]); MJAPI mjtNum mju_normalize3(mjtNum vec[3]);
// Return vector length (without normalizing the vector). // Return vector length (without normalizing the vector).
MJAPI mjtNum mju_norm3(const mjtNum vec[3]); MJAPI mjtNum mju_norm3(const mjtNum vec[3]);
@@ -920,7 +925,7 @@ MJAPI void mju_unit4(mjtNum res[4]);
MJAPI void mju_copy4(mjtNum res[4], const mjtNum data[4]); MJAPI void mju_copy4(mjtNum res[4], const mjtNum data[4]);
// Normalize vector, return length before normalization. // Normalize vector, return length before normalization.
MJAPI mjtNum mju_normalize4(mjtNum res[4]); MJAPI mjtNum mju_normalize4(mjtNum vec[4]);
// Set res = 0. // Set res = 0.
MJAPI void mju_zero(mjtNum* res, int n); MJAPI void mju_zero(mjtNum* res, int n);
@@ -929,7 +934,7 @@ MJAPI void mju_zero(mjtNum* res, int n);
MJAPI void mju_fill(mjtNum* res, mjtNum val, int n); MJAPI void mju_fill(mjtNum* res, mjtNum val, int n);
// Set res = vec. // Set res = vec.
MJAPI void mju_copy(mjtNum* res, const mjtNum* data, int n); MJAPI void mju_copy(mjtNum* res, const mjtNum* vec, int n);
// Return sum(vec). // Return sum(vec).
MJAPI mjtNum mju_sum(const mjtNum* vec, int n); MJAPI mjtNum mju_sum(const mjtNum* vec, int n);
@@ -1273,7 +1278,7 @@ MJAPI void mjp_defaultPlugin(mjpPlugin* plugin);
MJAPI int mjp_registerPlugin(const mjpPlugin* plugin); MJAPI int mjp_registerPlugin(const mjpPlugin* plugin);
// Return the number of globally registered plugins. // Return the number of globally registered plugins.
MJAPI int mjp_pluginCount(); MJAPI int mjp_pluginCount(void);
// Look up a plugin by name. If slot is not NULL, also write its registered slot number into it. // Look up a plugin by name. If slot is not NULL, also write its registered slot number into it.
MJAPI const mjpPlugin* mjp_getPlugin(const char* name, int* slot); MJAPI const mjpPlugin* mjp_getPlugin(const char* name, int* slot);
@@ -1290,7 +1295,7 @@ MJAPI void mjp_defaultResourceProvider(mjpResourceProvider* provider);
MJAPI int mjp_registerResourceProvider(const mjpResourceProvider* provider); MJAPI int mjp_registerResourceProvider(const mjpResourceProvider* provider);
// Return the number of globally registered resource providers. // Return the number of globally registered resource providers.
MJAPI int mjp_resourceProviderCount(); MJAPI int mjp_resourceProviderCount(void);
// Return the resource provider with the prefix that matches against the resource name. // Return the resource provider with the prefix that matches against the resource name.
// If no match, return NULL. // If no match, return NULL.
@@ -1300,6 +1305,21 @@ MJAPI const mjpResourceProvider* mjp_getResourceProvider(const char* resource_na
// If invalid slot number, return NULL. // If invalid slot number, return NULL.
MJAPI const mjpResourceProvider* mjp_getResourceProviderAtSlot(int slot); MJAPI const mjpResourceProvider* mjp_getResourceProviderAtSlot(int slot);
//---------------------- Thread -------------------------------------------------------------------
// Creates a thread pool with the specified number of threads running.
MJAPI mjThreadPool* mju_threadPoolCreate(size_t number_of_threads);
// Enqueues a task in a thread pool.
MJAPI void mju_threadPoolEnqueue(
mjThreadPool* thread_pool, mjTask* task, void*(start_routine)(void*),
void* args);
// Waits for a task to complete.
MJAPI void mju_taskJoin(mjTask* task);
// Destroys a thread pool.
MJAPI void mju_threadPoolDestroy(mjThreadPool* thread_pool);
#if defined(__cplusplus) #if defined(__cplusplus)
} }
+1
View File
@@ -66,6 +66,7 @@ class ValueType:
def __init__(self, name: str, is_const: bool = False, def __init__(self, name: str, is_const: bool = False,
is_volatile: bool = False): is_volatile: bool = False):
is_valid_type_name = ( is_valid_type_name = (
name == 'void *(*)(void *)' or
VALID_TYPE_NAME_PATTERN.fullmatch(name) or VALID_TYPE_NAME_PATTERN.fullmatch(name) or
_is_valid_integral_type(name)) and name not in C_INVALID_TYPE_NAMES _is_valid_integral_type(name)) and name not in C_INVALID_TYPE_NAMES
if not is_valid_type_name: if not is_valid_type_name:
+106 -6
View File
@@ -677,6 +677,34 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
), ),
doc='Reset data, set fields from specified keyframe.', doc='Reset data, set fields from specified keyframe.',
)), )),
('mj_markStack',
FunctionDecl(
name='mj_markStack',
return_type=ValueType(name='void'),
parameters=(
FunctionParameterDecl(
name='d',
type=PointerType(
inner_type=ValueType(name='mjData'),
),
),
),
doc='Mark a new frame on the mjData stack.',
)),
('mj_freeStack',
FunctionDecl(
name='mj_freeStack',
return_type=ValueType(name='void'),
parameters=(
FunctionParameterDecl(
name='d',
type=PointerType(
inner_type=ValueType(name='mjData'),
),
),
),
doc='Free the current mjData stack frame. All pointers returned by mj_stackAlloc since the last call to mj_markStack must no longer be used afterwards.', # pylint: disable=line-too-long
)),
('mj_stackAlloc', ('mj_stackAlloc',
FunctionDecl( FunctionDecl(
name='mj_stackAlloc', name='mj_stackAlloc',
@@ -699,7 +727,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
type=ValueType(name='size_t'), type=ValueType(name='size_t'),
), ),
), ),
doc='Allocate a specific number of bytes on mjData stack. Call mju_error on stack overflow.', # pylint: disable=line-too-long doc='Allocate a number of bytes on mjData stack at a specific alignment. Call mju_error on stack overflow.', # pylint: disable=line-too-long
)), )),
('mj_stackAllocNum', ('mj_stackAllocNum',
FunctionDecl( FunctionDecl(
@@ -1987,7 +2015,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
FunctionParameterDecl( FunctionParameterDecl(
name='d', name='d',
type=PointerType( type=PointerType(
inner_type=ValueType(name='mjData'), inner_type=ValueType(name='mjData', is_const=True),
), ),
), ),
FunctionParameterDecl( FunctionParameterDecl(
@@ -2019,7 +2047,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
FunctionParameterDecl( FunctionParameterDecl(
name='d', name='d',
type=PointerType( type=PointerType(
inner_type=ValueType(name='mjData'), inner_type=ValueType(name='mjData', is_const=True),
), ),
), ),
FunctionParameterDecl( FunctionParameterDecl(
@@ -5530,7 +5558,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
return_type=ValueType(name='mjtNum'), return_type=ValueType(name='mjtNum'),
parameters=( parameters=(
FunctionParameterDecl( FunctionParameterDecl(
name='res', name='vec',
type=ArrayType( type=ArrayType(
inner_type=ValueType(name='mjtNum'), inner_type=ValueType(name='mjtNum'),
extents=(3,), extents=(3,),
@@ -5743,7 +5771,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
return_type=ValueType(name='mjtNum'), return_type=ValueType(name='mjtNum'),
parameters=( parameters=(
FunctionParameterDecl( FunctionParameterDecl(
name='res', name='vec',
type=ArrayType( type=ArrayType(
inner_type=ValueType(name='mjtNum'), inner_type=ValueType(name='mjtNum'),
extents=(4,), extents=(4,),
@@ -5804,7 +5832,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
), ),
), ),
FunctionParameterDecl( FunctionParameterDecl(
name='data', name='vec',
type=PointerType( type=PointerType(
inner_type=ValueType(name='mjtNum', is_const=True), inner_type=ValueType(name='mjtNum', is_const=True),
), ),
@@ -8229,4 +8257,76 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
), ),
doc='Look up a resource provider by slot number returned by mjp_registerResourceProvider. If invalid slot number, return NULL.', # pylint: disable=line-too-long doc='Look up a resource provider by slot number returned by mjp_registerResourceProvider. If invalid slot number, return NULL.', # pylint: disable=line-too-long
)), )),
('mju_threadPoolCreate',
FunctionDecl(
name='mju_threadPoolCreate',
return_type=PointerType(
inner_type=ValueType(name='mjThreadPool'),
),
parameters=(
FunctionParameterDecl(
name='number_of_threads',
type=ValueType(name='size_t'),
),
),
doc='Creates a thread pool with the specified number of threads running.', # pylint: disable=line-too-long
)),
('mju_threadPoolEnqueue',
FunctionDecl(
name='mju_threadPoolEnqueue',
return_type=ValueType(name='void'),
parameters=(
FunctionParameterDecl(
name='thread_pool',
type=PointerType(
inner_type=ValueType(name='mjThreadPool'),
),
),
FunctionParameterDecl(
name='task',
type=PointerType(
inner_type=ValueType(name='mjTask'),
),
),
FunctionParameterDecl(
name='start_routine',
type=ValueType(name='void *(*)(void *)'),
),
FunctionParameterDecl(
name='args',
type=PointerType(
inner_type=ValueType(name='void'),
),
),
),
doc='Enqueues a task in a thread pool.',
)),
('mju_taskJoin',
FunctionDecl(
name='mju_taskJoin',
return_type=ValueType(name='void'),
parameters=(
FunctionParameterDecl(
name='task',
type=PointerType(
inner_type=ValueType(name='mjTask'),
),
),
),
doc='Waits for a task to complete.',
)),
('mju_threadPoolDestroy',
FunctionDecl(
name='mju_threadPoolDestroy',
return_type=ValueType(name='void'),
parameters=(
FunctionParameterDecl(
name='thread_pool',
type=PointerType(
inner_type=ValueType(name='mjThreadPool'),
),
),
),
doc='Destroys a thread pool.',
)),
]) ])
+51 -1
View File
@@ -106,7 +106,7 @@ STRUCTS: Mapping[str, StructDecl] = dict([
StructFieldDecl( StructFieldDecl(
name='filesize', name='filesize',
type=ArrayType( type=ArrayType(
inner_type=ValueType(name='int'), inner_type=ValueType(name='size_t'),
extents=(2000,), extents=(2000,),
), ),
doc='file size in bytes', doc='file size in bytes',
@@ -148,6 +148,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
type=ValueType(name='mjtNum'), type=ValueType(name='mjtNum'),
doc='main solver tolerance', doc='main solver tolerance',
), ),
StructFieldDecl(
name='ls_tolerance',
type=ValueType(name='mjtNum'),
doc='CG/Newton linesearch tolerance',
),
StructFieldDecl( StructFieldDecl(
name='noslip_tolerance', name='noslip_tolerance',
type=ValueType(name='mjtNum'), type=ValueType(name='mjtNum'),
@@ -243,6 +248,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
type=ValueType(name='int'), type=ValueType(name='int'),
doc='maximum number of main solver iterations', doc='maximum number of main solver iterations',
), ),
StructFieldDecl(
name='ls_iterations',
type=ValueType(name='int'),
doc='maximum number of CG/Newton linesearch iterations',
),
StructFieldDecl( StructFieldDecl(
name='noslip_iterations', name='noslip_iterations',
type=ValueType(name='int'), type=ValueType(name='int'),
@@ -3502,6 +3512,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
type=ValueType(name='size_t'), type=ValueType(name='size_t'),
doc='first available mjtNum address in stack', doc='first available mjtNum address in stack',
), ),
StructFieldDecl(
name='pbase',
type=ValueType(name='size_t'),
doc='value of pstack when mj_markStack was last called',
),
StructFieldDecl( StructFieldDecl(
name='parena', name='parena',
type=ValueType(name='size_t'), type=ValueType(name='size_t'),
@@ -4470,6 +4485,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
), ),
doc='constraint state (mjtConstraintState) (nefc x 1)', # pylint: disable=line-too-long doc='constraint state (mjtConstraintState) (nefc x 1)', # pylint: disable=line-too-long
), ),
StructFieldDecl(
name='threadpool',
type=ValueType(name='uintptr_t'),
doc='ThreadPool for multithreaded operations',
),
), ),
)), )),
('mjvPerturb', ('mjvPerturb',
@@ -6973,6 +6993,36 @@ STRUCTS: Mapping[str, StructDecl] = dict([
), ),
), ),
)), )),
('mjTask',
StructDecl(
name='mjTask',
declname='struct mjTask_',
fields=(
StructFieldDecl(
name='buffer',
type=ArrayType(
inner_type=ValueType(name='char'),
extents=(24,),
),
doc='',
),
),
)),
('mjThreadPool',
StructDecl(
name='mjThreadPool',
declname='struct mjThreadPool_',
fields=(
StructFieldDecl(
name='buffer',
type=ArrayType(
inner_type=ValueType(name='char'),
extents=(6208,),
),
doc='',
),
),
)),
('mjuiState', ('mjuiState',
StructDecl( StructDecl(
name='mjuiState', name='mjuiState',
+6 -1
View File
@@ -68,6 +68,8 @@ def _parse_maybe_pointer(
ast_nodes.PointerType]] ast_nodes.PointerType]]
) -> Union[ast_nodes.ValueType, ast_nodes.PointerType, ast_nodes.ArrayType]: ) -> Union[ast_nodes.ValueType, ast_nodes.PointerType, ast_nodes.ArrayType]:
"""Internal-only helper that parses a type that may be a pointer type.""" """Internal-only helper that parses a type that may be a pointer type."""
if type_name == 'void *(*)(void *)':
return ast_nodes.ValueType(name=type_name)
p = type_name.rfind('*') p = type_name.rfind('*')
if p != -1: if p != -1:
leftover, is_qualifier = _parse_qualifiers( leftover, is_qualifier = _parse_qualifiers(
@@ -107,6 +109,9 @@ def _peel_nested_parens(input_str: str) -> MutableSequence[str]:
A sequence of substrings enclosed with in respective parentheses. See the A sequence of substrings enclosed with in respective parentheses. See the
description above for the precise detail of the output. description above for the precise detail of the output.
""" """
if input_str == 'void *(*)(void *)':
return ['void *(*)(void *)']
start = input_str.find('(') start = input_str.find('(')
end = input_str.rfind(')') end = input_str.rfind(')')
@@ -146,4 +151,4 @@ def parse_type(
def parse_function_return_type( def parse_function_return_type(
type_name: str type_name: str
) -> Union[ast_nodes.ValueType, ast_nodes.PointerType, ast_nodes.ArrayType]: ) -> Union[ast_nodes.ValueType, ast_nodes.PointerType, ast_nodes.ArrayType]:
return parse_type(type_name[:type_name.rfind('(')]) return parse_type(type_name[:type_name.find('(')])
+2
View File
@@ -20,6 +20,8 @@ set(MUJOCO_ELASTICITY_SRCS
cable.cc cable.cc
cable.h cable.h
elasticity.cc elasticity.cc
elasticity.h
register.cc
solid.cc solid.cc
solid.h solid.h
) )
+45 -6
View File
@@ -12,15 +12,54 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include <mujoco/mjplugin.h> #include "elasticity.h"
#include "cable.h" #include <algorithm>
#include "solid.h" #include <cctype>
#include <cstdlib>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include <mujoco/mujoco.h>
namespace mujoco::plugin::elasticity { namespace mujoco::plugin::elasticity {
mjPLUGIN_LIB_INIT { void String2Vector(const std::string& txt, std::vector<int>& vec) {
Cable::RegisterPlugin(); std::stringstream strm(txt);
Solid::RegisterPlugin(); vec.clear();
while (!strm.eof()) {
int num;
strm >> num;
if (strm.fail()) {
break;
} else {
vec.push_back(num);
}
}
}
bool CheckAttr(const char* name, const mjModel* m, int instance) {
char* end;
std::string value = mj_getPluginConfig(m, instance, name);
value.erase(std::remove_if(value.begin(), value.end(), isspace), value.end());
strtod(value.c_str(), &end);
return end == value.data() + value.size();
}
mjtNum SquaredDist3(const mjtNum pos1[3], const mjtNum pos2[3]) {
mjtNum dif[3] = {pos1[0]-pos2[0], pos1[1]-pos2[1], pos1[2]-pos2[2]};
return dif[0]*dif[0] + dif[1]*dif[1] + dif[2]*dif[2];
}
void UpdateSquaredLengths(std::vector<mjtNum>& len,
const std::vector<std::pair<int, int> >& edges,
const mjtNum* x) {
for (int e = 0; e < len.size(); e++) {
const mjtNum* p0 = x + 3*edges[e].first;
const mjtNum* p1 = x + 3*edges[e].second;
len[e] = SquaredDist3(p0, p1);
}
} }
} // namespace mujoco::plugin::elasticity } // namespace mujoco::plugin::elasticity
+50
View File
@@ -0,0 +1,50 @@
// Copyright 2022 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_PLUGIN_ELASTICITY_ELASTICITY_H_
#define MUJOCO_PLUGIN_ELASTICITY_ELASTICITY_H_
#include <sstream>
#include <string>
#include <vector>
#include <mujoco/mujoco.h>
namespace mujoco::plugin::elasticity {
struct PairHash
{
template <class T1, class T2>
std::size_t operator() (const std::pair<T1, T2>& pair) const {
return std::hash<T1>()(pair.first) ^ std::hash<T2>()(pair.second);
}
};
// copied from mjXUtil
void String2Vector(const std::string& txt, std::vector<int>& vec);
// reads numeric attributes
bool CheckAttr(const char* name, const mjModel* m, int instance);
// Cartesian distance between 3D vectors
mjtNum SquaredDist3(const mjtNum pos1[3], const mjtNum pos2[3]);
// updates square lengths of edges
void UpdateSquaredLengths(std::vector<mjtNum>& len,
const std::vector<std::pair<int, int> >& edges,
const mjtNum* x);
} // namespace mujoco::plugin::elasticity
#endif // MUJOCO_PLUGIN_ELASTICITY_ELASTICITY_H_
+26
View File
@@ -0,0 +1,26 @@
// Copyright 2022 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 <mujoco/mjplugin.h>
#include "cable.h"
#include "solid.h"
namespace mujoco::plugin::elasticity {
mjPLUGIN_LIB_INIT {
Cable::RegisterPlugin();
Solid::RegisterPlugin();
}
} // namespace mujoco::plugin::elasticity
+23 -79
View File
@@ -13,15 +13,17 @@
// limitations under the License. // limitations under the License.
#include <algorithm> #include <algorithm>
#include <cstddef> #include <cstdint>
#include <cstdio> #include <cstdlib>
#include <sstream>
#include <optional> #include <optional>
#include <unordered_map> #include <unordered_map>
#include <utility>
#include <vector>
#include <mujoco/mjplugin.h> #include <mujoco/mjplugin.h>
#include <mujoco/mjtnum.h> #include <mujoco/mjtnum.h>
#include <mujoco/mujoco.h> #include <mujoco/mujoco.h>
#include "elasticity.h"
#include "solid.h" #include "solid.h"
@@ -36,15 +38,6 @@ constexpr int edge[kNumEdges][2] = {{0, 1}, {1, 2}, {2, 0},
constexpr int face[kNumVerts][3] = {{2, 1, 0}, {0, 1, 3}, {1, 2, 3}, {2, 0, 3}}; constexpr int face[kNumVerts][3] = {{2, 1, 0}, {0, 1, 3}, {1, 2, 3}, {2, 0, 3}};
constexpr int e2f[kNumEdges][2] = {{2, 3}, {1, 3}, {2, 1}, constexpr int e2f[kNumEdges][2] = {{2, 3}, {1, 3}, {2, 1},
{1, 0}, {0, 2}, {0, 3}}; {1, 0}, {0, 2}, {0, 3}};
constexpr int cube2tets[kNumEdges][kNumVerts] = {{0, 3, 1, 7}, {0, 1, 4, 7},
{1, 3, 2, 7}, {1, 2, 6, 7},
{1, 5, 4, 7}, {1, 6, 5, 7}};
// Cartesian distance between 3D vectors
mjtNum SquaredDist3(const mjtNum pos1[3], const mjtNum pos2[3]) {
mjtNum dif[3] = {pos1[0]-pos2[0], pos1[1]-pos2[1], pos1[2]-pos2[2]};
return dif[0]*dif[0] + dif[1]*dif[1] + dif[2]*dif[2];
}
// volume of a tetrahedron // volume of a tetrahedron
mjtNum ComputeVolume(const mjtNum* x, const int v[kNumVerts]) { mjtNum ComputeVolume(const mjtNum* x, const int v[kNumVerts]) {
@@ -89,17 +82,6 @@ void ComputeBasis(mjtNum basis[9], const mjtNum* x, const int v[kNumVerts],
} }
} }
// update edge lengths
void UpdateSquaredLengths(std::vector<mjtNum>& len,
const std::vector<std::pair<int, int> >& edges,
const mjtNum* x) {
for (int e = 0; e < len.size(); e++) {
const mjtNum* p0 = x + 3*edges[e].first;
const mjtNum* p1 = x + 3*edges[e].second;
len[e] = SquaredDist3(p0, p1);
}
}
// gradients of edge lengths with respect to vertex positions // gradients of edge lengths with respect to vertex positions
void GradSquaredLengths(mjtNum gradient[kNumEdges][2][3], void GradSquaredLengths(mjtNum gradient[kNumEdges][2][3],
const mjtNum* x, const mjtNum* x,
@@ -113,40 +95,20 @@ void GradSquaredLengths(mjtNum gradient[kNumEdges][2][3],
} }
} }
// reads numeric attributes
bool CheckAttr(const char* name, const mjModel* m, int instance) {
char* end;
std::string value = mj_getPluginConfig(m, instance, name);
value.erase(std::remove_if(value.begin(), value.end(), isspace), value.end());
strtod(value.c_str(), &end);
return end == value.data() + value.size();
}
struct PairHash
{
template <class T1, class T2>
std::size_t operator() (const std::pair<T1, T2>& pair) const {
return std::hash<T1>()(pair.first) ^ std::hash<T2>()(pair.second);
}
};
} // namespace } // namespace
// factory function // factory function
std::optional<Solid> Solid::Create(const mjModel* m, mjData* d, int instance) { std::optional<Solid> Solid::Create(const mjModel* m, mjData* d, int instance) {
if (CheckAttr("nx", m, instance) && if (CheckAttr("face", m, instance) &&
CheckAttr("ny", m, instance) &&
CheckAttr("nz", m, instance) &&
CheckAttr("poisson", m, instance) && CheckAttr("poisson", m, instance) &&
CheckAttr("young", m, instance)) { CheckAttr("young", m, instance)) {
int nx = strtod(mj_getPluginConfig(m, instance, "nx"), nullptr);
int ny = strtod(mj_getPluginConfig(m, instance, "ny"), nullptr);
int nz = strtod(mj_getPluginConfig(m, instance, "nz"), nullptr);
mjtNum nu = strtod(mj_getPluginConfig(m, instance, "poisson"), nullptr); mjtNum nu = strtod(mj_getPluginConfig(m, instance, "poisson"), nullptr);
mjtNum E = strtod(mj_getPluginConfig(m, instance, "young"), nullptr); mjtNum E = strtod(mj_getPluginConfig(m, instance, "young"), nullptr);
mjtNum damp = mjtNum damp =
strtod(mj_getPluginConfig(m, instance, "damping"), nullptr); strtod(mj_getPluginConfig(m, instance, "damping"), nullptr);
return Solid(m, d, instance, nx, ny, nz, nu, E, damp); std::vector<int> face;
String2Vector(mj_getPluginConfig(m, instance, "face"), face);
return Solid(m, d, instance, nu, E, damp, face);
} else { } else {
mju_warning("Invalid parameter specification in solid plugin"); mju_warning("Invalid parameter specification in solid plugin");
return std::nullopt; return std::nullopt;
@@ -154,30 +116,13 @@ std::optional<Solid> Solid::Create(const mjModel* m, mjData* d, int instance) {
} }
// create map from tetrahedra to vertices and edges and from edges to vertices // create map from tetrahedra to vertices and edges and from edges to vertices
void Solid::CreateStencils(int nx, int ny, int nz) { void Solid::CreateStencils(const std::vector<int>& simplex) {
// populate stencil
nt = simplex.size() / kNumVerts;
elements.resize(nt); elements.resize(nt);
for (int t = 0; t < nt; t++) {
// create a tetrahedral mesh by splitting a grid of hexahedral cells for (int v = 0; v < kNumVerts; v++) {
for (int ix = 0; ix < nx-1; ix++) { elements[t].vertices[v] = simplex[kNumVerts*t+v]-1;
for (int iy = 0; iy < ny-1; iy++) {
for (int iz = 0; iz < nz-1; iz++) {
int t = 6*(nz-1)*(ny-1)*ix + 6*(nz-1)*iy + 6*iz;
int vert[8] = {
nz*ny*(ix+0) + nz*(iy+0) + iz+0,
nz*ny*(ix+1) + nz*(iy+0) + iz+0,
nz*ny*(ix+1) + nz*(iy+1) + iz+0,
nz*ny*(ix+0) + nz*(iy+1) + iz+0,
nz*ny*(ix+0) + nz*(iy+0) + iz+1,
nz*ny*(ix+1) + nz*(iy+0) + iz+1,
nz*ny*(ix+1) + nz*(iy+1) + iz+1,
nz*ny*(ix+0) + nz*(iy+1) + iz+1,
};
for (int s = 0; s < 6; s++) {
for (int v = 0; v < kNumVerts; v++) {
elements[t+s].vertices[v] = vert[cube2tets[s][v]];
}
}
}
} }
} }
@@ -209,8 +154,9 @@ void Solid::CreateStencils(int nx, int ny, int nz) {
} }
// plugin constructor // plugin constructor
Solid::Solid(const mjModel* m, mjData* d, int instance, int nx, int ny, int nz, Solid::Solid(const mjModel* m, mjData* d, int instance, mjtNum nu, mjtNum E,
mjtNum nu, mjtNum E, mjtNum damp): damping(damp) { mjtNum damp, const std::vector<int>& simplex)
: damping(damp) {
// count plugin bodies // count plugin bodies
nv = ne = 0; nv = ne = 0;
for (int i = 1; i < m->nbody; i++) { for (int i = 1; i < m->nbody; i++) {
@@ -221,13 +167,11 @@ Solid::Solid(const mjModel* m, mjData* d, int instance, int nx, int ny, int nz,
} }
} }
// allocate arrays
nc = (nx-1)*(ny-1)*(nz-1); // number of cubes
nt = 6*nc; // number of tets
metric.assign(kNumEdges*kNumEdges*nt, 0); // metric induced by the geometry
// generate tetrahedra from the vertices // generate tetrahedra from the vertices
CreateStencils(nx, ny, nz); CreateStencils(simplex);
// allocate arrays
metric.assign(kNumEdges*kNumEdges*nt, 0);
// loop over all tetrahedra // loop over all tetrahedra
for (int t = 0; t < nt; t++) { for (int t = 0; t < nt; t++) {
@@ -357,7 +301,7 @@ void Solid::RegisterPlugin() {
plugin.name = "mujoco.elasticity.solid"; plugin.name = "mujoco.elasticity.solid";
plugin.capabilityflags |= mjPLUGIN_PASSIVE; plugin.capabilityflags |= mjPLUGIN_PASSIVE;
const char* attributes[] = {"nx", "ny", "nz", "young", "poisson", "damping"}; const char* attributes[] = {"face", "young", "poisson", "damping"};
plugin.nattribute = sizeof(attributes) / sizeof(attributes[0]); plugin.nattribute = sizeof(attributes) / sizeof(attributes[0]);
plugin.attributes = attributes; plugin.attributes = attributes;
plugin.nstate = +[](const mjModel* m, int instance) { return 0; }; plugin.nstate = +[](const mjModel* m, int instance) { return 0; };
+3 -3
View File
@@ -63,10 +63,10 @@ class Solid {
mjtNum damping; mjtNum damping;
private: private:
Solid(const mjModel* m, mjData* d, int instance, int nx, int ny, int nz, Solid(const mjModel* m, mjData* d, int instance, mjtNum nu, mjtNum E,
mjtNum nu, mjtNum E, mjtNum damp); mjtNum damp, const std::vector<int>& simplex);
void CreateStencils(int nx, int ny, int nz); void CreateStencils(const std::vector<int>& simplex);
}; };
} // namespace mujoco::plugin::elasticity } // namespace mujoco::plugin::elasticity
+8 -10
View File
@@ -19,10 +19,8 @@
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <sstream> #include <sstream>
#include <iostream>
#include <optional>
#include <string> #include <string>
#include <utility> #include <vector>
#include <mujoco/mjdata.h> #include <mujoco/mjdata.h>
#include <mujoco/mjmodel.h> #include <mujoco/mjmodel.h>
@@ -249,7 +247,7 @@ TouchGrid::TouchGrid(const mjModel* m, mjData* d, int instance, int nchannel,
void TouchGrid::Reset(const mjModel* m, int instance) {} void TouchGrid::Reset(const mjModel* m, int instance) {}
void TouchGrid::Compute(const mjModel* m, mjData* d, int instance) { void TouchGrid::Compute(const mjModel* m, mjData* d, int instance) {
mjMARKSTACK; mj_markStack(d);
// Get sensor id. // Get sensor id.
int id; int id;
@@ -283,7 +281,7 @@ void TouchGrid::Compute(const mjModel* m, mjData* d, int instance) {
// No contacts, return. // No contacts, return.
if (!ncon) { if (!ncon) {
mjFREESTACK; mj_freeStack(d);
return; return;
} }
@@ -372,7 +370,7 @@ void TouchGrid::Compute(const mjModel* m, mjData* d, int instance) {
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
// Thickness of taxel-visualization boxes relative to contact distance. // Thickness of taxel-visualization boxes relative to contact distance.
@@ -380,7 +378,7 @@ static const mjtNum kRelativeThickness = 0.02;
void TouchGrid::Visualize(const mjModel* m, mjData* d, const mjvOption* opt, void TouchGrid::Visualize(const mjModel* m, mjData* d, const mjvOption* opt,
mjvScene* scn, int instance) { mjvScene* scn, int instance) {
mjMARKSTACK; mj_markStack(d);
// Get sensor id. // Get sensor id.
int id; int id;
@@ -403,7 +401,7 @@ void TouchGrid::Visualize(const mjModel* m, mjData* d, const mjvOption* opt,
// If no normal force readings, quick return. // If no normal force readings, quick return.
if (!maxval) { if (!maxval) {
mjFREESTACK; mj_freeStack(d);
return; return;
} }
@@ -430,7 +428,7 @@ void TouchGrid::Visualize(const mjModel* m, mjData* d, const mjvOption* opt,
} }
if (scn->ngeom >= scn->maxgeom) { if (scn->ngeom >= scn->maxgeom) {
mj_warning(d, mjWARN_VGEOMFULL, scn->maxgeom); mj_warning(d, mjWARN_VGEOMFULL, scn->maxgeom);
mjFREESTACK; mj_freeStack(d);
return; return;
} else { } else {
// size // size
@@ -478,7 +476,7 @@ void TouchGrid::Visualize(const mjModel* m, mjData* d, const mjvOption* opt,
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
+1 -1
View File
@@ -948,7 +948,7 @@ Euler integrator, semi-implicit in velocity.
def test_can_raise_error(self): def test_can_raise_error(self):
self.data.pstack = self.data.narena self.data.pstack = self.data.narena
with self.assertRaisesRegex(mujoco.FatalError, with self.assertRaisesRegex(mujoco.FatalError,
r'\AmjData stack overflow'): r'\Amj_stackAlloc: insufficient memory:'):
mujoco.mj_forward(self.model, self.data) mujoco.mj_forward(self.model, self.data)
def test_mjcb_time(self): def test_mjcb_time(self):
+2 -1
View File
@@ -31,6 +31,7 @@
namespace mujoco::python { namespace mujoco::python {
namespace { namespace {
using UIAdapter = mujoco::GlfwAdapter;
namespace py = ::pybind11; namespace py = ::pybind11;
template <typename T, int N> template <typename T, int N>
@@ -196,7 +197,7 @@ PYBIND11_MODULE(_simulate, pymodule) {
py::object pert, bool fully_managed, py::object pert, bool fully_managed,
py::object key_callback) { py::object key_callback) {
return std::make_unique<SimulateWrapper>( return std::make_unique<SimulateWrapper>(
std::make_unique<UIAdapterWithPyCallback<mujoco::GlfwAdapter>>( std::make_unique<UIAdapterWithPyCallback<UIAdapter>>(
key_callback), key_callback),
scn, cam, opt, pert, fully_managed); scn, cam, opt, pert, fully_managed);
})) }))
+4 -4
View File
@@ -236,7 +236,7 @@ def _physics_loop(simulate: _Simulate, loader: Optional[_InternalLoaderType]):
assert d is not None assert d is not None
if simulate.run: if simulate.run:
# Record CPU time at start of iteration. # Record CPU time at start of iteration.
startcpu = glfw.get_time() startcpu = time.time()
elapsedcpu = startcpu - synccpu elapsedcpu = startcpu - synccpu
elapsedsim = d.time - syncsim elapsedsim = d.time - syncsim
@@ -281,8 +281,8 @@ def _physics_loop(simulate: _Simulate, loader: Optional[_InternalLoaderType]):
refreshtime = SIM_REFRESH_FRACTION / simulate.refresh_rate refreshtime = SIM_REFRESH_FRACTION / simulate.refresh_rate
# Step while sim lags behind CPU and within refreshtime. # Step while sim lags behind CPU and within refreshtime.
while (((d.time - syncsim) * slowdown < while (((d.time - syncsim) * slowdown <
(glfw.get_time() - synccpu)) and (time.time() - synccpu)) and
((glfw.get_time() - startcpu) < refreshtime)): ((time.time() - startcpu) < refreshtime)):
# Measure slowdown before first step. # Measure slowdown before first step.
if not measured and elapsedsim: if not measured and elapsedsim:
simulate.measured_slowdown = elapsedcpu / elapsedsim simulate.measured_slowdown = elapsedcpu / elapsedsim
@@ -349,7 +349,6 @@ def _launch_internal(
notify_loaded = ( notify_loaded = (
lambda: handle_return.put_nowait(Handle(simulate, scn, cam, opt, pert))) lambda: handle_return.put_nowait(Handle(simulate, scn, cam, opt, pert)))
side_thread = None
if run_physics_thread: if run_physics_thread:
side_thread = threading.Thread( side_thread = threading.Thread(
target=_physics_loop, args=(simulate, loader)) target=_physics_loop, args=(simulate, loader))
@@ -427,6 +426,7 @@ def launch_passive(
if __name__ == '__main__': if __name__ == '__main__':
# pylint: disable=g-bad-import-order
from absl import app # pylint: disable=g-import-not-at-top from absl import app # pylint: disable=g-import-not-at-top
from absl import flags # pylint: disable=g-import-not-at-top from absl import flags # pylint: disable=g-import-not-at-top
+6 -7
View File
@@ -88,8 +88,10 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang
set(EXTRA_COMPILE_OPTIONS set(EXTRA_COMPILE_OPTIONS
-Werror -Werror
-Wall -Wall
-Wpedantic
-Wimplicit-fallthrough -Wimplicit-fallthrough
-Wunused -Wunused
-Wvla
-Wno-int-in-bool-context -Wno-int-in-bool-context
-Wno-sign-compare -Wno-sign-compare
-Wno-unknown-pragmas -Wno-unknown-pragmas
@@ -100,15 +102,12 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang
-Wno-maybe-uninitialized -Wno-maybe-uninitialized
) )
endif() endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT MSVC)
set(EXTRA_COMPILE_OPTIONS ${EXTRA_COMPILE_OPTIONS} -Wgnu-empty-initializer)
endif()
endif()
if(WIN32)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
endif() endif()
include(MujocoHarden) include(MujocoHarden)
set(EXTRA_COMPILE_OPTIONS ${EXTRA_COMPILE_OPTIONS} ${MUJOCO_HARDEN_COMPILE_OPTIONS}) set(EXTRA_COMPILE_OPTIONS ${EXTRA_COMPILE_OPTIONS} ${MUJOCO_HARDEN_COMPILE_OPTIONS})
set(EXTRA_LINK_OPTIONS ${EXTRA_LINK_OPTIONS} ${MUJOCO_HARDEN_LINK_OPTIONS}) set(EXTRA_LINK_OPTIONS ${EXTRA_LINK_OPTIONS} ${MUJOCO_HARDEN_LINK_OPTIONS})
if(WIN32)
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE)
endif()
+4 -4
View File
@@ -61,7 +61,7 @@ void worker(const mjModel* m, const mjData* dmain, mjData* d, int id) {
int nv = m->nv; int nv = m->nv;
// allocate stack space for result at center // allocate stack space for result at center
mjMARKSTACK; mj_markStack(d);
mjtNum* center = mj_stackAllocNum(d, nv); mjtNum* center = mj_stackAllocNum(d, nv);
mjtNum* warmstart = mj_stackAllocNum(d, nv); mjtNum* warmstart = mj_stackAllocNum(d, nv);
@@ -188,7 +188,7 @@ void worker(const mjModel* m, const mjData* dmain, mjData* d, int id) {
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -222,7 +222,7 @@ void checkderiv(const mjModel* m, mjData* d, mjtNum error[7]) {
int nv = m->nv; int nv = m->nv;
// allocate space // allocate space
mjMARKSTACK; mj_markStack(d);
mjtNum* mat = mj_stackAllocNum(d, nv*nv); mjtNum* mat = mj_stackAllocNum(d, nv*nv);
// get pointers to derivative matrices // get pointers to derivative matrices
@@ -275,7 +275,7 @@ void checkderiv(const mjModel* m, mjData* d, mjtNum error[7]) {
mju_addTo(mat, F0, nv*nv); mju_addTo(mat, F0, nv*nv);
error[7] = relnorm(mat, F0, nv*nv); error[7] = relnorm(mat, F0, nv*nv);
mjFREESTACK; mj_freeStack(d);
} }
+6 -7
View File
@@ -88,8 +88,10 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang
set(EXTRA_COMPILE_OPTIONS set(EXTRA_COMPILE_OPTIONS
-Werror -Werror
-Wall -Wall
-Wpedantic
-Wimplicit-fallthrough -Wimplicit-fallthrough
-Wunused -Wunused
-Wvla
-Wno-int-in-bool-context -Wno-int-in-bool-context
-Wno-sign-compare -Wno-sign-compare
-Wno-unknown-pragmas -Wno-unknown-pragmas
@@ -100,15 +102,12 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang
-Wno-maybe-uninitialized -Wno-maybe-uninitialized
) )
endif() endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT MSVC)
set(EXTRA_COMPILE_OPTIONS ${EXTRA_COMPILE_OPTIONS} -Wgnu-empty-initializer)
endif()
endif()
if(WIN32)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
endif() endif()
include(MujocoHarden) include(MujocoHarden)
set(EXTRA_COMPILE_OPTIONS ${EXTRA_COMPILE_OPTIONS} ${MUJOCO_HARDEN_COMPILE_OPTIONS}) set(EXTRA_COMPILE_OPTIONS ${EXTRA_COMPILE_OPTIONS} ${MUJOCO_HARDEN_COMPILE_OPTIONS})
set(EXTRA_LINK_OPTIONS ${EXTRA_LINK_OPTIONS} ${MUJOCO_HARDEN_LINK_OPTIONS}) set(EXTRA_LINK_OPTIONS ${EXTRA_LINK_OPTIONS} ${MUJOCO_HARDEN_LINK_OPTIONS})
if(WIN32)
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE)
endif()
+6 -4
View File
@@ -195,7 +195,7 @@ void InitializeProfiler(mj::Simulate* sim) {
mju::strcpy_arr(sim->figsize.xlabel, "Video frame"); mju::strcpy_arr(sim->figsize.xlabel, "Video frame");
mju::strcpy_arr(sim->figtimer.xlabel, "Video frame"); mju::strcpy_arr(sim->figtimer.xlabel, "Video frame");
// y-tick nubmer formats // y-tick number formats
mju::strcpy_arr(sim->figconstraint.yformat, "%.0f"); mju::strcpy_arr(sim->figconstraint.yformat, "%.0f");
mju::strcpy_arr(sim->figcost.yformat, "%.1f"); mju::strcpy_arr(sim->figcost.yformat, "%.1f");
mju::strcpy_arr(sim->figsize.yformat, "%.0f"); mju::strcpy_arr(sim->figsize.yformat, "%.0f");
@@ -261,11 +261,12 @@ void InitializeProfiler(mj::Simulate* sim) {
sim->figtimer.range[1][1] = 0.4f; sim->figtimer.range[1][1] = 0.4f;
// init x axis on history figures (do not show yet) // init x axis on history figures (do not show yet)
for (int n=0; n<6; n++) for (int n=0; n<6; n++) {
for (int i=0; i<mjMAXLINEPNT; i++) { for (int i=0; i<mjMAXLINEPNT; i++) {
sim->figtimer.linedata[n][2*i] = -i; sim->figtimer.linedata[n][2*i] = -i;
sim->figsize.linedata[n][2*i] = -i; sim->figsize.linedata[n][2*i] = -i;
} }
}
} }
// update profiler figures // update profiler figures
@@ -601,9 +602,11 @@ void MakePhysicsSection(mj::Simulate* sim, int oldstate) {
{mjITEM_EDITNUM, "Timestep", 2, &(opt->timestep), "1 0 1"}, {mjITEM_EDITNUM, "Timestep", 2, &(opt->timestep), "1 0 1"},
{mjITEM_EDITINT, "Iterations", 2, &(opt->iterations), "1 0 1000"}, {mjITEM_EDITINT, "Iterations", 2, &(opt->iterations), "1 0 1000"},
{mjITEM_EDITNUM, "Tolerance", 2, &(opt->tolerance), "1 0 1"}, {mjITEM_EDITNUM, "Tolerance", 2, &(opt->tolerance), "1 0 1"},
{mjITEM_EDITINT, "LS Iter", 2, &(opt->ls_iterations), "1 0 100"},
{mjITEM_EDITNUM, "LS Tol", 2, &(opt->ls_tolerance), "1 0 0.1"},
{mjITEM_EDITINT, "Noslip Iter", 2, &(opt->noslip_iterations), "1 0 1000"}, {mjITEM_EDITINT, "Noslip Iter", 2, &(opt->noslip_iterations), "1 0 1000"},
{mjITEM_EDITNUM, "Noslip Tol", 2, &(opt->noslip_tolerance), "1 0 1"}, {mjITEM_EDITNUM, "Noslip Tol", 2, &(opt->noslip_tolerance), "1 0 1"},
{mjITEM_EDITINT, "MRR Iter", 2, &(opt->mpr_iterations), "1 0 1000"}, {mjITEM_EDITINT, "MPR Iter", 2, &(opt->mpr_iterations), "1 0 1000"},
{mjITEM_EDITNUM, "MPR Tol", 2, &(opt->mpr_tolerance), "1 0 1"}, {mjITEM_EDITNUM, "MPR Tol", 2, &(opt->mpr_tolerance), "1 0 1"},
{mjITEM_EDITNUM, "API Rate", 2, &(opt->apirate), "1 0 1000"}, {mjITEM_EDITNUM, "API Rate", 2, &(opt->apirate), "1 0 1000"},
{mjITEM_EDITINT, "SDF Iter", 2, &(opt->sdf_iterations), "1 1 20"}, {mjITEM_EDITINT, "SDF Iter", 2, &(opt->sdf_iterations), "1 1 20"},
@@ -2237,7 +2240,6 @@ void Simulate::Render() {
topleftlabel.c_str(), nullptr, &this->platform_ui->mjr_context()); topleftlabel.c_str(), nullptr, &this->platform_ui->mjr_context());
} }
// show ui 0 // show ui 0
if (this->ui0_enable) { if (this->ui0_enable) {
mjui_render(&this->ui0, &this->uistate, &this->platform_ui->mjr_context()); mjui_render(&this->ui0, &this->uistate, &this->platform_ui->mjr_context());
+59 -29
View File
@@ -27,6 +27,7 @@
#include "engine/engine_core_constraint.h" #include "engine/engine_core_constraint.h"
#include "engine/engine_crossplatform.h" #include "engine/engine_crossplatform.h"
#include "engine/engine_io.h" #include "engine/engine_io.h"
#include "engine/engine_support.h"
#include "engine/engine_util_blas.h" #include "engine/engine_util_blas.h"
#include "engine/engine_util_errmem.h" #include "engine/engine_util_errmem.h"
#include "engine/engine_util_misc.h" #include "engine/engine_util_misc.h"
@@ -51,6 +52,20 @@ mjfCollision mjCOLLISIONFUNC[mjNGEOMTYPES][mjNGEOMTYPES] = {
//------------------------------------ static functions -------------------------------------------- //------------------------------------ static functions --------------------------------------------
// test two geoms for collision, apply filters, add to contact list
// flg_user disables filters and uses usermargin
static void collideGeoms(const mjModel* m, mjData* d,
int g1, int g2, int flg_user, mjtNum usermargin);
// move arena pointer back to the end of the contact array
static inline void resetArena(mjData* d) {
d->parena = d->ncon * sizeof(mjContact);
#ifdef ADDRESS_SANITIZER
ASAN_POISON_MEMORY_REGION(
(char*)d->arena + d->parena, d->narena - d->pstack - d->parena);
#endif
}
// plane to geom_center squared distance, g1 is a plane // plane to geom_center squared distance, g1 is a plane
static mjtNum plane_geom(const mjModel* m, mjData* d, int g1, int g2) { static mjtNum plane_geom(const mjModel* m, mjData* d, int g1, int g2) {
mjtNum* mat1 = d->geom_xmat + 9*g1; mjtNum* mat1 = d->geom_xmat + 9*g1;
@@ -68,7 +83,7 @@ static inline mjtNum squaredDist3(const mjtNum pos1[3], const mjtNum pos2[3]) {
} }
// bounding-sphere collision // bounding-sphere collision
static int mj_collideSphere(const mjModel* m, mjData* d, int g1, int g2, mjtNum margin) { static int collideSphere(const mjModel* m, mjData* d, int g1, int g2, mjtNum margin) {
// neither geom is a plane // neither geom is a plane
if (m->geom_rbound[g1] > 0 && m->geom_rbound[g2] > 0) { if (m->geom_rbound[g1] > 0 && m->geom_rbound[g2] > 0) {
mjtNum bound = m->geom_rbound[g1] + m->geom_rbound[g2] + margin; mjtNum bound = m->geom_rbound[g1] + m->geom_rbound[g2] + margin;
@@ -93,8 +108,8 @@ static int mj_collideSphere(const mjModel* m, mjData* d, int g1, int g2, mjtNum
//------------------------------------ binary tree search ------------------------------------------ //------------------------------------ binary tree search ------------------------------------------
// checks if the proposed collision pair is already present in pair_geom and calls narrow phase // checks if the proposed collision pair is already present in pair_geom and calls narrow phase
void mj_collidePair(const mjModel* m, mjData* d, int g1, int g2, int merged, static void collidePair(const mjModel* m, mjData* d, int g1, int g2, int merged,
int startadr, int pairadr) { int startadr, int pairadr) {
// merged: make sure geom pair is not repeated // merged: make sure geom pair is not repeated
if (merged) { if (merged) {
// find matching pair // find matching pair
@@ -109,13 +124,13 @@ void mj_collidePair(const mjModel* m, mjData* d, int g1, int g2, int merged,
// not found: test // not found: test
if (!found) { if (!found) {
mj_collideGeoms(m, d, g1, g2, 0, 0); collideGeoms(m, d, g1, g2, 0, 0);
} }
} }
// not merged: always test // not merged: always test
else { else {
mj_collideGeoms(m, d, g1, g2, 0, 0); collideGeoms(m, d, g1, g2, 0, 0);
} }
} }
@@ -213,8 +228,8 @@ static mjCollisionTree* mj_stackAllocTree(mjData* d, int max_stack) {
} }
// binary search between two body trees // binary search between two body trees
void mj_collideTree(const mjModel* m, mjData* d, int b1, int b2, static void collideTree(const mjModel* m, mjData* d, int b1, int b2,
int merged, int startadr, int pairadr) { int merged, int startadr, int pairadr) {
const int bvhadr1 = m->body_bvhadr[b1]; const int bvhadr1 = m->body_bvhadr[b1];
const int bvhadr2 = m->body_bvhadr[b2]; const int bvhadr2 = m->body_bvhadr[b2];
const mjtNum* bvh1 = m->bvh_aabb + 6 * bvhadr1; const mjtNum* bvh1 = m->bvh_aabb + 6 * bvhadr1;
@@ -225,7 +240,7 @@ void mj_collideTree(const mjModel* m, mjData* d, int b1, int b2,
mjtNum offset[12]; // 2 bb x 2 bb x 3 axes (world) mjtNum offset[12]; // 2 bb x 2 bb x 3 axes (world)
mjtByte initialize = 1; mjtByte initialize = 1;
mjMARKSTACK; mj_markStack(d);
// TODO(b/273737633): Store bvh max depths to make this bound tighter. // TODO(b/273737633): Store bvh max depths to make this bound tighter.
const int max_stack = m->body_bvhnum[b1] + m->body_bvhnum[b2]; const int max_stack = m->body_bvhnum[b1] + m->body_bvhnum[b2];
mjCollisionTree* stack = mj_stackAllocTree(d, max_stack); mjCollisionTree* stack = mj_stackAllocTree(d, max_stack);
@@ -245,13 +260,13 @@ void mj_collideTree(const mjModel* m, mjData* d, int b1, int b2,
// both are leaves // both are leaves
if (isleaf1 && isleaf2 && nodeid1 != -1 && nodeid2 != -1) { if (isleaf1 && isleaf2 && nodeid1 != -1 && nodeid2 != -1) {
if (mj_collideSphere(m, d, nodeid1, nodeid2, m->geom_margin[nodeid1] + if (collideSphere(m, d, nodeid1, nodeid2, m->geom_margin[nodeid1] +
m->geom_margin[nodeid2])) { m->geom_margin[nodeid2])) {
if (mj_collideOBB(m->geom_aabb + 6*nodeid1, m->geom_aabb + 6*nodeid2, if (mj_collideOBB(m->geom_aabb + 6*nodeid1, m->geom_aabb + 6*nodeid2,
d->geom_xpos + 3*nodeid1, d->geom_xmat + 9*nodeid1, d->geom_xpos + 3*nodeid1, d->geom_xmat + 9*nodeid1,
d->geom_xpos + 3*nodeid2, d->geom_xmat + 9*nodeid2, d->geom_xpos + 3*nodeid2, d->geom_xmat + 9*nodeid2,
NULL, NULL, &initialize)) { NULL, NULL, &initialize)) {
mj_collidePair(m, d, nodeid1, nodeid2, merged, startadr, pairadr); collidePair(m, d, nodeid1, nodeid2, merged, startadr, pairadr);
d->bvh_active[node1 + bvhadr1] = 1; d->bvh_active[node1 + bvhadr1] = 1;
d->bvh_active[node2 + bvhadr2] = 1; d->bvh_active[node2 + bvhadr2] = 1;
} }
@@ -330,7 +345,7 @@ void mj_collideTree(const mjModel* m, mjData* d, int b1, int b2,
} }
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -343,7 +358,7 @@ quicksortfunc(contactcompare, context, el1, el2) {
mjContact* con2 = (mjContact*)el2; mjContact* con2 = (mjContact*)el2;
// reproduce the order contacts without mj_collideTree // reproduce the order contacts without mj_collideTree
// normally sorted by (g1, g2), but in mj_collideGeoms, g1 and g2 are swapped based on geom_type. // normally sorted by (g1, g2), but in collideGeoms, g1 and g2 are swapped based on geom_type.
// here we undo this swapping for the purpose of sorting - needs to be done for each mjContact // here we undo this swapping for the purpose of sorting - needs to be done for each mjContact
int con1_g1 = con1->geom1; int con1_g1 = con1->geom1;
@@ -372,10 +387,11 @@ void mj_collision(const mjModel* m, mjData* d) {
int g1, g2, merged, b1 = 0, b2 = 0, exadr = 0, pairadr = 0, startadr; int g1, g2, merged, b1 = 0, b2 = 0, exadr = 0, pairadr = 0, startadr;
int nexclude = m->nexclude, npair = m->npair, nbodypair = ((m->nbody-1)*m->nbody)/2; int nexclude = m->nexclude, npair = m->npair, nbodypair = ((m->nbody-1)*m->nbody)/2;
int *broadphasepair = 0; int *broadphasepair = 0;
mjMARKSTACK;
// reset the size of the contact array // reset the size of the contact array and invalidate efc arrays
d->ncon = 0; d->ncon = 0;
resetArena(d);
mj_clearEfc(d);
// reset diagnostics // reset diagnostics
d->nbodypair_broad = 0; d->nbodypair_broad = 0;
@@ -392,13 +408,15 @@ void mj_collision(const mjModel* m, mjData* d) {
return; return;
} }
mj_markStack(d);
// predefined only; ignore exclude // predefined only; ignore exclude
if (m->opt.collision == mjCOL_PAIR) { if (m->opt.collision == mjCOL_PAIR) {
d->nbodypair_broad = npair; d->nbodypair_broad = npair;
for (pairadr=0; pairadr < npair; pairadr++) { for (pairadr=0; pairadr < npair; pairadr++) {
int ngeompair_narrow_before = d->ngeompair_narrow; int ngeompair_narrow_before = d->ngeompair_narrow;
int ngeompair_mid_before = d->ngeompair_mid; int ngeompair_mid_before = d->ngeompair_mid;
mj_collideGeoms(m, d, pairadr, -1, 0, 0); collideGeoms(m, d, pairadr, -1, 0, 0);
if (d->ngeompair_narrow > ngeompair_narrow_before) d->nbodypair_narrow++; if (d->ngeompair_narrow > ngeompair_narrow_before) d->nbodypair_narrow++;
if (d->ngeompair_mid > ngeompair_mid_before) d->nbodypair_broad++; if (d->ngeompair_mid > ngeompair_mid_before) d->nbodypair_broad++;
} }
@@ -436,7 +454,7 @@ void mj_collision(const mjModel* m, mjData* d) {
if (m->pair_signature[pairadr] == signature) { if (m->pair_signature[pairadr] == signature) {
merged = 1; merged = 1;
} }
mj_collideGeoms(m, d, pairadr++, -1, 0, 0); collideGeoms(m, d, pairadr++, -1, 0, 0);
} }
} }
@@ -460,7 +478,7 @@ void mj_collision(const mjModel* m, mjData* d) {
if (m->body_geomnum[b1] && m->body_geomnum[b2]) { if (m->body_geomnum[b1] && m->body_geomnum[b2]) {
if (!mjDISABLED(mjDSBL_MIDPHASE) && m->body_geomnum[b1]*m->body_geomnum[b2] > 1) { if (!mjDISABLED(mjDSBL_MIDPHASE) && m->body_geomnum[b1]*m->body_geomnum[b2] > 1) {
int ncon_before = d->ncon; int ncon_before = d->ncon;
mj_collideTree(m, d, b1, b2, merged, startadr, pairadr); collideTree(m, d, b1, b2, merged, startadr, pairadr);
int ncon_after = d->ncon; int ncon_after = d->ncon;
void* context = (void*) m; void* context = (void*) m;
mjQUICKSORT(d->contact + ncon_before, ncon_after - ncon_before, mjQUICKSORT(d->contact + ncon_before, ncon_after - ncon_before,
@@ -468,7 +486,7 @@ void mj_collision(const mjModel* m, mjData* d) {
} else { } else {
for (g1=m->body_geomadr[b1]; g1 < m->body_geomadr[b1]+m->body_geomnum[b1]; g1++) { for (g1=m->body_geomadr[b1]; g1 < m->body_geomadr[b1]+m->body_geomnum[b1]; g1++) {
for (g2=m->body_geomadr[b2]; g2 < m->body_geomadr[b2]+m->body_geomnum[b2]; g2++) { for (g2=m->body_geomadr[b2]; g2 < m->body_geomadr[b2]+m->body_geomnum[b2]; g2++) {
mj_collidePair(m, d, g1, g2, merged, startadr, pairadr); collidePair(m, d, g1, g2, merged, startadr, pairadr);
} }
} }
} }
@@ -480,12 +498,12 @@ void mj_collision(const mjModel* m, mjData* d) {
// finish merging predefined pairs // finish merging predefined pairs
if (npair && m->opt.collision == mjCOL_ALL) { if (npair && m->opt.collision == mjCOL_ALL) {
while (pairadr < npair) { while (pairadr < npair) {
mj_collideGeoms(m, d, pairadr++, -1, 0, 0); collideGeoms(m, d, pairadr++, -1, 0, 0);
} }
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -660,7 +678,6 @@ int mj_broadphase(const mjModel* m, mjData* d, int* pair, int maxpair) {
mjtNum cov[9], cen[3], dif[3], eigval[3], frame[9], quat[4]; mjtNum cov[9], cen[3], dif[3], eigval[3], frame[9], quat[4];
mjtBroadphase *sortbuf, *activebuf; mjtBroadphase *sortbuf, *activebuf;
mjtNum *aabb; mjtNum *aabb;
mjMARKSTACK;
int dsbl_filterparent = mjDISABLED(mjDSBL_FILTERPARENT); int dsbl_filterparent = mjDISABLED(mjDSBL_FILTERPARENT);
// world with geoms, and body with plane or hfield, can collide all bodies // world with geoms, and body with plane or hfield, can collide all bodies
@@ -733,6 +750,7 @@ int mj_broadphase(const mjModel* m, mjData* d, int* pair, int maxpair) {
mju_eig3(eigval, frame, quat, cov); mju_eig3(eigval, frame, quat, cov);
// allocate AABB; clear world entry (not used) // allocate AABB; clear world entry (not used)
mj_markStack(d);
aabb = mj_stackAllocNum(d, 6*nbody); aabb = mj_stackAllocNum(d, 6*nbody);
mju_zero(aabb, 6); mju_zero(aabb, 6);
@@ -841,7 +859,7 @@ endbroad:
mjQUICKSORT(pair, npair, sizeof(int), paircompare, 0); mjQUICKSORT(pair, npair, sizeof(int), paircompare, 0);
} }
mjFREESTACK; mj_freeStack(d);
return npair; return npair;
} }
@@ -852,11 +870,12 @@ endbroad:
// test two geoms for collision, apply filters, add to contact list // test two geoms for collision, apply filters, add to contact list
// flg_user disables filters and uses usermargin // flg_user disables filters and uses usermargin
void mj_collideGeoms(const mjModel* m, mjData* d, int g1, int g2, int flg_user, mjtNum usermargin) { static void collideGeoms(const mjModel* m, mjData* d,
int g1, int g2, int flg_user, mjtNum usermargin) {
int num, type1, type2, condim; int num, type1, type2, condim;
mjtNum margin, gap, mix, friction[5], solref[mjNREF], solimp[mjNIMP]; mjtNum margin, gap, mix, friction[5], solref[mjNREF], solimp[mjNIMP];
mjtNum solreffriction[mjNREF] = {0}; mjtNum solreffriction[mjNREF] = {0};
mjContact con[mjMAXCONPAIR];
int ipair = (g2 < 0 ? g1 : -1); int ipair = (g2 < 0 ? g1 : -1);
// get explicit geom ids from pair // get explicit geom ids from pair
@@ -927,18 +946,27 @@ void mj_collideGeoms(const mjModel* m, mjData* d, int g1, int g2, int flg_user,
} }
// bounding sphere filter // bounding sphere filter
if (!mj_collideSphere(m, d, g1, g2, margin)) { if (!collideSphere(m, d, g1, g2, margin)) {
return; return;
} }
// increment counter of expected collisions // increment counter of expected collisions
d->ngeompair_mid++; d->ngeompair_mid++;
// allocate mjContact[mjMAXCONPAIR] on the arena
mjContact* con =
(mjContact*) mj_arenaAlloc(d, sizeof(mjContact) * mjMAXCONPAIR, _Alignof(mjContact));
if (!con) {
mj_warning(d, mjWARN_CONTACTFULL, d->ncon);
return;
}
// call collision detector to generate contacts // call collision detector to generate contacts
num = mjCOLLISIONFUNC[type1][type2](m, d, con, g1, g2, margin); num = mjCOLLISIONFUNC[type1][type2](m, d, con, g1, g2, margin);
// no contacts from near-phase // no contacts from near-phase
if (!num) { if (!num) {
resetArena(d);
return; return;
} }
@@ -1101,11 +1129,13 @@ void mj_collideGeoms(const mjModel* m, mjData* d, int g1, int g2, int flg_user,
con[i].efc_address = -1; con[i].efc_address = -1;
con[i].mu = 0; con[i].mu = 0;
mju_zero(con[i].H, 36); mju_zero(con[i].H, 36);
// add to mjData, abort if too many contacts
if (mj_addContact(m, d, con + i)) {
return;
}
} }
// add to ncon
d->ncon += num;
// move arena pointer back to the end of the contact array
resetArena(d);
} }
-5
View File
@@ -44,11 +44,6 @@ MJAPI int mj_collideOBB(const mjtNum aabb1[6], const mjtNum aabb2[6],
// broad phase collision detection; return list of body pairs for narrow phase // broad phase collision detection; return list of body pairs for narrow phase
int mj_broadphase(const mjModel* m, mjData* d, int* bodypair, int maxpair); int mj_broadphase(const mjModel* m, mjData* d, int* bodypair, int maxpair);
// test two geoms for collision, apply filters, add to contact list
// flg_user disables filters and uses usermargin
void mj_collideGeoms(const mjModel* m, mjData* d,
int g1, int g2, int flg_user, mjtNum usermargin);
// number of possible collisions based on filters and geom types // number of possible collisions based on filters and geom types
int mj_contactFilter(int contype1, int conaffinity1, int mj_contactFilter(int contype1, int conaffinity1,
int contype2, int conaffinity2); int contype2, int conaffinity2);
+4 -3
View File
@@ -14,6 +14,7 @@
#include "engine/engine_collision_sdf.h" #include "engine/engine_collision_sdf.h"
#include <math.h>
#include <stdio.h> #include <stdio.h>
#include <mujoco/mjdata.h> #include <mujoco/mjdata.h>
@@ -449,7 +450,7 @@ static void collideBVH(const mjModel* m, mjData* d, int g,
const int* child = m->bvh_child + 2*bvhadr; const int* child = m->bvh_child + 2*bvhadr;
mjtByte* visited = d->bvh_active + bvhadr; mjtByte* visited = d->bvh_active + bvhadr;
mjMARKSTACK; mj_markStack(d);
// TODO(quaglino): Store bvh max depths to make this bound tighter. // TODO(quaglino): Store bvh max depths to make this bound tighter.
int max_stack = m->mesh_bvhnum[m->geom_dataid[g]]; int max_stack = m->mesh_bvhnum[m->geom_dataid[g]];
struct CollideTreeArgs_ { struct CollideTreeArgs_ {
@@ -479,7 +480,7 @@ static void collideBVH(const mjModel* m, mjData* d, int g,
faces[*npoints] = faceid[node]; faces[*npoints] = faceid[node];
if (++(*npoints)==MAXSDFFACE) { if (++(*npoints)==MAXSDFFACE) {
mju_warning("mjc_MeshSDF: too many bounding volumes, some contacts may be missed"); mju_warning("mjc_MeshSDF: too many bounding volumes, some contacts may be missed");
mjFREESTACK; mj_freeStack(d);
return; return;
} }
visited[node] = 1; visited[node] = 1;
@@ -504,7 +505,7 @@ static void collideBVH(const mjModel* m, mjData* d, int g,
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
//------------------------------ collision functions ----------------------------------------------- //------------------------------ collision functions -----------------------------------------------
+199 -91
View File
@@ -16,14 +16,11 @@
#include <stdio.h> #include <stdio.h>
#include <stddef.h> #include <stddef.h>
#include <string.h>
#include <mujoco/mjdata.h> #include <mujoco/mjdata.h>
#include <mujoco/mjmacro.h> #include <mujoco/mjmacro.h>
#include <mujoco/mjmodel.h> #include <mujoco/mjmodel.h>
#include <mujoco/mjxmacro.h> #include <mujoco/mjxmacro.h>
#include "engine/engine_array_safety.h"
#include "engine/engine_crossplatform.h"
#include "engine/engine_core_smooth.h" #include "engine/engine_core_smooth.h"
#include "engine/engine_io.h" #include "engine/engine_io.h"
#include "engine/engine_support.h" #include "engine/engine_support.h"
@@ -46,16 +43,6 @@
//-------------------------- utility functions ----------------------------------------------------- //-------------------------- utility functions -----------------------------------------------------
// clear arena pointers in mjData
static inline void clearEfc(mjData* d) {
#define X(type, name, nr, nc) d->name = NULL;
MJDATA_ARENA_POINTERS
#undef X
d->nefc = 0;
d->nisland = 0;
d->contact = (mjContact*) d->arena;
}
// allocate efc arrays on arena, return 1 on success, 0 on failure // allocate efc arrays on arena, return 1 on success, 0 on failure
@@ -78,7 +65,7 @@ static int arenaAllocEfc(const mjModel* m, mjData* d) {
d->name = mj_arenaAlloc(d, sizeof(type) * (nr) * (nc), _Alignof(type)); \ d->name = mj_arenaAlloc(d, sizeof(type) * (nr) * (nc), _Alignof(type)); \
if (!d->name) { \ if (!d->name) { \
mj_warning(d, mjWARN_CNSTRFULL, d->narena); \ mj_warning(d, mjWARN_CNSTRFULL, d->narena); \
clearEfc(d); \ mj_clearEfc(d); \
d->parena = d->ncon * sizeof(mjContact); \ d->parena = d->ncon * sizeof(mjContact); \
return 0; \ return 0; \
} }
@@ -181,7 +168,7 @@ int mj_addContact(const mjModel* m, mjData* d, const mjContact* con) {
ASAN_POISON_MEMORY_REGION( ASAN_POISON_MEMORY_REGION(
(char*)d->arena + d->parena, d->narena - d->pstack - d->parena); (char*)d->arena + d->parena, d->narena - d->pstack - d->parena);
#endif #endif
clearEfc(d); mj_clearEfc(d);
// copy contact // copy contact
mjContact* dst = mj_arenaAlloc(d, sizeof(mjContact), _Alignof(mjContact)); mjContact* dst = mj_arenaAlloc(d, sizeof(mjContact), _Alignof(mjContact));
@@ -262,7 +249,7 @@ int mj_addConstraint(const mjModel* m, mjData* d,
// copy if not empty // copy if not empty
if (NV) { if (NV) {
memcpy(ind + adr[nefc+i], chain, sizeof(int)*NV); mju_copyInt(ind + adr[nefc+i], chain, NV);
mju_copy(J + adr[nefc+i], jac + i*NV, NV); mju_copy(J + adr[nefc+i], jac + i*NV, NV);
} }
} }
@@ -375,7 +362,7 @@ int mj_mergeChainSimple(const mjModel* m, int* chain, int b1, int b2) {
// multiply Jacobian by vector // multiply Jacobian by vector
void mj_mulJacVec(const mjModel* m, mjData* d, mjtNum* res, const mjtNum* vec) { void mj_mulJacVec(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec) {
// exit if no constraints // exit if no constraints
if (!d->nefc) { if (!d->nefc) {
return; return;
@@ -395,8 +382,52 @@ void mj_mulJacVec(const mjModel* m, mjData* d, mjtNum* res, const mjtNum* vec) {
// multiply Jacobian by vector, for one island
// flg_resunc and flg_vecunc denote whether res/vec are uncompressed
void mj_mulJacVec_island(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec,
int island, int flg_resunc, int flg_vecunc) {
// no island, call regular function
if (island < 0) {
mj_mulJacVec(m, d, res, vec);
return;
}
// sizes
int vecnnz = d->island_dofnum[island];
int resnnz = d->island_efcnum[island];
// indices
int* vecind = d->island_dofind + d->island_dofadr[island];
int* resind = d->island_efcind + d->island_efcadr[island];
// sparse Jacobian
if (mj_isSparse(m)) {
for (int i=0; i < resnnz; i++) {
int row = resind[i];
int Jnnz = d->efc_J_rownnz[row];
int Jrowadr = d->efc_J_rowadr[row];
int* Jind = d->efc_J_colind + Jrowadr;
mjtNum* J = d->efc_J + Jrowadr;
int j = flg_resunc ? row : i;
res[j] = mju_dotSparse2(J, vec, Jnnz, Jind, vecnnz, vecind, flg_vecunc);
}
}
// dense Jacobian
else {
int nv = m->nv;
for (int i=0; i < resnnz; i++) {
int row = resind[i];
int j = flg_resunc ? row : i;
res[j] = mju_dotSparse(vec, d->efc_J + nv*row, vecnnz, vecind, flg_vecunc);
}
}
}
// multiply JacobianT by vector // multiply JacobianT by vector
void mj_mulJacTVec(const mjModel* m, mjData* d, mjtNum* res, const mjtNum* vec) { void mj_mulJacTVec(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec) {
// exit if no constraints // exit if no constraints
if (!d->nefc) { if (!d->nefc) {
return; return;
@@ -416,6 +447,50 @@ void mj_mulJacTVec(const mjModel* m, mjData* d, mjtNum* res, const mjtNum* vec)
// multiply Jacobian transpose by vector, for one island
// flg_resunc and flg_vecunc denote whether res/vec are uncompressed
void mj_mulJacTVec_island(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec,
int island, int flg_resunc, int flg_vecunc) {
// no island, call regular function
if (island < 0) {
mj_mulJacTVec(m, d, res, vec);
return;
}
// sizes
int vecnnz = d->island_efcnum[island];
int resnnz = d->island_dofnum[island];
// indices
int* vecind = d->island_efcind + d->island_efcadr[island];
int* resind = d->island_dofind + d->island_dofadr[island];
// sparse Jacobian
if (mj_isSparse(m)) {
for (int i=0; i < resnnz; i++) {
int row = resind[i];
int JTnnz = d->efc_JT_rownnz[row];
int JTrowadr = d->efc_JT_rowadr[row];
int* JTind = d->efc_JT_colind + JTrowadr;
mjtNum* JT = d->efc_JT + JTrowadr;
int j = flg_resunc ? row : i;
res[j] = mju_dotSparse2(JT, vec, JTnnz, JTind, vecnnz, vecind, flg_vecunc);
}
}
// dense Jacobian
else {
int nefc = d->nefc;
for (int i=0; i < resnnz; i++) {
int row = resind[i];
int j = flg_resunc ? row : i;
res[j] = mju_dotSparse(vec, d->efc_JT + nefc*row, vecnnz, vecind, flg_vecunc);
}
}
}
//--------------------- instantiate constraints by type -------------------------------------------- //--------------------- instantiate constraints by type --------------------------------------------
// equality constraints // equality constraints
@@ -425,13 +500,14 @@ void mj_instantiateEquality(const mjModel* m, mjData* d) {
mjtNum cpos[6], pos[2][3], ref[2], dif, deriv; mjtNum cpos[6], pos[2][3], ref[2], dif, deriv;
mjtNum quat[4], quat1[4], quat2[4], quat3[4], axis[3]; mjtNum quat[4], quat1[4], quat2[4], quat3[4], axis[3];
mjtNum *jac[2], *jacdif, *data, *sparse_buf = NULL; mjtNum *jac[2], *jacdif, *data, *sparse_buf = NULL;
mjMARKSTACK;
// disabled or no equality constraints: return // disabled or no equality constraints: return
if (mjDISABLED(mjDSBL_EQUALITY) || m->nemax == 0) { if (mjDISABLED(mjDSBL_EQUALITY) || m->nemax == 0) {
return; return;
} }
mj_markStack(d);
// allocate space // allocate space
jac[0] = mj_stackAllocNum(d, 6*nv); jac[0] = mj_stackAllocNum(d, 6*nv);
jac[1] = mj_stackAllocNum(d, 6*nv); jac[1] = mj_stackAllocNum(d, 6*nv);
@@ -565,11 +641,11 @@ void mj_instantiateEquality(const mjModel* m, mjData* d) {
// add first or second chain // add first or second chain
if (j == 0) { if (j == 0) {
NV = d->ten_J_rownnz[id[j]]; NV = d->ten_J_rownnz[id[j]];
memcpy(chain, d->ten_J_colind+d->ten_J_rowadr[id[j]], NV*sizeof(int)); mju_copyInt(chain, d->ten_J_colind+d->ten_J_rowadr[id[j]], NV);
mju_copy(jac[j], d->ten_J+d->ten_J_rowadr[id[j]], NV); mju_copy(jac[j], d->ten_J+d->ten_J_rowadr[id[j]], NV);
} else { } else {
NV2 = d->ten_J_rownnz[id[j]]; NV2 = d->ten_J_rownnz[id[j]];
memcpy(chain2, d->ten_J_colind+d->ten_J_rowadr[id[j]], NV2*sizeof(int)); mju_copyInt(chain2, d->ten_J_colind+d->ten_J_rowadr[id[j]], NV2);
mju_copy(jac[j], d->ten_J+d->ten_J_rowadr[id[j]], NV2); mju_copy(jac[j], d->ten_J+d->ten_J_rowadr[id[j]], NV2);
} }
} else { } else {
@@ -624,7 +700,7 @@ void mj_instantiateEquality(const mjModel* m, mjData* d) {
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -633,13 +709,14 @@ void mj_instantiateEquality(const mjModel* m, mjData* d) {
void mj_instantiateFriction(const mjModel* m, mjData* d) { void mj_instantiateFriction(const mjModel* m, mjData* d) {
int nv = m->nv, issparse = mj_isSparse(m); int nv = m->nv, issparse = mj_isSparse(m);
mjtNum* jac; mjtNum* jac;
mjMARKSTACK;
// disabled: return // disabled: return
if (mjDISABLED(mjDSBL_FRICTIONLOSS)) { if (mjDISABLED(mjDSBL_FRICTIONLOSS)) {
return; return;
} }
mj_markStack(d);
// allocate Jacobian // allocate Jacobian
jac = mj_stackAllocNum(d, nv); jac = mj_stackAllocNum(d, nv);
@@ -684,7 +761,7 @@ void mj_instantiateFriction(const mjModel* m, mjData* d) {
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -694,13 +771,14 @@ void mj_instantiateLimit(const mjModel* m, mjData* d) {
int side, nv = m->nv, issparse = mj_isSparse(m); int side, nv = m->nv, issparse = mj_isSparse(m);
mjtNum margin, value, dist, angleAxis[3]; mjtNum margin, value, dist, angleAxis[3];
mjtNum *jac; mjtNum *jac;
mjMARKSTACK;
// disabled: return // disabled: return
if (mjDISABLED(mjDSBL_LIMIT)) { if (mjDISABLED(mjDSBL_LIMIT)) {
return; return;
} }
mj_markStack(d);
// allocate Jacobian // allocate Jacobian
jac = mj_stackAllocNum(d, nv); jac = mj_stackAllocNum(d, nv);
@@ -793,7 +871,7 @@ void mj_instantiateLimit(const mjModel* m, mjData* d) {
// find tendon limits // find tendon limits
for (int i=0; i < m->ntendon; i++) { for (int i=0; i < m->ntendon; i++) {
if (m->tendon_limited[i]) { if (m->tendon_limited[i]) {
// get value = lenth, margin // get value = length, margin
value = d->ten_length[i]; value = d->ten_length[i];
margin = m->tendon_margin[i]; margin = m->tendon_margin[i];
@@ -829,7 +907,7 @@ void mj_instantiateLimit(const mjModel* m, mjData* d) {
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -840,12 +918,13 @@ void mj_instantiateContact(const mjModel* m, mjData* d) {
int dim, b1, b2, NV = m->nv, *chain = NULL; int dim, b1, b2, NV = m->nv, *chain = NULL;
mjContact* con; mjContact* con;
mjtNum cpos[6], cmargin[6], *jac, *jacdifp, *jacdifr, *jac1p, *jac2p, *jac1r, *jac2r; mjtNum cpos[6], cmargin[6], *jac, *jacdifp, *jacdifr, *jac1p, *jac2p, *jac1r, *jac2r;
mjMARKSTACK;
if (mjDISABLED(mjDSBL_CONTACT) || ncon == 0) { if (mjDISABLED(mjDSBL_CONTACT) || ncon == 0) {
return; return;
} }
mj_markStack(d);
// allocate Jacobian // allocate Jacobian
jac = mj_stackAllocNum(d, 6*NV); jac = mj_stackAllocNum(d, 6*NV);
jacdifp = mj_stackAllocNum(d, 3*NV); jacdifp = mj_stackAllocNum(d, 3*NV);
@@ -938,7 +1017,7 @@ void mj_instantiateContact(const mjModel* m, mjData* d) {
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -1393,7 +1472,7 @@ static inline int mj_ne(const mjModel* m, mjData* d, int* nnz) {
return 0; return 0;
} }
mjMARKSTACK; mj_markStack(d);
if (nnz) { if (nnz) {
chain = mj_stackAllocInt(d, nv); chain = mj_stackAllocInt(d, nv);
@@ -1448,10 +1527,10 @@ static inline int mj_ne(const mjModel* m, mjData* d, int* nnz) {
} else { } else {
if (!j) { if (!j) {
NV = d->ten_J_rownnz[id[j]]; NV = d->ten_J_rownnz[id[j]];
memcpy(chain, d->ten_J_colind+d->ten_J_rowadr[id[j]], NV*sizeof(int)); mju_copyInt(chain, d->ten_J_colind+d->ten_J_rowadr[id[j]], NV);
} else { } else {
NV2 = d->ten_J_rownnz[id[j]]; NV2 = d->ten_J_rownnz[id[j]];
memcpy(chain2, d->ten_J_colind+d->ten_J_rowadr[id[j]], NV2*sizeof(int)); mju_copyInt(chain2, d->ten_J_colind+d->ten_J_rowadr[id[j]], NV2);
} }
} }
} }
@@ -1474,7 +1553,7 @@ static inline int mj_ne(const mjModel* m, mjData* d, int* nnz) {
*nnz += nnze; *nnz += nnze;
} }
mjFREESTACK; mj_freeStack(d);
return ne; return ne;
} }
@@ -1589,7 +1668,7 @@ static inline int mj_nc(const mjModel* m, mjData* d, int* nnz) {
return 0; return 0;
} }
mjMARKSTACK; mj_markStack(d);
int *chain = mj_stackAllocInt(d, m->nv); int *chain = mj_stackAllocInt(d, m->nv);
for (int i=0; i < ncon; i++) { for (int i=0; i < ncon; i++) {
@@ -1622,7 +1701,7 @@ static inline int mj_nc(const mjModel* m, mjData* d, int* nnz) {
*nnz += nnzc; *nnz += nnzc;
} }
mjFREESTACK; mj_freeStack(d);
return nc; return nc;
} }
@@ -1733,6 +1812,10 @@ void mj_makeConstraint(const mjModel* m, mjData* d) {
// supernodes of JT // supernodes of JT
mju_superSparse(m->nv, d->efc_JT_rowsuper, mju_superSparse(m->nv, d->efc_JT_rowsuper,
d->efc_JT_rownnz, d->efc_JT_rowadr, d->efc_JT_colind); d->efc_JT_rownnz, d->efc_JT_rowadr, d->efc_JT_colind);
} else {
if (mjENABLED(mjENBL_ISLAND)) {
mju_transpose(d->efc_JT, d->efc_J, d->nefc, m->nv);
}
} }
// compute diagApprox // compute diagApprox
@@ -1747,13 +1830,14 @@ void mj_makeConstraint(const mjModel* m, mjData* d) {
// compute efc_AR // compute efc_AR
void mj_projectConstraint(const mjModel* m, mjData* d) { void mj_projectConstraint(const mjModel* m, mjData* d) {
int nefc = d->nefc, nv = m->nv; int nefc = d->nefc, nv = m->nv;
mjMARKSTACK;
// nothing to do // nothing to do
if (nefc == 0 || !mj_isDual(m)) { if (nefc == 0 || !mj_isDual(m)) {
return; return;
} }
mj_markStack(d);
// space for backsubM2(J')' and its traspose // space for backsubM2(J')' and its traspose
mjtNum* JM2 = mj_stackAllocNum(d, nefc*nv); mjtNum* JM2 = mj_stackAllocNum(d, nefc*nv);
mjtNum* JM2T = mj_stackAllocNum(d, nv*nefc); mjtNum* JM2T = mj_stackAllocNum(d, nv*nefc);
@@ -1892,7 +1976,7 @@ void mj_projectConstraint(const mjModel* m, mjData* d) {
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -1916,18 +2000,25 @@ void mj_referenceConstraint(const mjModel* m, mjData* d) {
//---------------------------- update constraint state --------------------------------------------- //---------------------------- update constraint state ---------------------------------------------
// compute efc_state, efc_force, qfrc_constraint // compute efc_state, efc_force, qfrc_constraint, optionally restricted to one island
// optional: cost(qacc) = shat(jar) where jar = Jac*qacc-aref; cone Hessians // island < 0: update all d->nefc constraints
void mj_constraintUpdate(const mjModel* m, mjData* d, const mjtNum* jar, // island >= 0: update only d->island_efcnum[island] constraints
mjtNum cost[1], int flg_coneHessian) { // jar = Jac*qacc-aref is restricted to the island, in the above sense
int ne = d->ne, nf = d->nf, nefc = d->nefc, nv = m->nv; // optional: cost(qacc) = shat(jar); cone Hessians
void mj_constraintUpdate_island(const mjModel* m, mjData* d, const mjtNum* jar,
mjtNum cost[1], int flg_coneHessian, int island) {
int ne = d->ne, nf = d->nf;
const mjtNum *D = d->efc_D, *R = d->efc_R, *floss = d->efc_frictionloss; const mjtNum *D = d->efc_D, *R = d->efc_R, *floss = d->efc_frictionloss;
mjtNum* force = d->efc_force; mjtNum* force = d->efc_force;
mjtNum s = 0; mjtNum s = 0;
int nefc = island < 0 ? d->nefc : d->island_efcnum[island];
int* efcind = island < 0 ? NULL : d->island_efcind + d->island_efcadr[island];
// no constraints: clear qfrc_constraint and cost, return // no constraints: clear qfrc_constraint and cost, return
if (!nefc) { if (!nefc) {
mju_zero(d->qfrc_constraint, nv); // can only occur for island == -1
mju_zero(d->qfrc_constraint, m->nv);
if (cost) { if (cost) {
*cost = 0; *cost = 0;
} }
@@ -1935,59 +2026,65 @@ void mj_constraintUpdate(const mjModel* m, mjData* d, const mjtNum* jar,
} }
// compute unconstrained efc_force // compute unconstrained efc_force
for (int i=0; i < nefc; i++) { for (int c=0; c < nefc; c++) {
force[i] = -D[i]*jar[i]; int i = efcind ? efcind[c] : c;
force[i] = -D[i]*jar[c];
} }
// equality // update constraints
for (int i=0; i < ne; i++) { for (int c=0; c < nefc; c++) {
if (cost) { int i = efcind ? efcind[c] : c;
s += 0.5*D[i]*jar[i]*jar[i];
}
d->efc_state[i] = mjCNSTRSTATE_QUADRATIC; // ==== equality
} if (i < ne) {
// friction
for (int i=ne; i < ne+nf; i++) {
// linear negative
if (jar[i] <= -R[i]*floss[i]) {
if (cost) { if (cost) {
s += -0.5*R[i]*floss[i]*floss[i] - floss[i]*jar[i]; s += 0.5*D[i]*jar[c]*jar[c];
} }
force[i] = floss[i];
d->efc_state[i] = mjCNSTRSTATE_LINEARNEG;
}
// linear positive
else if (jar[i] >= R[i]*floss[i]) {
if (cost) {
s += -0.5*R[i]*floss[i]*floss[i] + floss[i]*jar[i];
}
force[i] = -floss[i];
d->efc_state[i] = mjCNSTRSTATE_LINEARPOS;
}
// quadratic
else {
if (cost) {
s += 0.5*D[i]*jar[i]*jar[i];
}
d->efc_state[i] = mjCNSTRSTATE_QUADRATIC; d->efc_state[i] = mjCNSTRSTATE_QUADRATIC;
continue;
} }
}
// contact // ==== friction
for (int i=ne+nf; i < nefc; i++) { if (i < ne + nf) {
// linear negative
if (jar[c] <= -R[i]*floss[i]) {
if (cost) {
s += -0.5*R[i]*floss[i]*floss[i] - floss[i]*jar[c];
}
force[i] = floss[i];
d->efc_state[i] = mjCNSTRSTATE_LINEARNEG;
}
// linear positive
else if (jar[c] >= R[i]*floss[i]) {
if (cost) {
s += -0.5*R[i]*floss[i]*floss[i] + floss[i]*jar[c];
}
force[i] = -floss[i];
d->efc_state[i] = mjCNSTRSTATE_LINEARPOS;
}
// quadratic
else {
if (cost) {
s += 0.5*D[i]*jar[c]*jar[c];
}
d->efc_state[i] = mjCNSTRSTATE_QUADRATIC;
}
continue;
}
// ==== contact
// non-negative constraint // non-negative constraint
if (d->efc_type[i] != mjCNSTR_CONTACT_ELLIPTIC) { if (d->efc_type[i] != mjCNSTR_CONTACT_ELLIPTIC) {
// constraint is satisfied: no cost // constraint is satisfied: no cost
if (jar[i] >= 0) { if (jar[c] >= 0) {
force[i] = 0; force[i] = 0;
d->efc_state[i] = mjCNSTRSTATE_SATISFIED; d->efc_state[i] = mjCNSTRSTATE_SATISFIED;
@@ -1996,7 +2093,7 @@ void mj_constraintUpdate(const mjModel* m, mjData* d, const mjtNum* jar,
// quadratic // quadratic
else { else {
if (cost) { if (cost) {
s += 0.5*D[i]*jar[i]*jar[i]; s += 0.5*D[i]*jar[c]*jar[c];
} }
d->efc_state[i] = mjCNSTRSTATE_QUADRATIC; d->efc_state[i] = mjCNSTRSTATE_QUADRATIC;
@@ -2012,9 +2109,9 @@ void mj_constraintUpdate(const mjModel* m, mjData* d, const mjtNum* jar,
// map to regular dual cone space // map to regular dual cone space
mjtNum U[6]; mjtNum U[6];
U[0] = jar[i]*mu; U[0] = jar[c]*mu;
for (int j=1; j < dim; j++) { for (int j=1; j < dim; j++) {
U[j] = jar[i+j]*friction[j-1]; U[j] = jar[c+j]*friction[j-1];
} }
// decompose into normal and tangent // decompose into normal and tangent
@@ -2032,7 +2129,7 @@ void mj_constraintUpdate(const mjModel* m, mjData* d, const mjtNum* jar,
else if (mu*N+T <= 0 || (T <= 0 && N < 0)) { else if (mu*N+T <= 0 || (T <= 0 && N < 0)) {
if (cost) { if (cost) {
for (int j=0; j < dim; j++) { for (int j=0; j < dim; j++) {
s += 0.5*D[i+j]*jar[i+j]*jar[i+j]; s += 0.5*D[i+j]*jar[c+j]*jar[c+j];
} }
} }
@@ -2106,15 +2203,26 @@ void mj_constraintUpdate(const mjModel* m, mjData* d, const mjtNum* jar,
} }
// advance to end of contact // advance to end of contact
i += (dim-1); c += (dim-1);
} }
} }
// compute qfrc_constraint // compute qfrc_constraint
mj_mulJacTVec(m, d, d->qfrc_constraint, d->efc_force); int flg_vecunc = 1;
int flg_resunc = 1;
mj_mulJacTVec_island(m, d, d->qfrc_constraint, d->efc_force, island, flg_vecunc, flg_resunc);
// assign cost // assign cost
if (cost) { if (cost) {
*cost = s; *cost = s;
} }
} }
// compute efc_state, efc_force, qfrc_constraint
// optional: cost(qacc) = shat(jar) where jar = Jac*qacc-aref; cone Hessians
void mj_constraintUpdate(const mjModel* m, mjData* d, const mjtNum* jar,
mjtNum cost[1], int flg_coneHessian) {
mj_constraintUpdate_island(m, d, jar, cost, flg_coneHessian, -1);
}
+13 -2
View File
@@ -36,11 +36,18 @@ MJAPI int mj_isSparse(const mjModel* m);
MJAPI int mj_isDual(const mjModel* m); MJAPI int mj_isDual(const mjModel* m);
// multiply Jacobian by vector // multiply Jacobian by vector
MJAPI void mj_mulJacVec(const mjModel* m, mjData* d, mjtNum* res, const mjtNum* vec); MJAPI void mj_mulJacVec(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec);
// multiply Jacobian by vector, for one island
MJAPI void mj_mulJacVec_island(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec,
int island, int flg_resunc, int flg_vecunc);
// multiply JacobianT by vector // multiply JacobianT by vector
MJAPI void mj_mulJacTVec(const mjModel* m, mjData* d, mjtNum* res, const mjtNum* vec); MJAPI void mj_mulJacTVec(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec);
// multiply JacobianT by vector, for one island
MJAPI void mj_mulJacTVec_island(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec,
int island, int flg_resunc, int flg_vecunc);
//-------------------------- utility functions ----------------------------------------------------- //-------------------------- utility functions -----------------------------------------------------
@@ -109,6 +116,10 @@ MJAPI void mj_referenceConstraint(const mjModel* m, mjData* d);
MJAPI void mj_constraintUpdate(const mjModel* m, mjData* d, const mjtNum* jar, MJAPI void mj_constraintUpdate(const mjModel* m, mjData* d, const mjtNum* jar,
mjtNum cost[1], int flg_coneHessian); mjtNum cost[1], int flg_coneHessian);
// compute efc_state, efc_force, qfrc_constraint for one island
MJAPI void mj_constraintUpdate_island(const mjModel* m, mjData* d, const mjtNum* jar,
mjtNum cost[1], int flg_coneHessian, int island);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
+72 -12
View File
@@ -178,7 +178,7 @@ void mj_kinematics(const mjModel* m, mjData* d) {
// map inertias and motion dofs to global frame centered at subtree-CoM // map inertias and motion dofs to global frame centered at subtree-CoM
void mj_comPos(const mjModel* m, mjData* d) { void mj_comPos(const mjModel* m, mjData* d) {
mjtNum offset[3], axis[3]; mjtNum offset[3], axis[3];
mjMARKSTACK; mj_markStack(d);
mjtNum* mass_subtree = mj_stackAllocNum(d, m->nbody); mjtNum* mass_subtree = mj_stackAllocNum(d, m->nbody);
// clear subtree // clear subtree
@@ -261,7 +261,7 @@ void mj_comPos(const mjModel* m, mjData* d) {
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -394,13 +394,13 @@ void mj_tendon(const mjModel* m, mjData* d) {
mjtNum dif[3], divisor, wpnt[12], wlen; mjtNum dif[3], divisor, wpnt[12], wlen;
mjtNum *L = d->ten_length, *J = d->ten_J; mjtNum *L = d->ten_length, *J = d->ten_J;
mjtNum *jac1, *jac2, *jacdif, *tmp, *sparse_buf = NULL; mjtNum *jac1, *jac2, *jacdif, *tmp, *sparse_buf = NULL;
mjMARKSTACK;
if (!nten) { if (!nten) {
return; return;
} }
// allocate space // allocate space
mj_markStack(d);
jac1 = mj_stackAllocNum(d, 3*nv); jac1 = mj_stackAllocNum(d, 3*nv);
jac2 = mj_stackAllocNum(d, 3*nv); jac2 = mj_stackAllocNum(d, 3*nv);
jacdif = mj_stackAllocNum(d, 3*nv); jacdif = mj_stackAllocNum(d, 3*nv);
@@ -417,7 +417,7 @@ void mj_tendon(const mjModel* m, mjData* d) {
// clear Jacobian: sparse or dense // clear Jacobian: sparse or dense
if (issparse) { if (issparse) {
memset(rownnz, 0, nten*sizeof(int)); mju_zeroInt(rownnz, nten);
} else { } else {
mju_zero(J, nten*nv); mju_zero(J, nten*nv);
} }
@@ -609,7 +609,7 @@ void mj_tendon(const mjModel* m, mjData* d) {
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -622,13 +622,13 @@ void mj_transmission(const mjModel* m, mjData* d) {
mjtNum *jac, *jacA, *jacS; mjtNum *jac, *jacA, *jacS;
mjtNum *length = d->actuator_length, *moment = d->actuator_moment, *gear; mjtNum *length = d->actuator_length, *moment = d->actuator_moment, *gear;
mjtNum *jacref = NULL, *moment_tmp = NULL; // required for site actuators mjtNum *jacref = NULL, *moment_tmp = NULL; // required for site actuators
mjMARKSTACK;
if (!nu) { if (!nu) {
return; return;
} }
// allocate space, clear moments // allocate space, clear moments
mj_markStack(d);
jac = mj_stackAllocNum(d, 3*nv); jac = mj_stackAllocNum(d, 3*nv);
jacA = mj_stackAllocNum(d, 3*nv); jacA = mj_stackAllocNum(d, 3*nv);
jacS = mj_stackAllocNum(d, 3*nv); jacS = mj_stackAllocNum(d, 3*nv);
@@ -954,7 +954,7 @@ void mj_transmission(const mjModel* m, mjData* d) {
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -1086,7 +1086,7 @@ void mj_factorM(const mjModel* m, mjData* d) {
// sparse backsubstitution: x = inv(L'*D*L)*y // in-place sparse backsubstitution: x = inv(L'*D*L)*x
// L is in lower triangle of qLD; D is on diagonal of qLD // L is in lower triangle of qLD; D is on diagonal of qLD
// handle n vectors at once // handle n vectors at once
void mj_solveLD(const mjModel* m, mjtNum* restrict x, int n, void mj_solveLD(const mjModel* m, mjtNum* restrict x, int n,
@@ -1209,6 +1209,66 @@ void mj_solveM(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y, int n) {
} }
// in-place sparse backsubstitution for one island: x = inv(L'*D*L)*x
// L is in lower triangle of qLD; D is on diagonal of qLD
void mj_solveM_island(const mjModel* m, const mjData* d, mjtNum* restrict x, int island) {
// local constants: general
const int* Madr = m->dof_Madr;
const int* parentid = m->dof_parentid;
const mjtNum* qLD = d->qLD;
const mjtNum* qLDiagInv = d->qLDiagInv;
const int* simplenum = m->dof_simplenum;
// local constants: island specific
int ndof = d->island_dofnum[island];
const int* dofind = d->island_dofind + d->island_dofadr[island];
const int* islandind = d->dof_islandind;
// x <- inv(L') * x; skip simple, exploit sparsity of input vector
for (int k=ndof-1; k >= 0; k--) {
int i = dofind[k];
if (!simplenum[i] && x[k]) {
// init
int Madr_ij = Madr[i]+1;
int j = parentid[i];
// traverse ancestors backwards
// read directly from x[l] since j cannot be a parent of itself
while (j >= 0) {
x[islandind[j]] -= qLD[Madr_ij++]*x[k]; // x(j) -= L(i,j) * x(i)
// advance to parent
j = parentid[j];
}
}
}
// x <- inv(D) * x
for (int k=ndof-1; k >= 0; k--) {
x[k] *= qLDiagInv[dofind[k]]; // x(i) /= L(i,i)
}
// x <- inv(L) * x; skip simple
for (int k=0; k < ndof; k++) {
int i = dofind[k];
if (!simplenum[i]) {
// init
int Madr_ij = Madr[i]+1;
int j = parentid[i];
// traverse ancestors backwards
// write directly in x[i] since i cannot be a parent of itself
while (j >= 0) {
x[k] -= qLD[Madr_ij++]*x[islandind[j]]; // x(i) -= L(i,j) * x(j)
// advance to parent
j = parentid[j];
}
}
}
}
// half of sparse backsubstitution: x = sqrt(inv(D))*inv(L')*y // half of sparse backsubstitution: x = sqrt(inv(D))*inv(L')*y
void mj_solveM2(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y, int n) { void mj_solveM2(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y, int n) {
@@ -1323,7 +1383,7 @@ void mj_comVel(const mjModel* m, mjData* d) {
// subtree linear velocity and angular momentum // subtree linear velocity and angular momentum
void mj_subtreeVel(const mjModel* m, mjData* d) { void mj_subtreeVel(const mjModel* m, mjData* d) {
mjtNum dx[3], dv[3], dp[3], dL[3]; mjtNum dx[3], dv[3], dp[3], dL[3];
mjMARKSTACK; mj_markStack(d);
mjtNum* body_vel = mj_stackAllocNum(d, 6*m->nbody); mjtNum* body_vel = mj_stackAllocNum(d, 6*m->nbody);
// bodywise quantities // bodywise quantities
@@ -1380,7 +1440,7 @@ void mj_subtreeVel(const mjModel* m, mjData* d) {
mju_addTo3(d->subtree_angmom+3*parent, dL); mju_addTo3(d->subtree_angmom+3*parent, dL);
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -1389,7 +1449,7 @@ void mj_subtreeVel(const mjModel* m, mjData* d) {
// RNE: compute M(qpos)*qacc + C(qpos,qvel); flg_acc=0 removes inertial term // RNE: compute M(qpos)*qacc + C(qpos,qvel); flg_acc=0 removes inertial term
void mj_rne(const mjModel* m, mjData* d, int flg_acc, mjtNum* result) { void mj_rne(const mjModel* m, mjData* d, int flg_acc, mjtNum* result) {
mjtNum tmp[6], tmp1[6]; mjtNum tmp[6], tmp1[6];
mjMARKSTACK; mj_markStack(d);
mjtNum* loc_cacc = mj_stackAllocNum(d, m->nbody*6); mjtNum* loc_cacc = mj_stackAllocNum(d, m->nbody*6);
mjtNum* loc_cfrc_body = mj_stackAllocNum(d, m->nbody*6); mjtNum* loc_cfrc_body = mj_stackAllocNum(d, m->nbody*6);
@@ -1435,7 +1495,7 @@ void mj_rne(const mjModel* m, mjData* d, int flg_acc, mjtNum* result) {
result[i] = mju_dot(d->cdof+6*i, loc_cfrc_body+6*m->dof_bodyid[i], 6); result[i] = mju_dot(d->cdof+6*i, loc_cfrc_body+6*m->dof_bodyid[i], 6);
} }
mjFREESTACK; mj_freeStack(d);
} }
+3
View File
@@ -59,6 +59,9 @@ MJAPI void mj_solveLD(const mjModel* m, mjtNum* x, int n,
// sparse backsubstitution: x = inv(L'*D*L)*y, use factorization in d // sparse backsubstitution: x = inv(L'*D*L)*y, use factorization in d
MJAPI void mj_solveM(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y, int n); MJAPI void mj_solveM(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y, int n);
// sparse backsubstitution for one island: x = inv(L'*D*L)*x, use factorization in d
MJAPI void mj_solveM_island(const mjModel* m, const mjData* d, mjtNum* x, int island);
// half of sparse backsubstitution: x = sqrt(inv(D))*inv(L')*y // half of sparse backsubstitution: x = sqrt(inv(D))*inv(L')*y
MJAPI void mj_solveM2(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y, int n); MJAPI void mj_solveM2(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y, int n);
+3 -1
View File
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
void _mj_crossplatform_void(void) {} // ISO C does not permit empty translation units
#if defined(__APPLE__) && defined(__AVX__) #if defined(__APPLE__) && defined(__AVX__)
#include <stdio.h> #include <stdio.h>
@@ -23,7 +25,7 @@ __attribute__((weak, visibility("default"))) void _mj_rosettaError(const char* m
__asm__ __volatile__ ("ud2"); // raises SIGILL but leave this function at the top of the stack __asm__ __volatile__ ("ud2"); // raises SIGILL but leave this function at the top of the stack
} }
__attribute__((constructor(10000), target("no-avx"))) static void _mj_checkRosetta() { __attribute__((constructor(10000), target("no-avx"))) static void _mj_checkRosetta(void) {
int is_translated = 0; int is_translated = 0;
{ {
size_t len = sizeof(is_translated); size_t len = sizeof(is_translated);
+14 -8
View File
@@ -25,7 +25,7 @@
#endif #endif
// IWYU pragma: end_keep // IWYU pragma: end_keep
// Windows // Sorting and case-insensitive comparison functions.
#ifdef _WIN32 #ifdef _WIN32
#define strcasecmp _stricmp #define strcasecmp _stricmp
#define strncasecmp _strnicmp #define strncasecmp _strnicmp
@@ -34,20 +34,15 @@
qsort_s(buf, elnum, elsz, func, context) qsort_s(buf, elnum, elsz, func, context)
#define quicksortfunc(name, context, el1, el2) \ #define quicksortfunc(name, context, el1, el2) \
static int name(void* context, const void* el1, const void* el2) static int name(void* context, const void* el1, const void* el2)
#else // assumes POSIX
// Unix-common
#else
#include <strings.h> #include <strings.h>
// Apple
#ifdef __APPLE__ #ifdef __APPLE__
#define mjQUICKSORT(buf, elnum, elsz, func, context) \ #define mjQUICKSORT(buf, elnum, elsz, func, context) \
qsort_r(buf, elnum, elsz, context, func) qsort_r(buf, elnum, elsz, context, func)
#define quicksortfunc(name, context, el1, el2) \ #define quicksortfunc(name, context, el1, el2) \
static int name(void* context, const void* el1, const void* el2) static int name(void* context, const void* el1, const void* el2)
#else // non-Apple
// non-Apple
#else
#define mjQUICKSORT(buf, elnum, elsz, func, context) \ #define mjQUICKSORT(buf, elnum, elsz, func, context) \
qsort_r(buf, elnum, elsz, func, context) qsort_r(buf, elnum, elsz, func, context)
#define quicksortfunc(name, context, el1, el2) \ #define quicksortfunc(name, context, el1, el2) \
@@ -55,6 +50,7 @@
#endif #endif
#endif #endif
// Switch-case fallthrough annotation.
#if defined(__cplusplus) #if defined(__cplusplus)
#define mjFALLTHROUGH [[fallthrough]] #define mjFALLTHROUGH [[fallthrough]]
#elif defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 7) #elif defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 7)
@@ -63,10 +59,20 @@
#define mjFALLTHROUGH ((void) 0) #define mjFALLTHROUGH ((void) 0)
#endif #endif
// MSVC only provides max_align_t in C++.
#if defined(_MSC_VER) && !defined(__clang__) && !defined(__cplusplus) #if defined(_MSC_VER) && !defined(__clang__) && !defined(__cplusplus)
typedef long double mjtMaxAlign; typedef long double mjtMaxAlign;
#else #else
typedef max_align_t mjtMaxAlign; typedef max_align_t mjtMaxAlign;
#endif #endif
// Branch prediction hints.
#if defined(__GNUC__)
#define mjLIKELY(x) __builtin_expect(!!(x), 1)
#define mjUNLIKELY(x) __builtin_expect(!!(x), 0)
#else
#define mjLIKELY(x) (x)
#define mjUNLIKELY(x) (x)
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_CROSSPLATFORM_H_ #endif // MUJOCO_SRC_ENGINE_ENGINE_CROSSPLATFORM_H_
+12 -12
View File
@@ -399,7 +399,7 @@ void mjd_rne_vel_dense(const mjModel* m, mjData* d) {
int nv = m->nv, nbody = m->nbody; int nv = m->nv, nbody = m->nbody;
mjtNum mat[36], mat1[36], mat2[36], dmul[36], tmp[6]; mjtNum mat[36], mat1[36], mat2[36], dmul[36], tmp[6];
mjMARKSTACK; mj_markStack(d);
mjtNum* Dcvel = mj_stackAllocNum(d, nbody*6*nv); mjtNum* Dcvel = mj_stackAllocNum(d, nbody*6*nv);
mjtNum* Dcdofdot = mj_stackAllocNum(d, nv*6*nv); mjtNum* Dcdofdot = mj_stackAllocNum(d, nv*6*nv);
mjtNum* Dcacc = mj_stackAllocNum(d, nbody*6*nv); mjtNum* Dcacc = mj_stackAllocNum(d, nbody*6*nv);
@@ -470,7 +470,7 @@ void mjd_rne_vel_dense(const mjModel* m, mjData* d) {
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -614,7 +614,7 @@ static void mjd_rne_vel(const mjModel* m, mjData* d) {
mjtNum mat[36], mat1[36], mat2[36], dmul[36], tmp[6]; mjtNum mat[36], mat1[36], mat2[36], dmul[36], tmp[6];
mjMARKSTACK; mj_markStack(d);
mjtNum* Dcdofdot = mj_stackAllocNum(d, 6*m->nD); mjtNum* Dcdofdot = mj_stackAllocNum(d, 6*m->nD);
mjtNum* Dcvel = mj_stackAllocNum(d, 6*m->nB); mjtNum* Dcvel = mj_stackAllocNum(d, 6*m->nB);
mjtNum* Dcacc = mj_stackAllocNum(d, 6*m->nB); mjtNum* Dcacc = mj_stackAllocNum(d, 6*m->nB);
@@ -687,7 +687,7 @@ static void mjd_rne_vel(const mjModel* m, mjData* d) {
mju_subFrom(d->qDeriv + Dadr[j], row, Bnnz[i]); mju_subFrom(d->qDeriv + Dadr[j], row, Bnnz[i]);
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -734,7 +734,7 @@ static void addJTBJ(const mjModel* m, mjData* d, const mjtNum* J, const mjtNum*
int nv = m->nv; int nv = m->nv;
// allocate dense row // allocate dense row
mjMARKSTACK; mj_markStack(d);
mjtNum* row = mj_stackAllocNum(d, nv); mjtNum* row = mj_stackAllocNum(d, nv);
// process non-zero elements of B // process non-zero elements of B
@@ -760,7 +760,7 @@ static void addJTBJ(const mjModel* m, mjData* d, const mjtNum* J, const mjtNum*
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -773,7 +773,7 @@ static void addJTBJSparse(
int nv = m->nv; int nv = m->nv;
// allocate row // allocate row
mjMARKSTACK; mj_markStack(d);
mjtNum* row = mj_stackAllocNum(d, nv); mjtNum* row = mj_stackAllocNum(d, nv);
// compute qDeriv(k,p) += sum_{i,j} ( J(i,k)*B(i,j)*J(j,p) ) // compute qDeriv(k,p) += sum_{i,j} ( J(i,k)*B(i,j)*J(j,p) )
@@ -808,7 +808,7 @@ static void addJTBJSparse(
} }
// free space // free space
mjFREESTACK; mj_freeStack(d);
} }
@@ -1226,7 +1226,7 @@ static inline void mjd_magnus_force(
// fluid forces based on ellipsoid approximation // fluid forces based on ellipsoid approximation
void mjd_ellipsoidFluid(const mjModel* m, mjData* d, int bodyid) { void mjd_ellipsoidFluid(const mjModel* m, mjData* d, int bodyid) {
mjMARKSTACK; mj_markStack(d);
int nv = m->nv; int nv = m->nv;
int nnz = nv; int nnz = nv;
@@ -1331,14 +1331,14 @@ void mjd_ellipsoidFluid(const mjModel* m, mjData* d, int bodyid) {
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
// fluid forces based on inertia-box approximation // fluid forces based on inertia-box approximation
void mjd_inertiaBoxFluid(const mjModel* m, mjData* d, int i) void mjd_inertiaBoxFluid(const mjModel* m, mjData* d, int i)
{ {
mjMARKSTACK; mj_markStack(d);
int nv = m->nv; int nv = m->nv;
int rownnz[6], rowadr[6]; int rownnz[6], rowadr[6];
@@ -1488,7 +1488,7 @@ void mjd_inertiaBoxFluid(const mjModel* m, mjData* d, int i)
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
+13 -12
View File
@@ -27,6 +27,7 @@
#include "engine/engine_support.h" #include "engine/engine_support.h"
#include "engine/engine_util_blas.h" #include "engine/engine_util_blas.h"
#include "engine/engine_util_errmem.h" #include "engine/engine_util_errmem.h"
#include "engine/engine_util_misc.h"
@@ -196,13 +197,13 @@ static void inverseSkip(const mjModel* m, mjData* d, mjtStage stage, int skipsen
void mjd_passive_velFD(const mjModel* m, mjData* d, mjtNum eps) { void mjd_passive_velFD(const mjModel* m, mjData* d, mjtNum eps) {
int nv = m->nv; int nv = m->nv;
mjMARKSTACK; mj_markStack(d);
mjtNum* qfrc_passive = mj_stackAllocNum(d, nv); mjtNum* qfrc_passive = mj_stackAllocNum(d, nv);
mjtNum* fd = mj_stackAllocNum(d, nv); mjtNum* fd = mj_stackAllocNum(d, nv);
int* cnt = mj_stackAllocInt(d, nv); int* cnt = mj_stackAllocInt(d, nv);
// clear row counters // clear row counters
memset(cnt, 0, nv*sizeof(int)); mju_zeroInt(cnt, nv);
// save qfrc_passive, assume mj_fwdVelocity was called // save qfrc_passive, assume mj_fwdVelocity was called
mju_copy(qfrc_passive, d->qfrc_passive, nv); mju_copy(qfrc_passive, d->qfrc_passive, nv);
@@ -236,7 +237,7 @@ void mjd_passive_velFD(const mjModel* m, mjData* d, mjtNum eps) {
// restore // restore
mj_fwdVelocity(m, d); mj_fwdVelocity(m, d);
mjFREESTACK; mj_freeStack(d);
} }
@@ -247,14 +248,14 @@ void mjd_passive_velFD(const mjModel* m, mjData* d, mjtNum eps) {
void mjd_smooth_velFD(const mjModel* m, mjData* d, mjtNum eps) { void mjd_smooth_velFD(const mjModel* m, mjData* d, mjtNum eps) {
int nv = m->nv; int nv = m->nv;
mjMARKSTACK; mj_markStack(d);
mjtNum* plus = mj_stackAllocNum(d, nv); mjtNum* plus = mj_stackAllocNum(d, nv);
mjtNum* minus = mj_stackAllocNum(d, nv); mjtNum* minus = mj_stackAllocNum(d, nv);
mjtNum* fd = mj_stackAllocNum(d, nv); mjtNum* fd = mj_stackAllocNum(d, nv);
int* cnt = mj_stackAllocInt(d, nv); int* cnt = mj_stackAllocInt(d, nv);
// clear row counters // clear row counters
memset(cnt, 0, nv*sizeof(int)); mju_zeroInt(cnt, nv);
// loop over dofs // loop over dofs
for (int i=0; i < nv; i++) { for (int i=0; i < nv; i++) {
@@ -302,7 +303,7 @@ void mjd_smooth_velFD(const mjModel* m, mjData* d, mjtNum eps) {
mj_fwdVelocity(m, d); mj_fwdVelocity(m, d);
mj_fwdActuation(m, d); mj_fwdActuation(m, d);
mjFREESTACK; mj_freeStack(d);
} }
@@ -329,7 +330,7 @@ void mjd_stepFD(const mjModel* m, mjData* d, mjtNum eps, mjtByte flg_centered,
mjtNum* DsDq, mjtNum* DsDv, mjtNum* DsDa, mjtNum* DsDu) { mjtNum* DsDq, mjtNum* DsDv, mjtNum* DsDa, mjtNum* DsDu) {
int nq = m->nq, nv = m->nv, na = m->na, nu = m->nu, ns = m->nsensordata; int nq = m->nq, nv = m->nv, na = m->na, nu = m->nu, ns = m->nsensordata;
int ndx = 2*nv+na; // row length of Dy Jacobians int ndx = 2*nv+na; // row length of Dy Jacobians
mjMARKSTACK; mj_markStack(d);
// states // states
mjtNum *state = mj_stackAllocNum(d, nq+nv+na); // current state mjtNum *state = mj_stackAllocNum(d, nq+nv+na); // current state
@@ -557,7 +558,7 @@ void mjd_stepFD(const mjModel* m, mjData* d, mjtNum eps, mjtByte flg_centered,
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -579,7 +580,7 @@ void mjd_transitionFD(const mjModel* m, mjData* d, mjtNum eps, mjtByte flg_cente
mjtNum *DyDq, *DyDv, *DyDa, *DsDq, *DsDv, *DsDa; mjtNum *DyDq, *DyDv, *DyDa, *DsDq, *DsDv, *DsDa;
DyDq = DyDv = DyDa = DsDq = DsDv = DsDa = NULL; DyDq = DyDv = DyDa = DsDq = DsDv = DsDa = NULL;
mjMARKSTACK; mj_markStack(d);
// allocate transposed matrices // allocate transposed matrices
mjtNum *AT = A ? mj_stackAllocNum(d, ndx*ndx) : NULL; // state-transition matrix (transposed) mjtNum *AT = A ? mj_stackAllocNum(d, ndx*ndx) : NULL; // state-transition matrix (transposed)
@@ -610,7 +611,7 @@ void mjd_transitionFD(const mjModel* m, mjData* d, mjtNum eps, mjtByte flg_cente
if (C) mju_transpose(C, CT, ndx, ns); if (C) mju_transpose(C, CT, ndx, ns);
if (D) mju_transpose(D, DT, nu, ns); if (D) mju_transpose(D, DT, nu, ns);
mjFREESTACK; mj_freeStack(d);
} }
// finite differenced Jacobians of (force, sensors) = mj_inverse(state, acceleration) // finite differenced Jacobians of (force, sensors) = mj_inverse(state, acceleration)
@@ -634,7 +635,6 @@ void mjd_inverseFD(const mjModel* m, mjData* d, mjtNum eps, mjtByte flg_actuatio
mjtNum *DsDq, mjtNum *DsDv, mjtNum *DsDa, mjtNum *DsDq, mjtNum *DsDv, mjtNum *DsDa,
mjtNum *DmDq) { mjtNum *DmDq) {
int nq = m->nq, nv = m->nv, nM = m->nM, ns = m->nsensordata; int nq = m->nq, nv = m->nv, nM = m->nM, ns = m->nsensordata;
mjMARKSTACK;
if (m->opt.integrator == mjINT_RK4) { if (m->opt.integrator == mjINT_RK4) {
mjERROR("RK4 integrator is not supported"); mjERROR("RK4 integrator is not supported");
@@ -648,6 +648,7 @@ void mjd_inverseFD(const mjModel* m, mjData* d, mjtNum eps, mjtByte flg_actuatio
int skipsensor = !DsDq && !DsDv && !DsDa; int skipsensor = !DsDq && !DsDv && !DsDa;
// local vectors // local vectors
mj_markStack(d);
mjtNum *pos = mj_stackAllocNum(d, nq); // position mjtNum *pos = mj_stackAllocNum(d, nq); // position
mjtNum *force = mj_stackAllocNum(d, nv); // force mjtNum *force = mj_stackAllocNum(d, nv); // force
mjtNum *force_plus = mj_stackAllocNum(d, nv); // nudged force mjtNum *force_plus = mj_stackAllocNum(d, nv); // nudged force
@@ -730,5 +731,5 @@ void mjd_inverseFD(const mjModel* m, mjData* d, mjtNum eps, mjtByte flg_actuatio
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
+12 -12
View File
@@ -199,7 +199,7 @@ void mj_fwdActuation(const mjModel* m, mjData* d) {
} }
// local, clamped copy of ctrl // local, clamped copy of ctrl
mjMARKSTACK; mj_markStack(d);
mjtNum *ctrl = mj_stackAllocNum(d, nu); mjtNum *ctrl = mj_stackAllocNum(d, nu);
if (mjDISABLED(mjDSBL_CLAMPCTRL)) { if (mjDISABLED(mjDSBL_CLAMPCTRL)) {
mju_copy(ctrl, d->ctrl, nu); mju_copy(ctrl, d->ctrl, nu);
@@ -392,7 +392,7 @@ void mj_fwdActuation(const mjModel* m, mjData* d) {
} }
} }
mjFREESTACK; mj_freeStack(d);
TM_END(mjTIMER_ACTUATION); TM_END(mjTIMER_ACTUATION);
} }
@@ -401,7 +401,7 @@ void mj_fwdActuation(const mjModel* m, mjData* d) {
// add up all non-constraint forces, compute qacc_smooth // add up all non-constraint forces, compute qacc_smooth
void mj_fwdAcceleration(const mjModel* m, mjData* d) { void mj_fwdAcceleration(const mjModel* m, mjData* d) {
TM_START; TM_START;
mjMARKSTACK; mj_markStack(d);
int nv = m->nv; int nv = m->nv;
// qforce = sum of all non-constraint forces // qforce = sum of all non-constraint forces
@@ -413,7 +413,7 @@ void mj_fwdAcceleration(const mjModel* m, mjData* d) {
// qacc_smooth = M \ qfr_smooth // qacc_smooth = M \ qfr_smooth
mj_solveM(m, d, d->qacc_smooth, d->qfrc_smooth, 1); mj_solveM(m, d, d->qacc_smooth, d->qfrc_smooth, 1);
mjFREESTACK; mj_freeStack(d);
TM_END(mjTIMER_ACCELERATION); TM_END(mjTIMER_ACCELERATION);
} }
@@ -425,7 +425,7 @@ static void warmstart(const mjModel* m, mjData* d) {
// warmstart with best of (qacc_warmstart, qacc_smooth) // warmstart with best of (qacc_warmstart, qacc_smooth)
if (!mjDISABLED(mjDSBL_WARMSTART)) { if (!mjDISABLED(mjDSBL_WARMSTART)) {
mjMARKSTACK; mj_markStack(d);
mjtNum* jar = mj_stackAllocNum(d, nefc); mjtNum* jar = mj_stackAllocNum(d, nefc);
// start with qacc = qacc_warmstart // start with qacc = qacc_warmstart
@@ -479,7 +479,7 @@ static void warmstart(const mjModel* m, mjData* d) {
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
// coldstart with qacc = qacc_smooth, efc_force = 0 // coldstart with qacc = qacc_smooth, efc_force = 0
@@ -589,7 +589,7 @@ static void mj_advance(const mjModel* m, mjData* d,
// Euler integrator, semi-implicit in velocity, possibly skipping factorisation // Euler integrator, semi-implicit in velocity, possibly skipping factorisation
void mj_EulerSkip(const mjModel* m, mjData* d, int skipfactor) { void mj_EulerSkip(const mjModel* m, mjData* d, int skipfactor) {
int nv = m->nv, nM = m->nM; int nv = m->nv, nM = m->nM;
mjMARKSTACK; mj_markStack(d);
mjtNum* qfrc = mj_stackAllocNum(d, nv); mjtNum* qfrc = mj_stackAllocNum(d, nv);
mjtNum* qacc = mj_stackAllocNum(d, nv); mjtNum* qacc = mj_stackAllocNum(d, nv);
@@ -633,7 +633,7 @@ void mj_EulerSkip(const mjModel* m, mjData* d, int skipfactor) {
// advance state and time // advance state and time
mj_advance(m, d, d->act_dot, qacc, NULL); mj_advance(m, d, d->act_dot, qacc, NULL);
mjFREESTACK; mj_freeStack(d);
} }
@@ -665,7 +665,6 @@ void mj_RungeKutta(const mjModel* m, mjData* d, int N) {
mjtNum C[9], T[9], *X[10], *F[10], *dX; mjtNum C[9], T[9], *X[10], *F[10], *dX;
const mjtNum* A = (N == 4 ? RK4_A : 0); const mjtNum* A = (N == 4 ? RK4_A : 0);
const mjtNum* B = (N == 4 ? RK4_B : 0); const mjtNum* B = (N == 4 ? RK4_B : 0);
mjMARKSTACK;
// check order // check order
if (!A) { if (!A) {
@@ -673,6 +672,7 @@ void mj_RungeKutta(const mjModel* m, mjData* d, int N) {
} }
// allocate space for intermediate solutions // allocate space for intermediate solutions
mj_markStack(d);
dX = mj_stackAllocNum(d, 2*nv+na); dX = mj_stackAllocNum(d, 2*nv+na);
for (int i=0; i < N; i++) { for (int i=0; i < N; i++) {
X[i] = mj_stackAllocNum(d, nq+nv+na); X[i] = mj_stackAllocNum(d, nq+nv+na);
@@ -746,7 +746,7 @@ void mj_RungeKutta(const mjModel* m, mjData* d, int N) {
// advance state and time // advance state and time
mj_advance(m, d, dX+2*nv, dX+nv, dX); mj_advance(m, d, dX+2*nv, dX+nv, dX);
mjFREESTACK; mj_freeStack(d);
} }
@@ -755,7 +755,7 @@ void mj_RungeKutta(const mjModel* m, mjData* d, int N) {
void mj_implicitSkip(const mjModel* m, mjData* d, int skipfactor) { void mj_implicitSkip(const mjModel* m, mjData* d, int skipfactor) {
int nv = m->nv; int nv = m->nv;
mjMARKSTACK; mj_markStack(d);
mjtNum* qfrc = mj_stackAllocNum(d, nv); mjtNum* qfrc = mj_stackAllocNum(d, nv);
mjtNum* qacc = mj_stackAllocNum(d, nv); mjtNum* qacc = mj_stackAllocNum(d, nv);
@@ -810,7 +810,7 @@ void mj_implicitSkip(const mjModel* m, mjData* d, int skipfactor) {
// advance state and time // advance state and time
mj_advance(m, d, d->act_dot, qacc, NULL); mj_advance(m, d, d->act_dot, qacc, NULL);
mjFREESTACK; mj_freeStack(d);
} }
+9 -8
View File
@@ -95,7 +95,7 @@ static void mj_discreteAcc(const mjModel* m, mjData* d) {
int nv = m->nv, dof_damping; int nv = m->nv, dof_damping;
mjtNum *qacc = d->qacc; mjtNum *qacc = d->qacc;
mjMARKSTACK; mj_markStack(d);
mjtNum* qfrc = mj_stackAllocNum(d, nv); mjtNum* qfrc = mj_stackAllocNum(d, nv);
// use selected integrator // use selected integrator
@@ -119,6 +119,7 @@ static void mj_discreteAcc(const mjModel* m, mjData* d) {
// if disabled or no dof damping, nothing to do // if disabled or no dof damping, nothing to do
if (!dof_damping) { if (!dof_damping) {
mj_freeStack(d);
return; return;
} }
@@ -168,7 +169,7 @@ static void mj_discreteAcc(const mjModel* m, mjData* d) {
// solve for qacc: qfrc = M * qacc // solve for qacc: qfrc = M * qacc
mj_solveM(m, d, qacc, qfrc, 1); mj_solveM(m, d, qacc, qfrc, 1);
mjFREESTACK; mj_freeStack(d);
} }
@@ -185,7 +186,7 @@ void mj_invConstraint(const mjModel* m, mjData* d) {
return; return;
} }
mjMARKSTACK; mj_markStack(d);
mjtNum* jar = mj_stackAllocNum(d, nefc); mjtNum* jar = mj_stackAllocNum(d, nefc);
// compute jar = Jac*qacc - aref // compute jar = Jac*qacc - aref
@@ -195,7 +196,7 @@ void mj_invConstraint(const mjModel* m, mjData* d) {
// call update function // call update function
mj_constraintUpdate(m, d, jar, NULL, 0); mj_constraintUpdate(m, d, jar, NULL, 0);
mjFREESTACK; mj_freeStack(d);
TM_END(mjTIMER_CONSTRAINT); TM_END(mjTIMER_CONSTRAINT);
} }
@@ -205,7 +206,7 @@ void mj_invConstraint(const mjModel* m, mjData* d) {
void mj_inverseSkip(const mjModel* m, mjData* d, void mj_inverseSkip(const mjModel* m, mjData* d,
int skipstage, int skipsensor) { int skipstage, int skipsensor) {
TM_START; TM_START;
mjMARKSTACK; mj_markStack(d);
mjtNum* qacc; mjtNum* qacc;
int nv = m->nv; int nv = m->nv;
@@ -258,7 +259,7 @@ void mj_inverseSkip(const mjModel* m, mjData* d,
mju_copy(d->qacc, qacc, nv); mju_copy(d->qacc, qacc, nv);
} }
mjFREESTACK; mj_freeStack(d);
TM_END(mjTIMER_INVERSE); TM_END(mjTIMER_INVERSE);
} }
@@ -277,7 +278,6 @@ void mj_inverse(const mjModel* m, mjData* d) {
void mj_compareFwdInv(const mjModel* m, mjData* d) { void mj_compareFwdInv(const mjModel* m, mjData* d) {
int nv = m->nv, nefc = d->nefc; int nv = m->nv, nefc = d->nefc;
mjtNum *qforce, *dif, *save_qfrc_constraint, *save_efc_force; mjtNum *qforce, *dif, *save_qfrc_constraint, *save_efc_force;
mjMARKSTACK;
// clear result, return if no constraints // clear result, return if no constraints
d->solver_fwdinv[0] = d->solver_fwdinv[1] = 0; d->solver_fwdinv[0] = d->solver_fwdinv[1] = 0;
@@ -286,6 +286,7 @@ void mj_compareFwdInv(const mjModel* m, mjData* d) {
} }
// allocate // allocate
mj_markStack(d);
qforce = mj_stackAllocNum(d, nv); qforce = mj_stackAllocNum(d, nv);
dif = mj_stackAllocNum(d, nv); dif = mj_stackAllocNum(d, nv);
save_qfrc_constraint = mj_stackAllocNum(d, nv); save_qfrc_constraint = mj_stackAllocNum(d, nv);
@@ -313,5 +314,5 @@ void mj_compareFwdInv(const mjModel* m, mjData* d) {
mju_copy(d->qfrc_constraint, save_qfrc_constraint, nv); mju_copy(d->qfrc_constraint, save_qfrc_constraint, nv);
mju_copy(d->efc_force, save_efc_force, nefc); mju_copy(d->efc_force, save_efc_force, nefc);
mjFREESTACK; mj_freeStack(d);
} }
+133 -51
View File
@@ -26,7 +26,7 @@
#include <mujoco/mjmacro.h> #include <mujoco/mjmacro.h>
#include <mujoco/mjplugin.h> #include <mujoco/mjplugin.h>
#include <mujoco/mjxmacro.h> #include <mujoco/mjxmacro.h>
#include "engine/engine_array_safety.h" // IWYU pragma: keep #include "engine/engine_crossplatform.h"
#include "engine/engine_resource.h" #include "engine/engine_resource.h"
#include "engine/engine_macro.h" #include "engine/engine_macro.h"
#include "engine/engine_plugin.h" #include "engine/engine_plugin.h"
@@ -35,6 +35,11 @@
#include "engine/engine_util_misc.h" #include "engine/engine_util_misc.h"
#include "engine/engine_vfs.h" #include "engine/engine_vfs.h"
#ifdef ADDRESS_SANITIZER
#include <sanitizer/asan_interface.h>
#include <sanitizer/common_interface_defs.h>
#endif
#ifdef MEMORY_SANITIZER #ifdef MEMORY_SANITIZER
#include <sanitizer/msan_interface.h> #include <sanitizer/msan_interface.h>
#endif #endif
@@ -43,8 +48,30 @@
#pragma warning (disable: 4305) // disable MSVC warning: truncation from 'double' to 'float' #pragma warning (disable: 4305) // disable MSVC warning: truncation from 'double' to 'float'
#endif #endif
// add red zone padding when built with asan, to detect out-of-bound accesses
#ifdef ADDRESS_SANITIZER
#define mjREDZONE 32
#else
#define mjREDZONE 0
#endif
static const int MAX_ARRAY_SIZE = INT_MAX / 4; static const int MAX_ARRAY_SIZE = INT_MAX / 4;
// compute a % b with a fast code path if the second argument is a power of 2
static inline size_t fastmod(size_t a, size_t b) {
// (b & (b - 1)) == 0 implies that b is a power of 2
if (mjLIKELY((b & (b - 1)) == 0)) {
return a & (b - 1);
}
return a % b;
}
typedef struct {
size_t pbase; // value of d->pbase immediately before mj_markStack
size_t pstack; // value of d->pstack immediately before mj_markStack
void* pc; // program counter of the call site of mj_markStack (only set when under asan)
} mjStackFrame;
//------------------------------ mjLROpt ----------------------------------------------------------- //------------------------------ mjLROpt -----------------------------------------------------------
// set default options for length range computation // set default options for length range computation
@@ -86,6 +113,9 @@ void mj_defaultSolRefImp(mjtNum* solref, mjtNum* solimp) {
// set model options to default values // set model options to default values
void mj_defaultOption(mjOption* opt) { void mj_defaultOption(mjOption* opt) {
// fill opt with zeros in case struct is padded
memset(opt, 0, sizeof(mjOption));
// timing parameters // timing parameters
opt->timestep = 0.002; opt->timestep = 0.002;
opt->apirate = 100; opt->apirate = 100;
@@ -93,6 +123,7 @@ void mj_defaultOption(mjOption* opt) {
// solver parameters // solver parameters
opt->impratio = 1; opt->impratio = 1;
opt->tolerance = 1e-8; opt->tolerance = 1e-8;
opt->ls_tolerance = 0.01;
opt->noslip_tolerance = 1e-6; opt->noslip_tolerance = 1e-6;
opt->mpr_tolerance = 1e-6; opt->mpr_tolerance = 1e-6;
@@ -120,6 +151,7 @@ void mj_defaultOption(mjOption* opt) {
opt->jacobian = mjJAC_AUTO; opt->jacobian = mjJAC_AUTO;
opt->solver = mjSOL_NEWTON; opt->solver = mjSOL_NEWTON;
opt->iterations = 100; opt->iterations = 100;
opt->ls_iterations = 50;
opt->noslip_iterations = 0; opt->noslip_iterations = 0;
opt->mpr_iterations = 50; opt->mpr_iterations = 50;
opt->disableflags = 0; opt->disableflags = 0;
@@ -656,7 +688,7 @@ void mj_saveModel(const mjModel* m, const char* filename, void* buffer, int buff
// load model from binary MJB resource // load model from binary MJB resource
static mjModel* _mj_loadModel(const char* filename, int vfs_provider) { mjModel* mj_loadModel(const char* filename, const mjVFS* vfs) {
int header[NHEADER] = {0}; int header[NHEADER] = {0};
int expected_header[NHEADER] = {ID, sizeof(mjtNum), getnint(), getnsize(), getnptr()}; int expected_header[NHEADER] = {ID, sizeof(mjtNum), getnint(), getnsize(), getnptr()};
int ints[256]; int ints[256];
@@ -665,8 +697,11 @@ static mjModel* _mj_loadModel(const char* filename, int vfs_provider) {
mjModel *m = 0; mjModel *m = 0;
mjResource* r = NULL; mjResource* r = NULL;
if((r = mju_openResource(filename, vfs_provider)) == NULL) { // first try vfs, otherwise try a provider or OS filesystem
return NULL; if ((r = mju_openVfsResource(filename, vfs)) == NULL) {
if ((r = mju_openResource(filename)) == NULL) {
return NULL;
}
} }
const void* buffer = NULL; const void* buffer = NULL;
@@ -780,27 +815,6 @@ static mjModel* _mj_loadModel(const char* filename, int vfs_provider) {
// load model from binary MJB file
// if vfs is not NULL, look up file in vfs before reading from disk
mjModel* mj_loadModel(const char* filename, const mjVFS* vfs) {
if (vfs == NULL) {
return _mj_loadModel(filename, 0);
}
int index = mj_registerVfsProvider(vfs);
if (index < 1) {
mjERROR("could not allocate memory");
return NULL;
}
mjModel* model = _mj_loadModel(filename, index);
mjp_unregisterResourceProvider(index);
return model;
}
// de-allocate mjModel // de-allocate mjModel
void mj_deleteModel(mjModel* m) { void mj_deleteModel(mjModel* m) {
if (m) { if (m) {
@@ -841,11 +855,11 @@ static void makeDSparse(const mjModel* m, mjData* d) {
int* rowadr = d->D_rowadr; int* rowadr = d->D_rowadr;
int* colind = d->D_colind; int* colind = d->D_colind;
mjMARKSTACK; mj_markStack(d);
int* remaining = mj_stackAllocInt(d, nv); int* remaining = mj_stackAllocInt(d, nv);
// compute rownnz // compute rownnz
memset(rownnz, 0, nv * sizeof(int)); mju_zeroInt(rownnz, nv);
for (int i = nv - 1; i >= 0; i--) { for (int i = nv - 1; i >= 0; i--) {
// init at diagonal // init at diagonal
int j = i; int j = i;
@@ -865,7 +879,7 @@ static void makeDSparse(const mjModel* m, mjData* d) {
} }
// populate colind // populate colind
memcpy(remaining, rownnz, nv * sizeof(int)); mju_copyInt(remaining, rownnz, nv);
for (int i = nv - 1; i >= 0; i--) { for (int i = nv - 1; i >= 0; i--) {
// init at diagonal // init at diagonal
remaining[i]--; remaining[i]--;
@@ -889,7 +903,7 @@ static void makeDSparse(const mjModel* m, mjData* d) {
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -902,7 +916,7 @@ static void makeBSparse(const mjModel* m, mjData* d) {
int* colind = d->B_colind; int* colind = d->B_colind;
// set rownnz to subtree dofs counts, including self // set rownnz to subtree dofs counts, including self
memset(rownnz, 0, sizeof(int) * nbody); mju_zeroInt(rownnz, nbody);
for (int i = nbody - 1; i > 0; i--) { for (int i = nbody - 1; i > 0; i--) {
rownnz[i] += m->body_dofnum[i]; rownnz[i] += m->body_dofnum[i];
rownnz[m->body_parentid[i]] += rownnz[i]; rownnz[m->body_parentid[i]] += rownnz[i];
@@ -934,9 +948,9 @@ static void makeBSparse(const mjModel* m, mjData* d) {
} }
// allocate and clear incremental row counts // allocate and clear incremental row counts
mjMARKSTACK; mj_markStack(d);
int* cnt = mj_stackAllocInt(d, nbody); int* cnt = mj_stackAllocInt(d, nbody);
memset(cnt, 0, sizeof(int) * nbody); mju_zeroInt(cnt, nbody);
// add subtree dofs to colind // add subtree dofs to colind
for (int i = nbody - 1; i > 0; i--) { for (int i = nbody - 1; i > 0; i--) {
@@ -982,7 +996,7 @@ static void makeBSparse(const mjModel* m, mjData* d) {
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -1105,6 +1119,8 @@ static mjData* _makeData(const mjModel* m) {
} }
} }
d->threadpool = 0;
return d; return d;
} }
@@ -1193,6 +1209,8 @@ mjData* mj_copyData(mjData* dest, const mjModel* m, const mjData* src) {
} }
} }
dest->threadpool = src->threadpool;
return dest; return dest;
} }
@@ -1200,12 +1218,12 @@ mjData* mj_copyData(mjData* dest, const mjModel* m, const mjData* src) {
// allocate memory from the mjData arena // allocate memory from the mjData arena
void* mj_arenaAlloc(mjData* d, size_t bytes, size_t alignment) { void* mj_arenaAlloc(mjData* d, size_t bytes, size_t alignment) {
size_t misalignment = d->parena % alignment; size_t misalignment = fastmod(d->parena, alignment);
size_t padding = misalignment ? alignment - misalignment : 0; size_t padding = misalignment ? alignment - misalignment : 0;
// check size // check size
size_t bytes_available = d->narena - d->pstack; size_t bytes_available = d->narena - d->pstack;
if (d->parena + padding + bytes > bytes_available) { if (mjUNLIKELY(d->parena + padding + bytes > bytes_available)) {
return NULL; return NULL;
} }
@@ -1231,17 +1249,10 @@ void* mj_arenaAlloc(mjData* d, size_t bytes, size_t alignment) {
// declared inline so that modular arithmetic with specific alignments can be optimized out // declared inline so that modular arithmetic with specific alignments can be optimized out
static inline void* stackalloc(mjData* d, size_t size, size_t alignment) { static inline void* stackalloc(mjData* d, size_t size, size_t alignment) {
// return NULL if empty // return NULL if empty
if (!size) { if (mjUNLIKELY(!size)) {
return NULL; return NULL;
} }
// add red zone padding when built with asan, to detect out-of-bound accesses
#ifdef ADDRESS_SANITIZER
#define mjREDZONE 32
#else
#define mjREDZONE 0
#endif
// size of entire arena/stack in bytes // size of entire arena/stack in bytes
size_t stack_size_bytes = d->narena; size_t stack_size_bytes = d->narena;
@@ -1255,7 +1266,7 @@ static inline void* stackalloc(mjData* d, size_t size, size_t alignment) {
uintptr_t start_ptr = end_ptr - (size + mjREDZONE); uintptr_t start_ptr = end_ptr - (size + mjREDZONE);
// align the pointer // align the pointer
start_ptr -= start_ptr % alignment; start_ptr -= fastmod(start_ptr, alignment);
// new top of the stack // new top of the stack
uintptr_t new_pstack_ptr = start_ptr - mjREDZONE; uintptr_t new_pstack_ptr = start_ptr - mjREDZONE;
@@ -1268,8 +1279,8 @@ static inline void* stackalloc(mjData* d, size_t size, size_t alignment) {
// check size // check size
size_t stack_available_bytes = end_ptr - ((uintptr_t)d->arena + d->parena); size_t stack_available_bytes = end_ptr - ((uintptr_t)d->arena + d->parena);
size_t stack_required_bytes = end_ptr - new_pstack_ptr; size_t stack_required_bytes = end_ptr - new_pstack_ptr;
if (stack_required_bytes > stack_available_bytes) { if (mjUNLIKELY(stack_required_bytes > stack_available_bytes)) {
mju_error("mjData stack overflow: max = %zu, available = %zu, requested = %zu " mju_error("mj_stackAlloc: insufficient memory: max = %zu, available = %zu, requested = %zu "
"(ne = %d, nf = %d, nefc = %d, ncon = %d)", "(ne = %d, nf = %d, nefc = %d, ncon = %d)",
stack_size_bytes, stack_available_bytes, stack_required_bytes, stack_size_bytes, stack_available_bytes, stack_required_bytes,
d->ne, d->nf, d->nefc, d->ncon); d->ne, d->nf, d->nefc, d->ncon);
@@ -1285,16 +1296,14 @@ static inline void* stackalloc(mjData* d, size_t size, size_t alignment) {
} }
// store new stack usage in the red zone // store new stack usage in the red zone
ASAN_UNPOISON_MEMORY_REGION(new_pstack_ptr, sizeof(size_t)); ASAN_UNPOISON_MEMORY_REGION((void*)new_pstack_ptr, sizeof(size_t));
*(size_t*)new_pstack_ptr = usage; *(size_t*)new_pstack_ptr = usage;
ASAN_POISON_MEMORY_REGION(new_pstack_ptr, sizeof(size_t)); ASAN_POISON_MEMORY_REGION((void*)new_pstack_ptr, sizeof(size_t));
// unpoison the actual usable allocation // unpoison the actual usable allocation
ASAN_UNPOISON_MEMORY_REGION(start_ptr, size); ASAN_UNPOISON_MEMORY_REGION((void*)start_ptr, size);
#endif #endif
#undef mjREDZONE
// update pstack and max usage statistics // update pstack and max usage statistics
d->pstack = new_pstack; d->pstack = new_pstack;
d->maxuse_stack = mjMAX(d->maxuse_stack, usage); d->maxuse_stack = mjMAX(d->maxuse_stack, usage);
@@ -1303,6 +1312,73 @@ static inline void* stackalloc(mjData* d, size_t size, size_t alignment) {
return (void*)start_ptr; return (void*)start_ptr;
} }
// mjData mark stack frame
#ifdef ADDRESS_SANITIZER
__attribute__((noinline))
#endif
void mj_markStack(mjData* d) {
size_t pstack_old = d->pstack;
mjStackFrame* s =
(mjStackFrame*) stackalloc(d, sizeof(mjStackFrame), _Alignof(mjStackFrame));
s->pbase = d->pbase;
s->pstack = pstack_old;
#ifdef ADDRESS_SANITIZER
// store the program counter to the caller so that we can compare against mj_freeStack later
s->pc = __sanitizer_return_address();
#endif
d->pbase = d->pstack - mjREDZONE;
}
// mjData free stack frame
#ifdef ADDRESS_SANITIZER
__attribute__((noinline))
#endif
void mj_freeStack(mjData* d) {
if (mjUNLIKELY(!d->pbase)) {
return;
}
mjStackFrame* s = (mjStackFrame*) ((char*)d->arena + d->narena - d->pbase);
#ifdef ADDRESS_SANITIZER
#define mjSYMBOLIZELEN 256
// symbolize s->pc to get the function name of most recent caller to mj_markStack
char markstack_func[mjSYMBOLIZELEN];
__sanitizer_symbolize_pc(s->pc, "%f", markstack_func, mjSYMBOLIZELEN);
markstack_func[mjSYMBOLIZELEN - 1] = '\0';
// symbolize current program counter to get the function name of caller to this function
char freestack_func[mjSYMBOLIZELEN];
__sanitizer_symbolize_pc(__sanitizer_return_address(), "%f", freestack_func, mjSYMBOLIZELEN);
freestack_func[mjSYMBOLIZELEN - 1] = '\0';
// raise an error if caller function name doesn't match the most recent caller of mj_markStack
if (strncmp(markstack_func, freestack_func, mjSYMBOLIZELEN)) {
char dbginfo[mjSYMBOLIZELEN];
__sanitizer_symbolize_pc(
s->pc, "mj_markStack %F at %S has no corresponding mj_freeStack",
dbginfo, sizeof(dbginfo));
dbginfo[mjSYMBOLIZELEN - 1] = '\0';
mjERROR("%s", dbginfo);
}
#undef mjSYMBOLIZELEN
#endif
// restore pbase and pstack
d->pbase = s->pbase;
d->pstack = s->pstack;
// if running under asan, poison the newly freed memory region
#ifdef ADDRESS_SANITIZER
ASAN_POISON_MEMORY_REGION((char*)d->arena + d->parena, d->narena - d->pstack - d->parena);
#endif
}
void* mj_stackAlloc(mjData* d, size_t bytes, size_t alignment) { void* mj_stackAlloc(mjData* d, size_t bytes, size_t alignment) {
return stackalloc(d, bytes, alignment); return stackalloc(d, bytes, alignment);
} }
@@ -1329,6 +1405,7 @@ static void _resetData(const mjModel* m, mjData* d, unsigned char debug_value) {
// clear stack pointer // clear stack pointer
d->pstack = 0; d->pstack = 0;
d->pbase = 0;
// clear arena pointers // clear arena pointers
d->parena = 0; d->parena = 0;
@@ -1496,6 +1573,11 @@ void mj_resetDataKeyframe(const mjModel* m, mjData* d, int key) {
// de-allocate mjData // de-allocate mjData
void mj_deleteData(mjData* d) { void mj_deleteData(mjData* d) {
if (d) { if (d) {
#ifdef ADDRESS_SANITIZER
// raise an error if there's a dangling stack frame
mj_freeStack(d);
#endif
// destroy plugin instances // destroy plugin instances
for (int i = 0; i < d->nplugin; ++i) { for (int i = 0; i < d->nplugin; ++i) {
const mjpPlugin* plugin = mjp_getPluginAtSlot(d->plugin[i]); const mjpPlugin* plugin = mjp_getPluginAtSlot(d->plugin[i]);
+17
View File
@@ -18,6 +18,7 @@
#include <mujoco/mjdata.h> #include <mujoco/mjdata.h>
#include <mujoco/mjexport.h> #include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h> #include <mujoco/mjmodel.h>
#include <mujoco/mjxmacro.h>
#ifdef __cplusplus #ifdef __cplusplus
#include <cstddef> #include <cstddef>
@@ -105,6 +106,12 @@ MJAPI void mj_resetDataKeyframe(const mjModel* m, mjData* d, int key);
// mjData arena allocate // mjData arena allocate
MJAPI void* mj_arenaAlloc(mjData* d, size_t bytes, size_t alignment); MJAPI void* mj_arenaAlloc(mjData* d, size_t bytes, size_t alignment);
// mjData mark stack frame
MJAPI void mj_markStack(mjData* d);
// mjData free stack frame
MJAPI void mj_freeStack(mjData* d);
// mjData stack allocate // mjData stack allocate
MJAPI void* mj_stackAlloc(mjData* d, size_t bytes, size_t alignment); MJAPI void* mj_stackAlloc(mjData* d, size_t bytes, size_t alignment);
@@ -117,6 +124,16 @@ MJAPI int* mj_stackAllocInt(mjData* d, int size);
// de-allocate data // de-allocate data
MJAPI void mj_deleteData(mjData* d); MJAPI void mj_deleteData(mjData* d);
// clear arena pointers in mjData
static inline void mj_clearEfc(mjData* d) {
#define X(type, name, nr, nc) d->name = NULL;
MJDATA_ARENA_POINTERS
#undef X
d->nefc = 0;
d->nisland = 0;
d->contact = (mjContact*) d->arena;
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
+125 -72
View File
@@ -16,17 +16,15 @@
#include <stdio.h> #include <stdio.h>
#include <stddef.h> #include <stddef.h>
#include <string.h>
#include <mujoco/mjdata.h> #include <mujoco/mjdata.h>
#include <mujoco/mjmacro.h>
#include <mujoco/mjmodel.h> #include <mujoco/mjmodel.h>
#include <mujoco/mjxmacro.h> #include <mujoco/mjxmacro.h>
#include "engine/engine_core_constraint.h" #include "engine/engine_core_constraint.h"
#include "engine/engine_crossplatform.h"
#include "engine/engine_io.h" #include "engine/engine_io.h"
#include "engine/engine_support.h" #include "engine/engine_support.h"
#include "engine/engine_util_errmem.h" #include "engine/engine_util_errmem.h"
#include "engine/engine_util_misc.h"
#ifdef MEMORY_SANITIZER #ifdef MEMORY_SANITIZER
#include <sanitizer/msan_interface.h> #include <sanitizer/msan_interface.h>
@@ -75,7 +73,7 @@ int mj_floodFill(int* island, int nr, const int* rownnz, const int* rowadr, cons
island[v] = nisland; island[v] = nisland;
// push adjacent vertices onto stack // push adjacent vertices onto stack
memcpy(stack + nstack, colind + rowadr[v], rownnz[v]*sizeof(int)); mju_copyInt(stack + nstack, colind + rowadr[v], rownnz[v]);
nstack += rownnz[v]; nstack += rownnz[v];
} }
@@ -160,13 +158,12 @@ static int countMaxEdge(const mjModel* m, const mjData* d) {
// return id of next tree in Jacobian row i that is different from tree, -1 if not found // return id of next tree in Jacobian row i that is different from tree, -1 if not found
// write the index of the found tree to *index if given // start search from *index
// start search from *index if given, otherwise 0 // write the index of the found tree to *index
// if J is (dense/sparse) *index is the (column/nonzro) index, respectively // if J is (dense/sparse) *index is the (column/nonzero) index, respectively
static int treeNext(const mjModel* m, const mjData* d, int tree, int i, int *index) { static int treeNext(const mjModel* m, const mjData* d, int tree, int i, int *index) {
int tree_next = -1; int tree_next = -1;
int j0 = index ? *index : 0; // start searching at *index if given, otherwise 0 int j; // local loop variable, saved to *index
int j; // loop variable, saved to *index
// sparse // sparse
if (mj_isSparse(m)) { if (mj_isSparse(m)) {
@@ -174,7 +171,7 @@ static int treeNext(const mjModel* m, const mjData* d, int tree, int i, int *ind
int* colind = d->efc_J_colind + d->efc_J_rowadr[i]; int* colind = d->efc_J_colind + d->efc_J_rowadr[i];
// loop over remaining nonzeros, look for different tree // loop over remaining nonzeros, look for different tree
for (j=j0; j < rownnz; j++) { for (j=(*index); j < rownnz; j++) {
int tree_j = m->dof_treeid[colind[j]]; int tree_j = m->dof_treeid[colind[j]];
if (tree_j != tree) { if (tree_j != tree) {
// found different tree // found different tree
@@ -189,7 +186,7 @@ static int treeNext(const mjModel* m, const mjData* d, int tree, int i, int *ind
int nv = m->nv; int nv = m->nv;
// scan row, look for different tree // scan row, look for different tree
for (j=j0; j < nv; j++) { for (j=(*index); j < nv; j++) {
if (d->efc_J[nv*i + j]) { if (d->efc_J[nv*i + j]) {
int tree_j = m->dof_treeid[j]; int tree_j = m->dof_treeid[j];
if (tree_j != tree) { if (tree_j != tree) {
@@ -202,13 +199,94 @@ static int treeNext(const mjModel* m, const mjData* d, int tree, int i, int *ind
} }
// save last index // save last index
if (index) *index = j; *index = j;
return tree_next; return tree_next;
} }
// find first and possibly second nonegative tree ids in Jacobian row i
// if row i is special-cased (no more trees), return -1
// otherwise call treeNext, starting scan at index 0, return index
static int treeFirst(const mjModel* m, const mjData* d, int tree[2], int i) {
int efc_type = d->efc_type[i];
int efc_id = d->efc_id[i];
// clear outputs
tree[0] = -1;
tree[1] = -1;
// ==== fast handling of special cases
// joint friction
if (efc_type == mjCNSTR_FRICTION_DOF) {
tree[0] = m->dof_treeid[efc_id];
return -1;
}
// joint limit
if (efc_type == mjCNSTR_LIMIT_JOINT) {
tree[0] = m->dof_treeid[m->jnt_dofadr[efc_id]];
return -1;
}
// contact
if (efc_type == mjCNSTR_CONTACT_FRICTIONLESS ||
efc_type == mjCNSTR_CONTACT_PYRAMIDAL ||
efc_type == mjCNSTR_CONTACT_ELLIPTIC) {
tree[0] = m->body_treeid[m->geom_bodyid[d->contact[efc_id].geom1]];
tree[1] = m->body_treeid[m->geom_bodyid[d->contact[efc_id].geom2]];
// handle static bodies
if (tree[0] < 0) {
if (tree[1] < 0) {
mjERROR("contact %d is between two static bodies", efc_id); // SHOULD NOT OCCUR
} else {
int tmp = tree[0];
tree[0] = tree[1];
tree[1] = tmp;
}
}
return -1;
}
// connect or weld constraints
if (efc_type == mjCNSTR_EQUALITY) {
mjtEq eq_type = m->eq_type[efc_id];
if (eq_type == mjEQ_CONNECT || eq_type == mjEQ_WELD) {
tree[0] = m->body_treeid[m->eq_obj1id[efc_id]];
tree[1] = m->body_treeid[m->eq_obj2id[efc_id]];
// handle static bodies
if (tree[0] < 0) {
if (tree[1] < 0) {
mjERROR("equality %d is between two static bodies", efc_id); // SHOULD NOT OCCUR
} else {
int tmp = tree[0];
tree[0] = tree[1];
tree[1] = tmp;
}
}
return -1;
}
}
// ==== generic case: scan Jacobian
int index = 0;
tree[0] = treeNext(m, d, -1, i, &index);
if (tree[0] < 0) {
mjERROR("no tree found for constraint %d", i); // SHOULD NOT OCCUR
}
return index;
}
// add 0 edges, 1 self-edge or 2 flipped edges to array, increment treenedge // add 0 edges, 1 self-edge or 2 flipped edges to array, increment treenedge
// return current number of edges // return current number of edges
static int addEdge(int* treenedge, int* edge, int nedge, int tree1, int tree2, int nedge_max) { static int addEdge(int* treenedge, int* edge, int nedge, int tree1, int tree2, int nedge_max) {
@@ -273,10 +351,9 @@ static int findEdges(const mjModel* m, const mjData* d, int* treenedge, int* edg
int nefc = d->nefc; int nefc = d->nefc;
int efc_type = -1; int efc_type = -1;
int efc_id = -1; int efc_id = -1;
int tree1, tree2;
// clear treenedge // clear treenedge
memset(treenedge, 0, m->ntree*sizeof(int)); mju_zeroInt(treenedge, m->ntree);
int nedge = 0; int nedge = 0;
for (int i=0; i < nefc; i++) { for (int i=0; i < nefc; i++) {
@@ -287,60 +364,34 @@ static int findEdges(const mjModel* m, const mjData* d, int* treenedge, int* edg
efc_type = d->efc_type[i]; efc_type = d->efc_type[i];
efc_id = d->efc_id[i]; efc_id = d->efc_id[i];
// ==== fast handling of special cases int tree[2];
int index = treeFirst(m, d, tree, i);
int tree1 = tree[0];
int tree2 = tree[1];
// joint friction // no more edges to find, add and continue
if (efc_type == mjCNSTR_FRICTION_DOF) { if (index == -1) {
tree1 = m->dof_treeid[efc_id]; nedge = addEdge(treenedge, edge, nedge, tree1, tree2 == -1 ? tree1 : tree2, nedge_max);
nedge = addEdge(treenedge, edge, nedge, tree1, tree1, nedge_max);
continue; continue;
} }
// joint limit // possibly more edges, scan Jacobian row
if (efc_type == mjCNSTR_LIMIT_JOINT) { else {
tree1 = m->dof_treeid[m->jnt_dofadr[efc_id]]; tree2 = treeNext(m, d, tree1, i, &index);
nedge = addEdge(treenedge, edge, nedge, tree1, tree1, nedge_max);
continue;
}
// contact if (tree2 == -1) {
if (efc_type == mjCNSTR_CONTACT_FRICTIONLESS || // 1 tree found: add self-edge
efc_type == mjCNSTR_CONTACT_PYRAMIDAL || nedge = addEdge(treenedge, edge, nedge, tree1, tree1, nedge_max);
efc_type == mjCNSTR_CONTACT_ELLIPTIC) { } else {
tree1 = m->body_treeid[m->geom_bodyid[d->contact[efc_id].geom1]]; // 2 trees found: add edge, keep scanning and adding until no more trees
tree2 = m->body_treeid[m->geom_bodyid[d->contact[efc_id].geom2]];
nedge = addEdge(treenedge, edge, nedge, tree1, tree2, nedge_max);
continue;
}
// connect or weld constraints
if (efc_type == mjCNSTR_EQUALITY) {
mjtEq eq_type = m->eq_type[efc_id];
if (eq_type == mjEQ_CONNECT || eq_type == mjEQ_WELD) {
tree1 = m->body_treeid[m->eq_obj1id[efc_id]];
tree2 = m->body_treeid[m->eq_obj2id[efc_id]];
nedge = addEdge(treenedge, edge, nedge, tree1, tree2, nedge_max); nedge = addEdge(treenedge, edge, nedge, tree1, tree2, nedge_max);
continue; int tree3 = treeNext(m, d, tree2, i, &index);
} while (tree3 > -1 && tree3 != tree2) {
} tree1 = tree2;
tree2 = tree3;
// ==== generic case: scan Jacobian nedge = addEdge(treenedge, edge, nedge, tree1, tree2, nedge_max);
int index = 0; tree3 = treeNext(m, d, tree2, i, &index);
tree1 = treeNext(m, d, -1, i, &index); }
tree2 = treeNext(m, d, tree1, i, &index);
if (tree2 == -1) {
// 1 tree found: add self-edge
nedge = addEdge(treenedge, edge, nedge, tree1, tree1, nedge_max);
} else {
// 2 trees found: add edge, keep scanning and adding until no more trees
nedge = addEdge(treenedge, edge, nedge, tree1, tree2, nedge_max);
int tree3 = treeNext(m, d, tree2, i, &index);
while (tree3 > -1 && tree3 != tree2) {
tree1 = tree2;
tree2 = tree3;
nedge = addEdge(treenedge, edge, nedge, tree1, tree2, nedge_max);
tree3 = treeNext(m, d, tree2, i, &index);
} }
} }
} }
@@ -361,7 +412,7 @@ void mj_island(const mjModel* m, mjData* d) {
return; return;
} }
mjMARKSTACK; mj_markStack(d);
// allocate edge array // allocate edge array
int nedge_max = countMaxEdge(m, d); int nedge_max = countMaxEdge(m, d);
@@ -395,7 +446,7 @@ void mj_island(const mjModel* m, mjData* d) {
// allocate island arrays on arena // allocate island arrays on arena
if (!arenaAllocIsland(m, d)) { if (!arenaAllocIsland(m, d)) {
mjFREESTACK; mj_freeStack(d);
return; return;
} }
@@ -403,10 +454,10 @@ void mj_island(const mjModel* m, mjData* d) {
// compute dof_island, island_dofnum // compute dof_island, island_dofnum
int num_dof_unc = 0; // number of unconstrained dofs int num_dof_unc = 0; // number of unconstrained dofs
memset(d->island_dofnum, 0, nisland*sizeof(int)); mju_zeroInt(d->island_dofnum, nisland);
for (int i=0; i < nv; i++) { for (int i=0; i < nv; i++) {
// dof_island // dof_island
int island = tree_island[m->dof_treeid[i]];; int island = tree_island[m->dof_treeid[i]];
d->dof_island[i] = island; d->dof_island[i] = island;
// island_dofnum // island_dofnum
@@ -424,7 +475,7 @@ void mj_island(const mjModel* m, mjData* d) {
} }
// reset island_dofnum // reset island_dofnum
memset(d->island_dofnum, 0, nisland*sizeof(int)); mju_zeroInt(d->island_dofnum, nisland);
// compute dof_islandind, island_dofind // compute dof_islandind, island_dofind
int num_dof_island = 0; int num_dof_island = 0;
@@ -444,15 +495,17 @@ void mj_island(const mjModel* m, mjData* d) {
mjERROR("not all islands assigned to dofs"); mjERROR("not all islands assigned to dofs");
} }
// finalize dof_islandind: set remaning indices to -1 // finalize dof_islandind: set remaining indices to -1
for (int i=num_dof_island; i < nv; i++) { for (int i=num_dof_island; i < nv; i++) {
d->island_dofind[i] = -1; d->island_dofind[i] = -1;
} }
// compute efc_island, island_efcnum // compute efc_island, island_efcnum
memset(d->island_efcnum, 0, nisland*sizeof(int)); mju_zeroInt(d->island_efcnum, nisland);
for (int i=0; i < nefc; i++) { for (int i=0; i < nefc; i++) {
int island = tree_island[treeNext(m, d, -1, i, NULL)]; int tree[2];
treeFirst(m, d, tree, i);
int island = tree_island[tree[0]];
d->efc_island[i] = island; d->efc_island[i] = island;
d->island_efcnum[island]++; d->island_efcnum[island]++;
} }
@@ -464,7 +517,7 @@ void mj_island(const mjModel* m, mjData* d) {
} }
// reset island_efcnum // reset island_efcnum
memset(d->island_efcnum, 0, nisland*sizeof(int)); mju_zeroInt(d->island_efcnum, nisland);
// compute efc_islandind // compute efc_islandind
for (int i=0; i < nefc; i++) { for (int i=0; i < nefc; i++) {
@@ -472,5 +525,5 @@ void mj_island(const mjModel* m, mjData* d) {
d->island_efcind[d->island_efcadr[island] + (d->island_efcnum[island]++)] = i; d->island_efcind[d->island_efcadr[island] + (d->island_efcnum[island]++)] = i;
} }
mjFREESTACK; mj_freeStack(d);
} }
+1 -1
View File
@@ -26,7 +26,7 @@ extern "C" {
// find disjoint subgraphs ("islands") given sparse symmetric adjacency matrix // find disjoint subgraphs ("islands") given sparse symmetric adjacency matrix
MJAPI int mj_floodFill(int* island, int nr, const int* rownnz, const int* rowadr, const int* colind, MJAPI int mj_floodFill(int* island, int nr, const int* rownnz, const int* rowadr, const int* colind,
int* scratch); int* stack);
//-------------------------- top-level API for island construction --------------------------------- //-------------------------- top-level API for island construction ---------------------------------
+3 -3
View File
@@ -24,9 +24,9 @@
// thread local macro // thread local macro
#ifdef _MSC_VER #ifdef _MSC_VER
#define mjTHREADLOCAL __declspec(thread) #define mjTHREADLOCAL __declspec(thread)
#else #else
#define mjTHREADLOCAL _Thread_local #define mjTHREADLOCAL _Thread_local
#endif #endif
@@ -41,7 +41,7 @@
//-------------------------- compiler builtin ------------------------------------------------------ //-------------------------- compiler builtin ------------------------------------------------------
#ifndef __has_builtin #ifndef __has_builtin
#define __has_builtin(x) 0 #define __has_builtin(x) 0
#endif #endif
//-------------------------- pointer arithmetic ---------------------------------------------------- //-------------------------- pointer arithmetic ----------------------------------------------------
+17 -102
View File
@@ -24,6 +24,7 @@
#include <cctype> #include <cctype>
#include <cstddef> #include <cstddef>
#include <cstdlib> #include <cstdlib>
#include <cstdio>
#include <cstring> #include <cstring>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@@ -57,9 +58,6 @@ constexpr int kMaxAttributes = 255;
constexpr int kCacheLine = 256; constexpr int kCacheLine = 256;
// vfs prefix
constexpr const char* kVfsPrefix = mjVFS_PREFIX;
// A table of registered plugins, implemented as a linked list of array "blocks". // A table of registered plugins, implemented as a linked list of array "blocks".
// This is a compromise that maintains a good degree of memory locality while not invalidating // This is a compromise that maintains a good degree of memory locality while not invalidating
// existing pointers when growing the table. It is expected that for most users, the number of // existing pointers when growing the table. It is expected that for most users, the number of
@@ -270,6 +268,8 @@ bool ResourceProvidersAreIdentical(const mjpResourceProvider* p1, const mjpResou
p1->open == p2->open && p1->open == p2->open &&
p1->read == p2->read && p1->read == p2->read &&
p1->close == p2->close && p1->close == p2->close &&
p1->getdir == p2->getdir &&
p1->modified == p2->modified &&
p1->data == p2->data); p1->data == p2->data);
} }
@@ -532,18 +532,7 @@ void mjp_defaultResourceProvider(mjpResourceProvider* provider) {
// globally register a resource provider (thread-safe), return new slot id // globally register a resource provider (thread-safe), return new slot id
int mjp_registerResourceProvider(const mjpResourceProvider* provider) { int mjp_registerResourceProvider(const mjpResourceProvider* provider) {
// check against reserved prefixes // check if prefix is valid URI scheme format
if (PrefixesAreIdentical(kVfsPrefix, provider->prefix)) {
mju_warning("provider->prefix is '%s' which is reserved", provider->prefix);
return -1;
}
return mjp_registerResourceProviderInternal(provider);
}
// internal version of mjp_registerResourceProvider without prechecks on reserved prefixes
int mjp_registerResourceProviderInternal(const mjpResourceProvider* provider) {
// check if prefix is valid URI scheme format
if (!IsValidURISchemeFormat(provider->prefix)) { if (!IsValidURISchemeFormat(provider->prefix)) {
mju_warning("provider->prefix is '%s' which is not a valid URI scheme format", mju_warning("provider->prefix is '%s' which is not a valid URI scheme format",
provider->prefix); provider->prefix);
@@ -562,35 +551,25 @@ int mjp_registerResourceProviderInternal(const mjpResourceProvider* provider) {
// Do not handle objects with nontrivial destructors outside of this lambda. // Do not handle objects with nontrivial destructors outside of this lambda.
// Do not call mju_error inside this lambda. // Do not call mju_error inside this lambda.
int slot = [&]() -> int { int slot = [&]() -> int {
bool vfs_provider = false;
std::unique_ptr<char[]> prefix; std::unique_ptr<char[]> prefix;
// check if this is a VFS provider
if (PrefixesAreIdentical(mjVFS_PREFIX, provider->prefix)) {
vfs_provider = true;
}
// copy prefix // copy prefix
if (!vfs_provider) { prefix = CopyName(provider->prefix);
prefix = CopyName(provider->prefix); if (!prefix) {
if (!prefix) { if (strklen(provider->prefix) == -1) {
if (strklen(provider->prefix) == -1) { std::snprintf(err, sizeof(err),
std::snprintf(err, sizeof(err), "provider->prefix length exceeds the maximum limit of %d", kMaxNameLength);
"provider->prefix length exceeds the maximum limit of %d", kMaxNameLength); } else {
} else { std::snprintf(err, sizeof(err), "failed to allocate memory for resource provider prefix");
std::snprintf(err, sizeof(err), "failed to allocate memory for resource provider prefix");
}
return -1;
} }
return -1;
} }
Global<mjpResourceProvider>& global = GetGlobal<mjpResourceProvider>(); Global<mjpResourceProvider>& global = GetGlobal<mjpResourceProvider>();
auto lock = global.lock_mutex_exclusively(); auto lock = global.lock_mutex_exclusively();
int count = global.count().load(std::memory_order_acquire); int count = global.count().load(std::memory_order_acquire);
int local_idx = 0; int local_idx = 0;
int free_idx = -1, free_local_idx = -1;
PluginTable<mjpResourceProvider>* table = &global.table(); PluginTable<mjpResourceProvider>* table = &global.table();
PluginTable<mjpResourceProvider>* free_table = nullptr;
// check if a non-identical provider with the same name has already been registered // check if a non-identical provider with the same name has already been registered
for (int i = 0; i < count; ++i, ++local_idx) { for (int i = 0; i < count; ++i, ++local_idx) {
@@ -600,17 +579,7 @@ int mjp_registerResourceProviderInternal(const mjpResourceProvider* provider) {
} }
mjpResourceProvider& existing = table->plugins[local_idx]; mjpResourceProvider& existing = table->plugins[local_idx];
// VFS providers can safely go in open slots if (existing.prefix != nullptr) {
if (vfs_provider && existing.prefix == nullptr && free_idx == -1) {
free_table = table;
free_idx = i;
free_local_idx = local_idx;
// can skip the rest
break;
}
if (!vfs_provider && existing.prefix != nullptr) {
// if identical then return slot number // if identical then return slot number
if (PrefixesAreIdentical(provider->prefix, existing.prefix)) { if (PrefixesAreIdentical(provider->prefix, existing.prefix)) {
if (ResourceProvidersAreIdentical(provider, &existing)) { if (ResourceProvidersAreIdentical(provider, &existing)) {
@@ -626,7 +595,7 @@ int mjp_registerResourceProviderInternal(const mjpResourceProvider* provider) {
} }
// allocate a new block of PluginTable if the last allocated block is full // allocate a new block of PluginTable if the last allocated block is full
if (free_local_idx == -1 && local_idx == PluginTable<mjpResourceProvider>::kBlockSize) { if (local_idx == PluginTable<mjpResourceProvider>::kBlockSize) {
local_idx = 0; local_idx = 0;
table = AddNewTableBlock<mjpResourceProvider>(table); table = AddNewTableBlock<mjpResourceProvider>(table);
if (!table) { if (!table) {
@@ -636,19 +605,12 @@ int mjp_registerResourceProviderInternal(const mjpResourceProvider* provider) {
// all checks passed, register the plugin into the global table // all checks passed, register the plugin into the global table
mjpResourceProvider& registered_provider = table->plugins[local_idx]; mjpResourceProvider& registered_provider = table->plugins[local_idx];
if (free_local_idx != -1) {
registered_provider = free_table->plugins[free_local_idx];
}
registered_provider = *provider; registered_provider = *provider;
registered_provider.prefix = (!vfs_provider) ? prefix.release() : kVfsPrefix; registered_provider.prefix = prefix.release();
// increment the global plugin count with a release memory barrier // increment the global plugin count with a release memory barrier
if (free_idx == -1) { global.count().store(count + 1, std::memory_order_release);
free_idx = count; return count;
global.count().store(count + 1, std::memory_order_release);
}
return free_idx;
}(); }();
// ========= ATTENTION! ========================================================================== // ========= ATTENTION! ==========================================================================
@@ -663,46 +625,6 @@ int mjp_registerResourceProviderInternal(const mjpResourceProvider* provider) {
return slot+1; return slot+1;
} }
// globally unregister resource provider (thread-safe)
// only used for VFS resource providers
void mjp_unregisterResourceProvider(int slot) {
// shift slot to zero-index
slot--;
if (slot < 0) {
return;
}
// get global table, acquire lock
Global<mjpResourceProvider>& global = GetGlobal<mjpResourceProvider>();
auto lock = global.lock_mutex_exclusively();
int count = global.count().load(std::memory_order_acquire);
if (slot >= count) {
return;
}
PluginTable<mjpResourceProvider>* table = &global.table();
// iterate over blocks in the global table until the local index is less than the block size
int local_idx = slot;
while (local_idx >= PluginTable<mjpResourceProvider>::kBlockSize) {
local_idx -= PluginTable<mjpResourceProvider>::kBlockSize;
table = table->next;
if (!table) {
return;
}
}
// local_idx is now a valid index into the current block
mjpResourceProvider& provider = table->plugins[local_idx];
// no-op for anything other than VFS resource providers
if (provider.prefix == kVfsPrefix) {
provider.prefix = nullptr;
}
}
// return the number of globally registered resource providers // return the number of globally registered resource providers
int mjp_resourceProviderCount() { int mjp_resourceProviderCount() {
return GetGlobal<mjpResourceProvider>().count().load(std::memory_order_acquire); return GetGlobal<mjpResourceProvider>().count().load(std::memory_order_acquire);
@@ -728,12 +650,6 @@ const mjpResourceProvider* mjp_getResourceProvider(const char* resource_name) {
return nullptr; return nullptr;
} }
// since multiple VFS resource providers can be registered with the same
// prefix, it doesn't make sense to try to match against them
if (PrefixesAreIdentical(kVfsPrefix, file_prefix.c_str())) {
return nullptr;
}
Global<mjpResourceProvider>& global = GetGlobal<mjpResourceProvider>(); Global<mjpResourceProvider>& global = GetGlobal<mjpResourceProvider>();
auto lock = global.lock_mutex_exclusively(); auto lock = global.lock_mutex_exclusively();
PluginTable<mjpResourceProvider>* table = &global.table(); PluginTable<mjpResourceProvider>* table = &global.table();
@@ -743,7 +659,6 @@ const mjpResourceProvider* mjp_getResourceProvider(const char* resource_name) {
for (int i = 0; for (int i = 0;
i < PluginTable<mjpPlugin>::kBlockSize && found_slot < count; i < PluginTable<mjpPlugin>::kBlockSize && found_slot < count;
++i, ++found_slot) { ++i, ++found_slot) {
const mjpResourceProvider& provider = table->plugins[i]; const mjpResourceProvider& provider = table->plugins[i];
const char *prefix = provider.prefix; const char *prefix = provider.prefix;
+2 -5
View File
@@ -31,14 +31,11 @@ MJAPI int mjp_registerPlugin(const mjpPlugin* plugin);
// globally register a resource provider (thread-safe), return new slot id // globally register a resource provider (thread-safe), return new slot id
MJAPI int mjp_registerResourceProvider(const mjpResourceProvider* provider); MJAPI int mjp_registerResourceProvider(const mjpResourceProvider* provider);
// globally unregister a resource provider (thread-safe)
MJAPI void mjp_unregisterResourceProvider(int slot);
// return the number of globally registered plugins // return the number of globally registered plugins
MJAPI int mjp_pluginCount(); MJAPI int mjp_pluginCount(void);
// return the number of globally registered resource providers // return the number of globally registered resource providers
MJAPI int mjp_resourceProviderCount(); MJAPI int mjp_resourceProviderCount(void);
// look up a plugin by name, optionally also get its registered slot number // look up a plugin by name, optionally also get its registered slot number
MJAPI const mjpPlugin* mjp_getPlugin(const char* name, int* slot); MJAPI const mjpPlugin* mjp_getPlugin(const char* name, int* slot);
+22 -9
View File
@@ -730,8 +730,13 @@ void mj_printModel(const mjModel* m, const char* filename) {
// valid printf-style format string for a single float value // valid printf-style format string for a single float value
void mj_printFormattedData(const mjModel* m, mjData* d, const char* filename, void mj_printFormattedData(const mjModel* m, mjData* d, const char* filename,
const char* float_format) { const char* float_format) {
// stack in use, SHOULD NOT OCCUR
if (d->pstack) {
mjERROR("attempting to print mjData when stack is in use");
}
mjtNum *M; mjtNum *M;
mjMARKSTACK; mj_markStack(d);
// check format string // check format string
if (!validateFloatFormat(float_format)) { if (!validateFloatFormat(float_format)) {
@@ -739,11 +744,6 @@ void mj_printFormattedData(const mjModel* m, mjData* d, const char* filename,
float_format = FLOAT_FORMAT; float_format = FLOAT_FORMAT;
} }
// stack in use, SHOULD NOT OCCUR
if (d->pstack) {
mjERROR("attempting to print mjData when stack is in use");
}
// get file // get file
FILE* fp; FILE* fp;
if (filename) { if (filename) {
@@ -755,7 +755,7 @@ void mj_printFormattedData(const mjModel* m, mjData* d, const char* filename,
// check for nullptr // check for nullptr
if (!fp) { if (!fp) {
mju_warning("Could not open file '%s' for writing mjModel", filename); mju_warning("Could not open file '%s' for writing mjModel", filename);
mjFREESTACK; mj_freeStack(d);
return; return;
} }
@@ -776,7 +776,10 @@ void mj_printFormattedData(const mjModel* m, mjData* d, const char* filename,
fprintf(fp, "SIZES\n"); fprintf(fp, "SIZES\n");
#define X(type, name) \ #define X(type, name) \
if (strcmp(#name, "pstack") != 0 && strcmp(#name, "parena") != 0) { \ if (strcmp(#name, "pstack") != 0 && \
strcmp(#name, "pbase") != 0 && \
strcmp(#name, "parena") != 0 && \
strcmp(#name, "threadpool") != 0) { \
const char* format = _Generic( \ const char* format = _Generic( \
d->name, \ d->name, \
int : INT_FORMAT, \ int : INT_FORMAT, \
@@ -792,6 +795,16 @@ void mj_printFormattedData(const mjModel* m, mjData* d, const char* filename,
MJDATA_SCALAR MJDATA_SCALAR
#undef X #undef X
int threadpool = 0;
if (d->threadpool) {
threadpool = 1;
}
fprintf(fp, " ");
fprintf(fp, NAME_FORMAT, "threadpool");
fprintf(fp, INT_FORMAT, threadpool);
fprintf(fp, "\n");
fprintf(fp, "\n"); fprintf(fp, "\n");
// WARNING // WARNING
@@ -1132,7 +1145,7 @@ void mj_printFormattedData(const mjModel* m, mjData* d, const char* filename,
fclose(fp); fclose(fp);
} }
mjFREESTACK; mj_freeStack(d);
} }
+3 -2
View File
@@ -15,6 +15,7 @@
#include "engine/engine_ray.h" #include "engine/engine_ray.h"
#include <math.h>
#include <stddef.h> #include <stddef.h>
#include <mujoco/mjdata.h> #include <mujoco/mjdata.h>
@@ -1158,7 +1159,7 @@ static mjtNum mju_singleRay(const mjModel* m, mjData* d, const mjtNum pnt[3], co
void mj_multiRay(const mjModel* m, mjData* d, const mjtNum pnt[3], const mjtNum* vec, void mj_multiRay(const mjModel* m, mjData* d, const mjtNum pnt[3], const mjtNum* vec,
const mjtByte* geomgroup, mjtByte flg_static, int bodyexclude, const mjtByte* geomgroup, mjtByte flg_static, int bodyexclude,
int* geomid, mjtNum* dist, int nray, mjtNum cutoff) { int* geomid, mjtNum* dist, int nray, mjtNum cutoff) {
mjMARKSTACK; mj_markStack(d);
// allocate source // allocate source
mjtNum* geom_ba = mj_stackAllocNum(d, 4*m->ngeom); mjtNum* geom_ba = mj_stackAllocNum(d, 4*m->ngeom);
@@ -1173,5 +1174,5 @@ void mj_multiRay(const mjModel* m, mjData* d, const mjtNum pnt[3], const mjtNum*
dist[i] = mju_singleRay(m, d, pnt, vec+3*i, geom_eliminate, geom_ba, geomid+i); dist[i] = mju_singleRay(m, d, pnt, vec+3*i, geom_eliminate, geom_ba, geomid+i);
} }
mjFREESTACK; mj_freeStack(d);
} }
+92 -82
View File
@@ -16,40 +16,35 @@
#include <limits.h> #include <limits.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#include <unistd.h>
#endif
#ifdef _WIN32
#define stat _stat
#endif
#include <mujoco/mjmodel.h>
#include <mujoco/mjplugin.h> #include <mujoco/mjplugin.h>
#include "engine/engine_plugin.h" #include "engine/engine_plugin.h"
#include "engine/engine_util_errmem.h" #include "engine/engine_util_errmem.h"
// file buffer used internally for the OS filesystem // file buffer used internally for the OS filesystem
typedef struct { typedef struct {
void* buffer; uint8_t* buffer; // raw bytes from file
int nbuffer; size_t nbuffer; // size of buffer in bytes
time_t mtime; // last modified time
} file_buffer; } file_buffer;
// helper function to fill data from resource provider into provider
static void fillResource(const mjpResourceProvider* provider, mjResource* resource) {
if (provider == NULL) {
resource->read = NULL;
resource->close = NULL;
resource->getdir = NULL;
resource->provider_data = NULL;
} else {
resource->read = provider->read;
resource->close = provider->close;
resource->getdir = provider->getdir;
resource->provider_data = provider->data;
}
}
// open the given resource; if the name doesn't have a prefix matching with a // open the given resource; if the name doesn't have a prefix matching with a
// resource provider, then the default_provider is used // resource provider, then the OS filesystem is used
// if default_provider non-positive, then the OS filesystem is used mjResource* mju_openResource(const char* name) {
mjResource* mju_openResource(const char* name, int default_provider) {
mjResource* resource = (mjResource*) mju_malloc(sizeof(mjResource)); mjResource* resource = (mjResource*) mju_malloc(sizeof(mjResource));
const mjpResourceProvider* provider = NULL; const mjpResourceProvider* provider = NULL;
if (resource == NULL) { if (resource == NULL) {
@@ -57,19 +52,22 @@ mjResource* mju_openResource(const char* name, int default_provider) {
return NULL; return NULL;
} }
// clear out resource
memset(resource, 0, sizeof(mjResource));
// copy name // copy name
resource->name = mju_malloc(sizeof(char) * (strlen(name) + 1)); resource->name = mju_malloc(sizeof(char) * (strlen(name) + 1));
if (resource->name == NULL) { if (resource->name == NULL) {
mju_free(resource); mju_closeResource(resource);
mjERROR("could not allocate memory"); mjERROR("could not allocate memory");
return NULL; return NULL;
} }
strcpy(resource->name, name); memcpy(resource->name, name, sizeof(char) * (strlen(name) + 1));
// find provider based off prefix of name // find provider based off prefix of name
provider = mjp_getResourceProvider(name); provider = mjp_getResourceProvider(name);
if (provider != NULL) { if (provider != NULL) {
fillResource(provider, resource); resource->provider = provider;
if (provider->open(resource)) { if (provider->open(resource)) {
return resource; return resource;
} }
@@ -77,49 +75,27 @@ mjResource* mju_openResource(const char* name, int default_provider) {
mju_warning("mju_openResource: could not open resource '%s' " mju_warning("mju_openResource: could not open resource '%s' "
"using a resource provider matching prefix '%s'", "using a resource provider matching prefix '%s'",
name, provider->prefix); name, provider->prefix);
mju_free(resource->name); mju_closeResource(resource);
mju_free(resource);
return NULL;
}
// fallback to default provider
if (default_provider > 0) {
provider = mjp_getResourceProviderAtSlot(default_provider);
if (provider == NULL) {
mju_warning("mju_openResource: unknown resource provider at slot %d",
default_provider);
mju_free(resource->name);
mju_free(resource);
return NULL;
}
fillResource(provider, resource);
if (provider->open(resource)) {
return resource;
}
mju_warning("mju_openResource: could not open resource '%s' "
"with default provider at slot %d",
name, default_provider);
mju_free(resource->name);
mju_free(resource);
return NULL; return NULL;
} }
// lastly fallback to OS filesystem // lastly fallback to OS filesystem
else { resource->provider = NULL;
fillResource(NULL, resource); resource->data = mju_malloc(sizeof(file_buffer));
resource->data = mju_malloc(sizeof(file_buffer)); file_buffer* fb = (file_buffer*) resource->data;
file_buffer* fb = (file_buffer*) resource->data; fb->buffer = mju_fileToMemory(name, &(fb->nbuffer));
fb->buffer = mju_fileToMemory(name, &(fb->nbuffer)); if (fb->buffer == NULL) {
if (fb->buffer == NULL) { mju_warning("mju_openResource: unknown file '%s'", name);
mju_warning("mju_openResource: unknown file '%s'", name); mju_closeResource(resource);
mju_free(fb); return NULL;
mju_free(resource->name);
mju_free(resource);
return NULL;
}
} }
struct stat file_stat;
if (stat(name, &file_stat) == 0) {
memcpy(&fb->mtime, &file_stat.st_mtime, sizeof(time_t));
} else {
memset(&fb->mtime, 0, sizeof(time_t));
}
return resource; return resource;
} }
@@ -131,20 +107,20 @@ void mju_closeResource(mjResource* resource) {
return; return;
} }
// use the resource provider to close resource // use the resource provider close callback
if (resource->close) { if (resource->provider && resource->provider->close) {
resource->close(resource); resource->provider->close(resource);
} } else {
// clear OS filesystem if present
// if provider is NULL, then OS filesystem is used
else {
file_buffer* fb = (file_buffer*) resource->data; file_buffer* fb = (file_buffer*) resource->data;
mju_free(fb->buffer); if (fb) {
mju_free(fb); if (fb->buffer) mju_free(fb->buffer);
mju_free(fb);
}
} }
// free name and resource // free resource
mju_free(resource->name); if (resource->name) mju_free(resource->name);
mju_free(resource); mju_free(resource);
} }
@@ -157,12 +133,12 @@ int mju_readResource(mjResource* resource, const void** buffer) {
return 0; return 0;
} }
if (resource->read) { if (resource->provider) {
return resource->read(resource, buffer); return resource->provider->read(resource, buffer);
} }
// if provider read callback is NULL, then OS filesystem is used // if provider is NULL, then OS filesystem is used
const file_buffer* fb = (file_buffer*) resource->data; const file_buffer* fb = (file_buffer*) resource->data;
*buffer = fb->buffer; *buffer = fb->buffer;
return fb->nbuffer; return fb->nbuffer;
@@ -175,14 +151,14 @@ void mju_getResourceDir(mjResource* resource, const char** dir, int* ndir) {
*dir = NULL; *dir = NULL;
*ndir = 0; *ndir = 0;
if (!resource) { if (resource == NULL) {
return; return;
} }
// provider is not OS filesystem // provider is not OS filesystem
if (resource->read) { if (resource->provider) {
if (resource->getdir) { if (resource->provider->getdir) {
resource->getdir(resource, dir, ndir); resource->provider->getdir(resource, dir, ndir);
} }
} else { } else {
*dir = resource->name; *dir = resource->name;
@@ -192,6 +168,40 @@ void mju_getResourceDir(mjResource* resource, const char** dir, int* ndir) {
// modified callback for OS filesystem
static int mju_isModifiedFile(const char* name, const file_buffer* fb) {
if (fb != NULL) {
struct stat file_stat;
if (stat(name, &file_stat) == 0) {
return difftime(fb->mtime, file_stat.st_mtime) < 0;
}
return -1;
}
return -2;
}
// Returns > 0 if resource has been modified since last read, 0 if not, and < 0
// if inconclusive
int mju_isModifiedResource(const mjResource* resource) {
if (resource == NULL) {
return -2;
}
// provider is not OS filesystem
if (resource->provider) {
if (resource->provider->modified) {
return resource->provider->modified(resource);
}
return 1; // default (modified)
}
return mju_isModifiedFile(resource->name, (file_buffer*) resource->data);
}
// get the length of the dirname portion of a given path // get the length of the dirname portion of a given path
int mju_dirnamelen(const char* path) { int mju_dirnamelen(const char* path) {
if (!path) { if (!path) {
@@ -211,7 +221,7 @@ int mju_dirnamelen(const char* path) {
// read file into memory buffer (allocated here with mju_malloc) // read file into memory buffer (allocated here with mju_malloc)
void* mju_fileToMemory(const char* filename, int* filesize) { void* mju_fileToMemory(const char* filename, size_t* filesize) {
// open file // open file
*filesize = 0; *filesize = 0;
FILE* fp = fopen(filename, "rb"); FILE* fp = fopen(filename, "rb");
+9 -4
View File
@@ -15,6 +15,8 @@
#ifndef MUJOCO_SRC_ENGINE_ENGINE_RESOURCE_H_ #ifndef MUJOCO_SRC_ENGINE_ENGINE_RESOURCE_H_
#define MUJOCO_SRC_ENGINE_ENGINE_RESOURCE_H_ #define MUJOCO_SRC_ENGINE_ENGINE_RESOURCE_H_
#include <stddef.h>
#include <mujoco/mjexport.h> #include <mujoco/mjexport.h>
#include "engine/engine_plugin.h" #include "engine/engine_plugin.h"
@@ -23,9 +25,8 @@ extern "C" {
#endif #endif
// open the given resource; if the name doesn't have a prefix matching with a // open the given resource; if the name doesn't have a prefix matching with a
// resource provider, then the default_provider is used // resource provider, then the OS filesystem is used
// if default_provider non-positive, then the OS filesystem is used MJAPI mjResource* mju_openResource(const char* name);
MJAPI mjResource* mju_openResource(const char* name, int default_provider);
// close the given resource; no-op if resource is NULL // close the given resource; no-op if resource is NULL
MJAPI void mju_closeResource(mjResource* resource); MJAPI void mju_closeResource(mjResource* resource);
@@ -37,11 +38,15 @@ MJAPI int mju_readResource(mjResource* resource, const void** buffer);
// sets for a resource with a name partitioned as {dir}{filename}, the dir and ndir pointers // sets for a resource with a name partitioned as {dir}{filename}, the dir and ndir pointers
MJAPI void mju_getResourceDir(mjResource* resource, const char** dir, int* ndir); MJAPI void mju_getResourceDir(mjResource* resource, const char** dir, int* ndir);
// Returns > 0 if resource has been modified since last read, 0 if not, and < 0
// if inconclusive
MJAPI int mju_isModifiedResource(const mjResource* resource);
// get the length of the dirname portion of a given path // get the length of the dirname portion of a given path
int mju_dirnamelen(const char* path); int mju_dirnamelen(const char* path);
// read file into memory buffer (allocated here with mju_malloc) // read file into memory buffer (allocated here with mju_malloc)
void* mju_fileToMemory(const char* filename, int* filesize); void* mju_fileToMemory(const char* filename, size_t* filesize);
#ifdef __cplusplus #ifdef __cplusplus
} }
+2 -2
View File
@@ -854,18 +854,18 @@ void mj_energyPos(const mjModel* m, mjData* d) {
// velocity-dependent energy (kinetic) // velocity-dependent energy (kinetic)
void mj_energyVel(const mjModel* m, mjData* d) { void mj_energyVel(const mjModel* m, mjData* d) {
mjtNum *vec; mjtNum *vec;
mjMARKSTACK;
// return if disabled (already cleared in potential) // return if disabled (already cleared in potential)
if (!mjENABLED(mjENBL_ENERGY)) { if (!mjENABLED(mjENBL_ENERGY)) {
return; return;
} }
mj_markStack(d);
vec = mj_stackAllocNum(d, m->nv); vec = mj_stackAllocNum(d, m->nv);
// kinetic energy: 0.5 * qvel' * M * qvel // kinetic energy: 0.5 * qvel' * M * qvel
mj_mulM(m, d, vec, d->qvel); mj_mulM(m, d, vec, d->qvel);
d->energy[1] = 0.5*mju_dot(vec, d->qvel, m->nv); d->energy[1] = 0.5*mju_dot(vec, d->qvel, m->nv);
mjFREESTACK; mj_freeStack(d);
} }
+4 -4
View File
@@ -61,7 +61,7 @@ static void mj_setM0(mjModel* m, mjData* d) {
static void set0(mjModel* m, mjData* d) { static void set0(mjModel* m, mjData* d) {
int id, id1, id2, dnum, nv = m->nv; int id, id1, id2, dnum, nv = m->nv;
mjtNum A[36] = {0}, pos[3], quat[4]; mjtNum A[36] = {0}, pos[3], quat[4];
mjMARKSTACK; mj_markStack(d);
mjtNum* jac = mj_stackAllocNum(d, 6*nv); mjtNum* jac = mj_stackAllocNum(d, 6*nv);
mjtNum* tmp = mj_stackAllocNum(d, 6*nv); mjtNum* tmp = mj_stackAllocNum(d, 6*nv);
int* cammode = 0; int* cammode = 0;
@@ -264,7 +264,7 @@ static void set0(mjModel* m, mjData* d) {
mju_copy3(m->light_dir0+3*i, d->light_xdir+3*i); mju_copy3(m->light_dir0+3*i, d->light_xdir+3*i);
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -283,7 +283,7 @@ static void setStat(mjModel* m, mjData* d) {
mjtNum xmin[3] = {1E+10, 1E+10, 1E+10}; mjtNum xmin[3] = {1E+10, 1E+10, 1E+10};
mjtNum xmax[3] = {-1E+10, -1E+10, -1E+10}; mjtNum xmax[3] = {-1E+10, -1E+10, -1E+10};
mjtNum rbound; mjtNum rbound;
mjMARKSTACK; mj_markStack(d);
mjtNum* body = mj_stackAllocNum(d, m->nbody); mjtNum* body = mj_stackAllocNum(d, m->nbody);
// compute bounding box of bodies, joint centers, geoms and sites // compute bounding box of bodies, joint centers, geoms and sites
@@ -383,7 +383,7 @@ static void setStat(mjModel* m, mjData* d) {
m->stat.meaninertia /= m->nv; m->stat.meaninertia /= m->nv;
} }
mjFREESTACK; mj_freeStack(d);
} }
+24 -26
View File
@@ -168,7 +168,8 @@ static void residual(const mjModel* m, mjData* d, mjtNum* res, int i, int dim, i
for (int j=0; j < dim; j++) { for (int j=0; j < dim; j++) {
res[j] = d->efc_b[i+j] + mju_dotSparse(d->efc_AR + d->efc_AR_rowadr[i+j], res[j] = d->efc_b[i+j] + mju_dotSparse(d->efc_AR + d->efc_AR_rowadr[i+j],
d->efc_force, d->efc_AR_rownnz[i+j], d->efc_force, d->efc_AR_rownnz[i+j],
d->efc_AR_colind + d->efc_AR_rowadr[i+j]); d->efc_AR_colind + d->efc_AR_rowadr[i+j],
/*flg_unc1=*/0);
} }
} }
@@ -308,7 +309,7 @@ void mj_solPGS(const mjModel* m, mjData* d, int maxiter) {
mjtNum *mu, x, denom, improvement; mjtNum *mu, x, denom, improvement;
mjtNum v[6], v1[6], Athis[36], Ac[25], bc[5], res[6], oldforce[6]; mjtNum v[6], v1[6], Athis[36], Ac[25], bc[5], res[6], oldforce[6];
mjContact* con; mjContact* con;
mjMARKSTACK; mj_markStack(d);
mjtNum* ARinv = mj_stackAllocNum(d, nefc); mjtNum* ARinv = mj_stackAllocNum(d, nefc);
int* oldstate = mj_stackAllocInt(d, nefc); int* oldstate = mj_stackAllocInt(d, nefc);
@@ -463,7 +464,7 @@ void mj_solPGS(const mjModel* m, mjData* d, int maxiter) {
} }
// process state // process state
memcpy(oldstate, d->efc_state, nefc*sizeof(int)); mju_copyInt(oldstate, d->efc_state, nefc);
int nactive = dualState(m, d); int nactive = dualState(m, d);
int nchange = 0; int nchange = 0;
for (int i=0; i < nefc; i++) { for (int i=0; i < nefc; i++) {
@@ -496,7 +497,7 @@ void mj_solPGS(const mjModel* m, mjData* d, int maxiter) {
// map to joint space // map to joint space
dualFinish(m, d); dualFinish(m, d);
mjFREESTACK; mj_freeStack(d);
} }
@@ -510,7 +511,7 @@ void mj_solNoSlip(const mjModel* m, mjData* d, int maxiter) {
mjtNum *mu, improvement; mjtNum *mu, improvement;
mjtNum v[5], Ac[25], bc[5], res[5], oldforce[5], delta[5], mid, y, K0, K1; mjtNum v[5], Ac[25], bc[5], res[5], oldforce[5], delta[5], mid, y, K0, K1;
mjContact* con; mjContact* con;
mjMARKSTACK; mj_markStack(d);
mjtNum* ARinv = mj_stackAllocNum(d, nefc); mjtNum* ARinv = mj_stackAllocNum(d, nefc);
int* oldstate = mj_stackAllocInt(d, nefc); int* oldstate = mj_stackAllocInt(d, nefc);
@@ -679,7 +680,7 @@ void mj_solNoSlip(const mjModel* m, mjData* d, int maxiter) {
} }
// process state // process state
memcpy(oldstate, d->efc_state, nefc*sizeof(int)); mju_copyInt(oldstate, d->efc_state, nefc);
int nactive = dualState(m, d); int nactive = dualState(m, d);
int nchange = 0; int nchange = 0;
for (int i=0; i < nefc; i++) { for (int i=0; i < nefc; i++) {
@@ -702,7 +703,7 @@ void mj_solNoSlip(const mjModel* m, mjData* d, int maxiter) {
// map to joint space // map to joint space
dualFinish(m, d); dualFinish(m, d);
mjFREESTACK; mj_freeStack(d);
} }
@@ -1092,9 +1093,6 @@ static int updateBracket(const mjModel* m, mjData* d, mjCGContext* ctx,
static mjtNum CGsearch(const mjModel* m, mjData* d, mjCGContext* ctx) { static mjtNum CGsearch(const mjModel* m, mjData* d, mjCGContext* ctx) {
mjCGPnt p0, p1, p2, pmid, p1next, p2next; mjCGPnt p0, p1, p2, pmid, p1next, p2next;
const int LSmaxiter = 50;
const mjtNum LStolscl = 0.01;
// clear results // clear results
ctx->LSiter = 0; ctx->LSiter = 0;
ctx->LSresult = 0; ctx->LSresult = 0;
@@ -1108,7 +1106,7 @@ static mjtNum CGsearch(const mjModel* m, mjData* d, mjCGContext* ctx) {
} }
// compute scaled gradtol and slope scaling // compute scaled gradtol and slope scaling
mjtNum gtol = m->opt.tolerance * LStolscl * snorm * m->stat.meaninertia * mjMAX(1, m->nv); mjtNum gtol = m->opt.tolerance * m->opt.ls_tolerance * snorm * m->stat.meaninertia * mjMAX(1, m->nv);
mjtNum slopescl = 1 / (snorm * m->stat.meaninertia * mjMAX(1, m->nv)); mjtNum slopescl = 1 / (snorm * m->stat.meaninertia * mjMAX(1, m->nv));
// compute Mv, Jv // compute Mv, Jv
@@ -1179,7 +1177,7 @@ static mjtNum CGsearch(const mjModel* m, mjData* d, mjCGContext* ctx) {
// one-sided search // one-sided search
int p2update = 0; int p2update = 0;
while (p1.deriv[0]*dir <= -gtol && ctx->LSiter < LSmaxiter) { while (p1.deriv[0]*dir <= -gtol && ctx->LSiter < m->opt.ls_iterations) {
// save current // save current
p2 = p1; p2 = p1;
p2update = 1; p2update = 1;
@@ -1196,7 +1194,7 @@ static mjtNum CGsearch(const mjModel* m, mjData* d, mjCGContext* ctx) {
} }
// check for failure to bracket // check for failure to bracket
if (ctx->LSiter >= LSmaxiter) { if (ctx->LSiter >= m->opt.ls_iterations) {
ctx->LSresult = 3; // could not bracket ctx->LSresult = 3; // could not bracket
ctx->LSslope = mju_abs(p1.deriv[0])*slopescl; ctx->LSslope = mju_abs(p1.deriv[0])*slopescl;
return p1.alpha; return p1.alpha;
@@ -1215,7 +1213,7 @@ static mjtNum CGsearch(const mjModel* m, mjData* d, mjCGContext* ctx) {
CGeval(m, d, ctx, &p1next); CGeval(m, d, ctx, &p1next);
// bracketed search // bracketed search
while (ctx->LSiter < LSmaxiter) { while (ctx->LSiter < m->opt.ls_iterations) {
// evaluate at midpoint // evaluate at midpoint
pmid.alpha = 0.5*(p1.alpha + p2.alpha); pmid.alpha = 0.5*(p1.alpha + p2.alpha);
CGeval(m, d, ctx, &pmid); CGeval(m, d, ctx, &pmid);
@@ -1276,7 +1274,7 @@ static mjtNum CGsearch(const mjModel* m, mjData* d, mjCGContext* ctx) {
static void HessianCone(const mjModel* m, mjData* d, mjCGContext* ctx) { static void HessianCone(const mjModel* m, mjData* d, mjCGContext* ctx) {
int nv = m->nv, nefc = d->nefc; int nv = m->nv, nefc = d->nefc;
mjtNum local[36]; mjtNum local[36];
mjMARKSTACK; mj_markStack(d);
// storage for L'*J // storage for L'*J
mjtNum* LTJ = mj_stackAllocNum(d, 6*nv); mjtNum* LTJ = mj_stackAllocNum(d, 6*nv);
@@ -1313,7 +1311,7 @@ static void HessianCone(const mjModel* m, mjData* d, mjCGContext* ctx) {
for (int r=0; r < dim; r++) { for (int r=0; r < dim; r++) {
// copy data for this row // copy data for this row
mju_copy(LTJ_row, LTJ+r*nnz, nnz); mju_copy(LTJ_row, LTJ+r*nnz, nnz);
memcpy(LTJ_ind, d->efc_J_colind+d->efc_J_rowadr[i+r], nnz*sizeof(int)); mju_copyInt(LTJ_ind, d->efc_J_colind+d->efc_J_rowadr[i+r], nnz);
// update // update
mju_cholUpdateSparse(ctx->Hcone, LTJ_row, nv, 1, mju_cholUpdateSparse(ctx->Hcone, LTJ_row, nv, 1,
@@ -1346,7 +1344,7 @@ static void HessianCone(const mjModel* m, mjData* d, mjCGContext* ctx) {
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -1354,7 +1352,7 @@ static void HessianCone(const mjModel* m, mjData* d, mjCGContext* ctx) {
// compute and factorize Hessian: direct method // compute and factorize Hessian: direct method
static void HessianDirect(const mjModel* m, mjData* d, mjCGContext* ctx) { static void HessianDirect(const mjModel* m, mjData* d, mjCGContext* ctx) {
int nv = m->nv, nefc = d->nefc; int nv = m->nv, nefc = d->nefc;
mjMARKSTACK; mj_markStack(d);
// compute D corresponding to quad states // compute D corresponding to quad states
mjtNum* D = mj_stackAllocNum(d, nefc); mjtNum* D = mj_stackAllocNum(d, nefc);
@@ -1426,7 +1424,7 @@ static void HessianDirect(const mjModel* m, mjData* d, mjCGContext* ctx) {
ctx->nnz = nv*nv; ctx->nnz = nv*nv;
} }
mjFREESTACK; mj_freeStack(d);
// add cones if present // add cones if present
if (ctx->ncone) { if (ctx->ncone) {
@@ -1443,7 +1441,7 @@ static void HessianDirect(const mjModel* m, mjData* d, mjCGContext* ctx) {
static void HessianIncremental(const mjModel* m, mjData* d, static void HessianIncremental(const mjModel* m, mjData* d,
mjCGContext* ctx, const int* oldstate) { mjCGContext* ctx, const int* oldstate) {
int rank, nv = m->nv, nefc = d->nefc; int rank, nv = m->nv, nefc = d->nefc;
mjMARKSTACK; mj_markStack(d);
// local space // local space
mjtNum* vec = mj_stackAllocNum(d, nv); mjtNum* vec = mj_stackAllocNum(d, nv);
@@ -1475,7 +1473,7 @@ static void HessianIncremental(const mjModel* m, mjData* d,
// scale vec, copy colind // scale vec, copy colind
mju_scl(vec, d->efc_J+adr, mju_sqrt(d->efc_D[i]), nnz); mju_scl(vec, d->efc_J+adr, mju_sqrt(d->efc_D[i]), nnz);
memcpy(vec_ind, d->efc_J_colind+adr, nnz*sizeof(int)); mju_copyInt(vec_ind, d->efc_J_colind+adr, nnz);
// sparse update // sparse update
rank = mju_cholUpdateSparse(ctx->H, vec, nv, flag_update, rank = mju_cholUpdateSparse(ctx->H, vec, nv, flag_update,
@@ -1489,7 +1487,7 @@ static void HessianIncremental(const mjModel* m, mjData* d,
// recompute H directly if accuracy lost // recompute H directly if accuracy lost
if (rank < nv) { if (rank < nv) {
mjFREESTACK; mj_freeStack(d);
HessianDirect(m, d, ctx); HessianDirect(m, d, ctx);
// nothing else to do // nothing else to do
@@ -1503,7 +1501,7 @@ static void HessianIncremental(const mjModel* m, mjData* d,
HessianCone(m, d, ctx); HessianCone(m, d, ctx);
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -1514,7 +1512,7 @@ static void mj_solCGNewton(const mjModel* m, mjData* d, int maxiter, int flg_New
mjtNum alpha, beta; mjtNum alpha, beta;
mjtNum *gradold = NULL, *Mgradold = NULL, *Mgraddif = NULL; mjtNum *gradold = NULL, *Mgradold = NULL, *Mgraddif = NULL;
mjCGContext ctx; mjCGContext ctx;
mjMARKSTACK; mj_markStack(d);
// allocate context // allocate context
CGallocate(m, d, &ctx, flg_Newton); CGallocate(m, d, &ctx, flg_Newton);
@@ -1562,7 +1560,7 @@ static void mj_solCGNewton(const mjModel* m, mjData* d, int maxiter, int flg_New
mju_copy(gradold, ctx.grad, nv); mju_copy(gradold, ctx.grad, nv);
mju_copy(Mgradold, ctx.Mgrad, nv); mju_copy(Mgradold, ctx.Mgrad, nv);
} }
memcpy(oldstate, d->efc_state, nefc*sizeof(int)); mju_copyInt(oldstate, d->efc_state, nefc);
mjtNum oldcost = ctx.cost; mjtNum oldcost = ctx.cost;
// update // update
@@ -1624,7 +1622,7 @@ static void mj_solCGNewton(const mjModel* m, mjData* d, int maxiter, int flg_New
d->solver_nnz = 0; d->solver_nnz = 0;
} }
mjFREESTACK; mj_freeStack(d);
} }
+103 -41
View File
@@ -14,12 +14,13 @@
#include "engine/engine_support.h" #include "engine/engine_support.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h> #include <string.h>
#include <mujoco/mjdata.h> #include <mujoco/mjdata.h>
#include <mujoco/mjmacro.h> #include <mujoco/mjmacro.h>
#include <mujoco/mjmodel.h> #include <mujoco/mjmodel.h>
#include "engine/engine_array_safety.h"
#include "engine/engine_core_constraint.h" #include "engine/engine_core_constraint.h"
#include "engine/engine_crossplatform.h" #include "engine/engine_crossplatform.h"
#include "engine/engine_io.h" #include "engine/engine_io.h"
@@ -278,7 +279,7 @@ void mj_jacBodyCom(const mjModel* m, const mjData* d, mjtNum* jacp, mjtNum* jacr
// compute subtree-com Jacobian // compute subtree-com Jacobian
void mj_jacSubtreeCom(const mjModel* m, mjData* d, mjtNum* jacp, int body) { void mj_jacSubtreeCom(const mjModel* m, mjData* d, mjtNum* jacp, int body) {
int nv = m->nv; int nv = m->nv;
mjMARKSTACK; mj_markStack(d);
mjtNum* jacp_b = mj_stackAllocNum(d, 3*nv); mjtNum* jacp_b = mj_stackAllocNum(d, 3*nv);
// clear output // clear output
@@ -299,7 +300,7 @@ void mj_jacSubtreeCom(const mjModel* m, mjData* d, mjtNum* jacp, int body) {
// normalize by subtree mass // normalize by subtree mass
mju_scl(jacp, jacp, 1/m->body_subtreemass[body], 3*nv); mju_scl(jacp, jacp, 1/m->body_subtreemass[body], 3*nv);
mjFREESTACK; mj_freeStack(d);
} }
@@ -324,7 +325,7 @@ void mj_jacPointAxis(const mjModel* m, mjData* d, mjtNum* jacPoint, mjtNum* jacA
int nv = m->nv; int nv = m->nv;
// get full Jacobian of point // get full Jacobian of point
mjMARKSTACK; mj_markStack(d);
mjtNum* jacp = (jacPoint ? jacPoint : mj_stackAllocNum(d, 3*nv)); mjtNum* jacp = (jacPoint ? jacPoint : mj_stackAllocNum(d, 3*nv));
mjtNum* jacr = mj_stackAllocNum(d, 3*nv); mjtNum* jacr = mj_stackAllocNum(d, 3*nv);
mj_jac(m, d, jacp, jacr, point, body); mj_jac(m, d, jacp, jacr, point, body);
@@ -338,7 +339,7 @@ void mj_jacPointAxis(const mjModel* m, mjData* d, mjtNum* jacPoint, mjtNum* jacA
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -827,25 +828,27 @@ void mj_fullM(const mjModel* m, mjtNum* dst, const mjtNum* M) {
// multiply vector by inertia matrix // multiply vector by inertia matrix
void mj_mulM(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec) { void mj_mulM(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec) {
int adr, nv = m->nv; int nv = m->nv;
const mjtNum* M = d->qM; const mjtNum* M = d->qM;
const int* dofMadr = m->dof_Madr; const int* Madr = m->dof_Madr;
const int* parentid = m->dof_parentid;
const int* simplenum = m->dof_simplenum;
mju_zero(res, nv); mju_zero(res, nv);
for (int i=0; i < nv; i++) { for (int i=0; i < nv; i++) {
#ifdef mjUSEAVX #ifdef mjUSEAVX
// simple: diagonal division, AVX // simple: diagonal multiplication, AVX
if (m->dof_simplenum[i] >= 4) { if (simplenum[i] >= 4) {
// init // init
__m256d result, val1, val2; __m256d result, val1, val2;
// parallel computation // parallel computation
val1 = _mm256_loadu_pd(vec+i); val1 = _mm256_loadu_pd(vec+i);
val2 = _mm256_set_pd(M[dofMadr[i+3]], val2 = _mm256_set_pd(M[Madr[i+3]],
M[dofMadr[i+2]], M[Madr[i+2]],
M[dofMadr[i+1]], M[Madr[i+1]],
M[dofMadr[i+0]]); M[Madr[i+0]]);
result = _mm256_mul_pd(val1, val2); result = _mm256_mul_pd(val1, val2);
// store result // store result
@@ -856,29 +859,88 @@ void mj_mulM(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec)
continue; continue;
} }
#endif #endif
// address in M
int adr = Madr[i];
// simple: diagonal multiplication // compute diagonal
if (m->dof_simplenum[i]) { res[i] = M[adr]*vec[i];
res[i] = M[dofMadr[i]]*vec[i];
// simple dof: continue
if (simplenum[i]) {
continue;
} }
// regular: full multiplication // compute off-diagonals
else { int j = parentid[i];
// diagonal while (j >= 0) {
adr = dofMadr[i];
res[i] += M[adr]*vec[i];
// off-diagonal
int j = m->dof_parentid[i];
adr++; adr++;
while (j >= 0) { res[i] += M[adr]*vec[j];
res[i] += M[adr]*vec[j]; res[j] += M[adr]*vec[i];
res[j] += M[adr]*vec[i];
// advance to next element // advance to parent
j = m->dof_parentid[j]; j = parentid[j];
adr++; }
}
}
// multiply vector by inertia matrix for one dof island
void mj_mulM_island(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec,
int island, int flg_vecunc) {
// if no island, call regular function
if (island < 0) {
mj_mulM(m, d, res, vec);
return;
}
// local constants: general
const mjtNum* M = d->qM;
const int* Madr = m->dof_Madr;
const int* parentid = m->dof_parentid;
const int* simplenum = m->dof_simplenum;
// local constants: island specific
int ndof = d->island_dofnum[island];
const int* dofind = d->island_dofind + d->island_dofadr[island];
const int* islandind = d->dof_islandind;
mju_zero(res, ndof);
for (int k=0; k < ndof; k++) {
// address in full dof vector
int i = dofind[k];
// address in M
int adr = Madr[i];
// diagonal
if (flg_vecunc) {
res[k] = M[adr]*vec[i];
} else {
res[k] = M[adr]*vec[k];
}
// simple dof: continue
if (simplenum[i]) {
continue;
}
// off-diagonal
int j = parentid[i];
while (j >= 0) {
adr++;
int l = islandind[j];
if (flg_vecunc) {
res[k] += M[adr]*vec[j];
res[l] += M[adr]*vec[i];
} else {
res[k] += M[adr]*vec[l];
res[l] += M[adr]*vec[k];
} }
// advance to parent
j = parentid[j];
} }
} }
} }
@@ -952,7 +1014,7 @@ void mj_addM(const mjModel* m, mjData* d, mjtNum* dst,
// sparse // sparse
if (rownnz && rowadr && colind) { if (rownnz && rowadr && colind) {
int nv = m->nv; int nv = m->nv;
mjMARKSTACK; mj_markStack(d);
// create sparse inertia matrix M // create sparse inertia matrix M
int nnz = m->nD; // use sparse dof-dof matrix int nnz = m->nD; // use sparse dof-dof matrix
int* M_rownnz = mj_stackAllocInt(d, nv); // actual nnz count int* M_rownnz = mj_stackAllocInt(d, nv); // actual nnz count
@@ -962,7 +1024,7 @@ void mj_addM(const mjModel* m, mjData* d, mjtNum* dst,
mj_makeMSparse(m, d, M, M_rownnz, NULL, M_colind); mj_makeMSparse(m, d, M, M_rownnz, NULL, M_colind);
mj_addMSparse(m, d, dst, rownnz, rowadr, colind, M, mj_addMSparse(m, d, dst, rownnz, rowadr, colind, M,
M_rownnz, NULL, M_colind); M_rownnz, NULL, M_colind);
mjFREESTACK; mj_freeStack(d);
} }
// dense // dense
@@ -997,7 +1059,7 @@ void mj_makeMSparse(const mjModel* m, mjData* d, mjtNum* M,
} }
// backward pass over dofs: construct M_row(i) in reverse order // backward pass over dofs: construct M_row(i) in reverse order
int col = M_rowadr[i]; // current column in row i int col = M_rowadr[i]; // current column in row i
for (int j = i; j >= 0; j = m->dof_parentid[j]) { for (int j = i; j >= 0; j = m->dof_parentid[j]) {
M[col] = d->qM[Madr++]; M[col] = d->qM[Madr++];
M_colind[col++] = j; M_colind[col++] = j;
@@ -1049,7 +1111,7 @@ void mj_addMSparse(const mjModel* m, mjData* d, mjtNum* dst,
M_rowadr = d->D_rowadr; M_rowadr = d->D_rowadr;
} }
mjMARKSTACK; mj_markStack(d);
int* buf_ind = mj_stackAllocInt(d, nv); int* buf_ind = mj_stackAllocInt(d, nv);
mjtNum* sparse_buf = mj_stackAllocNum(d, nv); mjtNum* sparse_buf = mj_stackAllocNum(d, nv);
@@ -1059,7 +1121,7 @@ void mj_addMSparse(const mjModel* m, mjData* d, mjtNum* dst,
rownnz[i], M_rownnz[i], colind + rowadr[i], rownnz[i], M_rownnz[i], colind + rowadr[i],
M_colind + M_rowadr[i], sparse_buf, buf_ind); M_colind + M_rowadr[i], sparse_buf, buf_ind);
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -1096,11 +1158,11 @@ void mj_addMDense(const mjModel* m, mjData* d, mjtNum* dst) {
// dst[D] = src[M], handle different sparsity representations // dst[D] = src[M], handle different sparsity representations
void mj_copyM2DSparse(const mjModel* m, mjData* d, mjtNum* dst, const mjtNum* src) { void mj_copyM2DSparse(const mjModel* m, mjData* d, mjtNum* dst, const mjtNum* src) {
int nv = m->nv; int nv = m->nv;
mjMARKSTACK; mj_markStack(d);
// init remaining // init remaining
int* remaining = mj_stackAllocInt(d, nv); int* remaining = mj_stackAllocInt(d, nv);
memcpy(remaining, d->D_rownnz, nv * sizeof(int)); mju_copyInt(remaining, d->D_rownnz, nv);
// copy data // copy data
for (int i = nv - 1; i >= 0; i--) { for (int i = nv - 1; i >= 0; i--) {
@@ -1123,7 +1185,7 @@ void mj_copyM2DSparse(const mjModel* m, mjData* d, mjtNum* dst, const mjtNum* sr
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -1161,7 +1223,7 @@ void mj_applyFT(const mjModel* m, mjData* d,
int nv = m->nv; int nv = m->nv;
// allocate local variables // allocate local variables
mjMARKSTACK; mj_markStack(d);
mjtNum* jacp = mj_stackAllocNum(d, 3*nv); mjtNum* jacp = mj_stackAllocNum(d, 3*nv);
mjtNum* jacr = mj_stackAllocNum(d, 3*nv); mjtNum* jacr = mj_stackAllocNum(d, 3*nv);
mjtNum* qforce = mj_stackAllocNum(d, nv); mjtNum* qforce = mj_stackAllocNum(d, nv);
@@ -1184,7 +1246,7 @@ void mj_applyFT(const mjModel* m, mjData* d,
mju_addTo(qfrc_target, qforce, nv); mju_addTo(qfrc_target, qforce, nv);
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -1525,7 +1587,7 @@ int mj_version(void) {
// current version of MuJoCo as a null-terminated string // current version of MuJoCo as a null-terminated string
const char* mj_versionString() { const char* mj_versionString(void) {
static const char versionstring[] = mjVERSIONSTRING; static const char versionstring[] = mjVERSIONSTRING;
return versionstring; return versionstring;
} }
+8 -1
View File
@@ -15,9 +15,12 @@
#ifndef MUJOCO_SRC_ENGINE_ENGINE_SUPPORT_H_ #ifndef MUJOCO_SRC_ENGINE_ENGINE_SUPPORT_H_
#define MUJOCO_SRC_ENGINE_ENGINE_SUPPORT_H_ #define MUJOCO_SRC_ENGINE_ENGINE_SUPPORT_H_
#include <stdint.h>
#include <mujoco/mjdata.h> #include <mujoco/mjdata.h>
#include <mujoco/mjexport.h> #include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h> #include <mujoco/mjmodel.h>
#include <mujoco/mjtnum.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -108,6 +111,10 @@ MJAPI void mj_fullM(const mjModel* m, mjtNum* dst, const mjtNum* M);
// multiply vector by inertia matrix // multiply vector by inertia matrix
MJAPI void mj_mulM(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec); MJAPI void mj_mulM(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec);
// multiply vector by inertia matrix for one dof island
MJAPI void mj_mulM_island(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec,
int island, int flg_vecunc);
// multiply vector by (inertia matrix)^(1/2) // multiply vector by (inertia matrix)^(1/2)
MJAPI void mj_mulM2(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec); MJAPI void mj_mulM2(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec);
@@ -193,7 +200,7 @@ MJAPI void mj_warning(mjData* d, int warning, int info);
MJAPI int mj_version(void); MJAPI int mj_version(void);
// current version of MuJoCo as a null-terminated string // current version of MuJoCo as a null-terminated string
MJAPI const char* mj_versionString(); MJAPI const char* mj_versionString(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
+2 -7
View File
@@ -16,7 +16,6 @@
#include <string.h> #include <string.h>
#include <mujoco/mjmacro.h>
#include <mujoco/mjtnum.h> #include <mujoco/mjtnum.h>
#ifdef mjUSEPLATFORMSIMD #ifdef mjUSEPLATFORMSIMD
@@ -238,9 +237,7 @@ mjtNum mju_normalize4(mjtNum vec[4]) {
// res = 0 // res = 0
void mju_zero(mjtNum* res, int n) { void mju_zero(mjtNum* res, int n) {
if (n > 0) { memset(res, 0, n*sizeof(mjtNum));
memset(res, 0, n*sizeof(mjtNum));
}
} }
@@ -256,9 +253,7 @@ void mju_fill(mjtNum* res, mjtNum val, int n) {
// res = vec // res = vec
void mju_copy(mjtNum* res, const mjtNum* vec, int n) { void mju_copy(mjtNum* res, const mjtNum* vec, int n) {
if (n > 0) { memcpy(res, vec, n*sizeof(mjtNum));
memcpy(res, vec, n*sizeof(mjtNum));
}
} }
+18 -11
View File
@@ -70,7 +70,7 @@ typedef void (*callback_fn)(const char*);
static mjTHREADLOCAL callback_fn _mjPRIVATE_tls_error_fn = NULL; static mjTHREADLOCAL callback_fn _mjPRIVATE_tls_error_fn = NULL;
static mjTHREADLOCAL callback_fn _mjPRIVATE_tls_warning_fn = NULL; static mjTHREADLOCAL callback_fn _mjPRIVATE_tls_warning_fn = NULL;
callback_fn _mjPRIVATE__get_tls_error_fn() { callback_fn _mjPRIVATE__get_tls_error_fn(void) {
return _mjPRIVATE_tls_error_fn; return _mjPRIVATE_tls_error_fn;
} }
@@ -78,7 +78,7 @@ void _mjPRIVATE__set_tls_error_fn(callback_fn h) {
_mjPRIVATE_tls_error_fn = h; _mjPRIVATE_tls_error_fn = h;
} }
callback_fn _mjPRIVATE__get_tls_warning_fn() { callback_fn _mjPRIVATE__get_tls_warning_fn(void) {
return _mjPRIVATE_tls_warning_fn; return _mjPRIVATE_tls_warning_fn;
} }
@@ -113,20 +113,17 @@ void mju_writeLog(const char* type, const char* msg) {
} }
} }
void mju_error_v(const char* msg, va_list args) {
char errmsg[1000];
// Format msg into errmsg
vsnprintf(errmsg, mjSIZEOFARRAY(errmsg), msg, args);
void mju_error_raw(const char* msg) {
if (_mjPRIVATE_tls_error_fn) { if (_mjPRIVATE_tls_error_fn) {
_mjPRIVATE_tls_error_fn(errmsg); _mjPRIVATE_tls_error_fn(msg);
} else if (mju_user_error) { } else if (mju_user_error) {
mju_user_error(errmsg); mju_user_error(msg);
} else { } else {
// write to log and console // write to log and console
mju_writeLog("ERROR", errmsg); mju_writeLog("ERROR", msg);
printf("ERROR: %s\n\nPress Enter to exit ...", errmsg); printf("ERROR: %s\n\nPress Enter to exit ...", msg);
// pause, exit // pause, exit
getchar(); getchar();
@@ -135,6 +132,16 @@ void mju_error_v(const char* msg, va_list args) {
} }
void mju_error_v(const char* msg, va_list args) {
// Format msg into errmsg
char errmsg[1024];
vsnprintf(errmsg, mjSIZEOFARRAY(errmsg), msg, args);
mju_error_raw(errmsg);
}
// write message to logfile and console, pause and exit // write message to logfile and console, pause and exit
void mju_error(const char* msg, ...) { void mju_error(const char* msg, ...) {
va_list args; va_list args;
@@ -147,7 +154,7 @@ void mju_error(const char* msg, ...) {
// write message to logfile and console // write message to logfile and console
void mju_warning(const char* msg, ...) { void mju_warning(const char* msg, ...) {
char wrnmsg[1000]; char wrnmsg[1024];
// Format msg into wrnmsg // Format msg into wrnmsg
va_list args; va_list args;
+13 -38
View File
@@ -18,6 +18,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <mujoco/mjexport.h> #include <mujoco/mjexport.h>
#include <mujoco/mjmacro.h> #include <mujoco/mjmacro.h>
@@ -31,8 +32,8 @@ extern "C" {
#define mjPRINTFLIKE(n, m) __attribute__((format(printf, n, m))) #define mjPRINTFLIKE(n, m) __attribute__((format(printf, n, m)))
#else #else
#define mjPRINTFLIKE(n, m) #define mjPRINTFLIKE(n, m)
#endif // __GNUC__ #endif // __GNUC__
#endif // mjPRINTFLIKE #endif // mjPRINTFLIKE
//------------------------------ user handlers ----------------------------------------------------- //------------------------------ user handlers -----------------------------------------------------
@@ -54,6 +55,7 @@ MJAPI void _mjPRIVATE__set_tls_warning_fn(void (*h)(const char*));
//------------------------------ errors and warnings ----------------------------------------------- //------------------------------ errors and warnings -----------------------------------------------
// errors // errors
MJAPI void mju_error_raw(const char* msg);
MJAPI void mju_error(const char* msg, ...) mjPRINTFLIKE(1, 2); MJAPI void mju_error(const char* msg, ...) mjPRINTFLIKE(1, 2);
MJAPI void mju_error_v(const char* msg, va_list args); MJAPI void mju_error_v(const char* msg, va_list args);
MJAPI void mju_error_i(const char* msg, int i); MJAPI void mju_error_i(const char* msg, int i);
@@ -69,42 +71,15 @@ MJAPI void mju_writeLog(const char* type, const char* msg);
//------------------------------ internal error macros -------------------------------------------- //------------------------------ internal error macros --------------------------------------------
// need at least c99 or c++11 // internal macro to prepend the calling function name to the error message
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ #define mjERROR(...) \
(defined(__cplusplus) && __cplusplus >= 201103L) { \
char _errbuf[1024]; \
// macro to get the first argument size_t _funclen = strlen(__func__); \
#define _GET_MSG(msg, ...) msg strncpy(_errbuf, __func__, sizeof(_errbuf)); \
snprintf(_errbuf + _funclen, sizeof(_errbuf) - _funclen, ": " __VA_ARGS__); \
// helper function for the mjERROR macro mju_error_raw(_errbuf); \
// formats buf as '{prefix}: {msg}' and passes along to mju_error_v }
static inline void _mju_error_prefix(char *buf, size_t nbuf, const char* prefix,
const char* msg, ...) mjPRINTFLIKE(4, 5);
static inline void _mju_error_prefix(char *buf, size_t nbuf, const char* prefix,
const char* msg, ...) {
snprintf(buf, nbuf, "%s: %s", prefix, msg);
va_list args;
va_start(args, msg);
mju_error_v(buf, args);
va_end(args);
}
// macro to get first argument
#define _GET_MSG(msg, ...) msg
// internal macro to prepend the calling function name to the error message
// standard support for variadic macros with zero arguments is only now
// supported in C23 and C++20 so we rely on a helper function to get around this
// in a portable way
#define mjERROR(...) { \
char _buf[sizeof(_GET_MSG(__VA_ARGS__)) + sizeof(__func__) + 1]; \
_mju_error_prefix(_buf, sizeof(_buf), __func__, __VA_ARGS__); \
}
#else
#define mjERROR mju_error
#endif // c99 or c++11
//------------------------------ malloc and free --------------------------------------------------- //------------------------------ malloc and free ---------------------------------------------------
+14
View File
@@ -1207,6 +1207,20 @@ int mju_isZero(mjtNum* vec, int n) {
// set integer vector to 0
void mju_zeroInt(int* res, int n) {
memset(res, 0, n*sizeof(int));
}
// copy int vector vec into res
void mju_copyInt(int* res, const int* vec, int n) {
memcpy(res, vec, n*sizeof(int));
}
// standard normal random number generator (optional second number) // standard normal random number generator (optional second number)
mjtNum mju_standardNormal(mjtNum* num2) { mjtNum mju_standardNormal(mjtNum* num2) {
const mjtNum scale = 2.0/((mjtNum)RAND_MAX); const mjtNum scale = 2.0/((mjtNum)RAND_MAX);
+6
View File
@@ -119,6 +119,12 @@ MJAPI int mju_isBad(mjtNum x);
// return 1 if all elements are 0 // return 1 if all elements are 0
MJAPI int mju_isZero(mjtNum* vec, int n); MJAPI int mju_isZero(mjtNum* vec, int n);
// set integer vector to 0
MJAPI void mju_zeroInt(int* res, int n);
// copy int vector vec into res
MJAPI void mju_copyInt(int* res, const int* vec, int n);
// standard normal random number generator (optional second number) // standard normal random number generator (optional second number)
MJAPI mjtNum mju_standardNormal(mjtNum* num2); MJAPI mjtNum mju_standardNormal(mjtNum* num2);
+6 -7
View File
@@ -16,7 +16,6 @@
#include <math.h> #include <math.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <mujoco/mjdata.h> #include <mujoco/mjdata.h>
#include <mujoco/mjmacro.h> #include <mujoco/mjmacro.h>
@@ -148,7 +147,7 @@ int mju_cholFactorSparse(mjtNum* mat, int n, mjtNum mindiag,
mjData* d) { mjData* d) {
int rank = n; int rank = n;
mjMARKSTACK; mj_markStack(d);
int* buf_ind = mj_stackAllocInt(d, n); int* buf_ind = mj_stackAllocInt(d, n);
mjtNum* sparse_buf = mj_stackAllocNum(d, n); mjtNum* sparse_buf = mj_stackAllocNum(d, n);
@@ -199,7 +198,7 @@ int mju_cholFactorSparse(mjtNum* mat, int n, mjtNum mindiag,
} }
} }
mjFREESTACK; mj_freeStack(d);
return rank; return rank;
} }
@@ -235,7 +234,7 @@ void mju_cholSolveSparse(mjtNum* res, const mjtNum* mat, const mjtNum* vec, int
// x(i) -= sum_j L(i,j)*x(j), j=0:i-1 // x(i) -= sum_j L(i,j)*x(j), j=0:i-1
if (nnz > 1) { if (nnz > 1) {
res[i] -= mju_dotSparse(mat+adr, res, nnz-1, colind+adr); res[i] -= mju_dotSparse(mat+adr, res, nnz-1, colind+adr, /*flg_unc1=*/0);
// modulo AVX, the above line does // modulo AVX, the above line does
// for (int j=0; j<nnz-1; j++) // for (int j=0; j<nnz-1; j++)
// res[i] -= mat[adr+j]*res[colind[adr+j]]; // res[i] -= mat[adr+j]*res[colind[adr+j]];
@@ -254,7 +253,7 @@ void mju_cholSolveSparse(mjtNum* res, const mjtNum* mat, const mjtNum* vec, int
int mju_cholUpdateSparse(mjtNum* mat, mjtNum* x, int n, int flg_plus, int mju_cholUpdateSparse(mjtNum* mat, mjtNum* x, int n, int flg_plus,
int* rownnz, int* rowadr, int* colind, int x_nnz, int* x_ind, int* rownnz, int* rowadr, int* colind, int x_nnz, int* x_ind,
mjData* d) { mjData* d) {
mjMARKSTACK; mj_markStack(d);
int* buf_ind = mj_stackAllocInt(d, n); int* buf_ind = mj_stackAllocInt(d, n);
mjtNum* sparse_buf = mj_stackAllocNum(d, n); mjtNum* sparse_buf = mj_stackAllocNum(d, n);
@@ -295,7 +294,7 @@ int mju_cholUpdateSparse(mjtNum* mat, mjtNum* x, int n, int flg_plus,
i = i - 1 + (new_x_nnz - i); i = i - 1 + (new_x_nnz - i);
} }
mjFREESTACK; mj_freeStack(d);
return rank; return rank;
} }
@@ -592,7 +591,7 @@ void mju_factorLUSparse(mjtNum* LU, int n, int* scratch,
int* remaining = scratch; int* remaining = scratch;
// set remaining = rownnz // set remaining = rownnz
memcpy(remaining, rownnz, n*sizeof(int)); mju_copyInt(remaining, rownnz, n);
// diagonal elements (i,i) // diagonal elements (i,i)
for (int i=n-1; i >= 0; i--) { for (int i=n-1; i >= 0; i--) {
+45 -23
View File
@@ -13,24 +13,25 @@
// limitations under the License. // limitations under the License.
#include "engine/engine_util_sparse.h" #include "engine/engine_util_sparse.h"
#include "engine/engine_util_sparse_avx.h" #include "engine/engine_util_sparse_avx.h" // IWYU pragma: keep
#include <string.h> #include <string.h>
#include <mujoco/mjdata.h> #include <mujoco/mjdata.h>
#include <mujoco/mjmacro.h>
#include <mujoco/mjtnum.h> #include <mujoco/mjtnum.h>
#include "engine/engine_io.h" #include "engine/engine_io.h"
#include "engine/engine_util_blas.h" #include "engine/engine_util_blas.h"
#include "engine/engine_util_misc.h"
//------------------------------ sparse operations ------------------------------------------------- //------------------------------ sparse operations -------------------------------------------------
// dot-product, first vector is sparse // dot-product, first vector is sparse
// flg_unc1: is vec1 memory layout uncompressed
mjtNum mju_dotSparse(const mjtNum* vec1, const mjtNum* vec2, mjtNum mju_dotSparse(const mjtNum* vec1, const mjtNum* vec2,
const int nnz1, const int* ind1) { const int nnz1, const int* ind1, int flg_unc1) {
#ifdef mjUSEAVX #ifdef mjUSEAVX
return mju_dotSparse_avx(vec1, vec2, nnz1, ind1); return mju_dotSparse_avx(vec1, vec2, nnz1, ind1, flg_unc1);
#else #else
int i = 0; int i = 0;
mjtNum res = 0; mjtNum res = 0;
@@ -40,17 +41,33 @@ mjtNum mju_dotSparse(const mjtNum* vec1, const mjtNum* vec2,
mjtNum res2 = 0; mjtNum res2 = 0;
mjtNum res3 = 0; mjtNum res3 = 0;
for (; i <= n_4; i+=4) {
res0 += vec1[i+0] * vec2[ind1[i+0]]; if (flg_unc1) {
res1 += vec1[i+1] * vec2[ind1[i+1]]; for (; i <= n_4; i+=4) {
res2 += vec1[i+2] * vec2[ind1[i+2]]; res0 += vec1[ind1[i+0]] * vec2[ind1[i+0]];
res3 += vec1[i+3] * vec2[ind1[i+3]]; res1 += vec1[ind1[i+1]] * vec2[ind1[i+1]];
res2 += vec1[ind1[i+2]] * vec2[ind1[i+2]];
res3 += vec1[ind1[i+3]] * vec2[ind1[i+3]];
}
} else {
for (; i <= n_4; i+=4) {
res0 += vec1[i+0] * vec2[ind1[i+0]];
res1 += vec1[i+1] * vec2[ind1[i+1]];
res2 += vec1[i+2] * vec2[ind1[i+2]];
res3 += vec1[i+3] * vec2[ind1[i+3]];
}
} }
res = (res0 + res2) + (res1 + res3); res = (res0 + res2) + (res1 + res3);
// scalar part // scalar part
for (; i < nnz1; i++) { if (flg_unc1) {
res += vec1[i] * vec2[ind1[i]]; for (; i < nnz1; i++) {
res += vec1[ind1[i]] * vec2[ind1[i]];
}
} else {
for (; i < nnz1; i++) {
res += vec1[i] * vec2[ind1[i]];
}
} }
return res; return res;
@@ -91,9 +108,10 @@ void mju_dotSparseX3(mjtNum* res0, mjtNum* res1, mjtNum* res2,
// dot-product, both vectors are sparse // dot-product, both vectors are sparse
// flg_unc2: is vec2 memory layout uncompressed
mjtNum mju_dotSparse2(const mjtNum* vec1, const mjtNum* vec2, mjtNum mju_dotSparse2(const mjtNum* vec1, const mjtNum* vec2,
const int nnz1, const int* ind1, const int nnz1, const int* ind1,
const int nnz2, const int* ind2) { const int nnz2, const int* ind2, int flg_unc2) {
int i1 = 0, i2 = 0; int i1 = 0, i2 = 0;
mjtNum res = 0; mjtNum res = 0;
@@ -108,7 +126,12 @@ mjtNum mju_dotSparse2(const mjtNum* vec1, const mjtNum* vec2,
// match: accumulate result, advance both // match: accumulate result, advance both
if (adr1 == adr2) { if (adr1 == adr2) {
res += vec1[i1++] * vec2[i2++]; if (flg_unc2) {
res += vec1[i1++] * vec2[adr2];
i2++;
} else {
res += vec1[i1++] * vec2[i2++];
}
} }
// otherwise advance smaller // otherwise advance smaller
@@ -176,7 +199,7 @@ void mju_mulMatVecSparse(mjtNum* res, const mjtNum* mat, const mjtNum* vec,
#else #else
// regular sparse dot-product // regular sparse dot-product
for (int r=0; r < nr; r++) { for (int r=0; r < nr; r++) {
res[r] = mju_dotSparse(mat+rowadr[r], vec, rownnz[r], colind+rowadr[r]); res[r] = mju_dotSparse(mat+rowadr[r], vec, rownnz[r], colind+rowadr[r], /*flg_unc1=*/0);
} }
#endif // mjUSEAVX #endif // mjUSEAVX
} }
@@ -249,8 +272,8 @@ int mju_combineSparse(mjtNum* dst, const mjtNum* src, mjtNum a, mjtNum b,
// copy dst into buf // copy dst into buf
if (dst_nnz) { if (dst_nnz) {
memcpy(buf, dst, dst_nnz*sizeof(mjtNum)); mju_copy(buf, dst, dst_nnz);
memcpy(buf_ind, dst_ind, dst_nnz*sizeof(int)); mju_copyInt(buf_ind, dst_ind, dst_nnz);
} }
// prepare to merge buf and src into dst // prepare to merge buf and src into dst
@@ -370,7 +393,7 @@ void mju_transposeSparse(mjtNum* res, const mjtNum* mat, int nr, int nc,
int* res_rownnz, int* res_rowadr, int* res_colind, int* res_rownnz, int* res_rowadr, int* res_colind,
const int* rownnz, const int* rowadr, const int* colind) { const int* rownnz, const int* rowadr, const int* colind) {
// clear number of non-zeros for each row of transposed // clear number of non-zeros for each row of transposed
memset(res_rownnz, 0, nc*sizeof(int)); mju_zeroInt(res_rownnz, nc);
// total number of non-zeros of mat // total number of non-zeros of mat
int nnz = rowadr[nr-1] + rownnz[nr-1]; int nnz = rowadr[nr-1] + rownnz[nr-1];
@@ -450,13 +473,12 @@ void mju_sqrMatTDSparseInit(int* res_rownnz, int* res_rowadr,
const int* rownnzT, const int* rowadrT, const int* rownnzT, const int* rowadrT,
const int* colindT, const int* rowsuperT, const int* colindT, const int* rowsuperT,
mjData* d) { mjData* d) {
mjMARKSTACK; mj_markStack(d);
int* chain = mj_stackAllocInt(d, 2*nc); int* chain = mj_stackAllocInt(d, 2*nc);
int nchain = 0; int nchain = 0;
int* res_colind = NULL; int* res_colind = NULL;
for (int r=0; r < nc; r++) { for (int r=0; r < nc; r++) {
// supernode; copy everything to next row // supernode; copy everything to next row
if (rowsuperT && r > 0 && rowsuperT[r-1] > 0) { if (rowsuperT && r > 0 && rowsuperT[r-1] > 0) {
res_rownnz[r] = res_rownnz[r - 1]; res_rownnz[r] = res_rownnz[r - 1];
@@ -532,7 +554,7 @@ void mju_sqrMatTDSparseInit(int* res_rownnz, int* res_rowadr,
res_rowadr[r] = res_rowadr[r-1] + res_rownnz[r-1]; res_rowadr[r] = res_rowadr[r-1] + res_rownnz[r-1];
} }
mjFREESTACK; mj_freeStack(d);
} }
@@ -556,7 +578,7 @@ void mju_sqrMatTDSparse(mjtNum* res, const mjtNum* mat, const mjtNum* matT,
const int* colindT, const int* rowsuperT, const int* colindT, const int* rowsuperT,
mjData* d) { mjData* d) {
// allocate space for accumulation buffer and matT // allocate space for accumulation buffer and matT
mjMARKSTACK; mj_markStack(d);
// a dense row buffer that stores the current row in the resulting matrix // a dense row buffer that stores the current row in the resulting matrix
mjtNum* buffer = mj_stackAllocNum(d, nc); mjtNum* buffer = mj_stackAllocNum(d, nc);
@@ -575,7 +597,7 @@ void mju_sqrMatTDSparse(mjtNum* res, const mjtNum* mat, const mjtNum* matT,
// if rowsuper, use the previous row sparsity structure // if rowsuper, use the previous row sparsity structure
if (rowsuperT && i > 0 && rowsuperT[i-1]) { if (rowsuperT && i > 0 && rowsuperT[i-1]) {
res_rownnz[i] = res_rownnz[i-1]; res_rownnz[i] = res_rownnz[i-1];
memcpy(cols, res_colind+res_rowadr[i-1], res_rownnz[i]*sizeof(int)); mju_copyInt(cols, res_colind+res_rowadr[i-1], res_rownnz[i]);
} }
// iterate through each row of M' // iterate through each row of M'
@@ -665,5 +687,5 @@ void mju_sqrMatTDSparse(mjtNum* res, const mjtNum* mat, const mjtNum* matT,
} }
} }
mjFREESTACK; mj_freeStack(d);
} }
+6 -6
View File
@@ -25,14 +25,14 @@ extern "C" {
//------------------------------ sparse operations ------------------------------------------------- //------------------------------ sparse operations -------------------------------------------------
// dot-product, first vector is sparse // dot-product, vec1 is sparse, can be uncompressed
MJAPI mjtNum mju_dotSparse(const mjtNum* vec1, const mjtNum* vec2, MJAPI mjtNum mju_dotSparse(const mjtNum* vec1, const mjtNum* vec2,
const int nnz1, const int* ind1); const int nnz1, const int* ind1, int flg_unc1);
// dot-product, both vectors are sparse // dot-product, both vectors are sparse, vec2 can be uncompressed
mjtNum mju_dotSparse2(const mjtNum* vec1, const mjtNum* vec2, MJAPI mjtNum mju_dotSparse2(const mjtNum* vec1, const mjtNum* vec2,
const int nnz1, const int* ind1, const int nnz1, const int* ind1,
const int nnz2, const int* ind2); const int nnz2, const int* ind2, int flg_unc2);
// convert matrix from dense to sparse // convert matrix from dense to sparse
MJAPI void mju_dense2sparse(mjtNum* res, const mjtNum* mat, int nr, int nc, MJAPI void mju_dense2sparse(mjtNum* res, const mjtNum* mat, int nr, int nc,
+46 -16
View File
@@ -30,9 +30,10 @@
//------------------------------ sparse operations using avx --------------------------------------- //------------------------------ sparse operations using avx ---------------------------------------
// dot-product, first vector is sparse // dot-product, first vector is sparse
// flg_unc1: is vec1 memory layout uncompressed
static inline static inline
mjtNum mju_dotSparse_avx(const mjtNum* vec1, const mjtNum* vec2, mjtNum mju_dotSparse_avx(const mjtNum* vec1, const mjtNum* vec2,
const int nnz1, const int* ind1) { const int nnz1, const int* ind1, int flg_unc1) {
int i = 0; int i = 0;
mjtNum res = 0; mjtNum res = 0;
int nnz1_4 = nnz1 - 4; int nnz1_4 = nnz1 - 4;
@@ -47,20 +48,43 @@ mjtNum mju_dotSparse_avx(const mjtNum* vec1, const mjtNum* vec2,
vec2[ind1[2]], vec2[ind1[2]],
vec2[ind1[1]], vec2[ind1[1]],
vec2[ind1[0]]); vec2[ind1[0]]);
val1 = _mm256_loadu_pd(vec1); if (flg_unc1) {
val1 = _mm256_set_pd(vec1[ind1[3]],
vec1[ind1[2]],
vec1[ind1[1]],
vec1[ind1[0]]);
} else {
val1 = _mm256_loadu_pd(vec1);
}
sum = _mm256_mul_pd(val1, val2); sum = _mm256_mul_pd(val1, val2);
i = 4; i = 4;
// parallel computation // parallel computation
while (i<=nnz1_4) { if (flg_unc1) {
val1 = _mm256_loadu_pd(vec1+i); while (i<=nnz1_4) {
val2 = _mm256_set_pd(vec2[ind1[i+3]], val1 = _mm256_set_pd(vec1[ind1[i+3]],
vec2[ind1[i+2]], vec1[ind1[i+2]],
vec2[ind1[i+1]], vec1[ind1[i+1]],
vec2[ind1[i+0]]); vec1[ind1[i+0]]);
prod = _mm256_mul_pd(val1, val2); val2 = _mm256_set_pd(vec2[ind1[i+3]],
sum = _mm256_add_pd(sum, prod); vec2[ind1[i+2]],
i += 4; vec2[ind1[i+1]],
vec2[ind1[i+0]]);
prod = _mm256_mul_pd(val1, val2);
sum = _mm256_add_pd(sum, prod);
i += 4;
}
} else {
while (i<=nnz1_4) {
val1 = _mm256_loadu_pd(vec1+i);
val2 = _mm256_set_pd(vec2[ind1[i+3]],
vec2[ind1[i+2]],
vec2[ind1[i+1]],
vec2[ind1[i+0]]);
prod = _mm256_mul_pd(val1, val2);
sum = _mm256_add_pd(sum, prod);
i += 4;
}
} }
// reduce // reduce
@@ -72,8 +96,14 @@ mjtNum mju_dotSparse_avx(const mjtNum* vec1, const mjtNum* vec2,
} }
// scalar part // scalar part
for (; i<nnz1; i++) { if (flg_unc1) {
res += vec1[i] * vec2[ind1[i]]; for (; i < nnz1; i++) {
res += vec1[ind1[i]] * vec2[ind1[i]];
}
} else {
for (; i < nnz1; i++) {
res += vec1[i] * vec2[ind1[i]];
}
} }
return res; return res;
@@ -179,7 +209,7 @@ void mju_mulMatVecSparse_avx(mjtNum* res, const mjtNum* mat, const mjtNum* vec,
if (!rowsuper) { if (!rowsuper) {
// regular sparse dot-product // regular sparse dot-product
for (int r=0; r<nr; r++) { for (int r=0; r<nr; r++) {
res[r] = mju_dotSparse_avx(mat+rowadr[r], vec, rownnz[r], colind+rowadr[r]); res[r] = mju_dotSparse_avx(mat+rowadr[r], vec, rownnz[r], colind+rowadr[r], /*flg_unc2=*/0);
} }
return; return;
@@ -202,7 +232,7 @@ void mju_mulMatVecSparse_avx(mjtNum* res, const mjtNum* mat, const mjtNum* vec,
// handle remaining rows // handle remaining rows
while (rs>0) { while (rs>0) {
res[r] = mju_dotSparse_avx(mat+rowadr[r], vec, rownnz[r], colind+rowadr[r]); res[r] = mju_dotSparse_avx(mat+rowadr[r], vec, rownnz[r], colind+rowadr[r], /*flg_unc2=*/0);
r++; r++;
rs--; rs--;
@@ -213,7 +243,7 @@ void mju_mulMatVecSparse_avx(mjtNum* res, const mjtNum* mat, const mjtNum* vec,
} }
else { else {
res[r] = mju_dotSparse_avx(mat+rowadr[r], vec, rownnz[r], colind+rowadr[r]); res[r] = mju_dotSparse_avx(mat+rowadr[r], vec, rownnz[r], colind+rowadr[r], /*flg_unc2=*/0);
} }
} }
} }
+50 -16
View File
@@ -14,10 +14,10 @@
#include "engine/engine_vfs.h" #include "engine/engine_vfs.h"
#include <stddef.h>
#include <string.h> #include <string.h>
#include "engine/engine_array_safety.h" #include "engine/engine_array_safety.h"
#include "engine/engine_plugin.h"
#include "engine/engine_resource.h" #include "engine/engine_resource.h"
#include "engine/engine_util_errmem.h" #include "engine/engine_util_errmem.h"
#include "engine/engine_util_misc.h" #include "engine/engine_util_misc.h"
@@ -90,7 +90,7 @@ int mj_addFileVFS(mjVFS* vfs, const char* directory, const char* filename) {
mjSTRNCPY(vfs->filename[vfs->nfile], newname); mjSTRNCPY(vfs->filename[vfs->nfile], newname);
// allocate and read // allocate and read
int filesize = 0; size_t filesize = 0;
vfs->filedata[vfs->nfile] = mju_fileToMemory(fullname, &filesize); vfs->filedata[vfs->nfile] = mju_fileToMemory(fullname, &filesize);
if (!vfs->filedata[vfs->nfile]) { if (!vfs->filedata[vfs->nfile]) {
return -1; return -1;
@@ -211,11 +211,11 @@ void mj_deleteVFS(mjVFS* vfs) {
// open callback for the VFS resource provider // open callback for the VFS resource provider
static int vfs_open_callback(mjResource* resource) { static int vfs_open_callback(mjResource* resource) {
if (!resource || !resource->provider_data || !resource->name) { if (!resource || !resource->name || !resource->data) {
return 0; return 0;
} }
const mjVFS* vfs = (const mjVFS*) resource->provider_data; const mjVFS* vfs = (const mjVFS*) resource->data;
return mj_findFileVFS(vfs, resource->name) >= 0; return mj_findFileVFS(vfs, resource->name) >= 0;
} }
@@ -223,12 +223,12 @@ static int vfs_open_callback(mjResource* resource) {
// read callback for the VFS resource provider // read callback for the VFS resource provider
static int vfs_read_callback(mjResource* resource, const void** buffer) { static int vfs_read_callback(mjResource* resource, const void** buffer) {
if (!resource || !resource->provider_data) { if (!resource || !resource->name || !resource->data) {
*buffer = NULL; *buffer = NULL;
return -1; return -1;
} }
const mjVFS* vfs = (const mjVFS*) resource->provider_data; const mjVFS* vfs = (const mjVFS*) resource->data;
int i = mj_findFileVFS(vfs, resource->name); int i = mj_findFileVFS(vfs, resource->name);
if (i < 0) { if (i < 0) {
*buffer = NULL; *buffer = NULL;
@@ -260,16 +260,50 @@ static void vfs_getdir_callback(mjResource* resource, const char** dir, int* ndi
// registers a VFS resource provider; returns the index of the provider // open VFS resource
int mj_registerVfsProvider(const mjVFS* vfs) { mjResource* mju_openVfsResource(const char* name, const mjVFS* vfs) {
mjpResourceProvider provider = { if (vfs == NULL) {
.prefix = mjVFS_PREFIX, return NULL;
.open = &vfs_open_callback, }
.read = &vfs_read_callback,
.close = &vfs_close_callback, // VFS provider
.getdir = &vfs_getdir_callback, static struct mjpResourceProvider provider = {
.data = (void*) vfs .prefix = NULL,
.data = NULL,
.open = &vfs_open_callback,
.read = &vfs_read_callback,
.close = &vfs_close_callback,
.getdir = &vfs_getdir_callback,
.modified = NULL
}; };
return mjp_registerResourceProviderInternal(&provider); // create resource
mjResource* resource = (mjResource*) mju_malloc(sizeof(mjResource));
if (resource == NULL) {
mjERROR("could not allocate memory");
return NULL;
}
// clear out resource
memset(resource, 0, sizeof(mjResource));
// copy name
resource->name = mju_malloc(sizeof(char) * (strlen(name) + 1));
if (resource->name == NULL) {
mju_closeResource(resource);
mjERROR("could not allocate memory");
return NULL;
}
memcpy(resource->name, name, sizeof(char) * (strlen(name) + 1));
resource->data = (void*) vfs;
// open resource
resource->provider = &provider;
if (provider.open(resource)) {
return resource;
}
// not found in VFS
mju_closeResource(resource);
return NULL;
} }
+3 -2
View File
@@ -17,6 +17,7 @@
#include <mujoco/mjexport.h> #include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h> #include <mujoco/mjmodel.h>
#include <mujoco/mjplugin.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -40,8 +41,8 @@ MJAPI int mj_deleteFileVFS(mjVFS* vfs, const char* filename);
// delete all files from VFS // delete all files from VFS
MJAPI void mj_deleteVFS(mjVFS* vfs); MJAPI void mj_deleteVFS(mjVFS* vfs);
// registers a VFS resource provider; returns the index of the provider // open VFS resource
MJAPI int mj_registerVfsProvider(const mjVFS* vfs); MJAPI mjResource* mju_openVfsResource(const char* name, const mjVFS* vfs);
#ifdef __cplusplus #ifdef __cplusplus
} }
+3 -3
View File
@@ -19,7 +19,6 @@
#include <mujoco/mjdata.h> #include <mujoco/mjdata.h>
#include <mujoco/mjexport.h> #include <mujoco/mjexport.h>
#include <mujoco/mjmacro.h>
#include <mujoco/mjmodel.h> #include <mujoco/mjmodel.h>
#include <mujoco/mjvisualize.h> #include <mujoco/mjvisualize.h>
#include "engine/engine_core_smooth.h" #include "engine/engine_core_smooth.h"
@@ -518,7 +517,7 @@ void mjv_moveModel(const mjModel* m, int action, mjtNum reldx, mjtNum reldy,
// copy perturb pos,quat from selected body; set scale for perturbation // copy perturb pos,quat from selected body; set scale for perturbation
void mjv_initPerturb(const mjModel* m, mjData* d, const mjvScene* scn, mjvPerturb* pert) { void mjv_initPerturb(const mjModel* m, mjData* d, const mjvScene* scn, mjvPerturb* pert) {
mjMARKSTACK; mj_markStack(d);
int nv = m->nv; int nv = m->nv;
int sel = pert->select; int sel = pert->select;
@@ -529,6 +528,7 @@ void mjv_initPerturb(const mjModel* m, mjData* d, const mjvScene* scn, mjvPertur
// invalid selected body: return // invalid selected body: return
if (sel <= 0 || sel >= m->nbody) { if (sel <= 0 || sel >= m->nbody) {
mj_freeStack(d);
return; return;
} }
@@ -557,7 +557,7 @@ void mjv_initPerturb(const mjModel* m, mjData* d, const mjvScene* scn, mjvPertur
mju_sub3(dif, pert->refselpos, headpos); mju_sub3(dif, pert->refselpos, headpos);
pert->scale = mjv_frustumHeight(scn) * mju_dot3(dif, forward); pert->scale = mjv_frustumHeight(scn) * mju_dot3(dif, forward);
mjFREESTACK; mj_freeStack(d);
} }
+3 -3
View File
@@ -263,9 +263,9 @@ void mjv_updateSceneState(const mjModel* m, mjData* d, const mjvOption* opt,
mjvSceneState* scnstate) { mjvSceneState* scnstate) {
// Check that mjModel sizes haven't changed. // Check that mjModel sizes haven't changed.
#define X(var) #define X(var)
#define XMJV(var) \ #define XMJV(var) \
if (scnstate->model.var != m->var) { \ if (scnstate->model.var != m->var) { \
mjERROR("m->%s changed", #var); \ mjERROR("m->%s changed: %d vs %d", #var, scnstate->model.var, m->var); \
} }
MJMODEL_INTS MJMODEL_INTS
#undef XMJV #undef XMJV
+13 -4
View File
@@ -36,6 +36,11 @@
// Online: // Online:
// https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D1.5&extensions=GL_ARB_framebuffer_object&extensions=GL_ARB_seamless_cube_map&extensions=GL_ARB_vertex_buffer_object&extensions=GL_KHR_debug // https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D1.5&extensions=GL_ARB_framebuffer_object&extensions=GL_ARB_seamless_cube_map&extensions=GL_ARB_vertex_buffer_object&extensions=GL_KHR_debug
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
#if !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__APPLE__) && \ #if !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__APPLE__) && \
!defined(__HAIKU__) && !defined(_GNU_SOURCE) !defined(__HAIKU__) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE #define _GNU_SOURCE
@@ -1471,12 +1476,12 @@ static void mjGlad_find_coreGL(void) {
} }
} }
int mjGladLoadGLUnsafe() { int mjGladLoadGLUnsafe(void) {
if(mjGlad_open_gl()) { if (mjGlad_open_gl()) {
mjGLVersion.major = 0; mjGLVersion.minor = 0; mjGLVersion.major = 0; mjGLVersion.minor = 0;
glGetString = (PFNGLGETSTRINGPROC)mjGlad_get_proc("glGetString"); glGetString = (PFNGLGETSTRINGPROC)mjGlad_get_proc("glGetString");
if(glGetString == NULL) return 0; if (glGetString == NULL) return 0;
if(glGetString(GL_VERSION) == NULL) return 0; if (glGetString(GL_VERSION) == NULL) return 0;
mjGlad_find_coreGL(); mjGlad_find_coreGL();
mjGlad_load_GL_VERSION_1_0(mjGlad_get_proc); mjGlad_load_GL_VERSION_1_0(mjGlad_get_proc);
mjGlad_load_GL_VERSION_1_1(mjGlad_get_proc); mjGlad_load_GL_VERSION_1_1(mjGlad_get_proc);
@@ -1496,3 +1501,7 @@ int mjGladLoadGLUnsafe() {
return 0; return 0;
} }
} }
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
+2 -2
View File
@@ -140,7 +140,7 @@ static void makePlane(const mjModel* m, mjrContext* con) {
// record // record
grid[k][x] = left; grid[k][x] = left;
grid[k][x+1] = mjMAX(left, right); // just in case grid[k][x+1] = mjMAX(left, right); // just in case
} }
} }
@@ -1458,7 +1458,7 @@ void GLAPIENTRY debugCallback(GLenum source,
// returns 1 if MUJOCO_GL_DEBUG environment variable is set to 1 // returns 1 if MUJOCO_GL_DEBUG environment variable is set to 1
static int glDebugEnabled() { static int glDebugEnabled(void) {
char* debug = getenv("MUJOCO_GL_DEBUG"); char* debug = getenv("MUJOCO_GL_DEBUG");
return debug && strcmp(debug, "1") == 0; return debug && strcmp(debug, "1") == 0;
} }
+23
View File
@@ -0,0 +1,23 @@
# 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
#
# https://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.
set(MUJOCO_THREAD_SRCS
lockless_queue.h
task.cc
task.h
thread_pool.cc
thread_pool.h
)
target_sources(mujoco PRIVATE ${MUJOCO_THREAD_SRCS})
+154
View File
@@ -0,0 +1,154 @@
// 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.
// IWYU pragma: private, include "third_party/mujoco/include/mujoco.h"
// IWYU pragma: friend "third_party/(py/)?mujoco/.*"
#ifndef MUJOCO_SRC_THREAD_LOCKLESS_QUEUE_H_
#define MUJOCO_SRC_THREAD_LOCKLESS_QUEUE_H_
#include <atomic>
#include <climits>
#include <cstddef>
#include <thread>
namespace mujoco {
// A Lockless Queue allows for sending information quickly between different
// threads. This is a Multi-Producer Multi-Consumer Lockless Queue allowing for
// multiple threads to be adding items to the queue while multiple threads are
// consuming items from the queue. Internally it uses a Ring Buffer for storage
// so it will not grow as items are added. Push will block if the Queue is full
// and Pop will block if it is empty.
//
// For a basic overview of this category of structures:
// https://www.linuxjournal.com/content/lock-free-multi-producer-multi-consumer-queue-ring-buffer
template <typename T, size_t buffer_capacity>
class LocklessQueue {
public:
bool full() const {
return full_internal(
convert_to_index(read_cursor_), convert_to_index(write_cursor_));
}
bool empty() const {
return maximum_read_cursor_ == read_cursor_;
}
// Push an element into the queue.
void push(const T& input) {
// Reserve a slot in the queue
size_t current_write_cursor;
size_t dummy_current_write_cursor;
size_t next_write_cursor;
size_t current_write_index;
size_t current_read_index;
do {
// Check if the queue is full.
do {
current_write_cursor = write_cursor_.load();
current_write_index = convert_to_index(current_write_cursor);
next_write_cursor = get_next_cursor(current_write_cursor);
current_read_index = convert_to_index(read_cursor_.load());
} while (full_internal(current_read_index, current_write_index));
// Once it's not full, attempt to grab a slot to write.
dummy_current_write_cursor = current_write_cursor;
} while (!write_cursor_.compare_exchange_weak(
dummy_current_write_cursor, next_write_cursor));
// Write the entry.
buffer_[current_write_index].store(input);
// Increment maximum read cursor. Note here it has to wait if the compare
// and exchange fails as another thread might not have completed its write.
do {
dummy_current_write_cursor = current_write_cursor;
} while (!maximum_read_cursor_.compare_exchange_weak(
dummy_current_write_cursor, next_write_cursor));
}
// Pop an element from the queue.
T pop() {
size_t current_read_cursor;
size_t dummy_current_read_cursor;
size_t current_read_index;
size_t next_read_cursor;
size_t current_maximum_read_cursor;
size_t current_maximum_read_index;
bool empty = false;
T result;
do {
// Wait until the queue has an element
do {
if (empty) {
std::this_thread::yield();
}
current_read_cursor = read_cursor_.load();
current_maximum_read_cursor = maximum_read_cursor_.load();
current_read_index = convert_to_index(current_read_cursor);
current_maximum_read_index = convert_to_index(
current_maximum_read_cursor);
empty = empty_internal(
current_read_index, current_maximum_read_index);
} while (empty);
next_read_cursor = get_next_cursor(current_read_cursor);
// Attempt to grab the element, if unsuccessful then wait for the next
// element to arrive.
result = buffer_[current_read_index].load();
dummy_current_read_cursor = current_read_cursor;
} while (!read_cursor_.compare_exchange_weak(
dummy_current_read_cursor, next_read_cursor));
return result;
}
private:
size_t convert_to_index(size_t input) const {
return input % internal_buffer_capacity_;
}
size_t get_next_cursor(size_t input) const {
return (input + 1) % cursor_max_;
}
size_t get_next_index(size_t input) const {
return convert_to_index(get_next_cursor(input));
}
bool full_internal(size_t read_index, size_t write_index) const {
return get_next_index(write_index) == read_index;
}
bool empty_internal(size_t read_index, size_t write_index) const {
return read_index == write_index;
}
const size_t internal_buffer_capacity_ = buffer_capacity + 1;
const size_t cursor_max_ = UINT_MAX - (UINT_MAX % internal_buffer_capacity_);
std::atomic<size_t> read_cursor_ = 0;
std::atomic<size_t> write_cursor_ = 0;
std::atomic<size_t> maximum_read_cursor_ = 0;
std::atomic<T> buffer_[(buffer_capacity + 1)];
};
} // namespace mujoco
#endif // MUJOCO_SRC_THREAD_LOCKLESS_QUEUE_H_
+24
View File
@@ -0,0 +1,24 @@
// 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 "thread/task.h"
#include <mujoco/mjthread.h>
#include <mujoco/mujoco.h>
// waits for a task to complete
void mju_taskJoin(mjTask* task) {
mujoco::Task* task_ptr = static_cast<mujoco::Task*>(static_cast<void*>(task));
task_ptr->Join();
}
+70
View File
@@ -0,0 +1,70 @@
// 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.
// IWYU pragma: private, include "third_party/mujoco/include/mujoco.h"
// IWYU pragma: friend "third_party/(py/)?mujoco/.*"
#ifndef MUJOCO_SRC_THREAD_TASK_H_
#define MUJOCO_SRC_THREAD_TASK_H_
#ifdef __cplusplus
#include <atomic>
#include <new>
#include <thread>
namespace mujoco {
class Task {
public:
using FunctionPtr = void* (*)(void*);
enum Status {
QUEUED,
COMPLETE,
};
static void Initialize(
Task* task,
FunctionPtr start_routine,
void* args) {
// instantiate a task at the pointer passed in
new(task) Task();
task->start_routine_ = start_routine;
task->args_ = args;
task->status_ = Status::QUEUED;
}
void Execute() {
args_ = start_routine_(args_);
status_ = Status::COMPLETE;
}
void Join() {
while (status_ != Status::COMPLETE) {
std::this_thread::yield();
}
}
private:
FunctionPtr start_routine_;
void* args_;
std::atomic<Status> status_ = Status::QUEUED;
};
} // namespace mujoco
#endif // __cplusplus
#endif // MUJOCO_SRC_THREAD_TASK_H_
+52
View File
@@ -0,0 +1,52 @@
// 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 "thread/thread_pool.h"
#include <cstddef>
#include <mujoco/mjthread.h>
#include <mujoco/mujoco.h>
#include "thread/task.h"
static constexpr size_t kMaxThreads = 128;
// create a thread pool
mjThreadPool* mju_threadPoolCreate(size_t number_of_threads) {
mujoco::ThreadPool<kMaxThreads>* thread_pool =
new mujoco::ThreadPool<kMaxThreads>(number_of_threads);
return static_cast<mjThreadPool*>(static_cast<void*>(thread_pool));
}
// start a task in the threadpool
void mju_threadPoolEnqueue(
mjThreadPool* thread_pool, mjTask* task, mjStartRoutine start_routine,
void* args) {
mujoco::ThreadPool<kMaxThreads>* thread_pool_ptr =
static_cast<mujoco::ThreadPool<kMaxThreads>*>(
static_cast<void*>(thread_pool));
thread_pool_ptr->Enqueue(
static_cast<mujoco::Task*>(static_cast<void*>(task)), start_routine,
args);
}
// shutdown the threadpool and free the memory
void mju_threadPoolDestroy(mjThreadPool* thread_pool) {
mujoco::ThreadPool<kMaxThreads>* thread_pool_ptr =
static_cast<mujoco::ThreadPool<kMaxThreads>*>(
static_cast<void*>(thread_pool));
thread_pool_ptr->Shutdown();
delete thread_pool_ptr;
}
+102
View File
@@ -0,0 +1,102 @@
// 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.
// IWYU pragma: private, include "third_party/mujoco/include/mujoco.h"
// IWYU pragma: friend "third_party/(py/)?mujoco/.*"
#ifndef MUJOCO_SRC_THREAD_THREAD_POOL_H_
#define MUJOCO_SRC_THREAD_THREAD_POOL_H_
#ifdef __cplusplus
#include <atomic>
#include <cstddef>
#include <thread>
#include "thread/lockless_queue.h"
#include "thread/task.h"
namespace mujoco {
static constexpr size_t kThreadPoolQueueSize = 640;
template <size_t max_number_of_threads>
class ThreadPool {
public:
ThreadPool(size_t number_of_threads)
: number_of_threads_(number_of_threads) {
for (int i = 0; i < number_of_threads_; ++i) {
threads_[i] = std::thread(ThreadPoolWorker, static_cast<void*>(this));
}
}
// start a task in the threadpool
void Enqueue(
Task* task, Task::FunctionPtr start_routine, void* args) {
Task::Initialize(task, start_routine, args);
lockless_queue_.push(static_cast<void*>(task));
}
// shutdown the threadpool
void Shutdown() {
if (shutdown_) {
return;
}
shutdown_ = true;
Task shutdown_tasks[max_number_of_threads];
for (int i = 0; i < number_of_threads_; ++i) {
Enqueue(&shutdown_tasks[i], ShutdownFunction, nullptr);
}
for (int i = 0; i < number_of_threads_; ++i) {
threads_[i].join();
}
}
~ThreadPool() { Shutdown(); }
private:
// method executed by running threads
static void ThreadPoolWorker(void* arg) {
ThreadPool<max_number_of_threads>* thread_pool =
static_cast<ThreadPool<max_number_of_threads>*>(arg);
while (!thread_pool->shutdown_) {
Task* task = static_cast<Task*>(thread_pool->lockless_queue_.pop());
task->Execute();
}
}
// shutdown function passed to running threads to ensure cleans shutdown
static void* ShutdownFunction(void* args) {
return NULL;
}
// is the thread pool is being shut down
std::atomic<bool> shutdown_ = false;
// actual number of running threads in the threadpool
const size_t number_of_threads_;
// OS threads that are running in this pool
std::thread threads_[max_number_of_threads];
// queue of tasks to execute
LocklessQueue<void*, kThreadPoolQueueSize> lockless_queue_;
};
} // namespace mujoco
#endif // __cplusplus
#endif // MUJOCO_SRC_THREAD_THREAD_POOL_H_
+204 -53
View File
@@ -34,6 +34,7 @@
#include "user/user_model.h" #include "user/user_model.h"
#include "user/user_objects.h" #include "user/user_objects.h"
#include "user/user_util.h" #include "user/user_util.h"
#include "xml/xml_util.h"
namespace { namespace {
namespace mju = ::mujoco::util; namespace mju = ::mujoco::util;
@@ -235,7 +236,7 @@ bool mjCComposite::Make(mjCModel* model, mjCBody* body, char* error, int error_s
} }
// check spacing // check spacing
if (type==mjCOMPTYPE_GRID || type==mjCOMPTYPE_PARTICLE) { if (type==mjCOMPTYPE_GRID || (type==mjCOMPTYPE_PARTICLE && uservert.empty())) {
if (spacing < mju_max(def[0].geom.size[0], if (spacing < mju_max(def[0].geom.size[0],
mju_max(def[0].geom.size[1], def[0].geom.size[2]))) { mju_max(def[0].geom.size[1], def[0].geom.size[2]))) {
return comperr(error, "Spacing must be larger than geometry size", return comperr(error, "Spacing must be larger than geometry size",
@@ -314,66 +315,178 @@ bool mjCComposite::Make(mjCModel* model, mjCBody* body, char* error, int error_s
// make particles
bool mjCComposite::MakeParticle(mjCModel* model, mjCBody* body, char* error, int error_sz) { bool mjCComposite::MakeParticle(mjCModel* model, mjCBody* body, char* error, int error_sz) {
// create bodies and geoms char txt[100];
for (int ix=0; ix<count[0]; ix++) { std::vector<int> face;
for (int iy=0; iy<count[1]; iy++) {
for (int iz=0; iz<count[2]; iz++) {
// create body
char txt[100];
mjCBody* b = body->AddBody(NULL);
mju::sprintf_arr(txt, "%sB%d_%d_%d", prefix.c_str(), ix, iy, iz);
b->name = txt;
// set body position // populate vertices and names
b->pos[0] = offset[0] + spacing*(ix - 0.5*count[0]); if (uservert.empty()) {
b->pos[1] = offset[1] + spacing*(iy - 0.5*count[1]); if (spacing < mju_max(def[0].geom.size[0],
b->pos[2] = offset[2] + spacing*(iz - 0.5*count[2]); mju_max(def[0].geom.size[1], def[0].geom.size[2])))
return comperr(error, "Spacing must be larger than geometry size", error_sz);
// add slider joints if none defined for (int ix=0; ix<count[0]; ix++) {
if (!add[mjCOMPKIND_PARTICLE]) { for (int iy=0; iy<count[1]; iy++) {
for (int i=0; i<3; i++) { for (int iz=0; iz<count[2]; iz++) {
mjCJoint* jnt = b->AddJoint(&defjoint[mjCOMPKIND_JOINT][0], false); uservert.push_back(spacing*(ix - 0.5*count[0]));
jnt->def = body->def; uservert.push_back(spacing*(iy - 0.5*count[1]));
jnt->type = mjJNT_SLIDE; uservert.push_back(spacing*(iz - 0.5*count[2]));
mjuu_setvec(jnt->pos, 0, 0, 0);
mjuu_setvec(jnt->axis, 0, 0, 0);
jnt->axis[i] = 1;
}
}
// add user-specified joints mju::sprintf_arr(txt, "%sB%d_%d_%d", prefix.c_str(), ix, iy, iz);
else { username.push_back(std::string(txt));
for (auto defjnt : defjoint[mjCOMPKIND_PARTICLE]) {
mjCJoint* jnt = b->AddJoint(&defjnt, false);
jnt->def = body->def;
}
}
// add geom
mjCGeom* g = b->AddGeom(def);
g->def = body->def;
// add plugin
if (plugin_instance) {
b->is_plugin = true;
b->plugin_name = plugin_name;
b->plugin_instance = plugin_instance;
b->plugin_instance_name = plugin_instance_name;
// propagate attributes
if (plugin_name == "mujoco.elasticity.solid") {
b->plugin_instance->config_attribs["nx"] = std::to_string(count[0]);
b->plugin_instance->config_attribs["ny"] = std::to_string(count[1]);
b->plugin_instance->config_attribs["nz"] = std::to_string(count[2]);
}
} }
} }
} }
} }
// skin // create faces
if (userface.empty()) {
if (dim == 3) {
int cube2tets[6][4] = {{0, 3, 1, 7}, {0, 1, 4, 7},
{1, 3, 2, 7}, {1, 2, 6, 7},
{1, 5, 4, 7}, {1, 6, 5, 7}};
for (int ix = 0; ix < count[0]-1; ix++) {
for (int iy = 0; iy < count[1]-1; iy++) {
for (int iz = 0; iz < count[2]-1; iz++) {
int vert[8] = {
count[2]*count[1]*(ix+0) + count[2]*(iy+0) + iz+0,
count[2]*count[1]*(ix+1) + count[2]*(iy+0) + iz+0,
count[2]*count[1]*(ix+1) + count[2]*(iy+1) + iz+0,
count[2]*count[1]*(ix+0) + count[2]*(iy+1) + iz+0,
count[2]*count[1]*(ix+0) + count[2]*(iy+0) + iz+1,
count[2]*count[1]*(ix+1) + count[2]*(iy+0) + iz+1,
count[2]*count[1]*(ix+1) + count[2]*(iy+1) + iz+1,
count[2]*count[1]*(ix+0) + count[2]*(iy+1) + iz+1,
};
for (int s = 0; s < 6; s++) {
for (int v = 0; v < 4; v++) {
face.push_back(vert[cube2tets[s][v]]+1);
}
}
}
}
}
} else if (dim == 2) {
int quad2tri[2][3] = {{0, 1, 2}, {0, 2, 3}};
for (int ix = 0; ix < count[0]-1; ix++) {
for (int iy = 0; iy < count[1]-1; iy++) {
int vert[4] = {
count[2]*count[1]*(ix+0) + count[2]*(iy+0),
count[2]*count[1]*(ix+1) + count[2]*(iy+0),
count[2]*count[1]*(ix+1) + count[2]*(iy+1),
count[2]*count[1]*(ix+0) + count[2]*(iy+1),
};
for (int s = 0; s < 2; s++) {
for (int v = 0; v < 3; v++) {
face.push_back(vert[quad2tri[s][v]]+1);
}
}
}
}
}
mjXUtil::Vector2String(userface, face);
} else {
dim = 2; // can only load a surface for now
mjXUtil::String2Vector(userface, face);
}
// compute volume
std::vector<mjtNum> volume(uservert.size()/3);
mjtNum t = 1;
if (dim == 2 && plugin_instance) {
// do nothing for now (until new passive forces are supported)
}
if (!userface.empty()) {
mjXUtil::String2Vector(userface, face);
for (int j=0; j<face.size()/3; j++) {
mjtNum area[3];
mjtNum edge1[3];
mjtNum edge2[3];
for (int i=0; i<3; i++) {
edge1[i] = uservert[3*(face[3*j+1]-1)+i] - uservert[3*(face[3*j]-1)+i];
edge2[i] = uservert[3*(face[3*j+2]-1)+i] - uservert[3*(face[3*j]-1)+i];
}
mjuu_crossvec(area, edge1, edge2);
for (int i=0; i<3; i++) {
volume[face[3*j+i]-1] += sqrt(mjuu_dot3(area, area)) / 2 * t;
}
}
} else {
for (int i=0; i<uservert.size()/3; i++) {
volume[i] = 6 * spacing * spacing / 2 * t;
}
}
// create bodies and geoms
for (int i=0; i<uservert.size()/3; i++) {
// create body
mjCBody* b = body->AddBody(NULL);
if (!username.empty()) {
b->name = username[i];
} else {
mju::sprintf_arr(txt, "%sB%d", prefix.c_str(), i);
b->name = txt;
}
// set body position
b->pos[0] = offset[0] + uservert[3*i];
b->pos[1] = offset[1] + uservert[3*i+1];
b->pos[2] = offset[2] + uservert[3*i+2];
// add slider joints if none defined
if (!add[mjCOMPKIND_PARTICLE]) {
for (int i=0; i<3; i++) {
mjCJoint* jnt = b->AddJoint(&defjoint[mjCOMPKIND_JOINT][0], false);
jnt->def = body->def;
jnt->type = mjJNT_SLIDE;
mjuu_setvec(jnt->pos, 0, 0, 0);
mjuu_setvec(jnt->axis, 0, 0, 0);
jnt->axis[i] = 1;
}
}
// add user-specified joints
else {
for (auto defjnt : defjoint[mjCOMPKIND_PARTICLE]) {
mjCJoint* jnt = b->AddJoint(&defjnt, false);
jnt->def = body->def;
}
}
// add geom
mjCGeom* g = b->AddGeom(def);
g->def = body->def;
// add site
mjCSite* s = b->AddSite(def);
s->def = body->def;
s->type = mjGEOM_SPHERE;
mju::sprintf_arr(txt, "%sS%d", prefix.c_str(), i);
s->name = txt;
// add plugin
if (plugin_instance) {
b->is_plugin = true;
b->plugin_name = plugin_name;
b->plugin_instance = plugin_instance;
b->plugin_instance_name = plugin_instance_name;
if (i==0 && !plugin_instance->config_attribs["face"].empty()) {
return comperr(error, "Face attribute already exists in plugin", error_sz);
}
b->plugin_instance->config_attribs["face"] = userface;
// update density
if (dim == 2) {
g->density *= volume[i] / (4./3. * mjPI * pow(g->size[0], 3));
}
}
}
if (skin) { if (skin) {
MakeSkin3(model); MakeSkin3(model);
} }
@@ -2040,8 +2153,46 @@ void mjCComposite::MakeSkin3(mjCModel* model) {
skin->inflate = skininflate; skin->inflate = skininflate;
skin->group = skingroup; skin->group = skingroup;
// copy skin from existing mesh
if (type==mjCOMPTYPE_PARTICLE && username.empty()) {
std::vector<int> face;
mjXUtil::String2Vector(userface, face);
int nvert = uservert.size()/3;
for (int j=0; j<2; j++) {
for (int i=0; i<nvert; i++) {
skin->vert.push_back(0);
skin->vert.push_back(0);
skin->vert.push_back(0);
mju::sprintf_arr(txt, "%sB%d", prefix.c_str(), i);
skin->bodyname.push_back(txt);
skin->bindpos.push_back(0);
skin->bindpos.push_back(0);
skin->bindpos.push_back(0);
skin->bindquat.push_back(1);
skin->bindquat.push_back(0);
skin->bindquat.push_back(0);
skin->bindquat.push_back(0);
vector<int> vertid;
vector<float> vertweight;
vertid.push_back(j*nvert+i);
vertweight.push_back(1);
skin->vertid.push_back(vertid);
skin->vertweight.push_back(vertweight);
}
for (int i=0; i<face.size()/3; i++) {
skin->face.push_back(j*nvert+face[3*i]-1);
skin->face.push_back(j*nvert+face[3*i+(j==0 ? 1 : 2)]-1);
skin->face.push_back(j*nvert+face[3*i+(j==0 ? 2 : 1)]-1);
}
}
}
// box // box
if (type==mjCOMPTYPE_BOX || type==mjCOMPTYPE_PARTICLE) { else if (type==mjCOMPTYPE_BOX || type==mjCOMPTYPE_PARTICLE) {
// z-faces // z-faces
MakeSkin3Box(skin, count[0], count[1], 1, vcnt, "%sB%d_%d_0"); MakeSkin3Box(skin, count[0], count[1], 1, vcnt, "%sB%d_%d_0");
fmt = "%sB%d_%d_" + string(cnt2); fmt = "%sB%d_%d_" + string(cnt2);
+4
View File
@@ -106,9 +106,13 @@ class mjCComposite {
// currently used only for cable // currently used only for cable
std::string initial; // root boundary type std::string initial; // root boundary type
std::vector<float> uservert; // user-specified vertex positions std::vector<float> uservert; // user-specified vertex positions
std::string userface; // connectivity
mjtNum size[3]; // rope size (meaning depends on the shape) mjtNum size[3]; // rope size (meaning depends on the shape)
mjtCompShape curve[3]; // geometric shape mjtCompShape curve[3]; // geometric shape
// body names used in the skin
std::vector<std::string> username;
// plugin support // plugin support
bool is_plugin; bool is_plugin;
std::string plugin_name; std::string plugin_name;
+24 -6
View File
@@ -13,12 +13,14 @@
// limitations under the License. // limitations under the License.
#include <algorithm> #include <algorithm>
#include <array>
#include <cmath> #include <cmath>
#include <csetjmp> #include <csetjmp>
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <memory> #include <memory>
#include <optional>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -27,10 +29,27 @@
#define TINYOBJLOADER_IMPLEMENTATION #define TINYOBJLOADER_IMPLEMENTATION
#endif #endif
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
#pragma clang diagnostic ignored "-Wnested-anon-types"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
#include <MC.h> #include <MC.h>
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
#include <mujoco/mjmacro.h>
#include <mujoco/mjmodel.h> #include <mujoco/mjmodel.h>
#include "cc/array_safety.h" #include <mujoco/mjtnum.h>
#include <mujoco/mjplugin.h>
#include "engine/engine_crossplatform.h" #include "engine/engine_crossplatform.h"
#include "engine/engine_plugin.h"
#include "engine/engine_resource.h" #include "engine/engine_resource.h"
#include "engine/engine_util_blas.h" #include "engine/engine_util_blas.h"
#include "engine/engine_util_errmem.h" #include "engine/engine_util_errmem.h"
@@ -329,7 +348,7 @@ void mjCMesh::LoadSDF() {
// compiler // compiler
void mjCMesh::Compile(int vfs_provider) { void mjCMesh::Compile(const mjVFS* vfs) {
// load file // load file
if (!file_.empty()) { if (!file_.empty()) {
// remove path from file if necessary // remove path from file if necessary
@@ -348,7 +367,7 @@ void mjCMesh::Compile(int vfs_provider) {
} }
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file_); string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file_);
mjResource* resource = LoadResource(filename, vfs_provider); mjResource* resource = LoadResource(filename, vfs);
try { try {
if (asset_type == "model/stl") { if (asset_type == "model/stl") {
@@ -1836,8 +1855,7 @@ mjCSkin::~mjCSkin() {
// compiler // compiler
void mjCSkin::Compile(int vfs_provider) { void mjCSkin::Compile(const mjVFS* vfs) {
// load file // load file
if (!file.empty()) { if (!file.empty()) {
// make sure data is not present // make sure data is not present
@@ -1865,7 +1883,7 @@ void mjCSkin::Compile(int vfs_provider) {
} }
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file); string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file);
mjResource* resource = LoadResource(filename, vfs_provider); mjResource* resource = LoadResource(filename, vfs);
try { try {
LoadSKN(resource); LoadSKN(resource);
+7 -7
View File
@@ -2423,7 +2423,7 @@ static void warninghandler(const char* msg) {
// compiler // compiler
mjModel* mjCModel::Compile(int vfs_provider) { mjModel* mjCModel::Compile(const mjVFS* vfs) {
// The volatile keyword is necessary to prevent a possible memory leak due to // The volatile keyword is necessary to prevent a possible memory leak due to
// an interaction between longjmp and compiler optimization. Specifically, at // an interaction between longjmp and compiler optimization. Specifically, at
// the point where the setjmp takes places, these pointers have never been // the point where the setjmp takes places, these pointers have never been
@@ -2454,7 +2454,7 @@ mjModel* mjCModel::Compile(int vfs_provider) {
// TryCompile resulted in an mju_error which was converted to a longjmp. // TryCompile resulted in an mju_error which was converted to a longjmp.
throw mjCError(0, "engine error: %s", errortext); throw mjCError(0, "engine error: %s", errortext);
} }
TryCompile(*const_cast<mjModel**>(&m), *const_cast<mjData**>(&data), vfs_provider); TryCompile(*const_cast<mjModel**>(&m), *const_cast<mjData**>(&data), vfs);
} catch (mjCError err) { } catch (mjCError err) {
// deallocate everything allocated in Compile // deallocate everything allocated in Compile
mj_deleteModel(m); mj_deleteModel(m);
@@ -2480,7 +2480,7 @@ mjModel* mjCModel::Compile(int vfs_provider) {
} }
void mjCModel::TryCompile(mjModel*& m, mjData*& d, int vfs_provider) { void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
// check if nan test works // check if nan test works
double test = mjNAN; double test = mjNAN;
if (mjuu_defined(test)) { if (mjuu_defined(test)) {
@@ -2557,7 +2557,7 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, int vfs_provider) {
// compile meshes (needed for geom compilation) // compile meshes (needed for geom compilation)
for (int i=0; i<meshes.size(); i++) { for (int i=0; i<meshes.size(); i++) {
meshes[i]->Compile(vfs_provider); meshes[i]->Compile(vfs);
} }
// automatically set nuser fields // automatically set nuser fields
@@ -2616,9 +2616,9 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, int vfs_provider) {
} }
// compile all other objects except for keyframes // compile all other objects except for keyframes
for (int i=0; i<skins.size(); i++) skins[i]->Compile(vfs_provider); for (int i=0; i<skins.size(); i++) skins[i]->Compile(vfs);
for (int i=0; i<hfields.size(); i++) hfields[i]->Compile(vfs_provider); for (int i=0; i<hfields.size(); i++) hfields[i]->Compile(vfs);
for (int i=0; i<textures.size(); i++) textures[i]->Compile(vfs_provider); for (int i=0; i<textures.size(); i++) textures[i]->Compile(vfs);
for (int i=0; i<materials.size(); i++) materials[i]->Compile(); for (int i=0; i<materials.size(); i++) materials[i]->Compile();
for (int i=0; i<pairs.size(); i++) pairs[i]->Compile(); for (int i=0; i<pairs.size(); i++) pairs[i]->Compile();
for (int i=0; i<excludes.size(); i++) excludes[i]->Compile(); for (int i=0; i<excludes.size(); i++) excludes[i]->Compile();
+3 -3
View File
@@ -65,7 +65,7 @@ class mjCModel {
mjCModel(); // constructor mjCModel(); // constructor
~mjCModel(); // destructor ~mjCModel(); // destructor
mjModel* Compile(int vfs_provider = 0); // COMPILER: construct mjModel mjModel* Compile(const mjVFS* vfs = 0); // COMPILER: construct mjModel
bool CopyBack(const mjModel*); // DECOMPILER: copy numeric back bool CopyBack(const mjModel*); // DECOMPILER: copy numeric back
void FuseStatic(void); // fuse static bodies with parent void FuseStatic(void); // fuse static bodies with parent
void FuseReindex(mjCBody* body); // reindex elements during fuse void FuseReindex(mjCBody* body); // reindex elements during fuse
@@ -163,8 +163,8 @@ class mjCModel {
int nuser_sensor; // number of mjtNums in sensor_user int nuser_sensor; // number of mjtNums in sensor_user
private: private:
void TryCompile(mjModel*& m, mjData*& d, int vfs_provider); void TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs);
mjModel* _Compile(int vfs_provider); mjModel* _Compile(const mjVFS* vfs);
void Clear(void); // clear objects allocated by Compile void Clear(void); // clear objects allocated by Compile
+24 -29
View File
@@ -14,22 +14,22 @@
#include "user/user_objects.h" #include "user/user_objects.h"
#include <algorithm>
#include <cmath> #include <cmath>
#include <cstddef> #include <cstddef>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <iostream>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <string_view>
#include <vector> #include <vector>
#include "lodepng.h" #include "lodepng.h"
#include <mujoco/mjmacro.h> #include <mujoco/mjmacro.h>
#include <mujoco/mjmodel.h> #include <mujoco/mjmodel.h>
#include <mujoco/mjplugin.h> #include <mujoco/mjplugin.h>
#include <mujoco/mjtnum.h>
#include "cc/array_safety.h" #include "cc/array_safety.h"
#include "engine/engine_core_smooth.h"
#include "engine/engine_crossplatform.h"
#include "engine/engine_resource.h" #include "engine/engine_resource.h"
#include "engine/engine_io.h" #include "engine/engine_io.h"
#include "engine/engine_passive.h" #include "engine/engine_passive.h"
@@ -39,6 +39,7 @@
#include "engine/engine_util_misc.h" #include "engine/engine_util_misc.h"
#include "engine/engine_util_solve.h" #include "engine/engine_util_solve.h"
#include "engine/engine_util_spatial.h" #include "engine/engine_util_spatial.h"
#include "engine/engine_vfs.h"
#include "user/user_model.h" #include "user/user_model.h"
#include "user/user_util.h" #include "user/user_util.h"
@@ -130,7 +131,7 @@ mjCError::mjCError(const mjCBase* obj, const char* msg, const char* str, int pos
// constructor // constructor
mjCAlternative::mjCAlternative() { mjCAlternative::mjCAlternative() {
axisangle[0] = xyaxes[0] = zaxis[0] = euler[0] = fullinertia[0] = mjNAN; axisangle[0] = xyaxes[0] = zaxis[0] = euler[0] = fullinertia[0] = mjNAN;
}; }
// compute frame orientation given alternative specifications // compute frame orientation given alternative specifications
@@ -495,17 +496,14 @@ mjCBase::mjCBase() {
// load resource if found (fallback to OS filesystem) // load resource if found (fallback to OS filesystem)
mjResource* mjCBase::LoadResource(string filename, int provider) { mjResource* mjCBase::LoadResource(string filename, const mjVFS* vfs) {
mjResource* r = nullptr; mjResource* r = nullptr;
const char* cname = filename.c_str(); const char* cname = filename.c_str();
// try reading from given provider // try reading from provided VFS
if ((r = mju_openResource(cname, provider)) == nullptr) { if ((r = mju_openVfsResource(cname, vfs)) == nullptr) {
if (!provider) { // not in vfs try a provider or fallback to OS filesystem
throw mjCError(0, "file not found: '%s'", cname); if ((r = mju_openResource(filename.c_str())) == nullptr) {
}
// if provider wasn't the OS filesystem try to fallback to OS filesystem
if ((r = mju_openResource(filename.c_str(), 0)) == nullptr) {
throw mjCError(this, "resource not found via provider or OS filesystem: '%s'", cname); throw mjCError(this, "resource not found via provider or OS filesystem: '%s'", cname);
} }
} }
@@ -1516,7 +1514,6 @@ void mjCGeom::SetFluidCoefs(void) {
// get semiaxes // get semiaxes
switch (type) { switch (type) {
case mjGEOM_SPHERE: case mjGEOM_SPHERE:
dx = size[0]; dx = size[0];
dy = size[0]; dy = size[0];
@@ -2194,7 +2191,7 @@ void mjCHField::LoadPNG(mjResource* resource) {
// compiler // compiler
void mjCHField::Compile(int vfs_provider) { void mjCHField::Compile(const mjVFS* vfs) {
// check size parameters // check size parameters
for (int i=0; i<4; i++) for (int i=0; i<4; i++)
if (size[i]<=0) if (size[i]<=0)
@@ -2226,7 +2223,7 @@ void mjCHField::Compile(int vfs_provider) {
} }
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file); string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file);
mjResource* resource = LoadResource(filename, vfs_provider); mjResource* resource = LoadResource(filename, vfs);
try { try {
if (asset_type == "image/png") { if (asset_type == "image/png") {
@@ -2617,7 +2614,7 @@ void mjCTexture::LoadCustom(mjResource* resource,
// load from PNG or custom file, flip if specified // load from PNG or custom file, flip if specified
void mjCTexture::LoadFlip(string filename, int vfs_provider, void mjCTexture::LoadFlip(string filename, const mjVFS* vfs,
std::vector<unsigned char>& image, std::vector<unsigned char>& image,
unsigned int& w, unsigned int& h) { unsigned int& w, unsigned int& h) {
std::string asset_type = GetAssetContentType(filename, content_type); std::string asset_type = GetAssetContentType(filename, content_type);
@@ -2631,7 +2628,7 @@ void mjCTexture::LoadFlip(string filename, int vfs_provider,
throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str()); throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str());
} }
mjResource* resource = LoadResource(filename, vfs_provider); mjResource* resource = LoadResource(filename, vfs);
try { try {
if (asset_type == "image/png") { if (asset_type == "image/png") {
@@ -2693,11 +2690,11 @@ void mjCTexture::LoadFlip(string filename, int vfs_provider,
// load 2D // load 2D
void mjCTexture::Load2D(string filename, int vfs_provider) { void mjCTexture::Load2D(string filename, const mjVFS* vfs) {
// load PNG or custom // load PNG or custom
unsigned int w, h; unsigned int w, h;
std::vector<unsigned char> image; std::vector<unsigned char> image;
LoadFlip(filename, vfs_provider, image, w, h); LoadFlip(filename, vfs, image, w, h);
// assign size // assign size
width = w; width = w;
@@ -2716,7 +2713,7 @@ void mjCTexture::Load2D(string filename, int vfs_provider) {
// load cube or skybox from single file (repeated or grid) // load cube or skybox from single file (repeated or grid)
void mjCTexture::LoadCubeSingle(string filename, int vfs_provider) { void mjCTexture::LoadCubeSingle(string filename, const mjVFS* vfs) {
// check gridsize // check gridsize
if (gridsize[0]<1 || gridsize[1]<1 || gridsize[0]*gridsize[1]>12) { if (gridsize[0]<1 || gridsize[1]<1 || gridsize[0]*gridsize[1]>12) {
throw mjCError(this, throw mjCError(this,
@@ -2727,7 +2724,7 @@ void mjCTexture::LoadCubeSingle(string filename, int vfs_provider) {
// load PNG or custom // load PNG or custom
unsigned int w, h; unsigned int w, h;
std::vector<unsigned char> image; std::vector<unsigned char> image;
LoadFlip(filename, vfs_provider, image, w, h); LoadFlip(filename, vfs, image, w, h);
// check gridsize for compatibility // check gridsize for compatibility
if (w/gridsize[1]!=h/gridsize[0] || (w%gridsize[1]) || (h%gridsize[0])) { if (w/gridsize[1]!=h/gridsize[0] || (w%gridsize[1]) || (h%gridsize[0])) {
@@ -2816,7 +2813,7 @@ void mjCTexture::LoadCubeSingle(string filename, int vfs_provider) {
// load cube or skybox from separate file // load cube or skybox from separate file
void mjCTexture::LoadCubeSeparate(int vfs_provider) { void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
// keep track of which faces were defined // keep track of which faces were defined
int loaded[6] = {0, 0, 0, 0, 0, 0}; int loaded[6] = {0, 0, 0, 0, 0, 0};
@@ -2834,7 +2831,7 @@ void mjCTexture::LoadCubeSeparate(int vfs_provider) {
// load PNG or custom // load PNG or custom
unsigned int w, h; unsigned int w, h;
std::vector<unsigned char> image; std::vector<unsigned char> image;
LoadFlip(filename, vfs_provider, image, w, h); LoadFlip(filename, vfs, image, w, h);
// PNG must be square // PNG must be square
if (w!=h) { if (w!=h) {
@@ -2888,7 +2885,7 @@ void mjCTexture::LoadCubeSeparate(int vfs_provider) {
// compiler // compiler
void mjCTexture::Compile(int vfs_provider) { void mjCTexture::Compile(const mjVFS* vfs) {
// builtin // builtin
if (builtin!=mjBUILTIN_NONE) { if (builtin!=mjBUILTIN_NONE) {
// check size // check size
@@ -2931,9 +2928,9 @@ void mjCTexture::Compile(int vfs_provider) {
// dispatch // dispatch
if (type==mjTEXTURE_2D) { if (type==mjTEXTURE_2D) {
Load2D(filename, vfs_provider); Load2D(filename, vfs);
} else { } else {
LoadCubeSingle(filename, vfs_provider); LoadCubeSingle(filename, vfs);
} }
} }
@@ -2961,7 +2958,7 @@ void mjCTexture::Compile(int vfs_provider) {
} }
// only cube and skybox // only cube and skybox
LoadCubeSeparate(vfs_provider); LoadCubeSeparate(vfs);
} }
// make sure someone allocated data; SHOULD NOT OCCUR // make sure someone allocated data; SHOULD NOT OCCUR
@@ -4499,7 +4496,6 @@ mjCKey::~mjCKey() {
// compiler // compiler
void mjCKey::Compile(const mjModel* m) { void mjCKey::Compile(const mjModel* m) {
// qpos: allocate or check size // qpos: allocate or check size
if (qpos.empty()) { if (qpos.empty()) {
qpos.resize(m->nq); qpos.resize(m->nq);
@@ -4574,7 +4570,6 @@ void mjCKey::Compile(const mjModel* m) {
} else if (ctrl.size()!=m->nu) { } else if (ctrl.size()!=m->nu) {
throw mjCError(this, "key %d: invalid ctrl size, expected length %d", nullptr, id, m->nu); throw mjCError(this, "key %d: invalid ctrl size, expected length %d", nullptr, id, m->nu);
} }
} }
+10 -9
View File
@@ -23,6 +23,7 @@
#include "lodepng.h" #include "lodepng.h"
#include <mujoco/mjmodel.h> #include <mujoco/mjmodel.h>
#include <mujoco/mjplugin.h>
// forward declarations of all mjC/X classes // forward declarations of all mjC/X classes
class mjCError; class mjCError;
@@ -173,7 +174,7 @@ class mjCBase {
public: public:
// load resource if found (fallback to OS filesystem) // load resource if found (fallback to OS filesystem)
mjResource* LoadResource(std::string filename, int provider); mjResource* LoadResource(std::string filename, const mjVFS* vfs);
// Get and sanitize content type from raw_text if not empty, otherwise parse // Get and sanitize content type from raw_text if not empty, otherwise parse
// content type from resource_name; throw on failure // content type from resource_name; throw on failure
@@ -572,7 +573,7 @@ class mjCMesh: public mjCBase {
void set_usertexcoord(std::optional<std::vector<float>>&& usertexcoord); void set_usertexcoord(std::optional<std::vector<float>>&& usertexcoord);
void set_userface(std::optional<std::vector<int>>&& userface); void set_userface(std::optional<std::vector<int>>&& userface);
void Compile(int vfs_provider); // compiler void Compile(const mjVFS* vfs); // compiler
double* GetPosPtr(mjtMeshType type); // get position double* GetPosPtr(mjtMeshType type); // get position
double* GetQuatPtr(mjtMeshType type); // get orientation double* GetQuatPtr(mjtMeshType type); // get orientation
double* GetInertiaBoxPtr(mjtMeshType type); // get inertia box double* GetInertiaBoxPtr(mjtMeshType type); // get inertia box
@@ -696,7 +697,7 @@ class mjCSkin: public mjCBase {
private: private:
mjCSkin(mjCModel* = 0); // constructor mjCSkin(mjCModel* = 0); // constructor
~mjCSkin(); // destructor ~mjCSkin(); // destructor
void Compile(int vfs_provider); // compiler void Compile(const mjVFS* vfs); // compiler
void LoadSKN(mjResource* resource); // load skin in SKN BIN format void LoadSKN(mjResource* resource); // load skin in SKN BIN format
int matid; // material id int matid; // material id
@@ -723,7 +724,7 @@ class mjCHField : public mjCBase {
private: private:
mjCHField(mjCModel* model); // constructor mjCHField(mjCModel* model); // constructor
~mjCHField(); // destructor ~mjCHField(); // destructor
void Compile(int vfs_provider); // compiler void Compile(const mjVFS* vfs); // compiler
void LoadCustom(mjResource* resource); // load from custom format void LoadCustom(mjResource* resource); // load from custom format
void LoadPNG(mjResource* resource); // load from PNG format void LoadPNG(mjResource* resource); // load from PNG format
@@ -768,15 +769,15 @@ class mjCTexture : public mjCBase {
private: private:
mjCTexture(mjCModel*); // constructor mjCTexture(mjCModel*); // constructor
~mjCTexture(); // destructior ~mjCTexture(); // destructior
void Compile(int vfs_provider); // compiler void Compile(const mjVFS* vfs); // compiler
void Builtin2D(void); // make builtin 2D void Builtin2D(void); // make builtin 2D
void BuiltinCube(void); // make builtin cube void BuiltinCube(void); // make builtin cube
void Load2D(std::string filename, int vfs_provider); // load 2D from file void Load2D(std::string filename, const mjVFS* vfs); // load 2D from file
void LoadCubeSingle(std::string filename, int vfs_provider); // load cube from single file void LoadCubeSingle(std::string filename, const mjVFS* vfs); // load cube from single file
void LoadCubeSeparate(int vfs_provider); // load cube from separate files void LoadCubeSeparate(const mjVFS* vfs); // load cube from separate files
void LoadFlip(std::string filename, int vfs_provider, // load and flip void LoadFlip(std::string filename, const mjVFS* vfs, // load and flip
std::vector<unsigned char>& image, std::vector<unsigned char>& image,
unsigned int& w, unsigned int& h); unsigned int& w, unsigned int& h);
+13 -11
View File
@@ -22,9 +22,11 @@
#include <string> #include <string>
#include <mujoco/mjmodel.h>
#include "cc/array_safety.h" #include "cc/array_safety.h"
#include "engine/engine_crossplatform.h" #include "engine/engine_crossplatform.h"
#include "engine/engine_resource.h" #include "engine/engine_resource.h"
#include "engine/engine_vfs.h"
#include "user/user_model.h" #include "user/user_model.h"
#include "user/user_util.h" #include "user/user_util.h"
#include "xml/xml_native_reader.h" #include "xml/xml_native_reader.h"
@@ -110,7 +112,7 @@ string mjWriteXML(mjCModel* model, char* error, int error_sz) {
// find include elements recursively, replace them with subtree from xml file // find include elements recursively, replace them with subtree from xml file
static XMLElement* mjIncludeXML(XMLElement* elem, string dir, static XMLElement* mjIncludeXML(XMLElement* elem, string dir,
int vfs_provider, vector<string>& included) { const mjVFS* vfs, vector<string>& included) {
// include element: process // include element: process
if (!strcasecmp(elem->Value(), "include")) { if (!strcasecmp(elem->Value(), "include")) {
// make sure include has no children // make sure include has no children
@@ -133,9 +135,9 @@ static XMLElement* mjIncludeXML(XMLElement* elem, string dir,
// get data source // get data source
mjResource *resource = nullptr; mjResource *resource = nullptr;
const char* xmlstring = nullptr; const char* xmlstring = nullptr;
if ((resource = mju_openResource(filename.c_str(), vfs_provider)) == nullptr) { if ((resource = mju_openVfsResource(filename.c_str(), vfs)) == nullptr) {
// load from OS filesystem // load from provider or OS filesystem
if (!vfs_provider || (resource = mju_openResource(filename.c_str(), 0)) == nullptr) { if ((resource = mju_openResource(filename.c_str())) == nullptr) {
throw mjXError(elem, "Could not open file '%s'", filename.c_str()); throw mjXError(elem, "Could not open file '%s'", filename.c_str());
} }
} }
@@ -196,14 +198,14 @@ static XMLElement* mjIncludeXML(XMLElement* elem, string dir,
} }
// run XMLInclude on first new child // run XMLInclude on first new child
return mjIncludeXML(first->ToElement(), dir, vfs_provider, included); return mjIncludeXML(first->ToElement(), dir, vfs, included);
} }
// otherwise check all child elements, return self // otherwise check all child elements, return self
else { else {
XMLElement* child = elem->FirstChildElement(); XMLElement* child = elem->FirstChildElement();
while (child) { while (child) {
child = mjIncludeXML(child, dir, vfs_provider, included); child = mjIncludeXML(child, dir, vfs, included);
if (child) { if (child) {
child = child->NextSiblingElement(); child = child->NextSiblingElement();
} }
@@ -215,7 +217,7 @@ static XMLElement* mjIncludeXML(XMLElement* elem, string dir,
// Main parser function // Main parser function
mjCModel* mjParseXML(const char* filename, int vfs_provider, char* error, int error_sz) { mjCModel* mjParseXML(const char* filename, const mjVFS* vfs, char* error, int error_sz) {
LocaleOverride locale_override; LocaleOverride locale_override;
// check arguments // check arguments
@@ -235,9 +237,9 @@ mjCModel* mjParseXML(const char* filename, int vfs_provider, char* error, int er
// get data source // get data source
mjResource* resource = nullptr; mjResource* resource = nullptr;
const char* xmlstring = nullptr; const char* xmlstring = nullptr;
if ((resource = mju_openResource(filename, vfs_provider)) == nullptr) { if ((resource = mju_openVfsResource(filename, vfs)) == nullptr) {
// load from OS filesystem // load from provider or fallback to OS filesystem
if (!vfs_provider || (resource = mju_openResource(filename, 0)) == nullptr) { if ((resource = mju_openResource(filename)) == nullptr) {
if (error) { if (error) {
snprintf(error, error_sz, "mjParseXML: could not open file '%s'", filename); snprintf(error, error_sz, "mjParseXML: could not open file '%s'", filename);
} }
@@ -303,7 +305,7 @@ mjCModel* mjParseXML(const char* filename, int vfs_provider, char* error, int er
// find include elements, replace them with subtree from xml file // find include elements, replace them with subtree from xml file
vector<string> included; vector<string> included;
included.push_back(filename); included.push_back(filename);
mjIncludeXML(root, model->modelfiledir, vfs_provider, included); mjIncludeXML(root, model->modelfiledir, vfs, included);
// parse MuJoCo model // parse MuJoCo model
mjXReader parser; mjXReader parser;
+1 -1
View File
@@ -26,7 +26,7 @@
std::string mjWriteXML(mjCModel* model, char* error, int error_sz); std::string mjWriteXML(mjCModel* model, char* error, int error_sz);
// Main parser function // Main parser function
mjCModel* mjParseXML(const char* filename, int vfs_provider, char* error, int error_sz); mjCModel* mjParseXML(const char* filename, const mjVFS* vfs, char* error, int error_sz);
#endif // MUJOCO_SRC_XML_XML_H_ #endif // MUJOCO_SRC_XML_XML_H_
+8 -30
View File
@@ -70,20 +70,23 @@ static std::mutex themutex;
//---------------------------------- Functions ----------------------------------------------------- //---------------------------------- Functions -----------------------------------------------------
// mj_loadXML helper function // parse XML file in MJCF or URDF format, compile it, return low-level model
mjModel* _loadXML(const char* filename, int vfs_provider, // if vfs is not NULL, look up files in vfs before reading from disk
char* error, int error_sz) { // error can be NULL; otherwise assumed to have size error_sz
mjModel* mj_loadXML(const char* filename, const mjVFS* vfs,
char* error, int error_sz) {
// serialize access to themodel // serialize access to themodel
std::lock_guard<std::mutex> lock(themutex); std::lock_guard<std::mutex> lock(themutex);
// parse new model // parse new model
mjCModel* newmodel = mjParseXML(filename, vfs_provider, error, error_sz); mjCModel* newmodel = mjParseXML(filename, vfs, error, error_sz);
if (!newmodel) { if (!newmodel) {
return nullptr; return nullptr;
} }
// compile new model // compile new model
mjModel* m = newmodel->Compile(vfs_provider); mjModel* m = newmodel->Compile(vfs);
if (!m) { if (!m) {
mjCopyError(error, newmodel->GetError().message, error_sz); mjCopyError(error, newmodel->GetError().message, error_sz);
delete newmodel; delete newmodel;
@@ -106,31 +109,6 @@ mjModel* _loadXML(const char* filename, int vfs_provider,
// parse XML file in MJCF or URDF format, compile it, return low-level model
// if vfs is not NULL, look up files in vfs before reading from disk
// error can be NULL; otherwise assumed to have size error_sz
mjModel* mj_loadXML(const char* filename, const mjVFS* vfs,
char* error, int error_sz) {
if (vfs == nullptr) {
return _loadXML(filename, 0, error, error_sz);
}
int index = mj_registerVfsProvider(vfs);
if (index < 1) {
if (error) {
snprintf(error, error_sz, "mj_loadXML: could not register VFS");
}
return nullptr;
}
mjModel* model = _loadXML(filename, index, error, error_sz);
mjp_unregisterResourceProvider(index);
return model;
}
// update XML data structures with info from low-level model, save as MJCF // update XML data structures with info from low-level model, save as MJCF
// returns 1 if successful, 0 otherwise // returns 1 if successful, 0 otherwise
// error can be NULL; otherwise assumed to have size error_sz // error can be NULL; otherwise assumed to have size error_sz
+11 -6
View File
@@ -95,12 +95,12 @@ static const char* MJCF[nMJCF][mjXATTRNUM] = {
"inttotal", "interval", "tolrange"}, "inttotal", "interval", "tolrange"},
{">"}, {">"},
{"option", "*", "24", {"option", "*", "26",
"timestep", "apirate", "impratio", "tolerance", "noslip_tolerance", "mpr_tolerance", "timestep", "apirate", "impratio", "tolerance", "ls_tolerance", "noslip_tolerance",
"gravity", "wind", "magnetic", "density", "viscosity", "mpr_tolerance", "gravity", "wind", "magnetic", "density", "viscosity",
"o_margin", "o_solref", "o_solimp", "o_margin", "o_solref", "o_solimp",
"integrator", "collision", "cone", "jacobian", "integrator", "collision", "cone", "jacobian",
"solver", "iterations", "noslip_iterations", "mpr_iterations", "solver", "iterations", "ls_iterations", "noslip_iterations", "mpr_iterations",
"sdf_iterations", "sdf_initpoints"}, "sdf_iterations", "sdf_initpoints"},
{"<"}, {"<"},
{"flag", "?", "22", "constraint", "equality", "frictionloss", "limit", "contact", {"flag", "?", "22", "constraint", "equality", "frictionloss", "limit", "contact",
@@ -270,8 +270,8 @@ static const char* MJCF[nMJCF][mjXATTRNUM] = {
{"light", "*", "15", "name", "class", "directional", "castshadow", "active", {"light", "*", "15", "name", "class", "directional", "castshadow", "active",
"pos", "dir", "attenuation", "cutoff", "exponent", "ambient", "diffuse", "specular", "pos", "dir", "attenuation", "cutoff", "exponent", "ambient", "diffuse", "specular",
"mode", "target"}, "mode", "target"},
{"composite", "*", "12", "prefix", "type", "count", "spacing", "offset", {"composite", "*", "13", "prefix", "type", "count", "spacing", "offset",
"flatinertia", "solrefsmooth", "solimpsmooth", "vertex", "flatinertia", "solrefsmooth", "solimpsmooth", "vertex", "face",
"initial", "curve", "size"}, "initial", "curve", "size"},
{"<"}, {"<"},
{"plugin", "*", "2", "plugin", "instance"}, {"plugin", "*", "2", "plugin", "instance"},
@@ -958,6 +958,7 @@ void mjXReader::Option(XMLElement* section, mjOption* opt) {
ReadAttr(section, "apirate", 1, &opt->apirate, text); ReadAttr(section, "apirate", 1, &opt->apirate, text);
ReadAttr(section, "impratio", 1, &opt->impratio, text); ReadAttr(section, "impratio", 1, &opt->impratio, text);
ReadAttr(section, "tolerance", 1, &opt->tolerance, text); ReadAttr(section, "tolerance", 1, &opt->tolerance, text);
ReadAttr(section, "ls_tolerance", 1, &opt->ls_tolerance, text);
ReadAttr(section, "noslip_tolerance", 1, &opt->noslip_tolerance, text); ReadAttr(section, "noslip_tolerance", 1, &opt->noslip_tolerance, text);
ReadAttr(section, "mpr_tolerance", 1, &opt->mpr_tolerance, text); ReadAttr(section, "mpr_tolerance", 1, &opt->mpr_tolerance, text);
ReadAttr(section, "gravity", 3, opt->gravity, text); ReadAttr(section, "gravity", 3, opt->gravity, text);
@@ -976,6 +977,7 @@ void mjXReader::Option(XMLElement* section, mjOption* opt) {
MapValue(section, "jacobian", &opt->jacobian, jac_map, jac_sz); MapValue(section, "jacobian", &opt->jacobian, jac_map, jac_sz);
MapValue(section, "solver", &opt->solver, solver_map, solver_sz); MapValue(section, "solver", &opt->solver, solver_map, solver_sz);
ReadAttrInt(section, "iterations", &opt->iterations); ReadAttrInt(section, "iterations", &opt->iterations);
ReadAttrInt(section, "ls_iterations", &opt->ls_iterations);
ReadAttrInt(section, "noslip_iterations", &opt->noslip_iterations); ReadAttrInt(section, "noslip_iterations", &opt->noslip_iterations);
ReadAttrInt(section, "mpr_iterations", &opt->mpr_iterations); ReadAttrInt(section, "mpr_iterations", &opt->mpr_iterations);
ReadAttrInt(section, "sdf_iterations", &opt->sdf_iterations); ReadAttrInt(section, "sdf_iterations", &opt->sdf_iterations);
@@ -1943,6 +1945,9 @@ void mjXReader::OneComposite(XMLElement* elem, mjCBody* pbody, mjCDef* def) {
String2Vector(text, comp.uservert); String2Vector(text, comp.uservert);
} }
// shell
ReadAttrTxt(elem, "face", comp.userface);
// process curve string // process curve string
std::istringstream iss(curves); std::istringstream iss(curves);
int i = 0; int i = 0;
+2
View File
@@ -793,6 +793,7 @@ void mjXWriter::Option(XMLElement* root) {
WriteAttr(section, "apirate", 1, &model->option.apirate, &opt.apirate); WriteAttr(section, "apirate", 1, &model->option.apirate, &opt.apirate);
WriteAttr(section, "impratio", 1, &model->option.impratio, &opt.impratio); WriteAttr(section, "impratio", 1, &model->option.impratio, &opt.impratio);
WriteAttr(section, "tolerance", 1, &model->option.tolerance, &opt.tolerance); WriteAttr(section, "tolerance", 1, &model->option.tolerance, &opt.tolerance);
WriteAttr(section, "ls_tolerance", 1, &model->option.ls_tolerance, &opt.ls_tolerance);
WriteAttr(section, "noslip_tolerance", 1, &model->option.noslip_tolerance, &opt.noslip_tolerance); WriteAttr(section, "noslip_tolerance", 1, &model->option.noslip_tolerance, &opt.noslip_tolerance);
WriteAttr(section, "mpr_tolerance", 1, &model->option.mpr_tolerance, &opt.mpr_tolerance); WriteAttr(section, "mpr_tolerance", 1, &model->option.mpr_tolerance, &opt.mpr_tolerance);
WriteAttr(section, "gravity", 3, model->option.gravity, opt.gravity); WriteAttr(section, "gravity", 3, model->option.gravity, opt.gravity);
@@ -816,6 +817,7 @@ void mjXWriter::Option(XMLElement* root) {
WriteAttrKey(section, "solver", solver_map, solver_sz, WriteAttrKey(section, "solver", solver_map, solver_sz,
model->option.solver, opt.solver); model->option.solver, opt.solver);
WriteAttrInt(section, "iterations", model->option.iterations, opt.iterations); WriteAttrInt(section, "iterations", model->option.iterations, opt.iterations);
WriteAttrInt(section, "ls_iterations", model->option.ls_iterations, opt.ls_iterations);
WriteAttrInt(section, "noslip_iterations", model->option.noslip_iterations, opt.noslip_iterations); WriteAttrInt(section, "noslip_iterations", model->option.noslip_iterations, opt.noslip_iterations);
WriteAttrInt(section, "mpr_iterations", model->option.mpr_iterations, opt.mpr_iterations); WriteAttrInt(section, "mpr_iterations", model->option.mpr_iterations, opt.mpr_iterations);
WriteAttrInt(section, "sdf_iterations", model->option.sdf_iterations, opt.sdf_iterations); WriteAttrInt(section, "sdf_iterations", model->option.sdf_iterations, opt.sdf_iterations);

Some files were not shown because too many files have changed in this diff Show More