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

Adds new function in MeshManager for performing convex decomposition #583

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions graphics/include/gz/common/MeshManager.hh
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,21 @@ namespace gz
const Mesh *_m2, const int _operation,
const gz::math::Pose3d &_offset = gz::math::Pose3d::Zero);

/// \brief Perform convex decomposition on a submesh.
/// The submesh is decomposed into multiple convex submeshes. The output
/// submeshes contain vertices and indices but texture coordinates
/// not preserved.
/// \param[in] _subMesh Input submesh to decompose.
/// \param[in] _maxConvexHulls Maximum number of convex hull submeshes to
/// produce.
/// \param[in] _voxelResolution Voxel resolution to use. Higher value
/// produces more accurate shapes.
/// \return A vector of decomposed submeshes.
public: std::vector<SubMesh> ConvexDecomposition(
const common::SubMesh *_subMesh,
std::size_t _maxConvexHulls = 16u,
std::size_t _voxelResolution = 200000u);

/// \brief Converts a vector of polylines into a table of vertices and
/// a list of edges (each made of 2 points from the table of vertices.
/// \param[in] _polys the polylines
Expand Down
82 changes: 82 additions & 0 deletions graphics/src/MeshManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
#include <unordered_set>
#include <cctype>

// Suppress warnings for VHACD
#pragma GCC diagnostic ignored "-Wfloat-equal"
#pragma GCC diagnostic ignored "-Wswitch-default"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#define ENABLE_VHACD_IMPLEMENTATION 1
#include "VHACD.h"
#pragma GCC diagnostic pop

#ifndef _WIN32
#include "gz/common/GTSMeshUtils.hh"
#include "gz/common/MeshCSG.hh"
Expand Down Expand Up @@ -1649,3 +1657,77 @@ void MeshManager::SetAssimpEnvs()
this->dataPtr->forceAssimp = true;
}
}

//////////////////////////////////////////////////
std::vector<SubMesh>
MeshManager::ConvexDecomposition(const SubMesh *_subMesh,
std::size_t _maxConvexHulls,
std::size_t _voxelResolution)
{
std::vector<SubMesh> decomposed;
Copy link
Contributor

Choose a reason for hiding this comment

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

include <vector>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

addressed in #585 that targets main


if (!_subMesh)
return decomposed;

auto vertexCount = _subMesh->VertexCount();
auto indexCount = _subMesh->IndexCount();
auto triangleCount = indexCount / 3u;

float *points = new float[vertexCount * 3u];
for (std::size_t i = 0; i < vertexCount; ++i)
{
std::size_t idx = i * 3u;
points[idx] = _subMesh->Vertex(i).X();
points[idx + 1] = _subMesh->Vertex(i).Y();
points[idx + 2] = _subMesh->Vertex(i).Z();
}

uint32_t *indices = new uint32_t[indexCount];
Copy link
Contributor

Choose a reason for hiding this comment

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

include <cstdint>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

addressed in #585 that targets main

for (std::size_t i = 0; i < indexCount; ++i)
{
indices[i] = _subMesh->Index(i);
}

VHACD::IVHACD *iface = VHACD::CreateVHACD();
VHACD::IVHACD::Parameters parameters;
parameters.m_maxConvexHulls = _maxConvexHulls;
parameters.m_resolution = _voxelResolution;
iface->Compute(points, vertexCount, indices, triangleCount, parameters);

if (!iface->GetNConvexHulls())
return decomposed;

for (std::size_t n = 0; n < iface->GetNConvexHulls(); ++n)
{
VHACD::IVHACD::ConvexHull ch;
iface->GetConvexHull(n, ch);

SubMesh convexMesh;
for (std::size_t i = 0u; i < ch.m_points.size(); ++i)
{
const VHACD::Vertex &p = ch.m_points[i];
gz::math::Vector3d vertex(p.mX, p.mY, p.mZ);
convexMesh.AddVertex(vertex);

// add dummy normal
auto norm = vertex;
norm.Normalize();
convexMesh.AddNormal(norm);
}

for (std::size_t i = 0u; i < ch.m_triangles.size(); ++i)
{
const VHACD::Triangle &tri = ch.m_triangles[i];
convexMesh.AddIndex(tri.mI0);
convexMesh.AddIndex(tri.mI1);
convexMesh.AddIndex(tri.mI2);
}
decomposed.push_back(convexMesh);
}

delete [] points;
delete [] indices;
iface->Release();

return decomposed;
}
43 changes: 43 additions & 0 deletions graphics/src/MeshManager_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "gz/common/config.hh"

#include "gz/common/testing/AutoLogFixture.hh"
#include "gz/common/testing/TestPaths.hh"

using namespace gz;

Expand Down Expand Up @@ -289,4 +290,46 @@ TEST_F(MeshManager, Remove)
mgr->RemoveAll();
EXPECT_FALSE(mgr->HasMesh("sphere"));
}

/////////////////////////////////////////////////
TEST_F(MeshManager, ConvexDecomposition)
{
auto mgr = common::MeshManager::Instance();
const common::Mesh *boxMesh = mgr->Load(
common::testing::TestFile("data", "box.dae"));

EXPECT_EQ(1u, boxMesh->SubMeshCount());
auto decomposed = mgr->ConvexDecomposition(
boxMesh->SubMeshByIndex(0u).lock().get());

// Decomposing a box should just produce a box
EXPECT_EQ(1u, decomposed.size());
common::SubMesh &boxSubmesh = decomposed[0];
// A convex hull of a box should contain exactly 8 vertices
EXPECT_EQ(8u, boxSubmesh.VertexCount());
EXPECT_EQ(8u, boxSubmesh.NormalCount());
EXPECT_EQ(36u, boxSubmesh.IndexCount());

std::size_t maxConvexHulls = 4;
std::size_t resolution = 1000;
const common::Mesh *drillMesh = mgr->Load(
common::testing::TestFile("data", "cordless_drill",
"meshes", "cordless_drill.dae"));
EXPECT_EQ(1u, drillMesh->SubMeshCount());
decomposed = mgr->ConvexDecomposition(
drillMesh->SubMeshByIndex(0u).lock().get(), maxConvexHulls, resolution);

// A drill should be decomposed into multiple submeshes
EXPECT_LT(1u, decomposed.size());
EXPECT_GE(maxConvexHulls, decomposed.size());
// Check submeshes are not empty
for (const auto &d : decomposed)
{
const common::SubMesh &drillSubmesh = d;
EXPECT_LT(3u, boxSubmesh.VertexCount());
EXPECT_EQ(drillSubmesh.VertexCount(), drillSubmesh.NormalCount());
EXPECT_LT(3u, drillSubmesh.IndexCount());
}
}

#endif
Loading
Loading