Table-driven attribute reading: rebase the reader on generated rows.
Every mechanical attribute read in MJCF now derives from mjcf.schema. generate_read_table.py emits typed mjXAttr rows (mjcf_read_table.inc, doc_test-gated) binding each attribute to its spec struct field; field offsets are offsetof() expressions, so binding mistakes are compile errors, and the field's C type -- parsed from the headers -- selects the row kind, so mjtNum-versus-double is decided by the struct, not the schema. mjXReader::ReadAttrTable is the generic loop; its static core also serves the section parsers and records XML-authored fields via mjs_setAuthored for attach conflict resolution. Row kinds cover strings, string lists, numeric scalars and vectors (exact and ranged), enums (int- and byte-width), bitwise flag sets, bools, unbounded typed vectors, fixed char arrays, and identity constants declared by 'set'. The rows are inline variables, and carry the writing=custom flag, because the writer will share them. The keyword maps the rows reference are generated too: the ~48 hand-written mjMap tables become mjcf_map.h, one map and size constant per enum as C++17 inline variables, retiring the hand-maintained extern block in xml_base.h. Map names follow the schema enum names (fluid->fluidshape, TFAuto->FalseTrueAuto, FAuto->FalseAuto, joint->jointtype, geom->geomtype, jac->jacobian); all maps are key-order- and value-identical to the hand tables they replace, and bool_map is hand-emitted (the bool type is built in, not a schema enum). The OneX() parsers reduce to genuine irregulars, schema-marked as reading=custom: orientation alternatives, file attributes (VFS and asset-dir context), the actuator shorthand remappings and per-type input maps, springlength's one-value copy, mesh builtin construction, hfield elevation, texture cube files, flexcomp seeding, the memory suffix parse, and the flag bit families. All 41 sensors that are pure identity-plus-references -- including the frame family and insidesite -- dispatch through a generated tag table; frame-sensor objtype/reftype vocabulary tightens from the full mju_str2Type namespace to the documented body/xbody/geom/site/camera subset, so an invalid keyword now fails at parse time instead of compile time. The equality family and both tendon types read shared group rows; the twelve actuator shorthands share the general rows, with per-tag legality enforced by the schema check. Sections bind non-mjs structs, the visual sub-sections reaching their anonymous sub-structs through member paths declared by an element-level field= facet. Latent irregularities surfaced by the migration and preserved via schema declarations or remnants: key's name is set even when absent, eulerseq and gridlayout are fixed char arrays (chars[n], arity in characters), gridlayout's length-must-match-gridsize stays a value-conditional remnant, and constructor-style elements (tendon wraps, asset model, replicate, attach) are annotated as such -- their attributes are arguments, not field writes. Two coherence tests guard the schema against the C sources: every schema enum constant must be a member of the C enum it claims, and every C member must be a keyword, a count sentinel, or a documented exemption; and generate_default_table.py emits one row per defaulted attribute (mjcf_default_table.inc), compared by SchemaDefaultsTest against a freshly-constructed spec -- the schema cannot disagree with the C default-constructors without failing the suite. Verified: doc_test regenerates and diffs every artifact; the full suite; and an A/B harness compiling the model corpus against the pre-migration reader -- saved XML and binary models are byte-identical. PiperOrigin-RevId: 958075724 Change-Id: I9715fe4deeb438eec988fd5084d74ba8b466b10b
This commit is contained in:
committed by
Copybara-Service
parent
790f8fac30
commit
4278c7b0cd
@@ -392,7 +392,7 @@ void mjXWriter::OneJoint(XMLElement* elem, const mjCJoint* joint, mjCDef* def,
|
||||
|
||||
// defaults and regular
|
||||
if (joint->type != def->Joint().type) {
|
||||
WriteAttrTxt(elem, "type", FindValue(joint_map, joint_sz, joint->type));
|
||||
WriteAttrTxt(elem, "type", FindValue(jointtype_map, jointtype_sz, joint->type));
|
||||
}
|
||||
WriteAttrInt(elem, "group", joint->group, def->Joint().group);
|
||||
WriteAttr(elem, "ref", 1, &joint->ref, &zero);
|
||||
@@ -410,11 +410,11 @@ void mjXWriter::OneJoint(XMLElement* elem, const mjCJoint* joint, mjCDef* def,
|
||||
WriteAttr(elem, "stiffness", nstiff, joint->stiffness, def->Joint().stiffness);
|
||||
}
|
||||
if (joint->type != mjJNT_FREE) {
|
||||
WriteAttrKey(elem, "limited", TFAuto_map, 3, joint->limited, def->Joint().limited);
|
||||
WriteAttrKey(elem, "limited", FalseTrueAuto_map, 3, joint->limited, def->Joint().limited);
|
||||
}
|
||||
WriteAttr(elem, "range", 2, joint->range, def->Joint().range);
|
||||
if (joint->type != mjJNT_FREE && joint->type != mjJNT_BALL) {
|
||||
WriteAttrKey(elem, "actuatorfrclimited", TFAuto_map, 3, joint->actfrclimited,
|
||||
WriteAttrKey(elem, "actuatorfrclimited", FalseTrueAuto_map, 3, joint->actfrclimited,
|
||||
def->Joint().actfrclimited);
|
||||
}
|
||||
WriteAttrKey(elem, "actuatorgravcomp", bool_map, 2, joint->actgravcomp, def->Joint().actgravcomp);
|
||||
@@ -485,7 +485,7 @@ void mjXWriter::OneGeom(XMLElement* elem, const mjCGeom* geom, mjCDef* def, stri
|
||||
}
|
||||
|
||||
// defaults and regular
|
||||
WriteAttrKey(elem, "type", geom_map, mjNGEOMTYPES, geom->type, def->Geom().type);
|
||||
WriteAttrKey(elem, "type", geomtype_map, mjNGEOMTYPES, geom->type, def->Geom().type);
|
||||
WriteAttrInt(elem, "contype", geom->contype, def->Geom().contype);
|
||||
WriteAttrInt(elem, "conaffinity", geom->conaffinity, def->Geom().conaffinity);
|
||||
WriteAttrInt(elem, "condim", geom->condim, def->Geom().condim);
|
||||
@@ -500,10 +500,10 @@ void mjXWriter::OneGeom(XMLElement* elem, const mjCGeom* geom, mjCDef* def, stri
|
||||
WriteAttr(elem, "surfacevel", 6, geom->surfacevel, def->Geom().surfacevel, true);
|
||||
WriteAttr(elem, "adhesion", 1, &geom->adhesion, &def->Geom().adhesion);
|
||||
WriteAttrKey(elem, "fluidshape",
|
||||
fluid_map, 2, geom->fluid_ellipsoid, def->Geom().fluid_ellipsoid);
|
||||
fluidshape_map, 2, geom->fluid_ellipsoid, def->Geom().fluid_ellipsoid);
|
||||
WriteAttr(elem, "fluidcoef", 5, geom->fluid_coefs, def->Geom().fluid_coefs);
|
||||
if (geom->type != mjGEOM_MESH) {
|
||||
WriteAttrKey(elem, "shellinertia", meshtype_map, 2, geom->typeinertia,
|
||||
WriteAttrKey(elem, "shellinertia", bool_map, 2, geom->typeinertia,
|
||||
def->Geom().typeinertia);
|
||||
}
|
||||
if (mjuu_defined(geom->mass)) {
|
||||
@@ -558,7 +558,7 @@ void mjXWriter::OneSite(XMLElement* elem, const mjCSite* site, mjCDef* def, stri
|
||||
|
||||
// defaults and regular
|
||||
WriteAttrInt(elem, "group", site->group, def->Site().group);
|
||||
WriteAttrKey(elem, "type", geom_map, mjNGEOMTYPES, site->type, def->Site().type);
|
||||
WriteAttrKey(elem, "type", geomtype_map, mjNGEOMTYPES, site->type, def->Site().type);
|
||||
if (site->get_material() != def->Site().get_material()) {
|
||||
WriteAttrTxt(elem, "material", site->get_material());
|
||||
}
|
||||
@@ -772,8 +772,8 @@ void mjXWriter::OneTendon(XMLElement* elem, const mjCTendon* tendon, mjCDef* def
|
||||
true);
|
||||
WriteAttr(elem, "solimpfriction", mjNIMP, tendon->solimp_friction, def->Tendon().solimp_friction,
|
||||
true);
|
||||
WriteAttrKey(elem, "limited", TFAuto_map, 3, tendon->limited, def->Tendon().limited);
|
||||
WriteAttrKey(elem, "actuatorfrclimited", TFAuto_map, 3, tendon->actfrclimited, def->Tendon().actfrclimited);
|
||||
WriteAttrKey(elem, "limited", FalseTrueAuto_map, 3, tendon->limited, def->Tendon().limited);
|
||||
WriteAttrKey(elem, "actuatorfrclimited", FalseTrueAuto_map, 3, tendon->actfrclimited, def->Tendon().actfrclimited);
|
||||
WriteAttr(elem, "range", 2, tendon->range, def->Tendon().range);
|
||||
WriteAttr(elem, "actuatorfrcrange", 2, tendon->actfrcrange, def->Tendon().actfrcrange);
|
||||
WriteAttr(elem, "margin", 1, &tendon->margin, &def->Tendon().margin);
|
||||
@@ -863,11 +863,11 @@ void mjXWriter::OneActuator(XMLElement* elem, const mjCActuator* actuator, mjCDe
|
||||
WriteAttrInt(elem, "nsample", actuator->nsample, def->Actuator().nsample);
|
||||
WriteAttrKey(elem, "interp", interp_map, interp_sz, actuator->interp, def->Actuator().interp);
|
||||
WriteAttr(elem, "delay", 1, &actuator->delay, &def->Actuator().delay);
|
||||
WriteAttrKey(elem, "ctrllimited", TFAuto_map, 3, actuator->ctrllimited, def->Actuator().ctrllimited);
|
||||
WriteAttrKey(elem, "ctrllimited", FalseTrueAuto_map, 3, actuator->ctrllimited, def->Actuator().ctrllimited);
|
||||
WriteAttr(elem, "ctrlrange", 2, actuator->ctrlrange, def->Actuator().ctrlrange);
|
||||
WriteAttrKey(elem, "forcelimited", TFAuto_map, 3, actuator->forcelimited, def->Actuator().forcelimited);
|
||||
WriteAttrKey(elem, "forcelimited", FalseTrueAuto_map, 3, actuator->forcelimited, def->Actuator().forcelimited);
|
||||
WriteAttr(elem, "forcerange", 2, actuator->forcerange, def->Actuator().forcerange);
|
||||
WriteAttrKey(elem, "actlimited", TFAuto_map, 3, actuator->actlimited, def->Actuator().actlimited);
|
||||
WriteAttrKey(elem, "actlimited", FalseTrueAuto_map, 3, actuator->actlimited, def->Actuator().actlimited);
|
||||
WriteAttr(elem, "actrange", 2, actuator->actrange, def->Actuator().actrange);
|
||||
WriteAttr(elem, "lengthrange", 2, actuator->lengthrange, def->Actuator().lengthrange);
|
||||
WriteAttr(elem, "gear", 6, actuator->gear, def->Actuator().gear);
|
||||
@@ -1086,7 +1086,7 @@ void mjXWriter::Option(XMLElement* root) {
|
||||
model->option.integrator, opt.integrator);
|
||||
WriteAttrKey(section, "cone", cone_map, cone_sz,
|
||||
model->option.cone, opt.cone);
|
||||
WriteAttrKey(section, "jacobian", jac_map, jac_sz,
|
||||
WriteAttrKey(section, "jacobian", jacobian_map, jacobian_sz,
|
||||
model->option.jacobian, opt.jacobian);
|
||||
WriteAttrKey(section, "solver", solver_map, solver_sz,
|
||||
model->option.solver, opt.solver);
|
||||
@@ -1743,7 +1743,7 @@ void mjXWriter::Body(XMLElement* elem, mjCBody* body, mjCFrame* frame, string_vi
|
||||
}
|
||||
|
||||
// simple optimization
|
||||
WriteAttrKey(elem, "simple", FAuto_map, 2, body->simple, 1);
|
||||
WriteAttrKey(elem, "simple", FalseAuto_map, 2, body->simple, 1);
|
||||
|
||||
// userdata
|
||||
WriteVector(elem, "user", body->get_userdata());
|
||||
|
||||
Reference in New Issue
Block a user