Change LoadModelFromString to return a smart pointer, add MakeData, and update tests to have C++ RAII clean up model and data.
PiperOrigin-RevId: 935980153 Change-Id: I41d25bfab4935494dc984168820cb7cad123cadf
This commit is contained in:
committed by
Copybara-Service
parent
34d142ee50
commit
1490336955
+188
-237
@@ -37,16 +37,12 @@ namespace {
|
||||
|
||||
using MjCMeshTest = MujocoTest;
|
||||
|
||||
static const char* const kMeshPath =
|
||||
"user/testdata/mesh.xml";
|
||||
static const char* const kMeshPath = "user/testdata/mesh.xml";
|
||||
static const char* const kDuplicateVerticesPath =
|
||||
"user/testdata/duplicate_vertices.xml";
|
||||
static const char* const kCubePath =
|
||||
"user/testdata/cube.xml";
|
||||
static const char* const kCubeCompletePath =
|
||||
"user/testdata/cube_complete.obj";
|
||||
static const char* const kTorusPath =
|
||||
"user/testdata/torus.xml";
|
||||
static const char* const kCubePath = "user/testdata/cube.xml";
|
||||
static const char* const kCubeCompletePath = "user/testdata/cube_complete.obj";
|
||||
static const char* const kTorusPath = "user/testdata/torus.xml";
|
||||
static const char* const kTorusMaxhullVertPath =
|
||||
"user/testdata/torus_maxhullvert.xml";
|
||||
static const char* const kTorusDefaultMaxhullVertPath =
|
||||
@@ -57,18 +53,14 @@ static const char* const kConvexInertiaPath =
|
||||
"user/testdata/inertia_convex.xml";
|
||||
static const char* const kConcaveInertiaPath =
|
||||
"user/testdata/inertia_concave.xml";
|
||||
static const char* const kShellInertiaPath =
|
||||
"user/testdata/inertia_shell.xml";
|
||||
static const char* const kTorusQuadsPath =
|
||||
"user/testdata/torus_quads.xml";
|
||||
static const char* const kShellInertiaPath = "user/testdata/inertia_shell.xml";
|
||||
static const char* const kTorusQuadsPath = "user/testdata/torus_quads.xml";
|
||||
static const char* const kTexturedTorusPath =
|
||||
"user/testdata/textured_torus.xml";
|
||||
static const char* const kDuplicateOBJPath =
|
||||
"user/testdata/duplicate.xml";
|
||||
static const char* const kDuplicateOBJPath = "user/testdata/duplicate.xml";
|
||||
static const char* const kMalformedFaceOBJPath =
|
||||
"user/testdata/malformed_face.xml";
|
||||
static const char* const kCubeSkinPath =
|
||||
"user/testdata/cube_skin.xml";
|
||||
static const char* const kCubeSkinPath = "user/testdata/cube_skin.xml";
|
||||
|
||||
static const char* const kNoDecoderForMeshErrorMsh =
|
||||
"no decoder found for mesh";
|
||||
@@ -94,13 +86,8 @@ TEST_F(MjCMeshTest, UnknownMeshFormat) {
|
||||
</mujoco>
|
||||
)";
|
||||
|
||||
std::vector<std::string> invalid_names = {
|
||||
"noextension",
|
||||
"anobj",
|
||||
"f",
|
||||
"mesh.exe",
|
||||
"file%s"
|
||||
};
|
||||
std::vector<std::string> invalid_names = {"noextension", "anobj", "f",
|
||||
"mesh.exe", "file%s"};
|
||||
mjVFS vfs;
|
||||
mj_defaultVFS(&vfs);
|
||||
|
||||
@@ -108,9 +95,9 @@ TEST_F(MjCMeshTest, UnknownMeshFormat) {
|
||||
mj_addBufferVFS(&vfs, name.c_str(), nullptr, 0);
|
||||
std::string xml = absl::StrFormat(xml_format, name);
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model =
|
||||
MjModelPtr model =
|
||||
LoadModelFromString(xml.c_str(), error.data(), error.size(), &vfs);
|
||||
ASSERT_THAT(model, testing::IsNull())
|
||||
ASSERT_THAT(model.get(), testing::IsNull())
|
||||
<< "Should fail to load a mesh named: " << name;
|
||||
EXPECT_THAT(error.data(), HasSubstr(kNoDecoderForMeshErrorMsh));
|
||||
EXPECT_THAT(error.data(), HasSubstr(name));
|
||||
@@ -141,8 +128,8 @@ TEST_F(MjCMeshTest, LoadMSHWithVFS) {
|
||||
mj_defaultVFS(vfs.get());
|
||||
|
||||
// should fallback to OS filesystem
|
||||
mjModel* model = LoadModelFromString(xml, error, error_sz, vfs.get());
|
||||
EXPECT_THAT(model, IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error, error_sz, vfs.get());
|
||||
EXPECT_THAT(model.get(), IsNull());
|
||||
EXPECT_THAT(error, HasSubstr("Error opening file"));
|
||||
mj_deleteVFS(vfs.get());
|
||||
}
|
||||
@@ -167,8 +154,8 @@ TEST_F(MjCMeshTest, LoadOBJWithVFS) {
|
||||
mj_defaultVFS(vfs.get());
|
||||
|
||||
// should fallback to OS filesystem
|
||||
mjModel* model = LoadModelFromString(xml, error, error_sz, vfs.get());
|
||||
EXPECT_THAT(model, IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error, error_sz, vfs.get());
|
||||
EXPECT_THAT(model.get(), IsNull());
|
||||
EXPECT_THAT(error, HasSubstr("Error opening file"));
|
||||
mj_deleteVFS(vfs.get());
|
||||
}
|
||||
@@ -193,8 +180,8 @@ TEST_F(MjCMeshTest, LoadSTLWithVFS) {
|
||||
mj_defaultVFS(vfs.get());
|
||||
|
||||
// should fallback to OS filesystem
|
||||
mjModel* model = LoadModelFromString(xml, error, error_sz, vfs.get());
|
||||
EXPECT_THAT(model, IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error, error_sz, vfs.get());
|
||||
EXPECT_THAT(model.get(), IsNull());
|
||||
EXPECT_THAT(error, HasSubstr("Error opening file"));
|
||||
mj_deleteVFS(vfs.get());
|
||||
}
|
||||
@@ -221,8 +208,8 @@ TEST_F(MjCMeshTest, LoadMSHWithContentType) {
|
||||
mj_defaultVFS(vfs.get());
|
||||
|
||||
// should try opening the file (not found obviously)
|
||||
mjModel* model = LoadModelFromString(xml, error, error_sz, vfs.get());
|
||||
EXPECT_THAT(model, IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error, error_sz, vfs.get());
|
||||
EXPECT_THAT(model.get(), IsNull());
|
||||
EXPECT_THAT(error, HasSubstr("Error opening file"));
|
||||
mj_deleteVFS(vfs.get());
|
||||
}
|
||||
@@ -247,8 +234,8 @@ TEST_F(MjCMeshTest, LoadOBJWithContentType) {
|
||||
mj_defaultVFS(vfs.get());
|
||||
|
||||
// should try opening the file (not found obviously)
|
||||
mjModel* model = LoadModelFromString(xml, error, error_sz, vfs.get());
|
||||
EXPECT_THAT(model, IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error, error_sz, vfs.get());
|
||||
EXPECT_THAT(model.get(), IsNull());
|
||||
EXPECT_THAT(error, HasSubstr("Error opening file"));
|
||||
mj_deleteVFS(vfs.get());
|
||||
}
|
||||
@@ -273,8 +260,8 @@ TEST_F(MjCMeshTest, LoadSTLWithContentType) {
|
||||
mj_defaultVFS(vfs.get());
|
||||
|
||||
// should try opening the file (not found obviously)
|
||||
mjModel* model = LoadModelFromString(xml, error, error_sz, vfs.get());
|
||||
EXPECT_THAT(model, IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error, error_sz, vfs.get());
|
||||
EXPECT_THAT(model.get(), IsNull());
|
||||
EXPECT_THAT(error, HasSubstr("Error opening file"));
|
||||
mj_deleteVFS(vfs.get());
|
||||
}
|
||||
@@ -300,8 +287,8 @@ TEST_F(MjCMeshTest, LoadMSHWithContentTypeError) {
|
||||
mj_addBufferVFS(&vfs, "some_file", nullptr, 0);
|
||||
|
||||
// should error with unknown file type
|
||||
mjModel* model = LoadModelFromString(xml, error, error_sz, &vfs);
|
||||
EXPECT_THAT(model, IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error, error_sz, &vfs);
|
||||
EXPECT_THAT(model.get(), IsNull());
|
||||
EXPECT_THAT(error, HasSubstr(kNoDecoderForMeshErrorMsh));
|
||||
mj_deleteVFS(&vfs);
|
||||
}
|
||||
@@ -327,8 +314,8 @@ TEST_F(MjCMeshTest, LoadMSHWithInvalidContentType) {
|
||||
mj_addBufferVFS(&vfs, "some_file", nullptr, 0);
|
||||
|
||||
// should error with unknown file type
|
||||
mjModel* model = LoadModelFromString(xml, error, error_sz, &vfs);
|
||||
EXPECT_THAT(model, IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error, error_sz, &vfs);
|
||||
EXPECT_THAT(model.get(), IsNull());
|
||||
EXPECT_THAT(error, HasSubstr(kNoDecoderForMeshErrorMsh));
|
||||
mj_deleteVFS(&vfs);
|
||||
}
|
||||
@@ -353,8 +340,8 @@ TEST_F(MjCMeshTest, LoadMSHWithContentTypeParam) {
|
||||
mj_defaultVFS(vfs.get());
|
||||
|
||||
// should try opening the file (not found obviously)
|
||||
mjModel* model = LoadModelFromString(xml, error, error_sz, vfs.get());
|
||||
EXPECT_THAT(model, IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error, error_sz, vfs.get());
|
||||
EXPECT_THAT(model.get(), IsNull());
|
||||
EXPECT_THAT(error, HasSubstr("Error opening file"));
|
||||
mj_deleteVFS(vfs.get());
|
||||
}
|
||||
@@ -438,8 +425,7 @@ TEST_F(MjCMeshTest, SaveMeshOnce) {
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
|
||||
// Confirm the mesh file is loaded and stored in paths
|
||||
EXPECT_EQ(
|
||||
std::string(&model->paths[model->mesh_pathadr[0]]), "cube.obj");
|
||||
EXPECT_EQ(std::string(&model->paths[model->mesh_pathadr[0]]), "cube.obj");
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, Not(testing::HasSubstr("vertex")));
|
||||
mj_deleteModel(model);
|
||||
@@ -457,9 +443,8 @@ TEST_F(MjCMeshTest, TinyMeshLoads) {
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model, NotNull()) << error;
|
||||
mj_deleteModel(model);
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), NotNull()) << error;
|
||||
}
|
||||
|
||||
// ------------- test max hull vert -------------------------------------------
|
||||
@@ -498,9 +483,8 @@ TEST_F(MjCMeshTest, FaceNormalAutogenerated) {
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model, NotNull()) << error;
|
||||
mj_deleteModel(model);
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), NotNull()) << error;
|
||||
}
|
||||
|
||||
// ------------- test inertia -------------------------------------------------
|
||||
@@ -521,9 +505,8 @@ TEST_F(MjCMeshTest, SmallInertiaLoads) {
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model, NotNull()) << error;
|
||||
mj_deleteModel(model);
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), NotNull()) << error;
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, TinyInertiaFails) {
|
||||
@@ -586,11 +569,13 @@ TEST_F(MjCMeshTest, FlippedFaceFailsExactInertia) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model, testing::IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr(
|
||||
"Error: faces of mesh 'example_mesh' have inconsistent orientation. "
|
||||
"Please check the faces containing the vertices 1 and 2."));
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model.get(), testing::IsNull());
|
||||
EXPECT_THAT(
|
||||
error.data(),
|
||||
HasSubstr(
|
||||
"Error: faces of mesh 'example_mesh' have inconsistent orientation. "
|
||||
"Please check the faces containing the vertices 1 and 2."));
|
||||
}
|
||||
|
||||
void CheckTetrahedronWasRescaled(mjModel* model) {
|
||||
@@ -598,12 +583,12 @@ void CheckTetrahedronWasRescaled(mjModel* model) {
|
||||
// with vertices (0, 0, 0), (1, 0, 0), (0, 2, 0), (0, 0, 3)
|
||||
// after mesh preprocessing is performed
|
||||
std::vector<mjtNum> vert = {
|
||||
-0.51610732078552246, -0.57402724027633667, -0.5283237099647522,
|
||||
0.42337465286254883, -0.90627568960189819, -0.61189728975296021,
|
||||
0.065528042614459991, 1.2306677103042603, -1.1645441055297852,
|
||||
0.027204651385545731, 0.24963514506816864, 2.3047652244567871};
|
||||
-0.51610732078552246, -0.57402724027633667, -0.5283237099647522,
|
||||
0.42337465286254883, -0.90627568960189819, -0.61189728975296021,
|
||||
0.065528042614459991, 1.2306677103042603, -1.1645441055297852,
|
||||
0.027204651385545731, 0.24963514506816864, 2.3047652244567871};
|
||||
mjtNum tolerance = std::numeric_limits<float>::epsilon();
|
||||
for (int i=0; i < 12; ++i) {
|
||||
for (int i = 0; i < 12; ++i) {
|
||||
EXPECT_NEAR(model->mesh_vert[i], vert[i], tolerance);
|
||||
}
|
||||
}
|
||||
@@ -622,10 +607,9 @@ TEST_F(MjCMeshTest, FlippedFaceAllowedWorld) {
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model, NotNull()) << error;
|
||||
CheckTetrahedronWasRescaled(model);
|
||||
mj_deleteModel(model);
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), NotNull()) << error;
|
||||
CheckTetrahedronWasRescaled(model.get());
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, FlippedFaceAllowedNoMass) {
|
||||
@@ -644,10 +628,9 @@ TEST_F(MjCMeshTest, FlippedFaceAllowedNoMass) {
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model, NotNull()) << error;
|
||||
CheckTetrahedronWasRescaled(model);
|
||||
mj_deleteModel(model);
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), NotNull()) << error;
|
||||
CheckTetrahedronWasRescaled(model.get());
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, FlippedFaceAllowedInertial) {
|
||||
@@ -667,10 +650,9 @@ TEST_F(MjCMeshTest, FlippedFaceAllowedInertial) {
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model, NotNull()) << error;
|
||||
CheckTetrahedronWasRescaled(model);
|
||||
mj_deleteModel(model);
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), NotNull()) << error;
|
||||
CheckTetrahedronWasRescaled(model.get());
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, FlippedFaceAllowedNegligibleArea) {
|
||||
@@ -689,10 +671,9 @@ TEST_F(MjCMeshTest, FlippedFaceAllowedNegligibleArea) {
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model, NotNull()) << error;
|
||||
CheckTetrahedronWasRescaled(model);
|
||||
mj_deleteModel(model);
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), NotNull()) << error;
|
||||
CheckTetrahedronWasRescaled(model.get());
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, AreaTooSmall) {
|
||||
@@ -711,8 +692,8 @@ TEST_F(MjCMeshTest, AreaTooSmall) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model, IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model.get(), IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("mesh surface area is too small"));
|
||||
}
|
||||
|
||||
@@ -732,10 +713,9 @@ TEST_F(MjCMeshTest, VolumeTooSmall) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model, testing::IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model.get(), testing::IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("mesh volume is too small"));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, VisualVolumeTooSmall) {
|
||||
@@ -759,10 +739,9 @@ TEST_F(MjCMeshTest, VisualVolumeTooSmall) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model, testing::IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model.get(), testing::IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("mesh volume is too small"));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, VisualVolumeSmallAllowedShell) {
|
||||
@@ -786,12 +765,11 @@ TEST_F(MjCMeshTest, VisualVolumeSmallAllowedShell) {
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model, NotNull()) << error;
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), NotNull()) << error;
|
||||
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);
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, VolumeSmallAllowedShell) {
|
||||
@@ -810,12 +788,11 @@ TEST_F(MjCMeshTest, VolumeSmallAllowedShell) {
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model, NotNull()) << error;
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), NotNull()) << error;
|
||||
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);
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, Flex2DElasticityRequiresPositiveThickness) {
|
||||
@@ -829,8 +806,8 @@ TEST_F(MjCMeshTest, Flex2DElasticityRequiresPositiveThickness) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model, testing::IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model.get(), testing::IsNull());
|
||||
EXPECT_THAT(error.data(),
|
||||
HasSubstr("2d elasticity requires positive thickness"));
|
||||
}
|
||||
@@ -847,9 +824,8 @@ TEST_F(MjCMeshTest, InterpolatedFlexSupportsBendElasticityWithWarning) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model, testing::NotNull()) << error.data();
|
||||
mj_deleteModel(model);
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model.get(), testing::NotNull()) << error.data();
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, InterpolatedFlexSupportsBothElasticityWithWarning) {
|
||||
@@ -864,9 +840,8 @@ TEST_F(MjCMeshTest, InterpolatedFlexSupportsBothElasticityWithWarning) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model, testing::NotNull()) << error.data();
|
||||
mj_deleteModel(model);
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model.get(), testing::NotNull()) << error.data();
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, Flex2DElasticityRequires2DFlex) {
|
||||
@@ -880,8 +855,8 @@ TEST_F(MjCMeshTest, Flex2DElasticityRequires2DFlex) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model, testing::IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model.get(), testing::IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("2d elasticity requires 2d flex"));
|
||||
}
|
||||
|
||||
@@ -908,23 +883,24 @@ TEST_F(MjCMeshTest, VolumeNegativeThrowsError) {
|
||||
|
||||
for (int nmesh : {3, 16, 17, 50}) {
|
||||
std::string mesh_definitions = "";
|
||||
for (int i = 1; i < nmesh+1; i++) {
|
||||
for (int i = 1; i < nmesh + 1; i++) {
|
||||
mesh_definitions += absl::StrFormat(bad_mesh, i);
|
||||
}
|
||||
|
||||
std::string geom_definitions = "";
|
||||
for (int i = 1; i < nmesh+1; i++) {
|
||||
for (int i = 1; i < nmesh + 1; i++) {
|
||||
geom_definitions += absl::StrFormat(geom, i);
|
||||
}
|
||||
|
||||
std::string xml_str = xml;
|
||||
absl::StrReplaceAll({{"MESH_DEFINITIONS", mesh_definitions},
|
||||
{"GEOM_DEFINITIONS", geom_definitions}}, &xml_str);
|
||||
{"GEOM_DEFINITIONS", geom_definitions}},
|
||||
&xml_str);
|
||||
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml_str.c_str(),
|
||||
error.data(), error.size());
|
||||
EXPECT_THAT(model, IsNull());
|
||||
MjModelPtr model =
|
||||
LoadModelFromString(xml_str.c_str(), error.data(), error.size());
|
||||
EXPECT_THAT(model.get(), IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("mesh volume is negative"));
|
||||
}
|
||||
}
|
||||
@@ -964,22 +940,22 @@ TEST_F(MjCMeshTest, ExactConcaveInertia) {
|
||||
mjtNum m_hole = .9 * .8 * .8 * density;
|
||||
mjtNum m_cube = 1. * density;
|
||||
mjtNum m_concave_cube = m_cube - m_hole;
|
||||
mjtNum I_cube = m_cube/6.;
|
||||
mjtNum I_cube = m_cube / 6.;
|
||||
// due to the asymmetric hole, the com position has changed
|
||||
// so we need to use https://en.wikipedia.org/wiki/Parallel_axis_theorem
|
||||
mjtNum d_cube = .5 - model->body_ipos[5];
|
||||
mjtNum d_hole = .55 - model->body_ipos[5];
|
||||
mjtNum I1 = I_cube - m_hole*(.8*.8 + .8*.8)/12;
|
||||
mjtNum I2 = I_cube - m_hole*(.8*.8 + .9*.9)/12
|
||||
+ m_cube*d_cube*d_cube - m_hole*d_hole*d_hole;
|
||||
mjtNum I1 = I_cube - m_hole * (.8 * .8 + .8 * .8) / 12;
|
||||
mjtNum I2 = I_cube - m_hole * (.8 * .8 + .9 * .9) / 12 +
|
||||
m_cube * d_cube * d_cube - m_hole * d_hole * d_hole;
|
||||
EXPECT_NEAR(model->body_mass[1], m_concave_cube, kMaxAbsErr);
|
||||
EXPECT_NEAR(model->body_mass[2], m_concave_cube, kMaxAbsErr);
|
||||
EXPECT_NEAR(model->body_mass[3], m_concave_cube, kMaxAbsErr);
|
||||
EXPECT_NEAR(model->body_mass[4], m_concave_cube, kMaxAbsErr);
|
||||
for (int i = 3; i < 15; i += 3) {
|
||||
EXPECT_NEAR(model->body_inertia[i], I1, kMaxAbsErr);
|
||||
EXPECT_NEAR(model->body_inertia[i+1], I2, kMaxAbsErr);
|
||||
EXPECT_NEAR(model->body_inertia[i+2], I2, kMaxAbsErr);
|
||||
EXPECT_NEAR(model->body_inertia[i + 1], I2, kMaxAbsErr);
|
||||
EXPECT_NEAR(model->body_inertia[i + 2], I2, kMaxAbsErr);
|
||||
}
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
@@ -990,7 +966,7 @@ TEST_F(MjCMeshTest, ExactConvexInertia) {
|
||||
mjModel* model = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
|
||||
// https://en.wikipedia.org/wiki/List_of_moments_of_inertia
|
||||
mjtNum m_solid_cube = 1.;
|
||||
mjtNum I_solid_cube = 1./6. * m_solid_cube;
|
||||
mjtNum I_solid_cube = 1. / 6. * m_solid_cube;
|
||||
EXPECT_LE(mju_abs(model->body_mass[1] - m_solid_cube), kMaxAbsErr);
|
||||
EXPECT_LE(mju_abs(model->body_mass[2] - m_solid_cube), kMaxAbsErr);
|
||||
for (int i = 3; i < 9; i++) {
|
||||
@@ -1005,7 +981,7 @@ TEST_F(MjCMeshTest, ExactShellInertia) {
|
||||
mjModel* model = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
|
||||
// see https://en.wikipedia.org/wiki/List_of_moments_of_inertia
|
||||
mjtNum m_hollow_cube = 6.;
|
||||
mjtNum I_hollow_cube = 5./18. * m_hollow_cube;
|
||||
mjtNum I_hollow_cube = 5. / 18. * m_hollow_cube;
|
||||
EXPECT_LE(mju_abs(model->body_mass[1] - m_hollow_cube), kMaxAbsErr);
|
||||
EXPECT_LE(mju_abs(model->body_inertia[3] - I_hollow_cube), kMaxAbsErr);
|
||||
EXPECT_LE(mju_abs(model->body_inertia[4] - I_hollow_cube), kMaxAbsErr);
|
||||
@@ -1026,8 +1002,8 @@ TEST_F(MjCMeshTest, MeshPosQuat) {
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model, NotNull()) << error;
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), NotNull()) << error;
|
||||
// loading the mesh results in an offset of the geom's pos and quat due to the
|
||||
// fact that the geom's center is not the volumetric center of the mesh. To
|
||||
// recover the geom's originally specified pose, the offset used is stored in
|
||||
@@ -1035,16 +1011,15 @@ TEST_F(MjCMeshTest, MeshPosQuat) {
|
||||
// and orientation, first invert the specified mesh_pos and mesh_quat
|
||||
mjtNum inverse_mesh_pos[3];
|
||||
mjtNum inverse_mesh_quat[4];
|
||||
mju_negPose(inverse_mesh_pos, inverse_mesh_quat,
|
||||
&model->mesh_pos[0], &model->mesh_quat[0]);
|
||||
mju_negPose(inverse_mesh_pos, inverse_mesh_quat, &model->mesh_pos[0],
|
||||
&model->mesh_quat[0]);
|
||||
|
||||
// apply the inverted mesh_pos and inverted mesh_quat to the geom's pos and
|
||||
// quat. It should match the originally specified values
|
||||
mjtNum recovered_pos[3];
|
||||
mjtNum recovered_quat[4];
|
||||
mju_mulPose(recovered_pos, recovered_quat,
|
||||
&model->geom_pos[0], &model->geom_quat[0],
|
||||
inverse_mesh_pos, inverse_mesh_quat);
|
||||
mju_mulPose(recovered_pos, recovered_quat, &model->geom_pos[0],
|
||||
&model->geom_quat[0], inverse_mesh_pos, inverse_mesh_quat);
|
||||
EXPECT_NEAR(recovered_pos[0], 0, MjTol(1e-12, 1e-6));
|
||||
EXPECT_NEAR(recovered_pos[1], 0, MjTol(1e-12, 1e-6));
|
||||
EXPECT_NEAR(recovered_pos[2], 0, MjTol(1e-12, 1e-6));
|
||||
@@ -1055,11 +1030,10 @@ TEST_F(MjCMeshTest, MeshPosQuat) {
|
||||
EXPECT_NEAR(recovered_quat[3], 0, MjTol(1e-12, 1e-6));
|
||||
|
||||
// same test on the other geom
|
||||
mju_negPose(inverse_mesh_pos, inverse_mesh_quat,
|
||||
&model->mesh_pos[0], &model->mesh_quat[0]);
|
||||
mju_mulPose(recovered_pos, recovered_quat,
|
||||
&model->geom_pos[3], &model->geom_quat[4],
|
||||
inverse_mesh_pos, inverse_mesh_quat);
|
||||
mju_negPose(inverse_mesh_pos, inverse_mesh_quat, &model->mesh_pos[0],
|
||||
&model->mesh_quat[0]);
|
||||
mju_mulPose(recovered_pos, recovered_quat, &model->geom_pos[3],
|
||||
&model->geom_quat[4], inverse_mesh_pos, inverse_mesh_quat);
|
||||
EXPECT_NEAR(recovered_pos[0], 1, MjTol(1e-12, 1e-6));
|
||||
EXPECT_NEAR(recovered_pos[1], 2, MjTol(1e-12, 1e-6));
|
||||
EXPECT_NEAR(recovered_pos[2], 3, MjTol(1e-12, 1e-6));
|
||||
@@ -1068,8 +1042,6 @@ TEST_F(MjCMeshTest, MeshPosQuat) {
|
||||
EXPECT_NEAR(recovered_quat[1], 0.5, MjTol(1e-12, 1e-6));
|
||||
EXPECT_NEAR(recovered_quat[2], 0.5, MjTol(1e-12, 1e-6));
|
||||
EXPECT_NEAR(recovered_quat[3], 0.5, MjTol(1e-12, 1e-6));
|
||||
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, MeshPosQuatShellInertia) {
|
||||
@@ -1085,8 +1057,8 @@ TEST_F(MjCMeshTest, MeshPosQuatShellInertia) {
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model, NotNull()) << error;
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), NotNull()) << error;
|
||||
// loading the mesh results in an offset of the geom's pos and quat due to the
|
||||
// fact that the geom's center is not the volumetric center of the mesh. To
|
||||
// recover the geom's originally specified pose, the offset used is stored in
|
||||
@@ -1094,16 +1066,15 @@ TEST_F(MjCMeshTest, MeshPosQuatShellInertia) {
|
||||
// and orientation, first invert the specified mesh_pos and mesh_quat
|
||||
mjtNum inverse_mesh_pos[3];
|
||||
mjtNum inverse_mesh_quat[4];
|
||||
mju_negPose(inverse_mesh_pos, inverse_mesh_quat,
|
||||
&model->mesh_pos[0], &model->mesh_quat[0]);
|
||||
mju_negPose(inverse_mesh_pos, inverse_mesh_quat, &model->mesh_pos[0],
|
||||
&model->mesh_quat[0]);
|
||||
|
||||
// apply the inverted mesh_pos and inverted mesh_quat to the geom's pos and
|
||||
// quat. It should match the originally specified values
|
||||
mjtNum recovered_pos[3];
|
||||
mjtNum recovered_quat[4];
|
||||
mju_mulPose(recovered_pos, recovered_quat,
|
||||
&model->geom_pos[0], &model->geom_quat[0],
|
||||
inverse_mesh_pos, inverse_mesh_quat);
|
||||
mju_mulPose(recovered_pos, recovered_quat, &model->geom_pos[0],
|
||||
&model->geom_quat[0], inverse_mesh_pos, inverse_mesh_quat);
|
||||
EXPECT_NEAR(recovered_pos[0], 0, MjTol(1e-12, 1e-6));
|
||||
EXPECT_NEAR(recovered_pos[1], 0, MjTol(1e-12, 1e-6));
|
||||
EXPECT_NEAR(recovered_pos[2], 0, MjTol(1e-12, 1e-6));
|
||||
@@ -1114,11 +1085,10 @@ TEST_F(MjCMeshTest, MeshPosQuatShellInertia) {
|
||||
EXPECT_NEAR(recovered_quat[3], 0, MjTol(1e-12, 1e-6));
|
||||
|
||||
// same test on the other geom
|
||||
mju_negPose(inverse_mesh_pos, inverse_mesh_quat,
|
||||
&model->mesh_pos[0], &model->mesh_quat[0]);
|
||||
mju_mulPose(recovered_pos, recovered_quat,
|
||||
&model->geom_pos[3], &model->geom_quat[4],
|
||||
inverse_mesh_pos, inverse_mesh_quat);
|
||||
mju_negPose(inverse_mesh_pos, inverse_mesh_quat, &model->mesh_pos[0],
|
||||
&model->mesh_quat[0]);
|
||||
mju_mulPose(recovered_pos, recovered_quat, &model->geom_pos[3],
|
||||
&model->geom_quat[4], inverse_mesh_pos, inverse_mesh_quat);
|
||||
EXPECT_NEAR(recovered_pos[0], 1, MjTol(1e-12, 1e-6));
|
||||
EXPECT_NEAR(recovered_pos[1], 2, MjTol(1e-12, 1e-6));
|
||||
EXPECT_NEAR(recovered_pos[2], 3, MjTol(1e-12, 1e-6));
|
||||
@@ -1127,7 +1097,6 @@ TEST_F(MjCMeshTest, MeshPosQuatShellInertia) {
|
||||
EXPECT_NEAR(recovered_quat[1], 0.5, MjTol(1e-12, 1e-6));
|
||||
EXPECT_NEAR(recovered_quat[2], 0.5, MjTol(1e-12, 1e-6));
|
||||
EXPECT_NEAR(recovered_quat[3], 0.5, MjTol(1e-12, 1e-6));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, MeshScale) {
|
||||
@@ -1144,12 +1113,11 @@ TEST_F(MjCMeshTest, MeshScale) {
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model, NotNull()) << error;
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), NotNull()) << error;
|
||||
|
||||
EXPECT_THAT(AsVector(model->mesh_scale + 0, 3), ElementsAre(1, 1, 1));
|
||||
EXPECT_THAT(AsVector(model->mesh_scale + 3, 3), ElementsAre(0.9, 1, -1));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, NegativeScaleUserMeshCompiles) {
|
||||
@@ -1166,9 +1134,8 @@ TEST_F(MjCMeshTest, NegativeScaleUserMeshCompiles) {
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model, NotNull()) << error;
|
||||
mj_deleteModel(model);
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), NotNull()) << error;
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, NegativeScaleUserMeshMatchesPositiveScale) {
|
||||
@@ -1199,22 +1166,19 @@ TEST_F(MjCMeshTest, NegativeScaleUserMeshMatchesPositiveScale) {
|
||||
)";
|
||||
|
||||
char error[1024];
|
||||
mjModel* pos_model = LoadModelFromString(pos_xml, error, sizeof(error));
|
||||
ASSERT_THAT(pos_model, NotNull()) << error;
|
||||
MjModelPtr pos_model = LoadModelFromString(pos_xml, error, sizeof(error));
|
||||
ASSERT_THAT(pos_model.get(), NotNull()) << error;
|
||||
|
||||
mjModel* neg_model = LoadModelFromString(neg_xml, error, sizeof(error));
|
||||
ASSERT_THAT(neg_model, NotNull()) << error;
|
||||
MjModelPtr neg_model = LoadModelFromString(neg_xml, error, sizeof(error));
|
||||
ASSERT_THAT(neg_model.get(), NotNull()) << error;
|
||||
|
||||
ASSERT_EQ(pos_model->nmeshface, neg_model->nmeshface);
|
||||
|
||||
for (int i = 0; i < pos_model->nmeshface; i++) {
|
||||
EXPECT_EQ(pos_model->mesh_face[3*i + 0], neg_model->mesh_face[3*i + 0]);
|
||||
EXPECT_EQ(pos_model->mesh_face[3*i + 1], neg_model->mesh_face[3*i + 2]);
|
||||
EXPECT_EQ(pos_model->mesh_face[3*i + 2], neg_model->mesh_face[3*i + 1]);
|
||||
EXPECT_EQ(pos_model->mesh_face[3 * i + 0], neg_model->mesh_face[3 * i + 0]);
|
||||
EXPECT_EQ(pos_model->mesh_face[3 * i + 1], neg_model->mesh_face[3 * i + 2]);
|
||||
EXPECT_EQ(pos_model->mesh_face[3 * i + 2], neg_model->mesh_face[3 * i + 1]);
|
||||
}
|
||||
|
||||
mj_deleteModel(pos_model);
|
||||
mj_deleteModel(neg_model);
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, ShellInertiaTest) {
|
||||
@@ -1231,12 +1195,11 @@ TEST_F(MjCMeshTest, ShellInertiaTest) {
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model, NotNull()) << error;
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), NotNull()) << error;
|
||||
|
||||
EXPECT_THAT(AsVector(model->mesh_scale + 0, 3), ElementsAre(1, 1, 1));
|
||||
EXPECT_THAT(AsVector(model->mesh_scale + 3, 3), ElementsAre(0.9, 1, -1));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
// ----------------------------- texcoord -------------------------------------
|
||||
@@ -1251,9 +1214,8 @@ TEST_F(MjCMeshTest, CreateFaceTexCoord) {
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model, NotNull()) << error;
|
||||
mj_deleteModel(model);
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), NotNull()) << error;
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, UseFaceTexCoord) {
|
||||
@@ -1267,21 +1229,20 @@ TEST_F(MjCMeshTest, UseFaceTexCoord) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model, NotNull()) << error.data();
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2*model->mesh_facetexcoord[ 0]], .0);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2*model->mesh_facetexcoord[ 1]], .2);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2*model->mesh_facetexcoord[ 2]], .1);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2*model->mesh_facetexcoord[ 3]], .0);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2*model->mesh_facetexcoord[ 4]], .1);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2*model->mesh_facetexcoord[ 5]], .3);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2*model->mesh_facetexcoord[ 6]], .2);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2*model->mesh_facetexcoord[ 7]], .0);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2*model->mesh_facetexcoord[ 8]], .3);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2*model->mesh_facetexcoord[ 9]], .1);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2*model->mesh_facetexcoord[10]], .2);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2*model->mesh_facetexcoord[11]], .3);
|
||||
mj_deleteModel(model);
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model.get(), NotNull()) << error.data();
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2 * model->mesh_facetexcoord[0]], .0);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2 * model->mesh_facetexcoord[1]], .2);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2 * model->mesh_facetexcoord[2]], .1);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2 * model->mesh_facetexcoord[3]], .0);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2 * model->mesh_facetexcoord[4]], .1);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2 * model->mesh_facetexcoord[5]], .3);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2 * model->mesh_facetexcoord[6]], .2);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2 * model->mesh_facetexcoord[7]], .0);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2 * model->mesh_facetexcoord[8]], .3);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2 * model->mesh_facetexcoord[9]], .1);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2 * model->mesh_facetexcoord[10]], .2);
|
||||
EXPECT_FLOAT_EQ(model->mesh_texcoord[2 * model->mesh_facetexcoord[11]], .3);
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, MissingTexCoord) {
|
||||
@@ -1294,8 +1255,8 @@ TEST_F(MjCMeshTest, MissingTexCoord) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model, testing::IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model.get(), testing::IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("texcoord must be 2*nv"));
|
||||
}
|
||||
|
||||
@@ -1312,10 +1273,9 @@ TEST_F(MjCMeshTest, NaNConvexHullDisallowed) {
|
||||
MockWarningHandler warning_handler;
|
||||
warning_handler.ExpectWarnings();
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model, testing::IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model.get(), testing::IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("vertex coordinate 0 is not finite"));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, InvalidIndexInFace) {
|
||||
@@ -1333,10 +1293,9 @@ TEST_F(MjCMeshTest, InvalidIndexInFace) {
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model, IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), IsNull());
|
||||
EXPECT_THAT(error, HasSubstr("in face 0, vertex index 6 does not exist"));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, QhullCache) {
|
||||
@@ -1351,7 +1310,7 @@ TEST_F(MjCMeshTest, QhullCache) {
|
||||
</worldbody>
|
||||
</mujoco>)";
|
||||
|
||||
static constexpr char xml2[] = R"(
|
||||
static constexpr char xml2[] = R"(
|
||||
<mujoco>
|
||||
<asset>
|
||||
<mesh name="box" file="cube_complete.obj"/>
|
||||
@@ -1367,17 +1326,16 @@ TEST_F(MjCMeshTest, QhullCache) {
|
||||
mj_addFileVFS(&vfs, "", GetTestDataFilePath(kCubeCompletePath).c_str());
|
||||
|
||||
std::array<char, 1000> error;
|
||||
mjModel* model = LoadModelFromString(xml1, error.data(), error.size(), &vfs);
|
||||
ASSERT_THAT(model, NotNull()) << "Failed to load model: " << error.data();
|
||||
MjModelPtr model =
|
||||
LoadModelFromString(xml1, error.data(), error.size(), &vfs);
|
||||
ASSERT_THAT(model.get(), NotNull())
|
||||
<< "Failed to load model: " << error.data();
|
||||
EXPECT_THAT(model->mesh_graphadr[0], -1);
|
||||
|
||||
mj_deleteModel(model);
|
||||
|
||||
model = LoadModelFromString(xml2, error.data(), error.size(), &vfs);
|
||||
ASSERT_THAT(model, NotNull()) << "Failed to load model: " << error.data();
|
||||
ASSERT_THAT(model.get(), NotNull())
|
||||
<< "Failed to load model: " << error.data();
|
||||
EXPECT_GT(model->mesh_graphadr[0], -1);
|
||||
|
||||
mj_deleteModel(model);
|
||||
mj_deleteVFS(&vfs);
|
||||
}
|
||||
|
||||
@@ -1395,8 +1353,8 @@ TEST_F(MjCMeshTest, ColocatedMeshError) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model, IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model.get(), IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("colocated"));
|
||||
}
|
||||
|
||||
@@ -1414,8 +1372,8 @@ TEST_F(MjCMeshTest, CollinearMeshError) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model, IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model.get(), IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("collinear"));
|
||||
}
|
||||
|
||||
@@ -1433,8 +1391,8 @@ TEST_F(MjCMeshTest, CoplanarMeshError) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model, IsNull());
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(model.get(), IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("coplanar"));
|
||||
}
|
||||
|
||||
@@ -1478,8 +1436,7 @@ bool AreAabbsAdjacent(const mjtNum* aabb1, const mjtNum* aabb2) {
|
||||
const mjtNum half_size1 = aabb1[dim + 3];
|
||||
const mjtNum center2 = aabb2[dim];
|
||||
const mjtNum half_size2 = aabb2[dim + 3];
|
||||
const mjtNum gap =
|
||||
std::abs(center1 - center2) - (half_size1 + half_size2);
|
||||
const mjtNum gap = std::abs(center1 - center2) - (half_size1 + half_size2);
|
||||
if (std::abs(gap) < kEps) {
|
||||
touching_dims++;
|
||||
} else if (gap < -kEps) {
|
||||
@@ -1533,15 +1490,15 @@ TEST_F(MjCMeshTest, OctreeIsBalanced) {
|
||||
const int level2 = model->oct_depth[octree_adr + node2_idx];
|
||||
if (std::abs(level1 - level2) > 1) {
|
||||
if (unbalanced_pairs < 10) {
|
||||
ADD_FAILURE()
|
||||
<< "Nodes " << node1_idx << " (level " << level1 << ") and "
|
||||
<< node2_idx << " (level " << level2 << ") are not balanced."
|
||||
<< "\nAABB1: center=(" << aabb1[0] << ", " << aabb1[1] << ", "
|
||||
<< aabb1[2] << "), half_size=(" << aabb1[3] << ", "
|
||||
<< aabb1[4] << ", " << aabb1[5] << ")"
|
||||
<< "\nAABB2: center=(" << aabb2[0] << ", " << aabb2[1] << ", "
|
||||
<< aabb2[2] << "), half_size=(" << aabb2[3] << ", "
|
||||
<< aabb2[4] << ", " << aabb2[5] << ")";
|
||||
ADD_FAILURE() << "Nodes " << node1_idx << " (level " << level1
|
||||
<< ") and " << node2_idx << " (level " << level2
|
||||
<< ") are not balanced."
|
||||
<< "\nAABB1: center=(" << aabb1[0] << ", " << aabb1[1]
|
||||
<< ", " << aabb1[2] << "), half_size=(" << aabb1[3]
|
||||
<< ", " << aabb1[4] << ", " << aabb1[5] << ")"
|
||||
<< "\nAABB2: center=(" << aabb2[0] << ", " << aabb2[1]
|
||||
<< ", " << aabb2[2] << "), half_size=(" << aabb2[3]
|
||||
<< ", " << aabb2[4] << ", " << aabb2[5] << ")";
|
||||
}
|
||||
unbalanced_pairs++;
|
||||
}
|
||||
@@ -1610,8 +1567,7 @@ TEST_F(MjCMeshTest, OctreeHangingNodeInterpolation) {
|
||||
|
||||
// decide which node is finer and which is coarser
|
||||
const int finer_node_idx = (level1 > level2) ? node1_idx : node2_idx;
|
||||
const int coarser_node_idx =
|
||||
(level1 > level2) ? node2_idx : node1_idx;
|
||||
const int coarser_node_idx = (level1 > level2) ? node2_idx : node1_idx;
|
||||
const mjtNum* coarser_aabb =
|
||||
&model->oct_aabb[(octree_adr + coarser_node_idx) * 6];
|
||||
const mjtNum* finer_aabb =
|
||||
@@ -1729,12 +1685,11 @@ TEST_F(MjCMeshTest, OctreeNotComputedForNonSDF) {
|
||||
}
|
||||
|
||||
mjtNum CubeSDF(mjtNum p[3], mjtNum b[3]) {
|
||||
mjtNum q[3] = {mju_abs(p[0]) - b[0],
|
||||
mju_abs(p[1]) - b[1],
|
||||
mjtNum q[3] = {mju_abs(p[0]) - b[0], mju_abs(p[1]) - b[1],
|
||||
mju_abs(p[2]) - b[2]};
|
||||
return mju_sqrt(std::pow(std::max(q[0], (mjtNum)0), 2) +
|
||||
std::pow(std::max(q[1], (mjtNum)0), 2) +
|
||||
std::pow(std::max(q[2], (mjtNum)0), 2)) +
|
||||
std::pow(std::max(q[1], (mjtNum)0), 2) +
|
||||
std::pow(std::max(q[2], (mjtNum)0), 2)) +
|
||||
std::min(std::max(q[0], std::max(q[1], q[2])), (mjtNum)0);
|
||||
}
|
||||
|
||||
@@ -1751,12 +1706,12 @@ TEST_F(MjCMeshTest, OctreeCube) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* m = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(m, NotNull()) << error.data();
|
||||
MjModelPtr m = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(m.get(), NotNull()) << error.data();
|
||||
EXPECT_EQ(m->noct, 63497);
|
||||
mjData* d = mj_makeData(m);
|
||||
MjDataPtr d = MakeData(m);
|
||||
ASSERT_THAT(d, NotNull());
|
||||
mj_forward(m, d);
|
||||
mj_forward(m.get(), d.get());
|
||||
mjSDF sdf;
|
||||
int instance = 0;
|
||||
mjtGeom geomtype = mjGEOM_SDF;
|
||||
@@ -1768,11 +1723,10 @@ TEST_F(MjCMeshTest, OctreeCube) {
|
||||
mjtNum pnt[3][3] = {{1, 1, 1}, {.5, .5, .5}, {.5, 0, 0}};
|
||||
mjtNum size[3] = {1, 1, 1};
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
EXPECT_NEAR(mjc_distance(m, d, &sdf, pnt[i]), CubeSDF(pnt[i], size), 1e-1)
|
||||
EXPECT_NEAR(mjc_distance(m.get(), d.get(), &sdf, pnt[i]),
|
||||
CubeSDF(pnt[i], size), 1e-1)
|
||||
<< "i = " << i;
|
||||
}
|
||||
mj_deleteModel(m);
|
||||
mj_deleteData(d);
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, HemisphereSizes) {
|
||||
@@ -1786,15 +1740,14 @@ TEST_F(MjCMeshTest, HemisphereSizes) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, NotNull()) << error.data();
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model.get(), NotNull()) << error.data();
|
||||
EXPECT_EQ(model->mesh_vertnum[0], 2 * (0 + 1) * (0 + 2) + 2);
|
||||
EXPECT_EQ(model->mesh_vertnum[1], 2 * (1 + 1) * (1 + 2) + 2);
|
||||
EXPECT_EQ(model->mesh_vertnum[2], 2 * (2 + 1) * (2 + 2) + 2);
|
||||
EXPECT_EQ(model->mesh_facenum[0], 4 * (0 + 1) * (0 + 2));
|
||||
EXPECT_EQ(model->mesh_facenum[1], 4 * (1 + 1) * (1 + 2));
|
||||
EXPECT_EQ(model->mesh_facenum[2], 4 * (2 + 1) * (2 + 2));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, SphereSizes) {
|
||||
@@ -1808,15 +1761,14 @@ TEST_F(MjCMeshTest, SphereSizes) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, NotNull()) << error.data();
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model.get(), NotNull()) << error.data();
|
||||
EXPECT_EQ(model->mesh_vertnum[0], 2 + 10 * std::pow(4, 0));
|
||||
EXPECT_EQ(model->mesh_vertnum[1], 2 + 10 * std::pow(4, 1));
|
||||
EXPECT_EQ(model->mesh_vertnum[2], 2 + 10 * std::pow(4, 2));
|
||||
EXPECT_EQ(model->mesh_facenum[0], 20 * std::pow(4, 0));
|
||||
EXPECT_EQ(model->mesh_facenum[1], 20 * std::pow(4, 1));
|
||||
EXPECT_EQ(model->mesh_facenum[2], 20 * std::pow(4, 2));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(MjCMeshTest, MeshMaterial) {
|
||||
@@ -1835,11 +1787,10 @@ TEST_F(MjCMeshTest, MeshMaterial) {
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, NotNull()) << error.data();
|
||||
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model.get(), NotNull()) << error.data();
|
||||
EXPECT_EQ(model->geom_matid[0], 1);
|
||||
EXPECT_EQ(model->geom_matid[1], 0);
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user