Print warning and default to computing mesh inertia from convex hull if volume is negative (malformed mesh).
PiperOrigin-RevId: 526053615 Change-Id: I3974b3433da513f869b1bdab5e98232e215f6509
This commit is contained in:
committed by
Copybara-Service
parent
a2a1ac9fc2
commit
3db483c248
+36
-19
@@ -877,6 +877,33 @@ void mjCMesh::LoadMSH(int default_provider) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void mjCMesh::ComputeVolume(double CoM[3], mjtMeshType type,
|
||||||
|
const double facecen[3], bool exactmeshinertia) {
|
||||||
|
double nrm[3];
|
||||||
|
double cen[3];
|
||||||
|
GetVolumeRef(type) = 0;
|
||||||
|
mjuu_zerovec(CoM, 3);
|
||||||
|
for (int i=0; i<nface; i++) {
|
||||||
|
// get area, normal and center
|
||||||
|
double a = _triangle(nrm, cen, vert+3*face[3*i], vert+3*face[3*i+1], vert+3*face[3*i+2]);
|
||||||
|
|
||||||
|
// compute and add volume
|
||||||
|
const double vec[3] = {cen[0]-facecen[0], cen[1]-facecen[1], cen[2]-facecen[2]};
|
||||||
|
double vol = type==mjSHELL_MESH ? a : mjuu_dot3(vec, nrm) * a / 3;
|
||||||
|
|
||||||
|
// if legacy computation requested, then always positive
|
||||||
|
if (!exactmeshinertia && type==mjVOLUME_MESH) {
|
||||||
|
vol = fabs(vol);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add pyramid com
|
||||||
|
GetVolumeRef(type) += vol;
|
||||||
|
for (int j=0; j<3; j++) {
|
||||||
|
CoM[j] += vol*(cen[j]*3.0/4.0 + facecen[j]/4.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// apply transformations
|
// apply transformations
|
||||||
void mjCMesh::Process() {
|
void mjCMesh::Process() {
|
||||||
@@ -888,6 +915,7 @@ void mjCMesh::Process() {
|
|||||||
|
|
||||||
double nrm[3];
|
double nrm[3];
|
||||||
double cen[3];
|
double cen[3];
|
||||||
|
bool exactmeshinertia = model->exactmeshinertia;
|
||||||
|
|
||||||
if (type==mjVOLUME_MESH) {
|
if (type==mjVOLUME_MESH) {
|
||||||
// translate
|
// translate
|
||||||
@@ -995,27 +1023,16 @@ void mjCMesh::Process() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// compute CoM and volume from pyramid volumes
|
// compute CoM and volume from pyramid volumes
|
||||||
GetVolumeRef(type) = 0;
|
ComputeVolume(CoM, type, facecen, model->exactmeshinertia);
|
||||||
for (int i=0; i<nface; i++) {
|
|
||||||
// get area, normal and center
|
|
||||||
double a = _triangle(nrm, cen, vert+3*face[3*i], vert+3*face[3*i+1], vert+3*face[3*i+2]);
|
|
||||||
|
|
||||||
// compute and add volume
|
// perform computation again if volume is negative
|
||||||
const double vec[3] = {cen[0]-facecen[0], cen[1]-facecen[1], cen[2]-facecen[2]};
|
if (GetVolumeRef(type) < mjMINVAL && exactmeshinertia) {
|
||||||
double vol = type==mjSHELL_MESH ? a : mjuu_dot3(vec, nrm) * a / 3;
|
mju_warning("Malformed mesh %s, computing mesh inertia from convex hull", name.c_str());
|
||||||
|
exactmeshinertia = false;
|
||||||
// if legacy computation requested, then always positive
|
ComputeVolume(CoM, type, facecen, exactmeshinertia);
|
||||||
if (!model->exactmeshinertia && type==mjVOLUME_MESH) {
|
|
||||||
vol = fabs(vol);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add pyramid com
|
|
||||||
GetVolumeRef(type) += vol;
|
|
||||||
for (int j=0; j<3; j++) {
|
|
||||||
CoM[j] += vol*(cen[j]*3.0/4.0 + facecen[j]/4.0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// require positive volume
|
// require positive volume
|
||||||
if (GetVolumeRef(type) < mjMINVAL) {
|
if (GetVolumeRef(type) < mjMINVAL) {
|
||||||
validvolume = GetVolumeRef(type) < 0 ? -1 : 0;
|
validvolume = GetVolumeRef(type) < 0 ? -1 : 0;
|
||||||
@@ -1051,7 +1068,7 @@ void mjCMesh::Process() {
|
|||||||
double vol = type==mjSHELL_MESH ? a : mjuu_dot3(cen, nrm) * a / 3;
|
double vol = type==mjSHELL_MESH ? a : mjuu_dot3(cen, nrm) * a / 3;
|
||||||
|
|
||||||
// if legacy computation requested, then always positive
|
// if legacy computation requested, then always positive
|
||||||
if (!model->exactmeshinertia && type==mjVOLUME_MESH) {
|
if (!exactmeshinertia && type==mjVOLUME_MESH) {
|
||||||
vol = fabs(vol);
|
vol = fabs(vol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -523,6 +523,10 @@ class mjCMesh: public mjCBase {
|
|||||||
void RemoveRepeated(void); // remove repeated vertices
|
void RemoveRepeated(void); // remove repeated vertices
|
||||||
void CheckMesh(void); // check if the mesh is valid
|
void CheckMesh(void); // check if the mesh is valid
|
||||||
|
|
||||||
|
// compute the volume and center-of-mass of the mesh given the face center
|
||||||
|
void ComputeVolume(double CoM[3], mjtMeshType type, const double facecen[3],
|
||||||
|
bool exactmeshinertia);
|
||||||
|
|
||||||
// mesh properties that indicate a well-formed mesh
|
// mesh properties that indicate a well-formed mesh
|
||||||
std::pair<int, int> invalidorientation; // indices of invalid edge; -1 if none
|
std::pair<int, int> invalidorientation; // indices of invalid edge; -1 if none
|
||||||
bool validarea; // false if the area is too small
|
bool validarea; // false if the area is too small
|
||||||
|
|||||||
@@ -464,7 +464,7 @@ TEST_F(MjCMeshTest, VolumeTooSmall) {
|
|||||||
EXPECT_THAT(error.data(), HasSubstr("mesh volume is too small"));
|
EXPECT_THAT(error.data(), HasSubstr("mesh volume is too small"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MjCMeshTest, VolumeNegative) {
|
TEST_F(MjCMeshTest, VolumeNegativeDefaultsLegacy) {
|
||||||
static constexpr char xml[] = R"(
|
static constexpr char xml[] = R"(
|
||||||
<mujoco>
|
<mujoco>
|
||||||
<compiler exactmeshinertia="true"/>
|
<compiler exactmeshinertia="true"/>
|
||||||
@@ -482,8 +482,12 @@ TEST_F(MjCMeshTest, VolumeNegative) {
|
|||||||
)";
|
)";
|
||||||
std::array<char, 1024> error;
|
std::array<char, 1024> error;
|
||||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||||
EXPECT_THAT(model, testing::IsNull());
|
EXPECT_THAT(model, testing::NotNull());
|
||||||
EXPECT_THAT(error.data(), HasSubstr("mesh volume is negative"));
|
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(), HasSubstr("Malformed"));
|
||||||
|
mj_deleteModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MjCMeshTest, VolumeTooSmallAllowedWorld) {
|
TEST_F(MjCMeshTest, VolumeTooSmallAllowedWorld) {
|
||||||
|
|||||||
Reference in New Issue
Block a user