diff --git a/src/xml/xml_native_writer.cc b/src/xml/xml_native_writer.cc
index c3ae1016..436e4fbb 100644
--- a/src/xml/xml_native_writer.cc
+++ b/src/xml/xml_native_writer.cc
@@ -289,6 +289,11 @@ void mjXWriter::OneGeom(XMLElement* elem, mjCGeom* pgeom, mjCDef* def) {
WriteAttr(elem, "margin", 1, &pgeom->margin, &def->geom.margin);
WriteAttr(elem, "gap", 1, &pgeom->gap, &def->geom.gap);
WriteAttr(elem, "gap", 1, &pgeom->gap, &def->geom.gap);
+ if (mjuu_defined(pgeom->_mass)) {
+ WriteAttr(elem, "mass", 1, &pgeom->mass, &def->geom.mass);
+ } else {
+ WriteAttr(elem, "density", 1, &pgeom->density, &def->geom.density);
+ }
if (pgeom->material != def->geom.material) {
WriteAttrTxt(elem, "material", pgeom->material);
}
diff --git a/test/xml/xml_native_writer_test.cc b/test/xml/xml_native_writer_test.cc
index d7be6e65..7fe2a73a 100644
--- a/test/xml/xml_native_writer_test.cc
+++ b/test/xml/xml_native_writer_test.cc
@@ -143,6 +143,94 @@ TEST_F(XMLWriterTest, KeepsActlimited) {
mj_deleteModel(model);
}
+TEST_F(XMLWriterTest, UndefinedMassDensity) {
+ static constexpr char xml[] = R"(
+
+
+
+
+
+
+
+ )";
+ mjModel* model = LoadModelFromString(xml);
+ std::string saved_xml = SaveAndReadXml(model);
+ EXPECT_THAT(saved_xml, Not(HasSubstr("density")));
+ EXPECT_THAT(saved_xml, Not(HasSubstr("mass")));
+ mj_deleteModel(model);
+}
+
+TEST_F(XMLWriterTest, WritesDefaults) {
+ static constexpr char xml[] = R"(
+
+
+
+
+
+
+
+
+
+
+ )";
+ mjModel* model = LoadModelFromString(xml);
+ std::string saved_xml = SaveAndReadXml(model);
+ EXPECT_THAT(saved_xml, Not(HasSubstr("mass")));
+ EXPECT_THAT(saved_xml, HasSubstr(""));
+ mj_deleteModel(model);
+}
+
+TEST_F(XMLWriterTest, WritesDensity) {
+ static constexpr char xml[] = R"(
+
+
+
+
+
+
+
+ )";
+ mjModel* model = LoadModelFromString(xml);
+ std::string saved_xml = SaveAndReadXml(model);
+ EXPECT_THAT(saved_xml, HasSubstr("density=\"100\""));
+ EXPECT_THAT(saved_xml, Not(HasSubstr("mass")));
+ mj_deleteModel(model);
+}
+
+TEST_F(XMLWriterTest, WritesMass) {
+ static constexpr char xml[] = R"(
+
+
+
+
+
+
+
+ )";
+ mjModel* model = LoadModelFromString(xml);
+ std::string saved_xml = SaveAndReadXml(model);
+ EXPECT_THAT(saved_xml, Not(HasSubstr("density")));
+ EXPECT_THAT(saved_xml, HasSubstr("mass=\"0.1\""));
+ mj_deleteModel(model);
+}
+
+TEST_F(XMLWriterTest, OverwritesDensity) {
+ static constexpr char xml[] = R"(
+
+
+
+
+
+
+
+ )";
+ mjModel* model = LoadModelFromString(xml);
+ std::string saved_xml = SaveAndReadXml(model);
+ EXPECT_THAT(saved_xml, Not(HasSubstr("density")));
+ EXPECT_THAT(saved_xml, HasSubstr("mass=\"100\""));
+ mj_deleteModel(model);
+}
+
TEST_F(XMLWriterTest, UsesTwoSpaces) {
static constexpr char xml[] = R"(