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