Add ability to specify a material assignment to a mesh asset.
This extends both the spec and the XML definition to allow specification of a material name on a mesh. This material acts as a fallback in the case that the referencing geom element does not specify a material of it's own. PiperOrigin-RevId: 798232378 Change-Id: Iaf300c727732744ea1b613e64334ee505ac6e209
This commit is contained in:
committed by
Copybara-Service
parent
42d8454555
commit
1fb1810b07
+14
-7
@@ -1424,6 +1424,11 @@ The full list of processing steps applied by the compiler to each mesh is as fol
|
|||||||
The parameters used to generate a builtin mesh. The number and type of parameters and their semantic depends on the
|
The parameters used to generate a builtin mesh. The number and type of parameters and their semantic depends on the
|
||||||
mesh type. See :ref:`mesh/builtin<asset-mesh-builtin>` for details.
|
mesh type. See :ref:`mesh/builtin<asset-mesh-builtin>` for details.
|
||||||
|
|
||||||
|
.. _asset-mesh-material:
|
||||||
|
|
||||||
|
:at:`material`: :at-val:`string, optional`
|
||||||
|
Fallback material for mesh geoms that do not specify their own material.
|
||||||
|
|
||||||
.. _mesh-plugin:
|
.. _mesh-plugin:
|
||||||
|
|
||||||
:el-prefix:`mesh/` |-| **plugin** (?)
|
:el-prefix:`mesh/` |-| **plugin** (?)
|
||||||
@@ -2553,13 +2558,15 @@ helps clarify the role of bodies and geoms in MuJoCo.
|
|||||||
.. _body-geom-material:
|
.. _body-geom-material:
|
||||||
|
|
||||||
:at:`material`: :at-val:`string, optional`
|
:at:`material`: :at-val:`string, optional`
|
||||||
If specified, this attribute applies a material to the geom. The material determines the visual properties of the
|
If specified, this attribute applies a material to the geom. Otherwise, if unspecified and the type of the geom is
|
||||||
geom. The only exception is color: if the rgba attribute below is different from its internal default, it takes
|
a **mesh** the compiler will apply the mesh asset :ref:`material <asset-mesh-material>` if present.
|
||||||
precedence while the remaining material properties are still applied. Note that if the same material is referenced
|
|
||||||
from multiple geoms (as well as sites and tendons) and the user changes some of its properties at runtime, these
|
The material determines the visual properties of the geom. The only exception is color: if the rgba attribute below
|
||||||
changes will take effect immediately for all model elements referencing the material. This is because the compiler
|
is different from its internal default, it takes precedence while the remaining material properties are still
|
||||||
saves the material and its properties as a separate element in mjModel, and the elements using this material only
|
applied. Note that if the same material is referenced from multiple geoms (as well as sites and tendons) and the user
|
||||||
keep a reference to it.
|
changes some of its properties at runtime, these changes will take effect immediately for all model elements
|
||||||
|
referencing the material. This is because the compiler saves the material and its properties as a separate element in
|
||||||
|
mjModel, and the elements using this material only keep a reference to it.
|
||||||
|
|
||||||
.. _body-geom-rgba:
|
.. _body-geom-rgba:
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,8 @@
|
|||||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||||
| | | | :ref:`maxhullvert<asset-mesh-maxhullvert>` | :ref:`inertia<asset-mesh-inertia>` | :ref:`builtin<asset-mesh-builtin>` | :ref:`params<asset-mesh-params>` | |
|
| | | | :ref:`maxhullvert<asset-mesh-maxhullvert>` | :ref:`inertia<asset-mesh-inertia>` | :ref:`builtin<asset-mesh-builtin>` | :ref:`params<asset-mesh-params>` | |
|
||||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||||
|
| | | | :ref:`material<asset-mesh-material>` | | | | |
|
||||||
|
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
| |_2| mesh |br| |_2| |L| | | .. table:: |
|
| |_2| mesh |br| |_2| |L| | | .. table:: |
|
||||||
| :ref:`plugin | \* | :class: mjcf-attributes |
|
| :ref:`plugin | \* | :class: mjcf-attributes |
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ General
|
|||||||
- Added support for shells with a curved reference configuration. See this `example
|
- Added support for shells with a curved reference configuration. See this `example
|
||||||
<https://github.com/google-deepmind/mujoco/blob/main/model/flex/basket.xml>`__.
|
<https://github.com/google-deepmind/mujoco/blob/main/model/flex/basket.xml>`__.
|
||||||
|
|
||||||
|
- Added support for assigning a default material to a mesh asset using the :ref:`mesh/material <asset-mesh-material>`
|
||||||
|
attribute.
|
||||||
|
|
||||||
MJX
|
MJX
|
||||||
^^^
|
^^^
|
||||||
- Promote ``ten_length`` to the public MJX API. Add Warp support for ``mjx.tendon``.
|
- Promote ``ten_length`` to the public MJX API. Add Warp support for ``mjx.tendon``.
|
||||||
|
|||||||
@@ -2144,6 +2144,7 @@ typedef struct mjsMesh_ { // mesh specification
|
|||||||
mjIntVec* userface; // user vertex indices
|
mjIntVec* userface; // user vertex indices
|
||||||
mjIntVec* userfacetexcoord; // user texcoord indices
|
mjIntVec* userfacetexcoord; // user texcoord indices
|
||||||
mjsPlugin plugin; // sdf plugin
|
mjsPlugin plugin; // sdf plugin
|
||||||
|
mjString* material; // name of material
|
||||||
mjString* info; // message appended to compiler errors
|
mjString* info; // message appended to compiler errors
|
||||||
} mjsMesh;
|
} mjsMesh;
|
||||||
typedef struct mjsHField_ { // height field specification
|
typedef struct mjsHField_ { // height field specification
|
||||||
|
|||||||
@@ -480,6 +480,7 @@ typedef struct mjsMesh_ { // mesh specification
|
|||||||
mjIntVec* userface; // user vertex indices
|
mjIntVec* userface; // user vertex indices
|
||||||
mjIntVec* userfacetexcoord; // user texcoord indices
|
mjIntVec* userfacetexcoord; // user texcoord indices
|
||||||
mjsPlugin plugin; // sdf plugin
|
mjsPlugin plugin; // sdf plugin
|
||||||
|
mjString* material; // name of material
|
||||||
mjString* info; // message appended to compiler errors
|
mjString* info; // message appended to compiler errors
|
||||||
} mjsMesh;
|
} mjsMesh;
|
||||||
|
|
||||||
|
|||||||
@@ -9353,6 +9353,13 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
|||||||
type=ValueType(name='mjsPlugin'),
|
type=ValueType(name='mjsPlugin'),
|
||||||
doc='sdf plugin',
|
doc='sdf plugin',
|
||||||
),
|
),
|
||||||
|
StructFieldDecl(
|
||||||
|
name='material',
|
||||||
|
type=PointerType(
|
||||||
|
inner_type=ValueType(name='mjString'),
|
||||||
|
),
|
||||||
|
doc='name of material',
|
||||||
|
),
|
||||||
StructFieldDecl(
|
StructFieldDecl(
|
||||||
name='info',
|
name='info',
|
||||||
type=PointerType(
|
type=PointerType(
|
||||||
|
|||||||
@@ -1478,6 +1478,34 @@ class SpecsTest(absltest.TestCase):
|
|||||||
self.assertEqual(mj_model.sensor_dim[0], 4)
|
self.assertEqual(mj_model.sensor_dim[0], 4)
|
||||||
self.assertEqual(mj_model.sensor_dim[1], 1)
|
self.assertEqual(mj_model.sensor_dim[1], 1)
|
||||||
|
|
||||||
|
def test_mesh_material(self):
|
||||||
|
spec = mujoco.MjSpec()
|
||||||
|
|
||||||
|
spec.add_material(name='red', rgba=(1, 0, 0, 1))
|
||||||
|
spec.add_material(name='green', rgba=(0, 1, 0, 1))
|
||||||
|
|
||||||
|
mesh = spec.add_mesh(name='sphere')
|
||||||
|
mesh.make_sphere(subdivision=1)
|
||||||
|
mesh.material = 'red'
|
||||||
|
|
||||||
|
geom = spec.worldbody.add_geom()
|
||||||
|
geom.type = mujoco.mjtGeom.mjGEOM_MESH
|
||||||
|
geom.meshname = 'sphere'
|
||||||
|
|
||||||
|
geom_2 = spec.worldbody.add_geom()
|
||||||
|
geom_2.type = mujoco.mjtGeom.mjGEOM_MESH
|
||||||
|
geom_2.meshname = 'sphere'
|
||||||
|
geom_2.material = 'green'
|
||||||
|
|
||||||
|
model = spec.compile()
|
||||||
|
|
||||||
|
self.assertEqual(model.geom_matid[0], 0)
|
||||||
|
self.assertEqual(model.geom_matid[1], 1)
|
||||||
|
|
||||||
|
mesh.material = 'green'
|
||||||
|
model = spec.compile()
|
||||||
|
|
||||||
|
self.assertEqual(model.geom_matid[0], 1)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
absltest.main()
|
absltest.main()
|
||||||
|
|||||||
@@ -278,6 +278,7 @@ void mjCMesh::PointToLocal() {
|
|||||||
spec.userface = &spec_face_;
|
spec.userface = &spec_face_;
|
||||||
spec.usertexcoord = &spec_texcoord_;
|
spec.usertexcoord = &spec_texcoord_;
|
||||||
spec.userfacetexcoord = &spec_facetexcoord_;
|
spec.userfacetexcoord = &spec_facetexcoord_;
|
||||||
|
spec.material = &spec_material_;
|
||||||
spec.plugin.plugin_name = &plugin_name;
|
spec.plugin.plugin_name = &plugin_name;
|
||||||
spec.plugin.name = &plugin_instance_name;
|
spec.plugin.name = &plugin_instance_name;
|
||||||
spec.info = &info;
|
spec.info = &info;
|
||||||
@@ -317,6 +318,7 @@ void mjCMesh::CopyFromSpec() {
|
|||||||
content_type_ = spec_content_type_;
|
content_type_ = spec_content_type_;
|
||||||
normal_ = spec_normal_;
|
normal_ = spec_normal_;
|
||||||
face_ = spec_face_;
|
face_ = spec_face_;
|
||||||
|
material_ = spec_material_;
|
||||||
ProcessVertices(spec_vert_);
|
ProcessVertices(spec_vert_);
|
||||||
texcoord_ = spec_texcoord_;
|
texcoord_ = spec_texcoord_;
|
||||||
facetexcoord_ = spec_facetexcoord_;
|
facetexcoord_ = spec_facetexcoord_;
|
||||||
@@ -476,6 +478,7 @@ void mjCMesh::CacheMesh(mjCCache* cache, const mjResource* resource) {
|
|||||||
mesh->polygon_map_ = polygon_map_;
|
mesh->polygon_map_ = polygon_map_;
|
||||||
mesh->surface_ = surface_;
|
mesh->surface_ = surface_;
|
||||||
mesh->volume_ = volume_;
|
mesh->volume_ = volume_;
|
||||||
|
mesh->material_ = material_;
|
||||||
std::copy(boxsz_, boxsz_ + 3, mesh->boxsz_);
|
std::copy(boxsz_, boxsz_ + 3, mesh->boxsz_);
|
||||||
std::copy(aamm_, aamm_ + 6, mesh->aamm_);
|
std::copy(aamm_, aamm_ + 6, mesh->aamm_);
|
||||||
std::copy(pos_, pos_ + 3, mesh->pos_);
|
std::copy(pos_, pos_ + 3, mesh->pos_);
|
||||||
|
|||||||
+17
-15
@@ -1879,7 +1879,23 @@ void mjCModel::IndexAssets(bool discard) {
|
|||||||
for (int i=0; i < geoms_.size(); i++) {
|
for (int i=0; i < geoms_.size(); i++) {
|
||||||
mjCGeom* geom = geoms_[i];
|
mjCGeom* geom = geoms_[i];
|
||||||
|
|
||||||
// find material by name
|
|
||||||
|
// find mesh by name
|
||||||
|
if (!geom->get_meshname().empty()) {
|
||||||
|
mjCMesh* mesh = static_cast<mjCMesh*>(FindObject(mjOBJ_MESH, geom->get_meshname()));
|
||||||
|
if (mesh) {
|
||||||
|
if (!geom->visual_) {
|
||||||
|
mesh->SetNotVisual(); // reset to true by mesh->Compile()
|
||||||
|
}
|
||||||
|
geom->mesh = (discard && geom->visual_) ? nullptr : mesh;
|
||||||
|
mesh->spec.needsdf |= geom->spec.type == mjGEOM_SDF;
|
||||||
|
} else {
|
||||||
|
throw mjCError(geom, "mesh '%s' not found in geom %d", geom->get_meshname().c_str(), i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// find material by name, this has to happen after mesh assignment so that if
|
||||||
|
// the geom does not specify a material but the mesh does it can fall back.
|
||||||
if (!geom->get_material().empty()) {
|
if (!geom->get_material().empty()) {
|
||||||
mjCBase* material = FindObject(mjOBJ_MATERIAL, geom->get_material());
|
mjCBase* material = FindObject(mjOBJ_MATERIAL, geom->get_material());
|
||||||
if (material) {
|
if (material) {
|
||||||
@@ -1889,20 +1905,6 @@ void mjCModel::IndexAssets(bool discard) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// find mesh by name
|
|
||||||
if (!geom->get_meshname().empty()) {
|
|
||||||
mjCBase* mesh = FindObject(mjOBJ_MESH, geom->get_meshname());
|
|
||||||
if (mesh) {
|
|
||||||
if (!geom->visual_) {
|
|
||||||
((mjCMesh*)mesh)->SetNotVisual(); // reset to true by mesh->Compile()
|
|
||||||
}
|
|
||||||
geom->mesh = (discard && geom->visual_) ? nullptr : (mjCMesh*)mesh;
|
|
||||||
static_cast<mjCMesh*>(mesh)->spec.needsdf |= geom->spec.type == mjGEOM_SDF;
|
|
||||||
} else {
|
|
||||||
throw mjCError(geom, "mesh '%s' not found in geom %d", geom->get_meshname().c_str(), i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// find hfield by name
|
// find hfield by name
|
||||||
if (!geom->get_hfieldname().empty()) {
|
if (!geom->get_hfieldname().empty()) {
|
||||||
mjCBase* hfield = FindObject(mjOBJ_HFIELD, geom->get_hfieldname());
|
mjCBase* hfield = FindObject(mjOBJ_HFIELD, geom->get_hfieldname());
|
||||||
|
|||||||
@@ -3365,7 +3365,12 @@ void mjCGeom::ComputeAABB(void) {
|
|||||||
mjuu_copyvec(aabb+3, size, 3);
|
mjuu_copyvec(aabb+3, size, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& mjCGeom::get_material() const {
|
||||||
|
if (mesh && spec_material_.empty()) {
|
||||||
|
return mesh->Material();
|
||||||
|
}
|
||||||
|
return spec_material_;
|
||||||
|
}
|
||||||
|
|
||||||
// compiler
|
// compiler
|
||||||
void mjCGeom::Compile(void) {
|
void mjCGeom::Compile(void) {
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ class mjCSite;
|
|||||||
class mjCCamera;
|
class mjCCamera;
|
||||||
class mjCLight;
|
class mjCLight;
|
||||||
class mjCHField;
|
class mjCHField;
|
||||||
class mjCFlex; // defined in user_mesh.h
|
class mjCFlex;
|
||||||
class mjCMesh; // defined in user_mesh.h
|
class mjCMesh;
|
||||||
class mjCSkin; // defined in user_mesh.h
|
class mjCSkin;
|
||||||
class mjCTexture;
|
class mjCTexture;
|
||||||
class mjCMaterial;
|
class mjCMaterial;
|
||||||
class mjCPair;
|
class mjCPair;
|
||||||
@@ -753,7 +753,7 @@ class mjCGeom : public mjCGeom_, private mjsGeom {
|
|||||||
const std::vector<double>& get_userdata() const { return userdata_; }
|
const std::vector<double>& get_userdata() const { return userdata_; }
|
||||||
const std::string& get_hfieldname() const { return spec_hfieldname_; }
|
const std::string& get_hfieldname() const { return spec_hfieldname_; }
|
||||||
const std::string& get_meshname() const { return spec_meshname_; }
|
const std::string& get_meshname() const { return spec_meshname_; }
|
||||||
const std::string& get_material() const { return spec_material_; }
|
const std::string& get_material() const;
|
||||||
void del_material() { spec_material_.clear(); }
|
void del_material() { spec_material_.clear(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -1029,6 +1029,7 @@ class mjCMesh_ : public mjCBase {
|
|||||||
std::vector<int> face_; // vertex indices
|
std::vector<int> face_; // vertex indices
|
||||||
std::vector<int> facenormal_; // normal indices
|
std::vector<int> facenormal_; // normal indices
|
||||||
std::vector<int> facetexcoord_; // texcoord indices
|
std::vector<int> facetexcoord_; // texcoord indices
|
||||||
|
std::string material_; // mesh fallback material
|
||||||
|
|
||||||
std::string spec_content_type_;
|
std::string spec_content_type_;
|
||||||
std::string spec_file_;
|
std::string spec_file_;
|
||||||
@@ -1038,6 +1039,7 @@ class mjCMesh_ : public mjCBase {
|
|||||||
std::vector<int> spec_face_;
|
std::vector<int> spec_face_;
|
||||||
std::vector<int> spec_facenormal_;
|
std::vector<int> spec_facenormal_;
|
||||||
std::vector<int> spec_facetexcoord_;
|
std::vector<int> spec_facetexcoord_;
|
||||||
|
std::string spec_material_;
|
||||||
|
|
||||||
// used by the compiler
|
// used by the compiler
|
||||||
bool needreorient_; // needs reorientation
|
bool needreorient_; // needs reorientation
|
||||||
@@ -1117,6 +1119,8 @@ class mjCMesh: public mjCMesh_, private mjsMesh {
|
|||||||
const std::vector<int>& Face() const { return face_; }
|
const std::vector<int>& Face() const { return face_; }
|
||||||
const std::vector<int>& UserFace() const { return spec_face_; }
|
const std::vector<int>& UserFace() const { return spec_face_; }
|
||||||
mjtMeshInertia Inertia() const { return spec.inertia; }
|
mjtMeshInertia Inertia() const { return spec.inertia; }
|
||||||
|
const std::string& Material() const { return material_; }
|
||||||
|
|
||||||
// setters
|
// setters
|
||||||
void SetNeedHull(bool needhull) { needhull_ = needhull; }
|
void SetNeedHull(bool needhull) { needhull_ = needhull; }
|
||||||
|
|
||||||
|
|||||||
@@ -230,9 +230,9 @@ const char* MJCF[nMJCF][mjXATTRNUM] = {
|
|||||||
|
|
||||||
{"asset", "*", "0"},
|
{"asset", "*", "0"},
|
||||||
{"<"},
|
{"<"},
|
||||||
{"mesh", "*", "16", "name", "class", "content_type", "file", "vertex", "normal",
|
{"mesh", "*", "17", "name", "class", "content_type", "file", "vertex", "normal",
|
||||||
"texcoord", "face", "refpos", "refquat", "scale", "smoothnormal",
|
"texcoord", "face", "refpos", "refquat", "scale", "smoothnormal",
|
||||||
"maxhullvert", "inertia", "builtin", "params"},
|
"maxhullvert", "inertia", "builtin", "params", "material"},
|
||||||
{"<"},
|
{"<"},
|
||||||
{"plugin", "*", "2", "plugin", "instance"},
|
{"plugin", "*", "2", "plugin", "instance"},
|
||||||
{"<"},
|
{"<"},
|
||||||
@@ -1569,6 +1569,11 @@ void mjXReader::OneMesh(XMLElement* elem, mjsMesh* mesh, const mjVFS* vfs) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string material;
|
||||||
|
if (ReadAttrTxt(elem, "material", material)) {
|
||||||
|
mjs_setString(mesh->material, material.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
// write error info
|
// write error info
|
||||||
mjs_setString(mesh->info, ("line " + std::to_string(elem->GetLineNum())).c_str());
|
mjs_setString(mesh->info, ("line " + std::to_string(elem->GetLineNum())).c_str());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1379,5 +1379,28 @@ TEST_F(MjCMeshTest, SphereSizes) {
|
|||||||
mj_deleteModel(model);
|
mj_deleteModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MjCMeshTest, MeshMaterial) {
|
||||||
|
static constexpr char xml[] = R"(
|
||||||
|
<mujoco model="mesh_material">
|
||||||
|
<asset>
|
||||||
|
<mesh name="h0" builtin="sphere" params="0" material="m1"/>
|
||||||
|
<material name="m0" rgba="1 0 0 1"/>
|
||||||
|
<material name="m1" rgba="1 1 0 1"/>
|
||||||
|
</asset>
|
||||||
|
|
||||||
|
<worldbody>
|
||||||
|
<geom name="g0" type="mesh" mesh="h0"/>
|
||||||
|
<geom name="g1" type="mesh" mesh="h0" material="m0"/>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>
|
||||||
|
)";
|
||||||
|
std::array<char, 1024> error;
|
||||||
|
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||||
|
ASSERT_THAT(model, NotNull()) << error.data();
|
||||||
|
EXPECT_EQ(model->geom_matid[0], 1);
|
||||||
|
EXPECT_EQ(model->geom_matid[1], 0);
|
||||||
|
mj_deleteModel(model);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace mujoco
|
} // namespace mujoco
|
||||||
|
|||||||
Reference in New Issue
Block a user