Move mesh inertia specification from global <compiler> to <asset/mesh>.

Three options:
- Convex: Use convex hull
- Exact: Use exact geometry (requires watertight mesh)
- Legacy: Legacy approximation (deprecated)

Fixes #2152

PiperOrigin-RevId: 688949900
Change-Id: I4537f343db917195c6fd58d517b189d5ee3be1cd
This commit is contained in:
Alessio Quaglino
2024-10-23 07:05:32 -07:00
committed by Copybara-Service
parent 0c97dcf1fd
commit b598d79b3f
19 changed files with 170 additions and 87 deletions
+2 -2
View File
@@ -1,8 +1,8 @@
<mujoco>
<compiler fitaabb="false" exactmeshinertia="true"/>
<compiler fitaabb="false"/>
<asset>
<mesh file="stanford_bunny.obj"/>
<mesh file="stanford_bunny.obj" inertia="exact"/>
</asset>
<worldbody>
+24
View File
@@ -0,0 +1,24 @@
<mujoco>
<asset>
<mesh name="incomplete_convex" inertia="convex"
vertex="0 0 0 1 0 0 0 1 0 0 0 1"
face="2 0 3 0 1 3 1 2 3" />
<mesh name="incomplete"
vertex="0 0 0 1 0 0 0 1 0 0 0 1"
face="2 0 3 0 1 3 1 2 3"/>
<mesh name="complete"
vertex="0 0 0 1 0 0 0 1 0 0 0 1"
face="2 0 3 0 1 3 1 2 3 1 0 2"/>
</asset>
<worldbody>
<body>
<geom type="mesh" mesh="incomplete_convex"/>
</body>
<body>
<geom type="mesh" mesh="incomplete"/>
</body>
<body>
<geom type="mesh" mesh="complete"/>
</body>
</worldbody>
</mujoco>
+6 -4
View File
@@ -1,9 +1,11 @@
<mujoco>
<compiler exactmeshinertia="true"/>
<default>
<mesh scale="10 10 10" inertia="exact"/>
</default>
<asset>
<mesh file="cube_cup.obj" scale="10 10 10"/>
<mesh file="cube_cup_quad.obj" scale="10 10 10"/>
<mesh file="cube_cup_hi.obj" scale="10 10 10"/>
<mesh file="cube_cup.obj"/>
<mesh file="cube_cup_quad.obj"/>
<mesh file="cube_cup_hi.obj"/>
</asset>
<worldbody>
<light directional="true" diffuse=".6 .6 .6" specular="0.2 0.2 0.2" pos="0 0 4" dir="0 0 -1"/>
+1 -1
View File
@@ -1,7 +1,7 @@
<mujoco>
<asset>
<mesh file="cube.obj"/>
<mesh name="inline_cube"
<mesh name="inline_cube" inertia="convex"
vertex="
-.5 -.5 -.5
.5 -.5 -.5
+24 -21
View File
@@ -50,6 +50,8 @@ static const char* const kTorusDefaultMaxhullVertPath =
"user/testdata/torus_maxhullvert_default.xml";
static const char* const kTorusShellPath =
"user/testdata/torus_shell.xml";
static const char* const kCompareInertiaPath =
"user/testdata/inertia_compare.xml";
static const char* const kConvexInertiaPath =
"user/testdata/inertia_convex.xml";
static const char* const kConcaveInertiaPath =
@@ -506,21 +508,35 @@ TEST_F(MjCMeshTest, TinyInertiaFails) {
"mass and inertia of moving bodies must be larger than mjMINVAL"));
}
TEST_F(MjCMeshTest, FlippedFaceAllowedInexactInertia) {
TEST_F(MjCMeshTest, FlippedFaceAllowedLegacyInertia) {
const std::string xml_path = GetTestDataFilePath(kMalformedFaceOBJPath);
std::array<char, 1024> error;
mjModel* model = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
EXPECT_THAT(model, testing::NotNull());
EXPECT_THAT(model, testing::NotNull()) << error.data();
EXPECT_THAT(model->nmeshface, 4);
mj_deleteModel(model);
}
TEST_F(MjCMeshTest, MissingFaceAllowedConvexInertia) {
const std::string xml_path = GetTestDataFilePath(kCompareInertiaPath);
std::array<char, 1024> error;
mjModel* model = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
EXPECT_THAT(model, testing::NotNull()) << error.data();
EXPECT_THAT(model->nmeshface, 10);
EXPECT_THAT(model->body_inertia[3], model->body_inertia[9]);
EXPECT_THAT(model->body_inertia[4], model->body_inertia[10]);
EXPECT_THAT(model->body_inertia[5], model->body_inertia[11]);
EXPECT_NE(model->body_inertia[3], model->body_inertia[6]);
EXPECT_NE(model->body_inertia[4], model->body_inertia[7]);
EXPECT_NE(model->body_inertia[5], model->body_inertia[8]);
mj_deleteModel(model);
}
TEST_F(MjCMeshTest, FlippedFaceFailsExactInertia) {
static constexpr char xml[] = R"(
<mujoco>
<compiler exactmeshinertia="true"/>
<asset>
<mesh name="example_mesh"
<mesh name="example_mesh" inertia="exact"
vertex="0 0 0 1 0 0 0 1 0 0 0 1"
face="2 0 3 0 1 3 1 2 3 0 1 2" />
</asset>
@@ -742,10 +758,9 @@ TEST_F(MjCMeshTest, VolumeSmallAllowedShell) {
mj_deleteModel(model);
}
TEST_F(MjCMeshTest, VolumeNegativeDefaultsLegacy) {
TEST_F(MjCMeshTest, VolumeNegativeThrowsError) {
static constexpr char xml[] = R"(
<mujoco>
<compiler exactmeshinertia="true"/>
<asset>
MESH_DEFINITIONS
</asset>
@@ -758,7 +773,7 @@ TEST_F(MjCMeshTest, VolumeNegativeDefaultsLegacy) {
)";
static constexpr char bad_mesh[] = R"(
<mesh name="bad_mesh%d"
<mesh name="bad_mesh%d" inertia="exact"
vertex="0 0 0 1 0 0 0 1 0 0 0 1"
face="3 0 2 0 3 1 1 3 2 0 1 2"/>\n"
)";
@@ -782,20 +797,8 @@ TEST_F(MjCMeshTest, VolumeNegativeDefaultsLegacy) {
std::array<char, 1024> error;
mjModel* model = LoadModelFromString(xml_str.c_str(),
error.data(), error.size());
EXPECT_THAT(model, NotNull()) << error.data();
EXPECT_LE(mju_abs(model->geom_size[0]), 1);
EXPECT_LE(mju_abs(model->geom_size[1]), 1);
EXPECT_LE(mju_abs(model->geom_size[2]), 1);
EXPECT_THAT(error.data(), StartsWith("Malformed mesh 'bad_mesh1'"));
// first 7 warnings fit in the length-500 warning buffer
for (int i = 2; i < mjMIN(nmesh+1, 8); ++i) {
std::string msg = "Malformed mesh 'bad_mesh" + std::to_string(i);
EXPECT_THAT(error.data(), HasSubstr(msg));
}
mj_deleteModel(model);
EXPECT_THAT(model, IsNull());
EXPECT_THAT(error.data(), HasSubstr("mesh volume is negative"));
}
}