Track authored flags for global attributes

PiperOrigin-RevId: 931585539
Change-Id: Ifdc8c59de6c5a553daf6e0af09d8192aff6b0610
This commit is contained in:
Yuval Tassa
2026-06-13 02:57:52 -07:00
committed by Copybara-Service
parent 0ea9c7cb3a
commit 67a1ea6dca
21 changed files with 823 additions and 195 deletions
+10
View File
@@ -1420,6 +1420,16 @@ Compiler options.
.. mujoco-include:: mjsCompiler
.. _mjsAuthored:
mjsAuthored
~~~~~~~~~~~
Authored tracking bitmasks for ``mjModel`` structs.
.. mujoco-include:: mjsAuthored
.. _mjsBody:
mjsBody
+16
View File
@@ -1588,7 +1588,20 @@ typedef struct mjsCompiler_ { // compiler options
mjLROpt LRopt; // options for lengthrange computation
mjString* meshdir; // mesh and hfield directory
mjString* texturedir; // texture directory
uint64_t authored; // bitmask of authored compiler fields
} mjsCompiler;
typedef struct mjsAuthored_ { // authored tracking bitmasks for mjModel structs
uint64_t option; // authored mjOption fields
int disableflags; // individual authored disable flags
int enableflags; // individual authored enable flags
int disableactuator; // individual authored actuator groups
uint64_t visual_global; // authored visual.global fields
uint64_t visual_quality; // authored visual.quality fields
uint64_t visual_headlight; // authored visual.headlight fields
uint64_t visual_map; // authored visual.map fields
uint64_t visual_scale; // authored visual.scale fields
uint64_t visual_rgba; // authored visual.rgba fields
} mjsAuthored;
typedef struct mjSpec_ { // model specification
mjsElement* element; // element type
mjString* modelname; // model name
@@ -1625,6 +1638,9 @@ typedef struct mjSpec_ { // model specification
// other
mjtByte hasImplicitPluginElem; // already encountered an implicit plugin sensor/actuator
// authored tracking bitmasks for mjModel structs
mjsAuthored authored;
} mjSpec;
typedef struct mjsOrientation_ { // alternative orientation specifiers
mjtOrientation type; // active orientation specifier
+26
View File
@@ -20,19 +20,24 @@
#include <mujoco/mjplugin.h>
#include <mujoco/mjrender.h>
#include <mujoco/mjspec.h>
#include <mujoco/mjspecmacro.h>
#include <mujoco/mjtype.h>
#include <mujoco/mjui.h>
#include <mujoco/mjvisualize.h>
#include <mujoco/mjxmacro.h>
#if defined(__cplusplus)
#define MJ_ASSERT_SIZE(type, size) \
static_assert(sizeof(type) == (size), #type " must be " #size " bytes for MuJoCo ABI stability")
#define MJ_STATIC_ASSERT(expr) static_assert(expr)
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#define MJ_ASSERT_SIZE(type, size) \
_Static_assert(sizeof(type) == (size), #type " must be " #size " bytes for MuJoCo ABI stability")
#define MJ_STATIC_ASSERT(expr) _Static_assert(expr, #expr)
#else
#define MJ_ASSERT_SIZE(type, size) \
typedef char mj_assert_##type[sizeof(type) == (size) ? 1 : -1]
#define MJ_STATIC_ASSERT(expr)
#endif
// primitive types
@@ -125,6 +130,27 @@ MJ_ASSERT_SIZE(mjtSection, 4);
// mjplugin.h
MJ_ASSERT_SIZE(mjtPluginCapabilityBit, 4);
// authored bitmask field count assertions
// each authored bitmask is uint64_t, so each FIELDS macro must have <= 64 entries
#define X(type, name, dim) +1
#define XVEC(type, name, dim) +1
// mjsCompiler
MJ_STATIC_ASSERT((0 MJSCOMPILER_FIELDS) <= 64);
// mjOption and mjVisual
MJ_STATIC_ASSERT((0 MJOPTION_FIELDS) <= 64);
MJ_STATIC_ASSERT((0 MJVISUAL_GLOBAL_FIELDS) <= 64);
MJ_STATIC_ASSERT((0 MJVISUAL_QUALITY_FIELDS) <= 64);
MJ_STATIC_ASSERT((0 MJVISUAL_HEADLIGHT_FIELDS) <= 64);
MJ_STATIC_ASSERT((0 MJVISUAL_MAP_FIELDS) <= 64);
MJ_STATIC_ASSERT((0 MJVISUAL_SCALE_FIELDS) <= 64);
MJ_STATIC_ASSERT((0 MJVISUAL_RGBA_FIELDS) <= 64);
#undef X
#undef XVEC
#undef MJ_ASSERT_SIZE
#undef MJ_STATIC_ASSERT
#endif // MUJOCO_MJASSERT_H_
+18
View File
@@ -174,9 +174,24 @@ typedef struct mjsCompiler_ { // compiler options
mjLROpt LRopt; // options for lengthrange computation
mjString* meshdir; // mesh and hfield directory
mjString* texturedir; // texture directory
uint64_t authored; // bitmask of authored compiler fields
} mjsCompiler;
typedef struct mjsAuthored_ { // authored tracking bitmasks for mjModel structs
uint64_t option; // authored mjOption fields
int disableflags; // individual authored disable flags
int enableflags; // individual authored enable flags
int disableactuator; // individual authored actuator groups
uint64_t visual_global; // authored visual.global fields
uint64_t visual_quality; // authored visual.quality fields
uint64_t visual_headlight; // authored visual.headlight fields
uint64_t visual_map; // authored visual.map fields
uint64_t visual_scale; // authored visual.scale fields
uint64_t visual_rgba; // authored visual.rgba fields
} mjsAuthored;
typedef struct mjSpec_ { // model specification
mjsElement* element; // element type
mjString* modelname; // model name
@@ -213,6 +228,9 @@ typedef struct mjSpec_ { // model specification
// other
mjtByte hasImplicitPluginElem; // already encountered an implicit plugin sensor/actuator
// authored tracking bitmasks for mjModel structs
mjsAuthored authored;
} mjSpec;
+2 -1
View File
@@ -43,7 +43,8 @@
X ( int, alignfree, 1 ) \
X ( mjLROpt, LRopt, 1 ) \
X ( mjString*, meshdir, 1 ) \
X ( mjString*, texturedir, 1 )
X ( mjString*, texturedir, 1 ) \
X ( uint64_t, authored, 1 )
//-------------------------------- mjSpec ----------------------------------------------------------
@@ -82,8 +82,10 @@ def _value_binding_code(
field.name == 'mjsPlugin'
or field.name == 'mjsOrientation'
or field.name == 'mjsCompiler'
or field.name == 'mjsAuthored'
):
fulltype = fulltype + '&' # plugin, orientation, compiler aren't pointers
# plugin, orientation, compiler, authored aren't pointers
fulltype = fulltype + '&'
else:
fulltype = fulltype + '*'
# non-mjs structs
+67
View File
@@ -6996,6 +6996,68 @@ STRUCTS: Mapping[str, StructDecl] = dict([
),
doc='texture directory',
),
StructFieldDecl(
name='authored',
type=ValueType(name='uint64_t'),
doc='bitmask of authored compiler fields',
),
),
)),
('mjsAuthored',
StructDecl(
name='mjsAuthored',
declname='struct mjsAuthored_',
fields=(
StructFieldDecl(
name='option',
type=ValueType(name='uint64_t'),
doc='authored mjOption fields',
),
StructFieldDecl(
name='disableflags',
type=ValueType(name='int'),
doc='individual authored disable flags',
),
StructFieldDecl(
name='enableflags',
type=ValueType(name='int'),
doc='individual authored enable flags',
),
StructFieldDecl(
name='disableactuator',
type=ValueType(name='int'),
doc='individual authored actuator groups',
),
StructFieldDecl(
name='visual_global',
type=ValueType(name='uint64_t'),
doc='authored visual.global fields',
),
StructFieldDecl(
name='visual_quality',
type=ValueType(name='uint64_t'),
doc='authored visual.quality fields',
),
StructFieldDecl(
name='visual_headlight',
type=ValueType(name='uint64_t'),
doc='authored visual.headlight fields',
),
StructFieldDecl(
name='visual_map',
type=ValueType(name='uint64_t'),
doc='authored visual.map fields',
),
StructFieldDecl(
name='visual_scale',
type=ValueType(name='uint64_t'),
doc='authored visual.scale fields',
),
StructFieldDecl(
name='visual_rgba',
type=ValueType(name='uint64_t'),
doc='authored visual.rgba fields',
),
),
)),
('mjSpec',
@@ -7136,6 +7198,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
type=ValueType(name='mjtByte'),
doc='already encountered an implicit plugin sensor/actuator',
),
StructFieldDecl(
name='authored',
type=ValueType(name='mjsAuthored'),
doc='authored tracking bitmasks for mjModel structs',
),
),
)),
('mjsOrientation',
+1
View File
@@ -61,6 +61,7 @@ using MjsTuple = ::mjsTuple;
using MjsKey = ::mjsKey;
using MjsDefault = ::mjsDefault;
using MjsCompiler = ::mjsCompiler;
using MjsAuthored = ::mjsAuthored;
using MjOption = ::mjOption;
using MjSolverStat = ::mjSolverStat;
using MjStatistic = ::mjStatistic;
+1
View File
@@ -267,6 +267,7 @@ PYBIND11_MODULE(_specs, m) {
py::class_<raw::MjVisualHeadlight> mjVisualHeadlight(m, "MjVisualHeadlight");
py::class_<raw::MjVisualRgba> mjVisualRgba(m, "MjVisualRgba");
py::class_<raw::MjsCompiler> mjsCompiler(m, "MjsCompiler");
py::class_<raw::MjsAuthored> mjsAuthored(m, "MjsAuthored");
DefineArray<char>(m, "MjCharVec");
DefineArray<std::string>(m, "MjStringVec");
DefineArray<std::byte>(m, "MjByteVec");
+71
View File
@@ -2102,5 +2102,76 @@ class SpecsTest(absltest.TestCase):
model = spec.compile()
self.assertIsNotNone(model)
def test_authored_struct(self):
spec = mujoco.MjSpec()
# authored struct should be accessible with correct fields
self.assertEqual(spec.authored.option, 0)
self.assertEqual(spec.authored.disableflags, 0)
self.assertEqual(spec.authored.enableflags, 0)
self.assertEqual(spec.authored.disableactuator, 0)
self.assertEqual(spec.authored.visual_global, 0)
self.assertEqual(spec.authored.visual_quality, 0)
self.assertEqual(spec.authored.visual_headlight, 0)
self.assertEqual(spec.authored.visual_map, 0)
self.assertEqual(spec.authored.visual_scale, 0)
self.assertEqual(spec.authored.visual_rgba, 0)
# compiler authored should be zero
self.assertEqual(spec.compiler.authored, 0)
def test_authored_flags_from_xml(self):
spec = mujoco.MjSpec.from_string("""
<mujoco>
<option timestep="0.01">
<flag constraint="disable" energy="enable"/>
</option>
<compiler boundmass="1"/>
<visual>
<global fovy="60"/>
<quality shadowsize="1024"/>
</visual>
<worldbody/>
</mujoco>
""")
# disable/enable flags should be tracked
self.assertNotEqual(
spec.authored.disableflags & mujoco.mjtDisableBit.mjDSBL_CONSTRAINT, 0)
self.assertEqual(
spec.authored.disableflags & mujoco.mjtDisableBit.mjDSBL_CONTACT, 0)
self.assertNotEqual(
spec.authored.enableflags & mujoco.mjtEnableBit.mjENBL_ENERGY, 0)
self.assertEqual(
spec.authored.enableflags & mujoco.mjtEnableBit.mjENBL_OVERRIDE, 0)
# option authored bitmask should be nonzero (timestep was authored)
self.assertNotEqual(spec.authored.option, 0)
# compiler authored bitmask should be nonzero (boundmass was authored)
self.assertNotEqual(spec.compiler.authored, 0)
# visual authored bitmask should be nonzero (fovy, shadowsize were authored)
self.assertNotEqual(spec.authored.visual_global, 0)
self.assertNotEqual(spec.authored.visual_quality, 0)
# visual sections that were not authored should be zero
self.assertEqual(spec.authored.visual_headlight, 0)
self.assertEqual(spec.authored.visual_map, 0)
self.assertEqual(spec.authored.visual_scale, 0)
self.assertEqual(spec.authored.visual_rgba, 0)
def test_authored_defaults_zero(self):
spec = mujoco.MjSpec.from_string("""
<mujoco>
<worldbody/>
</mujoco>
""")
# nothing authored in an empty model
self.assertEqual(spec.authored.option, 0)
self.assertEqual(spec.authored.disableflags, 0)
self.assertEqual(spec.authored.enableflags, 0)
self.assertEqual(spec.compiler.authored, 0)
self.assertEqual(spec.authored.visual_global, 0)
self.assertEqual(spec.authored.visual_quality, 0)
self.assertEqual(spec.authored.visual_map, 0)
if __name__ == '__main__':
absltest.main()
+168
View File
@@ -31,6 +31,8 @@
#include <vector>
#include <mujoco/mujoco.h>
#include <mujoco/mjspecmacro.h>
#include <mujoco/mjxmacro.h>
#include "engine/engine_support.h"
#include "engine/engine_util_errmem.h"
#include "user/user_cache.h"
@@ -2373,3 +2375,169 @@ mjCache* mj_getCache() {
}();
return &cache_cwrapper;
}
// return 1 if a field was authored, 0 otherwise
int mjs_isAuthored(const void* elem_ptr, const void* field_ptr) {
if (!elem_ptr || !field_ptr) return 0;
const mjsElement* el = *reinterpret_cast<const mjsElement* const*>(elem_ptr);
if (!el) return 0;
// model-level sub-structs (compiler, option, visual)
if (el->elemtype == mjOBJ_MODEL) {
const mjCModel* cel = static_cast<const mjCModel*>(el);
int idx = 0;
#define CHECK_FIELD(FIELD_PATH, AUTHORED_MASK) \
if (field_ptr == &FIELD_PATH) return (AUTHORED_MASK & (1ULL << idx)) != 0; \
idx++;
#define CHECK_FIELD_VEC(FIELD_PATH, AUTHORED_MASK) \
if (field_ptr == FIELD_PATH || field_ptr == &FIELD_PATH) \
return (AUTHORED_MASK & (1ULL << idx)) != 0; \
idx++;
#define X(type, name, dim) CHECK_FIELD(cel->spec.compiler.name, cel->spec.compiler.authored)
#define XVEC(type, name, dim) CHECK_FIELD_VEC(cel->spec.compiler.name, cel->spec.compiler.authored)
idx = 0;
MJSCOMPILER_FIELDS
#undef X
#undef XVEC
#define X(type, name, dim) CHECK_FIELD(cel->spec.option.name, cel->spec.authored.option)
#define XVEC(type, name, dim) CHECK_FIELD_VEC(cel->spec.option.name, cel->spec.authored.option)
idx = 0;
MJOPTION_FIELDS
#undef X
#undef XVEC
#define X(type, name, dim) \
CHECK_FIELD(cel->spec.visual.global.name, cel->spec.authored.visual_global)
idx = 0;
MJVISUAL_GLOBAL_FIELDS
#undef X
#define X(type, name, dim) \
CHECK_FIELD(cel->spec.visual.quality.name, cel->spec.authored.visual_quality)
idx = 0;
MJVISUAL_QUALITY_FIELDS
#undef X
#define X(type, name, dim) \
CHECK_FIELD(cel->spec.visual.headlight.name, cel->spec.authored.visual_headlight)
#define XVEC(type, name, dim) \
CHECK_FIELD_VEC(cel->spec.visual.headlight.name, cel->spec.authored.visual_headlight)
idx = 0;
MJVISUAL_HEADLIGHT_FIELDS
#undef X
#undef XVEC
#define X(type, name, dim) CHECK_FIELD(cel->spec.visual.map.name, cel->spec.authored.visual_map)
idx = 0;
MJVISUAL_MAP_FIELDS
#undef X
#define X(type, name, dim) CHECK_FIELD(cel->spec.visual.scale.name, cel->spec.authored.visual_scale)
idx = 0;
MJVISUAL_SCALE_FIELDS
#undef X
#define XVEC(type, name, dim) \
CHECK_FIELD_VEC(cel->spec.visual.rgba.name, cel->spec.authored.visual_rgba)
idx = 0;
MJVISUAL_RGBA_FIELDS
#undef XVEC
#undef CHECK_FIELD
#undef CHECK_FIELD_VEC
}
return 0;
}
// record explicit authoring of an element's field
void mjs_setAuthored(const void* elem_ptr, const void* field_ptr, int authored) {
if (!elem_ptr || !field_ptr) return;
mjsElement* el = const_cast<mjsElement*>(*reinterpret_cast<const mjsElement* const*>(elem_ptr));
if (!el) return;
#define SET_FIELD(FIELD_PATH, AUTHORED_MASK) \
if (field_ptr == &FIELD_PATH) { \
if (authored) \
AUTHORED_MASK |= (1ULL << idx); \
else \
AUTHORED_MASK &= ~(1ULL << idx); \
return; \
} \
idx++;
#define SET_FIELD_VEC(FIELD_PATH, AUTHORED_MASK) \
if (field_ptr == FIELD_PATH || field_ptr == &FIELD_PATH) { \
if (authored) \
AUTHORED_MASK |= (1ULL << idx); \
else \
AUTHORED_MASK &= ~(1ULL << idx); \
return; \
} \
idx++;
// model-level sub-structs (compiler, option, visual)
if (el->elemtype == mjOBJ_MODEL) {
mjCModel* cel = static_cast<mjCModel*>(el);
int idx = 0;
#define X(type, name, dim) SET_FIELD(cel->spec.compiler.name, cel->spec.compiler.authored)
#define XVEC(type, name, dim) SET_FIELD_VEC(cel->spec.compiler.name, cel->spec.compiler.authored)
idx = 0;
MJSCOMPILER_FIELDS
#undef X
#undef XVEC
#define X(type, name, dim) SET_FIELD(cel->spec.option.name, cel->spec.authored.option)
#define XVEC(type, name, dim) SET_FIELD_VEC(cel->spec.option.name, cel->spec.authored.option)
idx = 0;
MJOPTION_FIELDS
#undef X
#undef XVEC
#define X(type, name, dim) SET_FIELD(cel->spec.visual.global.name, cel->spec.authored.visual_global)
idx = 0;
MJVISUAL_GLOBAL_FIELDS
#undef X
#define X(type, name, dim) \
SET_FIELD(cel->spec.visual.quality.name, cel->spec.authored.visual_quality)
idx = 0;
MJVISUAL_QUALITY_FIELDS
#undef X
#define X(type, name, dim) \
SET_FIELD(cel->spec.visual.headlight.name, cel->spec.authored.visual_headlight)
#define XVEC(type, name, dim) \
SET_FIELD_VEC(cel->spec.visual.headlight.name, cel->spec.authored.visual_headlight)
idx = 0;
MJVISUAL_HEADLIGHT_FIELDS
#undef X
#undef XVEC
#define X(type, name, dim) SET_FIELD(cel->spec.visual.map.name, cel->spec.authored.visual_map)
idx = 0;
MJVISUAL_MAP_FIELDS
#undef X
#define X(type, name, dim) SET_FIELD(cel->spec.visual.scale.name, cel->spec.authored.visual_scale)
idx = 0;
MJVISUAL_SCALE_FIELDS
#undef X
#define XVEC(type, name, dim) \
SET_FIELD_VEC(cel->spec.visual.rgba.name, cel->spec.authored.visual_rgba)
idx = 0;
MJVISUAL_RGBA_FIELDS
#undef XVEC
}
#undef SET_FIELD
#undef SET_FIELD_VEC
}
+6
View File
@@ -431,6 +431,12 @@ MJAPI const void* mjs_getPluginAttributes(const mjsPlugin* plugin);
//---------------------------------- Other utilities -----------------------------------------------
// Return 1 if a field was authored or mutated relative to its inherited default, 0 otherwise.
MJAPI int mjs_isAuthored(const void* elem_ptr, const void* field_ptr);
// Record explicit authoring of an element's field.
MJAPI void mjs_setAuthored(const void* elem_ptr, const void* field_ptr, int authored);
// Set element's default.
MJAPI void mjs_setDefault(mjsElement* element, const mjsDefault* def);
+232 -182
View File
@@ -25,17 +25,14 @@
#include <sstream>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <vector>
#include <mujoco/mujoco.h>
#include <mujoco/mjmodel.h>
#include <mujoco/mjplugin.h>
#include <mujoco/mjtype.h>
#include <mujoco/mjvisualize.h>
#include "engine/engine_plugin.h"
#include "engine/engine_support.h"
#include "engine/engine_util_errmem.h"
#include "engine/engine_util_misc.h"
#include <mujoco/mjspec.h>
#include "user/user_api.h"
@@ -53,6 +50,81 @@ using std::vector;
using mujoco::user::FilePath;
using tinyxml2::XMLElement;
//---------------------------------- helper utilities ----------------------------------------------
// GetAttrPtr: overload for scalar and pointer fields
template <typename T>
inline auto GetAttrPtr(T& val) -> std::enable_if_t<!std::is_array_v<T>, decltype(&val)> {
if constexpr (std::is_pointer_v<T>) {
return val;
} else {
return &val;
}
}
// GetAttrPtr: overload for array fields
template <typename T, size_t N>
inline T* GetAttrPtr(T (&arr)[N]) { return arr; }
// helper class for reading attributes while recording authored bits
struct Reader {
Reader(XMLElement* xml_node, const void* elem) : xml_node_(xml_node), elem_(elem) {}
template <typename T>
int operator()(const char* attr, int len, T& data, bool required = false, bool exact = true) {
int res = mjXReader::ReadAttr(xml_node_, attr, len, GetAttrPtr(data), text_, required, exact);
if (res) mjs_setAuthored(elem_, &data, 1);
return res;
}
template <typename T>
bool operator()(const char* attr, T& data, const mjMap* map, int mapsz, bool required = false) {
int map_val_temp;
bool res = mjXReader::MapValue(xml_node_, attr, &map_val_temp, map, mapsz, required);
if (res) {
data = static_cast<T>(map_val_temp);
mjs_setAuthored(elem_, &data, 1);
}
return res;
}
bool operator()(const char* attr, int& data, bool required = false) {
bool res = mjXUtil::ReadAttrInt(xml_node_, attr, &data, required);
if (res) mjs_setAuthored(elem_, &data, 1);
return res;
}
bool operator()(const char* attr, mjString* target) {
std::string txt_temp;
bool res = mjXUtil::ReadAttrTxt(xml_node_, attr, txt_temp);
if (res) {
mjs_setString(target, txt_temp.c_str());
mjs_setAuthored(elem_, target, 1);
}
return res;
}
template <typename T>
bool txt(const char* attr, T& target, void (&set_func)(T&, const char*)) {
std::string txt_temp;
bool res = mjXUtil::ReadAttrTxt(xml_node_, attr, txt_temp);
if (res) {
set_func(target, txt_temp.c_str());
mjs_setAuthored(elem_, &target, 1);
}
return res;
}
void set_node(XMLElement* node) { xml_node_ = node; }
XMLElement* xml_node_;
const void* elem_;
std::string text_;
};
void ReadPluginConfigs(tinyxml2::XMLElement* elem, mjsPlugin* p) {
std::map<string, string, std::less<> > config_attribs;
XMLElement* child = FirstChildElement(elem);
@@ -1023,7 +1095,7 @@ void mjXReader::Parse(XMLElement* root, const mjVFS* vfs) {
for (XMLElement* section = FirstChildElement(root, "option"); section;
section = NextSiblingElement(section, "option")) {
Option(section, &spec->option);
Option(section, spec, &spec->option);
}
for (XMLElement* section = FirstChildElement(root, "size"); section;
@@ -1116,73 +1188,49 @@ void mjXReader::Parse(XMLElement* root, const mjVFS* vfs) {
// compiler section parser
void mjXReader::Compiler(XMLElement* section, mjSpec* s) {
string text;
int n;
Reader read(section, s);
// top-level attributes
if (MapValue(section, "autolimits", &n, bool_map, 2)) {
s->compiler.autolimits = (n == 1);
}
ReadAttr(section, "boundmass", 1, &s->compiler.boundmass, text);
ReadAttr(section, "boundinertia", 1, &s->compiler.boundinertia, text);
ReadAttr(section, "settotalmass", 1, &s->compiler.settotalmass, text);
if (MapValue(section, "balanceinertia", &n, bool_map, 2)) {
s->compiler.balanceinertia = (n == 1);
}
if (MapValue(section, "strippath", &n, bool_map, 2)) {
s->strippath = (n == 1);
}
if (MapValue(section, "fitaabb", &n, bool_map, 2)) {
s->compiler.fitaabb = (n == 1);
}
if (MapValue(section, "coordinate", &n, coordinate_map, 2)) {
read("autolimits", s->compiler.autolimits, bool_map, 2);
read("boundmass", 1, s->compiler.boundmass);
read("boundinertia", 1, s->compiler.boundinertia);
read("settotalmass", 1, s->compiler.settotalmass);
read("balanceinertia", s->compiler.balanceinertia, bool_map, 2);
read("strippath", s->strippath, bool_map, 2);
read("fitaabb", s->compiler.fitaabb, bool_map, 2);
if (int n = 0; MapValue(section, "coordinate", &n, coordinate_map, 2)) {
if (n == 1) {
throw mjXError(section, "global coordinates no longer supported. To convert existing models, "
"load and save them in MuJoCo 2.3.3 or older");
}
}
if (MapValue(section, "angle", &n, angle_map, 2)) {
s->compiler.degree = (n == 1);
}
if (ReadAttrTxt(section, "eulerseq", text)) {
if (text.size() != 3) {
read("angle", s->compiler.degree, angle_map, 2);
if (ReadAttrTxt(section, "eulerseq", read.text_)) {
if (read.text_.size() != 3) {
throw mjXError(section, "euler format must have length 3");
}
memcpy(s->compiler.eulerseq, text.c_str(), 3);
memcpy(s->compiler.eulerseq, read.text_.c_str(), 3);
mjs_setAuthored(s, &s->compiler.eulerseq, 1);
}
if (ReadAttrTxt(section, "assetdir", text)) {
mjs_setString(s->compiler.meshdir, text.c_str());
mjs_setString(s->compiler.texturedir, text.c_str());
if (ReadAttrTxt(section, "assetdir", read.text_)) {
mjs_setString(s->compiler.meshdir, read.text_.c_str());
mjs_setString(s->compiler.texturedir, read.text_.c_str());
}
// meshdir and texturedir take precedence over assetdir
string meshdir, texturedir;
if (ReadAttrTxt(section, "meshdir", meshdir)) {
mjs_setString(s->compiler.meshdir, meshdir.c_str());
};
if (ReadAttrTxt(section, "texturedir", texturedir)) {
mjs_setString(s->compiler.texturedir, texturedir.c_str());
}
if (MapValue(section, "discardvisual", &n, bool_map, 2)) {
s->compiler.discardvisual = (n == 1);
}
if (MapValue(section, "usethread", &n, bool_map, 2)) {
s->compiler.usethread = (n == 1);
}
if (MapValue(section, "fusestatic", &n, bool_map, 2)) {
s->compiler.fusestatic = (n == 1);
}
MapValue(section, "inertiafromgeom", &s->compiler.inertiafromgeom, TFAuto_map, 3);
ReadAttr(section, "inertiagrouprange", 2, s->compiler.inertiagrouprange, text);
if (MapValue(section, "alignfree", &n, bool_map, 2)) {
s->compiler.alignfree = (n == 1);
}
if (MapValue(section, "saveinertial", &n, bool_map, 2)) {
s->compiler.saveinertial = (n == 1);
}
read("meshdir", s->compiler.meshdir);
read("texturedir", s->compiler.texturedir);
read("discardvisual", s->compiler.discardvisual, bool_map, 2);
read("usethread", s->compiler.usethread, bool_map, 2);
read("fusestatic", s->compiler.fusestatic, bool_map, 2);
read("inertiafromgeom", s->compiler.inertiafromgeom, TFAuto_map, 3);
read("inertiagrouprange", 2, s->compiler.inertiagrouprange);
read("alignfree", s->compiler.alignfree, bool_map, 2);
read("saveinertial", s->compiler.saveinertial, bool_map, 2);
// lengthrange subelement
XMLElement* elem = FindSubElem(section, "lengthrange");
if (elem) {
int n;
mjLROpt* opt = &(s->compiler.LRopt);
// flags
@@ -1195,6 +1243,7 @@ void mjXReader::Compiler(XMLElement* section, mjSpec* s) {
}
// algorithm parameters
string text;
ReadAttr(elem, "accel", 1, &opt->accel, text);
ReadAttr(elem, "maxforce", 1, &opt->maxforce, text);
ReadAttr(elem, "timeconst", 1, &opt->timeconst, text);
@@ -1208,45 +1257,44 @@ void mjXReader::Compiler(XMLElement* section, mjSpec* s) {
// option section parser
void mjXReader::Option(XMLElement* section, mjOption* opt) {
string text;
int n;
void mjXReader::Option(XMLElement* section, mjSpec* s, mjOption* opt) {
Reader read(section, s);
// read options
ReadAttr(section, "timestep", 1, &opt->timestep, text);
ReadAttr(section, "impratio", 1, &opt->impratio, text);
ReadAttr(section, "tolerance", 1, &opt->tolerance, text);
ReadAttr(section, "ls_tolerance", 1, &opt->ls_tolerance, text);
ReadAttr(section, "noslip_tolerance", 1, &opt->noslip_tolerance, text);
ReadAttr(section, "ccd_tolerance", 1, &opt->ccd_tolerance, text);
ReadAttr(section, "sleep_tolerance", 1, &opt->sleep_tolerance, text);
ReadAttr(section, "gravity", 3, opt->gravity, text);
ReadAttr(section, "wind", 3, opt->wind, text);
ReadAttr(section, "magnetic", 3, opt->magnetic, text);
ReadAttr(section, "density", 1, &opt->density, text);
ReadAttr(section, "viscosity", 1, &opt->viscosity, text);
read("timestep", 1, opt->timestep);
read("impratio", 1, opt->impratio);
read("tolerance", 1, opt->tolerance);
read("ls_tolerance", 1, opt->ls_tolerance);
read("noslip_tolerance", 1, opt->noslip_tolerance);
read("ccd_tolerance", 1, opt->ccd_tolerance);
read("sleep_tolerance", 1, opt->sleep_tolerance);
read("gravity", 3, opt->gravity);
read("wind", 3, opt->wind);
read("magnetic", 3, opt->magnetic);
read("density", 1, opt->density);
read("viscosity", 1, opt->viscosity);
ReadAttr(section, "o_margin", 1, &opt->o_margin, text);
ReadAttr(section, "o_solref", mjNREF, opt->o_solref, text, false, false);
ReadAttr(section, "o_solimp", mjNIMP, opt->o_solimp, text, false, false);
ReadAttr(section, "o_friction", 5, opt->o_friction, text, false, false);
read("o_margin", 1, opt->o_margin);
read("o_solref", mjNREF, opt->o_solref, false, false);
read("o_solimp", mjNIMP, opt->o_solimp, false, false);
read("o_friction", 5, opt->o_friction, false, false);
MapValue(section, "integrator", &opt->integrator, integrator_map, integrator_sz);
MapValue(section, "cone", &opt->cone, cone_map, cone_sz);
MapValue(section, "jacobian", &opt->jacobian, jac_map, jac_sz);
MapValue(section, "solver", &opt->solver, solver_map, solver_sz);
ReadAttrInt(section, "iterations", &opt->iterations);
ReadAttrInt(section, "ls_iterations", &opt->ls_iterations);
ReadAttrInt(section, "noslip_iterations", &opt->noslip_iterations);
ReadAttrInt(section, "ccd_iterations", &opt->ccd_iterations);
ReadAttrInt(section, "sdf_iterations", &opt->sdf_iterations);
ReadAttrInt(section, "sdf_initpoints", &opt->sdf_initpoints);
read("integrator", opt->integrator, integrator_map, integrator_sz);
read("cone", opt->cone, cone_map, cone_sz);
read("jacobian", opt->jacobian, jac_map, jac_sz);
read("solver", opt->solver, solver_map, solver_sz);
read("iterations", opt->iterations);
read("ls_iterations", opt->ls_iterations);
read("noslip_iterations", opt->noslip_iterations);
read("ccd_iterations", opt->ccd_iterations);
read("sdf_iterations", opt->sdf_iterations);
read("sdf_initpoints", opt->sdf_initpoints);
// actuatorgroupdisable
constexpr int num_bitflags = 31;
int disabled_act_groups[num_bitflags];
int num_found = ReadAttr(section, "actuatorgroupdisable", num_bitflags, disabled_act_groups,
text, false, false);
int num_found = read("actuatorgroupdisable", num_bitflags,
disabled_act_groups, false, false);
for (int i=0; i < num_found; i++) {
int group = disabled_act_groups[i];
if (group < 0) {
@@ -1256,15 +1304,19 @@ void mjXReader::Option(XMLElement* section, mjOption* opt) {
throw mjXError(section, "disabled actuator group value cannot exceed 30");
}
opt->disableactuator |= (1 << group);
s->authored.disableactuator |= (1 << group);
}
// read disable sub-element
XMLElement* elem = FindSubElem(section, "flag");
if (elem) {
#define READDSBL(NAME, MASK) \
if (MapValue(elem, NAME, &n, enable_map, 2)) { \
opt->disableflags ^= (opt->disableflags & MASK); \
opt->disableflags |= (n ? 0 : MASK); }
int n = 0;
#define READDSBL(NAME, MASK) \
if (MapValue(elem, NAME, &n, enable_map, 2)) { \
opt->disableflags ^= (opt->disableflags & MASK); \
opt->disableflags |= (n ? 0 : MASK); \
s->authored.disableflags |= MASK; \
}
READDSBL("constraint", mjDSBL_CONSTRAINT)
READDSBL("equality", mjDSBL_EQUALITY)
@@ -1288,10 +1340,12 @@ void mjXReader::Option(XMLElement* section, mjOption* opt) {
READDSBL("multiccd", mjDSBL_MULTICCD)
#undef READDSBL
#define READENBL(NAME, MASK) \
if (MapValue(elem, NAME, &n, enable_map, 2)) { \
opt->enableflags ^= (opt->enableflags & MASK); \
opt->enableflags |= (n ? MASK : 0); }
#define READENBL(NAME, MASK) \
if (MapValue(elem, NAME, &n, enable_map, 2)) { \
opt->enableflags ^= (opt->enableflags & MASK); \
opt->enableflags |= (n ? MASK : 0); \
s->authored.enableflags |= MASK; \
}
READENBL("override", mjENBL_OVERRIDE)
READENBL("energy", mjENBL_ENERGY)
@@ -3247,129 +3301,125 @@ void mjXReader::Custom(XMLElement* section) {
// visual section parser
void mjXReader::Visual(XMLElement* section) {
string text, name;
string name;
XMLElement* elem;
mjVisual* vis = &spec->visual;
int n;
Reader read(section, spec);
// iterate over child elements
elem = FirstChildElement(section);
while (elem) {
// get sub-element name
name = elem->Value();
read.set_node(elem);
// global sub-element
if (name == "global") {
ReadAttrInt(elem, "cameraid", &vis->global.cameraid);
if (MapValue(elem, "orthographic", &n, bool_map, 2)) {
vis->global.orthographic = (n == 1);
}
ReadAttr(elem, "fovy", 1, &vis->global.fovy, text);
ReadAttr(elem, "ipd", 1, &vis->global.ipd, text);
ReadAttr(elem, "azimuth", 1, &vis->global.azimuth, text);
ReadAttr(elem, "elevation", 1, &vis->global.elevation, text);
ReadAttr(elem, "linewidth", 1, &vis->global.linewidth, text);
ReadAttr(elem, "glow", 1, &vis->global.glow, text);
ReadAttrInt(elem, "offwidth", &vis->global.offwidth);
ReadAttrInt(elem, "offheight", &vis->global.offheight);
if (ReadAttr(elem, "realtime", 1, &vis->global.realtime, text)) {
read("cameraid", vis->global.cameraid);
read("orthographic", vis->global.orthographic, bool_map, 2);
read("fovy", 1, vis->global.fovy);
read("ipd", 1, vis->global.ipd);
read("azimuth", 1, vis->global.azimuth);
read("elevation", 1, vis->global.elevation);
read("linewidth", 1, vis->global.linewidth);
read("glow", 1, vis->global.glow);
read("offwidth", vis->global.offwidth);
read("offheight", vis->global.offheight);
if (read("realtime", 1, vis->global.realtime)) {
if (vis->global.realtime <= 0) {
throw mjXError(elem, "realtime must be greater than 0");
}
}
if (MapValue(elem, "ellipsoidinertia", &n, bool_map, 2)) {
vis->global.ellipsoidinertia = (n == 1);
}
if (MapValue(elem, "bvactive", &n, bool_map, 2)) {
vis->global.bvactive = (n == 1);
}
read("ellipsoidinertia", vis->global.ellipsoidinertia, bool_map, 2);
read("bvactive", vis->global.bvactive, bool_map, 2);
}
// quality sub-element
else if (name == "quality") {
ReadAttrInt(elem, "shadowsize", &vis->quality.shadowsize);
ReadAttrInt(elem, "offsamples", &vis->quality.offsamples);
ReadAttrInt(elem, "numslices", &vis->quality.numslices);
ReadAttrInt(elem, "numstacks", &vis->quality.numstacks);
ReadAttrInt(elem, "numquads", &vis->quality.numquads);
read("shadowsize", vis->quality.shadowsize);
read("offsamples", vis->quality.offsamples);
read("numslices", vis->quality.numslices);
read("numstacks", vis->quality.numstacks);
read("numquads", vis->quality.numquads);
}
// headlight sub-element
else if (name == "headlight") {
ReadAttr(elem, "ambient", 3, vis->headlight.ambient, text);
ReadAttr(elem, "diffuse", 3, vis->headlight.diffuse, text);
ReadAttr(elem, "specular", 3, vis->headlight.specular, text);
ReadAttrInt(elem, "active", &vis->headlight.active);
read("ambient", 3, vis->headlight.ambient);
read("diffuse", 3, vis->headlight.diffuse);
read("specular", 3, vis->headlight.specular);
read("active", vis->headlight.active);
}
// map sub-element
else if (name == "map") {
ReadAttr(elem, "stiffness", 1, &vis->map.stiffness, text);
ReadAttr(elem, "stiffnessrot", 1, &vis->map.stiffnessrot, text);
ReadAttr(elem, "force", 1, &vis->map.force, text);
ReadAttr(elem, "torque", 1, &vis->map.torque, text);
ReadAttr(elem, "alpha", 1, &vis->map.alpha, text);
ReadAttr(elem, "fogstart", 1, &vis->map.fogstart, text);
ReadAttr(elem, "fogend", 1, &vis->map.fogend, text);
ReadAttr(elem, "znear", 1, &vis->map.znear, text);
if (vis->map.znear <= 0) {
throw mjXError(elem, "znear must be strictly positive");
read("stiffness", 1, vis->map.stiffness);
read("stiffnessrot", 1, vis->map.stiffnessrot);
read("force", 1, vis->map.force);
read("torque", 1, vis->map.torque);
read("alpha", 1, vis->map.alpha);
read("fogstart", 1, vis->map.fogstart);
read("fogend", 1, vis->map.fogend);
if (read("znear", 1, vis->map.znear)) {
if (vis->map.znear <= 0) {
throw mjXError(elem, "znear must be strictly positive");
}
}
ReadAttr(elem, "zfar", 1, &vis->map.zfar, text);
ReadAttr(elem, "haze", 1, &vis->map.haze, text);
ReadAttr(elem, "shadowclip", 1, &vis->map.shadowclip, text);
ReadAttr(elem, "shadowscale", 1, &vis->map.shadowscale, text);
ReadAttr(elem, "actuatortendon", 1, &vis->map.actuatortendon, text);
read("zfar", 1, vis->map.zfar);
read("haze", 1, vis->map.haze);
read("shadowclip", 1, vis->map.shadowclip);
read("shadowscale", 1, vis->map.shadowscale);
read("actuatortendon", 1, vis->map.actuatortendon);
}
// scale sub-element
else if (name == "scale") {
ReadAttr(elem, "forcewidth", 1, &vis->scale.forcewidth, text);
ReadAttr(elem, "contactwidth", 1, &vis->scale.contactwidth, text);
ReadAttr(elem, "contactheight", 1, &vis->scale.contactheight, text);
ReadAttr(elem, "connect", 1, &vis->scale.connect, text);
ReadAttr(elem, "com", 1, &vis->scale.com, text);
ReadAttr(elem, "camera", 1, &vis->scale.camera, text);
ReadAttr(elem, "light", 1, &vis->scale.light, text);
ReadAttr(elem, "selectpoint", 1, &vis->scale.selectpoint, text);
ReadAttr(elem, "jointlength", 1, &vis->scale.jointlength, text);
ReadAttr(elem, "jointwidth", 1, &vis->scale.jointwidth, text);
ReadAttr(elem, "actuatorlength", 1, &vis->scale.actuatorlength, text);
ReadAttr(elem, "actuatorwidth", 1, &vis->scale.actuatorwidth, text);
ReadAttr(elem, "framelength", 1, &vis->scale.framelength, text);
ReadAttr(elem, "framewidth", 1, &vis->scale.framewidth, text);
ReadAttr(elem, "constraint", 1, &vis->scale.constraint, text);
ReadAttr(elem, "slidercrank", 1, &vis->scale.slidercrank, text);
ReadAttr(elem, "frustum", 1, &vis->scale.frustum, text);
read("forcewidth", 1, vis->scale.forcewidth);
read("contactwidth", 1, vis->scale.contactwidth);
read("contactheight", 1, vis->scale.contactheight);
read("connect", 1, vis->scale.connect);
read("com", 1, vis->scale.com);
read("camera", 1, vis->scale.camera);
read("light", 1, vis->scale.light);
read("selectpoint", 1, vis->scale.selectpoint);
read("jointlength", 1, vis->scale.jointlength);
read("jointwidth", 1, vis->scale.jointwidth);
read("actuatorlength", 1, vis->scale.actuatorlength);
read("actuatorwidth", 1, vis->scale.actuatorwidth);
read("framelength", 1, vis->scale.framelength);
read("framewidth", 1, vis->scale.framewidth);
read("constraint", 1, vis->scale.constraint);
read("slidercrank", 1, vis->scale.slidercrank);
read("frustum", 1, vis->scale.frustum);
}
// rgba sub-element
else if (name == "rgba") {
ReadAttr(elem, "fog", 4, vis->rgba.fog, text);
ReadAttr(elem, "haze", 4, vis->rgba.haze, text);
ReadAttr(elem, "force", 4, vis->rgba.force, text);
ReadAttr(elem, "inertia", 4, vis->rgba.inertia, text);
ReadAttr(elem, "joint", 4, vis->rgba.joint, text);
ReadAttr(elem, "actuator", 4, vis->rgba.actuator, text);
ReadAttr(elem, "actuatornegative", 4, vis->rgba.actuatornegative, text);
ReadAttr(elem, "actuatorpositive", 4, vis->rgba.actuatorpositive, text);
ReadAttr(elem, "com", 4, vis->rgba.com, text);
ReadAttr(elem, "camera", 4, vis->rgba.camera, text);
ReadAttr(elem, "light", 4, vis->rgba.light, text);
ReadAttr(elem, "selectpoint", 4, vis->rgba.selectpoint, text);
ReadAttr(elem, "connect", 4, vis->rgba.connect, text);
ReadAttr(elem, "contactpoint", 4, vis->rgba.contactpoint, text);
ReadAttr(elem, "contactforce", 4, vis->rgba.contactforce, text);
ReadAttr(elem, "contactfriction", 4, vis->rgba.contactfriction, text);
ReadAttr(elem, "contacttorque", 4, vis->rgba.contacttorque, text);
ReadAttr(elem, "contactgap", 4, vis->rgba.contactgap, text);
ReadAttr(elem, "rangefinder", 4, vis->rgba.rangefinder, text);
ReadAttr(elem, "constraint", 4, vis->rgba.constraint, text);
ReadAttr(elem, "slidercrank", 4, vis->rgba.slidercrank, text);
ReadAttr(elem, "crankbroken", 4, vis->rgba.crankbroken, text);
ReadAttr(elem, "frustum", 4, vis->rgba.frustum, text);
ReadAttr(elem, "bv", 4, vis->rgba.bv, text);
ReadAttr(elem, "bvactive", 4, vis->rgba.bvactive, text);
read("fog", 4, vis->rgba.fog);
read("haze", 4, vis->rgba.haze);
read("force", 4, vis->rgba.force);
read("inertia", 4, vis->rgba.inertia);
read("joint", 4, vis->rgba.joint);
read("actuator", 4, vis->rgba.actuator);
read("actuatornegative", 4, vis->rgba.actuatornegative);
read("actuatorpositive", 4, vis->rgba.actuatorpositive);
read("com", 4, vis->rgba.com);
read("camera", 4, vis->rgba.camera);
read("light", 4, vis->rgba.light);
read("selectpoint", 4, vis->rgba.selectpoint);
read("connect", 4, vis->rgba.connect);
read("contactpoint", 4, vis->rgba.contactpoint);
read("contactforce", 4, vis->rgba.contactforce);
read("contactfriction", 4, vis->rgba.contactfriction);
read("contacttorque", 4, vis->rgba.contacttorque);
read("contactgap", 4, vis->rgba.contactgap);
read("rangefinder", 4, vis->rgba.rangefinder);
read("constraint", 4, vis->rgba.constraint);
read("slidercrank", 4, vis->rgba.slidercrank);
read("crankbroken", 4, vis->rgba.crankbroken);
read("frustum", 4, vis->rgba.frustum);
read("bv", 4, vis->rgba.bv);
read("bvactive", 4, vis->rgba.bvactive);
}
// advance to next element
+1 -1
View File
@@ -44,7 +44,7 @@ class mjXReader : public mjXBase {
// XML sections embedded in all formats
static void Compiler(tinyxml2::XMLElement* section, mjSpec* s); // compiler section
static void Option(tinyxml2::XMLElement* section, mjOption* opt); // option section
static void Option(tinyxml2::XMLElement* section, mjSpec* s, mjOption* opt); // option section
static void Size(tinyxml2::XMLElement* section, mjSpec* s); // size section
private:
+1 -1
View File
@@ -99,7 +99,7 @@ void mjXURDF::Parse(
}
if ((section = FindSubElem(mjc, "option"))) {
mjXReader::Option(section, &spec->option);
mjXReader::Option(section, spec, &spec->option);
}
if ((section = FindSubElem(mjc, "size"))) {
+3 -2
View File
@@ -845,14 +845,15 @@ template int mjXUtil::ReadAttr(XMLElement* elem, const char* attr, int len,
// throw error if identically zero
int mjXUtil::ReadQuat(XMLElement* elem, const char* attr, double* data, std::string& text,
bool required) {
ReadAttr(elem, attr, /*len=*/4, data, text, required, /*exact=*/true);
int n = ReadAttr(elem, attr, /*len=*/4, data, text, required, /*exact=*/true);
if (n == 0) return 0;
// check for 0 quaternion
if (data[0] == 0 && data[1] == 0 && data[2] == 0 && data[3] == 0) {
throw mjXError(elem, "zero quaternion is not allowed");
}
return 4;
return n;
}
// read DOUBLE array into C++ vector, return number read
+4 -1
View File
@@ -30,7 +30,10 @@ namespace expected {
struct mjsElement { MJSELEMENT_FIELDS };
struct mjsCompiler { MJSCOMPILER_FIELDS };
struct mjSpec { MJSPEC_FIELDS };
struct mjSpec {
MJSPEC_FIELDS
mjsAuthored authored;
};
struct mjsOrientation { MJSORIENTATION_FIELDS };
struct mjsPlugin { MJSPLUGIN_FIELDS };
struct mjsBody { MJSBODY_FIELDS };
+68
View File
@@ -29,6 +29,7 @@
#include <mujoco/mujoco.h>
#include "src/cc/array_safety.h"
#include "src/engine/engine_util_errmem.h"
#include "src/user/user_api.h"
#include "src/xml/xml_api.h"
#include "test/compare_model.h"
#include "test/fixture.h"
@@ -64,6 +65,73 @@ TEST_F(XMLReaderTest, UniqueElementTest) {
EXPECT_THAT(error.data(), HasSubstr("unique element 'flag' found 2 times"));
}
TEST_F(XMLReaderTest, AuthoredFromXml) {
static constexpr char xml[] = R"(
<mujoco>
<compiler boundmass="1"/>
<option timestep="0.01" gravity="0 0 -5">
<flag constraint="disable"/>
</option>
<visual>
<global fovy="60"/>
<quality shadowsize="1024"/>
</visual>
<worldbody/>
</mujoco>
)";
std::array<char, 1024> error;
mjSpec* spec = mj_parseXMLString(xml, 0, error.data(), error.size());
ASSERT_THAT(spec, NotNull()) << error.data();
// option: timestep authored, gravity authored, density not authored
EXPECT_EQ(mjs_isAuthored(spec, &spec->option.timestep), 1);
EXPECT_EQ(mjs_isAuthored(spec, spec->option.gravity), 1);
EXPECT_EQ(mjs_isAuthored(spec, &spec->option.density), 0);
// compiler: boundmass authored, boundinertia not authored
EXPECT_EQ(mjs_isAuthored(spec, &spec->compiler.boundmass), 1);
EXPECT_EQ(mjs_isAuthored(spec, &spec->compiler.boundinertia), 0);
// flags: constraint disable authored, contact not authored
EXPECT_NE(spec->authored.disableflags & mjDSBL_CONSTRAINT, 0);
EXPECT_EQ(spec->authored.disableflags & mjDSBL_CONTACT, 0);
// visual global: fovy authored, ipd not authored
EXPECT_EQ(mjs_isAuthored(spec, &spec->visual.global.fovy), 1);
EXPECT_EQ(mjs_isAuthored(spec, &spec->visual.global.ipd), 0);
// visual quality: shadowsize authored, offsamples not authored
EXPECT_EQ(mjs_isAuthored(spec, &spec->visual.quality.shadowsize), 1);
EXPECT_EQ(mjs_isAuthored(spec, &spec->visual.quality.offsamples), 0);
mj_deleteSpec(spec);
}
TEST_F(XMLReaderTest, AuthoredDefaultsZero) {
static constexpr char xml[] = R"(
<mujoco>
<worldbody/>
</mujoco>
)";
std::array<char, 1024> error;
mjSpec* spec = mj_parseXMLString(xml, 0, error.data(), error.size());
ASSERT_THAT(spec, NotNull()) << error.data();
// nothing authored in an empty model
EXPECT_EQ(mjs_isAuthored(spec, &spec->option.timestep), 0);
EXPECT_EQ(mjs_isAuthored(spec, &spec->option.gravity), 0);
EXPECT_EQ(mjs_isAuthored(spec, &spec->compiler.boundmass), 0);
EXPECT_EQ(spec->authored.disableflags, 0);
EXPECT_EQ(spec->authored.enableflags, 0);
EXPECT_EQ(spec->authored.disableactuator, 0);
EXPECT_EQ(mjs_isAuthored(spec, &spec->visual.global.fovy), 0);
EXPECT_EQ(mjs_isAuthored(spec, &spec->visual.map.znear), 0);
mj_deleteSpec(spec);
}
TEST_F(XMLReaderTest, MemorySize) {
std::array<char, 1024> error;
{
+15
View File
@@ -5868,6 +5868,21 @@ public unsafe struct mjsCompiler_ {
public mjLROpt_ LRopt;
public void* meshdir;
public void* texturedir;
public UInt64 authored;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct mjsAuthored_ {
public UInt64 option;
public int disableflags;
public int enableflags;
public int disableactuator;
public UInt64 visual_global;
public UInt64 visual_quality;
public UInt64 visual_headlight;
public UInt64 visual_map;
public UInt64 visual_scale;
public UInt64 visual_rgba;
}
[StructLayout(LayoutKind.Sequential)]
+104 -3
View File
@@ -1503,6 +1503,75 @@ struct MjWarningStat {
bool owned_ = false;
};
struct MjsAuthored {
explicit MjsAuthored(mjsAuthored *ptr);
mjsAuthored* get() const;
void set(mjsAuthored* ptr);
uint64_t option() const {
return ptr_->option;
}
void set_option(uint64_t value) {
ptr_->option = value;
}
int disableflags() const {
return ptr_->disableflags;
}
void set_disableflags(int value) {
ptr_->disableflags = value;
}
int enableflags() const {
return ptr_->enableflags;
}
void set_enableflags(int value) {
ptr_->enableflags = value;
}
int disableactuator() const {
return ptr_->disableactuator;
}
void set_disableactuator(int value) {
ptr_->disableactuator = value;
}
uint64_t visual_global() const {
return ptr_->visual_global;
}
void set_visual_global(uint64_t value) {
ptr_->visual_global = value;
}
uint64_t visual_quality() const {
return ptr_->visual_quality;
}
void set_visual_quality(uint64_t value) {
ptr_->visual_quality = value;
}
uint64_t visual_headlight() const {
return ptr_->visual_headlight;
}
void set_visual_headlight(uint64_t value) {
ptr_->visual_headlight = value;
}
uint64_t visual_map() const {
return ptr_->visual_map;
}
void set_visual_map(uint64_t value) {
ptr_->visual_map = value;
}
uint64_t visual_scale() const {
return ptr_->visual_scale;
}
void set_visual_scale(uint64_t value) {
ptr_->visual_scale = value;
}
uint64_t visual_rgba() const {
return ptr_->visual_rgba;
}
void set_visual_rgba(uint64_t value) {
ptr_->visual_rgba = value;
}
private:
mjsAuthored* ptr_;
};
struct MjsElement {
explicit MjsElement(mjsElement *ptr);
mjsElement* get() const;
@@ -2273,6 +2342,12 @@ struct MjsCompiler {
*(ptr_->texturedir) = value;
}
}
uint64_t authored() const {
return ptr_->authored;
}
void set_authored(uint64_t value) {
ptr_->authored = value;
}
private:
mjsCompiler* ptr_;
@@ -5866,6 +5941,7 @@ struct MjSpec {
MjOption option;
MjVisual visual;
MjStatistic stat;
MjsAuthored authored;
};
struct MjsActuator {
@@ -7672,6 +7748,14 @@ void MjWarningStat::set(mjWarningStat* ptr) {
ptr_ = ptr;
}
MjsAuthored::MjsAuthored(mjsAuthored *ptr) : ptr_(ptr) {}
mjsAuthored* MjsAuthored::get() const {
return ptr_;
}
void MjsAuthored::set(mjsAuthored* ptr) {
ptr_ = ptr;
}
MjsElement::MjsElement(mjsElement *ptr) : ptr_(ptr) {}
mjsElement* MjsElement::get() const {
return ptr_;
@@ -8514,7 +8598,8 @@ MjSpec::MjSpec()
compiler(&ptr_->compiler),
option(&ptr_->option),
visual(&ptr_->visual),
stat(&ptr_->stat) {
stat(&ptr_->stat),
authored(&ptr_->authored) {
owned_ = true;
mjs_defaultSpec(ptr_);
};
@@ -8525,7 +8610,8 @@ MjSpec::MjSpec(mjSpec *ptr)
compiler(&ptr_->compiler),
option(&ptr_->option),
visual(&ptr_->visual),
stat(&ptr_->stat) {}
stat(&ptr_->stat),
authored(&ptr_->authored) {}
MjSpec::MjSpec(const MjSpec &other)
: ptr_(mj_copySpec(other.get())),
@@ -8533,7 +8619,8 @@ MjSpec::MjSpec(const MjSpec &other)
compiler(&ptr_->compiler),
option(&ptr_->option),
visual(&ptr_->visual),
stat(&ptr_->stat) {
stat(&ptr_->stat),
authored(&ptr_->authored) {
owned_ = true;
}
@@ -12630,6 +12717,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
emscripten::class_<MjSpec>("MjSpec")
.constructor<const MjSpec &>()
.property("timer", &MjSpec::timer)
.property("authored", &MjSpec::authored, reference())
.property("comment", &MjSpec::comment, &MjSpec::set_comment, reference())
.property("compiler", &MjSpec::compiler, reference())
.property("element", &MjSpec::element, reference())
@@ -12810,6 +12898,17 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.property("target", &MjsActuator::target, &MjsActuator::set_target, reference())
.property("trntype", &MjsActuator::trntype, &MjsActuator::set_trntype, reference())
.property("userdata", &MjsActuator::userdata, reference());
emscripten::class_<MjsAuthored>("MjsAuthored")
.property("disableactuator", &MjsAuthored::disableactuator, &MjsAuthored::set_disableactuator, reference())
.property("disableflags", &MjsAuthored::disableflags, &MjsAuthored::set_disableflags, reference())
.property("enableflags", &MjsAuthored::enableflags, &MjsAuthored::set_enableflags, reference())
.property("option", &MjsAuthored::option, &MjsAuthored::set_option, reference())
.property("visual_global", &MjsAuthored::visual_global, &MjsAuthored::set_visual_global, reference())
.property("visual_headlight", &MjsAuthored::visual_headlight, &MjsAuthored::set_visual_headlight, reference())
.property("visual_map", &MjsAuthored::visual_map, &MjsAuthored::set_visual_map, reference())
.property("visual_quality", &MjsAuthored::visual_quality, &MjsAuthored::set_visual_quality, reference())
.property("visual_rgba", &MjsAuthored::visual_rgba, &MjsAuthored::set_visual_rgba, reference())
.property("visual_scale", &MjsAuthored::visual_scale, &MjsAuthored::set_visual_scale, reference());
emscripten::class_<MjsBody>("MjsBody")
.property("alt", &MjsBody::alt, reference())
.property("childclass", &MjsBody::childclass, &MjsBody::set_childclass, reference())
@@ -12852,6 +12951,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
emscripten::class_<MjsCompiler>("MjsCompiler")
.property("LRopt", &MjsCompiler::LRopt, reference())
.property("alignfree", &MjsCompiler::alignfree, &MjsCompiler::set_alignfree, reference())
.property("authored", &MjsCompiler::authored, &MjsCompiler::set_authored, reference())
.property("autolimits", &MjsCompiler::autolimits, &MjsCompiler::set_autolimits, reference())
.property("balanceinertia", &MjsCompiler::balanceinertia, &MjsCompiler::set_balanceinertia, reference())
.property("boundinertia", &MjsCompiler::boundinertia, &MjsCompiler::set_boundinertia, reference())
@@ -13390,6 +13490,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.property("translate", &MjvScene::translate);
emscripten::register_optional<MjSpec>();
emscripten::register_optional<MjsActuator>();
emscripten::register_optional<MjsAuthored>();
emscripten::register_optional<MjsBody>();
emscripten::register_optional<MjsCamera>();
emscripten::register_optional<MjsCompiler>();
+6 -3
View File
@@ -666,7 +666,8 @@ MjSpec::MjSpec()
compiler(&ptr_->compiler),
option(&ptr_->option),
visual(&ptr_->visual),
stat(&ptr_->stat) {
stat(&ptr_->stat),
authored(&ptr_->authored) {
owned_ = true;
mjs_defaultSpec(ptr_);
};
@@ -677,7 +678,8 @@ MjSpec::MjSpec(mjSpec *ptr)
compiler(&ptr_->compiler),
option(&ptr_->option),
visual(&ptr_->visual),
stat(&ptr_->stat) {}
stat(&ptr_->stat),
authored(&ptr_->authored) {}
MjSpec::MjSpec(const MjSpec &other)
: ptr_(mj_copySpec(other.get())),
@@ -685,7 +687,8 @@ MjSpec::MjSpec(const MjSpec &other)
compiler(&ptr_->compiler),
option(&ptr_->option),
visual(&ptr_->visual),
stat(&ptr_->stat) {
stat(&ptr_->stat),
authored(&ptr_->authored) {
owned_ = true;
}