diff --git a/src/xml/xml_native_writer.cc b/src/xml/xml_native_writer.cc index 3dbe6530..31c3a1e4 100644 --- a/src/xml/xml_native_writer.cc +++ b/src/xml/xml_native_writer.cc @@ -768,6 +768,12 @@ void mjXWriter::Compiler(XMLElement* root) { if (model->exactmeshinertia) { WriteAttrTxt(section, "exactmeshinertia", "true"); } + if (model->boundmass) { + WriteAttr(section, "boundmass", 1, &model->boundmass); + } + if (model->boundinertia) { + WriteAttr(section, "boundinertia", 1, &model->boundinertia); + } // always enable autolimits. limited attributes will be written appropriately // TODO(b/245077553): Remove this when the default is true. WriteAttrTxt(section, "autolimits", "true"); diff --git a/test/xml/xml_native_writer_test.cc b/test/xml/xml_native_writer_test.cc index 6b12e83e..7d5b9ad2 100644 --- a/test/xml/xml_native_writer_test.cc +++ b/test/xml/xml_native_writer_test.cc @@ -208,6 +208,34 @@ TEST_F(XMLWriterTest, NotAddsInertial) { mj_deleteModel(model); } +TEST_F(XMLWriterTest, KeepsBoundMassInertia) { + static constexpr char xml[] = R"( + + + + )"; + mjModel* model = LoadModelFromString(xml); + ASSERT_THAT(model, NotNull()); + std::string saved_xml = SaveAndReadXml(model); + EXPECT_THAT(saved_xml, HasSubstr("boundmass=\"0.1\"")); + EXPECT_THAT(saved_xml, HasSubstr("boundinertia=\"0.2\"")); + mj_deleteModel(model); +} + +TEST_F(XMLWriterTest, DropsZeroBoundMassInertia) { + static constexpr char xml[] = R"( + + + + )"; + mjModel* model = LoadModelFromString(xml); + ASSERT_THAT(model, NotNull()); + std::string saved_xml = SaveAndReadXml(model); + EXPECT_THAT(saved_xml, Not(HasSubstr("boundmass"))); + EXPECT_THAT(saved_xml, Not(HasSubstr("boundinertia"))); + mj_deleteModel(model); +} + TEST_F(XMLWriterTest, DropsInertialIfFromGeom) { static constexpr char xml[] = R"(