Fix bug in mesh normal scaling for nonuniform mesh scales.
PiperOrigin-RevId: 946937791 Change-Id: I1b8a0ad4d573842ef9f76572dea25668dd319b52
This commit is contained in:
committed by
Copybara-Service
parent
fbf031a7d5
commit
f5f9d9efb7
@@ -36,6 +36,10 @@ General
|
|||||||
- Changed the default value of :ref:`sleep_tolerance<option-sleep_tolerance>` from 1e-4 to 1e-3 (1mm/sec in SI
|
- Changed the default value of :ref:`sleep_tolerance<option-sleep_tolerance>` from 1e-4 to 1e-3 (1mm/sec in SI
|
||||||
units).
|
units).
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
^^^^^^^^^
|
||||||
|
- Fixed a bug in the mesh compiler where normals were scaled as vectors rather than covectors.
|
||||||
|
|
||||||
Version 3.10.0 (June 22, 2026)
|
Version 3.10.0 (June 22, 2026)
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -1292,10 +1292,11 @@ void mjCMesh::ApplyTransformations(double* dvert) {
|
|||||||
dvert[3*i + 2] *= scale[2];
|
dvert[3*i + 2] *= scale[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// normals are covectors: transform by the inverse scale
|
||||||
for (int i = 0; i < nnormal(); i++) {
|
for (int i = 0; i < nnormal(); i++) {
|
||||||
normal_[3*i + 0] *= scale[0];
|
normal_[3*i + 0] /= scale[0];
|
||||||
normal_[3*i + 1] *= scale[1];
|
normal_[3*i + 1] /= scale[1];
|
||||||
normal_[3*i + 2] *= scale[2];
|
normal_[3*i + 2] /= scale[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1120,6 +1120,64 @@ TEST_F(MjCMeshTest, MeshScale) {
|
|||||||
EXPECT_THAT(AsVector(model->mesh_scale + 3, 3), ElementsAre(0.9, 1, -1));
|
EXPECT_THAT(AsVector(model->mesh_scale + 3, 3), ElementsAre(0.9, 1, -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MjCMeshTest, UserNormalsAnisotropicScale) {
|
||||||
|
// normals are covectors: under mesh scale s they must transform by 1/s.
|
||||||
|
// cube with per-vertex normals along the vertex directions, anisotropic
|
||||||
|
// scale; expected compiled normals follow (+-1/sx, +-1/sy, +-1/sz)
|
||||||
|
static constexpr char xml[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<asset>
|
||||||
|
<mesh name="cube" scale=".2 .1 .1"
|
||||||
|
vertex="-1 -1 -1 1 -1 -1 1 1 -1 -1 1 -1 -1 -1 1 1 -1 1 1 1 1 -1 1 1"
|
||||||
|
normal="-1 -1 -1 1 -1 -1 1 1 -1 -1 1 -1 -1 -1 1 1 -1 1 1 1 1 -1 1 1"
|
||||||
|
face="0 2 1 0 3 2 4 5 6 4 6 7 0 1 5 0 5 4 1 2 6 1 6 5
|
||||||
|
2 3 7 2 7 6 3 0 4 3 4 7"/>
|
||||||
|
</asset>
|
||||||
|
<worldbody>
|
||||||
|
<body>
|
||||||
|
<freejoint/>
|
||||||
|
<geom type="mesh" mesh="cube"/>
|
||||||
|
</body>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>
|
||||||
|
)";
|
||||||
|
char error[1024];
|
||||||
|
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||||
|
ASSERT_THAT(model, NotNull()) << error;
|
||||||
|
const mjModel* m = model.get();
|
||||||
|
int mid = 0;
|
||||||
|
const mjtNum* mq = m->mesh_quat + 4*mid;
|
||||||
|
const mjtNum* mp = m->mesh_pos + 3*mid;
|
||||||
|
double scale[3] = {.2, .1, .1};
|
||||||
|
int va = m->mesh_vertadr[mid];
|
||||||
|
int na = m->mesh_normaladr[mid];
|
||||||
|
int fa = m->mesh_faceadr[mid];
|
||||||
|
for (int i = 0; i < 3*m->mesh_facenum[mid]; i++) {
|
||||||
|
int vid = m->mesh_face[3*fa + i];
|
||||||
|
int nid = m->mesh_facenormal[3*fa + i];
|
||||||
|
|
||||||
|
// rotate vertex and normal back to the authored frame
|
||||||
|
mjtNum vm[3] = {m->mesh_vert[3*(va+vid)+0],
|
||||||
|
m->mesh_vert[3*(va+vid)+1],
|
||||||
|
m->mesh_vert[3*(va+vid)+2]};
|
||||||
|
mjtNum nm[3] = {m->mesh_normal[3*(na+nid)+0],
|
||||||
|
m->mesh_normal[3*(na+nid)+1],
|
||||||
|
m->mesh_normal[3*(na+nid)+2]};
|
||||||
|
mjtNum v[3], n[3];
|
||||||
|
mju_rotVecQuat(v, vm, mq);
|
||||||
|
mju_addTo3(v, mp);
|
||||||
|
mju_rotVecQuat(n, nm, mq);
|
||||||
|
|
||||||
|
// expected: sign pattern of the vertex, divided by scale, normalized
|
||||||
|
mjtNum expected[3];
|
||||||
|
for (int k = 0; k < 3; k++) {
|
||||||
|
expected[k] = (v[k] > 0 ? 1.0 : -1.0) / scale[k];
|
||||||
|
}
|
||||||
|
mju_normalize3(expected);
|
||||||
|
EXPECT_GT(mju_dot3(n, expected), 0.999);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(MjCMeshTest, NegativeScaleUserMeshCompiles) {
|
TEST_F(MjCMeshTest, NegativeScaleUserMeshCompiles) {
|
||||||
static constexpr char xml[] = R"(
|
static constexpr char xml[] = R"(
|
||||||
<mujoco>
|
<mujoco>
|
||||||
|
|||||||
Reference in New Issue
Block a user