diff --git a/doc/changelog.rst b/doc/changelog.rst index b50d64b1..ab454f4c 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -2,6 +2,15 @@ Changelog ========= +Upcoming version (not yet released) +----------------------------------- + +General +^^^^^^^ +1. Added `mjModel.mesh_scale`: the scaling applied to asset vertices, as specified in the :ref:`scale` + attribute. + + Version 3.1.4 (April 10th, 2024) -------------------------------- diff --git a/doc/includes/references.h b/doc/includes/references.h index 613dd037..c932b61e 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -1165,6 +1165,7 @@ struct mjModel_ { int* mesh_facenormal; // normal face data (nmeshface x 3) int* mesh_facetexcoord; // texture face data (nmeshface x 3) int* mesh_graph; // convex graph data (nmeshgraph x 1) + mjtNum* mesh_scale; // scaling applied to asset vertices (nmesh x 3) mjtNum* mesh_pos; // translation applied to asset vertices (nmesh x 3) mjtNum* mesh_quat; // rotation applied to asset vertices (nmesh x 4) int* mesh_pathadr; // address of asset path for mesh; -1: none (nmesh x 1) diff --git a/include/mujoco/mjmodel.h b/include/mujoco/mjmodel.h index 2f0031e2..80a0c4b4 100644 --- a/include/mujoco/mjmodel.h +++ b/include/mujoco/mjmodel.h @@ -880,6 +880,7 @@ struct mjModel_ { int* mesh_facenormal; // normal face data (nmeshface x 3) int* mesh_facetexcoord; // texture face data (nmeshface x 3) int* mesh_graph; // convex graph data (nmeshgraph x 1) + mjtNum* mesh_scale; // scaling applied to asset vertices (nmesh x 3) mjtNum* mesh_pos; // translation applied to asset vertices (nmesh x 3) mjtNum* mesh_quat; // rotation applied to asset vertices (nmesh x 4) int* mesh_pathadr; // address of asset path for mesh; -1: none (nmesh x 1) diff --git a/include/mujoco/mjxmacro.h b/include/mujoco/mjxmacro.h index 7cb4fa7b..0c7e03ca 100644 --- a/include/mujoco/mjxmacro.h +++ b/include/mujoco/mjxmacro.h @@ -365,6 +365,7 @@ XMJV( int, mesh_bvhadr, nmesh, 1 ) \ XMJV( int, mesh_bvhnum, nmesh, 1 ) \ XMJV( int, mesh_graphadr, nmesh, 1 ) \ + X ( mjtNum, mesh_scale, nmesh, 3 ) \ X ( mjtNum, mesh_pos, nmesh, 3 ) \ X ( mjtNum, mesh_quat, nmesh, 4 ) \ X ( float, mesh_vert, nmeshvert, 3 ) \ diff --git a/introspect/structs.py b/introspect/structs.py index 980a9f0f..7ac1a8f6 100644 --- a/introspect/structs.py +++ b/introspect/structs.py @@ -2655,6 +2655,13 @@ STRUCTS: Mapping[str, StructDecl] = dict([ ), doc='convex graph data (nmeshgraph x 1)', # pylint: disable=line-too-long ), + StructFieldDecl( + name='mesh_scale', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='scaling applied to asset vertices (nmesh x 3)', + ), StructFieldDecl( name='mesh_pos', type=PointerType( diff --git a/src/engine/engine_io.c b/src/engine/engine_io.c index 315884ac..0a6f1df3 100644 --- a/src/engine/engine_io.c +++ b/src/engine/engine_io.c @@ -534,11 +534,15 @@ mjModel* mj_makeModel( m->nuser_actuator = nuser_actuator; m->nuser_sensor = nuser_sensor; m->nnames = nnames; - m->nnames_map = mjLOAD_MULTIPLE - * (nbody + njnt + ngeom + nsite + ncam + nlight + nflex + nmesh - + nskin + nhfield + ntex + nmat + npair + nexclude + neq - + ntendon + nu + nsensor + nnumeric + ntext + ntuple - + nkey + nplugin); + long nnames_map = (long)nbody + njnt + ngeom + nsite + ncam + nlight + nflex + nmesh + nskin + + nhfield + ntex + nmat + npair + nexclude + neq + ntendon + nu + nsensor + + nnumeric + ntext + ntuple + nkey + nplugin; + if (nnames_map >= INT_MAX / mjLOAD_MULTIPLE) { + mju_free(m); + mju_warning("Invalid model: size of nnames_map is larger than INT_MAX"); + return 0; + } + m->nnames_map = mjLOAD_MULTIPLE * nnames_map; m->npaths = npaths; #define X(name) \ diff --git a/src/user/user_model.cc b/src/user/user_model.cc index dc106b9d..9026c045 100644 --- a/src/user/user_model.cc +++ b/src/user/user_model.cc @@ -2123,6 +2123,7 @@ void mjCModel::CopyObjects(mjModel* m) { m->mesh_graphadr[i] = (pme->szgraph() ? graph_adr : -1); m->mesh_bvhnum[i] = pme->tree().nbvh; m->mesh_bvhadr[i] = pme->tree().nbvh ? bvh_adr : -1; + copyvec(&m->mesh_scale[3 * i], pme->get_scale(), 3); copyvec(&m->mesh_pos[3 * i], pme->GetOffsetPosPtr(), 3); copyvec(&m->mesh_quat[4 * i], pme->GetOffsetQuatPtr(), 4); diff --git a/src/user/user_objects.h b/src/user/user_objects.h index 1b9f0f2d..dedaa782 100644 --- a/src/user/user_objects.h +++ b/src/user/user_objects.h @@ -815,12 +815,12 @@ class mjCMesh: public mjCMesh_, private mjsMesh { const mjCBoundingVolumeHierarchy& tree() { return tree_; } void Compile(const mjVFS* vfs); // compiler - double* GetPosPtr(mjtGeomInertia type); // get position - double* GetQuatPtr(mjtGeomInertia type); // get orientation + double* GetPosPtr(mjtGeomInertia type); // get position + double* GetQuatPtr(mjtGeomInertia type); // get orientation double* GetOffsetPosPtr(); // get position offset for geom double* GetOffsetQuatPtr(); // get orientation offset for geom - double* GetInertiaBoxPtr(mjtGeomInertia type); // get inertia box - double& GetVolumeRef(mjtGeomInertia type); // get volume + double* GetInertiaBoxPtr(mjtGeomInertia type); // get inertia box + double& GetVolumeRef(mjtGeomInertia type); // get volume void FitGeom(mjCGeom* geom, double* meshpos); // approximate mesh with simple geom bool HasTexcoord() const; // texcoord not null void DelTexcoord(); // delete texcoord @@ -852,7 +852,7 @@ class mjCMesh: public mjCMesh_, private mjsMesh { void ApplyTransformations(); // apply user transformations void ComputeFaceCentroid(double[3]); // compute centroid of all faces void RemoveRepeated(void); // remove repeated vertices - void CheckMesh(mjtGeomInertia type); // check if the mesh is valid + void CheckMesh(mjtGeomInertia type); // check if the mesh is valid // mesh data to be copied into mjModel float* vert_; // vertex data (3*nvert), relative to (pos, quat) diff --git a/test/user/user_mesh_test.cc b/test/user/user_mesh_test.cc index 0fc57317..56898655 100644 --- a/test/user/user_mesh_test.cc +++ b/test/user/user_mesh_test.cc @@ -58,6 +58,11 @@ static const char* const kDuplicateOBJPath = static const char* const kMalformedFaceOBJPath = "user/testdata/malformed_face.xml"; +std::vector AsVector(const mjtNum* array, int n) { + return std::vector(array, array + n); +} + +using ::testing::ElementsAre; using ::testing::HasSubstr; using ::testing::IsNull; using ::testing::NotNull; @@ -863,6 +868,27 @@ TEST_F(MjCMeshTest, MeshPosQuat) { mj_deleteModel(model); } +TEST_F(MjCMeshTest, MeshScale) { + static constexpr char xml[] = R"( + + + + + + + + + + + )"; + mjModel* model = LoadModelFromString(xml); + ASSERT_THAT(model, NotNull()); + + 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 ------------------------------------- TEST_F(MjCMeshTest, CreateFaceTexCoord) { diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index 993f35f1..704eaf09 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -5374,6 +5374,7 @@ public unsafe struct mjModel_ { public int* mesh_facenormal; public int* mesh_facetexcoord; public int* mesh_graph; + public double* mesh_scale; public double* mesh_pos; public double* mesh_quat; public int* mesh_pathadr;