Disallow repeated nameless assets that use the same file, except for URDFs.

PiperOrigin-RevId: 675114055
Change-Id: I142811ac2d8ef9e12bb8a06d5f3a2fdac02946da
This commit is contained in:
Alessio Quaglino
2024-09-16 05:49:45 -07:00
committed by Copybara-Service
parent 60a1921b34
commit 015bdeb756
6 changed files with 103 additions and 46 deletions
-11
View File
@@ -1431,7 +1431,6 @@ void mjCModel::IndexAssets(bool discard) {
template <typename T>
void mjCModel::SetDefaultNames(std::vector<T*>& assets) {
string stripped;
std::map<string, std::vector<int>> names;
// use filename if name is missing
for (int i=0; i<assets.size(); i++) {
@@ -1439,16 +1438,6 @@ void mjCModel::SetDefaultNames(std::vector<T*>& assets) {
if (assets[i]->name.empty()) {
stripped = mjuu_strippath(assets[i]->File());
assets[i]->name = mjuu_stripext(stripped);
names[assets[i]->name].push_back(i);
}
}
// add suffix if duplicates
for (auto const& [name, indices] : names) {
if (indices.size() > 1) {
for (int i=0; i<indices.size(); i++) {
assets[indices[i]]->name += "_" + std::to_string(i);
}
}
}
}
+33 -22
View File
@@ -575,6 +575,9 @@ mjsGeom* mjXURDF::Geom(XMLElement* geom_elem, mjsBody* pbody, bool collision) {
// mesh
else if ((temp = FindSubElem(elem, "mesh"))) {
mjsMesh* pmesh = 0;
bool newmesh = false;
// set geom type and read mesh attributes
pgeom->type = mjGEOM_MESH;
meshfile = ReadAttrStr(temp, "filename", true).value();
@@ -591,35 +594,43 @@ mjsGeom* mjXURDF::Geom(XMLElement* geom_elem, mjsBody* pbody, bool collision) {
std::string meshname = mjuu_strippath(meshfile);
meshname = mjuu_stripext(meshname);
// look for existing mesh
mjsMesh* mesh = mjs_asMesh(mjs_findElement(spec, mjOBJ_MESH, meshname.c_str()));
mjsMesh* pmesh = 0;
// does not exist: create
if (!mesh) {
if (meshes.find(meshname) == meshes.end()) {
// does not exist: create
pmesh = mjs_addMesh(spec, 0);
}
meshes[meshname].push_back(pmesh);
newmesh = true;
} else {
int i = 0;
// exists with different scale: append name with '1', create
else if (mesh->scale[0]!=meshscale[0] ||
mesh->scale[1]!=meshscale[1] ||
mesh->scale[2]!=meshscale[2]) {
pmesh = mjs_addMesh(spec, 0);
meshname = meshname + "1";
}
// find if it exists with the same scale
for (mjsMesh* mesh : meshes[meshname]) {
if (mesh->scale[0] == meshscale[0] &&
mesh->scale[1] == meshscale[1] &&
mesh->scale[2] == meshscale[2]) {
pmesh = mesh;
break;
}
i++;
}
// point to already existing spec
else {
pmesh = mesh;
// add a new spec making an incremental new name
if (i == meshes[meshname].size()) {
pmesh = mjs_addMesh(spec, 0);
meshes[meshname].push_back(pmesh);
meshname = meshname + std::to_string(i);
newmesh = true;
}
}
// set fields
mjs_setString(pmesh->file, meshfile.c_str());
mjs_setString(pmesh->name, meshname.c_str());
if (newmesh) {
mjs_setString(pmesh->file, meshfile.c_str());
mjs_setString(pmesh->name, meshname.c_str());
pmesh->scale[0] = meshscale[0];
pmesh->scale[1] = meshscale[1];
pmesh->scale[2] = meshscale[2];
}
mjs_setString(pgeom->meshname, meshname.c_str());
pmesh->scale[0] = meshscale[0];
pmesh->scale[1] = meshscale[1];
pmesh->scale[2] = meshscale[2];
}
else {
+2
View File
@@ -15,6 +15,7 @@
#ifndef THIRD_PARTY_MUJOCO_SRC_XML_XML_URDF_
#define THIRD_PARTY_MUJOCO_SRC_XML_XML_URDF_
#include <map>
#include <string>
#include <unordered_set>
#include <vector>
@@ -66,6 +67,7 @@ class mjXURDF : public mjXBase {
std::vector<std::string> urMat; // material name
std::vector<mjRGBA> urRGBA; // material RBG value
std::unordered_set<std::string> urGeomNames; // geom name
std::map<std::string, std::vector<mjsMesh*>> meshes; // map from name to mjsMesh
std::string urPrefix; // prefix to apply to all names
};
+3 -11
View File
@@ -280,22 +280,14 @@ TEST_F(UserDataTest, AutoNUserSensor) {
// ------------- test duplicate names ------------------------------------------
TEST_F(UserDataTest, DuplicateNames) {
static const char* const kFilePath = "user/testdata/load_twice.xml";
static const char* const kFilePath = "user/testdata/malformed_duplicated.xml";
const std::string xml_path = GetTestDataFilePath(kFilePath);
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
EXPECT_THAT(m, NotNull()) << error.data();
EXPECT_THAT(m->nmesh, 2);
for (int i = 0; i < m->nmesh; i++) {
char mesh_name[mjMAXUINAME] = "";
util::strcat_arr(mesh_name, m->names + m->name_meshadr[i]);
EXPECT_THAT(std::string(mesh_name), "cube_" + std::to_string(i));
}
mj_deleteModel(m);
EXPECT_THAT(m, IsNull());
EXPECT_STREQ(error.data(), "Error: repeated name 'cube' in mesh");
}
// ------------- test fusestatic -----------------------------------------------
+65 -2
View File
@@ -15,8 +15,6 @@
// Tests for xml/xml_api.cc.
#include <array>
#include <cstddef>
#include <cstring>
#include <string>
#include <vector>
@@ -28,6 +26,7 @@
namespace mujoco {
namespace {
using ::testing::IsNull;
using ::testing::NotNull;
// ---------------------------- test capsule --------------------------------
@@ -252,5 +251,69 @@ TEST_F(MujocoTest, ReadsJointTypes) {
mj_deleteModel(model);
}
TEST_F(MujocoTest, RepeatedMeshName) {
static constexpr char urdf[] = R"(
<robot name="">
<mujoco>
<compiler discardvisual="false"/>
</mujoco>
<link name="geom1">
<visual name="vis1">
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="mesh.obj" scale="1 1 1"/>
</geometry>
</visual>
</link>
<link name="geom2">
<visual name="vis2">
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="mesh.obj" scale="2 2 2"/>
</geometry>
</visual>
</link>
<link name="geom3">
<visual name="vis3">
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="mesh.obj" scale="3 3 3"/>
</geometry>
</visual>
</link>
<link name="geom4">
<visual name="vis4">
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="mesh.obj" scale="2 2 2"/>
</geometry>
</visual>
</link>
</robot>
)";
std::array<char, 1000> error;
mjSpec* spec = mj_parseXMLString(urdf, 0, error.data(), error.size());
EXPECT_THAT(spec, NotNull()) << error.data();
mjsMesh* mesh = mjs_asMesh(mjs_findElement(spec, mjOBJ_MESH, "mesh"));
mjsMesh* mesh1 = mjs_asMesh(mjs_findElement(spec, mjOBJ_MESH, "mesh1"));
mjsMesh* mesh2 = mjs_asMesh(mjs_findElement(spec, mjOBJ_MESH, "mesh2"));
mjsMesh* mesh3 = mjs_asMesh(mjs_findElement(spec, mjOBJ_MESH, "mesh3"));
EXPECT_THAT(mesh, NotNull());
EXPECT_THAT(mesh1, NotNull());
EXPECT_THAT(mesh2, NotNull());
EXPECT_THAT(mesh3, IsNull());
EXPECT_STREQ(mjs_getString(mesh->name), "mesh");
EXPECT_STREQ(mjs_getString(mesh1->name), "mesh1");
EXPECT_STREQ(mjs_getString(mesh2->name), "mesh2");
mj_deleteSpec(spec);
}
} // namespace
} // namespace mujoco