Skip to content

Commit 3ce5cf1

Browse files
authored
Merge pull request #2537 from JafarAbdi/feature/mjcf_mesh_with_vertices
Add support for vertex-based mesh definitions in MJCF
2 parents ecc688d + 01a36dd commit 3ce5cf1

File tree

5 files changed

+99
-10
lines changed

5 files changed

+99
-10
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
88

99
### Fixed
1010
- Fix mjcf Euler angle parsing: use xyz as a default value for eulerseq compiler option ([#2526](https://github.com/stack-of-tasks/pinocchio/pull/2526))
11+
- Add parsing meshes with vertices for MJCF format ([#2537](https://github.com/stack-of-tasks/pinocchio/pull/2537))
1112

1213
## [3.3.1] - 2024-12-13
1314

include/pinocchio/parsers/mjcf/mjcf-graph.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ namespace pinocchio
209209
Eigen::Vector3d scale = Eigen::Vector3d::Constant(1);
210210
// Path to the mesh file
211211
std::string filePath;
212+
// Vertices of the mesh
213+
Eigen::MatrixX3d vertices;
212214
};
213215

214216
/// @brief All informations related to a texture are stored here

src/parsers/mjcf/mjcf-graph-geom.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,19 @@ namespace pinocchio
7575
if (geom.geomType == "mesh")
7676
{
7777
MjcfMesh currentMesh = currentGraph.mapOfMeshes.at(geom.meshName);
78+
if (currentMesh.vertices.size() > 0)
79+
{
80+
auto vertices = currentMesh.vertices;
81+
// Scale vertices
82+
for (std::size_t i = 0; i < vertices.rows(); ++i)
83+
vertices.row(i) = vertices.row(i).cwiseProduct(currentMesh.scale.transpose());
84+
auto model = std::make_shared<hpp::fcl::BVHModel<fcl::OBBRSS>>();
85+
model->beginModel();
86+
model->addVertices(vertices);
87+
model->endModel();
88+
model->buildConvexHull(true, "Qt");
89+
return model->convex;
90+
}
7891
meshPath = currentMesh.filePath;
7992
meshScale = currentMesh.scale;
8093
hpp::fcl::BVHModelPtr_t bvh = meshLoader->load(meshPath, meshScale);

src/parsers/mjcf/mjcf-graph.cpp

+42-10
Original file line numberDiff line numberDiff line change
@@ -582,20 +582,52 @@ namespace pinocchio
582582

583583
MjcfMesh mesh;
584584
auto file = el.get_optional<std::string>("<xmlattr>.file");
585-
if (!file)
586-
throw std::invalid_argument("Only meshes with files are supported");
587-
588-
fs::path filePath(*file);
589-
std::string name = getName(el, filePath);
590-
591-
mesh.filePath =
592-
updatePath(compilerInfo.strippath, compilerInfo.meshdir, modelPath, filePath).string();
593-
594585
auto scale = el.get_optional<std::string>("<xmlattr>.scale");
595586
if (scale)
596587
mesh.scale = internal::getVectorFromStream<3>(*scale);
588+
if (file)
589+
{
590+
fs::path filePath(*file);
591+
std::string name = getName(el, filePath);
592+
593+
mesh.filePath =
594+
updatePath(compilerInfo.strippath, compilerInfo.meshdir, modelPath, filePath).string();
595+
mapOfMeshes.insert(std::make_pair(name, mesh));
596+
return;
597+
}
598+
599+
// Handle vertex-based mesh
600+
auto vertex = el.get_optional<std::string>("<xmlattr>.vertex");
601+
if (!vertex)
602+
{
603+
PINOCCHIO_THROW_PRETTY(
604+
std::invalid_argument, "Only meshes with files/vertices are supported.")
605+
}
606+
607+
auto name = el.get_optional<std::string>("<xmlattr>.name");
608+
if (!name)
609+
{
610+
PINOCCHIO_THROW_PRETTY(
611+
std::invalid_argument, "Mesh with vertices without a name is not supported");
612+
}
597613

598-
mapOfMeshes.insert(std::make_pair(name, mesh));
614+
// Parse and validate vertices
615+
Eigen::VectorXd meshVertices = internal::getUnknownSizeVectorFromStream(*vertex);
616+
if (meshVertices.size() % 3 != 0)
617+
{
618+
PINOCCHIO_THROW_PRETTY(
619+
std::invalid_argument, "Number of vertices is not a multiple of 3");
620+
}
621+
622+
// Convert to 3D vertex matrix
623+
const auto numVertices = meshVertices.size() / 3;
624+
Eigen::MatrixX3d vertices(numVertices, 3);
625+
for (auto i = 0; i < numVertices; ++i)
626+
{
627+
vertices.row(i) = meshVertices.segment<3>(3 * i).transpose();
628+
}
629+
mesh.vertices = vertices;
630+
mapOfMeshes.insert(std::make_pair(*name, mesh));
599631
}
600632

601633
void MjcfGraph::parseAsset(const ptree & el)

unittest/mjcf.cpp

+41
Original file line numberDiff line numberDiff line change
@@ -1357,4 +1357,45 @@ BOOST_AUTO_TEST_CASE(test_default_eulerseq)
13571357
BOOST_CHECK(graph.mapOfBodies["body"].bodyPlacement.isApprox(placement));
13581358
}
13591359

1360+
/// @brief Test parsing a mesh with vertices
1361+
/// @param
1362+
BOOST_AUTO_TEST_CASE(parse_mesh_with_vertices)
1363+
{
1364+
std::istringstream xmlDataNoStrip(R"(<mujoco model="parseVertices">
1365+
<asset>
1366+
<mesh name="chasis" scale=".01 .006 .0015"
1367+
vertex=" 9 2 0
1368+
-10 10 10
1369+
9 -2 0
1370+
10 3 -10
1371+
10 -3 -10
1372+
-8 10 -10
1373+
-10 -10 10
1374+
-8 -10 -10
1375+
-5 0 20"/>
1376+
</asset>
1377+
</mujoco>)");
1378+
1379+
auto namefile = createTempFile(xmlDataNoStrip);
1380+
1381+
typedef ::pinocchio::mjcf::details::MjcfGraph MjcfGraph;
1382+
pinocchio::Model model_m;
1383+
MjcfGraph::UrdfVisitor visitor(model_m);
1384+
1385+
MjcfGraph graph(visitor, "/fakeMjcf/fake.xml");
1386+
graph.parseGraphFromXML(namefile.name());
1387+
1388+
// Test Meshes
1389+
pinocchio::mjcf::details::MjcfMesh mesh = graph.mapOfMeshes.at("chasis");
1390+
BOOST_CHECK_EQUAL(mesh.scale, Eigen::Vector3d(0.01, 0.006, 0.0015));
1391+
Eigen::MatrixX3d vertices(9, 3);
1392+
vertices << 9, 2, 0, -10, 10, 10, 9, -2, 0, 10, 3, -10, 10, -3, -10, -8, 10, -10, -10, -10, 10,
1393+
-8, -10, -10, -5, 0, 20;
1394+
BOOST_CHECK_EQUAL(mesh.vertices.rows(), 9);
1395+
for (auto i = 0; i < mesh.vertices.rows(); ++i)
1396+
{
1397+
BOOST_CHECK(mesh.vertices.row(i) == vertices.row(i));
1398+
}
1399+
}
1400+
13601401
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)