Do not modify mesh position when fitting a geom to the mesh AABB.
Also, unify the meaning of fitting a geom to an AABB: it now means to find the smallest geom such that its AABB contains the mesh AABB. Fixes #2881. PiperOrigin-RevId: 817097544 Change-Id: I883c686feffa803bd16ba461db24a474dfec7712
This commit is contained in:
committed by
Copybara-Service
parent
3a299c49ca
commit
98682ae284
@@ -735,12 +735,12 @@ has any effect. The settings here are global and apply to the entire model.
|
|||||||
|
|
||||||
:at:`fitaabb`: :at-val:`[false, true], "false"`
|
:at:`fitaabb`: :at-val:`[false, true], "false"`
|
||||||
The compiler is able to replace a mesh with a geometric primitive fitted to that mesh; see :ref:`geom <body-geom>`
|
The compiler is able to replace a mesh with a geometric primitive fitted to that mesh; see :ref:`geom <body-geom>`
|
||||||
below. If this attribute is "true", the fitting procedure uses the axis-aligned bounding box (aabb) of the mesh.
|
below. If this attribute is "true", the fitting procedure uses the axis-aligned bounding box (AABB) of the mesh,
|
||||||
Otherwise it uses the equivalent-inertia box of the mesh. The type of geometric primitive used for fitting is
|
choosing the smallest primitive whose AABB contains the mesh AABB. Otherwise it uses the equivalent-inertia box of
|
||||||
specified separately for each geom. The models used to generate the image on the right can be found
|
the mesh. The type of geometric primitive used for fitting is specified separately for each geom. The models used to
|
||||||
`here <https://github.com/google-deepmind/mujoco/blob/main/test/user/testdata/fitmesh_inertiabox.xml>`__ (fit inertia
|
generate the image on the right can be found `here
|
||||||
box) and `here <https://github.com/google-deepmind/mujoco/blob/main/test/user/testdata/fitmesh_aabb.xml>`__ (fit
|
<https://github.com/google-deepmind/mujoco/blob/main/test/user/testdata/fitmesh_inertiabox.xml>`__ (fit inertia box)
|
||||||
aabb).
|
and `here <https://github.com/google-deepmind/mujoco/blob/main/test/user/testdata/fitmesh_aabb.xml>`__ (fit aabb).
|
||||||
|
|
||||||
.. _compiler-eulerseq:
|
.. _compiler-eulerseq:
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ MJX
|
|||||||
- Pull in MuJoCo Warp updatest to ``io.py``, and use ``naconmax`` instead of ``naconmax`` to set the maximum number of
|
- Pull in MuJoCo Warp updatest to ``io.py``, and use ``naconmax`` instead of ``naconmax`` to set the maximum number of
|
||||||
contacts over all environments.
|
contacts over all environments.
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
^^^^^^^^^
|
||||||
|
- Fix :github:issue:`2881`, :at:`fitaabb` was adding an offset to the mesh and applying an incorrect frame
|
||||||
|
transformation. Also, unify the meaning of fitting a geom to a mesh AABB: it now means to find the smallest geom such
|
||||||
|
that its AABB contains the mesh AABB.
|
||||||
|
|
||||||
Version 3.3.6 (September 15, 2025)
|
Version 3.3.6 (September 15, 2025)
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.4 MiB |
+14
-46
@@ -979,10 +979,7 @@ void mjCMesh::DelTexcoord() {
|
|||||||
|
|
||||||
|
|
||||||
// set geom size to match mesh
|
// set geom size to match mesh
|
||||||
void mjCMesh::FitGeom(mjCGeom* geom, double* meshpos) {
|
void mjCMesh::FitGeom(mjCGeom* geom, double center[3]) {
|
||||||
// copy mesh pos into meshpos
|
|
||||||
mjuu_copyvec(meshpos, GetPosPtr(), 3);
|
|
||||||
|
|
||||||
// use inertial box
|
// use inertial box
|
||||||
if (!model->compiler.fitaabb) {
|
if (!model->compiler.fitaabb) {
|
||||||
// get inertia box type (shell or volume)
|
// get inertia box type (shell or volume)
|
||||||
@@ -1016,64 +1013,35 @@ void mjCMesh::FitGeom(mjCGeom* geom, double* meshpos) {
|
|||||||
|
|
||||||
// use aamm
|
// use aamm
|
||||||
else {
|
else {
|
||||||
// find aabb box center
|
// find aabb box center and size
|
||||||
double cen[3] = {(aamm_[0]+aamm_[3])/2, (aamm_[1]+aamm_[4])/2, (aamm_[2]+aamm_[5])/2};
|
center[0] = (aamm_[0]+aamm_[3])/2;
|
||||||
|
center[1] = (aamm_[1]+aamm_[4])/2;
|
||||||
|
center[2] = (aamm_[2]+aamm_[5])/2;
|
||||||
|
double size[3] = {aamm_[3] - center[0], aamm_[4] - center[1], aamm_[5] - center[2]};
|
||||||
|
|
||||||
// add box center into meshpos
|
// compute smallest geom whose aabb contains the mesh aabb
|
||||||
meshpos[0] += cen[0];
|
|
||||||
meshpos[1] += cen[1];
|
|
||||||
meshpos[2] += cen[2];
|
|
||||||
|
|
||||||
// compute depending on type
|
|
||||||
switch (geom->type) {
|
switch (geom->type) {
|
||||||
case mjGEOM_SPHERE:
|
case mjGEOM_SPHERE:
|
||||||
// find maximum distance
|
geom->size[0] = max(max(size[0], size[1]), size[2]);
|
||||||
geom->size[0] = 0;
|
|
||||||
for (int i=0; i < nvert(); i++) {
|
|
||||||
double v[3] = {vert_[3*i], vert_[3*i+1], vert_[3*i+2]};
|
|
||||||
double dst = mjuu_dist3(v, cen);
|
|
||||||
geom->size[0] = max(geom->size[0], dst);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case mjGEOM_CAPSULE:
|
case mjGEOM_CAPSULE:
|
||||||
case mjGEOM_CYLINDER:
|
case mjGEOM_CYLINDER:
|
||||||
// find maximum distance in XY, separately in Z
|
// find maximum distance in XY, separately in Z
|
||||||
geom->size[0] = 0;
|
geom->size[0] = max(size[0], size[1]);
|
||||||
geom->size[1] = 0;
|
geom->size[1] = size[2];
|
||||||
for (int i=0; i < nvert(); i++) {
|
|
||||||
double v[3] = {vert_[3*i], vert_[3*i+1], vert_[3*i+2]};
|
|
||||||
double dst = sqrt((v[0]-cen[0])*(v[0]-cen[0]) +
|
|
||||||
(v[1]-cen[1])*(v[1]-cen[1]));
|
|
||||||
geom->size[0] = max(geom->size[0], dst);
|
|
||||||
|
|
||||||
// proceed with z: valid for cylinder
|
|
||||||
double dst2 = abs(v[2]-cen[2]);
|
|
||||||
geom->size[1] = max(geom->size[1], dst2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// special handling of capsule: consider curved cap
|
// special handling of capsule: consider curved cap
|
||||||
if (geom->type == mjGEOM_CAPSULE) {
|
if (geom->type == mjGEOM_CAPSULE) {
|
||||||
geom->size[1] = 0;
|
geom->size[1] -= geom->size[0];
|
||||||
for (int i=0; i < nvert(); i++) {
|
|
||||||
// get distance in XY and Z
|
|
||||||
double v[3] = {vert_[3*i], vert_[3*i+1], vert_[3*i+2]};
|
|
||||||
double dst = sqrt((v[0]-cen[0])*(v[0]-cen[0]) +
|
|
||||||
(v[1]-cen[1])*(v[1]-cen[1]));
|
|
||||||
double dst2 = abs(v[2]-cen[2]);
|
|
||||||
|
|
||||||
// get spherical elevation at horizontal distance dst
|
|
||||||
double h = geom->size[0] * sin(acos(dst/geom->size[0]));
|
|
||||||
geom->size[1] = max(geom->size[1], dst2-h);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case mjGEOM_ELLIPSOID:
|
case mjGEOM_ELLIPSOID:
|
||||||
case mjGEOM_BOX:
|
case mjGEOM_BOX:
|
||||||
geom->size[0] = aamm_[3] - cen[0];
|
geom->size[0] = size[0];
|
||||||
geom->size[1] = aamm_[4] - cen[1];
|
geom->size[1] = size[1];
|
||||||
geom->size[2] = aamm_[5] - cen[2];
|
geom->size[2] = size[2];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -3732,22 +3732,26 @@ void mjCGeom::Compile(void) {
|
|||||||
|
|
||||||
// save reference in case this is not an mjGEOM_MESH
|
// save reference in case this is not an mjGEOM_MESH
|
||||||
mjCMesh* pmesh = mesh;
|
mjCMesh* pmesh = mesh;
|
||||||
|
double center[3] = {0, 0, 0};
|
||||||
|
|
||||||
// fit geom if type is not mjGEOM_MESH
|
// fit geom if type is not mjGEOM_MESH
|
||||||
if (type != mjGEOM_MESH && type != mjGEOM_SDF) {
|
if (type != mjGEOM_MESH && type != mjGEOM_SDF) {
|
||||||
double meshpos[3];
|
mesh->FitGeom(this, center);
|
||||||
mesh->FitGeom(this, meshpos);
|
|
||||||
|
|
||||||
// remove reference to mesh
|
// remove reference to mesh
|
||||||
meshname_.clear();
|
meshname_.clear();
|
||||||
mesh = nullptr;
|
mesh = nullptr;
|
||||||
mjuu_copyvec(pmesh->GetPosPtr(), meshpos, 3);
|
|
||||||
} else if (typeinertia == mjINERTIA_SHELL) {
|
} else if (typeinertia == mjINERTIA_SHELL) {
|
||||||
throw mjCError(this, "for mesh geoms, inertia should be specified in the mesh asset");
|
throw mjCError(this, "for mesh geoms, inertia should be specified in the mesh asset");
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply geom pos/quat as offset
|
// rotate center to geom frame and add it to mesh frame
|
||||||
mjuu_frameaccum(pos, quat, pmesh->GetPosPtr(), pmesh->GetQuatPtr());
|
double meshpos[3];
|
||||||
|
mjuu_rotVecQuat(meshpos, center, pmesh->GetQuatPtr());
|
||||||
|
mjuu_addtovec(meshpos, pmesh->GetPosPtr(), 3);
|
||||||
|
|
||||||
|
// accumulate mesh frame into geom frame
|
||||||
|
mjuu_frameaccum(pos, quat, meshpos, pmesh->GetQuatPtr());
|
||||||
}
|
}
|
||||||
|
|
||||||
// check size parameters
|
// check size parameters
|
||||||
|
|||||||
@@ -1186,7 +1186,7 @@ class mjCMesh: public mjCMesh_, private mjsMesh {
|
|||||||
double* GetQuatPtr(); // get orientation
|
double* GetQuatPtr(); // get orientation
|
||||||
double* GetInertiaBoxPtr(); // get inertia box
|
double* GetInertiaBoxPtr(); // get inertia box
|
||||||
double GetVolumeRef() const; // get volume
|
double GetVolumeRef() const; // get volume
|
||||||
void FitGeom(mjCGeom* geom, double* meshpos); // approximate mesh with simple geom
|
void FitGeom(mjCGeom* geom, double center[3]); // approximate mesh with simple geom
|
||||||
bool HasTexcoord() const; // texcoord not null
|
bool HasTexcoord() const; // texcoord not null
|
||||||
void DelTexcoord(); // delete texcoord
|
void DelTexcoord(); // delete texcoord
|
||||||
bool IsVisual(void) const { return visual_; } // is geom visual
|
bool IsVisual(void) const { return visual_; } // is geom visual
|
||||||
|
|||||||
Vendored
+5
-5
@@ -22,19 +22,19 @@
|
|||||||
<geom type="mesh" mesh="stanford_bunny"/>
|
<geom type="mesh" mesh="stanford_bunny"/>
|
||||||
</body>
|
</body>
|
||||||
<body euler="90 0 0">
|
<body euler="90 0 0">
|
||||||
<geom name="bunny_sphere" type="sphere" mesh="stanford_bunny" rgba="1 0 1 1"/>
|
<geom name="bunny_sphere" type="sphere" mesh="stanford_bunny" rgba="1 0 1 .5"/>
|
||||||
</body>
|
</body>
|
||||||
<body euler="90 0 0" pos=".25 0 0">
|
<body euler="90 0 0" pos=".25 0 0">
|
||||||
<geom name="bunny_capsule" type="capsule" mesh="stanford_bunny" rgba="1 0 1 1"/>
|
<geom name="bunny_capsule" type="capsule" mesh="stanford_bunny" rgba="1 0 1 .5"/>
|
||||||
</body>
|
</body>
|
||||||
<body euler="90 0 0" pos=".5 0 0">
|
<body euler="90 0 0" pos=".5 0 0">
|
||||||
<geom name="bunny_ellipsoid" type="ellipsoid" mesh="stanford_bunny" rgba="1 0 1 1"/>
|
<geom name="bunny_ellipsoid" type="ellipsoid" mesh="stanford_bunny" rgba="1 0 1 .5"/>
|
||||||
</body>
|
</body>
|
||||||
<body euler="90 0 0" pos=".75 0 0">
|
<body euler="90 0 0" pos=".75 0 0">
|
||||||
<geom name="bunny_cylinder" type="cylinder" mesh="stanford_bunny" rgba="1 0 1 1"/>
|
<geom name="bunny_cylinder" type="cylinder" mesh="stanford_bunny" rgba="1 0 1 .5"/>
|
||||||
</body>
|
</body>
|
||||||
<body euler="90 0 0" pos="1 0 0">
|
<body euler="90 0 0" pos="1 0 0">
|
||||||
<geom name="bunny_box" type="box" mesh="stanford_bunny" rgba="1 0 1 1"/>
|
<geom name="bunny_box" type="box" mesh="stanford_bunny" rgba="1 0 1 .5"/>
|
||||||
</body>
|
</body>
|
||||||
<light name="left" pos="0 0 1"/>
|
<light name="left" pos="0 0 1"/>
|
||||||
<light name="right" pos="1 0 1"/>
|
<light name="right" pos="1 0 1"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user