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:
committed by
Copybara-Service
parent
1043633cc3
commit
159f23e6b4
@@ -1140,6 +1140,16 @@ behavior.
|
||||
.. mujoco-include:: mjsElement
|
||||
|
||||
|
||||
.. _mjsCompiler:
|
||||
|
||||
mjsCompiler
|
||||
~~~~~~~~~~
|
||||
|
||||
Compiler options.
|
||||
|
||||
.. mujoco-include:: mjsCompiler
|
||||
|
||||
|
||||
.. _mjsBody:
|
||||
|
||||
mjsBody
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user