From 57f71458065ac1e62dce5dc512081daa1b2b5e7d Mon Sep 17 00:00:00 2001 From: Sam Haves Date: Thu, 30 Oct 2025 12:53:40 -0700 Subject: [PATCH] Create mjpDecoder, mj_parse, and mju_decodeResource - plugin system similar to mjpResourceProvider, but instead of loading a resource it converts an existing mjResource into an mjSpec. - The returned spec is then composed into the referencing spec. - This enables different file types to generate arbitrary specs, and allows us to separate format parsing from compilation code. Follow up CLs will move some of the logic in src/engine for PNG, USD, KTX, OBJ loading into decoders. The mj_parse function MjSpec from a given file, it's a more generic version of mj_parseXML. In it's implementation, mj_parse as opposed to mj_parseXML will look for any registered decoder and not assume we are striclty dealing with MJCF. PiperOrigin-RevId: 826149497 Change-Id: I0ece26904280cb94bd5ded6dd5a565c539d60254 --- doc/APIreference/functions.rst | 44 + doc/includes/references.h | 15 + include/mujoco/mjplugin.h | 20 + include/mujoco/mujoco.h | 16 + .../introspect/codegen/generate_structs.py | 2 + python/mujoco/introspect/enums.py | 198 +- python/mujoco/introspect/functions.py | 90 + python/mujoco/introspect/structs.py | 2890 ++++++++--------- python/mujoco/specs.cc | 4 +- src/engine/engine_plugin.cc | 147 + src/engine/engine_plugin.h | 9 + src/user/user_api.cc | 18 + src/user/user_resource.cc | 17 + src/user/user_resource.h | 4 + src/xml/xml_native_reader.cc | 17 +- test/plugin/decoder/decoder_test.cc | 127 + 16 files changed, 2063 insertions(+), 1555 deletions(-) create mode 100644 test/plugin/decoder/decoder_test.cc diff --git a/doc/APIreference/functions.rst b/doc/APIreference/functions.rst index 44282c01..c848fb31 100644 --- a/doc/APIreference/functions.rst +++ b/doc/APIreference/functions.rst @@ -48,6 +48,17 @@ Parse spec from XML string. *Nullable:* ``vfs``, ``error`` +.. _mj_parse: + +`mj_parse <#mj_parse>`__ +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mj_parse + +Parse spec from a file. + +*Nullable:* ``vfs``, ``error`` + .. _mj_compile: `mj_compile <#mj_compile>`__ @@ -2926,6 +2937,39 @@ Look up a resource provider by slot number returned by mjp_registerResourceProvi If invalid slot number, return NULL. +.. _mjp_registerDecoder: + +`mjp_registerDecoder <#mjp_registerDecoder>`__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjp_registerDecoder + +Globally register a decoder. This function is thread-safe. + +If an identical mjpDecoder is already registered, this function does nothing. + +If a non-identical mjpDecoder with the same name is already registered, an mju_error is raised. + +.. _mjp_defaultDecoder: + +`mjp_defaultDecoder <#mjp_defaultDecoder>`__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjp_defaultDecoder + +Set default resource decoder definition. + +.. _mjp_findDecoder: + +`mjp_findDecoder <#mjp_findDecoder>`__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjp_findDecoder + +Return the resource provider with the prefix that matches against the resource name. + +If no match, return NULL. + .. _Thread: Threads diff --git a/doc/includes/references.h b/doc/includes/references.h index a3390fa5..d7ada630 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -1563,6 +1563,16 @@ struct mjpResourceProvider { void* data; // opaque data pointer (resource invariant) }; typedef struct mjpResourceProvider mjpResourceProvider; +struct mjpDecoder { + const char* content_type; + const char* extension; + // user-facing functions + mjfCanDecode can_decode; // quickly check if this decoder can handle the resource + mjfDecode decode; // main decoding function + // the caller takes ownership of the spec returned by decode and is responsible + // for cleaning it up +}; +typedef struct mjpDecoder mjpDecoder; typedef enum mjtPluginCapabilityBit_ { mjPLUGIN_ACTUATOR = 1<<0, // actuator forces mjPLUGIN_SENSOR = 1<<1, // sensor measurements @@ -3014,6 +3024,8 @@ void mj_clearCache(mjCache* cache); mjModel* mj_loadXML(const char* filename, const mjVFS* vfs, char* error, int error_sz); mjSpec* mj_parseXML(const char* filename, const mjVFS* vfs, char* error, int error_sz); mjSpec* mj_parseXMLString(const char* xml, const mjVFS* vfs, char* error, int error_sz); +mjSpec* mj_parse(const char* filename, const char* content_type, + const mjVFS* vfs, char* error, int error_sz); mjModel* mj_compile(mjSpec* s, const mjVFS* vfs); int mj_copyBack(mjSpec* s, const mjModel* m); int mj_recompile(mjSpec* s, const mjVFS* vfs, mjModel* m, mjData* d); @@ -3431,6 +3443,9 @@ int mjp_registerResourceProvider(const mjpResourceProvider* provider); int mjp_resourceProviderCount(void); const mjpResourceProvider* mjp_getResourceProvider(const char* resource_name); 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); 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/mjplugin.h b/include/mujoco/mjplugin.h index 44f2af20..a9e5127c 100644 --- a/include/mujoco/mjplugin.h +++ b/include/mujoco/mjplugin.h @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -64,6 +65,25 @@ struct mjpResourceProvider { }; typedef struct mjpResourceProvider mjpResourceProvider; +//---------------------------------- Decoder ------------------------------------------------------- + +// function pointer types +// return an mjSpec representing the decoded resource. +typedef mjSpec* (*mjfDecode)(const mjResource* resource); +// return true if the given resource can be decoded. +typedef int (*mjfCanDecode)(const mjResource* resource); + +// the struct defining the decoder plugin's interface +struct mjpDecoder { + const char* content_type; + const char* extension; + // user-facing functions + mjfCanDecode can_decode; // quickly check if this decoder can handle the resource + mjfDecode decode; // main decoding function + // the caller takes ownership of the spec returned by decode and is responsible + // for cleaning it up +}; +typedef struct mjpDecoder mjpDecoder; //---------------------------------- Plugins ------------------------------------------------------- diff --git a/include/mujoco/mujoco.h b/include/mujoco/mujoco.h index e8baea31..7100fcaa 100644 --- a/include/mujoco/mujoco.h +++ b/include/mujoco/mujoco.h @@ -123,6 +123,11 @@ MJAPI mjSpec* mj_parseXML(const char* filename, const mjVFS* vfs, char* error, i // Nullable: vfs, error MJAPI mjSpec* mj_parseXMLString(const char* xml, const mjVFS* vfs, char* error, int error_sz); +// Parse spec from a file. +// Nullable: vfs, error +MJAPI mjSpec* mj_parse(const char* filename, const char* content_type, + const mjVFS* vfs, char* error, int error_sz); + // Compile spec to model. // Nullable: vfs MJAPI mjModel* mj_compile(mjSpec* s, const mjVFS* vfs); @@ -1449,6 +1454,17 @@ MJAPI const mjpResourceProvider* mjp_getResourceProvider(const char* resource_na // If invalid slot number, return NULL. MJAPI const mjpResourceProvider* mjp_getResourceProviderAtSlot(int slot); +// Globally register a decoder. This function is thread-safe. +// If an identical mjpDecoder is already registered, this function does nothing. +// If a non-identical mjpDecoder with the same name is already registered, an mju_error is raised. +MJAPI void mjp_registerDecoder(const mjpDecoder* decoder); + +// Set default resource decoder definition. +MJAPI void mjp_defaultDecoder(mjpDecoder* decoder); + +// Return the resource provider with the prefix that matches against the resource name. +// If no match, return NULL. +MJAPI const mjpDecoder* mjp_findDecoder(const mjResource* resource, const char* content_type); //---------------------------------- Threads ------------------------------------------------------- diff --git a/python/mujoco/introspect/codegen/generate_structs.py b/python/mujoco/introspect/codegen/generate_structs.py index 24da6e2e..787b08e6 100644 --- a/python/mujoco/introspect/codegen/generate_structs.py +++ b/python/mujoco/introspect/codegen/generate_structs.py @@ -39,6 +39,8 @@ ClangJsonNode = Mapping[str, Any] _ANONYMOUS_KEY_PATTERN = re.compile(r'\d+:\d+(?=\))') _EXCLUDED = ( + 'mjpDecoder', + 'mjpDecoder_', 'mjpPlugin', 'mjpPlugin_', 'mjpResourceProvider', diff --git a/python/mujoco/introspect/enums.py b/python/mujoco/introspect/enums.py index db3b8d70..bb4abd59 100644 --- a/python/mujoco/introspect/enums.py +++ b/python/mujoco/introspect/enums.py @@ -553,6 +553,105 @@ ENUMS: Mapping[str, EnumDecl] = dict([ ('mjNTIMER', 15), ]), )), + ('mjtGeomInertia', + EnumDecl( + name='mjtGeomInertia', + declname='enum mjtGeomInertia_', + values=dict([ + ('mjINERTIA_VOLUME', 0), + ('mjINERTIA_SHELL', 1), + ]), + )), + ('mjtMeshInertia', + EnumDecl( + name='mjtMeshInertia', + declname='enum mjtMeshInertia_', + values=dict([ + ('mjMESH_INERTIA_CONVEX', 0), + ('mjMESH_INERTIA_EXACT', 1), + ('mjMESH_INERTIA_LEGACY', 2), + ('mjMESH_INERTIA_SHELL', 3), + ]), + )), + ('mjtMeshBuiltin', + EnumDecl( + name='mjtMeshBuiltin', + declname='enum mjtMeshBuiltin_', + values=dict([ + ('mjMESH_BUILTIN_NONE', 0), + ('mjMESH_BUILTIN_SPHERE', 1), + ('mjMESH_BUILTIN_HEMISPHERE', 2), + ('mjMESH_BUILTIN_CONE', 3), + ('mjMESH_BUILTIN_SUPERSPHERE', 4), + ('mjMESH_BUILTIN_SUPERTORUS', 5), + ('mjMESH_BUILTIN_WEDGE', 6), + ('mjMESH_BUILTIN_PLATE', 7), + ]), + )), + ('mjtBuiltin', + EnumDecl( + name='mjtBuiltin', + declname='enum mjtBuiltin_', + values=dict([ + ('mjBUILTIN_NONE', 0), + ('mjBUILTIN_GRADIENT', 1), + ('mjBUILTIN_CHECKER', 2), + ('mjBUILTIN_FLAT', 3), + ]), + )), + ('mjtMark', + EnumDecl( + name='mjtMark', + declname='enum mjtMark_', + values=dict([ + ('mjMARK_NONE', 0), + ('mjMARK_EDGE', 1), + ('mjMARK_CROSS', 2), + ('mjMARK_RANDOM', 3), + ]), + )), + ('mjtLimited', + EnumDecl( + name='mjtLimited', + declname='enum mjtLimited_', + values=dict([ + ('mjLIMITED_FALSE', 0), + ('mjLIMITED_TRUE', 1), + ('mjLIMITED_AUTO', 2), + ]), + )), + ('mjtAlignFree', + EnumDecl( + name='mjtAlignFree', + declname='enum mjtAlignFree_', + values=dict([ + ('mjALIGNFREE_FALSE', 0), + ('mjALIGNFREE_TRUE', 1), + ('mjALIGNFREE_AUTO', 2), + ]), + )), + ('mjtInertiaFromGeom', + EnumDecl( + name='mjtInertiaFromGeom', + declname='enum mjtInertiaFromGeom_', + values=dict([ + ('mjINERTIAFROMGEOM_FALSE', 0), + ('mjINERTIAFROMGEOM_TRUE', 1), + ('mjINERTIAFROMGEOM_AUTO', 2), + ]), + )), + ('mjtOrientation', + EnumDecl( + name='mjtOrientation', + declname='enum mjtOrientation_', + values=dict([ + ('mjORIENTATION_QUAT', 0), + ('mjORIENTATION_AXISANGLE', 1), + ('mjORIENTATION_XYAXES', 2), + ('mjORIENTATION_ZAXIS', 3), + ('mjORIENTATION_EULER', 4), + ]), + )), ('mjtCatBit', EnumDecl( name='mjtCatBit', @@ -774,105 +873,6 @@ ENUMS: Mapping[str, EnumDecl] = dict([ ('mjFONT_BIG', 2), ]), )), - ('mjtGeomInertia', - EnumDecl( - name='mjtGeomInertia', - declname='enum mjtGeomInertia_', - values=dict([ - ('mjINERTIA_VOLUME', 0), - ('mjINERTIA_SHELL', 1), - ]), - )), - ('mjtMeshInertia', - EnumDecl( - name='mjtMeshInertia', - declname='enum mjtMeshInertia_', - values=dict([ - ('mjMESH_INERTIA_CONVEX', 0), - ('mjMESH_INERTIA_EXACT', 1), - ('mjMESH_INERTIA_LEGACY', 2), - ('mjMESH_INERTIA_SHELL', 3), - ]), - )), - ('mjtMeshBuiltin', - EnumDecl( - name='mjtMeshBuiltin', - declname='enum mjtMeshBuiltin_', - values=dict([ - ('mjMESH_BUILTIN_NONE', 0), - ('mjMESH_BUILTIN_SPHERE', 1), - ('mjMESH_BUILTIN_HEMISPHERE', 2), - ('mjMESH_BUILTIN_CONE', 3), - ('mjMESH_BUILTIN_SUPERSPHERE', 4), - ('mjMESH_BUILTIN_SUPERTORUS', 5), - ('mjMESH_BUILTIN_WEDGE', 6), - ('mjMESH_BUILTIN_PLATE', 7), - ]), - )), - ('mjtBuiltin', - EnumDecl( - name='mjtBuiltin', - declname='enum mjtBuiltin_', - values=dict([ - ('mjBUILTIN_NONE', 0), - ('mjBUILTIN_GRADIENT', 1), - ('mjBUILTIN_CHECKER', 2), - ('mjBUILTIN_FLAT', 3), - ]), - )), - ('mjtMark', - EnumDecl( - name='mjtMark', - declname='enum mjtMark_', - values=dict([ - ('mjMARK_NONE', 0), - ('mjMARK_EDGE', 1), - ('mjMARK_CROSS', 2), - ('mjMARK_RANDOM', 3), - ]), - )), - ('mjtLimited', - EnumDecl( - name='mjtLimited', - declname='enum mjtLimited_', - values=dict([ - ('mjLIMITED_FALSE', 0), - ('mjLIMITED_TRUE', 1), - ('mjLIMITED_AUTO', 2), - ]), - )), - ('mjtAlignFree', - EnumDecl( - name='mjtAlignFree', - declname='enum mjtAlignFree_', - values=dict([ - ('mjALIGNFREE_FALSE', 0), - ('mjALIGNFREE_TRUE', 1), - ('mjALIGNFREE_AUTO', 2), - ]), - )), - ('mjtInertiaFromGeom', - EnumDecl( - name='mjtInertiaFromGeom', - declname='enum mjtInertiaFromGeom_', - values=dict([ - ('mjINERTIAFROMGEOM_FALSE', 0), - ('mjINERTIAFROMGEOM_TRUE', 1), - ('mjINERTIAFROMGEOM_AUTO', 2), - ]), - )), - ('mjtOrientation', - EnumDecl( - name='mjtOrientation', - declname='enum mjtOrientation_', - values=dict([ - ('mjORIENTATION_QUAT', 0), - ('mjORIENTATION_AXISANGLE', 1), - ('mjORIENTATION_XYAXES', 2), - ('mjORIENTATION_ZAXIS', 3), - ('mjORIENTATION_EULER', 4), - ]), - )), ('mjtButton', EnumDecl( name='mjtButton', diff --git a/python/mujoco/introspect/functions.py b/python/mujoco/introspect/functions.py index f69ba7f9..5a8ab934 100644 --- a/python/mujoco/introspect/functions.py +++ b/python/mujoco/introspect/functions.py @@ -301,6 +301,46 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ), doc='Parse spec from XML string.', )), + ('mj_parse', + FunctionDecl( + name='mj_parse', + return_type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + parameters=( + FunctionParameterDecl( + name='filename', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + 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, + ), + FunctionParameterDecl( + name='error', + type=PointerType( + inner_type=ValueType(name='char'), + ), + nullable=True, + ), + FunctionParameterDecl( + name='error_sz', + type=ValueType(name='int'), + ), + ), + doc='Parse spec from a file.', + )), ('mj_compile', FunctionDecl( name='mj_compile', @@ -9096,6 +9136,56 @@ 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 )), + ('mjp_registerDecoder', + FunctionDecl( + name='mjp_registerDecoder', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='decoder', + type=PointerType( + inner_type=ValueType(name='mjpDecoder', is_const=True), + ), + ), + ), + doc='Globally register a decoder. This function is thread-safe. If an identical mjpDecoder is already registered, this function does nothing. If a non-identical mjpDecoder with the same name is already registered, an mju_error is raised.', # pylint: disable=line-too-long + )), + ('mjp_defaultDecoder', + FunctionDecl( + name='mjp_defaultDecoder', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='decoder', + type=PointerType( + inner_type=ValueType(name='mjpDecoder'), + ), + ), + ), + doc='Set default resource decoder definition.', + )), + ('mjp_findDecoder', + FunctionDecl( + name='mjp_findDecoder', + return_type=PointerType( + inner_type=ValueType(name='mjpDecoder', is_const=True), + ), + parameters=( + FunctionParameterDecl( + name='resource', + type=PointerType( + inner_type=ValueType(name='mjResource', is_const=True), + ), + ), + FunctionParameterDecl( + name='content_type', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + ), + 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_threadPoolCreate', FunctionDecl( name='mju_threadPoolCreate', diff --git a/python/mujoco/introspect/structs.py b/python/mujoco/introspect/structs.py index 805c76c9..7f88a5ab 100644 --- a/python/mujoco/introspect/structs.py +++ b/python/mujoco/introspect/structs.py @@ -6375,1451 +6375,6 @@ STRUCTS: Mapping[str, StructDecl] = dict([ ), ), )), - ('mjvPerturb', - StructDecl( - name='mjvPerturb', - declname='struct mjvPerturb_', - fields=( - StructFieldDecl( - name='select', - type=ValueType(name='int'), - doc='selected body id; non-positive: none', - ), - StructFieldDecl( - name='flexselect', - type=ValueType(name='int'), - doc='selected flex id; negative: none', - ), - StructFieldDecl( - name='skinselect', - type=ValueType(name='int'), - doc='selected skin id; negative: none', - ), - StructFieldDecl( - name='active', - type=ValueType(name='int'), - doc='perturbation bitmask (mjtPertBit)', - ), - StructFieldDecl( - name='active2', - type=ValueType(name='int'), - doc='secondary perturbation bitmask (mjtPertBit)', - ), - StructFieldDecl( - name='refpos', - type=ArrayType( - inner_type=ValueType(name='mjtNum'), - extents=(3,), - ), - doc='reference position for selected object', - ), - StructFieldDecl( - name='refquat', - type=ArrayType( - inner_type=ValueType(name='mjtNum'), - extents=(4,), - ), - doc='reference orientation for selected object', - ), - StructFieldDecl( - name='refselpos', - type=ArrayType( - inner_type=ValueType(name='mjtNum'), - extents=(3,), - ), - doc='reference position for selection point', - ), - StructFieldDecl( - name='localpos', - type=ArrayType( - inner_type=ValueType(name='mjtNum'), - extents=(3,), - ), - doc='selection point in object coordinates', - ), - StructFieldDecl( - name='localmass', - type=ValueType(name='mjtNum'), - doc='spatial inertia at selection point', - ), - StructFieldDecl( - name='scale', - type=ValueType(name='mjtNum'), - doc='relative mouse motion-to-space scaling (set by initPerturb)', # pylint: disable=line-too-long - ), - ), - )), - ('mjvCamera', - StructDecl( - name='mjvCamera', - declname='struct mjvCamera_', - fields=( - StructFieldDecl( - name='type', - type=ValueType(name='int'), - doc='camera type (mjtCamera)', - ), - StructFieldDecl( - name='fixedcamid', - type=ValueType(name='int'), - doc='fixed camera id', - ), - StructFieldDecl( - name='trackbodyid', - type=ValueType(name='int'), - doc='body id to track', - ), - StructFieldDecl( - name='lookat', - type=ArrayType( - inner_type=ValueType(name='mjtNum'), - extents=(3,), - ), - doc='lookat point', - ), - StructFieldDecl( - name='distance', - type=ValueType(name='mjtNum'), - doc='distance to lookat point or tracked body', - ), - StructFieldDecl( - name='azimuth', - type=ValueType(name='mjtNum'), - doc='camera azimuth (deg)', - ), - StructFieldDecl( - name='elevation', - type=ValueType(name='mjtNum'), - doc='camera elevation (deg)', - ), - StructFieldDecl( - name='orthographic', - type=ValueType(name='int'), - doc='0: perspective; 1: orthographic', - ), - ), - )), - ('mjvGLCamera', - StructDecl( - name='mjvGLCamera', - declname='struct mjvGLCamera_', - fields=( - StructFieldDecl( - name='pos', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(3,), - ), - doc='position', - ), - StructFieldDecl( - name='forward', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(3,), - ), - doc='forward direction', - ), - StructFieldDecl( - name='up', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(3,), - ), - doc='up direction', - ), - StructFieldDecl( - name='frustum_center', - type=ValueType(name='float'), - doc='hor. center (left,right set to match aspect)', - ), - StructFieldDecl( - name='frustum_width', - type=ValueType(name='float'), - doc='width (not used for rendering)', - ), - StructFieldDecl( - name='frustum_bottom', - type=ValueType(name='float'), - doc='bottom', - ), - StructFieldDecl( - name='frustum_top', - type=ValueType(name='float'), - doc='top', - ), - StructFieldDecl( - name='frustum_near', - type=ValueType(name='float'), - doc='near', - ), - StructFieldDecl( - name='frustum_far', - type=ValueType(name='float'), - doc='far', - ), - StructFieldDecl( - name='orthographic', - type=ValueType(name='int'), - doc='0: perspective; 1: orthographic', - ), - ), - )), - ('mjvGeom', - StructDecl( - name='mjvGeom', - declname='struct mjvGeom_', - fields=( - StructFieldDecl( - name='type', - type=ValueType(name='int'), - doc='geom type (mjtGeom)', - ), - StructFieldDecl( - name='dataid', - type=ValueType(name='int'), - doc='mesh, hfield or plane id; -1: none; mesh: 2*id or 2*id+1 (hull)', # pylint: disable=line-too-long - ), - StructFieldDecl( - name='objtype', - type=ValueType(name='int'), - doc='mujoco object type; mjOBJ_UNKNOWN for decor', - ), - StructFieldDecl( - name='objid', - type=ValueType(name='int'), - doc='mujoco object id; -1 for decor', - ), - StructFieldDecl( - name='category', - type=ValueType(name='int'), - doc='visual category', - ), - StructFieldDecl( - name='matid', - type=ValueType(name='int'), - doc='material id; -1: no textured material', - ), - StructFieldDecl( - name='texcoord', - type=ValueType(name='int'), - doc='mesh or flex geom has texture coordinates', - ), - StructFieldDecl( - name='segid', - type=ValueType(name='int'), - doc='segmentation id; -1: not shown', - ), - StructFieldDecl( - name='size', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(3,), - ), - doc='size parameters', - ), - StructFieldDecl( - name='pos', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(3,), - ), - doc='Cartesian position', - ), - StructFieldDecl( - name='mat', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(9,), - ), - doc='Cartesian orientation', - ), - StructFieldDecl( - name='rgba', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(4,), - ), - doc='color and transparency', - ), - StructFieldDecl( - name='emission', - type=ValueType(name='float'), - doc='emission coef', - ), - StructFieldDecl( - name='specular', - type=ValueType(name='float'), - doc='specular coef', - ), - StructFieldDecl( - name='shininess', - type=ValueType(name='float'), - doc='shininess coef', - ), - StructFieldDecl( - name='reflectance', - type=ValueType(name='float'), - doc='reflectance coef', - ), - StructFieldDecl( - name='label', - type=ArrayType( - inner_type=ValueType(name='char'), - extents=(100,), - ), - doc='text label', - ), - StructFieldDecl( - name='camdist', - type=ValueType(name='float'), - doc='distance to camera (used by sorter)', - ), - StructFieldDecl( - name='modelrbound', - type=ValueType(name='float'), - doc='geom rbound from model, 0 if not model geom', - ), - StructFieldDecl( - name='transparent', - type=ValueType(name='mjtByte'), - doc='treat geom as transparent', - ), - ), - )), - ('mjvLight', - StructDecl( - name='mjvLight', - declname='struct mjvLight_', - fields=( - StructFieldDecl( - name='id', - type=ValueType(name='int'), - doc='light id, -1 for headlight', - ), - StructFieldDecl( - name='pos', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(3,), - ), - doc='position rel. to body frame', - ), - StructFieldDecl( - name='dir', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(3,), - ), - doc='direction rel. to body frame', - ), - StructFieldDecl( - name='type', - type=ValueType(name='int'), - doc='type (mjtLightType)', - ), - StructFieldDecl( - name='texid', - type=ValueType(name='int'), - doc='texture id for image lights', - ), - StructFieldDecl( - name='attenuation', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(3,), - ), - doc='OpenGL attenuation (quadratic model)', - ), - StructFieldDecl( - name='cutoff', - type=ValueType(name='float'), - doc='OpenGL cutoff', - ), - StructFieldDecl( - name='exponent', - type=ValueType(name='float'), - doc='OpenGL exponent', - ), - StructFieldDecl( - name='ambient', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(3,), - ), - doc='ambient rgb (alpha=1)', - ), - StructFieldDecl( - name='diffuse', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(3,), - ), - doc='diffuse rgb (alpha=1)', - ), - StructFieldDecl( - name='specular', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(3,), - ), - doc='specular rgb (alpha=1)', - ), - StructFieldDecl( - name='headlight', - type=ValueType(name='mjtByte'), - doc='headlight', - ), - StructFieldDecl( - name='castshadow', - type=ValueType(name='mjtByte'), - doc='does light cast shadows', - ), - StructFieldDecl( - name='bulbradius', - type=ValueType(name='float'), - doc='bulb radius for soft shadows', - ), - StructFieldDecl( - name='intensity', - type=ValueType(name='float'), - doc='intensity, in candelas', - ), - StructFieldDecl( - name='range', - type=ValueType(name='float'), - doc='range of effectiveness', - ), - ), - )), - ('mjvOption', - StructDecl( - name='mjvOption', - declname='struct mjvOption_', - fields=( - StructFieldDecl( - name='label', - type=ValueType(name='int'), - doc='what objects to label (mjtLabel)', - ), - StructFieldDecl( - name='frame', - type=ValueType(name='int'), - doc='which frame to show (mjtFrame)', - ), - StructFieldDecl( - name='geomgroup', - type=ArrayType( - inner_type=ValueType(name='mjtByte'), - extents=(6,), - ), - doc='geom visualization by group', - ), - StructFieldDecl( - name='sitegroup', - type=ArrayType( - inner_type=ValueType(name='mjtByte'), - extents=(6,), - ), - doc='site visualization by group', - ), - StructFieldDecl( - name='jointgroup', - type=ArrayType( - inner_type=ValueType(name='mjtByte'), - extents=(6,), - ), - doc='joint visualization by group', - ), - StructFieldDecl( - name='tendongroup', - type=ArrayType( - inner_type=ValueType(name='mjtByte'), - extents=(6,), - ), - doc='tendon visualization by group', - ), - StructFieldDecl( - name='actuatorgroup', - type=ArrayType( - inner_type=ValueType(name='mjtByte'), - extents=(6,), - ), - doc='actuator visualization by group', - ), - StructFieldDecl( - name='flexgroup', - type=ArrayType( - inner_type=ValueType(name='mjtByte'), - extents=(6,), - ), - doc='flex visualization by group', - ), - StructFieldDecl( - name='skingroup', - type=ArrayType( - inner_type=ValueType(name='mjtByte'), - extents=(6,), - ), - doc='skin visualization by group', - ), - StructFieldDecl( - name='flags', - type=ArrayType( - inner_type=ValueType(name='mjtByte'), - extents=(31,), - ), - doc='visualization flags (indexed by mjtVisFlag)', - ), - StructFieldDecl( - name='bvh_depth', - type=ValueType(name='int'), - doc='depth of the bounding volume hierarchy to be visualized', - ), - StructFieldDecl( - name='flex_layer', - type=ValueType(name='int'), - doc='element layer to be visualized for 3D flex', - ), - ), - )), - ('mjvScene', - StructDecl( - name='mjvScene', - declname='struct mjvScene_', - fields=( - StructFieldDecl( - name='maxgeom', - type=ValueType(name='int'), - doc='size of allocated geom buffer', - ), - StructFieldDecl( - name='ngeom', - type=ValueType(name='int'), - doc='number of geoms currently in buffer', - ), - StructFieldDecl( - name='geoms', - type=PointerType( - inner_type=ValueType(name='mjvGeom'), - ), - doc='buffer for geoms (ngeom)', - ), - StructFieldDecl( - name='geomorder', - type=PointerType( - inner_type=ValueType(name='int'), - ), - doc='buffer for ordering geoms by distance to camera (ngeom)', - ), - StructFieldDecl( - name='nflex', - type=ValueType(name='int'), - doc='number of flexes', - ), - StructFieldDecl( - name='flexedgeadr', - type=PointerType( - inner_type=ValueType(name='int'), - ), - doc='address of flex edges (nflex)', - ), - StructFieldDecl( - name='flexedgenum', - type=PointerType( - inner_type=ValueType(name='int'), - ), - doc='number of edges in flex (nflex)', - ), - StructFieldDecl( - name='flexvertadr', - type=PointerType( - inner_type=ValueType(name='int'), - ), - doc='address of flex vertices (nflex)', - ), - StructFieldDecl( - name='flexvertnum', - type=PointerType( - inner_type=ValueType(name='int'), - ), - doc='number of vertices in flex (nflex)', - ), - StructFieldDecl( - name='flexfaceadr', - type=PointerType( - inner_type=ValueType(name='int'), - ), - doc='address of flex faces (nflex)', - ), - StructFieldDecl( - name='flexfacenum', - type=PointerType( - inner_type=ValueType(name='int'), - ), - doc='number of flex faces allocated (nflex)', - ), - StructFieldDecl( - name='flexfaceused', - type=PointerType( - inner_type=ValueType(name='int'), - ), - doc='number of flex faces currently in use (nflex)', - ), - StructFieldDecl( - name='flexedge', - type=PointerType( - inner_type=ValueType(name='int'), - ), - doc='flex edge data (2*nflexedge)', - ), - StructFieldDecl( - name='flexvert', - type=PointerType( - inner_type=ValueType(name='float'), - ), - doc='flex vertices (3*nflexvert)', - ), - StructFieldDecl( - name='flexface', - type=PointerType( - inner_type=ValueType(name='float'), - ), - doc='flex faces vertices (9*sum(flexfacenum))', - ), - StructFieldDecl( - name='flexnormal', - type=PointerType( - inner_type=ValueType(name='float'), - ), - doc='flex face normals (9*sum(flexfacenum))', - ), - StructFieldDecl( - name='flextexcoord', - type=PointerType( - inner_type=ValueType(name='float'), - ), - doc='flex face texture coordinates (6*sum(flexfacenum))', - ), - StructFieldDecl( - name='flexvertopt', - type=ValueType(name='mjtByte'), - doc='copy of mjVIS_FLEXVERT mjvOption flag', - ), - StructFieldDecl( - name='flexedgeopt', - type=ValueType(name='mjtByte'), - doc='copy of mjVIS_FLEXEDGE mjvOption flag', - ), - StructFieldDecl( - name='flexfaceopt', - type=ValueType(name='mjtByte'), - doc='copy of mjVIS_FLEXFACE mjvOption flag', - ), - StructFieldDecl( - name='flexskinopt', - type=ValueType(name='mjtByte'), - doc='copy of mjVIS_FLEXSKIN mjvOption flag', - ), - StructFieldDecl( - name='nskin', - type=ValueType(name='int'), - doc='number of skins', - ), - StructFieldDecl( - name='skinfacenum', - type=PointerType( - inner_type=ValueType(name='int'), - ), - doc='number of faces in skin (nskin)', - ), - StructFieldDecl( - name='skinvertadr', - type=PointerType( - inner_type=ValueType(name='int'), - ), - doc='address of skin vertices (nskin)', - ), - StructFieldDecl( - name='skinvertnum', - type=PointerType( - inner_type=ValueType(name='int'), - ), - doc='number of vertices in skin (nskin)', - ), - StructFieldDecl( - name='skinvert', - type=PointerType( - inner_type=ValueType(name='float'), - ), - doc='skin vertex data (3*nskinvert)', - ), - StructFieldDecl( - name='skinnormal', - type=PointerType( - inner_type=ValueType(name='float'), - ), - doc='skin normal data (3*nskinvert)', - ), - StructFieldDecl( - name='nlight', - type=ValueType(name='int'), - doc='number of lights currently in buffer', - ), - StructFieldDecl( - name='lights', - type=ArrayType( - inner_type=ValueType(name='mjvLight'), - extents=(100,), - ), - doc='buffer for lights (nlight)', - ), - StructFieldDecl( - name='camera', - type=ArrayType( - inner_type=ValueType(name='mjvGLCamera'), - extents=(2,), - ), - doc='left and right camera', - ), - StructFieldDecl( - name='enabletransform', - type=ValueType(name='mjtByte'), - doc='enable model transformation', - ), - StructFieldDecl( - name='translate', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(3,), - ), - doc='model translation', - ), - StructFieldDecl( - name='rotate', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(4,), - ), - doc='model quaternion rotation', - ), - StructFieldDecl( - name='scale', - type=ValueType(name='float'), - doc='model scaling', - ), - StructFieldDecl( - name='stereo', - type=ValueType(name='int'), - doc='stereoscopic rendering (mjtStereo)', - ), - StructFieldDecl( - name='flags', - type=ArrayType( - inner_type=ValueType(name='mjtByte'), - extents=(10,), - ), - doc='rendering flags (indexed by mjtRndFlag)', - ), - StructFieldDecl( - name='framewidth', - type=ValueType(name='int'), - doc='frame pixel width; 0: disable framing', - ), - StructFieldDecl( - name='framergb', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(3,), - ), - doc='frame color', - ), - StructFieldDecl( - name='status', - type=ValueType(name='int'), - doc='status; 0: ok, 1: geoms exhausted', - ), - ), - )), - ('mjvFigure', - StructDecl( - name='mjvFigure', - declname='struct mjvFigure_', - fields=( - StructFieldDecl( - name='flg_legend', - type=ValueType(name='int'), - doc='show legend', - ), - StructFieldDecl( - name='flg_ticklabel', - type=ArrayType( - inner_type=ValueType(name='int'), - extents=(2,), - ), - doc='show grid tick labels (x,y)', - ), - StructFieldDecl( - name='flg_extend', - type=ValueType(name='int'), - doc='automatically extend axis ranges to fit data', - ), - StructFieldDecl( - name='flg_barplot', - type=ValueType(name='int'), - doc='isolated line segments (i.e. GL_LINES)', - ), - StructFieldDecl( - name='flg_selection', - type=ValueType(name='int'), - doc='vertical selection line', - ), - StructFieldDecl( - name='flg_symmetric', - type=ValueType(name='int'), - doc='symmetric y-axis', - ), - StructFieldDecl( - name='linewidth', - type=ValueType(name='float'), - doc='line width', - ), - StructFieldDecl( - name='gridwidth', - type=ValueType(name='float'), - doc='grid line width', - ), - StructFieldDecl( - name='gridsize', - type=ArrayType( - inner_type=ValueType(name='int'), - extents=(2,), - ), - doc='number of grid points in (x,y)', - ), - StructFieldDecl( - name='gridrgb', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(3,), - ), - doc='grid line rgb', - ), - StructFieldDecl( - name='figurergba', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(4,), - ), - doc='figure color and alpha', - ), - StructFieldDecl( - name='panergba', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(4,), - ), - doc='pane color and alpha', - ), - StructFieldDecl( - name='legendrgba', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(4,), - ), - doc='legend color and alpha', - ), - StructFieldDecl( - name='textrgb', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(3,), - ), - doc='text color', - ), - StructFieldDecl( - name='linergb', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(100, 3), - ), - doc='line colors', - ), - StructFieldDecl( - name='range', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(2, 2), - ), - doc='axis ranges; (min>=max) automatic', - ), - StructFieldDecl( - name='xformat', - type=ArrayType( - inner_type=ValueType(name='char'), - extents=(20,), - ), - doc='x-tick label format for sprintf', - ), - StructFieldDecl( - name='yformat', - type=ArrayType( - inner_type=ValueType(name='char'), - extents=(20,), - ), - doc='y-tick label format for sprintf', - ), - StructFieldDecl( - name='minwidth', - type=ArrayType( - inner_type=ValueType(name='char'), - extents=(20,), - ), - doc='string used to determine min y-tick width', - ), - StructFieldDecl( - name='title', - type=ArrayType( - inner_type=ValueType(name='char'), - extents=(1000,), - ), - doc='figure title; subplots separated with 2+ spaces', - ), - StructFieldDecl( - name='xlabel', - type=ArrayType( - inner_type=ValueType(name='char'), - extents=(100,), - ), - doc='x-axis label', - ), - StructFieldDecl( - name='linename', - type=ArrayType( - inner_type=ValueType(name='char'), - extents=(100, 100), - ), - doc='line names for legend', - ), - StructFieldDecl( - name='legendoffset', - type=ValueType(name='int'), - doc='number of lines to offset legend', - ), - StructFieldDecl( - name='subplot', - type=ValueType(name='int'), - doc='selected subplot (for title rendering)', - ), - StructFieldDecl( - name='highlight', - type=ArrayType( - inner_type=ValueType(name='int'), - extents=(2,), - ), - doc='if point is in legend rect, highlight line', - ), - StructFieldDecl( - name='highlightid', - type=ValueType(name='int'), - doc='if id>=0 and no point, highlight id', - ), - StructFieldDecl( - name='selection', - type=ValueType(name='float'), - doc='selection line x-value', - ), - StructFieldDecl( - name='linepnt', - type=ArrayType( - inner_type=ValueType(name='int'), - extents=(100,), - ), - doc='number of points in line; (0) disable', - ), - StructFieldDecl( - name='linedata', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(100, 2002), - ), - doc='line data (x,y)', - ), - StructFieldDecl( - name='xaxispixel', - type=ArrayType( - inner_type=ValueType(name='int'), - extents=(2,), - ), - doc='range of x-axis in pixels', - ), - StructFieldDecl( - name='yaxispixel', - type=ArrayType( - inner_type=ValueType(name='int'), - extents=(2,), - ), - doc='range of y-axis in pixels', - ), - StructFieldDecl( - name='xaxisdata', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(2,), - ), - doc='range of x-axis in data units', - ), - StructFieldDecl( - name='yaxisdata', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(2,), - ), - doc='range of y-axis in data units', - ), - ), - )), - ('mjSDF', - StructDecl( - name='mjSDF', - declname='struct mjSDF_', - fields=( - StructFieldDecl( - name='plugin', - type=PointerType( - inner_type=PointerType( - inner_type=ValueType(name='mjpPlugin', is_const=True), - ), - ), - doc='', - ), - StructFieldDecl( - name='id', - type=PointerType( - inner_type=ValueType(name='int'), - ), - doc='', - ), - StructFieldDecl( - name='type', - type=ValueType(name='mjtSDFType'), - doc='', - ), - StructFieldDecl( - name='relpos', - type=PointerType( - inner_type=ValueType(name='mjtNum'), - ), - doc='', - ), - StructFieldDecl( - name='relmat', - type=PointerType( - inner_type=ValueType(name='mjtNum'), - ), - doc='', - ), - StructFieldDecl( - name='geomtype', - type=PointerType( - inner_type=ValueType(name='mjtGeom'), - ), - doc='', - ), - ), - )), - ('mjrRect', - StructDecl( - name='mjrRect', - declname='struct mjrRect_', - fields=( - StructFieldDecl( - name='left', - type=ValueType(name='int'), - doc='left (usually 0)', - ), - StructFieldDecl( - name='bottom', - type=ValueType(name='int'), - doc='bottom (usually 0)', - ), - StructFieldDecl( - name='width', - type=ValueType(name='int'), - doc='width (usually buffer width)', - ), - StructFieldDecl( - name='height', - type=ValueType(name='int'), - doc='height (usually buffer height)', - ), - ), - )), - ('mjrContext', - StructDecl( - name='mjrContext', - declname='struct mjrContext_', - fields=( - StructFieldDecl( - name='lineWidth', - type=ValueType(name='float'), - doc='line width for wireframe rendering', - ), - StructFieldDecl( - name='shadowClip', - type=ValueType(name='float'), - doc='clipping radius for directional lights', - ), - StructFieldDecl( - name='shadowScale', - type=ValueType(name='float'), - doc='fraction of light cutoff for spot lights', - ), - StructFieldDecl( - name='fogStart', - type=ValueType(name='float'), - doc='fog start = stat.extent * vis.map.fogstart', - ), - StructFieldDecl( - name='fogEnd', - type=ValueType(name='float'), - doc='fog end = stat.extent * vis.map.fogend', - ), - StructFieldDecl( - name='fogRGBA', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(4,), - ), - doc='fog rgba', - ), - StructFieldDecl( - name='shadowSize', - type=ValueType(name='int'), - doc='size of shadow map texture', - ), - StructFieldDecl( - name='offWidth', - type=ValueType(name='int'), - doc='width of offscreen buffer', - ), - StructFieldDecl( - name='offHeight', - type=ValueType(name='int'), - doc='height of offscreen buffer', - ), - StructFieldDecl( - name='offSamples', - type=ValueType(name='int'), - doc='number of offscreen buffer multisamples', - ), - StructFieldDecl( - name='fontScale', - type=ValueType(name='int'), - doc='font scale', - ), - StructFieldDecl( - name='auxWidth', - type=ArrayType( - inner_type=ValueType(name='int'), - extents=(10,), - ), - doc='auxiliary buffer width', - ), - StructFieldDecl( - name='auxHeight', - type=ArrayType( - inner_type=ValueType(name='int'), - extents=(10,), - ), - doc='auxiliary buffer height', - ), - StructFieldDecl( - name='auxSamples', - type=ArrayType( - inner_type=ValueType(name='int'), - extents=(10,), - ), - doc='auxiliary buffer multisamples', - ), - StructFieldDecl( - name='offFBO', - type=ValueType(name='unsigned int'), - doc='offscreen framebuffer object', - ), - StructFieldDecl( - name='offFBO_r', - type=ValueType(name='unsigned int'), - doc='offscreen framebuffer for resolving multisamples', - ), - StructFieldDecl( - name='offColor', - type=ValueType(name='unsigned int'), - doc='offscreen color buffer', - ), - StructFieldDecl( - name='offColor_r', - type=ValueType(name='unsigned int'), - doc='offscreen color buffer for resolving multisamples', - ), - StructFieldDecl( - name='offDepthStencil', - type=ValueType(name='unsigned int'), - doc='offscreen depth and stencil buffer', - ), - StructFieldDecl( - name='offDepthStencil_r', - type=ValueType(name='unsigned int'), - doc='offscreen depth and stencil buffer for multisamples', - ), - StructFieldDecl( - name='shadowFBO', - type=ValueType(name='unsigned int'), - doc='shadow map framebuffer object', - ), - StructFieldDecl( - name='shadowTex', - type=ValueType(name='unsigned int'), - doc='shadow map texture', - ), - StructFieldDecl( - name='auxFBO', - type=ArrayType( - inner_type=ValueType(name='unsigned int'), - extents=(10,), - ), - doc='auxiliary framebuffer object', - ), - StructFieldDecl( - name='auxFBO_r', - type=ArrayType( - inner_type=ValueType(name='unsigned int'), - extents=(10,), - ), - doc='auxiliary framebuffer object for resolving', - ), - StructFieldDecl( - name='auxColor', - type=ArrayType( - inner_type=ValueType(name='unsigned int'), - extents=(10,), - ), - doc='auxiliary color buffer', - ), - StructFieldDecl( - name='auxColor_r', - type=ArrayType( - inner_type=ValueType(name='unsigned int'), - extents=(10,), - ), - doc='auxiliary color buffer for resolving', - ), - StructFieldDecl( - name='mat_texid', - type=ArrayType( - inner_type=ValueType(name='int'), - extents=(10000,), - ), - doc='material texture ids (-1: no texture)', - ), - StructFieldDecl( - name='mat_texuniform', - type=ArrayType( - inner_type=ValueType(name='int'), - extents=(1000,), - ), - doc='uniform cube mapping', - ), - StructFieldDecl( - name='mat_texrepeat', - type=ArrayType( - inner_type=ValueType(name='float'), - extents=(2000,), - ), - doc='texture repetition for 2d mapping', - ), - StructFieldDecl( - name='ntexture', - type=ValueType(name='int'), - doc='number of allocated textures', - ), - StructFieldDecl( - name='textureType', - type=ArrayType( - inner_type=ValueType(name='int'), - extents=(1000,), - ), - doc='type of texture (mjtTexture) (ntexture)', - ), - StructFieldDecl( - name='texture', - type=ArrayType( - inner_type=ValueType(name='unsigned int'), - extents=(1000,), - ), - doc='texture names', - ), - StructFieldDecl( - name='basePlane', - type=ValueType(name='unsigned int'), - doc='all planes from model', - ), - StructFieldDecl( - name='baseMesh', - type=ValueType(name='unsigned int'), - doc='all meshes from model', - ), - StructFieldDecl( - name='baseHField', - type=ValueType(name='unsigned int'), - doc='all height fields from model', - ), - StructFieldDecl( - name='baseBuiltin', - type=ValueType(name='unsigned int'), - doc='all builtin geoms, with quality from model', - ), - StructFieldDecl( - name='baseFontNormal', - type=ValueType(name='unsigned int'), - doc='normal font', - ), - StructFieldDecl( - name='baseFontShadow', - type=ValueType(name='unsigned int'), - doc='shadow font', - ), - StructFieldDecl( - name='baseFontBig', - type=ValueType(name='unsigned int'), - doc='big font', - ), - StructFieldDecl( - name='rangePlane', - type=ValueType(name='int'), - doc='all planes from model', - ), - StructFieldDecl( - name='rangeMesh', - type=ValueType(name='int'), - doc='all meshes from model', - ), - StructFieldDecl( - name='rangeHField', - type=ValueType(name='int'), - doc='all hfields from model', - ), - StructFieldDecl( - name='rangeBuiltin', - type=ValueType(name='int'), - doc='all builtin geoms, with quality from model', - ), - StructFieldDecl( - name='rangeFont', - type=ValueType(name='int'), - doc='all characters in font', - ), - StructFieldDecl( - name='nskin', - type=ValueType(name='int'), - doc='number of skins', - ), - StructFieldDecl( - name='skinvertVBO', - type=PointerType( - inner_type=ValueType(name='unsigned int'), - ), - doc='skin vertex position VBOs (nskin)', - ), - StructFieldDecl( - name='skinnormalVBO', - type=PointerType( - inner_type=ValueType(name='unsigned int'), - ), - doc='skin vertex normal VBOs (nskin)', - ), - StructFieldDecl( - name='skintexcoordVBO', - type=PointerType( - inner_type=ValueType(name='unsigned int'), - ), - doc='skin vertex texture coordinate VBOs (nskin)', - ), - StructFieldDecl( - name='skinfaceVBO', - type=PointerType( - inner_type=ValueType(name='unsigned int'), - ), - doc='skin face index VBOs (nskin)', - ), - StructFieldDecl( - name='charWidth', - type=ArrayType( - inner_type=ValueType(name='int'), - extents=(127,), - ), - doc='character widths: normal and shadow', - ), - StructFieldDecl( - name='charWidthBig', - type=ArrayType( - inner_type=ValueType(name='int'), - extents=(127,), - ), - doc='character widths: big', - ), - StructFieldDecl( - name='charHeight', - type=ValueType(name='int'), - doc='character heights: normal and shadow', - ), - StructFieldDecl( - name='charHeightBig', - type=ValueType(name='int'), - doc='character heights: big', - ), - StructFieldDecl( - name='glInitialized', - type=ValueType(name='int'), - doc='is OpenGL initialized', - ), - StructFieldDecl( - name='windowAvailable', - type=ValueType(name='int'), - doc='is default/window framebuffer available', - ), - StructFieldDecl( - name='windowSamples', - type=ValueType(name='int'), - doc='number of samples for default/window framebuffer', - ), - StructFieldDecl( - name='windowStereo', - type=ValueType(name='int'), - doc='is stereo available for default/window framebuffer', - ), - StructFieldDecl( - name='windowDoublebuffer', - type=ValueType(name='int'), - doc='is default/window framebuffer double buffered', - ), - StructFieldDecl( - name='currentBuffer', - type=ValueType(name='int'), - doc='currently active framebuffer: mjFB_WINDOW or mjFB_OFFSCREEN', # pylint: disable=line-too-long - ), - StructFieldDecl( - name='readPixelFormat', - type=ValueType(name='int'), - doc='default color pixel format for mjr_readPixels', - ), - StructFieldDecl( - name='readDepthMap', - type=ValueType(name='int'), - doc='depth mapping: mjDEPTH_ZERONEAR or mjDEPTH_ZEROFAR', - ), - ), - )), ('mjsElement', StructDecl( name='mjsElement', @@ -10698,6 +9253,1451 @@ STRUCTS: Mapping[str, StructDecl] = dict([ ), ), )), + ('mjvPerturb', + StructDecl( + name='mjvPerturb', + declname='struct mjvPerturb_', + fields=( + StructFieldDecl( + name='select', + type=ValueType(name='int'), + doc='selected body id; non-positive: none', + ), + StructFieldDecl( + name='flexselect', + type=ValueType(name='int'), + doc='selected flex id; negative: none', + ), + StructFieldDecl( + name='skinselect', + type=ValueType(name='int'), + doc='selected skin id; negative: none', + ), + StructFieldDecl( + name='active', + type=ValueType(name='int'), + doc='perturbation bitmask (mjtPertBit)', + ), + StructFieldDecl( + name='active2', + type=ValueType(name='int'), + doc='secondary perturbation bitmask (mjtPertBit)', + ), + StructFieldDecl( + name='refpos', + type=ArrayType( + inner_type=ValueType(name='mjtNum'), + extents=(3,), + ), + doc='reference position for selected object', + ), + StructFieldDecl( + name='refquat', + type=ArrayType( + inner_type=ValueType(name='mjtNum'), + extents=(4,), + ), + doc='reference orientation for selected object', + ), + StructFieldDecl( + name='refselpos', + type=ArrayType( + inner_type=ValueType(name='mjtNum'), + extents=(3,), + ), + doc='reference position for selection point', + ), + StructFieldDecl( + name='localpos', + type=ArrayType( + inner_type=ValueType(name='mjtNum'), + extents=(3,), + ), + doc='selection point in object coordinates', + ), + StructFieldDecl( + name='localmass', + type=ValueType(name='mjtNum'), + doc='spatial inertia at selection point', + ), + StructFieldDecl( + name='scale', + type=ValueType(name='mjtNum'), + doc='relative mouse motion-to-space scaling (set by initPerturb)', # pylint: disable=line-too-long + ), + ), + )), + ('mjvCamera', + StructDecl( + name='mjvCamera', + declname='struct mjvCamera_', + fields=( + StructFieldDecl( + name='type', + type=ValueType(name='int'), + doc='camera type (mjtCamera)', + ), + StructFieldDecl( + name='fixedcamid', + type=ValueType(name='int'), + doc='fixed camera id', + ), + StructFieldDecl( + name='trackbodyid', + type=ValueType(name='int'), + doc='body id to track', + ), + StructFieldDecl( + name='lookat', + type=ArrayType( + inner_type=ValueType(name='mjtNum'), + extents=(3,), + ), + doc='lookat point', + ), + StructFieldDecl( + name='distance', + type=ValueType(name='mjtNum'), + doc='distance to lookat point or tracked body', + ), + StructFieldDecl( + name='azimuth', + type=ValueType(name='mjtNum'), + doc='camera azimuth (deg)', + ), + StructFieldDecl( + name='elevation', + type=ValueType(name='mjtNum'), + doc='camera elevation (deg)', + ), + StructFieldDecl( + name='orthographic', + type=ValueType(name='int'), + doc='0: perspective; 1: orthographic', + ), + ), + )), + ('mjvGLCamera', + StructDecl( + name='mjvGLCamera', + declname='struct mjvGLCamera_', + fields=( + StructFieldDecl( + name='pos', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(3,), + ), + doc='position', + ), + StructFieldDecl( + name='forward', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(3,), + ), + doc='forward direction', + ), + StructFieldDecl( + name='up', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(3,), + ), + doc='up direction', + ), + StructFieldDecl( + name='frustum_center', + type=ValueType(name='float'), + doc='hor. center (left,right set to match aspect)', + ), + StructFieldDecl( + name='frustum_width', + type=ValueType(name='float'), + doc='width (not used for rendering)', + ), + StructFieldDecl( + name='frustum_bottom', + type=ValueType(name='float'), + doc='bottom', + ), + StructFieldDecl( + name='frustum_top', + type=ValueType(name='float'), + doc='top', + ), + StructFieldDecl( + name='frustum_near', + type=ValueType(name='float'), + doc='near', + ), + StructFieldDecl( + name='frustum_far', + type=ValueType(name='float'), + doc='far', + ), + StructFieldDecl( + name='orthographic', + type=ValueType(name='int'), + doc='0: perspective; 1: orthographic', + ), + ), + )), + ('mjvGeom', + StructDecl( + name='mjvGeom', + declname='struct mjvGeom_', + fields=( + StructFieldDecl( + name='type', + type=ValueType(name='int'), + doc='geom type (mjtGeom)', + ), + StructFieldDecl( + name='dataid', + type=ValueType(name='int'), + doc='mesh, hfield or plane id; -1: none; mesh: 2*id or 2*id+1 (hull)', # pylint: disable=line-too-long + ), + StructFieldDecl( + name='objtype', + type=ValueType(name='int'), + doc='mujoco object type; mjOBJ_UNKNOWN for decor', + ), + StructFieldDecl( + name='objid', + type=ValueType(name='int'), + doc='mujoco object id; -1 for decor', + ), + StructFieldDecl( + name='category', + type=ValueType(name='int'), + doc='visual category', + ), + StructFieldDecl( + name='matid', + type=ValueType(name='int'), + doc='material id; -1: no textured material', + ), + StructFieldDecl( + name='texcoord', + type=ValueType(name='int'), + doc='mesh or flex geom has texture coordinates', + ), + StructFieldDecl( + name='segid', + type=ValueType(name='int'), + doc='segmentation id; -1: not shown', + ), + StructFieldDecl( + name='size', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(3,), + ), + doc='size parameters', + ), + StructFieldDecl( + name='pos', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(3,), + ), + doc='Cartesian position', + ), + StructFieldDecl( + name='mat', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(9,), + ), + doc='Cartesian orientation', + ), + StructFieldDecl( + name='rgba', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(4,), + ), + doc='color and transparency', + ), + StructFieldDecl( + name='emission', + type=ValueType(name='float'), + doc='emission coef', + ), + StructFieldDecl( + name='specular', + type=ValueType(name='float'), + doc='specular coef', + ), + StructFieldDecl( + name='shininess', + type=ValueType(name='float'), + doc='shininess coef', + ), + StructFieldDecl( + name='reflectance', + type=ValueType(name='float'), + doc='reflectance coef', + ), + StructFieldDecl( + name='label', + type=ArrayType( + inner_type=ValueType(name='char'), + extents=(100,), + ), + doc='text label', + ), + StructFieldDecl( + name='camdist', + type=ValueType(name='float'), + doc='distance to camera (used by sorter)', + ), + StructFieldDecl( + name='modelrbound', + type=ValueType(name='float'), + doc='geom rbound from model, 0 if not model geom', + ), + StructFieldDecl( + name='transparent', + type=ValueType(name='mjtByte'), + doc='treat geom as transparent', + ), + ), + )), + ('mjvLight', + StructDecl( + name='mjvLight', + declname='struct mjvLight_', + fields=( + StructFieldDecl( + name='id', + type=ValueType(name='int'), + doc='light id, -1 for headlight', + ), + StructFieldDecl( + name='pos', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(3,), + ), + doc='position rel. to body frame', + ), + StructFieldDecl( + name='dir', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(3,), + ), + doc='direction rel. to body frame', + ), + StructFieldDecl( + name='type', + type=ValueType(name='int'), + doc='type (mjtLightType)', + ), + StructFieldDecl( + name='texid', + type=ValueType(name='int'), + doc='texture id for image lights', + ), + StructFieldDecl( + name='attenuation', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(3,), + ), + doc='OpenGL attenuation (quadratic model)', + ), + StructFieldDecl( + name='cutoff', + type=ValueType(name='float'), + doc='OpenGL cutoff', + ), + StructFieldDecl( + name='exponent', + type=ValueType(name='float'), + doc='OpenGL exponent', + ), + StructFieldDecl( + name='ambient', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(3,), + ), + doc='ambient rgb (alpha=1)', + ), + StructFieldDecl( + name='diffuse', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(3,), + ), + doc='diffuse rgb (alpha=1)', + ), + StructFieldDecl( + name='specular', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(3,), + ), + doc='specular rgb (alpha=1)', + ), + StructFieldDecl( + name='headlight', + type=ValueType(name='mjtByte'), + doc='headlight', + ), + StructFieldDecl( + name='castshadow', + type=ValueType(name='mjtByte'), + doc='does light cast shadows', + ), + StructFieldDecl( + name='bulbradius', + type=ValueType(name='float'), + doc='bulb radius for soft shadows', + ), + StructFieldDecl( + name='intensity', + type=ValueType(name='float'), + doc='intensity, in candelas', + ), + StructFieldDecl( + name='range', + type=ValueType(name='float'), + doc='range of effectiveness', + ), + ), + )), + ('mjvOption', + StructDecl( + name='mjvOption', + declname='struct mjvOption_', + fields=( + StructFieldDecl( + name='label', + type=ValueType(name='int'), + doc='what objects to label (mjtLabel)', + ), + StructFieldDecl( + name='frame', + type=ValueType(name='int'), + doc='which frame to show (mjtFrame)', + ), + StructFieldDecl( + name='geomgroup', + type=ArrayType( + inner_type=ValueType(name='mjtByte'), + extents=(6,), + ), + doc='geom visualization by group', + ), + StructFieldDecl( + name='sitegroup', + type=ArrayType( + inner_type=ValueType(name='mjtByte'), + extents=(6,), + ), + doc='site visualization by group', + ), + StructFieldDecl( + name='jointgroup', + type=ArrayType( + inner_type=ValueType(name='mjtByte'), + extents=(6,), + ), + doc='joint visualization by group', + ), + StructFieldDecl( + name='tendongroup', + type=ArrayType( + inner_type=ValueType(name='mjtByte'), + extents=(6,), + ), + doc='tendon visualization by group', + ), + StructFieldDecl( + name='actuatorgroup', + type=ArrayType( + inner_type=ValueType(name='mjtByte'), + extents=(6,), + ), + doc='actuator visualization by group', + ), + StructFieldDecl( + name='flexgroup', + type=ArrayType( + inner_type=ValueType(name='mjtByte'), + extents=(6,), + ), + doc='flex visualization by group', + ), + StructFieldDecl( + name='skingroup', + type=ArrayType( + inner_type=ValueType(name='mjtByte'), + extents=(6,), + ), + doc='skin visualization by group', + ), + StructFieldDecl( + name='flags', + type=ArrayType( + inner_type=ValueType(name='mjtByte'), + extents=(31,), + ), + doc='visualization flags (indexed by mjtVisFlag)', + ), + StructFieldDecl( + name='bvh_depth', + type=ValueType(name='int'), + doc='depth of the bounding volume hierarchy to be visualized', + ), + StructFieldDecl( + name='flex_layer', + type=ValueType(name='int'), + doc='element layer to be visualized for 3D flex', + ), + ), + )), + ('mjvScene', + StructDecl( + name='mjvScene', + declname='struct mjvScene_', + fields=( + StructFieldDecl( + name='maxgeom', + type=ValueType(name='int'), + doc='size of allocated geom buffer', + ), + StructFieldDecl( + name='ngeom', + type=ValueType(name='int'), + doc='number of geoms currently in buffer', + ), + StructFieldDecl( + name='geoms', + type=PointerType( + inner_type=ValueType(name='mjvGeom'), + ), + doc='buffer for geoms (ngeom)', + ), + StructFieldDecl( + name='geomorder', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='buffer for ordering geoms by distance to camera (ngeom)', + ), + StructFieldDecl( + name='nflex', + type=ValueType(name='int'), + doc='number of flexes', + ), + StructFieldDecl( + name='flexedgeadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='address of flex edges (nflex)', + ), + StructFieldDecl( + name='flexedgenum', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='number of edges in flex (nflex)', + ), + StructFieldDecl( + name='flexvertadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='address of flex vertices (nflex)', + ), + StructFieldDecl( + name='flexvertnum', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='number of vertices in flex (nflex)', + ), + StructFieldDecl( + name='flexfaceadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='address of flex faces (nflex)', + ), + StructFieldDecl( + name='flexfacenum', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='number of flex faces allocated (nflex)', + ), + StructFieldDecl( + name='flexfaceused', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='number of flex faces currently in use (nflex)', + ), + StructFieldDecl( + name='flexedge', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='flex edge data (2*nflexedge)', + ), + StructFieldDecl( + name='flexvert', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='flex vertices (3*nflexvert)', + ), + StructFieldDecl( + name='flexface', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='flex faces vertices (9*sum(flexfacenum))', + ), + StructFieldDecl( + name='flexnormal', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='flex face normals (9*sum(flexfacenum))', + ), + StructFieldDecl( + name='flextexcoord', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='flex face texture coordinates (6*sum(flexfacenum))', + ), + StructFieldDecl( + name='flexvertopt', + type=ValueType(name='mjtByte'), + doc='copy of mjVIS_FLEXVERT mjvOption flag', + ), + StructFieldDecl( + name='flexedgeopt', + type=ValueType(name='mjtByte'), + doc='copy of mjVIS_FLEXEDGE mjvOption flag', + ), + StructFieldDecl( + name='flexfaceopt', + type=ValueType(name='mjtByte'), + doc='copy of mjVIS_FLEXFACE mjvOption flag', + ), + StructFieldDecl( + name='flexskinopt', + type=ValueType(name='mjtByte'), + doc='copy of mjVIS_FLEXSKIN mjvOption flag', + ), + StructFieldDecl( + name='nskin', + type=ValueType(name='int'), + doc='number of skins', + ), + StructFieldDecl( + name='skinfacenum', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='number of faces in skin (nskin)', + ), + StructFieldDecl( + name='skinvertadr', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='address of skin vertices (nskin)', + ), + StructFieldDecl( + name='skinvertnum', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='number of vertices in skin (nskin)', + ), + StructFieldDecl( + name='skinvert', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='skin vertex data (3*nskinvert)', + ), + StructFieldDecl( + name='skinnormal', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='skin normal data (3*nskinvert)', + ), + StructFieldDecl( + name='nlight', + type=ValueType(name='int'), + doc='number of lights currently in buffer', + ), + StructFieldDecl( + name='lights', + type=ArrayType( + inner_type=ValueType(name='mjvLight'), + extents=(100,), + ), + doc='buffer for lights (nlight)', + ), + StructFieldDecl( + name='camera', + type=ArrayType( + inner_type=ValueType(name='mjvGLCamera'), + extents=(2,), + ), + doc='left and right camera', + ), + StructFieldDecl( + name='enabletransform', + type=ValueType(name='mjtByte'), + doc='enable model transformation', + ), + StructFieldDecl( + name='translate', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(3,), + ), + doc='model translation', + ), + StructFieldDecl( + name='rotate', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(4,), + ), + doc='model quaternion rotation', + ), + StructFieldDecl( + name='scale', + type=ValueType(name='float'), + doc='model scaling', + ), + StructFieldDecl( + name='stereo', + type=ValueType(name='int'), + doc='stereoscopic rendering (mjtStereo)', + ), + StructFieldDecl( + name='flags', + type=ArrayType( + inner_type=ValueType(name='mjtByte'), + extents=(10,), + ), + doc='rendering flags (indexed by mjtRndFlag)', + ), + StructFieldDecl( + name='framewidth', + type=ValueType(name='int'), + doc='frame pixel width; 0: disable framing', + ), + StructFieldDecl( + name='framergb', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(3,), + ), + doc='frame color', + ), + StructFieldDecl( + name='status', + type=ValueType(name='int'), + doc='status; 0: ok, 1: geoms exhausted', + ), + ), + )), + ('mjvFigure', + StructDecl( + name='mjvFigure', + declname='struct mjvFigure_', + fields=( + StructFieldDecl( + name='flg_legend', + type=ValueType(name='int'), + doc='show legend', + ), + StructFieldDecl( + name='flg_ticklabel', + type=ArrayType( + inner_type=ValueType(name='int'), + extents=(2,), + ), + doc='show grid tick labels (x,y)', + ), + StructFieldDecl( + name='flg_extend', + type=ValueType(name='int'), + doc='automatically extend axis ranges to fit data', + ), + StructFieldDecl( + name='flg_barplot', + type=ValueType(name='int'), + doc='isolated line segments (i.e. GL_LINES)', + ), + StructFieldDecl( + name='flg_selection', + type=ValueType(name='int'), + doc='vertical selection line', + ), + StructFieldDecl( + name='flg_symmetric', + type=ValueType(name='int'), + doc='symmetric y-axis', + ), + StructFieldDecl( + name='linewidth', + type=ValueType(name='float'), + doc='line width', + ), + StructFieldDecl( + name='gridwidth', + type=ValueType(name='float'), + doc='grid line width', + ), + StructFieldDecl( + name='gridsize', + type=ArrayType( + inner_type=ValueType(name='int'), + extents=(2,), + ), + doc='number of grid points in (x,y)', + ), + StructFieldDecl( + name='gridrgb', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(3,), + ), + doc='grid line rgb', + ), + StructFieldDecl( + name='figurergba', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(4,), + ), + doc='figure color and alpha', + ), + StructFieldDecl( + name='panergba', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(4,), + ), + doc='pane color and alpha', + ), + StructFieldDecl( + name='legendrgba', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(4,), + ), + doc='legend color and alpha', + ), + StructFieldDecl( + name='textrgb', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(3,), + ), + doc='text color', + ), + StructFieldDecl( + name='linergb', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(100, 3), + ), + doc='line colors', + ), + StructFieldDecl( + name='range', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(2, 2), + ), + doc='axis ranges; (min>=max) automatic', + ), + StructFieldDecl( + name='xformat', + type=ArrayType( + inner_type=ValueType(name='char'), + extents=(20,), + ), + doc='x-tick label format for sprintf', + ), + StructFieldDecl( + name='yformat', + type=ArrayType( + inner_type=ValueType(name='char'), + extents=(20,), + ), + doc='y-tick label format for sprintf', + ), + StructFieldDecl( + name='minwidth', + type=ArrayType( + inner_type=ValueType(name='char'), + extents=(20,), + ), + doc='string used to determine min y-tick width', + ), + StructFieldDecl( + name='title', + type=ArrayType( + inner_type=ValueType(name='char'), + extents=(1000,), + ), + doc='figure title; subplots separated with 2+ spaces', + ), + StructFieldDecl( + name='xlabel', + type=ArrayType( + inner_type=ValueType(name='char'), + extents=(100,), + ), + doc='x-axis label', + ), + StructFieldDecl( + name='linename', + type=ArrayType( + inner_type=ValueType(name='char'), + extents=(100, 100), + ), + doc='line names for legend', + ), + StructFieldDecl( + name='legendoffset', + type=ValueType(name='int'), + doc='number of lines to offset legend', + ), + StructFieldDecl( + name='subplot', + type=ValueType(name='int'), + doc='selected subplot (for title rendering)', + ), + StructFieldDecl( + name='highlight', + type=ArrayType( + inner_type=ValueType(name='int'), + extents=(2,), + ), + doc='if point is in legend rect, highlight line', + ), + StructFieldDecl( + name='highlightid', + type=ValueType(name='int'), + doc='if id>=0 and no point, highlight id', + ), + StructFieldDecl( + name='selection', + type=ValueType(name='float'), + doc='selection line x-value', + ), + StructFieldDecl( + name='linepnt', + type=ArrayType( + inner_type=ValueType(name='int'), + extents=(100,), + ), + doc='number of points in line; (0) disable', + ), + StructFieldDecl( + name='linedata', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(100, 2002), + ), + doc='line data (x,y)', + ), + StructFieldDecl( + name='xaxispixel', + type=ArrayType( + inner_type=ValueType(name='int'), + extents=(2,), + ), + doc='range of x-axis in pixels', + ), + StructFieldDecl( + name='yaxispixel', + type=ArrayType( + inner_type=ValueType(name='int'), + extents=(2,), + ), + doc='range of y-axis in pixels', + ), + StructFieldDecl( + name='xaxisdata', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(2,), + ), + doc='range of x-axis in data units', + ), + StructFieldDecl( + name='yaxisdata', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(2,), + ), + doc='range of y-axis in data units', + ), + ), + )), + ('mjSDF', + StructDecl( + name='mjSDF', + declname='struct mjSDF_', + fields=( + StructFieldDecl( + name='plugin', + type=PointerType( + inner_type=PointerType( + inner_type=ValueType(name='mjpPlugin', is_const=True), + ), + ), + doc='', + ), + StructFieldDecl( + name='id', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='type', + type=ValueType(name='mjtSDFType'), + doc='', + ), + StructFieldDecl( + name='relpos', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='relmat', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='', + ), + StructFieldDecl( + name='geomtype', + type=PointerType( + inner_type=ValueType(name='mjtGeom'), + ), + doc='', + ), + ), + )), + ('mjrRect', + StructDecl( + name='mjrRect', + declname='struct mjrRect_', + fields=( + StructFieldDecl( + name='left', + type=ValueType(name='int'), + doc='left (usually 0)', + ), + StructFieldDecl( + name='bottom', + type=ValueType(name='int'), + doc='bottom (usually 0)', + ), + StructFieldDecl( + name='width', + type=ValueType(name='int'), + doc='width (usually buffer width)', + ), + StructFieldDecl( + name='height', + type=ValueType(name='int'), + doc='height (usually buffer height)', + ), + ), + )), + ('mjrContext', + StructDecl( + name='mjrContext', + declname='struct mjrContext_', + fields=( + StructFieldDecl( + name='lineWidth', + type=ValueType(name='float'), + doc='line width for wireframe rendering', + ), + StructFieldDecl( + name='shadowClip', + type=ValueType(name='float'), + doc='clipping radius for directional lights', + ), + StructFieldDecl( + name='shadowScale', + type=ValueType(name='float'), + doc='fraction of light cutoff for spot lights', + ), + StructFieldDecl( + name='fogStart', + type=ValueType(name='float'), + doc='fog start = stat.extent * vis.map.fogstart', + ), + StructFieldDecl( + name='fogEnd', + type=ValueType(name='float'), + doc='fog end = stat.extent * vis.map.fogend', + ), + StructFieldDecl( + name='fogRGBA', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(4,), + ), + doc='fog rgba', + ), + StructFieldDecl( + name='shadowSize', + type=ValueType(name='int'), + doc='size of shadow map texture', + ), + StructFieldDecl( + name='offWidth', + type=ValueType(name='int'), + doc='width of offscreen buffer', + ), + StructFieldDecl( + name='offHeight', + type=ValueType(name='int'), + doc='height of offscreen buffer', + ), + StructFieldDecl( + name='offSamples', + type=ValueType(name='int'), + doc='number of offscreen buffer multisamples', + ), + StructFieldDecl( + name='fontScale', + type=ValueType(name='int'), + doc='font scale', + ), + StructFieldDecl( + name='auxWidth', + type=ArrayType( + inner_type=ValueType(name='int'), + extents=(10,), + ), + doc='auxiliary buffer width', + ), + StructFieldDecl( + name='auxHeight', + type=ArrayType( + inner_type=ValueType(name='int'), + extents=(10,), + ), + doc='auxiliary buffer height', + ), + StructFieldDecl( + name='auxSamples', + type=ArrayType( + inner_type=ValueType(name='int'), + extents=(10,), + ), + doc='auxiliary buffer multisamples', + ), + StructFieldDecl( + name='offFBO', + type=ValueType(name='unsigned int'), + doc='offscreen framebuffer object', + ), + StructFieldDecl( + name='offFBO_r', + type=ValueType(name='unsigned int'), + doc='offscreen framebuffer for resolving multisamples', + ), + StructFieldDecl( + name='offColor', + type=ValueType(name='unsigned int'), + doc='offscreen color buffer', + ), + StructFieldDecl( + name='offColor_r', + type=ValueType(name='unsigned int'), + doc='offscreen color buffer for resolving multisamples', + ), + StructFieldDecl( + name='offDepthStencil', + type=ValueType(name='unsigned int'), + doc='offscreen depth and stencil buffer', + ), + StructFieldDecl( + name='offDepthStencil_r', + type=ValueType(name='unsigned int'), + doc='offscreen depth and stencil buffer for multisamples', + ), + StructFieldDecl( + name='shadowFBO', + type=ValueType(name='unsigned int'), + doc='shadow map framebuffer object', + ), + StructFieldDecl( + name='shadowTex', + type=ValueType(name='unsigned int'), + doc='shadow map texture', + ), + StructFieldDecl( + name='auxFBO', + type=ArrayType( + inner_type=ValueType(name='unsigned int'), + extents=(10,), + ), + doc='auxiliary framebuffer object', + ), + StructFieldDecl( + name='auxFBO_r', + type=ArrayType( + inner_type=ValueType(name='unsigned int'), + extents=(10,), + ), + doc='auxiliary framebuffer object for resolving', + ), + StructFieldDecl( + name='auxColor', + type=ArrayType( + inner_type=ValueType(name='unsigned int'), + extents=(10,), + ), + doc='auxiliary color buffer', + ), + StructFieldDecl( + name='auxColor_r', + type=ArrayType( + inner_type=ValueType(name='unsigned int'), + extents=(10,), + ), + doc='auxiliary color buffer for resolving', + ), + StructFieldDecl( + name='mat_texid', + type=ArrayType( + inner_type=ValueType(name='int'), + extents=(10000,), + ), + doc='material texture ids (-1: no texture)', + ), + StructFieldDecl( + name='mat_texuniform', + type=ArrayType( + inner_type=ValueType(name='int'), + extents=(1000,), + ), + doc='uniform cube mapping', + ), + StructFieldDecl( + name='mat_texrepeat', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(2000,), + ), + doc='texture repetition for 2d mapping', + ), + StructFieldDecl( + name='ntexture', + type=ValueType(name='int'), + doc='number of allocated textures', + ), + StructFieldDecl( + name='textureType', + type=ArrayType( + inner_type=ValueType(name='int'), + extents=(1000,), + ), + doc='type of texture (mjtTexture) (ntexture)', + ), + StructFieldDecl( + name='texture', + type=ArrayType( + inner_type=ValueType(name='unsigned int'), + extents=(1000,), + ), + doc='texture names', + ), + StructFieldDecl( + name='basePlane', + type=ValueType(name='unsigned int'), + doc='all planes from model', + ), + StructFieldDecl( + name='baseMesh', + type=ValueType(name='unsigned int'), + doc='all meshes from model', + ), + StructFieldDecl( + name='baseHField', + type=ValueType(name='unsigned int'), + doc='all height fields from model', + ), + StructFieldDecl( + name='baseBuiltin', + type=ValueType(name='unsigned int'), + doc='all builtin geoms, with quality from model', + ), + StructFieldDecl( + name='baseFontNormal', + type=ValueType(name='unsigned int'), + doc='normal font', + ), + StructFieldDecl( + name='baseFontShadow', + type=ValueType(name='unsigned int'), + doc='shadow font', + ), + StructFieldDecl( + name='baseFontBig', + type=ValueType(name='unsigned int'), + doc='big font', + ), + StructFieldDecl( + name='rangePlane', + type=ValueType(name='int'), + doc='all planes from model', + ), + StructFieldDecl( + name='rangeMesh', + type=ValueType(name='int'), + doc='all meshes from model', + ), + StructFieldDecl( + name='rangeHField', + type=ValueType(name='int'), + doc='all hfields from model', + ), + StructFieldDecl( + name='rangeBuiltin', + type=ValueType(name='int'), + doc='all builtin geoms, with quality from model', + ), + StructFieldDecl( + name='rangeFont', + type=ValueType(name='int'), + doc='all characters in font', + ), + StructFieldDecl( + name='nskin', + type=ValueType(name='int'), + doc='number of skins', + ), + StructFieldDecl( + name='skinvertVBO', + type=PointerType( + inner_type=ValueType(name='unsigned int'), + ), + doc='skin vertex position VBOs (nskin)', + ), + StructFieldDecl( + name='skinnormalVBO', + type=PointerType( + inner_type=ValueType(name='unsigned int'), + ), + doc='skin vertex normal VBOs (nskin)', + ), + StructFieldDecl( + name='skintexcoordVBO', + type=PointerType( + inner_type=ValueType(name='unsigned int'), + ), + doc='skin vertex texture coordinate VBOs (nskin)', + ), + StructFieldDecl( + name='skinfaceVBO', + type=PointerType( + inner_type=ValueType(name='unsigned int'), + ), + doc='skin face index VBOs (nskin)', + ), + StructFieldDecl( + name='charWidth', + type=ArrayType( + inner_type=ValueType(name='int'), + extents=(127,), + ), + doc='character widths: normal and shadow', + ), + StructFieldDecl( + name='charWidthBig', + type=ArrayType( + inner_type=ValueType(name='int'), + extents=(127,), + ), + doc='character widths: big', + ), + StructFieldDecl( + name='charHeight', + type=ValueType(name='int'), + doc='character heights: normal and shadow', + ), + StructFieldDecl( + name='charHeightBig', + type=ValueType(name='int'), + doc='character heights: big', + ), + StructFieldDecl( + name='glInitialized', + type=ValueType(name='int'), + doc='is OpenGL initialized', + ), + StructFieldDecl( + name='windowAvailable', + type=ValueType(name='int'), + doc='is default/window framebuffer available', + ), + StructFieldDecl( + name='windowSamples', + type=ValueType(name='int'), + doc='number of samples for default/window framebuffer', + ), + StructFieldDecl( + name='windowStereo', + type=ValueType(name='int'), + doc='is stereo available for default/window framebuffer', + ), + StructFieldDecl( + name='windowDoublebuffer', + type=ValueType(name='int'), + doc='is default/window framebuffer double buffered', + ), + StructFieldDecl( + name='currentBuffer', + type=ValueType(name='int'), + doc='currently active framebuffer: mjFB_WINDOW or mjFB_OFFSCREEN', # pylint: disable=line-too-long + ), + StructFieldDecl( + name='readPixelFormat', + type=ValueType(name='int'), + doc='default color pixel format for mjr_readPixels', + ), + StructFieldDecl( + name='readDepthMap', + type=ValueType(name='int'), + doc='depth mapping: mjDEPTH_ZERONEAR or mjDEPTH_ZEROFAR', + ), + ), + )), ('mjuiState', StructDecl( name='mjuiState', diff --git a/python/mujoco/specs.cc b/python/mujoco/specs.cc index 535e2a2d..e3bfc7ef 100644 --- a/python/mujoco/specs.cc +++ b/python/mujoco/specs.cc @@ -281,8 +281,8 @@ PYBIND11_MODULE(_specs, m) { spec = LoadSpecFileImpl( filename, files, [&error](const char* filename, const mjVFS* vfs) { - return InterceptMjErrors(mj_parseXML)( - filename, vfs, error, sizeof(error)); + return InterceptMjErrors(mj_parse)( + filename, nullptr, vfs, error, sizeof(error)); }); if (!spec) { throw py::value_error(error); diff --git a/src/engine/engine_plugin.cc b/src/engine/engine_plugin.cc index 20a64bf1..f55eddb8 100644 --- a/src/engine/engine_plugin.cc +++ b/src/engine/engine_plugin.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -62,6 +63,16 @@ int strklen(const char* s) { return -1; } +// return filename extension +std::string getext(std::string_view filename) { + size_t dot = filename.find_last_of('.'); + + if (dot == std::string::npos) { + return ""; + } + return std::string(filename.substr(dot, filename.size() - dot)); +} + // copy a null-terminated string into a new heap-allocated char array managed by a unique_ptr std::unique_ptr CopyName(const char* s) { int len = strklen(s); @@ -265,6 +276,77 @@ bool GlobalTable::CopyObject(mjpResourceProvider& dst, cons return true; } +template <> +const char* GlobalTable::HumanReadableTypeName() { + return "resource decoder"; +} + +template <> +std::string_view GlobalTable::ObjectKey(const mjpDecoder& decoder) { + // When registering decoders, if the user provides both a content type and an extension we add two + // entries to the table. One with content_type set and extension unset, and one with the opposite. + // This means that within a vector, we will only ever have either content_type or extension. + if (decoder.content_type) { + if (int len = strklen(decoder.content_type); len != -1) { + return std::string_view(decoder.content_type, len); + } + } + return std::string_view(decoder.extension, strklen(decoder.extension)); +} + +// return true if two resource providers are identical +template <> +bool GlobalTable::ObjectEqual(const mjpDecoder& d1, const mjpDecoder& d2) { + // check if two resource providers are identical + if (!(CaseInsensitiveEqual(d1.content_type, d2.content_type) && + CaseInsensitiveEqual(d1.extension, d2.extension) && + d1.decode == d2.decode && + d1.can_decode == d2.can_decode)) { + return false; + } + return true; +} + +template <> +bool GlobalTable::CopyObject(mjpDecoder& dst, const mjpDecoder& src, ErrorMessage& err) { + // Just a list of pointers so copy directly. + dst = src; + dst.content_type = nullptr; + dst.extension = nullptr; + + if (src.content_type) { + std::unique_ptr content_type = CopyName(src.content_type); + if (!content_type) { + if (strklen(src.content_type) == -1) { + std::snprintf(err, sizeof(err), + "decoder->content_type length exceeds the maximum limit of %d", kMaxNameLength); + } else { + std::snprintf(err, sizeof(err), "failed to allocate memory for decoder content_type"); + } + return false; + } + + dst.content_type = content_type.release(); + } + + if (src.extension) { + std::unique_ptr extension = CopyName(src.extension); + if (!extension) { + if (strklen(src.extension) == -1) { + std::snprintf(err, sizeof(err), + "decoder->extension length exceeds the maximum limit of %d", kMaxNameLength); + } else { + std::snprintf(err, sizeof(err), "failed to allocate memory for decoder extension"); + } + return false; + } + + dst.extension = extension.release(); + } + + return true; +} + // globally register a plugin (thread-safe), return new slot id int mjp_registerPlugin(const mjpPlugin* plugin) { if (!plugin->name) { @@ -382,6 +464,71 @@ const mjpResourceProvider* mjp_getResourceProviderAtSlot(int slot) { return GlobalTable::GetSingleton().GetAtSlot(slot - 1); } +// register a resource decoder +void mjp_registerDecoder(const mjpDecoder* decoder) { + if (!decoder->decode || !decoder->can_decode) { + mju_warning("decoder must provide decode and can_decode callbacks."); + return; + } + + if (!decoder->content_type && !decoder->extension) { + mju_warning("decoder must provide content_type and/or extensions."); + return; + } + + mjpDecoder decoder_copy = *decoder; + + // Register with content_type + if (decoder->content_type) { + decoder_copy.extension = nullptr; + GlobalTable::GetSingleton().AppendIfUnique(decoder_copy); + } + + // Register with extensions + if (decoder->extension) { + decoder_copy.content_type = nullptr; + std::string extensions_str(decoder->extension); + std::stringstream ss(extensions_str); + std::string extension; + while (std::getline(ss, extension, '|')) { + if (!extension.empty()) { + decoder_copy.extension = extension.c_str(); + GlobalTable::GetSingleton().AppendIfUnique(decoder_copy); + } + } + } +} + +// set default resource decoder definition +void mjp_defaultDecoder(mjpDecoder* decoder) { + std::memset(decoder, 0, sizeof(*decoder)); +} + +// find a decoder that can process a given resource and content_type +const mjpDecoder* mjp_findDecoder(const mjResource* resource, const char* content_type) { + auto extension = getext(resource->name); + if (strklen(content_type) == -1 && extension.empty()) { + mju_warning("Must provide extension or content_type to mjp_findDecoder."); + return nullptr; + } + + if (strklen(content_type) > 0) { + auto* decoder = GlobalTable::GetSingleton().GetByKey(content_type, nullptr); + if (decoder && decoder->can_decode(resource)) { + return decoder; + } + } + + if (!extension.empty()) { + auto* decoder = GlobalTable::GetSingleton().GetByKey(extension.c_str(), nullptr); + if (decoder && decoder->can_decode(resource)) { + return decoder; + } + } + + return nullptr; +} + // load plugins from a dynamic library void mj_loadPluginLibrary(const char* path) { #if defined(_WIN32) || defined(__CYGWIN__) diff --git a/src/engine/engine_plugin.h b/src/engine/engine_plugin.h index d65b2cf9..74149686 100644 --- a/src/engine/engine_plugin.h +++ b/src/engine/engine_plugin.h @@ -62,6 +62,15 @@ MJAPI void mj_loadPluginLibrary(const char* path); // scan a directory and load all dynamic libraries MJAPI void mj_loadAllPluginLibraries(const char* directory, mjfPluginLibraryLoadCallback callback); +// registers a resource decoder +MJAPI void mjp_registerDecoder(const mjpDecoder* decoder); + +// set default decoder definition +MJAPI void mjp_defaultDecoder(mjpDecoder* decoder); + +// find a decoder that can process a given resource +MJAPI const mjpDecoder* mjp_findDecoder(const mjResource* resource, const char* content_type); + // ================================================================================================= // MuJoCo-internal functions beyond this point. // "Unsafe" suffix indicates that improper use of these functions may result in data races. diff --git a/src/user/user_api.cc b/src/user/user_api.cc index 1dead444..8393e101 100644 --- a/src/user/user_api.cc +++ b/src/user/user_api.cc @@ -31,6 +31,7 @@ #include "user/user_cache.h" #include "user/user_model.h" #include "user/user_objects.h" +#include "user/user_resource.h" #include "user/user_util.h" namespace { @@ -63,7 +64,24 @@ mjSpec* mj_copySpec(const mjSpec* s) { return &modelC->spec; } +// parse file into spec +mjSpec* mj_parse(const char* filename, const char* content_type, + const mjVFS* vfs, char* error, int error_sz) { + // early exit for existing XML workflow + auto filepath = mujoco::user::FilePath(filename); + if (filepath.Ext() == ".xml" || (content_type && std::strcmp(content_type, "text/xml") == 0)) { + return mj_parseXML(filename, vfs, error, error_sz); + } + mjResource* resource = mju_openResource("", filename, vfs, error, error_sz); + if (!resource) { + mju_error("Could not load resource %s", filename); + } + + mjSpec* spec = mju_decodeResource(resource, content_type); + mju_closeResource(resource); + return spec; +} // compile model mjModel* mj_compile(mjSpec* s, const mjVFS* vfs) { diff --git a/src/user/user_resource.cc b/src/user/user_resource.cc index c66bfa87..7d07432b 100644 --- a/src/user/user_resource.cc +++ b/src/user/user_resource.cc @@ -24,7 +24,9 @@ #include #include #include +#include #include +#include #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) #include @@ -262,3 +264,18 @@ int mju_isModifiedResource(const mjResource* resource, const char* timestamp) { // fallback to OS filesystem return FileModified(resource, timestamp); } + +mjSpec* mju_decodeResource(mjResource* resource, const char* content_type) { + const mjpDecoder* decoder = nullptr; + if (content_type) { + decoder = mjp_findDecoder(resource, content_type); + } else { + decoder = mjp_findDecoder(resource, mjuu_extToContentType(resource->name).c_str()); + } + if (!decoder) { + mju_error("Could not find decoder for resource '%s'", resource->name); + } + + return decoder->decode(resource); +} + diff --git a/src/user/user_resource.h b/src/user/user_resource.h index 1d2093b6..fc8e6013 100644 --- a/src/user/user_resource.h +++ b/src/user/user_resource.h @@ -46,6 +46,10 @@ MJAPI void mju_getResourceDir(mjResource* resource, const char** dir, int* ndir) // return < 0 if the resource is older than the given timestamp MJAPI int mju_isModifiedResource(const mjResource* resource, const char* timestamp); +// given a resource, find its decoder and return the decoded spec +// the caller takes ownership of the spec and is responsible for cleaning it up +MJAPI mjSpec* mju_decodeResource(mjResource* resource, const char* content_type); + #ifdef __cplusplus } #endif diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc index cd3c2316..93411a8d 100644 --- a/src/xml/xml_native_reader.cc +++ b/src/xml/xml_native_reader.cc @@ -3404,25 +3404,24 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) { // model sub-element else if (name == "model") { - string content_type; - if (!ReadAttrTxt(elem, "content_type", content_type)) { - content_type = "text/xml"; - } + std::string content_type; + ReadAttrTxt(elem, "content_type", content_type); // parse the child mjSpec* child = nullptr; std::array error; auto filename = modelfiledir_ + ReadAttrFile(elem, "file", vfs).value(); - if (content_type == "text/xml") { - child = mj_parseXML(filename.c_str(), vfs, error.data(), error.size()); #ifdef mjUSEUSD - } else if (content_type == "text/usd") { + if (content_type == "text/usd") { child = mj_parseUSD(filename.c_str(), vfs, error.data(), error.size()); -#endif // mjUSEUSD } else { - throw mjXError(elem, "unsupported content_type: %s", content_type.c_str()); +#endif // mjUSEUSD + child = mj_parse(filename.c_str(), content_type.c_str(), vfs, + error.data(), error.size()); +#ifdef mjUSEUSD } +#endif // mjUSEUSD if (!child) { throw mjXError(elem, "could not parse model file with error: %s", error.data()); diff --git a/test/plugin/decoder/decoder_test.cc b/test/plugin/decoder/decoder_test.cc new file mode 100644 index 00000000..f7076dd8 --- /dev/null +++ b/test/plugin/decoder/decoder_test.cc @@ -0,0 +1,127 @@ +// Copyright 2025 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. + +// Tests for decoder plugins. + +#include + +#include +#include +#include +#include +#include +#include "test/fixture.h" + +namespace mujoco { +namespace { + +// A simple mjSpec with one body and one geom. +static mjSpec* MakeSimpleSpec() { + mjSpec* s = mj_makeSpec(); + mjsBody* world = mjs_findBody(s, "world"); + mjsBody* body = mjs_addBody(world, nullptr); + mjsGeom* geom = mjs_addGeom(body, nullptr); + geom->size[0] = 1.0; + geom->size[1] = 1.0; + geom->size[2] = 1.0; + return s; +} + +// Always returns a simple mjSpec, ignoring the resource. +mjSpec* FakeDecode(const mjResource* resource) { return MakeSimpleSpec(); } + +// Can decode any resource that has a .fakeformat extension. +int FakeCanDecode(const mjResource* resource) { + const char* ext = strrchr(resource->name, '.'); + if (ext) { + return strcmp(ext, ".fakeformat") == 0 || + strcmp(ext, ".alsoFakeFormat") == 0; + } + return 0; +} + +mjpDecoder FakeDecoder() { + mjpDecoder decoder; + mjp_defaultDecoder(&decoder); + decoder.content_type = "model/fakeformat"; + decoder.extension = ".fakeformat|.alsoFakeFormat"; + decoder.can_decode = FakeCanDecode; + decoder.decode = FakeDecode; + return decoder; +} + +using DecoderPluginTest = MujocoTest; + +TEST_F(DecoderPluginTest, CanDecode) { + mjpDecoder decoder = FakeDecoder(); + mjp_registerDecoder(&decoder); + + static constexpr char xml[] = R"( + + + + + + + + + + )"; + char error[1024]; + + // create VFS with the XML model and a dummy mesh + mjVFS vfs; + mj_defaultVFS(&vfs); + mj_addBufferVFS(&vfs, "model.xml", xml, strlen(xml)); + mj_addBufferVFS(&vfs, "dummy.fakeformat", "0 1 2", strlen("0 1 2")); + mj_addBufferVFS(&vfs, "dummy.alsoFakeFormat", "0 1 2", strlen("0 1 2")); + + // Check referencing a resource via XML invokes the decoder. + mjModel* model = mj_loadXML("model.xml", &vfs, error, sizeof(error)); + ASSERT_THAT(model, testing::NotNull()) << error; + EXPECT_EQ(model->nbody, 2); // world + included body + EXPECT_EQ(model->ngeom, 1); + mj_deleteModel(model); + + // Check mj_parse with extension .fakeformat + mjSpec* spec = + mj_parse("dummy.fakeformat", nullptr, &vfs, error, sizeof(error)); + model = mj_compile(spec, &vfs); + EXPECT_EQ(model->nbody, 2); // world + included body + EXPECT_EQ(model->ngeom, 1); + mj_deleteModel(model); + mj_deleteSpec(spec); + + // Check mj_parse with extension .alsoFakeFormat + spec = mj_parse("dummy.alsoFakeFormat", nullptr, &vfs, error, sizeof(error)); + model = mj_compile(spec, &vfs); + EXPECT_EQ(model->nbody, 2); // world + included body + EXPECT_EQ(model->ngeom, 1); + mj_deleteModel(model); + mj_deleteSpec(spec); + + // Check mj_parse with content_type + spec = mj_parse("dummy.fakeformat", "model/fakeformat", &vfs, error, + sizeof(error)); + model = mj_compile(spec, &vfs); + EXPECT_EQ(model->nbody, 2); // world + included body + EXPECT_EQ(model->ngeom, 1); + mj_deleteModel(model); + mj_deleteSpec(spec); + + mj_deleteVFS(&vfs); +} + +} // namespace +} // namespace mujoco