Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support visualizing mesh collisions with convex decomposition #2352

Merged
merged 9 commits into from
Jun 25, 2024
40 changes: 40 additions & 0 deletions src/Conversions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,24 @@ msgs::Geometry gz::sim::convert(const sdf::Geometry &_in)
meshMsg->set_filename(asFullPath(meshSdf->Uri(), meshSdf->FilePath()));
meshMsg->set_submesh(meshSdf->Submesh());
meshMsg->set_center_submesh(meshSdf->CenterSubmesh());

if (!meshSdf->OptimizationStr().empty())
{
auto header = out.mutable_header()->add_data();
header->set_key("optimization");
header->add_value(meshSdf->OptimizationStr());
}
if (meshSdf->ConvexDecomposition())
{
auto header = out.mutable_header()->add_data();
header->set_key("max_convex_hulls");
header->add_value(std::to_string(
meshSdf->ConvexDecomposition()->MaxConvexHulls()));
header = out.mutable_header()->add_data();
header->set_key("voxel_resolution");
header->add_value(std::to_string(
meshSdf->ConvexDecomposition()->VoxelResolution()));
}
}
else if (_in.Type() == sdf::GeometryType::HEIGHTMAP && _in.HeightmapShape())
{
Expand Down Expand Up @@ -341,6 +359,28 @@ sdf::Geometry gz::sim::convert(const msgs::Geometry &_in)
meshShape.SetSubmesh(_in.mesh().submesh());
meshShape.SetCenterSubmesh(_in.mesh().center_submesh());

sdf::ConvexDecomposition convexDecomp;
bool setConvexDecomp = false;
for (int i = 0; i < _in.header().data_size(); ++i)
{
auto data = _in.header().data(i);
if (data.key() == "optimization" && data.value_size() > 0)
{
meshShape.SetOptimization(data.value(0));
}
if (data.key() == "max_convex_hulls" && data.value_size() > 0)
{
convexDecomp.SetMaxConvexHulls(std::stoul(data.value(0)));
setConvexDecomp = true;
}
if (data.key() == "voxel_resolution" && data.value_size() > 0)
{
convexDecomp.SetVoxelResolution(std::stoul(data.value(0)));
setConvexDecomp = true;
}
}
if (setConvexDecomp)
meshShape.SetConvexDecomposition(convexDecomp);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Could you remove the if and let just meshShape.SetConvexDecomposition(convexDecomp);?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep removed. c74e7c1

out.SetMeshShape(meshShape);
}
else if (_in.type() == msgs::Geometry::HEIGHTMAP && _in.has_heightmap())
Expand Down
19 changes: 19 additions & 0 deletions src/Conversions_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,11 @@ TEST(Conversions, GeometryMesh)
meshShape.SetUri("file://watermelon");
meshShape.SetSubmesh("grape");
meshShape.SetCenterSubmesh(true);
meshShape.SetOptimization("convex_decomposition");
sdf::ConvexDecomposition convexDecomp;
convexDecomp.SetMaxConvexHulls(4);
convexDecomp.SetVoxelResolution(10000);
meshShape.SetConvexDecomposition(convexDecomp);
geometry.SetMeshShape(meshShape);

auto geometryMsg = convert<msgs::Geometry>(geometry);
Expand All @@ -473,6 +478,15 @@ TEST(Conversions, GeometryMesh)
EXPECT_EQ("file://watermelon", geometryMsg.mesh().filename());
EXPECT_EQ("grape", geometryMsg.mesh().submesh());
EXPECT_TRUE(geometryMsg.mesh().center_submesh());
auto header = geometryMsg.header().data(0);
EXPECT_EQ("optimization", header.key());
EXPECT_EQ("convex_decomposition", header.value(0));
header = geometryMsg.header().data(1);
EXPECT_EQ("max_convex_hulls", header.key());
EXPECT_EQ("4", header.value(0));
header = geometryMsg.header().data(2);
EXPECT_EQ("voxel_resolution", header.key());
EXPECT_EQ("10000", header.value(0));

auto newGeometry = convert<sdf::Geometry>(geometryMsg);
EXPECT_EQ(sdf::GeometryType::MESH, newGeometry.Type());
Expand All @@ -481,6 +495,11 @@ TEST(Conversions, GeometryMesh)
EXPECT_EQ("file://watermelon", newGeometry.MeshShape()->Uri());
EXPECT_EQ("grape", newGeometry.MeshShape()->Submesh());
EXPECT_TRUE(newGeometry.MeshShape()->CenterSubmesh());
EXPECT_EQ("convex_decomposition", newGeometry.MeshShape()->OptimizationStr());
auto newConvexDecomp = newGeometry.MeshShape()->ConvexDecomposition();
ASSERT_NE(nullptr, newConvexDecomp);
EXPECT_EQ(4, newConvexDecomp->MaxConvexHulls());
EXPECT_EQ(10000, newConvexDecomp->VoxelResolution());
}

/////////////////////////////////////////////////
Expand Down
42 changes: 35 additions & 7 deletions src/Util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -891,32 +891,60 @@ const common::Mesh *optimizeMesh(const sdf::Mesh &_meshSdf,

auto &meshManager = *common::MeshManager::Instance();
std::size_t maxConvexHulls = 16u;
std::size_t voxelResolution = 200000u;
if (_meshSdf.Optimization() == sdf::MeshOptimization::CONVEX_HULL)
{
/// create 1 convex hull for the whole submesh
maxConvexHulls = 1u;
}
else if (_meshSdf.ConvexDecomposition())
{
// limit max number of convex hulls to generate
// set convex decomposition params: max number of convex hulls
// and voxel resolution
maxConvexHulls = _meshSdf.ConvexDecomposition()->MaxConvexHulls();
voxelResolution = _meshSdf.ConvexDecomposition()->VoxelResolution();
}
// Check if MeshManager contains the decomposed mesh already. If not
// add it to the MeshManager so we do not need to decompose it again.
const std::string convexMeshName =
_mesh.Name() + "_CONVEX_" + std::to_string(maxConvexHulls);
_mesh.Name() + "_" + _meshSdf.Submesh() + "_CONVEX_" +
std::to_string(maxConvexHulls) + "_" + std::to_string(voxelResolution);
auto *optimizedMesh = meshManager.MeshByName(convexMeshName);
if (!optimizedMesh)
{
// Merge meshes before convex decomposition
auto mergedMesh = gz::common::MeshManager::MergeSubMeshes(_mesh);
if (mergedMesh && mergedMesh->SubMeshCount() == 1u)
std::unique_ptr<common::Mesh> meshToDecompose =
std::make_unique<common::Mesh>();
// check if a particular submesh is requested
if (!_meshSdf.Submesh().empty())
{
for (unsigned int submeshIdx = 0;
submeshIdx < _mesh.SubMeshCount();
++submeshIdx)
{
auto submesh = _mesh.SubMeshByIndex(submeshIdx).lock();
if (submesh->Name() == _meshSdf.Submesh())
{
if (_meshSdf.CenterSubmesh())
submesh->Center(math::Vector3d::Zero);
meshToDecompose->AddSubMesh(*submesh.get());
break;
}
}
}
else
{
// Merge meshes before convex decomposition
meshToDecompose =
gz::common::MeshManager::MergeSubMeshes(_mesh);
}

if (meshToDecompose && meshToDecompose->SubMeshCount() == 1u)
{
// Decompose and add mesh to MeshManager
auto mergedSubmesh = mergedMesh->SubMeshByIndex(0u).lock();
auto mergedSubmesh = meshToDecompose->SubMeshByIndex(0u).lock();
std::vector<common::SubMesh> decomposed =
gz::common::MeshManager::ConvexDecomposition(
*mergedSubmesh.get(), maxConvexHulls);
*mergedSubmesh.get(), maxConvexHulls, voxelResolution);
gzdbg << "Optimizing mesh (" << _meshSdf.OptimizationStr() << "): "
<< _mesh.Name() << std::endl;
// Create decomposed mesh and add it to MeshManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <gz/msgs/stringmsg.pb.h>

#include <algorithm>
#include <cstddef>
#include <iostream>
#include <map>
#include <set>
Expand All @@ -39,6 +40,7 @@
#include <gz/common/MeshManager.hh>
#include <gz/common/Profiler.hh>
#include <gz/common/StringUtils.hh>
#include <gz/common/SubMesh.hh>
#include <gz/common/Uuid.hh>

#include <gz/gui/Application.hh>
Expand Down Expand Up @@ -1239,14 +1241,25 @@ rendering::GeometryPtr VisualizationCapabilitiesPrivate::CreateGeometry(

// Assume absolute path to mesh file
descriptor.meshName = fullPath;
descriptor.subMeshName = _geom.MeshShape()->Submesh();
descriptor.centerSubMesh = _geom.MeshShape()->CenterSubmesh();

gz::common::MeshManager *meshManager =
gz::common::MeshManager::Instance();
descriptor.mesh = meshManager->Load(descriptor.meshName);
if (descriptor.mesh)
{
if (_geom.MeshShape()->Optimization() != sdf::MeshOptimization::NONE)
{
const common::Mesh *optimizedMesh =
optimizeMesh(*_geom.MeshShape(), *descriptor.mesh);
if (optimizedMesh)
{
descriptor.mesh = optimizedMesh;
// if submesh is requested, it should be handled in the optimizeMesh
// function so we do not need need to pass these flags to
// gz-rendering
descriptor.subMeshName = "";
descriptor.centerSubMesh = false;
}
}
geom = this->scene->CreateMesh(descriptor);
}
else
Expand Down
Loading