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:
committed by
Copybara-Service
parent
0c97dcf1fd
commit
b598d79b3f
+20
-6
@@ -815,12 +815,6 @@ has any effect. The settings here are global and apply to the entire model.
|
||||
to the mass. This results in equivalent inertia boxes which extend far beyond the geometric boundaries of the model.
|
||||
Note that the built-in OpenGL visualizer can render equivalent inertia boxes.
|
||||
|
||||
.. _compiler-exactmeshinertia:
|
||||
|
||||
:at:`exactmeshinertia`: :at-val:`[false, true], "false"`
|
||||
If this attribute is set to false, computes mesh inertia with the legacy algorithm, which is exact only for convex
|
||||
meshes. If set to true, it is exact for any closed mesh geometry.
|
||||
|
||||
.. _compiler-alignfree:
|
||||
|
||||
:at:`alignfree`: :at-val:`[false, true], "false"`
|
||||
@@ -1249,6 +1243,24 @@ The full list of processing steps applied by the compiler to each mesh is as fol
|
||||
This attribute specifies the scaling that will be applied to the vertex data along each coordinate axis. Negative
|
||||
values are allowed, resulting in flipping the mesh along the corresponding axis.
|
||||
|
||||
.. _asset-mesh-inertia:
|
||||
|
||||
:at:`inertia`: :at-val:`[convex, exact, legacy], "legacy"`
|
||||
This attribute controls how the mesh is used when mass and inertia are
|
||||
:ref:`inferred from geometry<_compiler-inertiafromgeom>`. The current default value :at-val:`legacy` will be changed
|
||||
to :at-val:`convex` in a future release.
|
||||
|
||||
:at-val:`convex`
|
||||
Use the mesh's convex hull to compute volume and inertia.
|
||||
|
||||
:at-val:`exact`
|
||||
Use an exact algorithm to compute volume and inertia. This algorithm requires a well-oriented, watertight mesh and
|
||||
will error otherwise.
|
||||
|
||||
:at-val:`legacy`
|
||||
Use the legacy algorithm, which is similar to :at-val:`convex`, but leads to volume overcounting for non-convex
|
||||
meshes.
|
||||
|
||||
.. _asset-mesh-smoothnormal:
|
||||
|
||||
:at:`smoothnormal`: :at-val:`[false, true], "false"`
|
||||
@@ -8016,6 +8028,8 @@ if omitted.
|
||||
|
||||
.. _default-mesh-maxhullvert:
|
||||
|
||||
.. _default-mesh-inertia:
|
||||
|
||||
:el-prefix:`default/` |-| **mesh** (?)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
+3
-5
@@ -54,9 +54,7 @@
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`discardvisual<compiler-discardvisual>` | :ref:`convexhull<compiler-convexhull>` | :ref:`usethread<compiler-usethread>` | :ref:`fusestatic<compiler-fusestatic>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`inertiafromgeom<compiler-inertiafromgeom>` | :ref:`inertiagrouprange<compiler-inertiagrouprange>` | :ref:`exactmeshinertia<compiler-exactmeshinertia>` | :ref:`assetdir<compiler-assetdir>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`alignfree<compiler-alignfree>` | | | | |
|
||||
| | | | :ref:`inertiafromgeom<compiler-inertiafromgeom>` | :ref:`inertiagrouprange<compiler-inertiagrouprange>` | :ref:`assetdir<compiler-assetdir>` | :ref:`alignfree<compiler-alignfree>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_| compiler |br| |_| |L| | | .. table:: |
|
||||
@@ -105,7 +103,7 @@
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`refpos<asset-mesh-refpos>` | :ref:`refquat<asset-mesh-refquat>` | :ref:`scale<asset-mesh-scale>` | :ref:`smoothnormal<asset-mesh-smoothnormal>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`maxhullvert<asset-mesh-maxhullvert>` | | | | |
|
||||
| | | | :ref:`maxhullvert<asset-mesh-maxhullvert>` | :ref:`inertia<asset-mesh-inertia>` | | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| mesh |br| |_2| |L| | | .. table:: |
|
||||
@@ -1422,7 +1420,7 @@
|
||||
| :ref:`mesh | ? | :class: mjcf-attributes |
|
||||
| <default-mesh>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`scale<default-mesh-scale>` | :ref:`maxhullvert<default-mesh-maxhullvert>` | | | |
|
||||
| | | | :ref:`scale<default-mesh-scale>` | :ref:`maxhullvert<default-mesh-maxhullvert>` | :ref:`inertia<default-mesh-inertia>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_| default |br| |_| |L| | | .. table:: |
|
||||
|
||||
+6
-1
@@ -5,6 +5,11 @@ Changelog
|
||||
Upcoming version (not yet released)
|
||||
-----------------------------------
|
||||
|
||||
General
|
||||
^^^^^^^
|
||||
- The global compiler flag ``exactmeshinertia`` has been removed and replaced with the mesh-specific
|
||||
:ref:`inertia<asset-mesh-inertia>` attribute.
|
||||
|
||||
MJX
|
||||
^^^
|
||||
- Added ``apply_ft``, ``jac``, and ``xfrc_accumulate`` as public functions.
|
||||
@@ -1076,7 +1081,7 @@ Bug fixes
|
||||
12. Fixed bug in the handling of ellipsoid-based fluid model forces in the new implicitfast integrator.
|
||||
#. Removed spurious whole-arena copying in `mj_copyData`, which can considerably
|
||||
`slow down <https://github.com/google-deepmind/mujoco/issues/568>`__ the copying operation.
|
||||
#. Make :ref:`shellinertia<body-geom-shellinertia>` ignore :ref:`exactmeshinertia<compiler-exactmeshinertia>`, which is
|
||||
#. Make :ref:`shellinertia<body-geom-shellinertia>` ignore ``exactmeshinertia``, which is
|
||||
only used for legacy volume computations (`#759 <https://github.com/google-deepmind/mujoco/issues/759>`__).
|
||||
|
||||
|
||||
|
||||
@@ -1638,9 +1638,14 @@ struct mjrContext_ { // custom OpenGL context
|
||||
};
|
||||
typedef struct mjrContext_ mjrContext;
|
||||
typedef enum mjtGeomInertia_ { // type of inertia inference
|
||||
mjINERTIA_VOLUME, // mass distributed in the volume
|
||||
mjINERTIA_VOLUME = 0, // mass distributed in the volume
|
||||
mjINERTIA_SHELL, // mass distributed on the surface
|
||||
} mjtGeomInertia;
|
||||
typedef enum mjtMeshInertia_ { // type of mesh inertia
|
||||
mjINERTIA_CONVEX = 0, // convex mesh inertia
|
||||
mjINERTIA_EXACT, // exact mesh inertia
|
||||
mjINERTIA_LEGACY, // legacy mesh inertia
|
||||
} mjtMeshInertia;
|
||||
typedef enum mjtBuiltin_ { // type of built-in procedural texture
|
||||
mjBUILTIN_NONE = 0, // no built-in texture
|
||||
mjBUILTIN_GRADIENT, // gradient: rgb1->rgb2
|
||||
@@ -1700,7 +1705,6 @@ typedef struct mjSpec_ { // model specification
|
||||
mjtByte fusestatic; // fuse static bodies with parent
|
||||
int inertiafromgeom; // use geom inertias (mjtInertiaFromGeom)
|
||||
int inertiagrouprange[2]; // range of geom groups used to compute inertia
|
||||
mjtByte exactmeshinertia; // if false, use old formula
|
||||
int alignfree; // align free joints with inertial frame
|
||||
mjLROpt LRopt; // options for lengthrange computation
|
||||
|
||||
@@ -1989,6 +1993,7 @@ typedef struct mjsMesh_ { // mesh specification
|
||||
double refpos[3]; // reference position
|
||||
double refquat[4]; // reference orientation
|
||||
double scale[3]; // rescale mesh
|
||||
mjtMeshInertia inertia; // inertia type (convex, legacy, exact)
|
||||
mjtByte smoothnormal; // do not exclude large-angle faces from normals
|
||||
int maxhullvert; // maximum vertex count for the convex hull
|
||||
mjFloatVec* uservert; // user vertex data
|
||||
|
||||
@@ -57,11 +57,18 @@ extern "C" {
|
||||
//-------------------------------- enum types (mjt) ------------------------------------------------
|
||||
|
||||
typedef enum mjtGeomInertia_ { // type of inertia inference
|
||||
mjINERTIA_VOLUME, // mass distributed in the volume
|
||||
mjINERTIA_VOLUME = 0, // mass distributed in the volume
|
||||
mjINERTIA_SHELL, // mass distributed on the surface
|
||||
} mjtGeomInertia;
|
||||
|
||||
|
||||
typedef enum mjtMeshInertia_ { // type of mesh inertia
|
||||
mjINERTIA_CONVEX = 0, // convex mesh inertia
|
||||
mjINERTIA_EXACT, // exact mesh inertia
|
||||
mjINERTIA_LEGACY, // legacy mesh inertia
|
||||
} mjtMeshInertia;
|
||||
|
||||
|
||||
typedef enum mjtBuiltin_ { // type of built-in procedural texture
|
||||
mjBUILTIN_NONE = 0, // no built-in texture
|
||||
mjBUILTIN_GRADIENT, // gradient: rgb1->rgb2
|
||||
@@ -136,7 +143,6 @@ typedef struct mjSpec_ { // model specification
|
||||
mjtByte fusestatic; // fuse static bodies with parent
|
||||
int inertiafromgeom; // use geom inertias (mjtInertiaFromGeom)
|
||||
int inertiagrouprange[2]; // range of geom groups used to compute inertia
|
||||
mjtByte exactmeshinertia; // if false, use old formula
|
||||
int alignfree; // align free joints with inertial frame
|
||||
mjLROpt LRopt; // options for lengthrange computation
|
||||
|
||||
@@ -447,6 +453,7 @@ typedef struct mjsMesh_ { // mesh specification
|
||||
double refpos[3]; // reference position
|
||||
double refquat[4]; // reference orientation
|
||||
double scale[3]; // rescale mesh
|
||||
mjtMeshInertia inertia; // inertia type (convex, legacy, exact)
|
||||
mjtByte smoothnormal; // do not exclude large-angle faces from normals
|
||||
int maxhullvert; // maximum vertex count for the convex hull
|
||||
mjFloatVec* uservert; // user vertex data
|
||||
|
||||
@@ -727,6 +727,16 @@ ENUMS: Mapping[str, EnumDecl] = dict([
|
||||
('mjINERTIA_SHELL', 1),
|
||||
]),
|
||||
)),
|
||||
('mjtMeshInertia',
|
||||
EnumDecl(
|
||||
name='mjtMeshInertia',
|
||||
declname='enum mjtMeshInertia_',
|
||||
values=dict([
|
||||
('mjINERTIA_CONVEX', 0),
|
||||
('mjINERTIA_EXACT', 1),
|
||||
('mjINERTIA_LEGACY', 2),
|
||||
]),
|
||||
)),
|
||||
('mjtBuiltin',
|
||||
EnumDecl(
|
||||
name='mjtBuiltin',
|
||||
|
||||
@@ -8914,11 +8914,6 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
),
|
||||
doc='range of geom groups used to compute inertia',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='exactmeshinertia',
|
||||
type=ValueType(name='mjtByte'),
|
||||
doc='if false, use old formula',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='alignfree',
|
||||
type=ValueType(name='int'),
|
||||
@@ -10291,6 +10286,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
),
|
||||
doc='rescale mesh',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='inertia',
|
||||
type=ValueType(name='mjtMeshInertia'),
|
||||
doc='inertia type (convex, legacy, exact)',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='smoothnormal',
|
||||
type=ValueType(name='mjtByte'),
|
||||
|
||||
@@ -246,6 +246,7 @@ void mjs_defaultMesh(mjsMesh* mesh) {
|
||||
mesh->refquat[0] = 1;
|
||||
mesh->scale[0] = mesh->scale[1] = mesh->scale[2] = 1;
|
||||
mesh->maxhullvert = -1;
|
||||
mesh->inertia = mjINERTIA_LEGACY;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+22
-22
@@ -1250,22 +1250,24 @@ void mjCMesh::LoadMSH(mjResource* resource) {
|
||||
|
||||
|
||||
void mjCMesh::ComputeVolume(double CoM[3], mjtGeomInertia type,
|
||||
const double facecen[3], bool exactmeshinertia) {
|
||||
const double facecen[3]) {
|
||||
double nrm[3];
|
||||
double cen[3];
|
||||
GetVolumeRef(type) = 0;
|
||||
mjuu_zerovec(CoM, 3);
|
||||
for (int i=0; i < nface(); i++) {
|
||||
int nf = (inertia == mjINERTIA_CONVEX) ? graph_[1] : nface();
|
||||
int* f = (inertia == mjINERTIA_CONVEX) ? graph_ + 2 + 3*(graph_[0]+graph_[1]) : face_.data();
|
||||
float* vv = vert_.data();
|
||||
for (int i=0; i < nf; i++) {
|
||||
// get area, normal and center
|
||||
float* vv = vert_.data();
|
||||
double a = _triangle(nrm, cen, vv+3*face_[3*i], vv+3*face_[3*i+1], vv+3*face_[3*i+2]);
|
||||
double a = _triangle(nrm, cen, vv+3*f[3*i], vv+3*f[3*i+1], vv+3*f[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==mjINERTIA_SHELL ? a : mjuu_dot3(vec, nrm) * a / 3;
|
||||
|
||||
// if legacy computation requested, then always positive
|
||||
if (!exactmeshinertia && type==mjINERTIA_VOLUME) {
|
||||
if (inertia == mjINERTIA_LEGACY) {
|
||||
vol = abs(vol);
|
||||
}
|
||||
|
||||
@@ -1410,21 +1412,17 @@ void mjCMesh::Process() {
|
||||
for ( const auto type : { mjtGeomInertia::mjINERTIA_VOLUME, mjtGeomInertia::mjINERTIA_SHELL } ) {
|
||||
double CoM[3] = {0, 0, 0};
|
||||
double inert[6] = {0, 0, 0, 0, 0, 0};
|
||||
bool exactmeshinertia = model->exactmeshinertia;
|
||||
|
||||
// compute CoM and volume from pyramid volumes
|
||||
ComputeVolume(CoM, type, facecen, model->exactmeshinertia);
|
||||
ComputeVolume(CoM, type, facecen);
|
||||
|
||||
// perform computation with convex mesh if volume is negative
|
||||
if (GetVolumeRef(type) <= 0 && exactmeshinertia) {
|
||||
mju_warning("Malformed mesh '%s', computing mesh inertia from convex hull", name.c_str());
|
||||
exactmeshinertia = false;
|
||||
ComputeVolume(CoM, type, facecen, exactmeshinertia);
|
||||
}
|
||||
|
||||
// if volume is still invalid, skip the rest of the computations
|
||||
// if volume is invalid, skip the rest of the computations
|
||||
if (GetVolumeRef(type) < mjMINVAL) {
|
||||
validvolume_ = GetVolumeRef(type) < 0 ? -1 : 0;
|
||||
if (type == mjINERTIA_SHELL) {
|
||||
validarea_ = 0;
|
||||
} else {
|
||||
validvolume_ = GetVolumeRef(type) < 0 ? -1 : 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1447,17 +1445,19 @@ void mjCMesh::Process() {
|
||||
const int k[6][2] = {{0, 0}, {1, 1}, {2, 2}, {0, 1}, {0, 2}, {1, 2}};
|
||||
double P[6] = {0, 0, 0, 0, 0, 0};
|
||||
GetVolumeRef(type) = 0;
|
||||
for (int i=0; i < nface(); i++) {
|
||||
float* D = vert_.data()+3*face_[3*i];
|
||||
float* E = vert_.data()+3*face_[3*i+1];
|
||||
float* F = vert_.data()+3*face_[3*i+2];
|
||||
int nf = (inertia == mjINERTIA_CONVEX) ? graph_[1] : nface();
|
||||
int* f = (inertia == mjINERTIA_CONVEX) ? graph_ + 2 + 3*(graph_[0]+graph_[1]) : face_.data();
|
||||
for (int i=0; i < nf; i++) {
|
||||
float* D = vert_.data()+3*f[3*i];
|
||||
float* E = vert_.data()+3*f[3*i+1];
|
||||
float* F = vert_.data()+3*f[3*i+2];
|
||||
|
||||
// get area, normal and center; update volume
|
||||
double a = _triangle(nrm, cen, D, E, F);
|
||||
double vol = type==mjINERTIA_SHELL ? a : mjuu_dot3(cen, nrm) * a / 3;
|
||||
|
||||
// if legacy computation requested, then always positive
|
||||
if (!exactmeshinertia && type==mjINERTIA_VOLUME) {
|
||||
if (inertia == mjINERTIA_LEGACY) {
|
||||
vol = abs(vol);
|
||||
}
|
||||
|
||||
@@ -1554,7 +1554,7 @@ void mjCMesh::CheckMesh(mjtGeomInertia type) {
|
||||
if (!processed_) {
|
||||
return;
|
||||
}
|
||||
if ((invalidorientation_.first>=0 || invalidorientation_.second>=0) && model->exactmeshinertia)
|
||||
if ((invalidorientation_.first>=0 || invalidorientation_.second>=0) && inertia == mjINERTIA_EXACT)
|
||||
throw mjCError(this,
|
||||
"faces of mesh '%s' have inconsistent orientation. Please check the "
|
||||
"faces containing the vertices %d and %d.",
|
||||
|
||||
@@ -868,6 +868,7 @@ class mjCMesh: public mjCMesh_, private mjsMesh {
|
||||
const std::vector<float>& UserTexcoord() const { return spec_texcoord_; }
|
||||
const std::vector<int>& Face() const { return face_; }
|
||||
const std::vector<int>& UserFace() const { return spec_face_; }
|
||||
mjtMeshInertia Inertia() const { return spec.inertia; }
|
||||
|
||||
// setters
|
||||
void SetNeedHull(bool needhull) { needhull_ = needhull; }
|
||||
@@ -945,8 +946,7 @@ class mjCMesh: public mjCMesh_, private mjsMesh {
|
||||
std::vector<unsigned char> num_face_vertices_;
|
||||
|
||||
// compute the volume and center-of-mass of the mesh given the face center
|
||||
void ComputeVolume(double CoM[3], mjtGeomInertia type, const double facecen[3],
|
||||
bool exactmeshinertia);
|
||||
void ComputeVolume(double CoM[3], mjtGeomInertia gtype, const double facecen[3]);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ extern const mjMap bias_map[];
|
||||
extern const mjMap stage_map[];
|
||||
extern const mjMap datatype_map[];
|
||||
extern const mjMap meshtype_map[];
|
||||
extern const mjMap meshinertia_map[];
|
||||
extern const mjMap flexself_map[];
|
||||
|
||||
|
||||
|
||||
@@ -97,11 +97,10 @@ static void UpdateString(string& psuffix, int count, int i) {
|
||||
const char* MJCF[nMJCF][mjXATTRNUM] = {
|
||||
{"mujoco", "!", "1", "model"},
|
||||
{"<"},
|
||||
{"compiler", "*", "21", "autolimits", "boundmass", "boundinertia", "settotalmass",
|
||||
{"compiler", "*", "20", "autolimits", "boundmass", "boundinertia", "settotalmass",
|
||||
"balanceinertia", "strippath", "coordinate", "angle", "fitaabb", "eulerseq",
|
||||
"meshdir", "texturedir", "discardvisual", "convexhull", "usethread",
|
||||
"fusestatic", "inertiafromgeom", "inertiagrouprange", "exactmeshinertia",
|
||||
"assetdir", "alignfree"},
|
||||
"fusestatic", "inertiafromgeom", "inertiagrouprange", "assetdir", "alignfree"},
|
||||
{"<"},
|
||||
{"lengthrange", "?", "10", "mode", "useexisting", "uselimit",
|
||||
"accel", "maxforce", "timeconst", "timestep",
|
||||
@@ -150,7 +149,7 @@ const char* MJCF[nMJCF][mjXATTRNUM] = {
|
||||
|
||||
{"default", "R", "1", "class"},
|
||||
{"<"},
|
||||
{"mesh", "?", "2", "scale", "maxhullvert"},
|
||||
{"mesh", "?", "3", "scale", "maxhullvert", "inertia"},
|
||||
{"material", "?", "10", "texture", "emission", "specular", "shininess",
|
||||
"reflectance", "metallic", "roughness", "rgba", "texrepeat", "texuniform"},
|
||||
{"joint", "?", "22", "type", "group", "pos", "axis", "springdamper",
|
||||
@@ -226,9 +225,9 @@ const char* MJCF[nMJCF][mjXATTRNUM] = {
|
||||
|
||||
{"asset", "*", "0"},
|
||||
{"<"},
|
||||
{"mesh", "*", "13", "name", "class", "content_type", "file", "vertex", "normal",
|
||||
{"mesh", "*", "14", "name", "class", "content_type", "file", "vertex", "normal",
|
||||
"texcoord", "face", "refpos", "refquat", "scale", "smoothnormal",
|
||||
"maxhullvert"},
|
||||
"maxhullvert", "inertia"},
|
||||
{"<"},
|
||||
{"plugin", "*", "2", "plugin", "instance"},
|
||||
{"<"},
|
||||
@@ -790,6 +789,14 @@ const mjMap meshtype_map[2] = {
|
||||
};
|
||||
|
||||
|
||||
// mesh inertia type
|
||||
const mjMap meshinertia_map[3] = {
|
||||
{"convex", mjINERTIA_CONVEX},
|
||||
{"legacy", mjINERTIA_LEGACY},
|
||||
{"exact", mjINERTIA_EXACT}
|
||||
};
|
||||
|
||||
|
||||
// flexcomp type
|
||||
const mjMap fcomp_map[mjNFCOMPTYPES] = {
|
||||
{"grid", mjFCOMPTYPE_GRID},
|
||||
@@ -1020,9 +1027,6 @@ void mjXReader::Compiler(XMLElement* section, mjSpec* spec) {
|
||||
}
|
||||
MapValue(section, "inertiafromgeom", &spec->inertiafromgeom, TFAuto_map, 3);
|
||||
ReadAttr(section, "inertiagrouprange", 2, spec->inertiagrouprange, text);
|
||||
if (MapValue(section, "exactmeshinertia", &n, bool_map, 2)){
|
||||
spec->exactmeshinertia = (n==1);
|
||||
}
|
||||
if (MapValue(section, "alignfree", &n, bool_map, 2)) {
|
||||
spec->alignfree = (n==1);
|
||||
}
|
||||
@@ -1424,6 +1428,9 @@ void mjXReader::OneMesh(XMLElement* elem, mjsMesh* mesh, const mjVFS* vfs) {
|
||||
ReadAttr(elem, "refpos", 3, mesh->refpos, text);
|
||||
ReadAttr(elem, "refquat", 4, mesh->refquat, text);
|
||||
ReadAttr(elem, "scale", 3, mesh->scale, text);
|
||||
if (MapValue(elem, "inertia", &n, meshinertia_map, 3)) {
|
||||
mesh->inertia = (mjtMeshInertia)n;
|
||||
}
|
||||
|
||||
XMLElement* eplugin = FirstChildElement(elem, "plugin");
|
||||
if (eplugin) {
|
||||
|
||||
@@ -212,6 +212,9 @@ void mjXWriter::OneMesh(XMLElement* elem, const mjCMesh* mesh, mjCDef* def) {
|
||||
}
|
||||
WriteAttrTxt(elem, "content_type", mesh->ContentType());
|
||||
WriteAttrTxt(elem, "file", mesh->File());
|
||||
if (mesh->Inertia() != def->Mesh().Inertia()) {
|
||||
WriteAttrTxt(elem, "inertia", FindValue(meshinertia_map, 3, mesh->Inertia()));
|
||||
}
|
||||
|
||||
// write vertex data
|
||||
if (!mesh->UserVert().empty()) {
|
||||
@@ -930,9 +933,7 @@ void mjXWriter::Compiler(XMLElement* root) {
|
||||
if (!model->usethread) {
|
||||
WriteAttrTxt(section, "usethread", "false");
|
||||
}
|
||||
if (model->exactmeshinertia) {
|
||||
WriteAttrTxt(section, "exactmeshinertia", "true");
|
||||
}
|
||||
|
||||
if (model->boundmass) {
|
||||
WriteAttr(section, "boundmass", 1, &model->boundmass);
|
||||
}
|
||||
|
||||
+2
-2
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -451,8 +451,13 @@ public enum mjtFont : int{
|
||||
mjFONT_BIG = 2,
|
||||
}
|
||||
public enum mjtGeomInertia : int{
|
||||
mjINERTIA_VOLUME = 1,
|
||||
mjINERTIA_SHELL = 2,
|
||||
mjINERTIA_VOLUME = 0,
|
||||
mjINERTIA_SHELL = 1,
|
||||
}
|
||||
public enum mjtMeshInertia : int{
|
||||
mjINERTIA_CONVEX = 0,
|
||||
mjINERTIA_EXACT = 1,
|
||||
mjINERTIA_LEGACY = 2,
|
||||
}
|
||||
public enum mjtBuiltin : int{
|
||||
mjBUILTIN_NONE = 0,
|
||||
|
||||
Reference in New Issue
Block a user