Remember compiler options during attach.

Note:
- Move compiler options into `mjsCompiler` struct.
- The compiler options from the attached model are not written by mj_SaveXML.
PiperOrigin-RevId: 689391440
Change-Id: I1d63c146a32f87c737b7a55b64a54b0ffe3aecc9
This commit is contained in:
Alessio Quaglino
2024-10-24 08:29:03 -07:00
committed by Copybara-Service
parent 1043633cc3
commit 159f23e6b4
24 changed files with 302 additions and 204 deletions
+10
View File
@@ -1140,6 +1140,16 @@ behavior.
.. mujoco-include:: mjsElement .. mujoco-include:: mjsElement
.. _mjsCompiler:
mjsCompiler
~~~~~~~~~~
Compiler options.
.. mujoco-include:: mjsCompiler
.. _mjsBody: .. _mjsBody:
mjsBody mjsBody
+11 -8
View File
@@ -1683,22 +1683,15 @@ typedef enum mjtOrientation_ { // type of orientation specifier
typedef struct mjsElement_ { // element type, do not modify typedef struct mjsElement_ { // element type, do not modify
mjtObj elemtype; // element type mjtObj elemtype; // element type
} mjsElement; } mjsElement;
typedef struct mjSpec_ { // model specification typedef struct mjsCompiler_ { // compiler options
mjsElement* element; // element type
mjString* modelname; // model name
// compiler settings
mjtByte autolimits; // infer "limited" attribute based on range mjtByte autolimits; // infer "limited" attribute based on range
double boundmass; // enforce minimum body mass double boundmass; // enforce minimum body mass
double boundinertia; // enforce minimum body diagonal inertia double boundinertia; // enforce minimum body diagonal inertia
double settotalmass; // rescale masses and inertias; <=0: ignore double settotalmass; // rescale masses and inertias; <=0: ignore
mjtByte balanceinertia; // automatically impose A + B >= C rule mjtByte balanceinertia; // automatically impose A + B >= C rule
mjtByte strippath; // automatically strip paths from mesh files
mjtByte fitaabb; // meshfit to aabb instead of inertia box mjtByte fitaabb; // meshfit to aabb instead of inertia box
mjtByte degree; // angles in radians or degrees mjtByte degree; // angles in radians or degrees
char eulerseq[3]; // sequence for euler rotations char eulerseq[3]; // sequence for euler rotations
mjString* meshdir; // mesh and hfield directory
mjString* texturedir; // texture directory
mjtByte discardvisual; // discard visual geoms in parser mjtByte discardvisual; // discard visual geoms in parser
mjtByte convexhull; // compute mesh convex hulls mjtByte convexhull; // compute mesh convex hulls
mjtByte usethread; // use multiple threads to speed up compiler mjtByte usethread; // use multiple threads to speed up compiler
@@ -1707,6 +1700,16 @@ typedef struct mjSpec_ { // model specification
int inertiagrouprange[2]; // range of geom groups used to compute inertia int inertiagrouprange[2]; // range of geom groups used to compute inertia
int alignfree; // align free joints with inertial frame int alignfree; // align free joints with inertial frame
mjLROpt LRopt; // options for lengthrange computation mjLROpt LRopt; // options for lengthrange computation
} mjsCompiler;
typedef struct mjSpec_ { // model specification
mjsElement* element; // element type
mjString* modelname; // model name
// compiler data
mjsCompiler compiler; // compiler options
mjtByte strippath; // automatically strip paths from mesh files
mjString* meshdir; // mesh and hfield directory
mjString* texturedir; // texture directory
// engine data // engine data
mjOption option; // physics options mjOption option; // physics options
+13 -8
View File
@@ -121,22 +121,15 @@ typedef struct mjsElement_ { // element type, do not modify
} mjsElement; } mjsElement;
typedef struct mjSpec_ { // model specification typedef struct mjsCompiler_ { // compiler options
mjsElement* element; // element type
mjString* modelname; // model name
// compiler settings
mjtByte autolimits; // infer "limited" attribute based on range mjtByte autolimits; // infer "limited" attribute based on range
double boundmass; // enforce minimum body mass double boundmass; // enforce minimum body mass
double boundinertia; // enforce minimum body diagonal inertia double boundinertia; // enforce minimum body diagonal inertia
double settotalmass; // rescale masses and inertias; <=0: ignore double settotalmass; // rescale masses and inertias; <=0: ignore
mjtByte balanceinertia; // automatically impose A + B >= C rule mjtByte balanceinertia; // automatically impose A + B >= C rule
mjtByte strippath; // automatically strip paths from mesh files
mjtByte fitaabb; // meshfit to aabb instead of inertia box mjtByte fitaabb; // meshfit to aabb instead of inertia box
mjtByte degree; // angles in radians or degrees mjtByte degree; // angles in radians or degrees
char eulerseq[3]; // sequence for euler rotations char eulerseq[3]; // sequence for euler rotations
mjString* meshdir; // mesh and hfield directory
mjString* texturedir; // texture directory
mjtByte discardvisual; // discard visual geoms in parser mjtByte discardvisual; // discard visual geoms in parser
mjtByte convexhull; // compute mesh convex hulls mjtByte convexhull; // compute mesh convex hulls
mjtByte usethread; // use multiple threads to speed up compiler mjtByte usethread; // use multiple threads to speed up compiler
@@ -145,6 +138,18 @@ typedef struct mjSpec_ { // model specification
int inertiagrouprange[2]; // range of geom groups used to compute inertia int inertiagrouprange[2]; // range of geom groups used to compute inertia
int alignfree; // align free joints with inertial frame int alignfree; // align free joints with inertial frame
mjLROpt LRopt; // options for lengthrange computation mjLROpt LRopt; // options for lengthrange computation
} mjsCompiler;
typedef struct mjSpec_ { // model specification
mjsElement* element; // element type
mjString* modelname; // model name
// compiler data
mjsCompiler compiler; // compiler options
mjtByte strippath; // automatically strip paths from mesh files
mjString* meshdir; // mesh and hfield directory
mjString* texturedir; // texture directory
// engine data // engine data
mjOption option; // physics options mjOption option; // physics options
+48 -36
View File
@@ -8800,25 +8800,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
), ),
), ),
)), )),
('mjSpec', ('mjsCompiler',
StructDecl( StructDecl(
name='mjSpec', name='mjsCompiler',
declname='struct mjSpec_', declname='struct mjsCompiler_',
fields=( fields=(
StructFieldDecl(
name='element',
type=PointerType(
inner_type=ValueType(name='mjsElement'),
),
doc='element type',
),
StructFieldDecl(
name='modelname',
type=PointerType(
inner_type=ValueType(name='mjString'),
),
doc='model name',
),
StructFieldDecl( StructFieldDecl(
name='autolimits', name='autolimits',
type=ValueType(name='mjtByte'), type=ValueType(name='mjtByte'),
@@ -8844,11 +8830,6 @@ STRUCTS: Mapping[str, StructDecl] = dict([
type=ValueType(name='mjtByte'), type=ValueType(name='mjtByte'),
doc='automatically impose A + B >= C rule', doc='automatically impose A + B >= C rule',
), ),
StructFieldDecl(
name='strippath',
type=ValueType(name='mjtByte'),
doc='automatically strip paths from mesh files',
),
StructFieldDecl( StructFieldDecl(
name='fitaabb', name='fitaabb',
type=ValueType(name='mjtByte'), type=ValueType(name='mjtByte'),
@@ -8867,20 +8848,6 @@ STRUCTS: Mapping[str, StructDecl] = dict([
), ),
doc='sequence for euler rotations', doc='sequence for euler rotations',
), ),
StructFieldDecl(
name='meshdir',
type=PointerType(
inner_type=ValueType(name='mjString'),
),
doc='mesh and hfield directory',
),
StructFieldDecl(
name='texturedir',
type=PointerType(
inner_type=ValueType(name='mjString'),
),
doc='texture directory',
),
StructFieldDecl( StructFieldDecl(
name='discardvisual', name='discardvisual',
type=ValueType(name='mjtByte'), type=ValueType(name='mjtByte'),
@@ -8924,6 +8891,51 @@ STRUCTS: Mapping[str, StructDecl] = dict([
type=ValueType(name='mjLROpt'), type=ValueType(name='mjLROpt'),
doc='options for lengthrange computation', doc='options for lengthrange computation',
), ),
),
)),
('mjSpec',
StructDecl(
name='mjSpec',
declname='struct mjSpec_',
fields=(
StructFieldDecl(
name='element',
type=PointerType(
inner_type=ValueType(name='mjsElement'),
),
doc='element type',
),
StructFieldDecl(
name='modelname',
type=PointerType(
inner_type=ValueType(name='mjString'),
),
doc='model name',
),
StructFieldDecl(
name='compiler',
type=ValueType(name='mjsCompiler'),
doc='compiler options',
),
StructFieldDecl(
name='strippath',
type=ValueType(name='mjtByte'),
doc='automatically strip paths from mesh files',
),
StructFieldDecl(
name='meshdir',
type=PointerType(
inner_type=ValueType(name='mjString'),
),
doc='mesh and hfield directory',
),
StructFieldDecl(
name='texturedir',
type=PointerType(
inner_type=ValueType(name='mjString'),
),
doc='texture directory',
),
StructFieldDecl( StructFieldDecl(
name='option', name='option',
type=ValueType(name='mjOption'), type=ValueType(name='mjOption'),
@@ -39,8 +39,12 @@ def _value_binding_code(
fullvarname = 'ptr->' + varname fullvarname = 'ptr->' + varname
if field.name.startswith('mjs'): # all other mjs are raw structs if field.name.startswith('mjs'): # all other mjs are raw structs
fulltype = field.name.replace('mjs', 'raw::Mjs') fulltype = field.name.replace('mjs', 'raw::Mjs')
if field.name == 'mjsPlugin' or field.name == 'mjsOrientation': if (
fulltype = fulltype + '&' # plugin and orientation are not pointers field.name == 'mjsPlugin'
or field.name == 'mjsOrientation'
or field.name == 'mjsCompiler'
):
fulltype = fulltype + '&' # plugin, orientation, compiler are not pointers
else: else:
fulltype = fulltype + '*' fulltype = fulltype + '*'
# non-mjs structs # non-mjs structs
+1
View File
@@ -58,6 +58,7 @@ using MjsText = ::mjsText;
using MjsTuple = ::mjsTuple; using MjsTuple = ::mjsTuple;
using MjsKey = ::mjsKey; using MjsKey = ::mjsKey;
using MjsDefault = ::mjsDefault; using MjsDefault = ::mjsDefault;
using MjsCompiler = ::mjsCompiler;
using MjOption = ::mjOption; using MjOption = ::mjOption;
using MjSolverStat = ::mjSolverStat; using MjSolverStat = ::mjSolverStat;
using MjStatistic = ::mjStatistic; using MjStatistic = ::mjStatistic;
+1
View File
@@ -194,6 +194,7 @@ PYBIND11_MODULE(_specs, m) {
py::class_<raw::MjOption> mjOption(m, "MjOption"); py::class_<raw::MjOption> mjOption(m, "MjOption");
py::class_<raw::MjStatistic> mjStatistic(m, "MjStatistic"); py::class_<raw::MjStatistic> mjStatistic(m, "MjStatistic");
py::class_<raw::MjVisual> mjVisual(m, "MjVisual"); py::class_<raw::MjVisual> mjVisual(m, "MjVisual");
py::class_<raw::MjsCompiler> mjsCompiler(m, "MjsCompiler");
DefineArray<char>(m, "MjCharVec"); DefineArray<char>(m, "MjCharVec");
DefineArray<std::string>(m, "MjStringVec"); DefineArray<std::string>(m, "MjStringVec");
DefineArray<std::byte>(m, "MjByteVec"); DefineArray<std::byte>(m, "MjByteVec");
+18 -22
View File
@@ -34,28 +34,28 @@ class SpecsTest(absltest.TestCase):
spec = mujoco.MjSpec() spec = mujoco.MjSpec()
# Check that euler sequence order is set correctly. # Check that euler sequence order is set correctly.
self.assertEqual(spec.eulerseq[0], 'x') self.assertEqual(spec.compiler.eulerseq[0], 'x')
spec.eulerseq = ['z', 'y', 'x'] spec.compiler.eulerseq = ['z', 'y', 'x']
self.assertEqual(spec.eulerseq[0], 'z') self.assertEqual(spec.compiler.eulerseq[0], 'z')
# Change single elements of euler sequence. # Change single elements of euler sequence.
spec.eulerseq[0] = 'y' spec.compiler.eulerseq[0] = 'y'
spec.eulerseq[1] = 'z' spec.compiler.eulerseq[1] = 'z'
self.assertEqual(spec.eulerseq[0], 'y') self.assertEqual(spec.compiler.eulerseq[0], 'y')
self.assertEqual(spec.eulerseq[1], 'z') self.assertEqual(spec.compiler.eulerseq[1], 'z')
# eulerseq is iterable # eulerseq is iterable
self.assertEqual('yzx', ''.join(spec.eulerseq)) self.assertEqual('yzx', ''.join(spec.compiler.eulerseq))
# supports `len` # supports `len`
self.assertLen(spec.eulerseq, 3) self.assertLen(spec.compiler.eulerseq, 3)
# field checks for out-of-bound access on read and on write # field checks for out-of-bound access on read and on write
with self.assertRaises(IndexError): with self.assertRaises(IndexError):
spec.eulerseq[3] = 'x' spec.compiler.eulerseq[3] = 'x'
with self.assertRaises(IndexError): with self.assertRaises(IndexError):
spec.eulerseq[-1] = 'x' spec.compiler.eulerseq[-1] = 'x'
# Add a body, check that it has default orientation. # Add a body, check that it has default orientation.
body = spec.worldbody.add_body() body = spec.worldbody.add_body()
@@ -843,19 +843,15 @@ class SpecsTest(absltest.TestCase):
with self.assertRaises(IndexError): with self.assertRaises(IndexError):
material.textures[-1] = 'x' material.textures[-1] = 'x'
def test_attach_error(self): def test_attach_units(self):
child = mujoco.MjSpec() child = mujoco.MjSpec()
parent = mujoco.MjSpec() parent = mujoco.MjSpec()
parent.degree = not child.degree parent.compiler.degree = not child.compiler.degree
body = parent.worldbody.add_body() body = child.worldbody.add_body(euler=[90, 0, 0])
frame = child.worldbody.add_frame() frame = parent.worldbody.add_frame(euler=[-mujoco.mjPI / 2, 0, 0])
with self.assertRaises(ValueError) as cm: frame.attach_body(body, 'child-', '')
body.attach_frame(frame, '_', '') model = parent.compile()
self.assertEqual( np.testing.assert_almost_equal(model.body_quat[1], [1, 0, 0, 0])
str(cm.exception),
'Error: cannot attach mjSpecs with incompatible compiler/angle'
' attribute',
)
def test_attach_body_to_site(self): def test_attach_body_to_site(self):
child = mujoco.MjSpec() child = mujoco.MjSpec()
+9 -2
View File
@@ -69,7 +69,13 @@ mjSpec* mj_makeSpec() {
// copy model // copy model
mjSpec* mj_copySpec(const mjSpec* s) { mjSpec* mj_copySpec(const mjSpec* s) {
mjCModel* modelC = new mjCModel(*static_cast<mjCModel*>(s->element)); mjCModel* modelC = nullptr;
try {
modelC = new mjCModel(*static_cast<mjCModel*>(s->element));
} catch (mjCError& e) {
mju_error("Failed to copy spec: %s", e.message);
return nullptr;
}
return &modelC->spec; return &modelC->spec;
} }
@@ -177,7 +183,8 @@ mjsBody* mjs_attachToSite(mjsSite* parent, const mjsBody* child,
frame->spec.quat[1] = site->spec.quat[1]; frame->spec.quat[1] = site->spec.quat[1];
frame->spec.quat[2] = site->spec.quat[2]; frame->spec.quat[2] = site->spec.quat[2];
frame->spec.quat[3] = site->spec.quat[3]; frame->spec.quat[3] = site->spec.quat[3];
mjs_resolveOrientation(frame->spec.quat, spec->degree, spec->eulerseq, &site->spec.alt); mjs_resolveOrientation(frame->spec.quat, spec->compiler.degree,
spec->compiler.eulerseq, &site->spec.alt);
return mjs_attachBody(&frame->spec, child, prefix, suffix); return mjs_attachBody(&frame->spec, child, prefix, suffix);
} }
+4 -3
View File
@@ -98,8 +98,9 @@ mjCFlexcomp::mjCFlexcomp(void) {
// make flexcomp object // make flexcomp object
bool mjCFlexcomp::Make(mjSpec* spec, mjsBody* body, char* error, int error_sz) { bool mjCFlexcomp::Make(mjsBody* body, char* error, int error_sz) {
mjCModel* model = (mjCModel*)spec->element; mjCModel* model = static_cast<mjCBody*>(body->element)->model;
mjsCompiler* compiler = static_cast<mjCBody*>(body->element)->compiler;
mjsFlex* dflex = def.spec.flex; mjsFlex* dflex = def.spec.flex;
bool radial = (type == mjFCOMPTYPE_BOX || bool radial = (type == mjFCOMPTYPE_BOX ||
@@ -147,7 +148,7 @@ bool mjCFlexcomp::Make(mjSpec* spec, mjsBody* body, char* error, int error_sz) {
} }
// compute orientation // compute orientation
const char* alterr = mjs_resolveOrientation(quat, model->spec.degree, model->spec.eulerseq, &alt); const char* alterr = mjs_resolveOrientation(quat, compiler->degree, compiler->eulerseq, &alt);
if (alterr) { if (alterr) {
return comperr(error, alterr, error_sz); return comperr(error, alterr, error_sz);
} }
+1 -1
View File
@@ -42,7 +42,7 @@ typedef enum _mjtFcompType {
class mjCFlexcomp { class mjCFlexcomp {
public: public:
mjCFlexcomp(void); mjCFlexcomp(void);
bool Make(mjSpec* spec, mjsBody* body, char* error, int error_sz); bool Make(mjsBody* body, char* error, int error_sz);
bool MakeGrid(char* error, int error_sz); bool MakeGrid(char* error, int error_sz);
bool MakeBox(char* error, int error_sz); bool MakeBox(char* error, int error_sz);
+11 -11
View File
@@ -33,17 +33,17 @@ void mjs_defaultSpec(mjSpec* spec) {
spec->stat.center[0] = mjNAN; spec->stat.center[0] = mjNAN;
// compiler settings // compiler settings
spec->autolimits = 1; spec->compiler.autolimits = 1;
spec->settotalmass = -1; spec->compiler.settotalmass = -1;
spec->degree = 1; spec->compiler.degree = 1;
spec->eulerseq[0] = 'x'; spec->compiler.eulerseq[0] = 'x';
spec->eulerseq[1] = 'y'; spec->compiler.eulerseq[1] = 'y';
spec->eulerseq[2] = 'z'; spec->compiler.eulerseq[2] = 'z';
spec->convexhull = 1; spec->compiler.convexhull = 1;
spec->usethread = 1; spec->compiler.usethread = 1;
spec->inertiafromgeom = mjINERTIAFROMGEOM_AUTO; spec->compiler.inertiafromgeom = mjINERTIAFROMGEOM_AUTO;
spec->inertiagrouprange[1] = mjNGROUP-1; spec->compiler.inertiagrouprange[1] = mjNGROUP-1;
mj_defaultLROpt(&spec->LRopt); mj_defaultLROpt(&spec->compiler.LRopt);
// engine data // engine data
mj_defaultOption(&spec->option); mj_defaultOption(&spec->option);
+2 -2
View File
@@ -563,7 +563,7 @@ void mjCMesh::Compile(const mjVFS* vfs) {
} }
// make graph describing convex hull // make graph describing convex hull
if ((model->convexhull && needhull_) || face_.empty()) { if ((model->compiler.convexhull && needhull_) || face_.empty()) {
MakeGraph(); MakeGraph();
} }
@@ -739,7 +739,7 @@ void mjCMesh::FitGeom(mjCGeom* geom, double* meshpos) {
mjuu_copyvec(meshpos, GetPosPtr(geom->typeinertia), 3); mjuu_copyvec(meshpos, GetPosPtr(geom->typeinertia), 3);
// use inertial box // use inertial box
if (!model->fitaabb) { if (!model->compiler.fitaabb) {
// get inertia box type (shell or volume) // get inertia box type (shell or volume)
double* boxsz = GetInertiaBoxPtr(geom->typeinertia); double* boxsz = GetInertiaBoxPtr(geom->typeinertia);
switch (geom->type) { switch (geom->type) {
+17 -29
View File
@@ -227,8 +227,10 @@ void mjCModel::CopyList(std::vector<T*>& dest,
} }
// copy the element from the other model to this model // copy the element from the other model to this model
source[i]->ForgetKeyframes(); source[i]->ForgetKeyframes();
mjSpec* origin = FindSpec(mjs_getString(source[i]->model->spec.modelname));
dest.push_back(candidate); dest.push_back(candidate);
dest.back()->model = this; dest.back()->model = this;
dest.back()->compiler = origin ? &origin->compiler : &spec.compiler;
dest.back()->id = -1; dest.back()->id = -1;
} }
if (!dest.empty()) { if (!dest.empty()) {
@@ -354,20 +356,6 @@ static bool IsPluginActive(
mjCModel& mjCModel::operator+=(const mjCModel& other) { mjCModel& mjCModel::operator+=(const mjCModel& other) {
// TODO: use compiler settings stored in specs_ during compilation
std::string msg = "cannot attach mjSpecs with incompatible compiler/";
if (other.spec.degree != spec.degree) {
throw mjCError(nullptr, (msg + "angle attribute").c_str());
}
if (other.spec.autolimits != spec.autolimits) {
throw mjCError(nullptr, (msg + "autolimits attribute").c_str());
}
if (other.spec.eulerseq[0] != spec.eulerseq[0] ||
other.spec.eulerseq[1] != spec.eulerseq[1] ||
other.spec.eulerseq[2] != spec.eulerseq[2]) {
throw mjCError(nullptr, (msg + "eulerseq attribute").c_str());
}
// create global lists // create global lists
mjCBody *world = bodies_[0]; mjCBody *world = bodies_[0];
ResetTreeLists(); ResetTreeLists();
@@ -1784,8 +1772,8 @@ void mjCModel::LengthRange(mjModel* m, mjData* data) {
mjOption saveopt = m->opt; mjOption saveopt = m->opt;
m->opt.disableflags = mjDSBL_FRICTIONLOSS | mjDSBL_CONTACT | mjDSBL_PASSIVE | m->opt.disableflags = mjDSBL_FRICTIONLOSS | mjDSBL_CONTACT | mjDSBL_PASSIVE |
mjDSBL_GRAVITY | mjDSBL_ACTUATION; mjDSBL_GRAVITY | mjDSBL_ACTUATION;
if (LRopt.timestep>0) { if (compiler.LRopt.timestep>0) {
m->opt.timestep = LRopt.timestep; m->opt.timestep = compiler.LRopt.timestep;
} }
// number of threads available // number of threads available
@@ -1800,14 +1788,14 @@ void mjCModel::LengthRange(mjModel* m, mjData* data) {
m->actuator_biastype[i]==mjBIAS_MUSCLE); m->actuator_biastype[i]==mjBIAS_MUSCLE);
int isuser = (m->actuator_gaintype[i]==mjGAIN_USER || int isuser = (m->actuator_gaintype[i]==mjGAIN_USER ||
m->actuator_biastype[i]==mjBIAS_USER); m->actuator_biastype[i]==mjBIAS_USER);
if ((LRopt.mode==mjLRMODE_NONE) || if ((compiler.LRopt.mode==mjLRMODE_NONE) ||
(LRopt.mode==mjLRMODE_MUSCLE && !ismuscle) || (compiler.LRopt.mode==mjLRMODE_MUSCLE && !ismuscle) ||
(LRopt.mode==mjLRMODE_MUSCLEUSER && !ismuscle && !isuser)) { (compiler.LRopt.mode==mjLRMODE_MUSCLEUSER && !ismuscle && !isuser)) {
continue; continue;
} }
// use existing length range if available // use existing length range if available
if (LRopt.useexisting && if (compiler.LRopt.useexisting &&
(m->actuator_lengthrange[2*i] < m->actuator_lengthrange[2*i+1])) { (m->actuator_lengthrange[2*i] < m->actuator_lengthrange[2*i+1])) {
continue; continue;
} }
@@ -1817,10 +1805,10 @@ void mjCModel::LengthRange(mjModel* m, mjData* data) {
} }
// single thread // single thread
if (!usethread || cnt<2 || nthread<2) { if (!compiler.usethread || cnt<2 || nthread<2) {
char err[200]; char err[200];
for (int i=0; i<m->nu; i++) { for (int i=0; i<m->nu; i++) {
if (!mj_setLengthRange(m, data, i, &LRopt, err, 200)) { if (!mj_setLengthRange(m, data, i, &compiler.LRopt, err, 200)) {
throw mjCError(0, "%s", err); throw mjCError(0, "%s", err);
} }
} }
@@ -1844,7 +1832,7 @@ void mjCModel::LengthRange(mjModel* m, mjData* data) {
// prepare thread function arguments, clear errors // prepare thread function arguments, clear errors
LRThreadArg arg[kMaxCompilerThreads]; LRThreadArg arg[kMaxCompilerThreads];
for (int i=0; i<nthread; i++) { for (int i=0; i<nthread; i++) {
LRThreadArg temp = {m, pdata[i], i*num, num, &LRopt, err[i], 200}; LRThreadArg temp = {m, pdata[i], i*num, num, &compiler.LRopt, err[i], 200};
arg[i] = temp; arg[i] = temp;
err[i][0] = 0; err[i][0] = 0;
} }
@@ -3914,7 +3902,7 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
ProcessLists(); ProcessLists();
// delete visual assets // delete visual assets
if (discardvisual) { if (compiler.discardvisual) {
DeleteAll(materials_); DeleteAll(materials_);
DeleteTexcoord(flexes_); DeleteTexcoord(flexes_);
DeleteTexcoord(meshes_); DeleteTexcoord(meshes_);
@@ -3937,7 +3925,7 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
SetNuser(); SetNuser();
// compile meshes (needed for geom compilation) // compile meshes (needed for geom compilation)
if (usethread && meshes_.size() > 1) { if (compiler.usethread && meshes_.size() > 1) {
// multi-threaded mesh compile // multi-threaded mesh compile
CompileMeshes(vfs); CompileMeshes(vfs);
} else { } else {
@@ -3981,10 +3969,10 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
reassignid(excludes_); reassignid(excludes_);
// resolve asset references, compute sizes // resolve asset references, compute sizes
IndexAssets(discardvisual); IndexAssets(compiler.discardvisual);
SetSizes(); SetSizes();
// fuse static if enabled // fuse static if enabled
if (fusestatic) { if (compiler.fusestatic) {
FuseStatic(); FuseStatic();
} }
@@ -4144,8 +4132,8 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
CopyObjects(m); CopyObjects(m);
// scale mass // scale mass
if (settotalmass>0) { if (compiler.settotalmass>0) {
mj_setTotalmass(m, settotalmass); mj_setTotalmass(m, compiler.settotalmass);
} }
// set arena size into m->narena // set arena size into m->narena
+57 -28
View File
@@ -749,6 +749,7 @@ void mjCBase::SetFrame(mjCFrame* _frame) {
mjCBody::mjCBody(mjCModel* _model) { mjCBody::mjCBody(mjCModel* _model) {
// set model pointer // set model pointer
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
mjs_defaultBody(&spec); mjs_defaultBody(&spec);
elemtype = mjOBJ_BODY; elemtype = mjOBJ_BODY;
@@ -785,6 +786,8 @@ mjCBody::mjCBody(mjCModel* _model) {
mjCBody::mjCBody(const mjCBody& other, mjCModel* _model) { mjCBody::mjCBody(const mjCBody& other, mjCModel* _model) {
model = _model; model = _model;
mjSpec* origin = model->FindSpec(mjs_getString(other.model->spec.modelname));
compiler = origin ? &origin->compiler : &model->spec.compiler;
*this = other; *this = other;
} }
@@ -868,9 +871,11 @@ mjCBody& mjCBody::operator+=(const mjCFrame& other) {
} }
// copy input frame // copy input frame
mjSpec* origin = model->FindSpec(mjs_getString(other.model->spec.modelname));
frames.push_back(new mjCFrame(other)); frames.push_back(new mjCFrame(other));
frames.back()->body = this; frames.back()->body = this;
frames.back()->model = model; frames.back()->model = model;
frames.back()->compiler = origin ? &origin->compiler : &model->spec.compiler;
frames.back()->frame = other.frame; frames.back()->frame = other.frame;
frames.back()->NameSpace(other.model); frames.back()->NameSpace(other.model);
int i = frames.size(); int i = frames.size();
@@ -931,9 +936,11 @@ void mjCBody::CopyList(std::vector<T*>& dst, const std::vector<T*>& src,
if (pframe && !pframe->IsAncestor(src[i]->frame)) { if (pframe && !pframe->IsAncestor(src[i]->frame)) {
continue; // skip if the element is not inside pframe continue; // skip if the element is not inside pframe
} }
mjSpec* origin = model->FindSpec(mjs_getString(src[i]->model->spec.modelname));
dst.push_back(new T(*src[i])); dst.push_back(new T(*src[i]));
dst.back()->body = this; dst.back()->body = this;
dst.back()->model = model; dst.back()->model = model;
dst.back()->compiler = origin ? &origin->compiler : &model->spec.compiler;
dst.back()->id = -1; dst.back()->id = -1;
dst.back()->classname = src[i]->classname; dst.back()->classname = src[i]->classname;
@@ -1413,8 +1420,8 @@ void mjCBody::InertiaFromGeom(void) {
// select geoms based on group // select geoms based on group
sel.clear(); sel.clear();
for (int i=0; i<geoms.size(); i++) { for (int i=0; i<geoms.size(); i++) {
if (geoms[i]->group>=model->inertiagrouprange[0] && if (geoms[i]->group>=compiler->inertiagrouprange[0] &&
geoms[i]->group<=model->inertiagrouprange[1]) { geoms[i]->group<=compiler->inertiagrouprange[1]) {
sel.push_back(geoms[i]); sel.push_back(geoms[i]);
} }
} }
@@ -1559,7 +1566,7 @@ void mjCBody::Compile(void) {
// check and process orientation alternatives for body // check and process orientation alternatives for body
if (alt.type != mjORIENTATION_QUAT) { if (alt.type != mjORIENTATION_QUAT) {
const char* err = ResolveOrientation(quat, model->degree, model->eulerseq, alt); const char* err = ResolveOrientation(quat, compiler->degree, compiler->eulerseq, alt);
if (err) { if (err) {
throw mjCError(this, "error '%s' in frame alternative", err); throw mjCError(this, "error '%s' in frame alternative", err);
} }
@@ -1582,7 +1589,7 @@ void mjCBody::Compile(void) {
} }
if (ialt.type != mjORIENTATION_QUAT) { if (ialt.type != mjORIENTATION_QUAT) {
const char* err = ResolveOrientation(iquat, model->degree, model->eulerseq, ialt); const char* err = ResolveOrientation(iquat, compiler->degree, compiler->eulerseq, ialt);
if (err) { if (err) {
throw mjCError(this, "error '%s' in inertia alternative", err); throw mjCError(this, "error '%s' in inertia alternative", err);
} }
@@ -1591,15 +1598,15 @@ void mjCBody::Compile(void) {
// compile all geoms // compile all geoms
for (int i=0; i<geoms.size(); i++) { for (int i=0; i<geoms.size(); i++) {
geoms[i]->inferinertia = id>0 && geoms[i]->inferinertia = id>0 &&
(!explicitinertial || model->inertiafromgeom == mjINERTIAFROMGEOM_TRUE) && (!explicitinertial || compiler->inertiafromgeom == mjINERTIAFROMGEOM_TRUE) &&
geoms[i]->spec.group >= model->inertiagrouprange[0] && geoms[i]->spec.group >= compiler->inertiagrouprange[0] &&
geoms[i]->spec.group <= model->inertiagrouprange[1]; geoms[i]->spec.group <= compiler->inertiagrouprange[1];
geoms[i]->Compile(); geoms[i]->Compile();
} }
// set inertial frame from geoms if necessary // set inertial frame from geoms if necessary
if (id>0 && (model->inertiafromgeom==mjINERTIAFROMGEOM_TRUE || if (id>0 && (compiler->inertiafromgeom==mjINERTIAFROMGEOM_TRUE ||
(!mjuu_defined(ipos[0]) && model->inertiafromgeom==mjINERTIAFROMGEOM_AUTO))) { (!mjuu_defined(ipos[0]) && compiler->inertiafromgeom==mjINERTIAFROMGEOM_AUTO))) {
InertiaFromGeom(); InertiaFromGeom();
} }
@@ -1612,10 +1619,10 @@ void mjCBody::Compile(void) {
// check and correct mass and inertia // check and correct mass and inertia
if (id>0) { if (id>0) {
// fix minimum // fix minimum
mass = std::max(mass, model->boundmass); mass = std::max(mass, compiler->boundmass);
inertia[0] = std::max(inertia[0], model->boundinertia); inertia[0] = std::max(inertia[0], compiler->boundinertia);
inertia[1] = std::max(inertia[1], model->boundinertia); inertia[1] = std::max(inertia[1], compiler->boundinertia);
inertia[2] = std::max(inertia[2], model->boundinertia); inertia[2] = std::max(inertia[2], compiler->boundinertia);
// check for negative values // check for negative values
if (mass<0 || inertia[0]<0 || inertia[1]<0 ||inertia[2]<0) { if (mass<0 || inertia[0]<0 || inertia[1]<0 ||inertia[2]<0) {
@@ -1626,7 +1633,7 @@ void mjCBody::Compile(void) {
if (inertia[0] + inertia[1] < inertia[2] || if (inertia[0] + inertia[1] < inertia[2] ||
inertia[0] + inertia[2] < inertia[1] || inertia[0] + inertia[2] < inertia[1] ||
inertia[1] + inertia[2] < inertia[0]) { inertia[1] + inertia[2] < inertia[0]) {
if (model->balanceinertia) { if (compiler->balanceinertia) {
inertia[0] = inertia[1] = inertia[2] = (inertia[0] + inertia[1] + inertia[2])/3.0; inertia[0] = inertia[1] = inertia[2] = (inertia[0] + inertia[1] + inertia[2])/3.0;
} else { } else {
throw mjCError(this, "inertia must satisfy A + B >= C; use 'balanceinertia' to fix"); throw mjCError(this, "inertia must satisfy A + B >= C; use 'balanceinertia' to fix");
@@ -1654,7 +1661,7 @@ void mjCBody::Compile(void) {
bodies.empty() && // no child bodies AND bodies.empty() && // no child bodies AND
(joints[0]->spec.align == 1 || // either joint.align="true" (joints[0]->spec.align == 1 || // either joint.align="true"
(joints[0]->spec.align == 2 && // or joint.align="auto" (joints[0]->spec.align == 2 && // or joint.align="auto"
model->alignfree))); // and compiler.align="true" compiler->alignfree))); // and compiler->align="true"
// free-joint alignment, phase 1 (this body + child geoms) // free-joint alignment, phase 1 (this body + child geoms)
double ipos_inverse[3], iquat_inverse[4]; double ipos_inverse[3], iquat_inverse[4];
@@ -1740,7 +1747,7 @@ void mjCBody::Compile(void) {
} }
// if discarding visual geoms, use explicit inertias // if discarding visual geoms, use explicit inertias
if (model->discardvisual) { if (compiler->discardvisual) {
for (int j=0; j<geoms.size(); j++) { for (int j=0; j<geoms.size(); j++) {
if (geoms[j]->IsVisual()) { if (geoms[j]->IsVisual()) {
explicitinertial = true; explicitinertial = true;
@@ -1782,6 +1789,7 @@ mjCFrame::mjCFrame(mjCModel* _model, mjCFrame* _frame) {
elemtype = mjOBJ_FRAME; elemtype = mjOBJ_FRAME;
compiled = false; compiled = false;
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
body = NULL; body = NULL;
frame = _frame ? _frame : NULL; frame = _frame ? _frame : NULL;
last_attached = nullptr; last_attached = nullptr;
@@ -1905,7 +1913,7 @@ void mjCFrame::Compile() {
} }
CopyFromSpec(); CopyFromSpec();
const char* err = ResolveOrientation(quat, model->spec.degree, model->spec.eulerseq, alt); const char* err = ResolveOrientation(quat, compiler->degree, compiler->eulerseq, alt);
if (err) { if (err) {
throw mjCError(this, "orientation specification error '%s' in site %d", err, id); throw mjCError(this, "orientation specification error '%s' in site %d", err, id);
} }
@@ -1940,6 +1948,7 @@ mjCJoint::mjCJoint(mjCModel* _model, mjCDef* _def) {
// set model, def // set model, def
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main"; classname = _def ? _def->name : "main";
// point to local // point to local
@@ -2069,7 +2078,7 @@ int mjCJoint::Compile(void) {
// otherwise if limited is auto, check consistency wrt auto-limits // otherwise if limited is auto, check consistency wrt auto-limits
else if (limited == mjLIMITED_AUTO) { else if (limited == mjLIMITED_AUTO) {
bool hasrange = !(range[0]==0 && range[1]==0); bool hasrange = !(range[0]==0 && range[1]==0);
checklimited(this, model->autolimits, "joint", "", limited, hasrange); checklimited(this, compiler->autolimits, "joint", "", limited, hasrange);
} }
// resolve limits // resolve limits
@@ -2083,7 +2092,7 @@ int mjCJoint::Compile(void) {
} }
// convert limits to radians // convert limits to radians
if (model->degree && (type==mjJNT_HINGE || type==mjJNT_BALL)) { if (compiler->degree && (type==mjJNT_HINGE || type==mjJNT_BALL)) {
if (range[0]) { if (range[0]) {
range[0] *= mjPI/180.0; range[0] *= mjPI/180.0;
} }
@@ -2100,7 +2109,7 @@ int mjCJoint::Compile(void) {
// otherwise if actfrclimited is auto, check consistency wrt auto-limits // otherwise if actfrclimited is auto, check consistency wrt auto-limits
else if (actfrclimited == mjLIMITED_AUTO) { else if (actfrclimited == mjLIMITED_AUTO) {
bool hasrange = !(actfrcrange[0]==0 && actfrcrange[1]==0); bool hasrange = !(actfrcrange[0]==0 && actfrcrange[1]==0);
checklimited(this, model->autolimits, "joint", "", actfrclimited, hasrange); checklimited(this, compiler->autolimits, "joint", "", actfrclimited, hasrange);
} }
// resolve actuator force range limits // resolve actuator force range limits
@@ -2144,7 +2153,7 @@ int mjCJoint::Compile(void) {
} }
// convert reference angles to radians for hinge joints // convert reference angles to radians for hinge joints
if (type==mjJNT_HINGE && model->degree) { if (type==mjJNT_HINGE && compiler->degree) {
ref *= mjPI/180.0; ref *= mjPI/180.0;
springref *= mjPI/180.0; springref *= mjPI/180.0;
} }
@@ -2193,6 +2202,7 @@ mjCGeom::mjCGeom(mjCModel* _model, mjCDef* _def) {
// set model, def // set model, def
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main"; classname = _def ? _def->name : "main";
// point to local // point to local
@@ -2892,7 +2902,7 @@ void mjCGeom::Compile(void) {
// not 'fromto': try alternative // not 'fromto': try alternative
else { else {
const char* err = ResolveOrientation(quat, model->degree, model->eulerseq, alt); const char* err = ResolveOrientation(quat, compiler->degree, compiler->eulerseq, alt);
if (err) { if (err) {
throw mjCError(this, "orientation specification error '%s' in geom %d", err, id); throw mjCError(this, "orientation specification error '%s' in geom %d", err, id);
} }
@@ -3034,6 +3044,7 @@ mjCSite::mjCSite(mjCModel* _model, mjCDef* _def) {
// set model, def // set model, def
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main"; classname = _def ? _def->name : "main";
} }
@@ -3140,7 +3151,7 @@ void mjCSite::Compile(void) {
// alternative orientation // alternative orientation
else { else {
const char* err = ResolveOrientation(quat, model->degree, model->eulerseq, alt); const char* err = ResolveOrientation(quat, compiler->degree, compiler->eulerseq, alt);
if (err) { if (err) {
throw mjCError(this, "orientation specification error '%s' in site %d", err, id); throw mjCError(this, "orientation specification error '%s' in site %d", err, id);
} }
@@ -3179,6 +3190,7 @@ mjCCamera::mjCCamera(mjCModel* _model, mjCDef* _def) {
// set model, def // set model, def
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main"; classname = _def ? _def->name : "main";
// point to local // point to local
@@ -3248,7 +3260,7 @@ void mjCCamera::Compile(void) {
userdata_.resize(model->nuser_cam); userdata_.resize(model->nuser_cam);
// process orientation specifications // process orientation specifications
const char* err = ResolveOrientation(quat, model->degree, model->eulerseq, alt); const char* err = ResolveOrientation(quat, compiler->degree, compiler->eulerseq, alt);
if (err) { if (err) {
throw mjCError(this, "orientation specification error '%s' in camera %d", err, id); throw mjCError(this, "orientation specification error '%s' in camera %d", err, id);
} }
@@ -3330,6 +3342,7 @@ mjCLight::mjCLight(mjCModel* _model, mjCDef* _def) {
// set model, def // set model, def
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main"; classname = _def ? _def->name : "main";
PointToLocal(); PointToLocal();
@@ -3423,6 +3436,7 @@ mjCHField::mjCHField(mjCModel* _model) {
// set model pointer // set model pointer
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
// clear variables // clear variables
data.clear(); data.clear();
@@ -3678,6 +3692,7 @@ mjCTexture::mjCTexture(mjCModel* _model) {
// set model pointer // set model pointer
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
// clear user settings: single file // clear user settings: single file
spec_file_.clear(); spec_file_.clear();
@@ -4511,6 +4526,7 @@ mjCMaterial::mjCMaterial(mjCModel* _model, mjCDef* _def) {
} }
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main"; classname = _def ? _def->name : "main";
PointToLocal(); PointToLocal();
@@ -4597,6 +4613,7 @@ mjCPair::mjCPair(mjCModel* _model, mjCDef* _def) {
// set model, def // set model, def
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main"; classname = _def ? _def->name : "main";
// point to local // point to local
@@ -4817,6 +4834,7 @@ void mjCPair::Compile(void) {
mjCBodyPair::mjCBodyPair(mjCModel* _model) { mjCBodyPair::mjCBodyPair(mjCModel* _model) {
// set model pointer // set model pointer
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
elemtype = mjOBJ_EXCLUDE; elemtype = mjOBJ_EXCLUDE;
// set defaults // set defaults
@@ -4955,6 +4973,7 @@ mjCEquality::mjCEquality(mjCModel* _model, mjCDef* _def) {
// set model, def // set model, def
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main"; classname = _def ? _def->name : "main";
// point to local // point to local
@@ -5116,6 +5135,7 @@ mjCTendon::mjCTendon(mjCModel* _model, mjCDef* _def) {
// set model, def // set model, def
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main"; classname = _def ? _def->name : "main";
// point to local // point to local
@@ -5201,6 +5221,7 @@ mjCTendon::~mjCTendon() {
void mjCTendon::SetModel(mjCModel* _model) { void mjCTendon::SetModel(mjCModel* _model) {
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
for (int i=0; i<path.size(); i++) { for (int i=0; i<path.size(); i++) {
path[i]->model = _model; path[i]->model = _model;
} }
@@ -5423,7 +5444,7 @@ void mjCTendon::Compile(void) {
// if limited is auto, set to 1 if range is specified, otherwise unlimited // if limited is auto, set to 1 if range is specified, otherwise unlimited
if (limited == mjLIMITED_AUTO) { if (limited == mjLIMITED_AUTO) {
bool hasrange = !(range[0]==0 && range[1]==0); bool hasrange = !(range[0]==0 && range[1]==0);
checklimited(this, model->autolimits, "tendon", "", limited, hasrange); checklimited(this, compiler->autolimits, "tendon", "", limited, hasrange);
} }
// check limits // check limits
@@ -5447,6 +5468,7 @@ mjCWrap::mjCWrap(mjCModel* _model, mjCTendon* _tendon) {
// set model and tendon pointer // set model and tendon pointer
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
tendon = _tendon; tendon = _tendon;
// clear variables // clear variables
@@ -5591,6 +5613,7 @@ mjCActuator::mjCActuator(mjCModel* _model, mjCDef* _def) {
// set model, def // set model, def
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main"; classname = _def ? _def->name : "main";
// in case this actuator is not compiled // in case this actuator is not compiled
@@ -5846,15 +5869,15 @@ void mjCActuator::Compile(void) {
// if limited is auto, check for inconsistency wrt to autolimits // if limited is auto, check for inconsistency wrt to autolimits
if (forcelimited == mjLIMITED_AUTO) { if (forcelimited == mjLIMITED_AUTO) {
bool hasrange = !(forcerange[0]==0 && forcerange[1]==0); bool hasrange = !(forcerange[0]==0 && forcerange[1]==0);
checklimited(this, model->autolimits, "actuator", "force", forcelimited, hasrange); checklimited(this, compiler->autolimits, "actuator", "force", forcelimited, hasrange);
} }
if (ctrllimited == mjLIMITED_AUTO) { if (ctrllimited == mjLIMITED_AUTO) {
bool hasrange = !(ctrlrange[0]==0 && ctrlrange[1]==0); bool hasrange = !(ctrlrange[0]==0 && ctrlrange[1]==0);
checklimited(this, model->autolimits, "actuator", "ctrl", ctrllimited, hasrange); checklimited(this, compiler->autolimits, "actuator", "ctrl", ctrllimited, hasrange);
} }
if (actlimited == mjLIMITED_AUTO) { if (actlimited == mjLIMITED_AUTO) {
bool hasrange = !(actrange[0]==0 && actrange[1]==0); bool hasrange = !(actrange[0]==0 && actrange[1]==0);
checklimited(this, model->autolimits, "actuator", "act", actlimited, hasrange); checklimited(this, compiler->autolimits, "actuator", "act", actlimited, hasrange);
} }
// check limits // check limits
@@ -5951,6 +5974,7 @@ mjCSensor::mjCSensor(mjCModel* _model) {
// set model // set model
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
// clear private variables // clear private variables
spec_objname_.clear(); spec_objname_.clear();
@@ -6477,6 +6501,7 @@ mjCNumeric::mjCNumeric(mjCModel* _model) {
// set model pointer // set model pointer
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
// clear variables // clear variables
spec_data_.clear(); spec_data_.clear();
@@ -6566,6 +6591,7 @@ mjCText::mjCText(mjCModel* _model) {
// set model pointer // set model pointer
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
// clear variables // clear variables
spec_data_.clear(); spec_data_.clear();
@@ -6643,6 +6669,7 @@ mjCTuple::mjCTuple(mjCModel* _model) {
// set model pointer // set model pointer
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
// clear variables // clear variables
spec_objtype_.clear(); spec_objtype_.clear();
@@ -6776,6 +6803,7 @@ mjCKey::mjCKey(mjCModel* _model) {
// set model pointer // set model pointer
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
// clear variables // clear variables
spec_qpos_.clear(); spec_qpos_.clear();
@@ -6952,6 +6980,7 @@ mjCPlugin::mjCPlugin(mjCModel* _model) {
plugin_slot = -1; plugin_slot = -1;
parent = this; parent = this;
model = _model; model = _model;
if (_model) compiler = &_model->spec.compiler;
name.clear(); name.clear();
plugin_name.clear(); plugin_name.clear();
+2 -1
View File
@@ -212,7 +212,8 @@ class mjCBase : public mjCBase_ {
mjCBase& operator=(const mjCBase& other); mjCBase& operator=(const mjCBase& other);
mjCFrame* frame; // pointer to frame transformation mjCFrame* frame; // pointer to frame transformation
mjCModel* model; // pointer to model that created object mjCModel* model; // pointer to model that owns object
mjsCompiler* compiler; // pointer to the compiler options
virtual ~mjCBase() = default; // destructor virtual ~mjCBase() = default; // destructor
+2 -2
View File
@@ -365,8 +365,8 @@ mjSpec* ParseXML(const char* filename, const mjVFS* vfs,
// set reasonable default for parsing a URDF // set reasonable default for parsing a URDF
// this is separate from the Parser to allow multiple URDFs to be loaded. // this is separate from the Parser to allow multiple URDFs to be loaded.
spec->strippath = true; spec->strippath = true;
spec->fusestatic = true; spec->compiler.fusestatic = true;
spec->discardvisual = true; spec->compiler.discardvisual = true;
parser.SetModel(spec); parser.SetModel(spec);
parser.Parse(root); parser.Parse(root);
+19 -19
View File
@@ -972,19 +972,19 @@ void mjXReader::Compiler(XMLElement* section, mjSpec* spec) {
// top-level attributes // top-level attributes
if (MapValue(section, "autolimits", &n, bool_map, 2)) { if (MapValue(section, "autolimits", &n, bool_map, 2)) {
spec->autolimits = (n==1); spec->compiler.autolimits = (n==1);
} }
ReadAttr(section, "boundmass", 1, &spec->boundmass, text); ReadAttr(section, "boundmass", 1, &spec->compiler.boundmass, text);
ReadAttr(section, "boundinertia", 1, &spec->boundinertia, text); ReadAttr(section, "boundinertia", 1, &spec->compiler.boundinertia, text);
ReadAttr(section, "settotalmass", 1, &spec->settotalmass, text); ReadAttr(section, "settotalmass", 1, &spec->compiler.settotalmass, text);
if (MapValue(section, "balanceinertia", &n, bool_map, 2)) { if (MapValue(section, "balanceinertia", &n, bool_map, 2)) {
spec->balanceinertia = (n==1); spec->compiler.balanceinertia = (n==1);
} }
if (MapValue(section, "strippath", &n, bool_map, 2)) { if (MapValue(section, "strippath", &n, bool_map, 2)) {
spec->strippath = (n==1); spec->strippath = (n==1);
} }
if (MapValue(section, "fitaabb", &n, bool_map, 2)) { if (MapValue(section, "fitaabb", &n, bool_map, 2)) {
spec->fitaabb = (n==1); spec->compiler.fitaabb = (n==1);
} }
if (MapValue(section, "coordinate", &n, coordinate_map, 2)) { if (MapValue(section, "coordinate", &n, coordinate_map, 2)) {
if (n==1) { if (n==1) {
@@ -993,13 +993,13 @@ void mjXReader::Compiler(XMLElement* section, mjSpec* spec) {
} }
} }
if (MapValue(section, "angle", &n, angle_map, 2)) { if (MapValue(section, "angle", &n, angle_map, 2)) {
spec->degree = (n==1); spec->compiler.degree = (n==1);
} }
if (ReadAttrTxt(section, "eulerseq", text)) { if (ReadAttrTxt(section, "eulerseq", text)) {
if (text.size()!=3) { if (text.size()!=3) {
throw mjXError(section, "euler format must have length 3"); throw mjXError(section, "euler format must have length 3");
} }
memcpy(spec->eulerseq, text.c_str(), 3); memcpy(spec->compiler.eulerseq, text.c_str(), 3);
} }
if (ReadAttrTxt(section, "assetdir", text)) { if (ReadAttrTxt(section, "assetdir", text)) {
mjs_setString(spec->meshdir, text.c_str()); mjs_setString(spec->meshdir, text.c_str());
@@ -1014,27 +1014,27 @@ void mjXReader::Compiler(XMLElement* section, mjSpec* spec) {
mjs_setString(spec->texturedir, texturedir.c_str()); mjs_setString(spec->texturedir, texturedir.c_str());
} }
if (MapValue(section, "discardvisual", &n, bool_map, 2)) { if (MapValue(section, "discardvisual", &n, bool_map, 2)) {
spec->discardvisual = (n==1); spec->compiler.discardvisual = (n==1);
} }
if (MapValue(section, "convexhull", &n, bool_map, 2)) { if (MapValue(section, "convexhull", &n, bool_map, 2)) {
spec->convexhull = (n==1); spec->compiler.convexhull = (n==1);
} }
if (MapValue(section, "usethread", &n, bool_map, 2)) { if (MapValue(section, "usethread", &n, bool_map, 2)) {
spec->usethread = (n==1); spec->compiler.usethread = (n==1);
} }
if (MapValue(section, "fusestatic", &n, bool_map, 2)) { if (MapValue(section, "fusestatic", &n, bool_map, 2)) {
spec->fusestatic = (n==1); spec->compiler.fusestatic = (n==1);
} }
MapValue(section, "inertiafromgeom", &spec->inertiafromgeom, TFAuto_map, 3); MapValue(section, "inertiafromgeom", &spec->compiler.inertiafromgeom, TFAuto_map, 3);
ReadAttr(section, "inertiagrouprange", 2, spec->inertiagrouprange, text); ReadAttr(section, "inertiagrouprange", 2, spec->compiler.inertiagrouprange, text);
if (MapValue(section, "alignfree", &n, bool_map, 2)) { if (MapValue(section, "alignfree", &n, bool_map, 2)) {
spec->alignfree = (n==1); spec->compiler.alignfree = (n==1);
} }
// lengthrange subelement // lengthrange subelement
XMLElement* elem = FindSubElem(section, "lengthrange"); XMLElement* elem = FindSubElem(section, "lengthrange");
if (elem) { if (elem) {
mjLROpt* opt = &(spec->LRopt); mjLROpt* opt = &(spec->compiler.LRopt);
// flags // flags
MapValue(elem, "mode", &opt->mode, lrmode_map, lrmode_sz); MapValue(elem, "mode", &opt->mode, lrmode_map, lrmode_sz);
@@ -2759,7 +2759,7 @@ void mjXReader::OneFlexcomp(XMLElement* elem, mjsBody* body, const mjVFS* vfs) {
// make flexcomp // make flexcomp
char error[200]; char error[200];
bool res = fcomp.Make(spec, body, error, 200); bool res = fcomp.Make(body, error, 200);
// throw error // throw error
if (!res) { if (!res) {
@@ -3575,7 +3575,7 @@ void mjXReader::Body(XMLElement* section, mjsBody* body, mjsFrame* frame,
alt.type = mjORIENTATION_EULER; alt.type = mjORIENTATION_EULER;
mjuu_copyvec(alt.euler, euler, 3); mjuu_copyvec(alt.euler, euler, 3);
double rotation[4] = {1, 0, 0, 0}; double rotation[4] = {1, 0, 0, 0};
mjs_resolveOrientation(rotation, spec->degree, spec->eulerseq, &alt); mjs_resolveOrientation(rotation, spec->compiler.degree, spec->compiler.eulerseq, &alt);
// read childdef // read childdef
mjsDefault* childdef = 0; mjsDefault* childdef = 0;
@@ -3610,7 +3610,7 @@ void mjXReader::Body(XMLElement* section, mjsBody* body, mjsFrame* frame,
alt.euler[0] = i*euler[0]; alt.euler[0] = i*euler[0];
alt.euler[1] = i*euler[1]; alt.euler[1] = i*euler[1];
alt.euler[2] = i*euler[2]; alt.euler[2] = i*euler[2];
mjs_resolveOrientation(quat, spec->degree, spec->eulerseq, &alt); mjs_resolveOrientation(quat, spec->compiler.degree, spec->compiler.eulerseq, &alt);
mjuu_setvec(pframe->quat, quat[0], quat[1], quat[2], quat[3]); mjuu_setvec(pframe->quat, quat[0], quat[1], quat[2], quat[3]);
// process suffix // process suffix
+10 -10
View File
@@ -920,8 +920,8 @@ void mjXWriter::Compiler(XMLElement* root) {
XMLElement* section = InsertEnd(root, "compiler"); XMLElement* section = InsertEnd(root, "compiler");
// settings // settings
if (!model->convexhull) { if (!model->compiler.convexhull) {
WriteAttrTxt(section, "convexhull", FindValue(bool_map, 2, model->convexhull)); WriteAttrTxt(section, "convexhull", FindValue(bool_map, 2, model->compiler.convexhull));
} }
WriteAttrTxt(section, "angle", "radian"); WriteAttrTxt(section, "angle", "radian");
if (!model->get_meshdir().empty()) { if (!model->get_meshdir().empty()) {
@@ -930,20 +930,20 @@ void mjXWriter::Compiler(XMLElement* root) {
if (!model->get_texturedir().empty()) { if (!model->get_texturedir().empty()) {
WriteAttrTxt(section, "texturedir", model->get_texturedir()); WriteAttrTxt(section, "texturedir", model->get_texturedir());
} }
if (!model->usethread) { if (!model->compiler.usethread) {
WriteAttrTxt(section, "usethread", "false"); WriteAttrTxt(section, "usethread", "false");
} }
if (model->boundmass) { if (model->compiler.boundmass) {
WriteAttr(section, "boundmass", 1, &model->boundmass); WriteAttr(section, "boundmass", 1, &model->compiler.boundmass);
} }
if (model->boundinertia) { if (model->compiler.boundinertia) {
WriteAttr(section, "boundinertia", 1, &model->boundinertia); WriteAttr(section, "boundinertia", 1, &model->compiler.boundinertia);
} }
if (model->alignfree) { if (model->compiler.alignfree) {
WriteAttrTxt(section, "alignfree", "true"); WriteAttrTxt(section, "alignfree", "true");
} }
if (!model->autolimits) { if (!model->compiler.autolimits) {
WriteAttrTxt(section, "autolimits", "false"); WriteAttrTxt(section, "autolimits", "false");
} }
} }
@@ -1611,7 +1611,7 @@ void mjXWriter::Body(XMLElement* elem, mjCBody* body, mjCFrame* frame, string_vi
WriteVector(elem, "user", body->get_userdata()); WriteVector(elem, "user", body->get_userdata());
// write inertial // write inertial
if (body->explicitinertial && model->inertiafromgeom!=mjINERTIAFROMGEOM_TRUE) { if (body->explicitinertial && model->compiler.inertiafromgeom!=mjINERTIAFROMGEOM_TRUE) {
XMLElement* inertial = InsertEnd(elem, "inertial"); XMLElement* inertial = InsertEnd(elem, "inertial");
WriteAttr(inertial, "pos", 3, body->ipos); WriteAttr(inertial, "pos", 3, body->ipos);
WriteAttr(inertial, "quat", 4, body->iquat, unitq); WriteAttr(inertial, "quat", 4, body->iquat, unitq);
+2 -2
View File
@@ -109,7 +109,7 @@ void mjXURDF::Parse(
} }
// enforce required compiler defaults for URDF // enforce required compiler defaults for URDF
spec->degree = false; spec->compiler.degree = false;
// get model name // get model name
std::string modelname; std::string modelname;
@@ -316,7 +316,7 @@ void mjXURDF::Body(XMLElement* body_elem) {
} }
} }
// create geom if not discarded // create geom if not discarded
if (!spec->discardvisual) { if (!spec->compiler.discardvisual) {
pgeom = Geom(elem, pbody, false); pgeom = Geom(elem, pbody, false);
// save color // save color
+34 -14
View File
@@ -1839,23 +1839,43 @@ TEST_F(MujocoTest, RepeatedAttachKeyframe) {
mj_deleteModel(model_2); mj_deleteModel(model_2);
} }
TEST_F(MujocoTest, DifferentUnitsNotAllowed) { TEST_F(MujocoTest, DifferentUnitsAllowed) {
mjSpec* spec_1 = mj_makeSpec(); mjSpec* child = mj_makeSpec();
mjSpec* spec_2 = mj_makeSpec(); child->compiler.degree = 1;
spec_1->degree = 1; mjsBody* body = mjs_addBody(mjs_findBody(child, "world"), 0);
spec_2->degree = 0; body->alt.type = mjORIENTATION_EULER;
body->alt.euler[0] = 90;
mjsBody* body = mjs_addBody(mjs_findBody(spec_1, "world"), 0); mjSpec* parent = mj_makeSpec();
mjsFrame* frame = mjs_addFrame(mjs_findBody(spec_2, "world"), 0); parent->compiler.degree = 0;
mjsFrame* frame = mjs_addFrame(mjs_findBody(parent, "world"), 0);
frame->alt.type = mjORIENTATION_EULER;
frame->alt.euler[0] = -mjPI / 2;
constexpr char msg[] = "mjSpecs with incompatible compiler/angle"; EXPECT_THAT(mjs_attachBody(frame, body, "child-", ""), NotNull());
EXPECT_THAT(mjs_attachBody(frame, body, "child-", ""), IsNull()); mjModel* model = mj_compile(parent, 0);
EXPECT_THAT(mjs_attachFrame(body, frame, "child-", ""), IsNull()); EXPECT_THAT(model, NotNull());
EXPECT_THAT(mjs_getError(spec_1), HasSubstr(msg)); EXPECT_NEAR(model->body_quat[4], 1, 1e-12);
EXPECT_THAT(mjs_getError(spec_2), HasSubstr(msg)); EXPECT_NEAR(model->body_quat[5], 0, 1e-12);
EXPECT_NEAR(model->body_quat[6], 0, 1e-12);
EXPECT_NEAR(model->body_quat[7], 0, 1e-12);
mj_deleteSpec(spec_1); mjSpec* copy = mj_copySpec(parent);
mj_deleteSpec(spec_2); EXPECT_THAT(copy, NotNull());
mj_deleteModel(model);
mj_deleteSpec(child);
mj_deleteSpec(parent);
// check that deleting `parent` or `child` does not invalidate the copy
mjModel* copy_model = mj_compile(copy, 0);
EXPECT_THAT(copy_model, NotNull());
EXPECT_NEAR(copy_model->body_quat[0], 1, 1e-12);
EXPECT_NEAR(copy_model->body_quat[1], 0, 1e-12);
EXPECT_NEAR(copy_model->body_quat[2], 0, 1e-12);
EXPECT_NEAR(copy_model->body_quat[3], 0, 1e-12);
mj_deleteModel(copy_model);
mj_deleteSpec(copy);
} }
TEST_F(MujocoTest, CopyAttachedSpec) { TEST_F(MujocoTest, CopyAttachedSpec) {
+2 -2
View File
@@ -480,8 +480,8 @@ TEST_F(LengthRangeTest, LengthRangeThreading) {
DoubleNear(std::sqrt(5.0), 1e-3)); DoubleNear(std::sqrt(5.0), 1e-3));
// recompile without threads // recompile without threads
ASSERT_EQ(spec->usethread, 1); ASSERT_EQ(spec->compiler.usethread, 1);
spec->usethread = 0; spec->compiler.usethread = 0;
mjModel* model2 = mj_compile(spec, 0); mjModel* model2 = mj_compile(spec, 0);
EXPECT_THAT(model2, NotNull()) << error; EXPECT_THAT(model2, NotNull()) << error;
+2 -2
View File
@@ -1176,7 +1176,7 @@ TEST_F(MjCJointTest, AlignFree) {
std::array<char, 1024> err; std::array<char, 1024> err;
mjSpec* s = mj_parseXML(xml_path.c_str(), nullptr, err.data(), err.size()); mjSpec* s = mj_parseXML(xml_path.c_str(), nullptr, err.data(), err.size());
ASSERT_THAT(s, NotNull()) << err.data(); ASSERT_THAT(s, NotNull()) << err.data();
s->alignfree = 1; // auto-aligned free joint s->compiler.alignfree = 1; // auto-aligned free joint
mjModel* m = mj_compile(s, nullptr); mjModel* m = mj_compile(s, nullptr);
ASSERT_THAT(m, NotNull()); ASSERT_THAT(m, NotNull());
@@ -1186,7 +1186,7 @@ TEST_F(MjCJointTest, AlignFree) {
EXPECT_EQ(m->dof_simplenum[0], 6); EXPECT_EQ(m->dof_simplenum[0], 6);
// make unaligned model // make unaligned model
s->alignfree = 0; // unaligned free joint s->compiler.alignfree = 0; // unaligned free joint
mjModel* m_u = mj_compile(s, nullptr); mjModel* m_u = mj_compile(s, nullptr);
ASSERT_THAT(m_u, NotNull()); ASSERT_THAT(m_u, NotNull());
+20
View File
@@ -5716,6 +5716,26 @@ public unsafe struct mjrContext_ {
public int readDepthMap; public int readDepthMap;
} }
[StructLayout(LayoutKind.Sequential)]
public unsafe struct mjsCompiler_ {
public byte autolimits;
public double boundmass;
public double boundinertia;
public double settotalmass;
public byte balanceinertia;
public byte fitaabb;
public byte degree;
public fixed sbyte eulerseq[3];
public byte discardvisual;
public byte convexhull;
public byte usethread;
public byte fusestatic;
public int inertiafromgeom;
public fixed int inertiagrouprange[2];
public int alignfree;
public mjLROpt_ LRopt;
}
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public unsafe struct mjuiState_ { public unsafe struct mjuiState_ {
public int nrect; public int nrect;