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
|
||||
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:
|
||||
|
||||
:el-prefix:`mesh/` |-| **plugin** (?)
|
||||
@@ -2553,13 +2558,15 @@ helps clarify the role of bodies and geoms in MuJoCo.
|
||||
.. _body-geom-material:
|
||||
|
||||
:at:`material`: :at-val:`string, optional`
|
||||
If specified, this attribute applies a material to the geom. The material determines the visual properties of the
|
||||
geom. The only exception is color: if the rgba attribute below is different from its internal default, it takes
|
||||
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
|
||||
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.
|
||||
If specified, this attribute applies a material to the geom. Otherwise, if unspecified and the type of the geom is
|
||||
a **mesh** the compiler will apply the mesh asset :ref:`material <asset-mesh-material>` if present.
|
||||
|
||||
The material determines the visual properties of the geom. The only exception is color: if the rgba attribute below
|
||||
is different from its internal default, it takes 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 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:
|
||||
|
||||
|
||||
@@ -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:`material<asset-mesh-material>` | | | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| mesh |br| |_2| |L| | | .. table:: |
|
||||
| :ref:`plugin | \* | :class: mjcf-attributes |
|
||||
|
||||
@@ -31,6 +31,9 @@ General
|
||||
- Added support for shells with a curved reference configuration. See this `example
|
||||
<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
|
||||
^^^
|
||||
- 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* userfacetexcoord; // user texcoord indices
|
||||
mjsPlugin plugin; // sdf plugin
|
||||
mjString* material; // name of material
|
||||
mjString* info; // message appended to compiler errors
|
||||
} mjsMesh;
|
||||
typedef struct mjsHField_ { // height field specification
|
||||
|
||||
@@ -480,6 +480,7 @@ typedef struct mjsMesh_ { // mesh specification
|
||||
mjIntVec* userface; // user vertex indices
|
||||
mjIntVec* userfacetexcoord; // user texcoord indices
|
||||
mjsPlugin plugin; // sdf plugin
|
||||
mjString* material; // name of material
|
||||
mjString* info; // message appended to compiler errors
|
||||
} mjsMesh;
|
||||
|
||||
|
||||
@@ -9353,6 +9353,13 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
type=ValueType(name='mjsPlugin'),
|
||||
doc='sdf plugin',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='material',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjString'),
|
||||
),
|
||||
doc='name of material',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='info',
|
||||
type=PointerType(
|
||||
|
||||
@@ -1478,6 +1478,34 @@ class SpecsTest(absltest.TestCase):
|
||||
self.assertEqual(mj_model.sensor_dim[0], 4)
|
||||
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__':
|
||||
absltest.main()
|
||||
|
||||
@@ -278,6 +278,7 @@ void mjCMesh::PointToLocal() {
|
||||
spec.userface = &spec_face_;
|
||||
spec.usertexcoord = &spec_texcoord_;
|
||||
spec.userfacetexcoord = &spec_facetexcoord_;
|
||||
spec.material = &spec_material_;
|
||||
spec.plugin.plugin_name = &plugin_name;
|
||||
spec.plugin.name = &plugin_instance_name;
|
||||
spec.info = &info;
|
||||
@@ -317,6 +318,7 @@ void mjCMesh::CopyFromSpec() {
|
||||
content_type_ = spec_content_type_;
|
||||
normal_ = spec_normal_;
|
||||
face_ = spec_face_;
|
||||
material_ = spec_material_;
|
||||
ProcessVertices(spec_vert_);
|
||||
texcoord_ = spec_texcoord_;
|
||||
facetexcoord_ = spec_facetexcoord_;
|
||||
@@ -476,6 +478,7 @@ void mjCMesh::CacheMesh(mjCCache* cache, const mjResource* resource) {
|
||||
mesh->polygon_map_ = polygon_map_;
|
||||
mesh->surface_ = surface_;
|
||||
mesh->volume_ = volume_;
|
||||
mesh->material_ = material_;
|
||||
std::copy(boxsz_, boxsz_ + 3, mesh->boxsz_);
|
||||
std::copy(aamm_, aamm_ + 6, mesh->aamm_);
|
||||
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++) {
|
||||
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()) {
|
||||
mjCBase* material = FindObject(mjOBJ_MATERIAL, geom->get_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
|
||||
if (!geom->get_hfieldname().empty()) {
|
||||
mjCBase* hfield = FindObject(mjOBJ_HFIELD, geom->get_hfieldname());
|
||||
|
||||
@@ -3365,7 +3365,12 @@ void mjCGeom::ComputeAABB(void) {
|
||||
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
|
||||
void mjCGeom::Compile(void) {
|
||||
|
||||
@@ -49,9 +49,9 @@ class mjCSite;
|
||||
class mjCCamera;
|
||||
class mjCLight;
|
||||
class mjCHField;
|
||||
class mjCFlex; // defined in user_mesh.h
|
||||
class mjCMesh; // defined in user_mesh.h
|
||||
class mjCSkin; // defined in user_mesh.h
|
||||
class mjCFlex;
|
||||
class mjCMesh;
|
||||
class mjCSkin;
|
||||
class mjCTexture;
|
||||
class mjCMaterial;
|
||||
class mjCPair;
|
||||
@@ -753,7 +753,7 @@ class mjCGeom : public mjCGeom_, private mjsGeom {
|
||||
const std::vector<double>& get_userdata() const { return userdata_; }
|
||||
const std::string& get_hfieldname() const { return spec_hfieldname_; }
|
||||
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(); }
|
||||
|
||||
private:
|
||||
@@ -1029,6 +1029,7 @@ class mjCMesh_ : public mjCBase {
|
||||
std::vector<int> face_; // vertex indices
|
||||
std::vector<int> facenormal_; // normal indices
|
||||
std::vector<int> facetexcoord_; // texcoord indices
|
||||
std::string material_; // mesh fallback material
|
||||
|
||||
std::string spec_content_type_;
|
||||
std::string spec_file_;
|
||||
@@ -1038,6 +1039,7 @@ class mjCMesh_ : public mjCBase {
|
||||
std::vector<int> spec_face_;
|
||||
std::vector<int> spec_facenormal_;
|
||||
std::vector<int> spec_facetexcoord_;
|
||||
std::string spec_material_;
|
||||
|
||||
// used by the compiler
|
||||
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>& UserFace() const { return spec_face_; }
|
||||
mjtMeshInertia Inertia() const { return spec.inertia; }
|
||||
const std::string& Material() const { return material_; }
|
||||
|
||||
// setters
|
||||
void SetNeedHull(bool needhull) { needhull_ = needhull; }
|
||||
|
||||
|
||||
@@ -230,9 +230,9 @@ const char* MJCF[nMJCF][mjXATTRNUM] = {
|
||||
|
||||
{"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",
|
||||
"maxhullvert", "inertia", "builtin", "params"},
|
||||
"maxhullvert", "inertia", "builtin", "params", "material"},
|
||||
{"<"},
|
||||
{"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
|
||||
mjs_setString(mesh->info, ("line " + std::to_string(elem->GetLineNum())).c_str());
|
||||
}
|
||||
|
||||
@@ -1379,5 +1379,28 @@ TEST_F(MjCMeshTest, SphereSizes) {
|
||||
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 mujoco
|
||||
|
||||
Reference in New Issue
Block a user