// Copyright 2021 DeepMind Technologies Limited // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "xml/xml_native_reader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "tinyxml2.h" #include #include #include #include #include "engine/engine_plugin.h" #include "engine/engine_util_errmem.h" #include "engine/engine_util_misc.h" #include "user/user_api.h" #include "user/user_composite.h" #include "user/user_flexcomp.h" #include "user/user_util.h" #include "xml/xml_base.h" #include "xml/xml_util.h" namespace { using std::string; using std::vector; using tinyxml2::XMLElement; void ReadPluginConfigs(tinyxml2::XMLElement* elem, mjsPlugin* p) { std::map> config_attribs; XMLElement* child = FirstChildElement(elem); while (child) { std::string_view name = child->Value(); if (name == "config") { std::string key, value; mjXUtil::ReadAttrTxt(child, "key", key, /* required = */ true); if (config_attribs.find(key) != config_attribs.end()) { std::string err = "duplicate config key: " + key; throw mjXError(child, "%s", err.c_str()); } mjXUtil::ReadAttrTxt(child, "value", value, /* required = */ true); config_attribs[key] = value; } child = NextSiblingElement(child); } if (!p && !config_attribs.empty()) { throw mjXError(elem, "plugin configuration attributes cannot be used in an " "element that references a predefined plugin instance"); } else if (p) { mjs_setPluginAttributes(p, &config_attribs); } } } // namespace //---------------------------------- MJCF schema --------------------------------------------------- const char* MJCF[nMJCF][mjXATTRNUM] = { {"mujoco", "!", "1", "model"}, {"<"}, {"compiler", "*", "20", "autolimits", "boundmass", "boundinertia", "settotalmass", "balanceinertia", "strippath", "coordinate", "angle", "fitaabb", "eulerseq", "meshdir", "texturedir", "discardvisual", "convexhull", "usethread", "fusestatic", "inertiafromgeom", "inertiagrouprange", "exactmeshinertia", "assetdir"}, {"<"}, {"lengthrange", "?", "10", "mode", "useexisting", "uselimit", "accel", "maxforce", "timeconst", "timestep", "inttotal", "interval", "tolrange"}, {">"}, {"option", "*", "27", "timestep", "apirate", "impratio", "tolerance", "ls_tolerance", "noslip_tolerance", "mpr_tolerance", "gravity", "wind", "magnetic", "density", "viscosity", "o_margin", "o_solref", "o_solimp", "o_friction", "integrator", "cone", "jacobian", "solver", "iterations", "ls_iterations", "noslip_iterations", "mpr_iterations", "sdf_iterations", "sdf_initpoints", "actuatorgroupdisable"}, {"<"}, {"flag", "?", "22", "constraint", "equality", "frictionloss", "limit", "contact", "passive", "gravity", "clampctrl", "warmstart", "filterparent", "actuation", "refsafe", "sensor", "midphase", "eulerdamp", "override", "energy", "fwdinv", "invdiscrete", "sensornoise", "multiccd", "island"}, {">"}, {"size", "*", "14", "memory", "njmax", "nconmax", "nstack", "nuserdata", "nkey", "nuser_body", "nuser_jnt", "nuser_geom", "nuser_site", "nuser_cam", "nuser_tendon", "nuser_actuator", "nuser_sensor"}, {"visual", "*", "0"}, {"<"}, {"global", "?", "11", "fovy", "ipd", "azimuth", "elevation", "linewidth", "glow", "offwidth", "offheight", "realtime", "ellipsoidinertia", "bvactive"}, {"quality", "?", "5", "shadowsize", "offsamples", "numslices", "numstacks", "numquads"}, {"headlight", "?", "4", "ambient", "diffuse", "specular", "active"}, {"map", "?", "13", "stiffness", "stiffnessrot", "force", "torque", "alpha", "fogstart", "fogend", "znear", "zfar", "haze", "shadowclip", "shadowscale", "actuatortendon"}, {"scale", "?", "17", "forcewidth", "contactwidth", "contactheight", "connect", "com", "camera", "light", "selectpoint", "jointlength", "jointwidth", "actuatorlength", "actuatorwidth", "framelength", "framewidth", "constraint", "slidercrank", "frustum"}, {"rgba", "?", "25", "fog", "haze", "force", "inertia", "joint", "actuator", "actuatornegative", "actuatorpositive", "com", "camera", "light", "selectpoint", "connect", "contactpoint", "contactforce", "contactfriction", "contacttorque", "contactgap", "rangefinder", "constraint", "slidercrank", "crankbroken", "frustum", "bv", "bvactive"}, {">"}, {"statistic", "*", "5", "meaninertia", "meanmass", "meansize", "extent", "center"}, {"default", "R", "1", "class"}, {"<"}, {"mesh", "?", "1", "scale"}, {"material", "?", "8", "texture", "emission", "specular", "shininess", "reflectance", "rgba", "texrepeat", "texuniform"}, {"joint", "?", "21", "type", "group", "pos", "axis", "springdamper", "limited", "actuatorfrclimited", "solreflimit", "solimplimit", "solreffriction", "solimpfriction", "stiffness", "range", "actuatorfrcrange", "margin", "ref", "springref", "armature", "damping", "frictionloss", "user"}, {"geom", "?", "31", "type", "pos", "quat", "contype", "conaffinity", "condim", "group", "priority", "size", "material", "friction", "mass", "density", "shellinertia", "solmix", "solref", "solimp", "margin", "gap", "fromto", "axisangle", "xyaxes", "zaxis", "euler", "hfield", "mesh", "fitscale", "rgba", "fluidshape", "fluidcoef", "user"}, {"site", "?", "13", "type", "group", "pos", "quat", "material", "size", "fromto", "axisangle", "xyaxes", "zaxis", "euler", "rgba", "user"}, {"camera", "?", "16", "fovy", "ipd", "resolution", "pos", "quat", "axisangle", "xyaxes", "zaxis", "euler", "mode", "focal", "focalpixel", "principal", "principalpixel", "sensorsize", "user"}, {"light", "?", "12", "pos", "dir", "directional", "castshadow", "active", "attenuation", "cutoff", "exponent", "ambient", "diffuse", "specular", "mode"}, {"pair", "?", "7", "condim", "friction", "solref", "solreffriction", "solimp", "gap", "margin"}, {"equality", "?", "3", "active", "solref", "solimp"}, {"tendon", "?", "16", "group", "limited", "range", "solreflimit", "solimplimit", "solreffriction", "solimpfriction", "frictionloss", "springlength", "width", "material", "margin", "stiffness", "damping", "rgba", "user"}, {"general", "?", "18", "ctrllimited", "forcelimited", "actlimited", "ctrlrange", "forcerange", "actrange", "gear", "cranklength", "user", "group", "actdim", "dyntype", "gaintype", "biastype", "dynprm", "gainprm", "biasprm", "actearly"}, {"motor", "?", "8", "ctrllimited", "forcelimited", "ctrlrange", "forcerange", "gear", "cranklength", "user", "group"}, {"position", "?", "11", "ctrllimited", "forcelimited", "ctrlrange", "inheritrange", "forcerange", "gear", "cranklength", "user", "group", "kp", "kv"}, {"velocity", "?", "9", "ctrllimited", "forcelimited", "ctrlrange", "forcerange", "gear", "cranklength", "user", "group", "kv"}, {"intvelocity", "?", "12", "ctrllimited", "forcelimited", "ctrlrange", "forcerange", "actrange", "inheritrange", "gear", "cranklength", "user", "group", "kp", "kv"}, {"damper", "?", "8", "forcelimited", "ctrlrange", "forcerange", "gear", "cranklength", "user", "group", "kv"}, {"cylinder", "?", "12", "ctrllimited", "forcelimited", "ctrlrange", "forcerange", "gear", "cranklength", "user", "group", "timeconst", "area", "diameter", "bias"}, {"muscle", "?", "17", "ctrllimited", "forcelimited", "ctrlrange", "forcerange", "gear", "cranklength", "user", "group", "timeconst", "range", "force", "scale", "lmin", "lmax", "vmax", "fpmax", "fvmax"}, {"adhesion", "?", "6", "forcelimited", "ctrlrange", "forcerange", "gain", "user", "group"}, {">"}, {"extension", "*", "0"}, {"<"}, {"plugin", "*", "1", "plugin"}, {"<"}, {"instance", "*", "1", "name"}, {"<"}, {"config", "*", "2", "key", "value"}, {">"}, {">"}, {">"}, {"custom", "*", "0"}, {"<"}, {"numeric", "*", "3", "name", "size", "data"}, {"text", "*", "2", "name", "data"}, {"tuple", "*", "1", "name"}, {"<"}, {"element", "*", "3", "objtype", "objname", "prm"}, {">"}, {">"}, {"asset", "*", "0"}, {"<"}, {"mesh", "*", "12", "name", "class", "content_type", "file", "vertex", "normal", "texcoord", "face", "refpos", "refquat", "scale", "smoothnormal"}, {"<"}, {"plugin", "*", "2", "plugin", "instance"}, {"<"}, {"config", "*", "2", "key", "value"}, {">"}, {">"}, {"hfield", "*", "7", "name", "content_type", "file", "nrow", "ncol", "size", "elevation"}, {"skin", "*", "9", "name", "file", "material", "rgba", "inflate", "vertex", "texcoord", "face", "group"}, {"<"}, {"bone", "*", "5", "body", "bindpos", "bindquat", "vertid", "vertweight"}, {">"}, {"texture", "*", "22", "name", "type", "content_type", "file", "gridsize", "gridlayout", "fileright", "fileleft", "fileup", "filedown", "filefront", "fileback", "builtin", "rgb1", "rgb2", "mark", "markrgb", "random", "width", "height", "hflip", "vflip"}, {"material", "*", "10", "name", "class", "texture", "texrepeat", "texuniform", "emission", "specular", "shininess", "reflectance", "rgba"}, {">"}, {"body", "R", "11", "name", "childclass", "pos", "quat", "mocap", "axisangle", "xyaxes", "zaxis", "euler", "gravcomp", "user"}, {"<"}, {"inertial", "?", "9", "pos", "quat", "mass", "diaginertia", "axisangle", "xyaxes", "zaxis", "euler", "fullinertia"}, {"joint", "*", "23", "name", "class", "type", "group", "pos", "axis", "springdamper", "limited", "actuatorfrclimited", "solreflimit", "solimplimit", "solreffriction", "solimpfriction", "stiffness", "range", "actuatorfrcrange", "margin", "ref", "springref", "armature", "damping", "frictionloss", "user"}, {"freejoint", "*", "2", "name", "group"}, {"geom", "*", "33", "name", "class", "type", "contype", "conaffinity", "condim", "group", "priority", "size", "material", "friction", "mass", "density", "shellinertia", "solmix", "solref", "solimp", "margin", "gap", "fromto", "pos", "quat", "axisangle", "xyaxes", "zaxis", "euler", "hfield", "mesh", "fitscale", "rgba", "fluidshape", "fluidcoef", "user"}, {"<"}, {"plugin", "*", "2", "plugin", "instance"}, {"<"}, {"config", "*", "2", "key", "value"}, {">"}, {">"}, {"site", "*", "15", "name", "class", "type", "group", "pos", "quat", "material", "size", "fromto", "axisangle", "xyaxes", "zaxis", "euler", "rgba", "user"}, {"camera", "*", "19", "name", "class", "fovy", "ipd", "resolution", "pos", "quat", "axisangle", "xyaxes", "zaxis", "euler", "mode", "target", "focal", "focalpixel", "principal", "principalpixel", "sensorsize", "user"}, {"light", "*", "15", "name", "class", "directional", "castshadow", "active", "pos", "dir", "attenuation", "cutoff", "exponent", "ambient", "diffuse", "specular", "mode", "target"}, {"plugin", "*", "2", "plugin", "instance"}, {"<"}, {"config", "*", "2", "key", "value"}, {">"}, {"composite", "*", "13", "prefix", "type", "count", "spacing", "offset", "flatinertia", "solrefsmooth", "solimpsmooth", "vertex", "face", "initial", "curve", "size"}, {"<"}, {"joint", "*", "17", "kind", "group", "stiffness", "damping", "armature", "solreffix", "solimpfix", "type", "axis", "limited", "range", "margin", "solreflimit", "solimplimit", "frictionloss", "solreffriction", "solimpfriction"}, {"tendon", "*", "17", "kind", "group", "stiffness", "damping", "solreffix", "solimpfix", "limited", "range", "margin", "solreflimit", "solimplimit", "frictionloss", "solreffriction", "solimpfriction", "material", "rgba", "width"}, {"skin", "?", "6", "texcoord", "material", "group", "rgba", "inflate", "subgrid"}, {"geom", "?", "17", "type", "contype", "conaffinity", "condim", "group", "priority", "size", "material", "rgba", "friction", "mass", "density", "solmix", "solref", "solimp", "margin", "gap"}, {"site", "?", "4", "group", "size", "material", "rgba"}, {"pin", "*", "1", "coord"}, {"plugin", "*", "2", "plugin", "instance"}, {"<"}, {"config", "*", "2", "key", "value"}, {">"}, {">"}, {"flexcomp", "*", "24", "name", "type", "group", "dim", "count", "spacing", "radius", "rigid", "mass", "inertiabox", "scale", "file", "point", "element", "texcoord", "material", "rgba", "flatskin", "pos", "quat", "axisangle", "xyaxes", "zaxis", "euler"}, {"<"}, {"edge", "?", "5", "equality", "solref", "solimp", "stiffness", "damping"}, {"contact", "?", "13", "contype", "conaffinity", "condim", "priority", "friction", "solmix", "solref", "solimp", "margin", "gap", "internal", "selfcollide", "activelayers"}, {"pin", "*", "4", "id", "range", "grid", "gridrange"}, {"plugin", "*", "2", "plugin", "instance"}, {"<"}, {"config", "*", "2", "key", "value"}, {">"}, {">"}, {">"}, {"deformable", "*", "0"}, {"<"}, {"flex", "*", "11", "name", "group", "dim", "radius", "material", "rgba", "flatskin", "body", "vertex", "element", "texcoord"}, {"<"}, {"contact", "?", "13", "contype", "conaffinity", "condim", "priority", "friction", "solmix", "solref", "solimp", "margin", "gap", "internal", "selfcollide", "activelayers"}, {"edge", "?", "2", "stiffness", "damping"}, {">"}, {"skin", "*", "9", "name", "file", "material", "rgba", "inflate", "vertex", "texcoord", "face", "group"}, {"<"}, {"bone", "*", "5", "body", "bindpos", "bindquat", "vertid", "vertweight"}, {">"}, {">"}, {"contact", "*", "0"}, {"<"}, {"pair", "*", "11", "name", "class", "geom1", "geom2", "condim", "friction", "solref", "solreffriction", "solimp", "gap", "margin"}, {"exclude", "*", "3", "name", "body1", "body2"}, {">"}, {"equality", "*", "0"}, {"<"}, {"connect", "*", "8", "name", "class", "body1", "body2", "anchor", "active", "solref", "solimp"}, {"weld", "*", "10", "name", "class", "body1", "body2", "relpose", "anchor", "active", "solref", "solimp", "torquescale"}, {"joint", "*", "8", "name", "class", "joint1", "joint2", "polycoef", "active", "solref", "solimp"}, {"tendon", "*", "8", "name", "class", "tendon1", "tendon2", "polycoef", "active", "solref", "solimp"}, {"flex", "*", "6", "name", "class", "flex", "active", "solref", "solimp"}, {">"}, {"tendon", "*", "0"}, {"<"}, {"spatial", "*", "18", "name", "class", "group", "limited", "range", "solreflimit", "solimplimit", "solreffriction", "solimpfriction", "frictionloss", "springlength", "width", "material", "margin", "stiffness", "damping", "rgba", "user"}, {"<"}, {"site", "*", "1", "site"}, {"geom", "*", "2", "geom", "sidesite"}, {"pulley", "*", "1", "divisor"}, {">"}, {"fixed", "*", "15", "name", "class", "group", "limited", "range", "solreflimit", "solimplimit", "solreffriction", "solimpfriction", "frictionloss", "springlength", "margin", "stiffness", "damping", "user"}, {"<"}, {"joint", "*", "2", "joint", "coef"}, {">"}, {">"}, {"actuator", "*", "0"}, {"<"}, {"general", "*", "29", "name", "class", "group", "ctrllimited", "forcelimited", "actlimited", "ctrlrange", "forcerange", "actrange", "lengthrange", "gear", "cranklength", "user", "joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite", "body", "actdim", "dyntype", "gaintype", "biastype", "dynprm", "gainprm", "biasprm", "actearly"}, {"motor", "*", "18", "name", "class", "group", "ctrllimited", "forcelimited", "ctrlrange", "forcerange", "lengthrange", "gear", "cranklength", "user", "joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite"}, {"position", "*", "21", "name", "class", "group", "ctrllimited", "forcelimited", "ctrlrange", "inheritrange", "forcerange", "lengthrange", "gear", "cranklength", "user", "joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite", "kp", "kv"}, {"velocity", "*", "19", "name", "class", "group", "ctrllimited", "forcelimited", "ctrlrange", "forcerange", "lengthrange", "gear", "cranklength", "user", "joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite", "kv"}, {"intvelocity", "*", "22", "name", "class", "group", "ctrllimited", "forcelimited", "ctrlrange", "forcerange", "actrange", "inheritrange", "lengthrange", "gear", "cranklength", "user", "joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite", "kp", "kv"}, {"damper", "*", "18", "name", "class", "group", "forcelimited", "ctrlrange", "forcerange", "lengthrange", "gear", "cranklength", "user", "joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite", "kv"}, {"cylinder", "*", "22", "name", "class", "group", "ctrllimited", "forcelimited", "ctrlrange", "forcerange", "lengthrange", "gear", "cranklength", "user", "joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite", "timeconst", "area", "diameter", "bias"}, {"muscle", "*", "26", "name", "class", "group", "ctrllimited", "forcelimited", "ctrlrange", "forcerange", "lengthrange", "gear", "cranklength", "user", "joint", "jointinparent", "tendon", "slidersite", "cranksite", "timeconst", "tausmooth", "range", "force", "scale", "lmin", "lmax", "vmax", "fpmax", "fvmax"}, {"adhesion", "*", "9", "name", "class", "group", "forcelimited", "ctrlrange", "forcerange", "user", "body", "gain"}, {"plugin", "*", "24", "name", "class", "plugin", "instance", "group", "ctrllimited", "forcelimited", "actlimited", "ctrlrange", "forcerange", "actrange", "lengthrange", "gear", "cranklength", "joint", "jointinparent", "site", "dyntype", "dynprm", "tendon", "cranksite", "slidersite", "user", "actearly"}, {"<"}, {"config", "*", "2", "key", "value"}, {">"}, {">"}, {"sensor", "*", "0"}, {"<"}, {"touch", "*", "5", "name", "site", "cutoff", "noise", "user"}, {"accelerometer", "*", "5", "name", "site", "cutoff", "noise", "user"}, {"velocimeter", "*", "5", "name", "site", "cutoff", "noise", "user"}, {"gyro", "*", "5", "name", "site", "cutoff", "noise", "user"}, {"force", "*", "5", "name", "site", "cutoff", "noise", "user"}, {"torque", "*", "5", "name", "site", "cutoff", "noise", "user"}, {"magnetometer", "*", "5", "name", "site", "cutoff", "noise", "user"}, {"camprojection", "*", "6", "name", "site", "camera", "cutoff", "noise", "user"}, {"rangefinder", "*", "5", "name", "site", "cutoff", "noise", "user"}, {"jointpos", "*", "5", "name", "joint", "cutoff", "noise", "user"}, {"jointvel", "*", "5", "name", "joint", "cutoff", "noise", "user"}, {"tendonpos", "*", "5", "name", "tendon", "cutoff", "noise", "user"}, {"tendonvel", "*", "5", "name", "tendon", "cutoff", "noise", "user"}, {"actuatorpos", "*", "5", "name", "actuator", "cutoff", "noise", "user"}, {"actuatorvel", "*", "5", "name", "actuator", "cutoff", "noise", "user"}, {"actuatorfrc", "*", "5", "name", "actuator", "cutoff", "noise", "user"}, {"jointactuatorfrc", "*", "5", "name", "joint", "cutoff", "noise", "user"}, {"ballquat", "*", "5", "name", "joint", "cutoff", "noise", "user"}, {"ballangvel", "*", "5", "name", "joint", "cutoff", "noise", "user"}, {"jointlimitpos", "*", "5", "name", "joint", "cutoff", "noise", "user"}, {"jointlimitvel", "*", "5", "name", "joint", "cutoff", "noise", "user"}, {"jointlimitfrc", "*", "5", "name", "joint", "cutoff", "noise", "user"}, {"tendonlimitpos", "*", "5", "name", "tendon", "cutoff", "noise", "user"}, {"tendonlimitvel", "*", "5", "name", "tendon", "cutoff", "noise", "user"}, {"tendonlimitfrc", "*", "5", "name", "tendon", "cutoff", "noise", "user"}, {"framepos", "*", "8", "name", "objtype", "objname", "reftype", "refname", "cutoff", "noise", "user"}, {"framequat", "*", "8", "name", "objtype", "objname", "reftype", "refname", "cutoff", "noise", "user"}, {"framexaxis", "*", "8", "name", "objtype", "objname", "reftype", "refname", "cutoff", "noise", "user"}, {"frameyaxis", "*", "8", "name", "objtype", "objname", "reftype", "refname", "cutoff", "noise", "user"}, {"framezaxis", "*", "8", "name", "objtype", "objname", "reftype", "refname", "cutoff", "noise", "user"}, {"framelinvel", "*", "8", "name", "objtype", "objname", "reftype", "refname", "cutoff", "noise", "user"}, {"frameangvel", "*", "8", "name", "objtype", "objname", "reftype", "refname", "cutoff", "noise", "user"}, {"framelinacc", "*", "6", "name", "objtype", "objname", "cutoff", "noise", "user"}, {"frameangacc", "*", "6", "name", "objtype", "objname", "cutoff", "noise", "user"}, {"subtreecom", "*", "5", "name", "body", "cutoff", "noise", "user"}, {"subtreelinvel", "*", "5", "name", "body", "cutoff", "noise", "user"}, {"subtreeangmom", "*", "5", "name", "body", "cutoff", "noise", "user"}, {"clock", "*", "4", "name", "cutoff", "noise", "user"}, {"user", "*", "9", "name", "objtype", "objname", "datatype", "needstage", "dim", "cutoff", "noise", "user"}, {"plugin", "*", "9", "name", "plugin", "instance", "cutoff", "objtype", "objname", "reftype", "refname", "user"}, {"<"}, {"config", "*", "2", "key", "value"}, {">"}, {">"}, {"keyframe", "*", "0"}, {"<"}, {"key", "*", "8", "name", "time", "qpos", "qvel", "act", "mpos", "mquat", "ctrl"}, {">"}, {">"} }; //---------------------------------- MJCF keywords used in attributes ------------------------------ // coordinate type const mjMap coordinate_map[2] = { {"local", 0}, {"global", 1} }; // angle type const mjMap angle_map[2] = { {"radian", 0}, {"degree", 1} }; // bool type const mjMap bool_map[2] = { {"false", 0}, {"true", 1} }; // fluidshape type const mjMap fluid_map[2] = { {"none", 0}, {"ellipsoid", 1} }; // enable type const mjMap enable_map[2] = { {"disable", 0}, {"enable", 1} }; // TFAuto type const mjMap TFAuto_map[3] = { {"false", 0}, {"true", 1}, {"auto", 2} }; // joint type const int joint_sz = 4; const mjMap joint_map[joint_sz] = { {"free", mjJNT_FREE}, {"ball", mjJNT_BALL}, {"slide", mjJNT_SLIDE}, {"hinge", mjJNT_HINGE} }; // geom type const mjMap geom_map[mjNGEOMTYPES] = { {"plane", mjGEOM_PLANE}, {"hfield", mjGEOM_HFIELD}, {"sphere", mjGEOM_SPHERE}, {"capsule", mjGEOM_CAPSULE}, {"ellipsoid", mjGEOM_ELLIPSOID}, {"cylinder", mjGEOM_CYLINDER}, {"box", mjGEOM_BOX}, {"mesh", mjGEOM_MESH}, {"sdf", mjGEOM_SDF} }; // camlight type const int camlight_sz = 5; const mjMap camlight_map[camlight_sz] = { {"fixed", mjCAMLIGHT_FIXED}, {"track", mjCAMLIGHT_TRACK}, {"trackcom", mjCAMLIGHT_TRACKCOM}, {"targetbody", mjCAMLIGHT_TARGETBODY}, {"targetbodycom", mjCAMLIGHT_TARGETBODYCOM} }; // integrator type const int integrator_sz = 4; const mjMap integrator_map[integrator_sz] = { {"Euler", mjINT_EULER}, {"RK4", mjINT_RK4}, {"implicit", mjINT_IMPLICIT}, {"implicitfast", mjINT_IMPLICITFAST} }; // cone type const int cone_sz = 2; const mjMap cone_map[cone_sz] = { {"pyramidal", mjCONE_PYRAMIDAL}, {"elliptic", mjCONE_ELLIPTIC} }; // Jacobian type const int jac_sz = 3; const mjMap jac_map[jac_sz] = { {"dense", mjJAC_DENSE}, {"sparse", mjJAC_SPARSE}, {"auto", mjJAC_AUTO} }; // solver type const int solver_sz = 3; const mjMap solver_map[solver_sz] = { {"PGS", mjSOL_PGS}, {"CG", mjSOL_CG}, {"Newton", mjSOL_NEWTON} }; // constraint type const int equality_sz = 6; const mjMap equality_map[equality_sz] = { {"connect", mjEQ_CONNECT}, {"weld", mjEQ_WELD}, {"joint", mjEQ_JOINT}, {"tendon", mjEQ_TENDON}, {"flex", mjEQ_FLEX}, {"distance", mjEQ_DISTANCE} }; // type for texture const int texture_sz = 3; const mjMap texture_map[texture_sz] = { {"2d", mjTEXTURE_2D}, {"cube", mjTEXTURE_CUBE}, {"skybox", mjTEXTURE_SKYBOX} }; // builtin type for texture const int builtin_sz = 4; const mjMap builtin_map[builtin_sz] = { {"none", mjBUILTIN_NONE}, {"gradient", mjBUILTIN_GRADIENT}, {"checker", mjBUILTIN_CHECKER}, {"flat", mjBUILTIN_FLAT} }; // mark type for texture const int mark_sz = 4; const mjMap mark_map[mark_sz] = { {"none", mjMARK_NONE}, {"edge", mjMARK_EDGE}, {"cross", mjMARK_CROSS}, {"random", mjMARK_RANDOM} }; // dyn type const int dyn_sz = 6; const mjMap dyn_map[dyn_sz] = { {"none", mjDYN_NONE}, {"integrator", mjDYN_INTEGRATOR}, {"filter", mjDYN_FILTER}, {"filterexact", mjDYN_FILTEREXACT}, {"muscle", mjDYN_MUSCLE}, {"user", mjDYN_USER} }; // gain type const int gain_sz = 4; const mjMap gain_map[gain_sz] = { {"fixed", mjGAIN_FIXED}, {"affine", mjGAIN_AFFINE}, {"muscle", mjGAIN_MUSCLE}, {"user", mjGAIN_USER} }; // bias type const int bias_sz = 4; const mjMap bias_map[bias_sz] = { {"none", mjBIAS_NONE}, {"affine", mjBIAS_AFFINE}, {"muscle", mjBIAS_MUSCLE}, {"user", mjBIAS_USER} }; // stage type const int stage_sz = 4; const mjMap stage_map[stage_sz] = { {"none", mjSTAGE_NONE}, {"pos", mjSTAGE_POS}, {"vel", mjSTAGE_VEL}, {"acc", mjSTAGE_ACC} }; // data type const int datatype_sz = 4; const mjMap datatype_map[datatype_sz] = { {"real", mjDATATYPE_REAL}, {"positive", mjDATATYPE_POSITIVE}, {"axis", mjDATATYPE_AXIS}, {"quaternion", mjDATATYPE_QUATERNION} }; // LR mode const int lrmode_sz = 4; const mjMap lrmode_map[lrmode_sz] = { {"none", mjLRMODE_NONE}, {"muscle", mjLRMODE_MUSCLE}, {"muscleuser", mjLRMODE_MUSCLEUSER}, {"all", mjLRMODE_ALL} }; // composite type const mjMap comp_map[mjNCOMPTYPES] = { {"particle", mjCOMPTYPE_PARTICLE}, {"grid", mjCOMPTYPE_GRID}, {"rope", mjCOMPTYPE_ROPE}, {"loop", mjCOMPTYPE_LOOP}, {"cable", mjCOMPTYPE_CABLE}, {"cloth", mjCOMPTYPE_CLOTH}, {"box", mjCOMPTYPE_BOX}, {"cylinder", mjCOMPTYPE_CYLINDER}, {"ellipsoid", mjCOMPTYPE_ELLIPSOID} }; // composite joint kind const mjMap jkind_map[4] = { {"main", mjCOMPKIND_JOINT}, {"twist", mjCOMPKIND_TWIST}, {"stretch", mjCOMPKIND_STRETCH}, {"particle", mjCOMPKIND_PARTICLE} }; // composite rope shape const mjMap shape_map[mjNCOMPSHAPES] = { {"s", mjCOMPSHAPE_LINE}, {"cos(s)", mjCOMPSHAPE_COS}, {"sin(s)", mjCOMPSHAPE_SIN}, {"0", mjCOMPSHAPE_ZERO} }; // composite tendon kind const mjMap tkind_map[2] = { {"main", mjCOMPKIND_TENDON}, {"shear", mjCOMPKIND_SHEAR} }; // mesh type const mjMap meshtype_map[2] = { {"false", mjINERTIA_VOLUME}, {"true", mjINERTIA_SHELL}, }; // flexcomp type const mjMap fcomp_map[mjNFCOMPTYPES] = { {"grid", mjFCOMPTYPE_GRID}, {"box", mjFCOMPTYPE_BOX}, {"cylinder", mjFCOMPTYPE_CYLINDER}, {"ellipsoid", mjFCOMPTYPE_ELLIPSOID}, {"square", mjFCOMPTYPE_SQUARE}, {"disc", mjFCOMPTYPE_DISC}, {"mesh", mjFCOMPTYPE_MESH}, {"gmsh", mjFCOMPTYPE_GMSH}, {"direct", mjFCOMPTYPE_DIRECT} }; // flex selfcollide type const mjMap flexself_map[5] = { {"none", mjFLEXSELF_NONE}, {"narrow", mjFLEXSELF_NARROW}, {"bvh", mjFLEXSELF_BVH}, {"sap", mjFLEXSELF_SAP}, {"auto", mjFLEXSELF_AUTO}, }; //---------------------------------- class mjXReader implementation -------------------------------- // constructor mjXReader::mjXReader() : schema(MJCF, nMJCF) { readingdefaults = false; } // print schema void mjXReader::PrintSchema(std::stringstream& str, bool html, bool pad) { if (html) { schema.PrintHTML(str, 0, pad); } else { schema.Print(str, 0); } } // main entry point for XML parser // mjCModel is allocated here; caller is responsible for deallocation void mjXReader::Parse(XMLElement* root) { // check schema if (!schema.GetError().empty()) { throw mjXError(0, "XML Schema Construction Error: %s\n", schema.GetError().c_str()); } // validate XMLElement* bad = 0; if ((bad = schema.Check(root, 0))) { throw mjXError(bad, "Schema violation: %s\n", schema.GetError().c_str()); } // get model name string modelname; if (ReadAttrTxt(root, "model", modelname)) { mjs_setString(model->modelname, modelname.c_str()); } // get comment if (root->FirstChild() && root->FirstChild()->ToComment()) { mjs_setString(model->comment, root->FirstChild()->Value()); } else { mjs_setString(model->comment, ""); } //------------------- parse MuJoCo sections embedded in all XML formats for (XMLElement* section = FirstChildElement(root, "compiler"); section; section = NextSiblingElement(section, "compiler")) { Compiler(section, model); } for (XMLElement* section = FirstChildElement(root, "option"); section; section = NextSiblingElement(section, "option")) { Option(section, &model->option); } for (XMLElement* section = FirstChildElement(root, "size"); section; section = NextSiblingElement(section, "size")) { Size(section, model); } //------------------ parse MJCF-specific sections for (XMLElement* section = FirstChildElement(root, "visual"); section; section = NextSiblingElement(section, "visual")) { Visual(section); } for (XMLElement* section = FirstChildElement(root, "statistic"); section; section = NextSiblingElement(section, "statistic")) { Statistic(section); } readingdefaults = true; for (XMLElement* section = FirstChildElement(root, "default"); section; section = NextSiblingElement(section, "default")) { Default(section, -1); } readingdefaults = false; for (XMLElement* section = FirstChildElement(root, "extension"); section; section = NextSiblingElement(section, "extension")) { Extension(section); } for (XMLElement* section = FirstChildElement(root, "custom"); section; section = NextSiblingElement(section, "custom")) { Custom(section); } for (XMLElement* section = FirstChildElement(root, "asset"); section; section = NextSiblingElement(section, "asset")) { Asset(section); } for (XMLElement* section = FirstChildElement(root, "worldbody"); section; section = NextSiblingElement(section, "worldbody")) { Body(section, mjs_findBody(model, "world"), nullptr); } for (XMLElement* section = FirstChildElement(root, "contact"); section; section = NextSiblingElement(section, "contact")) { Contact(section); } for (XMLElement* section = FirstChildElement(root, "deformable"); section; section = NextSiblingElement(section, "deformable")) { Deformable(section); } for (XMLElement* section = FirstChildElement(root, "equality"); section; section = NextSiblingElement(section, "equality")) { Equality(section); } for (XMLElement* section = FirstChildElement(root, "tendon"); section; section = NextSiblingElement(section, "tendon")) { Tendon(section); } for (XMLElement* section = FirstChildElement(root, "actuator"); section; section = NextSiblingElement(section, "actuator")) { Actuator(section); } for (XMLElement* section = FirstChildElement(root, "sensor"); section; section = NextSiblingElement(section, "sensor")) { Sensor(section); } for (XMLElement* section = FirstChildElement(root, "keyframe"); section; section = NextSiblingElement(section, "keyframe")) { Keyframe(section); } } // compiler section parser void mjXReader::Compiler(XMLElement* section, mjSpec* mod) { string text; int n; // top-level attributes if (MapValue(section, "autolimits", &n, bool_map, 2)) { mod->autolimits = (n==1); } ReadAttr(section, "boundmass", 1, &mod->boundmass, text); ReadAttr(section, "boundinertia", 1, &mod->boundinertia, text); ReadAttr(section, "settotalmass", 1, &mod->settotalmass, text); if (MapValue(section, "balanceinertia", &n, bool_map, 2)) { mod->balanceinertia = (n==1); } if (MapValue(section, "strippath", &n, bool_map, 2)) { mod->strippath = (n==1); } if (MapValue(section, "fitaabb", &n, bool_map, 2)) { mod->fitaabb = (n==1); } if (MapValue(section, "coordinate", &n, coordinate_map, 2)) { if (n==1) { throw mjXError(section, "global coordinates no longer supported. To convert existing models, " "load and save them in MuJoCo 2.3.3 or older"); } } if (MapValue(section, "angle", &n, angle_map, 2)) { mod->degree = (n==1); } if (ReadAttrTxt(section, "eulerseq", text)) { if (text.size()!=3) { throw mjXError(section, "euler format must have length 3"); } memcpy(mod->euler, text.c_str(), 3); } if (ReadAttrTxt(section, "assetdir", text)) { mjs_setString(mod->meshdir, text.c_str()); mjs_setString(mod->texturedir, text.c_str()); } // meshdir and texturedir take precedence over assetdir std::string meshdir, texturedir; if (ReadAttrTxt(section, "meshdir", meshdir)) { mjs_setString(mod->meshdir, meshdir.c_str()); }; if (ReadAttrTxt(section, "texturedir", texturedir)) { mjs_setString(mod->texturedir, texturedir.c_str()); } if (MapValue(section, "discardvisual", &n, bool_map, 2)) { mod->discardvisual = (n==1); } if (MapValue(section, "convexhull", &n, bool_map, 2)) { mod->convexhull = (n==1); } if (MapValue(section, "usethread", &n, bool_map, 2)) { mod->usethread = (n==1); } if (MapValue(section, "fusestatic", &n, bool_map, 2)) { mod->fusestatic = (n==1); } MapValue(section, "inertiafromgeom", &mod->inertiafromgeom, TFAuto_map, 3); ReadAttr(section, "inertiagrouprange", 2, mod->inertiagrouprange, text); if (MapValue(section, "exactmeshinertia", &n, bool_map, 2)){ mod->exactmeshinertia = (n==1); } // lengthrange subelement XMLElement* elem = FindSubElem(section, "lengthrange"); if (elem) { mjLROpt* opt = &(mod->LRopt); // flags MapValue(elem, "mode", &opt->mode, lrmode_map, lrmode_sz); if (MapValue(elem, "useexisting", &n, bool_map, 2)) { opt->useexisting = (n==1); } if (MapValue(elem, "uselimit", &n, bool_map, 2)) { opt->uselimit = (n==1); } // algorithm parameters ReadAttr(elem, "accel", 1, &opt->accel, text); ReadAttr(elem, "maxforce", 1, &opt->maxforce, text); ReadAttr(elem, "timeconst", 1, &opt->timeconst, text); ReadAttr(elem, "timestep", 1, &opt->timestep, text); ReadAttr(elem, "inttotal", 1, &opt->inttotal, text); ReadAttr(elem, "interval", 1, &opt->interval, text); ReadAttr(elem, "tolrange", 1, &opt->tolrange, text); } } // option section parser void mjXReader::Option(XMLElement* section, mjOption* opt) { string text; int n; // read options ReadAttr(section, "timestep", 1, &opt->timestep, text); ReadAttr(section, "apirate", 1, &opt->apirate, text); ReadAttr(section, "impratio", 1, &opt->impratio, text); ReadAttr(section, "tolerance", 1, &opt->tolerance, text); ReadAttr(section, "ls_tolerance", 1, &opt->ls_tolerance, text); ReadAttr(section, "noslip_tolerance", 1, &opt->noslip_tolerance, text); ReadAttr(section, "mpr_tolerance", 1, &opt->mpr_tolerance, text); ReadAttr(section, "gravity", 3, opt->gravity, text); ReadAttr(section, "wind", 3, opt->wind, text); ReadAttr(section, "magnetic", 3, opt->magnetic, text); ReadAttr(section, "density", 1, &opt->density, text); ReadAttr(section, "viscosity", 1, &opt->viscosity, text); ReadAttr(section, "o_margin", 1, &opt->o_margin, text); ReadAttr(section, "o_solref", mjNREF, opt->o_solref, text, false, false); ReadAttr(section, "o_solimp", mjNIMP, opt->o_solimp, text, false, false); ReadAttr(section, "o_friction", 5, opt->o_friction, text, false, false); MapValue(section, "integrator", &opt->integrator, integrator_map, integrator_sz); MapValue(section, "cone", &opt->cone, cone_map, cone_sz); MapValue(section, "jacobian", &opt->jacobian, jac_map, jac_sz); MapValue(section, "solver", &opt->solver, solver_map, solver_sz); ReadAttrInt(section, "iterations", &opt->iterations); ReadAttrInt(section, "ls_iterations", &opt->ls_iterations); ReadAttrInt(section, "noslip_iterations", &opt->noslip_iterations); ReadAttrInt(section, "mpr_iterations", &opt->mpr_iterations); ReadAttrInt(section, "sdf_iterations", &opt->sdf_iterations); ReadAttrInt(section, "sdf_initpoints", &opt->sdf_initpoints); // actuatorgroupdisable constexpr int num_bitflags = 31; int disabled_act_groups[num_bitflags]; int num_found = ReadAttr(section, "actuatorgroupdisable", num_bitflags, disabled_act_groups, text, false, false); for (int i=0; i < num_found; i++) { int group = disabled_act_groups[i]; if (group < 0) { throw mjXError(section, "disabled actuator group value must be non-negative"); } if (group > num_bitflags - 1) { throw mjXError(section, "disabled actuator group value cannot exceed 30"); } opt->disableactuator |= (1 << group); } // read disable sub-element XMLElement* elem = FindSubElem(section, "flag"); if (elem) { #define READDSBL(NAME, MASK) \ if (MapValue(elem, NAME, &n, enable_map, 2)) { \ opt->disableflags ^= (opt->disableflags & MASK); \ opt->disableflags |= (n ? 0 : MASK); } READDSBL("constraint", mjDSBL_CONSTRAINT) READDSBL("equality", mjDSBL_EQUALITY) READDSBL("frictionloss", mjDSBL_FRICTIONLOSS) READDSBL("limit", mjDSBL_LIMIT) READDSBL("contact", mjDSBL_CONTACT) READDSBL("passive", mjDSBL_PASSIVE) READDSBL("gravity", mjDSBL_GRAVITY) READDSBL("clampctrl", mjDSBL_CLAMPCTRL) READDSBL("warmstart", mjDSBL_WARMSTART) READDSBL("filterparent", mjDSBL_FILTERPARENT) READDSBL("actuation", mjDSBL_ACTUATION) READDSBL("refsafe", mjDSBL_REFSAFE) READDSBL("sensor", mjDSBL_SENSOR) READDSBL("midphase", mjDSBL_MIDPHASE) READDSBL("eulerdamp", mjDSBL_EULERDAMP) #undef READDSBL #define READENBL(NAME, MASK) \ if (MapValue(elem, NAME, &n, enable_map, 2)) { \ opt->enableflags ^= (opt->enableflags & MASK); \ opt->enableflags |= (n ? MASK : 0); } READENBL("override", mjENBL_OVERRIDE) READENBL("energy", mjENBL_ENERGY) READENBL("fwdinv", mjENBL_FWDINV) READENBL("invdiscrete", mjENBL_INVDISCRETE) READENBL("sensornoise", mjENBL_SENSORNOISE) READENBL("multiccd", mjENBL_MULTICCD) READENBL("island", mjENBL_ISLAND) #undef READENBL } } // size section parser void mjXReader::Size(XMLElement* section, mjSpec* mod) { // read memory bytes { constexpr char err_msg[] = "unsigned integer with an optional suffix {K,M,G,T,P,E} is expected in " "attribute 'memory' (or the size specified is too big)"; auto memory = [&]() -> std::optional { const char* pstr = section->Attribute("memory"); if (!pstr) { return std::nullopt; } // trim entire string std::string trimmed; { std::istringstream strm((std::string(pstr))); strm >> trimmed; std::string trailing; strm >> trailing; if (!trailing.empty() || !strm.eof()) { throw mjXError(section, "%s", err_msg); } // allow explicit specification of the default "-1" value if (trimmed == "-1") { return std::nullopt; } } std::istringstream strm(trimmed); // check that the number is not negative if (strm.peek() == '-') { throw mjXError(section, "%s", err_msg); } std::size_t base_size; strm >> base_size; if (strm.fail()) { // either not an integer or the number without the suffix is already bigger than size_t throw mjXError(section, "%s", err_msg); } // parse the multiplier suffix int multiplier_bit = 0; if (!strm.eof()) { char suffix = strm.get(); if (suffix == 'K' || suffix == 'k') { multiplier_bit = 10; } else if (suffix == 'M' || suffix == 'm') { multiplier_bit = 20; } else if (suffix == 'G' || suffix == 'g') { multiplier_bit = 30; } else if (suffix == 'T' || suffix == 't') { multiplier_bit = 40; } else if (suffix == 'P' || suffix == 'p') { multiplier_bit = 50; } else if (suffix == 'E' || suffix == 'e') { multiplier_bit = 60; } // check for invalid suffix, or suffix longer than one character strm.get(); if (!multiplier_bit || !strm.eof()) { throw mjXError(section, "%s", err_msg); } } // check that the specified suffix isn't bigger than size_t if (multiplier_bit + 1 > std::numeric_limits::digits) { throw mjXError(section, "%s", err_msg); } // check that the suffix won't take the total size beyond size_t const std::size_t max_base_size = (std::numeric_limits::max() << multiplier_bit) >> multiplier_bit; if (base_size > max_base_size) { throw mjXError(section, "%s", err_msg); } const std::size_t total_size = base_size << multiplier_bit; return total_size; }(); if (memory.has_value()) { if (*memory / sizeof(mjtNum) > std::numeric_limits::max()) { throw mjXError(section, "%s", err_msg); } mod->memory = *memory; } } // read sizes ReadAttrInt(section, "nuserdata", &mod->nuserdata); ReadAttrInt(section, "nkey", &mod->nkey); ReadAttrInt(section, "nconmax", &mod->nconmax); if (mod->nconmax < -1) throw mjXError(section, "nconmax must be >= -1"); { int nstack = -1; const bool has_nstack = ReadAttrInt(section, "nstack", &nstack); if (has_nstack) { if (mod->nstack < -1) { throw mjXError(section, "nstack must be >= -1"); } if (mod->memory != -1 && nstack != -1) { throw mjXError(section, "either 'memory' and 'nstack' attribute can be specified, not both"); } mod->nstack = nstack; } } { int njmax = -1; const bool has_njmax = ReadAttrInt(section, "njmax", &njmax); if (has_njmax) { if (mod->njmax < -1) { throw mjXError(section, "njmax must be >= -1"); } if (mod->memory != -1 && njmax != -1) { throw mjXError(section, "either 'memory' and 'njmax' attribute can be specified, not both"); } mod->njmax = njmax; } } ReadAttrInt(section, "nuser_body", &mod->nuser_body); if (mod->nuser_body < -1) throw mjXError(section, "nuser_body must be >= -1"); ReadAttrInt(section, "nuser_jnt", &mod->nuser_jnt); if (mod->nuser_jnt < -1) throw mjXError(section, "nuser_jnt must be >= -1"); ReadAttrInt(section, "nuser_geom", &mod->nuser_geom); if (mod->nuser_geom < -1) throw mjXError(section, "nuser_geom must be >= -1"); ReadAttrInt(section, "nuser_site", &mod->nuser_site); if (mod->nuser_site < -1) throw mjXError(section, "nuser_site must be >= -1"); ReadAttrInt(section, "nuser_cam", &mod->nuser_cam); if (mod->nuser_cam < -1) throw mjXError(section, "nuser_cam must be >= -1"); ReadAttrInt(section, "nuser_tendon", &mod->nuser_tendon); if (mod->nuser_tendon < -1) throw mjXError(section, "nuser_tendon must be >= -1"); ReadAttrInt(section, "nuser_actuator", &mod->nuser_actuator); if (mod->nuser_actuator < -1) throw mjXError(section, "nuser_actuator must be >= -1"); ReadAttrInt(section, "nuser_sensor", &mod->nuser_sensor); if (mod->nuser_sensor < -1) throw mjXError(section, "nuser_sensor must be >= -1"); } // statistic section parser void mjXReader::Statistic(XMLElement* section) { string text; // read statistics ReadAttr(section, "meaninertia", 1, &model->stat.meaninertia, text); ReadAttr(section, "meanmass", 1, &model->stat.meanmass, text); ReadAttr(section, "meansize", 1, &model->stat.meansize, text); ReadAttr(section, "extent", 1, &model->stat.extent, text); if (mjuu_defined(model->stat.extent) && model->stat.extent<=0) { throw mjXError(section, "extent must be strictly positive"); } ReadAttr(section, "center", 3, model->stat.center, text); } //---------------------------------- one-element parsers ------------------------------------------- // flex element parser void mjXReader::OneFlex(XMLElement* elem, mjsFlex* pflex) { string text, name, classname, material; int n; // read attributes if (ReadAttrTxt(elem, "name", name)) { mjs_setString(pflex->name, name.c_str()); } if (ReadAttrTxt(elem, "classname", classname)) { mjs_setString(pflex->classname, classname.c_str()); } if (ReadAttrTxt(elem, "material", material)) { mjs_setString(pflex->material, material.c_str()); } ReadAttr(elem, "radius", 1, &pflex->radius, text); ReadAttr(elem, "rgba", 4, pflex->rgba, text); if (MapValue(elem, "flatskin", &n, bool_map, 2)) { pflex->flatskin = (n==1); } ReadAttrInt(elem, "dim", &pflex->dim); ReadAttrInt(elem, "group", &pflex->group); // read data vectors if (ReadAttrTxt(elem, "body", text, true)) { mjs_setStringVec(pflex->vertbody, text.c_str()); } if (ReadAttrTxt(elem, "vertex", text)) { std::vector vert = String2Vector(text); mjs_setDouble(pflex->vert, vert.data(), vert.size()); } if (ReadAttrTxt(elem, "element", text, true)) { std::vector elem = String2Vector(text); mjs_setInt(pflex->elem, elem.data(), elem.size()); } if (ReadAttrTxt(elem, "texcoord", text)) { std::vector texcoord = String2Vector(text); mjs_setFloat(pflex->texcoord, texcoord.data(), texcoord.size()); } // contact subelement XMLElement* cont = FirstChildElement(elem, "contact"); if (cont) { ReadAttrInt(cont, "contype", &pflex->contype); ReadAttrInt(cont, "conaffinity", &pflex->conaffinity); ReadAttrInt(cont, "condim", &pflex->condim); ReadAttrInt(cont, "priority", &pflex->priority); ReadAttr(cont, "friction", 3, pflex->friction, text, false, false); ReadAttr(cont, "solmix", 1, &pflex->solmix, text); ReadAttr(cont, "solref", mjNREF, pflex->solref, text, false, false); ReadAttr(cont, "solimp", mjNIMP, pflex->solimp, text, false, false); ReadAttr(cont, "margin", 1, &pflex->margin, text); ReadAttr(cont, "gap", 1, &pflex->gap, text); if (MapValue(cont, "internal", &n, bool_map, 2)) { pflex->internal = (n==1); } MapValue(cont, "selfcollide", &pflex->selfcollide, flexself_map, 5); ReadAttrInt(cont, "activelayers", &pflex->activelayers); } // edge subelement XMLElement* edge = FirstChildElement(elem, "edge"); if (edge) { ReadAttr(edge, "stiffness", 1, &pflex->edgestiffness, text); ReadAttr(edge, "damping", 1, &pflex->edgedamping, text); } // write error info mjs_setString(pflex->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); } // mesh element parser void mjXReader::OneMesh(XMLElement* elem, mjsMesh* pmesh) { int n; string text, name, classname, content_type; // read attributes if (ReadAttrTxt(elem, "name", name)) { mjs_setString(pmesh->name, name.c_str()); } if (ReadAttrTxt(elem, "class", classname)) { mjs_setString(pmesh->classname, classname.c_str()); } if (ReadAttrTxt(elem, "content_type", content_type)) { mjs_setString(pmesh->content_type, content_type.c_str()); } auto file = ReadAttrFile(elem, "file", MeshDir()); if (file) { mjs_setString(pmesh->file, file->c_str()); } ReadAttr(elem, "refpos", 3, pmesh->refpos, text); ReadAttr(elem, "refquat", 4, pmesh->refquat, text); ReadAttr(elem, "scale", 3, pmesh->scale, text); XMLElement* eplugin = FirstChildElement(elem, "plugin"); if (eplugin) { OnePlugin(eplugin, &pmesh->plugin); } if (MapValue(elem, "smoothnormal", &n, bool_map, 2)) { pmesh->smoothnormal = (n==1); } // read user vertex data if (ReadAttrTxt(elem, "vertex", text)) { auto uservert = ReadAttrVec(elem, "vertex"); if (uservert.has_value()) { mjs_setFloat(pmesh->uservert, uservert->data(), uservert->size()); } } // read user normal data if (ReadAttrTxt(elem, "normal", text)) { auto usernormal = ReadAttrVec(elem, "normal"); if (usernormal.has_value()) { mjs_setFloat(pmesh->usernormal, usernormal->data(), usernormal->size()); } } // read user texcoord data if (ReadAttrTxt(elem, "texcoord", text)) { auto usertexcoord = ReadAttrVec(elem, "texcoord"); if (usertexcoord.has_value()) { mjs_setFloat(pmesh->usertexcoord, usertexcoord->data(), usertexcoord->size()); } } // read user face data if (ReadAttrTxt(elem, "face", text)) { auto userface = ReadAttrVec(elem, "face"); if (userface.has_value()) { mjs_setInt(pmesh->userface, userface->data(), userface->size()); } } // write error info mjs_setString(pmesh->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); } // skin element parser void mjXReader::OneSkin(XMLElement* elem, mjsSkin* pskin) { string text, name, material; float data[4]; // read attributes if (ReadAttrTxt(elem, "name", name)) { mjs_setString(pskin->name, name.c_str()); } auto file = ReadAttrFile(elem, "file", AssetDir()); if (file.has_value()) { mjs_setString(pskin->file, file->c_str()); } if (ReadAttrTxt(elem, "material", material)) { mjs_setString(pskin->material, material.c_str()); } ReadAttrInt(elem, "group", &pskin->group); if (pskin->group<0 || pskin->group>=mjNGROUP) { throw mjXError(elem, "skin group must be between 0 and 5"); } ReadAttr(elem, "rgba", 4, pskin->rgba, text); ReadAttr(elem, "inflate", 1, &pskin->inflate, text); // read vertex data if (ReadAttrTxt(elem, "vertex", text)) { std::vector vert = String2Vector(text); mjs_setFloat(pskin->vert, vert.data(), vert.size()); } // read texcoord data if (ReadAttrTxt(elem, "texcoord", text)) { std::vector texcoord = String2Vector(text); mjs_setFloat(pskin->texcoord, texcoord.data(), texcoord.size()); } // read user face data if (ReadAttrTxt(elem, "face", text)) { std::vector face = String2Vector(text); mjs_setInt(pskin->face, face.data(), face.size()); } // read bones XMLElement* bone = FirstChildElement(elem, "bone"); std::vector bindpos; std::vector bindquat; while (bone) { // read body ReadAttrTxt(bone, "body", text, true); mjs_appendString(pskin->bodyname, text.c_str()); // read bindpos ReadAttr(bone, "bindpos", 3, data, text, true); bindpos.push_back(data[0]); bindpos.push_back(data[1]); bindpos.push_back(data[2]); // read bindquat ReadAttr(bone, "bindquat", 4, data, text, true); bindquat.push_back(data[0]); bindquat.push_back(data[1]); bindquat.push_back(data[2]); bindquat.push_back(data[3]); // read vertid ReadAttrTxt(bone, "vertid", text, true); vector tempid = String2Vector(text); mjs_appendIntVec(pskin->vertid, tempid.data(), tempid.size()); // read vertweight ReadAttrTxt(bone, "vertweight", text, true); vector tempweight = String2Vector(text); mjs_appendFloatVec(pskin->vertweight, tempweight.data(), tempweight.size()); // advance to next bone bone = NextSiblingElement(bone, "bone"); } // set bind vectors mjs_setFloat(pskin->bindpos, bindpos.data(), bindpos.size()); mjs_setFloat(pskin->bindquat, bindquat.data(), bindquat.size()); // write error info mjs_setString(pskin->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); } // material element parser void mjXReader::OneMaterial(XMLElement* elem, mjsMaterial* pmat) { string text, name, classname, texture; int n; // read attributes if (ReadAttrTxt(elem, "name", name)) { mjs_setString(pmat->name, name.c_str()); } if (ReadAttrTxt(elem, "class", classname)) { mjs_setString(pmat->classname, classname.c_str()); } if (ReadAttrTxt(elem, "texture", texture)) { mjs_setString(pmat->texture, texture.c_str()); } if (MapValue(elem, "texuniform", &n, bool_map, 2)) { pmat->texuniform = (n==1); } ReadAttr(elem, "texrepeat", 2, pmat->texrepeat, text); ReadAttr(elem, "emission", 1, &pmat->emission, text); ReadAttr(elem, "specular", 1, &pmat->specular, text); ReadAttr(elem, "shininess", 1, &pmat->shininess, text); ReadAttr(elem, "reflectance", 1, &pmat->reflectance, text); ReadAttr(elem, "rgba", 4, pmat->rgba, text); // write error info mjs_setString(pmat->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); } // joint element parser void mjXReader::OneJoint(XMLElement* elem, mjsJoint* pjoint) { string text, name, classname; std::vector userdata; int n; // read attributes if (ReadAttrTxt(elem, "name", name)) { mjs_setString(pjoint->name, name.c_str()); } if (ReadAttrTxt(elem, "class", classname)) { mjs_setString(pjoint->classname, classname.c_str()); } if (MapValue(elem, "type", &n, joint_map, joint_sz)) { pjoint->type = (mjtJoint)n; } MapValue(elem, "limited", &pjoint->limited, TFAuto_map, 3); MapValue(elem, "actuatorfrclimited", &pjoint->actfrclimited, TFAuto_map, 3); ReadAttrInt(elem, "group", &pjoint->group); ReadAttr(elem, "solreflimit", mjNREF, pjoint->solref_limit, text, false, false); ReadAttr(elem, "solimplimit", mjNIMP, pjoint->solimp_limit, text, false, false); ReadAttr(elem, "solreffriction", mjNREF, pjoint->solref_friction, text, false, false); ReadAttr(elem, "solimpfriction", mjNIMP, pjoint->solimp_friction, text, false, false); ReadAttr(elem, "pos", 3, pjoint->pos, text); ReadAttr(elem, "axis", 3, pjoint->axis, text); ReadAttr(elem, "springdamper", 2, pjoint->springdamper, text); ReadAttr(elem, "stiffness", 1, &pjoint->stiffness, text); ReadAttr(elem, "range", 2, pjoint->range, text); ReadAttr(elem, "actuatorfrcrange", 2, pjoint->actfrcrange, text); ReadAttr(elem, "margin", 1, &pjoint->margin, text); ReadAttr(elem, "ref", 1, &pjoint->ref, text); ReadAttr(elem, "springref", 1, &pjoint->springref, text); ReadAttr(elem, "armature", 1, &pjoint->armature, text); ReadAttr(elem, "damping", 1, &pjoint->damping, text); ReadAttr(elem, "frictionloss", 1, &pjoint->frictionloss, text); // read userdata if (ReadVector(elem, "user", userdata, text)) { mjs_setDouble(pjoint->userdata, userdata.data(), userdata.size()); } // write error info mjs_setString(pjoint->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); } // geom element parser void mjXReader::OneGeom(XMLElement* elem, mjsGeom* pgeom) { string text, name, classname; std::vector userdata; std::string hfieldname, meshname, material; int n; // read attributes if (ReadAttrTxt(elem, "name", name)) { mjs_setString(pgeom->name, name.c_str()); } if (ReadAttrTxt(elem, "class", classname)) { mjs_setString(pgeom->classname, classname.c_str()); } if (MapValue(elem, "type", &n, geom_map, mjNGEOMTYPES)) { pgeom->type = (mjtGeom)n; } ReadAttr(elem, "size", 3, pgeom->size, text, false, false); ReadAttrInt(elem, "contype", &pgeom->contype); ReadAttrInt(elem, "conaffinity", &pgeom->conaffinity); ReadAttrInt(elem, "condim", &pgeom->condim); ReadAttrInt(elem, "group", &pgeom->group); ReadAttrInt(elem, "priority", &pgeom->priority); ReadAttr(elem, "friction", 3, pgeom->friction, text, false, false); ReadAttr(elem, "solmix", 1, &pgeom->solmix, text); ReadAttr(elem, "solref", mjNREF, pgeom->solref, text, false, false); ReadAttr(elem, "solimp", mjNIMP, pgeom->solimp, text, false, false); ReadAttr(elem, "margin", 1, &pgeom->margin, text); ReadAttr(elem, "gap", 1, &pgeom->gap, text); if (ReadAttrTxt(elem, "hfield", hfieldname)) { mjs_setString(pgeom->hfieldname, hfieldname.c_str()); } if (ReadAttrTxt(elem, "mesh", meshname)) { mjs_setString(pgeom->meshname, meshname.c_str()); } ReadAttr(elem, "fitscale", 1, &pgeom->fitscale, text); if (ReadAttrTxt(elem, "material", material)) { mjs_setString(pgeom->material, material.c_str()); } ReadAttr(elem, "rgba", 4, pgeom->rgba, text); if (MapValue(elem, "fluidshape", &n, fluid_map, 2)) { pgeom->fluid_ellipsoid = (n == 1); } ReadAttr(elem, "fluidcoef", 5, pgeom->fluid_coefs, text, false, false); // read userdata if (ReadVector(elem, "user", userdata, text)) { mjs_setDouble(pgeom->userdata, userdata.data(), userdata.size()); } // plugin sub-element XMLElement* eplugin = FirstChildElement(elem, "plugin"); if (eplugin) { OnePlugin(eplugin, &pgeom->plugin); } // remaining attributes ReadAttr(elem, "mass", 1, &pgeom->mass, text); ReadAttr(elem, "density", 1, &pgeom->density, text); ReadAttr(elem, "fromto", 6, pgeom->fromto, text); ReadAttr(elem, "pos", 3, pgeom->pos, text); ReadQuat(elem, "quat", pgeom->quat, text); ReadAlternative(elem, pgeom->alt); // compute inertia using either solid or shell geometry if (MapValue(elem, "shellinertia", &n, meshtype_map, 2)) { pgeom->typeinertia = (mjtGeomInertia)n; } // write error info mjs_setString(pgeom->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); } // site element parser void mjXReader::OneSite(XMLElement* elem, mjsSite* site) { int n; string text, name, classname; std::vector userdata; std::string material; // read attributes if (ReadAttrTxt(elem, "name", name)) { mjs_setString(site->name, name.c_str()); } if (ReadAttrTxt(elem, "class", classname)) { mjs_setString(site->classname, classname.c_str()); } if (MapValue(elem, "type", &n, geom_map, mjNGEOMTYPES)) { site->type = (mjtGeom)n; } ReadAttr(elem, "size", 3, site->size, text, false, false); ReadAttrInt(elem, "group", &site->group); ReadAttr(elem, "pos", 3, site->pos, text); ReadQuat(elem, "quat", site->quat, text); if (ReadAttrTxt(elem, "material", material)) { mjs_setString(site->material, material.c_str()); } ReadAttr(elem, "rgba", 4, site->rgba, text); ReadAttr(elem, "fromto", 6, site->fromto, text); ReadAlternative(elem, site->alt); if (ReadVector(elem, "user", userdata, text)) { mjs_setDouble(site->userdata, userdata.data(), userdata.size()); } // write error info mjs_setString(site->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); } // camera element parser void mjXReader::OneCamera(XMLElement* elem, mjsCamera* pcam) { int n; string text, name, classname, targetbody; std::vector userdata; // read attributes if (ReadAttrTxt(elem, "name", name)) { mjs_setString(pcam->name, name.c_str()); } if (ReadAttrTxt(elem, "class", classname)) { mjs_setString(pcam->classname, classname.c_str()); } if (ReadAttrTxt(elem, "target", targetbody)) { mjs_setString(pcam->targetbody, targetbody.c_str()); } if (MapValue(elem, "mode", &n, camlight_map, camlight_sz)) { pcam->mode = (mjtCamLight)n; } ReadAttr(elem, "pos", 3, pcam->pos, text); ReadQuat(elem, "quat", pcam->quat, text); ReadAlternative(elem, pcam->alt); ReadAttr(elem, "ipd", 1, &pcam->ipd, text); bool has_principal = ReadAttr(elem, "principalpixel", 2, pcam->principal_pixel, text) || ReadAttr(elem, "principal", 2, pcam->principal_length, text); bool has_focal = ReadAttr(elem, "focalpixel", 2, pcam->focal_pixel, text) || ReadAttr(elem, "focal", 2, pcam->focal_length, text); bool needs_sensorsize = has_principal || has_focal; bool has_sensorsize = ReadAttr(elem, "sensorsize", 2, pcam->sensor_size, text, needs_sensorsize); bool has_fovy = ReadAttr(elem, "fovy", 1, &pcam->fovy, text); bool needs_resolution = has_focal || has_sensorsize; ReadAttr(elem, "resolution", 2, pcam->resolution, text, needs_resolution); if (pcam->resolution[0] < 0 || pcam->resolution[1] < 0) { throw mjXError(elem, "camera resolution cannot be negative"); } if (has_fovy && has_sensorsize) { throw mjXError( elem, "either 'fovy' or 'sensorsize' attribute can be specified, not both"); } // read userdata ReadVector(elem, "user", userdata, text); mjs_setDouble(pcam->userdata, userdata.data(), userdata.size()); // write error info mjs_setString(pcam->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); } // light element parser void mjXReader::OneLight(XMLElement* elem, mjsLight* plight) { int n; string text, name, classname, targetbody; // read attributes if (ReadAttrTxt(elem, "name", name)) { mjs_setString(plight->name, name.c_str()); } if (ReadAttrTxt(elem, "class", classname)) { mjs_setString(plight->classname, classname.c_str()); } if (ReadAttrTxt(elem, "target", targetbody)) { mjs_setString(plight->targetbody, targetbody.c_str()); } if (MapValue(elem, "mode", &n, camlight_map, camlight_sz)) { plight->mode = (mjtCamLight)n; } if (MapValue(elem, "directional", &n, bool_map, 2)) { plight->directional = (n==1); } if (MapValue(elem, "castshadow", &n, bool_map, 2)) { plight->castshadow = (n==1); } if (MapValue(elem, "active", &n, bool_map, 2)) { plight->active = (n==1); } ReadAttr(elem, "pos", 3, plight->pos, text); ReadAttr(elem, "dir", 3, plight->dir, text); ReadAttr(elem, "attenuation", 3, plight->attenuation, text); ReadAttr(elem, "cutoff", 1, &plight->cutoff, text); ReadAttr(elem, "exponent", 1, &plight->exponent, text); ReadAttr(elem, "ambient", 3, plight->ambient, text); ReadAttr(elem, "diffuse", 3, plight->diffuse, text); ReadAttr(elem, "specular", 3, plight->specular, text); // write error info mjs_setString(plight->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); } // pair element parser void mjXReader::OnePair(XMLElement* elem, mjsPair* ppair) { string text, name, classname, geomname1, geomname2; // regular only if (!readingdefaults) { if (ReadAttrTxt(elem, "class", classname)) { mjs_setString(ppair->classname, classname.c_str()); } if (ReadAttrTxt(elem, "geom1", geomname1)) { mjs_setString(ppair->geomname1, geomname1.c_str()); } if (ReadAttrTxt(elem, "geom2", geomname2)) { mjs_setString(ppair->geomname2, geomname2.c_str()); } } // read other parameters if (ReadAttrTxt(elem, "name", name)) { mjs_setString(ppair->name, name.c_str()); } ReadAttrInt(elem, "condim", &ppair->condim); ReadAttr(elem, "solref", mjNREF, ppair->solref, text, false, false); ReadAttr(elem, "solreffriction", mjNREF, ppair->solreffriction, text, false, false); ReadAttr(elem, "solimp", mjNIMP, ppair->solimp, text, false, false); ReadAttr(elem, "margin", 1, &ppair->margin, text); ReadAttr(elem, "gap", 1, &ppair->gap, text); ReadAttr(elem, "friction", 5, ppair->friction, text, false, false); // write error info mjs_setString(ppair->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); } // equality element parser void mjXReader::OneEquality(XMLElement* elem, mjsEquality* pequality) { int n; string text, name1, name2, name, classname; // read type (bad keywords already detected by schema) text = elem->Value(); pequality->type = (mjtEq)FindKey(equality_map, equality_sz, text); // regular only if (!readingdefaults) { if (ReadAttrTxt(elem, "name", name)) { mjs_setString(pequality->name, name.c_str()); } if (ReadAttrTxt(elem, "class", classname)) { mjs_setString(pequality->classname, classname.c_str()); }; switch (pequality->type) { case mjEQ_CONNECT: ReadAttrTxt(elem, "body1", name1, true); ReadAttrTxt(elem, "body2", name2); ReadAttr(elem, "anchor", 3, pequality->data, text, true); break; case mjEQ_WELD: ReadAttrTxt(elem, "body1", name1, true); ReadAttrTxt(elem, "body2", name2); ReadAttr(elem, "relpose", 7, pequality->data+3, text); ReadAttr(elem, "torquescale", 1, pequality->data+10, text); if (!ReadAttr(elem, "anchor", 3, pequality->data, text)) { mjuu_zerovec(pequality->data, 3); } break; case mjEQ_JOINT: ReadAttrTxt(elem, "joint1", name1, true); ReadAttrTxt(elem, "joint2", name2); ReadAttr(elem, "polycoef", 5, pequality->data, text); break; case mjEQ_TENDON: ReadAttrTxt(elem, "tendon1", name1, true); ReadAttrTxt(elem, "tendon2", name2); ReadAttr(elem, "polycoef", 5, pequality->data, text); break; case mjEQ_FLEX: ReadAttrTxt(elem, "flex", name1, true); break; case mjEQ_DISTANCE: throw mjXError(elem, "support for distance equality constraints was removed in MuJoCo 2.2.2"); break; default: // SHOULD NOT OCCUR throw mjXError(elem, "unrecognized equality constraint type"); } mjs_setString(pequality->name1, name1.c_str()); if (!name2.empty()) { mjs_setString(pequality->name2, name2.c_str()); } } // read attributes if (MapValue(elem, "active", &n, bool_map, 2)) { pequality->active = (n==1); } ReadAttr(elem, "solref", mjNREF, pequality->solref, text, false, false); ReadAttr(elem, "solimp", mjNIMP, pequality->solimp, text, false, false); // write error info mjs_setString(pequality->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); } // tendon element parser void mjXReader::OneTendon(XMLElement* elem, mjsTendon* pten) { string text, name, classname, material; std::vector userdata; // read attributes if (ReadAttrTxt(elem, "name", name)) { mjs_setString(pten->name, name.c_str()); } if (ReadAttrTxt(elem, "class", classname)) { mjs_setString(pten->classname, classname.c_str()); } ReadAttrInt(elem, "group", &pten->group); if (ReadAttrTxt(elem, "material", material)) { mjs_setString(pten->material, material.c_str()); } MapValue(elem, "limited", &pten->limited, TFAuto_map, 3); ReadAttr(elem, "width", 1, &pten->width, text); ReadAttr(elem, "solreflimit", mjNREF, pten->solref_limit, text, false, false); ReadAttr(elem, "solimplimit", mjNIMP, pten->solimp_limit, text, false, false); ReadAttr(elem, "solreffriction", mjNREF, pten->solref_friction, text, false, false); ReadAttr(elem, "solimpfriction", mjNIMP, pten->solimp_friction, text, false, false); ReadAttr(elem, "range", 2, pten->range, text); ReadAttr(elem, "margin", 1, &pten->margin, text); ReadAttr(elem, "stiffness", 1, &pten->stiffness, text); ReadAttr(elem, "damping", 1, &pten->damping, text); ReadAttr(elem, "frictionloss", 1, &pten->frictionloss, text); // read springlength, either one or two values; if one, copy to second value if (ReadAttr(elem, "springlength", 2, pten->springlength, text, false, false) == 1) { pten->springlength[1] = pten->springlength[0]; } ReadAttr(elem, "rgba", 4, pten->rgba, text); // read userdata if (ReadVector(elem, "user", userdata, text)) { mjs_setDouble(pten->userdata, userdata.data(), userdata.size()); } // write error info mjs_setString(pten->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); } // actuator element parser void mjXReader::OneActuator(XMLElement* elem, mjsActuator* pact) { string text, type, name, classname, target, slidersite, refsite; // common attributes if (ReadAttrTxt(elem, "name", name)) { mjs_setString(pact->name, name.c_str()); } if (ReadAttrTxt(elem, "class", classname)) { mjs_setString(pact->classname, classname.c_str()); } ReadAttrInt(elem, "group", &pact->group); MapValue(elem, "ctrllimited", &pact->ctrllimited, TFAuto_map, 3); MapValue(elem, "forcelimited", &pact->forcelimited, TFAuto_map, 3); MapValue(elem, "actlimited", &pact->actlimited, TFAuto_map, 3); ReadAttr(elem, "ctrlrange", 2, pact->ctrlrange, text); ReadAttr(elem, "forcerange", 2, pact->forcerange, text); ReadAttr(elem, "actrange", 2, pact->actrange, text); ReadAttr(elem, "lengthrange", 2, pact->lengthrange, text); ReadAttr(elem, "gear", 6, pact->gear, text, false, false); // transmission target and type int cnt = 0; if (ReadAttrTxt(elem, "joint", target)) { mjs_setString(pact->target, target.c_str()); pact->trntype = mjTRN_JOINT; cnt++; } if (ReadAttrTxt(elem, "jointinparent", target)) { mjs_setString(pact->target, target.c_str()); pact->trntype = mjTRN_JOINTINPARENT; cnt++; } if (ReadAttrTxt(elem, "tendon", target)) { mjs_setString(pact->target, target.c_str()); pact->trntype = mjTRN_TENDON; cnt++; } if (ReadAttrTxt(elem, "cranksite", target)) { mjs_setString(pact->target, target.c_str()); pact->trntype = mjTRN_SLIDERCRANK; cnt++; } if (ReadAttrTxt(elem, "site", target)) { mjs_setString(pact->target, target.c_str()); pact->trntype = mjTRN_SITE; cnt++; } if (ReadAttrTxt(elem, "body", target)) { mjs_setString(pact->target, target.c_str()); pact->trntype = mjTRN_BODY; cnt++; } // check for repeated transmission if (cnt>1) { throw mjXError(elem, "actuator can have at most one of transmission target"); } // slidercrank-specific parameters int r1 = ReadAttr(elem, "cranklength", 1, &pact->cranklength, text); int r2 = ReadAttrTxt(elem, "slidersite", slidersite); if (r2) { mjs_setString(pact->slidersite, slidersite.c_str()); } if ((r1 || r2) && pact->trntype!=mjTRN_SLIDERCRANK && pact->trntype!=mjTRN_UNDEFINED) { throw mjXError(elem, "cranklength and slidersite can only be used in slidercrank transmission"); } // site-specific parameters (refsite) int r3 = ReadAttrTxt(elem, "refsite", refsite); if (r3) { mjs_setString(pact->refsite, refsite.c_str()); } if (r3 && pact->trntype!=mjTRN_SITE && pact->trntype!=mjTRN_UNDEFINED) { throw mjXError(elem, "refsite can only be used with site transmission"); } // get predefined type type = elem->Value(); // explicit attributes if (type=="general") { // explicit attributes int n; if (MapValue(elem, "dyntype", &n, dyn_map, dyn_sz)) { pact->dyntype = (mjtDyn)n; } if (MapValue(elem, "gaintype", &n, gain_map, gain_sz)) { pact->gaintype = (mjtGain)n; } if (MapValue(elem, "biastype", &n, bias_map, bias_sz)) { pact->biastype = (mjtBias)n; } if (MapValue(elem, "actearly", &n, bool_map, 2)) { pact->actearly = (n==1); } ReadAttr(elem, "dynprm", mjNDYN, pact->dynprm, text, false, false); ReadAttr(elem, "gainprm", mjNGAIN, pact->gainprm, text, false, false); ReadAttr(elem, "biasprm", mjNBIAS, pact->biasprm, text, false, false); ReadAttrInt(elem, "actdim", &pact->actdim); } // direct drive motor else if (type=="motor") { // unit gain pact->gainprm[0] = 1; // implied parameters pact->dyntype = mjDYN_NONE; pact->gaintype = mjGAIN_FIXED; pact->biastype = mjBIAS_NONE; } // position or integrated velocity servo else if (type=="position" || type=="intvelocity") { // explicit attributes ReadAttr(elem, "kp", 1, pact->gainprm, text); pact->biasprm[1] = -pact->gainprm[0]; if (ReadAttr(elem, "kv", 1, pact->biasprm + 2, text)) { if (pact->biasprm[2] < 0) throw mjXError(elem, "kv cannot be negative"); pact->biasprm[2] *= -1; } ReadAttr(elem, "inheritrange", 1, &pact->inheritrange, text); if (pact->inheritrange > 0) { if (type == "position") { if (pact->ctrlrange[0] || pact->ctrlrange[1]) { throw mjXError(elem, "ctrlrange and inheritrange cannot both be defined"); } } else { if (pact->actrange[0] || pact->actrange[1]) { throw mjXError(elem, "actrange and inheritrange cannot both be defined"); } } } // implied parameters pact->gaintype = mjGAIN_FIXED; pact->biastype = mjBIAS_AFFINE; if (type=="intvelocity") { pact->dyntype = mjDYN_INTEGRATOR; pact->actlimited = 1; } } // velocity servo else if (type=="velocity") { // clear bias mjuu_zerovec(pact->biasprm, mjNBIAS); // explicit attributes ReadAttr(elem, "kv", 1, pact->gainprm, text); pact->biasprm[2] = -pact->gainprm[0]; // implied parameters pact->dyntype = mjDYN_NONE; pact->gaintype = mjGAIN_FIXED; pact->biastype = mjBIAS_AFFINE; } // damper else if (type=="damper") { // clear gain mjuu_zerovec(pact->gainprm, mjNGAIN); // explicit attributes ReadAttr(elem, "kv", 1, pact->gainprm+2, text); if (pact->gainprm[2]<0) throw mjXError(elem, "damping coefficient cannot be negative"); pact->gainprm[2] = -pact->gainprm[2]; // require nonnegative range if (pact->ctrlrange[0]<0 || pact->ctrlrange[1]<0) { throw mjXError(elem, "damper control range cannot be negative"); } // implied parameters pact->ctrllimited = 1; pact->dyntype = mjDYN_NONE; pact->gaintype = mjGAIN_AFFINE; pact->biastype = mjBIAS_NONE; } // cylinder else if (type=="cylinder") { // explicit attributes ReadAttr(elem, "timeconst", 1, pact->dynprm, text); ReadAttr(elem, "bias", 3, pact->biasprm, text); ReadAttr(elem, "area", 1, pact->gainprm, text); double diameter; if (ReadAttr(elem, "diameter", 1, &diameter, text)) { pact->gainprm[0] = mjPI / 4 * diameter*diameter; } // implied parameters pact->dyntype = mjDYN_FILTER; pact->gaintype = mjGAIN_FIXED; pact->biastype = mjBIAS_AFFINE; } // muscle else if (type=="muscle") { // set muscle defaults if same as global defaults if (pact->dynprm[0]==1) pact->dynprm[0] = 0.01; // tau act if (pact->dynprm[1]==0) pact->dynprm[1] = 0.04; // tau deact if (pact->gainprm[0]==1) pact->gainprm[0] = 0.75; // range[0] if (pact->gainprm[1]==0) pact->gainprm[1] = 1.05; // range[1] if (pact->gainprm[2]==0) pact->gainprm[2] = -1; // force if (pact->gainprm[3]==0) pact->gainprm[3] = 200; // scale if (pact->gainprm[4]==0) pact->gainprm[4] = 0.5; // lmin if (pact->gainprm[5]==0) pact->gainprm[5] = 1.6; // lmax if (pact->gainprm[6]==0) pact->gainprm[6] = 1.5; // vmax if (pact->gainprm[7]==0) pact->gainprm[7] = 1.3; // fpmax if (pact->gainprm[8]==0) pact->gainprm[8] = 1.2; // fvmax // explicit attributes ReadAttr(elem, "timeconst", 2, pact->dynprm, text); ReadAttr(elem, "tausmooth", 1, pact->dynprm+2, text); if (pact->dynprm[2]<0) throw mjXError(elem, "muscle tausmooth cannot be negative"); ReadAttr(elem, "range", 2, pact->gainprm, text); ReadAttr(elem, "force", 1, pact->gainprm+2, text); ReadAttr(elem, "scale", 1, pact->gainprm+3, text); ReadAttr(elem, "lmin", 1, pact->gainprm+4, text); ReadAttr(elem, "lmax", 1, pact->gainprm+5, text); ReadAttr(elem, "vmax", 1, pact->gainprm+6, text); ReadAttr(elem, "fpmax", 1, pact->gainprm+7, text); ReadAttr(elem, "fvmax", 1, pact->gainprm+8, text); // biasprm = gainprm for (int n=0; n<9; n++) { pact->biasprm[n] = pact->gainprm[n]; } // implied parameters pact->dyntype = mjDYN_MUSCLE; pact->gaintype = mjGAIN_MUSCLE; pact->biastype = mjBIAS_MUSCLE; } // adhesion else if (type=="adhesion") { // explicit attributes ReadAttr(elem, "gain", 1, pact->gainprm, text); if (pact->gainprm[0]<0) throw mjXError(elem, "adhesion gain cannot be negative"); // require nonnegative range ReadAttr(elem, "ctrlrange", 2, pact->ctrlrange, text); if (pact->ctrlrange[0]<0 || pact->ctrlrange[1]<0) { throw mjXError(elem, "adhesion control range cannot be negative"); } // implied parameters pact->ctrllimited = 1; pact->gaintype = mjGAIN_FIXED; pact->biastype = mjBIAS_NONE; } else if (type == "plugin") { OnePlugin(elem, &pact->plugin); int n; if (MapValue(elem, "dyntype", &n, dyn_map, dyn_sz)) { pact->dyntype = (mjtDyn)n; } if (MapValue(elem, "actearly", &n, bool_map, 2)) { pact->actearly = (n==1); } ReadAttr(elem, "dynprm", mjNDYN, pact->dynprm, text, false, false); } else { // SHOULD NOT OCCUR throw mjXError(elem, "unrecognized actuator type: %s", type.c_str()); } // read userdata std::vector userdata; if (ReadVector(elem, "user", userdata, text)) { mjs_setDouble(pact->userdata, userdata.data(), userdata.size()); } // write info mjs_setString(pact->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); } // make composite void mjXReader::OneComposite(XMLElement* elem, mjsBody* pbody, mjsDefault* def) { string text; int n; // create out-of-DOM element mjCComposite comp; // common properties ReadAttrTxt(elem, "prefix", comp.prefix); if (MapValue(elem, "type", &n, comp_map, mjNCOMPTYPES, true)) { comp.type = (mjtCompType)n; } ReadAttr(elem, "count", 3, comp.count, text, false, false); ReadAttr(elem, "spacing", 1, &comp.spacing, text, false); ReadAttr(elem, "offset", 3, comp.offset, text); ReadAttr(elem, "flatinertia", 1, &comp.flatinertia, text); // plugin XMLElement* eplugin = FirstChildElement(elem, "plugin"); if (eplugin) { OnePlugin(eplugin, &comp.plugin); } // cable std::string curves; ReadAttrTxt(elem, "curve", curves); ReadAttrTxt(elem, "initial", comp.initial); ReadAttr(elem, "size", 3, comp.size, text, false, false); if (ReadAttrTxt(elem, "vertex", text)) { comp.uservert = String2Vector(text); } // shell ReadAttrTxt(elem, "face", comp.userface); // process curve string std::istringstream iss(curves); int i = 0; while (iss) { iss >> text; if (i>2) { throw mjXError(elem, "The curve array must have a maximum of 3 components"); } comp.curve[i++] = (mjtCompShape)FindKey(shape_map, mjNCOMPSHAPES, text); if (iss.eof()){ break; } }; // skin XMLElement* eskin = FirstChildElement(elem, "skin"); if (eskin) { comp.skin = true; if (MapValue(eskin, "texcoord", &n, bool_map, 2)) { comp.skintexcoord = (n==1); } ReadAttrTxt(eskin, "material", comp.skinmaterial); ReadAttr(eskin, "rgba", 4, comp.skinrgba, text); ReadAttr(eskin, "inflate", 1, &comp.skininflate, text); ReadAttrInt(eskin, "subgrid", &comp.skinsubgrid); ReadAttrInt(eskin, "group", &comp.skingroup, 0); if (comp.skingroup<0 || comp.skingroup>=mjNGROUP) { throw mjXError(eskin, "skin group must be between 0 and 5"); } } // set type-specific defaults comp.SetDefault(); // parse smooth solver parameters after type-specific defaults are set ReadAttr(elem, "solrefsmooth", mjNREF, comp.solrefsmooth, text, false, false); ReadAttr(elem, "solimpsmooth", mjNIMP, comp.solimpsmooth, text, false, false); // geom XMLElement* egeom = FirstChildElement(elem, "geom"); if (egeom) { std::string material; mjsGeom& dgeom = *comp.def[0].spec.geom; if (MapValue(egeom, "type", &n, geom_map, mjNGEOMTYPES)) { dgeom.type = (mjtGeom)n; } ReadAttr(egeom, "size", 3, dgeom.size, text, false, false); ReadAttrInt(egeom, "contype", &dgeom.contype); ReadAttrInt(egeom, "conaffinity", &dgeom.conaffinity); ReadAttrInt(egeom, "condim", &dgeom.condim); ReadAttrInt(egeom, "group", &dgeom.group); ReadAttrInt(egeom, "priority", &dgeom.priority); ReadAttr(egeom, "friction", 3, dgeom.friction, text, false, false); ReadAttr(egeom, "solmix", 1, &dgeom.solmix, text); ReadAttr(egeom, "solref", mjNREF, dgeom.solref, text, false, false); ReadAttr(egeom, "solimp", mjNIMP, dgeom.solimp, text, false, false); ReadAttr(egeom, "margin", 1, &dgeom.margin, text); ReadAttr(egeom, "gap", 1, &dgeom.gap, text); if (ReadAttrTxt(egeom, "material", material)) { mjs_setString(dgeom.material, material.c_str()); } ReadAttr(egeom, "rgba", 4, dgeom.rgba, text); ReadAttr(egeom, "mass", 1, &dgeom.mass, text); ReadAttr(egeom, "density", 1, &dgeom.density, text); } // site XMLElement* esite = FirstChildElement(elem, "site"); if (esite) { std::string material; mjsSite& dsite = *comp.def[0].spec.site; ReadAttr(esite, "size", 3, dsite.size, text, false, false); ReadAttrInt(esite, "group", &dsite.group); ReadAttrTxt(esite, "material", material); ReadAttr(esite, "rgba", 4, dsite.rgba, text); mjs_setString(dsite.material, material.c_str()); } // joint XMLElement* ejnt = FirstChildElement(elem, "joint"); while (ejnt) { // kind int kind; MapValue(ejnt, "kind", &kind, jkind_map, 4, true); // create a new element if this kind already exists if (comp.add[kind]) { char error[200]; if (!comp.AddDefaultJoint(error, 200)) { throw mjXError(elem, "%s", error); } } comp.add[kind] = true; // get element mjsDefault* dspec = &comp.defjoint[(mjtCompKind)kind].back().spec; mjsJoint& djoint = *dspec->joint; mjsEquality& dequality = *dspec->equality; // particle joint if (MapValue(ejnt, "type", &n, joint_map, joint_sz)) { djoint.type = (mjtJoint)n; } ReadAttr(ejnt, "axis", 3, djoint.axis, text); // solreffix, solimpfix ReadAttr(ejnt, "solreffix", mjNREF, dequality.solref, text, false, false); ReadAttr(ejnt, "solimpfix", mjNIMP, dequality.solimp, text, false, false); // joint attributes MapValue(elem, "limited", &djoint.limited, TFAuto_map, 3); ReadAttrInt(ejnt, "group", &djoint.group); ReadAttr(ejnt, "solreflimit", mjNREF, djoint.solref_limit, text, false, false); ReadAttr(ejnt, "solimplimit", mjNIMP, djoint.solimp_limit, text, false, false); ReadAttr(ejnt, "solreffriction", mjNREF, djoint.solref_friction, text, false, false); ReadAttr(ejnt, "solimpfriction", mjNIMP, djoint.solimp_friction, text, false, false); ReadAttr(ejnt, "stiffness", 1, &djoint.stiffness, text); ReadAttr(ejnt, "range", 2, djoint.range, text); ReadAttr(ejnt, "margin", 1, &djoint.margin, text); ReadAttr(ejnt, "armature", 1, &djoint.armature, text); ReadAttr(ejnt, "damping", 1, &djoint.damping, text); ReadAttr(ejnt, "frictionloss", 1, &djoint.frictionloss, text); // advance ejnt = NextSiblingElement(ejnt, "joint"); } // tendon XMLElement* eten = FirstChildElement(elem, "tendon"); while (eten) { // kind int kind; MapValue(eten, "kind", &kind, tkind_map, 2, true); comp.add[kind] = true; // get default structs mjsTendon& dtendon = *comp.def[kind].spec.tendon; mjsEquality& dequality = *comp.def[kind].spec.equality; // solreffix, solimpfix ReadAttr(eten, "solreffix", mjNREF, dequality.solref, text, false, false); ReadAttr(eten, "solimpfix", mjNIMP, dequality.solimp, text, false, false); // tendon attributes std::string material; MapValue(elem, "limited", &dtendon.limited, TFAuto_map, 3); ReadAttrInt(eten, "group", &dtendon.group); ReadAttr(eten, "solreflimit", mjNREF, dtendon.solref_limit, text, false, false); ReadAttr(eten, "solimplimit", mjNIMP, dtendon.solimp_limit, text, false, false); ReadAttr(eten, "solreffriction", mjNREF, dtendon.solref_friction, text, false, false); ReadAttr(eten, "solimpfriction", mjNIMP, dtendon.solimp_friction, text, false, false); ReadAttr(eten, "range", 2, dtendon.range, text); ReadAttr(eten, "margin", 1, &dtendon.margin, text); ReadAttr(eten, "stiffness", 1, &dtendon.stiffness, text); ReadAttr(eten, "damping", 1, &dtendon.damping, text); ReadAttr(eten, "frictionloss", 1, &dtendon.frictionloss, text); ReadAttrTxt(eten, "material", material); mjs_setString(dtendon.material, material.c_str()); ReadAttr(eten, "rgba", 4, dtendon.rgba, text); ReadAttr(eten, "width", 1, &dtendon.width, text); // advance eten = NextSiblingElement(eten, "tendon"); } // pin XMLElement* epin = FirstChildElement(elem, "pin"); while (epin) { // read int coord[2] = {0, 0}; ReadAttr(epin, "coord", 2, coord, text, true, false); // insert 2 coordinates (2nd may be unused) comp.pin.push_back(coord[0]); comp.pin.push_back(coord[1]); // advance epin = NextSiblingElement(epin, "pin"); } // make composite char error[200]; bool res = comp.Make(model, pbody, error, 200); // throw error if (!res) { throw mjXError(elem, "%s", error); } } // make flexcomp void mjXReader::OneFlexcomp(XMLElement* elem, mjsBody* pbody) { string text, material; int n; // create out-of-DOM element mjCFlexcomp fcomp; mjsFlex& dflex = *fcomp.def.spec.flex; // common properties ReadAttrTxt(elem, "name", fcomp.name, true); if (MapValue(elem, "type", &n, fcomp_map, mjNFCOMPTYPES)) { fcomp.type = (mjtFcompType)n; } ReadAttr(elem, "count", 3, fcomp.count, text); ReadAttr(elem, "spacing", 3, fcomp.spacing, text); ReadAttr(elem, "scale", 3, fcomp.scale, text); ReadAttr(elem, "mass", 1, &fcomp.mass, text); ReadAttr(elem, "inertiabox", 1, &fcomp.inertiabox, text); fcomp.file = ReadAttrFile(elem, "file", modelfiledir_).value_or(""); if (ReadAttrTxt(elem, "material", material)) { mjs_setString(dflex.material, material.c_str()); } ReadAttr(elem, "rgba", 4, dflex.rgba, text); if (MapValue(elem, "flatskin", &n, bool_map, 2)) { dflex.flatskin = (n==1); } ReadAttrInt(elem, "dim", &dflex.dim); ReadAttr(elem, "radius", 1, &dflex.radius, text); ReadAttrInt(elem, "group", &dflex.group); // pose ReadAttr(elem, "pos", 3, fcomp.pos, text); ReadAttr(elem, "quat", 4, fcomp.quat, text); ReadAlternative(elem, fcomp.alt); // user or internal if (MapValue(elem, "rigid", &n, bool_map, 2)) { fcomp.rigid = (n==1); } if (ReadAttrTxt(elem, "point", text)){ fcomp.point = String2Vector(text); } if (ReadAttrTxt(elem, "element", text)){ fcomp.element = String2Vector(text); } if (ReadAttrTxt(elem, "texcoord", text)) { fcomp.texcoord = String2Vector(text); } // edge XMLElement* edge = FirstChildElement(elem, "edge"); if (edge) { if (MapValue(edge, "equality", &n, bool_map, 2)) { fcomp.equality = (n==1); } ReadAttr(edge, "solref", mjNREF, fcomp.def.spec.equality->solref, text, false, false); ReadAttr(edge, "solimp", mjNIMP, fcomp.def.spec.equality->solimp, text, false, false); ReadAttr(edge, "stiffness", 1, &dflex.edgestiffness, text); ReadAttr(edge, "damping", 1, &dflex.edgedamping, text); } // contact XMLElement* cont = FirstChildElement(elem, "contact"); if (cont) { ReadAttrInt(cont, "contype", &dflex.contype); ReadAttrInt(cont, "conaffinity", &dflex.conaffinity); ReadAttrInt(cont, "condim", &dflex.condim); ReadAttrInt(cont, "priority", &dflex.priority); ReadAttr(cont, "friction", 3, dflex.friction, text, false, false); ReadAttr(cont, "solmix", 1, &dflex.solmix, text); ReadAttr(cont, "solref", mjNREF, dflex.solref, text, false, false); ReadAttr(cont, "solimp", mjNIMP, dflex.solimp, text, false, false); ReadAttr(cont, "margin", 1, &dflex.margin, text); ReadAttr(cont, "gap", 1, &dflex.gap, text); if (MapValue(cont, "internal", &n, bool_map, 2)) { dflex.internal = (n==1); } MapValue(cont, "selfcollide", &dflex.selfcollide, flexself_map, 5); ReadAttrInt(cont, "activelayers", &dflex.activelayers); } // pin XMLElement* epin = FirstChildElement(elem, "pin"); while (epin) { // accumulate id, coord, range if (ReadAttrTxt(epin, "id", text)) { vector v = String2Vector(text); fcomp.pinid.insert(fcomp.pinid.end(), v.begin(), v.end()); } if (ReadAttrTxt(epin, "range", text)) { vector v = String2Vector(text); fcomp.pinrange.insert(fcomp.pinrange.end(), v.begin(), v.end()); } if (ReadAttrTxt(epin, "grid", text)) { vector v = String2Vector(text); fcomp.pingrid.insert(fcomp.pingrid.end(), v.begin(), v.end()); } if (ReadAttrTxt(epin, "gridrange", text)) { vector v = String2Vector(text); fcomp.pingridrange.insert(fcomp.pingridrange.end(), v.begin(), v.end()); } // advance epin = NextSiblingElement(epin, "pin"); } // plugin XMLElement* eplugin = FirstChildElement(elem, "plugin"); if (eplugin) { OnePlugin(eplugin, &fcomp.plugin); } // make flexcomp char error[200]; bool res = fcomp.Make(model, pbody, error, 200); // throw error if (!res) { throw mjXError(elem, "%s", error); } } // add plugin void mjXReader::OnePlugin(XMLElement* elem, mjsPlugin* plugin) { plugin->active = true; std::string name = ""; std::string instance_name = ""; ReadAttrTxt(elem, "plugin", name); ReadAttrTxt(elem, "instance", instance_name); mjs_setString(plugin->name, name.c_str()); mjs_setString(plugin->instance_name, instance_name.c_str()); if (instance_name.empty()) { plugin->instance = mjs_addPlugin(model)->instance; ReadPluginConfigs(elem, plugin); } else { model->hasImplicitPluginElem = true; } } //------------------ MJCF-specific sections -------------------------------------------------------- // default section parser void mjXReader::Default(XMLElement* section, int parentid) { XMLElement* elem; string text, name; mjsDefault* def; int thisid; // create new default, except at top level (already added in mjCModel ctor) text.clear(); ReadAttrTxt(section, "class", text); if (text.empty()) { if (parentid>=0) { throw mjXError(section, "empty class name"); } else { text = "main"; } } if (parentid>=0) { def = mjs_addDefault(model, text.c_str(), parentid, &thisid); if (!def) { throw mjXError(section, "repeated default class name"); } } else { thisid = 0; def = mjs_getSpecDefault(model); mjs_setString(def->name, text.c_str()); } // iterate over elements other than nested defaults elem = FirstChildElement(section); while (elem) { // get element name name = elem->Value(); // read mesh if (name=="mesh") OneMesh(elem, def->mesh); // read material else if (name=="material") OneMaterial(elem, def->material); // read joint else if (name=="joint") OneJoint(elem, def->joint); // read geom else if (name=="geom") OneGeom(elem, def->geom); // read site else if (name=="site") OneSite(elem, def->site); // read camera else if (name=="camera") OneCamera(elem, def->camera); // read light else if (name=="light") OneLight(elem, def->light); // read pair else if (name=="pair") OnePair(elem, def->pair); // read equality else if (name=="equality") OneEquality(elem, def->equality); // read tendon else if (name=="tendon") OneTendon(elem, def->tendon); // read actuator else if (name=="general" || name=="motor" || name=="position" || name=="velocity" || name=="damper" || name=="intvelocity" || name=="cylinder" || name=="muscle" || name=="adhesion") { OneActuator(elem, def->actuator); } // advance elem = NextSiblingElement(elem); } // iterate over nested defaults elem = FirstChildElement(section); while (elem) { // get element name name = elem->Value(); // read default if (name=="default") { Default(elem, thisid); } // advance elem = NextSiblingElement(elem); } } // extension section parser void mjXReader::Extension(XMLElement* section) { XMLElement* elem = FirstChildElement(section); std::vector> active_plugins; while (elem) { // get sub-element name std::string_view name = elem->Value(); if (name == "plugin") { std::string plugin_name; int plugin_slot = -1; ReadAttrTxt(elem, "plugin", plugin_name, /* required = */ true); const mjpPlugin* plugin = mjp_getPlugin(plugin_name.c_str(), &plugin_slot); if (!plugin) { throw mjXError(elem, "unknown plugin '%s'", plugin_name.c_str()); } bool already_declared = false; for (const auto& [existing_plugin, existing_slot] : active_plugins) { if (plugin == existing_plugin) { already_declared = true; break; } } if (!already_declared) { active_plugins.emplace_back(std::make_pair(plugin, plugin_slot)); } XMLElement* child = FirstChildElement(elem); while (child) { if (std::string(child->Value())=="instance") { if (model->hasImplicitPluginElem) { throw mjXError( child, "explicit plugin instance must appear before implicit plugin elements"); } string name; mjsPlugin* p = mjs_addPlugin(model); mjs_setString(p->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); ReadAttrTxt(child, "name", name, /* required = */ true); mjs_setString(p->name, name.c_str()); if (!p->name) { throw mjXError(child, "plugin instance must have a name"); } ReadPluginConfigs(child, p); p->plugin_slot = plugin_slot; } child = NextSiblingElement(child); } } // advance to next element elem = NextSiblingElement(elem); } mjs_setActivePlugins(model, &active_plugins); } // custom section parser void mjXReader::Custom(XMLElement* section) { string text, name; XMLElement* elem; double data[500]; // iterate over child elements elem = FirstChildElement(section); while (elem) { // get sub-element name name = elem->Value(); string elname; // numeric if (name=="numeric") { // create custom mjsNumeric* pnum = mjs_addNumeric(model); // write error info mjs_setString(pnum->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); // read attributes ReadAttrTxt(elem, "name", elname, true); mjs_setString(pnum->name, elname.c_str()); if (ReadAttrInt(elem, "size", &pnum->size)) { int sz = pnum->size < 500 ? pnum->size : 500; for (int i=0; isize = 501; } int len = ReadAttr(elem, "data", pnum->size, data, text, false, false); if (pnum->size==501) { pnum->size = len; } if (pnum->size<1 || pnum->size>500) { throw mjXError(elem, "custom field size must be between 1 and 500"); } // copy data mjs_setDouble(pnum->data, data, pnum->size); } // text else if (name=="text") { // create custom mjsText* pte = mjs_addText(model); // write error info mjs_setString(pte->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); // read attributes ReadAttrTxt(elem, "name", elname, true); mjs_setString(pte->name, elname.c_str()); ReadAttrTxt(elem, "data", text, true); if (text.empty()) { throw mjXError(elem, "text field cannot be empty"); } // copy data mjs_setString(pte->data, text.c_str()); } // tuple else if (name=="tuple") { // create custom mjsTuple* ptu = mjs_addTuple(model); // write error info mjs_setString(ptu->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); // read attributes ReadAttrTxt(elem, "name", elname, true); mjs_setString(ptu->name, elname.c_str()); // read objects and add XMLElement* obj = FirstChildElement(elem); std::vector objtype; std::string objname = ""; std::vector objprm; while (obj) { // get sub-element name name = obj->Value(); // new object if (name=="element") { // read type, check and assign ReadAttrTxt(obj, "objtype", text, true); mjtObj otype = (mjtObj)mju_str2Type(text.c_str()); if (otype==mjOBJ_UNKNOWN) { throw mjXError(obj, "unknown object type"); } objtype.push_back(otype); // read name and assign ReadAttrTxt(obj, "objname", text, true); objname += " " + text; // read parameter and assign double oprm = 0; ReadAttr(obj, "prm", 1, &oprm, text); objprm.push_back(oprm); } // advance to next object obj = NextSiblingElement(obj); } mjs_setInt(ptu->objtype, objtype.data(), objtype.size()); mjs_setStringVec(ptu->objname, objname.c_str()); mjs_setDouble(ptu->objprm, objprm.data(), objprm.size()); } // advance to next element elem = NextSiblingElement(elem); } } // visual section parser void mjXReader::Visual(XMLElement* section) { string text, name; XMLElement* elem; mjVisual* vis = &model->visual; // iterate over child elements elem = FirstChildElement(section); while (elem) { // get sub-element name name = elem->Value(); // global sub-element if (name=="global") { ReadAttr(elem, "fovy", 1, &vis->global.fovy, text); ReadAttr(elem, "ipd", 1, &vis->global.ipd, text); ReadAttr(elem, "azimuth", 1, &vis->global.azimuth, text); ReadAttr(elem, "elevation", 1, &vis->global.elevation, text); ReadAttr(elem, "linewidth", 1, &vis->global.linewidth, text); ReadAttr(elem, "glow", 1, &vis->global.glow, text); ReadAttrInt(elem, "offwidth", &vis->global.offwidth); ReadAttrInt(elem, "offheight", &vis->global.offheight); if (ReadAttr(elem, "realtime", 1, &vis->global.realtime, text)) { if (vis->global.realtime<=0) { throw mjXError(elem, "realtime must be greater than 0"); } } int ellipsoidinertia; if (MapValue(elem, "ellipsoidinertia", &ellipsoidinertia, bool_map, 2)) { vis->global.ellipsoidinertia = (ellipsoidinertia==1); } int bvactive; if (MapValue(elem, "bvactive", &bvactive, bool_map, 2)) { vis->global.bvactive = (bvactive==1); } } // quality sub-element else if (name=="quality") { ReadAttrInt(elem, "shadowsize", &vis->quality.shadowsize); ReadAttrInt(elem, "offsamples", &vis->quality.offsamples); ReadAttrInt(elem, "numslices", &vis->quality.numslices); ReadAttrInt(elem, "numstacks", &vis->quality.numstacks); ReadAttrInt(elem, "numquads", &vis->quality.numquads); } // headlight sub-element else if (name=="headlight") { ReadAttr(elem, "ambient", 3, vis->headlight.ambient, text); ReadAttr(elem, "diffuse", 3, vis->headlight.diffuse, text); ReadAttr(elem, "specular", 3, vis->headlight.specular, text); ReadAttrInt(elem, "active", &vis->headlight.active); } // map sub-element else if (name=="map") { ReadAttr(elem, "stiffness", 1, &vis->map.stiffness, text); ReadAttr(elem, "stiffnessrot", 1, &vis->map.stiffnessrot, text); ReadAttr(elem, "force", 1, &vis->map.force, text); ReadAttr(elem, "torque", 1, &vis->map.torque, text); ReadAttr(elem, "alpha", 1, &vis->map.alpha, text); ReadAttr(elem, "fogstart", 1, &vis->map.fogstart, text); ReadAttr(elem, "fogend", 1, &vis->map.fogend, text); ReadAttr(elem, "znear", 1, &vis->map.znear, text); if (vis->map.znear<=0) { throw mjXError(elem, "znear must be strictly positive"); } ReadAttr(elem, "zfar", 1, &vis->map.zfar, text); ReadAttr(elem, "haze", 1, &vis->map.haze, text); ReadAttr(elem, "shadowclip", 1, &vis->map.shadowclip, text); ReadAttr(elem, "shadowscale", 1, &vis->map.shadowscale, text); ReadAttr(elem, "actuatortendon", 1, &vis->map.actuatortendon, text); } // scale sub-element else if (name=="scale") { ReadAttr(elem, "forcewidth", 1, &vis->scale.forcewidth, text); ReadAttr(elem, "contactwidth", 1, &vis->scale.contactwidth, text); ReadAttr(elem, "contactheight", 1, &vis->scale.contactheight, text); ReadAttr(elem, "connect", 1, &vis->scale.connect, text); ReadAttr(elem, "com", 1, &vis->scale.com, text); ReadAttr(elem, "camera", 1, &vis->scale.camera, text); ReadAttr(elem, "light", 1, &vis->scale.light, text); ReadAttr(elem, "selectpoint", 1, &vis->scale.selectpoint, text); ReadAttr(elem, "jointlength", 1, &vis->scale.jointlength, text); ReadAttr(elem, "jointwidth", 1, &vis->scale.jointwidth, text); ReadAttr(elem, "actuatorlength", 1, &vis->scale.actuatorlength, text); ReadAttr(elem, "actuatorwidth", 1, &vis->scale.actuatorwidth, text); ReadAttr(elem, "framelength", 1, &vis->scale.framelength, text); ReadAttr(elem, "framewidth", 1, &vis->scale.framewidth, text); ReadAttr(elem, "constraint", 1, &vis->scale.constraint, text); ReadAttr(elem, "slidercrank", 1, &vis->scale.slidercrank, text); ReadAttr(elem, "frustum", 1, &vis->scale.frustum, text); } // rgba sub-element else if (name=="rgba") { ReadAttr(elem, "fog", 4, vis->rgba.fog, text); ReadAttr(elem, "haze", 4, vis->rgba.haze, text); ReadAttr(elem, "force", 4, vis->rgba.force, text); ReadAttr(elem, "inertia", 4, vis->rgba.inertia, text); ReadAttr(elem, "joint", 4, vis->rgba.joint, text); ReadAttr(elem, "actuator", 4, vis->rgba.actuator, text); ReadAttr(elem, "actuatornegative", 4, vis->rgba.actuatornegative, text); ReadAttr(elem, "actuatorpositive", 4, vis->rgba.actuatorpositive, text); ReadAttr(elem, "com", 4, vis->rgba.com, text); ReadAttr(elem, "camera", 4, vis->rgba.camera, text); ReadAttr(elem, "light", 4, vis->rgba.light, text); ReadAttr(elem, "selectpoint", 4, vis->rgba.selectpoint, text); ReadAttr(elem, "connect", 4, vis->rgba.connect, text); ReadAttr(elem, "contactpoint", 4, vis->rgba.contactpoint, text); ReadAttr(elem, "contactforce", 4, vis->rgba.contactforce, text); ReadAttr(elem, "contactfriction", 4, vis->rgba.contactfriction, text); ReadAttr(elem, "contacttorque", 4, vis->rgba.contacttorque, text); ReadAttr(elem, "contactgap", 4, vis->rgba.contactgap, text); ReadAttr(elem, "rangefinder", 4, vis->rgba.rangefinder, text); ReadAttr(elem, "constraint", 4, vis->rgba.constraint, text); ReadAttr(elem, "slidercrank", 4, vis->rgba.slidercrank, text); ReadAttr(elem, "crankbroken", 4, vis->rgba.crankbroken, text); ReadAttr(elem, "frustum", 4, vis->rgba.frustum, text); ReadAttr(elem, "bv", 4, vis->rgba.bv, text); ReadAttr(elem, "bvactive", 4, vis->rgba.bvactive, text); } // advance to next element elem = NextSiblingElement(elem); } } // asset section parser void mjXReader::Asset(XMLElement* section) { int n; string text, name, texname, content_type; XMLElement* elem; // iterate over child elements elem = FirstChildElement(section); while (elem) { // get sub-element name name = elem->Value(); // get class if specified, otherwise use default0 mjsDefault* def = GetClass(elem); if (!def) { def = mjs_getSpecDefault(model); } // texture sub-element if (name=="texture") { // create texture mjsTexture* ptex = mjs_addTexture(model); // write error info mjs_setString(ptex->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); // read attributes if (MapValue(elem, "type", &n, texture_map, texture_sz)) { ptex->type = (mjtTexture)n; } if (ReadAttrTxt(elem, "name", texname)) { mjs_setString(ptex->name, texname.c_str()); } if (ReadAttrTxt(elem, "content_type", content_type)) { mjs_setString(ptex->content_type, content_type.c_str()); } auto file = ReadAttrFile(elem, "file", TextureDir()); if (file.has_value()) { mjs_setString(ptex->file, file->c_str()); } ReadAttrInt(elem, "width", &ptex->width); ReadAttrInt(elem, "height", &ptex->height); ReadAttr(elem, "rgb1", 3, ptex->rgb1, text); ReadAttr(elem, "rgb2", 3, ptex->rgb2, text); ReadAttr(elem, "markrgb", 3, ptex->markrgb, text); ReadAttr(elem, "random", 1, &ptex->random, text); if (MapValue(elem, "builtin", &n, builtin_map, builtin_sz)) { ptex->builtin = (mjtBuiltin)n; } if (MapValue(elem, "mark", &n, mark_map, mark_sz)) { ptex->mark = (mjtMark)n; } if (MapValue(elem, "hflip", &n, bool_map, 2)) { ptex->hflip = (n!=0); } if (MapValue(elem, "vflip", &n, bool_map, 2)) { ptex->vflip = (n!=0); } // grid ReadAttr(elem, "gridsize", 2, ptex->gridsize, text); if (ReadAttrTxt(elem, "gridlayout", text)) { // check length if (text.length()>12) { throw mjXError(elem, "gridlayout length cannot exceed 12 characters"); } if (text.length()!=ptex->gridsize[0]*ptex->gridsize[1]) { throw mjXError(elem, "gridlayout length must match gridsize"); } memcpy(ptex->gridlayout, text.data(), text.length()); } // separate files std::vector cubefiles(6); cubefiles[0] = ReadAttrFile(elem, "fileright", TextureDir()).value_or(""); cubefiles[1] = ReadAttrFile(elem, "fileleft", TextureDir()).value_or(""); cubefiles[2] = ReadAttrFile(elem, "fileup", TextureDir()).value_or(""); cubefiles[3] = ReadAttrFile(elem, "filedown", TextureDir()).value_or(""); cubefiles[4] = ReadAttrFile(elem, "filefront", TextureDir()).value_or(""); cubefiles[5] = ReadAttrFile(elem, "fileback", TextureDir()).value_or(""); for (int i = 0; i < cubefiles.size(); i++) { mjs_setInStringVec(ptex->cubefiles, i, cubefiles[i].c_str()); } } // material sub-element else if (name=="material") { // create material and parse mjsMaterial* pmat = mjs_addMaterial(model, def); OneMaterial(elem, pmat); } // mesh sub-element else if (name=="mesh") { // create mesh and parse mjsMesh* pmesh = mjs_addMesh(model, def); OneMesh(elem, pmesh); } // skin sub-element... deprecate ??? else if (name=="skin") { // create skin and parse mjsSkin* pskin = mjs_addSkin(model); OneSkin(elem, pskin); } // hfield sub-element else if (name=="hfield") { // create hfield mjsHField* phf = mjs_addHField(model); // write error info mjs_setString(phf->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); // read attributes string name, content_type; if (ReadAttrTxt(elem, "name", name)) { mjs_setString(phf->name, name.c_str()); } if (ReadAttrTxt(elem, "content_type", content_type)) { mjs_setString(phf->content_type, content_type.c_str()); } auto file = ReadAttrFile(elem, "file", AssetDir()); if (file.has_value()) { mjs_setString(phf->file, file->c_str()); } ReadAttrInt(elem, "nrow", &phf->nrow); ReadAttrInt(elem, "ncol", &phf->ncol); ReadAttr(elem, "size", 4, phf->size, text, true); // allocate buffer for dynamic hfield, copy user data if given if (!file.has_value() && phf->nrow>0 && phf->ncol>0) { int nrow = phf->nrow; int ncol = phf->ncol; // read user data auto userdata = ReadAttrVec(elem, "elevation"); // user data given, copy into data if (userdata.has_value()) { if (userdata->size() != nrow*ncol) { throw mjXError(elem, "elevation data length must match nrow*ncol"); } // copy in reverse row order, so XML string is top-to-bottom std::vector flipped(nrow*ncol); for (int i = 0; i < nrow; i++) { int flip = nrow-1-i; for (int j = 0; j < ncol; j++) { flipped[flip*ncol + j] = userdata->data()[i*ncol + j]; } } mjs_setFloat(phf->userdata, flipped.data(), flipped.size()); } // user data not given, set to 0 else { std::vector zero(nrow*ncol); mjs_setFloat(phf->userdata, zero.data(), zero.size()); } } } // advance to next element elem = NextSiblingElement(elem); } } // body/world section parser; recursive void mjXReader::Body(XMLElement* section, mjsBody* pbody, mjsFrame* frame) { string text, name; XMLElement* elem; int n; // sanity check if (!pbody) { throw mjXError(section, "null body pointer"); } // no attributes allowed in world body if (mjs_getId(pbody->element)==0 && section->FirstAttribute() && !frame) { throw mjXError(section, "World body cannot have attributes"); } // iterate over sub-elements; attributes set while parsing parent body elem = FirstChildElement(section); while (elem) { // get sub-element name name = elem->Value(); // get class if specified, otherwise use body mjsDefault* def = GetClass(elem); if (!def) { def = mjs_getDefault(frame ? frame->element : pbody->element); } // inertial sub-element if (name=="inertial") { // no inertia allowed in world body if (mjs_getId(pbody->element)==0) { throw mjXError(elem, "World body cannot have inertia"); } pbody->explicitinertial = true; ReadAttr(elem, "pos", 3, pbody->ipos, text, true); ReadQuat(elem, "quat", pbody->iquat, text); ReadAttr(elem, "mass", 1, &pbody->mass, text, true); ReadAttr(elem, "diaginertia", 3, pbody->inertia, text); bool alt = ReadAlternative(elem, pbody->ialt); bool full = ReadAttr(elem, "fullinertia", 6, pbody->fullinertia, text); if (alt && full) { throw mjXError(elem, "multiple orientation specifiers are not allowed"); } } // joint sub-element else if (name=="joint") { // no joints allowed in world body if (mjs_getId(pbody->element)==0) { throw mjXError(elem, "World body cannot have joints"); } // create joint and parse mjsJoint* pjoint = mjs_addJoint(pbody, def); OneJoint(elem, pjoint); mjs_setFrame(pjoint->element, frame); } // freejoint sub-element else if (name=="freejoint") { // no joints allowed in world body if (mjs_getId(pbody->element)==0) { throw mjXError(elem, "World body cannot have joints"); } // create free joint without defaults mjsJoint* pjoint = mjs_addFreeJoint(pbody); mjs_setFrame(pjoint->element, frame); // save defaults after creation, to make sure writing is ok mjs_setDefault(pjoint->element, def); // read attributes std::string name; if (ReadAttrTxt(elem, "name", name)) { mjs_setString(pjoint->name, name.c_str()); } ReadAttrInt(elem, "group", &pjoint->group); } // geom sub-element else if (name=="geom") { // create geom and parse mjsGeom* pgeom = mjs_addGeom(pbody, def); OneGeom(elem, pgeom); mjs_setFrame(pgeom->element, frame); } // site sub-element else if (name=="site") { // create site and parse mjsSite* site = mjs_addSite(pbody, def); OneSite(elem, site); mjs_setFrame(site->element, frame); } // camera sub-element else if (name=="camera") { // create camera and parse mjsCamera* pcam = mjs_addCamera(pbody, def); OneCamera(elem, pcam); mjs_setFrame(pcam->element, frame); } // light sub-element else if (name=="light") { // create light and parse mjsLight* plight = mjs_addLight(pbody, def); OneLight(elem, plight); mjs_setFrame(plight->element, frame); } // plugin sub-element else if (name == "plugin") { OnePlugin(elem, &(pbody->plugin)); } // composite sub-element else if (name=="composite") { // parse composite OneComposite(elem, pbody, def); } // flexcomp sub-element else if (name=="flexcomp") { // parse flexcomp OneFlexcomp(elem, pbody); } // frame sub-element else if (name=="frame") { // read childdef mjsDefault* childdef = 0; if (ReadAttrTxt(elem, "childclass", text)) { childdef = mjs_findDefault(model, text.c_str()); mjs_findDefault(model, text.c_str()); if (!childdef) { throw mjXError(elem, "unknown default childclass"); } } // create frame mjsFrame* pframe = mjs_addFrame(pbody, frame); mjs_setString(pframe->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); mjs_setDefault(pframe->element, childdef ? childdef : def); // read attributes std::string name, childclass; if (ReadAttrTxt(elem, "name", name)) { mjs_setString(pframe->name, name.c_str()); } if (ReadAttrTxt(elem, "childclass", childclass)) { mjs_setString(pframe->childclass, childclass.c_str()); } ReadAttr(elem, "pos", 3, pframe->pos, text); ReadQuat(elem, "quat", pframe->quat, text); ReadAlternative(elem, pframe->alt); Body(elem, pbody, pframe); } // body sub-element else if (name=="body") { // read childdef mjsDefault* childdef = 0; if (ReadAttrTxt(elem, "childclass", text)) { childdef = mjs_findDefault(model, text.c_str()); mjs_findDefault(model, text.c_str()); if (!childdef) { throw mjXError(elem, "unknown default childclass"); } } // create child body mjsBody* pchild = mjs_addBody(pbody, childdef); mjs_setString(pchild->info, std::string("line = " + std::to_string(elem->GetLineNum())).c_str()); // read attributes std::string name, childclass; if (ReadAttrTxt(elem, "name", name)) { mjs_setString(pchild->name, name.c_str()); } if (ReadAttrTxt(elem, "childclass", childclass)) { mjs_setString(pchild->childclass, childclass.c_str()); } ReadAttr(elem, "pos", 3, pchild->pos, text); ReadQuat(elem, "quat", pchild->quat, text); if (MapValue(elem, "mocap", &n, bool_map, 2)) { pchild->mocap = (n==1); } ReadAlternative(elem, pchild->alt); // read gravcomp ReadAttr(elem, "gravcomp", 1, &pchild->gravcomp, text); // read userdata std::vector userdata; ReadVector(elem, "user", userdata, text); mjs_setDouble(pchild->userdata, userdata.data(), userdata.size()); // add frame mjs_setFrame(pchild->element, frame); // make recursive call Body(elem, pchild, nullptr); } // no match else { throw mjXError(elem, "unrecognized model element '%s'", name.c_str()); } // advance to next element elem = NextSiblingElement(elem); } } // contact section parser void mjXReader::Contact(XMLElement* section) { string text, name; XMLElement* elem; // iterate over child elements elem = FirstChildElement(section); while (elem) { // get sub-element name name = elem->Value(); // get class if specified, otherwise use default0 mjsDefault* def = GetClass(elem); if (!def) { def = mjs_getSpecDefault(model); } // geom pair to include if (name=="pair") { // create pair and parse mjsPair* ppair = mjs_addPair(model, def); OnePair(elem, ppair); } // body pair to exclude else if (name=="exclude") { mjsExclude* pexclude = mjs_addExclude(model); string exname, exbody1, exbody2; // write error info mjs_setString(pexclude->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); // read name and body names if (ReadAttrTxt(elem, "name", exname)) { mjs_setString(pexclude->name, exname.c_str()); } ReadAttrTxt(elem, "body1", exbody1, true); mjs_setString(pexclude->bodyname1, exbody1.c_str()); ReadAttrTxt(elem, "body2", exbody2, true); mjs_setString(pexclude->bodyname2, exbody2.c_str()); } // advance to next element elem = NextSiblingElement(elem); } } // constraint section parser void mjXReader::Equality(XMLElement* section) { XMLElement* elem; // iterate over child elements elem = FirstChildElement(section); while (elem) { // get class if specified, otherwise use default0 mjsDefault* def = GetClass(elem); if (!def) { def = mjs_getSpecDefault(model); } // create equality constraint and parse mjsEquality* pequality = mjs_addEquality(model, def); OneEquality(elem, pequality); // advance to next element elem = NextSiblingElement(elem); } } // deformable section parser void mjXReader::Deformable(XMLElement* section) { string name; XMLElement* elem; // iterate over child elements elem = FirstChildElement(section); while (elem) { // get sub-element name name = elem->Value(); // get class if specified, otherwise use default0 mjsDefault* def = GetClass(elem); if (!def) { def = mjs_getSpecDefault(model); } // flex sub-element if (name=="flex") { // create flex and parse mjsFlex* pflex = mjs_addFlex(model); OneFlex(elem, pflex); } // skin sub-element else if (name=="skin") { // create skin and parse mjsSkin* pskin = mjs_addSkin(model); OneSkin(elem, pskin); } // advance to next element elem = NextSiblingElement(elem); } } // tendon section parser void mjXReader::Tendon(XMLElement* section) { string text, text1; XMLElement* elem; double data; // iterate over child elements elem = FirstChildElement(section); while (elem) { // get class if specified, otherwise use default0 mjsDefault* def = GetClass(elem); if (!def) { def = mjs_getSpecDefault(model); } // create equality constraint and parse mjsTendon* pten = mjs_addTendon(model, def); OneTendon(elem, pten); // process wrap sub-elements XMLElement* sub = FirstChildElement(elem); while (sub) { // get wrap type string wrap = sub->Value(); mjsWrap* pwrap;; // read attributes depending on type if (wrap=="site") { ReadAttrTxt(sub, "site", text, true); pwrap = mjs_wrapSite(pten, text.c_str()); } else if (wrap=="geom") { ReadAttrTxt(sub, "geom", text, true); if (!ReadAttrTxt(sub, "sidesite", text1)) { text1.clear(); } pwrap = mjs_wrapGeom(pten, text.c_str(), text1.c_str()); } else if (wrap=="pulley") { ReadAttr(sub, "divisor", 1, &data, text, true); pwrap = mjs_wrapPulley(pten, data); } else if (wrap=="joint") { ReadAttrTxt(sub, "joint", text, true); ReadAttr(sub, "coef", 1, &data, text1, true); pwrap = mjs_wrapJoint(pten, text.c_str(), data); } else { throw mjXError(sub, "unknown wrap type"); // SHOULD NOT OCCUR } mjs_setString(pwrap->info, ("line = " + std::to_string(sub->GetLineNum())).c_str()); // advance to next sub-element sub = NextSiblingElement(sub); } // advance to next element elem = NextSiblingElement(elem); } } // actuator section parser void mjXReader::Actuator(XMLElement* section) { XMLElement* elem; // iterate over child elements elem = FirstChildElement(section); while (elem) { // get class if specified, otherwise use default0 mjsDefault* def = GetClass(elem); if (!def) { def = mjs_getSpecDefault(model); } // create actuator and parse mjsActuator* pact = mjs_addActuator(model, def); OneActuator(elem, pact); // advance to next element elem = NextSiblingElement(elem); } } // sensor section parser void mjXReader::Sensor(XMLElement* section) { int n; XMLElement* elem = FirstChildElement(section); while (elem) { // create sensor, get string type mjsSensor* psen = mjs_addSensor(model); string type = elem->Value(); string text, name, objname, refname; std::vector userdata; // read name, noise, userdata if (ReadAttrTxt(elem, "name", name)) { mjs_setString(psen->name, name.c_str()); } ReadAttr(elem, "cutoff", 1, &psen->cutoff, text); ReadAttr(elem, "noise", 1, &psen->noise, text); if (ReadVector(elem, "user", userdata, text)) { mjs_setDouble(psen->userdata, userdata.data(), userdata.size()); } // common robotic sensors, attached to a site if (type=="touch") { psen->type = mjSENS_TOUCH; psen->objtype = mjOBJ_SITE; ReadAttrTxt(elem, "site", objname, true); } else if (type=="accelerometer") { psen->type = mjSENS_ACCELEROMETER; psen->objtype = mjOBJ_SITE; ReadAttrTxt(elem, "site", objname, true); } else if (type=="velocimeter") { psen->type = mjSENS_VELOCIMETER; psen->objtype = mjOBJ_SITE; ReadAttrTxt(elem, "site", objname, true); } else if (type=="gyro") { psen->type = mjSENS_GYRO; psen->objtype = mjOBJ_SITE; ReadAttrTxt(elem, "site", objname, true); } else if (type=="force") { psen->type = mjSENS_FORCE; psen->objtype = mjOBJ_SITE; ReadAttrTxt(elem, "site", objname, true); } else if (type=="torque") { psen->type = mjSENS_TORQUE; psen->objtype = mjOBJ_SITE; ReadAttrTxt(elem, "site", objname, true); } else if (type=="magnetometer") { psen->type = mjSENS_MAGNETOMETER; psen->objtype = mjOBJ_SITE; ReadAttrTxt(elem, "site", objname, true); } else if (type=="camprojection") { psen->type = mjSENS_CAMPROJECTION; psen->objtype = mjOBJ_SITE; ReadAttrTxt(elem, "site", objname, true); ReadAttrTxt(elem, "camera", refname, true); psen->reftype = mjOBJ_CAMERA; } else if (type=="rangefinder") { psen->type = mjSENS_RANGEFINDER; psen->objtype = mjOBJ_SITE; ReadAttrTxt(elem, "site", objname, true); } // sensors related to scalar joints, tendons, actuators else if (type=="jointpos") { psen->type = mjSENS_JOINTPOS; psen->objtype = mjOBJ_JOINT; ReadAttrTxt(elem, "joint", objname, true); } else if (type=="jointvel") { psen->type = mjSENS_JOINTVEL; psen->objtype = mjOBJ_JOINT; ReadAttrTxt(elem, "joint", objname, true); } else if (type=="tendonpos") { psen->type = mjSENS_TENDONPOS; psen->objtype = mjOBJ_TENDON; ReadAttrTxt(elem, "tendon", objname, true); } else if (type=="tendonvel") { psen->type = mjSENS_TENDONVEL; psen->objtype = mjOBJ_TENDON; ReadAttrTxt(elem, "tendon", objname, true); } else if (type=="actuatorpos") { psen->type = mjSENS_ACTUATORPOS; psen->objtype = mjOBJ_ACTUATOR; ReadAttrTxt(elem, "actuator", objname, true); } else if (type=="actuatorvel") { psen->type = mjSENS_ACTUATORVEL; psen->objtype = mjOBJ_ACTUATOR; ReadAttrTxt(elem, "actuator", objname, true); } else if (type=="actuatorfrc") { psen->type = mjSENS_ACTUATORFRC; psen->objtype = mjOBJ_ACTUATOR; ReadAttrTxt(elem, "actuator", objname, true); } else if (type=="jointactuatorfrc") { psen->type = mjSENS_JOINTACTFRC; psen->objtype = mjOBJ_JOINT; ReadAttrTxt(elem, "joint", objname, true); } // sensors related to ball joints else if (type=="ballquat") { psen->type = mjSENS_BALLQUAT; psen->objtype = mjOBJ_JOINT; ReadAttrTxt(elem, "joint", objname, true); } else if (type=="ballangvel") { psen->type = mjSENS_BALLANGVEL; psen->objtype = mjOBJ_JOINT; ReadAttrTxt(elem, "joint", objname, true); } // joint and tendon limit sensors else if (type=="jointlimitpos") { psen->type = mjSENS_JOINTLIMITPOS; psen->objtype = mjOBJ_JOINT; ReadAttrTxt(elem, "joint", objname, true); } else if (type=="jointlimitvel") { psen->type = mjSENS_JOINTLIMITVEL; psen->objtype = mjOBJ_JOINT; ReadAttrTxt(elem, "joint", objname, true); } else if (type=="jointlimitfrc") { psen->type = mjSENS_JOINTLIMITFRC; psen->objtype = mjOBJ_JOINT; ReadAttrTxt(elem, "joint", objname, true); } else if (type=="tendonlimitpos") { psen->type = mjSENS_TENDONLIMITPOS; psen->objtype = mjOBJ_TENDON; ReadAttrTxt(elem, "tendon", objname, true); } else if (type=="tendonlimitvel") { psen->type = mjSENS_TENDONLIMITVEL; psen->objtype = mjOBJ_TENDON; ReadAttrTxt(elem, "tendon", objname, true); } else if (type=="tendonlimitfrc") { psen->type = mjSENS_TENDONLIMITFRC; psen->objtype = mjOBJ_TENDON; ReadAttrTxt(elem, "tendon", objname, true); } // sensors attached to an object with spatial frame: (x)body, geom, site, camera else if (type=="framepos") { psen->type = mjSENS_FRAMEPOS; ReadAttrTxt(elem, "objtype", text, true); psen->objtype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "objname", objname, true); if (ReadAttrTxt(elem, "reftype", text)) { psen->reftype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "refname", refname, true); } else if (ReadAttrTxt(elem, "refname", text)) { throw mjXError(elem, "refname '%s' given but reftype is missing", text.c_str()); } } else if (type=="framequat") { psen->type = mjSENS_FRAMEQUAT; ReadAttrTxt(elem, "objtype", text, true); psen->objtype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "objname", objname, true); if (ReadAttrTxt(elem, "reftype", text)) { psen->reftype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "refname", refname, true); } else if (ReadAttrTxt(elem, "refname", text)) { throw mjXError(elem, "refname '%s' given but reftype is missing", text.c_str()); } } else if (type=="framexaxis") { psen->type = mjSENS_FRAMEXAXIS; ReadAttrTxt(elem, "objtype", text, true); psen->objtype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "objname", objname, true); if (ReadAttrTxt(elem, "reftype", text)) { psen->reftype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "refname", refname, true); } else if (ReadAttrTxt(elem, "refname", text)) { throw mjXError(elem, "refname '%s' given but reftype is missing", text.c_str()); } } else if (type=="frameyaxis") { psen->type = mjSENS_FRAMEYAXIS; ReadAttrTxt(elem, "objtype", text, true); psen->objtype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "objname", objname, true); if (ReadAttrTxt(elem, "reftype", text)) { psen->reftype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "refname", refname, true); } else if (ReadAttrTxt(elem, "refname", text)) { throw mjXError(elem, "refname '%s' given but reftype is missing", text.c_str()); } } else if (type=="framezaxis") { psen->type = mjSENS_FRAMEZAXIS; ReadAttrTxt(elem, "objtype", text, true); psen->objtype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "objname", objname, true); if (ReadAttrTxt(elem, "reftype", text)) { psen->reftype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "refname", refname, true); } else if (ReadAttrTxt(elem, "refname", text)) { throw mjXError(elem, "refname '%s' given but reftype is missing", text.c_str()); } } else if (type=="framelinvel") { psen->type = mjSENS_FRAMELINVEL; ReadAttrTxt(elem, "objtype", text, true); psen->objtype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "objname", objname, true); if (ReadAttrTxt(elem, "reftype", text)) { psen->reftype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "refname", refname, true); } else if (ReadAttrTxt(elem, "refname", text)) { throw mjXError(elem, "refname '%s' given but reftype is missing", text.c_str()); } } else if (type=="frameangvel") { psen->type = mjSENS_FRAMEANGVEL; ReadAttrTxt(elem, "objtype", text, true); psen->objtype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "objname", objname, true); if (ReadAttrTxt(elem, "reftype", text)) { psen->reftype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "refname", refname, true); } else if (ReadAttrTxt(elem, "refname", text)) { throw mjXError(elem, "refname '%s' given but reftype is missing", text.c_str()); } } else if (type=="framelinacc") { psen->type = mjSENS_FRAMELINACC; ReadAttrTxt(elem, "objtype", text, true); psen->objtype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "objname", objname, true); } else if (type=="frameangacc") { psen->type = mjSENS_FRAMEANGACC; ReadAttrTxt(elem, "objtype", text, true); psen->objtype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "objname", objname, true); } // sensors related to kinematic subtrees; attached to a body (which is the subtree root) else if (type=="subtreecom") { psen->type = mjSENS_SUBTREECOM; psen->objtype = mjOBJ_BODY; ReadAttrTxt(elem, "body", objname, true); } else if (type=="subtreelinvel") { psen->type = mjSENS_SUBTREELINVEL; psen->objtype = mjOBJ_BODY; ReadAttrTxt(elem, "body", objname, true); } else if (type=="subtreeangmom") { psen->type = mjSENS_SUBTREEANGMOM; psen->objtype = mjOBJ_BODY; ReadAttrTxt(elem, "body", objname, true); } // global sensors else if (type=="clock") { psen->type = mjSENS_CLOCK; psen->objtype = mjOBJ_UNKNOWN; } // user-defined sensor else if (type=="user") { psen->type = mjSENS_USER; bool objname_given = ReadAttrTxt(elem, "objname", objname); if (ReadAttrTxt(elem, "objtype", text)) { if (!objname_given) { throw mjXError(elem, "objtype '%s' given but objname is missing", text.c_str()); } psen->objtype = (mjtObj)mju_str2Type(text.c_str()); } else if (objname_given) { throw mjXError(elem, "objname '%s' given but objtype is missing", objname.c_str()); } ReadAttrInt(elem, "dim", &psen->dim, true); // keywords if (MapValue(elem, "needstage", &n, stage_map, stage_sz)) { psen->needstage = (mjtStage)n; } if (MapValue(elem, "datatype", &n, datatype_map, datatype_sz)) { psen->datatype = (mjtDataType)n; } } else if (type=="plugin") { psen->type = mjSENS_PLUGIN; OnePlugin(elem, &psen->plugin); ReadAttrTxt(elem, "objtype", text); psen->objtype = (mjtObj)mju_str2Type(text.c_str()); ReadAttrTxt(elem, "objname", objname); if (psen->objtype != mjOBJ_UNKNOWN && objname.empty()) { throw mjXError(elem, "objtype is specified but objname is not"); } if (psen->objtype == mjOBJ_UNKNOWN && !objname.empty()) { throw mjXError(elem, "objname is specified but objtype is not"); } if (ReadAttrTxt(elem, "reftype", text)) { psen->reftype = (mjtObj)mju_str2Type(text.c_str()); } ReadAttrTxt(elem, "refname", refname); if (psen->reftype != mjOBJ_UNKNOWN && refname.empty()) { throw mjXError(elem, "reftype is specified but refname is not"); } if (psen->reftype == mjOBJ_UNKNOWN && !refname.empty()) { throw mjXError(elem, "refname is specified but reftype is not"); } } if (!objname.empty()) { mjs_setString(psen->objname, objname.c_str()); } if (!refname.empty()) { mjs_setString(psen->refname, refname.c_str()); } // write info mjs_setString(psen->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); // advance to next element elem = NextSiblingElement(elem); } } // keyframe section parser void mjXReader::Keyframe(XMLElement* section) { XMLElement* elem; int n; double data[1000]; // iterate over child elements elem = FirstChildElement(section); while (elem) { string text, name = ""; // add keyframe mjsKey* pk = mjs_addKey(model); // read name, time ReadAttrTxt(elem, "name", name); mjs_setString(pk->name, name.c_str()); ReadAttr(elem, "time", 1, &pk->time, text); // read qpos n = ReadAttr(elem, "qpos", 1000, data, text, false, false); if (n) { mjs_setDouble(pk->qpos, data, n); } // read qvel n = ReadAttr(elem, "qvel", 1000, data, text, false, false); if (n) { mjs_setDouble(pk->qvel, data, n); } // read act n = ReadAttr(elem, "act", 1000, data, text, false, false); if (n) { mjs_setDouble(pk->act, data, n); } // read mpos n = ReadAttr(elem, "mpos", 1000, data, text, false, false); if (n) { mjs_setDouble(pk->mpos, data, n); } // read mquat n = ReadAttr(elem, "mquat", 1000, data, text, false, false); if (n) { mjs_setDouble(pk->mquat, data, n); } // read ctrl n = ReadAttr(elem, "ctrl", 1000, data, text, false, false); if (n) { mjs_setDouble(pk->ctrl, data, n); } // advance to next element elem = NextSiblingElement(elem); } } // get defaults class mjsDefault* mjXReader::GetClass(XMLElement* section) { string text; mjsDefault* def = nullptr; if (ReadAttrTxt(section, "class", text)) { def = mjs_findDefault(model, text.c_str()); if (!def) { throw mjXError( section, std::string("unknown default class name '" + text + "'").c_str()); } } return def; } // return true if c is a directory path separator (i.e. '/' or '\' on windows) static bool IsSeperator(char c) { return c == '/' || c == '\\'; } void mjXReader::SetModelFileDir(std::string modelfiledir) { modelfiledir_ = modelfiledir; if (!modelfiledir_.empty() && !IsSeperator(modelfiledir_.back())) { modelfiledir_.append("/"); } } void mjXReader::SetAssetDir(std::string assetdir) { assetdir_ = assetdir; if (!assetdir_.empty() && !IsSeperator(assetdir_.back())) { assetdir_.append("/"); } } void mjXReader::SetMeshDir(std::string meshdir) { meshdir_ = meshdir; if (!meshdir_.empty() && !IsSeperator(meshdir_.back())) { meshdir_.append("/"); } } void mjXReader::SetTextureDir(std::string texturedir) { texturedir_ = texturedir; if (!texturedir_.empty() && !IsSeperator(texturedir_.back())) { texturedir_.append("/"); } } std::string mjXReader::AssetDir() const { return modelfiledir_ + assetdir_; } std::string mjXReader::MeshDir() const { if (meshdir_.empty()) { return AssetDir(); } return modelfiledir_ + meshdir_; } std::string mjXReader::TextureDir() const { if (texturedir_.empty()) { return AssetDir(); } return modelfiledir_ + texturedir_; }