Files
Mujoco_WASM/src/xml/xml_native_writer.h
T
Yuval Tassa 5e99ca6cb8 Table-driven attribute writing: rebase the writer on the shared rows.
The writer consumes the same generated rows as the reader.
mjXWriter::WriteAttrTable drives the mechanical attributes of an
element from its mjXAttr rows: each bound field is compared against
the class default at the same offset -- the default object is the same
struct type, so the rows carry no comparison values -- and attributes
equal to their default are skipped. A null default object means the
element has no defaults, and every defined value is written.
Ranged-arity rows write with trailing-default trimming, which the
reader makes round-trip exact by refilling from the same default.
Call sites upcast to the private mjs base (the friend declarations
permit it; mjCMesh gains the friendship its siblings had); the
comparison object is def->X().spec, a freshly-defaulted struct for
the sections, or zero-initialized for size, whose spec defaults (-1,
auto) are resolved by compilation.

Converted: pair, geom, site, joint, camera, light, material, the
equality family, both tendon types (the fixed rows are the spatial
rows without appearance attributes -- exactly the tag difference), the
actuator, flex with its three sub-elements, mesh, skin, option, the
six visual sub-sections, statistic and size. The remnants keep names,
files, resolved reference strings (the mjC classes null their private
base's string pointers; resolved names live behind accessors), and the
writing=custom policies the schema declares: compile directives never
saved (fromto, springdamper, fitscale), type-dependent lengths and
attributes (sizes, joint pos/axis/limited, shellinertia), and
alternatives (mass/density, fovy-versus-intrinsics, the plugin-gated
gain/bias family). Compiler keeps its write-if-nonzero policy;
keyframe keeps its model-sized vectors.

Saved files are canonical: attributes follow schema declaration order
with remnants trailing, and sections follow the schema's dependency
order (statistic before visual, deformable before the contact and
equality sections that name flexes, tendon before the equality
constraints that name tendons, custom demoted to the data tail).
Uniform behavior fixes fall out: default-equal positionals are
dropped, dynprm is trimmed like every other ranged vector, and mesh
material -- read into the spec but never written -- now survives
save/load round trips. Changelog entries ride along.

Verified: full suite, doc_test, and the two-tier A/B harness --
saved XML reorders attributes, and every corpus model reloads to a
byte-identical binary.
PiperOrigin-RevId: 958255003
Change-Id: I5fe7346014450db88b2f3f8680a8f616f7d31266
2026-08-03 02:27:22 -07:00

101 lines
5.2 KiB
C++

// 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.
#ifndef MUJOCO_SRC_XML_XML_NATIVE_WRITER_H_
#define MUJOCO_SRC_XML_XML_NATIVE_WRITER_H_
#include <cstdlib>
#include <string>
#include <string_view>
#include <mujoco/mjmodel.h>
#include <mujoco/mjspec.h>
#include "user/user_objects.h"
#include "xml/xml_base.h"
#include "tinyxml2.h"
class mjXWriter : public mjXBase {
public:
mjXWriter(); // constructor
virtual ~mjXWriter() = default; // destructor
void SetModel(mjSpec* _spec, const mjModel* m = nullptr);
// write XML document to string
std::string Write(char *error, std::size_t error_sz);
private:
// insert end child with given name, return child
tinyxml2::XMLElement* InsertEnd(tinyxml2::XMLElement* parent, const char* name);
// compiled model
mjCModel* model = 0;
// XML section writers
void Compiler(tinyxml2::XMLElement* root); // compiler section
void Option(tinyxml2::XMLElement* root); // option section
void Size(tinyxml2::XMLElement* root); // size section
void Visual(tinyxml2::XMLElement* root); // visual section
void Statistic(tinyxml2::XMLElement* root); // statistic section
void Default(tinyxml2::XMLElement* root, mjCDef* def); // default section
void Extension(tinyxml2::XMLElement* root); // extension section
void Custom(tinyxml2::XMLElement* root); // custom section
void Asset(tinyxml2::XMLElement* root); // asset section
void Contact(tinyxml2::XMLElement* root); // contact section
void Deformable(tinyxml2::XMLElement* root); // deformable section
void Equality(tinyxml2::XMLElement* root); // equality section
void Tendon(tinyxml2::XMLElement* root); // tendon section
void Actuator(tinyxml2::XMLElement* root); // actuator section
void Sensor(tinyxml2::XMLElement* root); // sensor section
void Keyframe(tinyxml2::XMLElement* root); // keyframe section
// body/world section
void Body(tinyxml2::XMLElement* elem, mjCBody* body, mjCFrame* frame, std::string_view childclass = "");
// table-driven attribute writing: the mechanical attributes of an element,
// driven by the same generated mjXAttr rows the reader uses. obj and def
// are the element's bound spec struct and its class-default counterpart;
// each bound field is compared against the default at the same offset, so
// attributes equal to their default are skipped. Strings, files and
// custom-read attributes remain in the OneX() remnants.
template <typename T>
void WriteAttrTable(tinyxml2::XMLElement* elem, const T* obj, const T* def,
const struct mjXAttr* rows, int nrow);
// single element writers, used in defaults and main body
void OneFlex(tinyxml2::XMLElement* elem, const mjCFlex* pflex);
void OneMesh(tinyxml2::XMLElement* elem, const mjCMesh* pmesh, mjCDef* def);
void OneSkin(tinyxml2::XMLElement* elem, const mjCSkin* pskin);
void OneMaterial(tinyxml2::XMLElement* elem, const mjCMaterial* pmaterial, mjCDef* def);
void OneJoint(tinyxml2::XMLElement* elem, const mjCJoint* pjoint, mjCDef* def,
std::string_view classname = "");
void OneGeom(tinyxml2::XMLElement* elem, const mjCGeom* pgeom, mjCDef* def,
std::string_view classname = "");
void OneSite(tinyxml2::XMLElement* elem, const mjCSite* psite, mjCDef* def,
std::string_view classname = "");
void OneCamera(tinyxml2::XMLElement* elem, const mjCCamera* pcamera,
mjCDef* def, std::string_view classname = "");
void OneLight(tinyxml2::XMLElement* elem, const mjCLight* plight, mjCDef* def,
std::string_view classname = "");
void OnePair(tinyxml2::XMLElement* elem, const mjCPair* ppair, mjCDef* def);
void OneEquality(tinyxml2::XMLElement* elem, const mjCEquality* pequality, mjCDef* def);
void OneTendon(tinyxml2::XMLElement* elem, const mjCTendon* ptendon, mjCDef* def);
void OneActuator(tinyxml2::XMLElement* elem, const mjCActuator* pactuator, mjCDef* def);
void OnePlugin(tinyxml2::XMLElement* elem, const mjsPlugin* plugin);
tinyxml2::XMLElement* OneFrame(tinyxml2::XMLElement* elem, mjCFrame* frame);
bool writingdefaults; // true during defaults write
};
#endif // MUJOCO_SRC_XML_XML_NATIVE_WRITER_H_