diff --git a/doc/APIreference/APIfunctions.rst b/doc/APIreference/APIfunctions.rst index 0a20ca2f..962520b1 100644 --- a/doc/APIreference/APIfunctions.rst +++ b/doc/APIreference/APIfunctions.rst @@ -29,6 +29,7 @@ API function can be classified as: - :ref:`Printing` of various quantities. - :ref:`Virtual file system`, used to load assets from memory. - :ref:`Asset cache`, used to speed up model compilation. + - :ref:`Resources`, interfacing with resource providers to load assets. - :ref:`Initialization` of data structures. - :ref:`Error and memory`. - :ref:`Miscellaneous` functions. diff --git a/doc/APIreference/functions.rst b/doc/APIreference/functions.rst index cfbf9eaf..ea0452c2 100644 --- a/doc/APIreference/functions.rst +++ b/doc/APIreference/functions.rst @@ -1551,6 +1551,77 @@ Get the internal asset cache used by the compiler. Clear the asset cache. +.. _Resources: + +Resources +^^^^^^^^^ + +Resources are the interface between :ref:`resource providers ` and MuJoCo model compilation code. +These functions provide the means to query the resource provider and obtain resources. +.. _mju_openResource: + +`mju_openResource <#mju_openResource>`__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_openResource + +Open a resource; if the name doesn't have a prefix matching a registered resource provider, +then the OS filesystem is used. + +*Nullable:* ``dir``, ``vfs``, ``error`` + +.. _mju_closeResource: + +`mju_closeResource <#mju_closeResource>`__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_closeResource + +Close a resource; no-op if resource is NULL. + +.. _mju_readResource: + +`mju_readResource <#mju_readResource>`__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_readResource + +Set buffer to bytes read from the resource and return number of bytes in buffer; +return negative value if error. + +.. _mju_getResourceDir: + +`mju_getResourceDir <#mju_getResourceDir>`__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_getResourceDir + +For a resource with a name partitioned as {dir}{filename}, get the dir and ndir pointers. + +.. _mju_isModifiedResource: + +`mju_isModifiedResource <#mju_isModifiedResource>`__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_isModifiedResource + +Compare resource timestamp to provided timestamp. + +Return 0 if timestamps match, >0 if resource is newer, <0 if resource is older. + +.. _mju_decodeResource: + +`mju_decodeResource <#mju_decodeResource>`__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_decodeResource + +Find the decoder for a resource and return the decoded spec. + +The caller takes ownership of the spec and is responsible for cleaning it up. + +*Nullable:* ``vfs`` + .. _Initialization: Initialization diff --git a/doc/APIreference/functions_override.rst b/doc/APIreference/functions_override.rst index 8d195b00..c43fe9bb 100644 --- a/doc/APIreference/functions_override.rst +++ b/doc/APIreference/functions_override.rst @@ -816,3 +816,8 @@ Note that derivatives depend only on :math:`h` and :math:`v` (in fact, on :math: All outputs are optional. *Nullable:* ``Dquat``, ``Dvel``, ``Dscale`` + +.. _Resources: + +Resources are the interface between :ref:`resource providers ` and MuJoCo model compilation code. +These functions provide the means to query the resource provider and obtain resources. diff --git a/doc/includes/references.h b/doc/includes/references.h index e435325e..9b6da3cb 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -3579,6 +3579,14 @@ const mjpResourceProvider* mjp_getResourceProviderAtSlot(int slot); void mjp_registerDecoder(const mjpDecoder* decoder); void mjp_defaultDecoder(mjpDecoder* decoder); const mjpDecoder* mjp_findDecoder(const mjResource* resource, const char* content_type); +mjResource* mju_openResource(const char* dir, const char* name, + const mjVFS* vfs, char* error, size_t nerror); +void mju_closeResource(mjResource* resource); +int mju_readResource(mjResource* resource, const void** buffer); +void mju_getResourceDir(mjResource* resource, const char** dir, int* ndir); +int mju_isModifiedResource(const mjResource* resource, const char* timestamp); +mjSpec* mju_decodeResource(mjResource* resource, const char* content_type, + const mjVFS* vfs); mjThreadPool* mju_threadPoolCreate(size_t number_of_threads); void mju_bindThreadPool(mjData* d, void* thread_pool); void mju_threadPoolEnqueue(mjThreadPool* thread_pool, mjTask* task); diff --git a/include/mujoco/mujoco.h b/include/mujoco/mujoco.h index e15e3384..672bd6e8 100644 --- a/include/mujoco/mujoco.h +++ b/include/mujoco/mujoco.h @@ -1522,6 +1522,36 @@ MJAPI void mjp_defaultDecoder(mjpDecoder* decoder); // If no match, return NULL. MJAPI const mjpDecoder* mjp_findDecoder(const mjResource* resource, const char* content_type); + +//---------------------------------- Resources ----------------------------------------------------- + +// Open a resource; if the name doesn't have a prefix matching a registered resource provider, +// then the OS filesystem is used. +// Nullable: dir, vfs, error +MJAPI mjResource* mju_openResource(const char* dir, const char* name, + const mjVFS* vfs, char* error, size_t nerror); + +// Close a resource; no-op if resource is NULL. +MJAPI void mju_closeResource(mjResource* resource); + +// Set buffer to bytes read from the resource and return number of bytes in buffer; +// return negative value if error. +MJAPI int mju_readResource(mjResource* resource, const void** buffer); + +// For a resource with a name partitioned as {dir}{filename}, get the dir and ndir pointers. +MJAPI void mju_getResourceDir(mjResource* resource, const char** dir, int* ndir); + +// Compare resource timestamp to provided timestamp. +// Return 0 if timestamps match, >0 if resource is newer, <0 if resource is older. +MJAPI int mju_isModifiedResource(const mjResource* resource, const char* timestamp); + +// Find the decoder for a resource and return the decoded spec. +// The caller takes ownership of the spec and is responsible for cleaning it up. +// Nullable: vfs +MJAPI mjSpec* mju_decodeResource(mjResource* resource, const char* content_type, + const mjVFS* vfs); + + //---------------------------------- Threads ------------------------------------------------------- // Create a thread pool with the specified number of threads running. diff --git a/python/mujoco/introspect/functions.py b/python/mujoco/introspect/functions.py index cfd60c74..787dacff 100644 --- a/python/mujoco/introspect/functions.py +++ b/python/mujoco/introspect/functions.py @@ -9600,6 +9600,160 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ), doc='Return the resource provider with the prefix that matches against the resource name. If no match, return NULL.', # pylint: disable=line-too-long )), + ('mju_openResource', + FunctionDecl( + name='mju_openResource', + return_type=PointerType( + inner_type=ValueType(name='mjResource'), + ), + parameters=( + FunctionParameterDecl( + name='dir', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + nullable=True, + ), + FunctionParameterDecl( + name='name', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + FunctionParameterDecl( + name='vfs', + type=PointerType( + inner_type=ValueType(name='mjVFS', is_const=True), + ), + nullable=True, + ), + FunctionParameterDecl( + name='error', + type=PointerType( + inner_type=ValueType(name='char'), + ), + nullable=True, + ), + FunctionParameterDecl( + name='nerror', + type=ValueType(name='size_t'), + ), + ), + doc="Open a resource; if the name doesn't have a prefix matching a registered resource provider, then the OS filesystem is used.", # pylint: disable=line-too-long + )), + ('mju_closeResource', + FunctionDecl( + name='mju_closeResource', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='resource', + type=PointerType( + inner_type=ValueType(name='mjResource'), + ), + ), + ), + doc='Close a resource; no-op if resource is NULL.', + )), + ('mju_readResource', + FunctionDecl( + name='mju_readResource', + return_type=ValueType(name='int'), + parameters=( + FunctionParameterDecl( + name='resource', + type=PointerType( + inner_type=ValueType(name='mjResource'), + ), + ), + FunctionParameterDecl( + name='buffer', + type=PointerType( + inner_type=PointerType( + inner_type=ValueType(name='void', is_const=True), + ), + ), + ), + ), + doc='Set buffer to bytes read from the resource and return number of bytes in buffer; return negative value if error.', # pylint: disable=line-too-long + )), + ('mju_getResourceDir', + FunctionDecl( + name='mju_getResourceDir', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='resource', + type=PointerType( + inner_type=ValueType(name='mjResource'), + ), + ), + FunctionParameterDecl( + name='dir', + type=PointerType( + inner_type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + ), + FunctionParameterDecl( + name='ndir', + type=PointerType( + inner_type=ValueType(name='int'), + ), + ), + ), + doc='For a resource with a name partitioned as {dir}{filename}, get the dir and ndir pointers.', # pylint: disable=line-too-long + )), + ('mju_isModifiedResource', + FunctionDecl( + name='mju_isModifiedResource', + return_type=ValueType(name='int'), + parameters=( + FunctionParameterDecl( + name='resource', + type=PointerType( + inner_type=ValueType(name='mjResource', is_const=True), + ), + ), + FunctionParameterDecl( + name='timestamp', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + ), + doc='Compare resource timestamp to provided timestamp. Return 0 if timestamps match, >0 if resource is newer, <0 if resource is older.', # pylint: disable=line-too-long + )), + ('mju_decodeResource', + FunctionDecl( + name='mju_decodeResource', + return_type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + parameters=( + FunctionParameterDecl( + name='resource', + type=PointerType( + inner_type=ValueType(name='mjResource'), + ), + ), + FunctionParameterDecl( + name='content_type', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + FunctionParameterDecl( + name='vfs', + type=PointerType( + inner_type=ValueType(name='mjVFS', is_const=True), + ), + nullable=True, + ), + ), + doc='Find the decoder for a resource and return the decoded spec. The caller takes ownership of the spec and is responsible for cleaning it up.', # pylint: disable=line-too-long + )), ('mju_threadPoolCreate', FunctionDecl( name='mju_threadPoolCreate', diff --git a/wasm/codegen/generators/constants.py b/wasm/codegen/generators/constants.py index f7063d1d..a365a6e2 100644 --- a/wasm/codegen/generators/constants.py +++ b/wasm/codegen/generators/constants.py @@ -185,7 +185,13 @@ _SKIPPED_GETTERS_AND_SETTERS: tuple[str, ...] = ( _SKIPPED_UTILITY_FUNCTIONS: tuple[str, ...] = ( # go/keep-sorted start "mj_readSensor", + "mju_closeResource", + "mju_decodeResource", + "mju_getResourceDir", "mju_getXMLDependencies", + "mju_isModifiedResource", + "mju_openResource", + "mju_readResource", # go/keep-sorted end )