mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 14:47:41 +02:00
attempt to close open mesh parts
This commit is contained in:
parent
f8e2cf8064
commit
0647f5b2d0
3 changed files with 174 additions and 73 deletions
|
@ -9,6 +9,7 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include "VHACDUtil.h"
|
#include "VHACDUtil.h"
|
||||||
|
|
||||||
|
@ -49,7 +50,7 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) {
|
||||||
|
|
||||||
reSortFBXGeometryMeshes(result);
|
reSortFBXGeometryMeshes(result);
|
||||||
} catch (const QString& error) {
|
} catch (const QString& error) {
|
||||||
qDebug() << "Error reading " << filename << ": " << error;
|
qDebug() << "Error reading" << filename << ":" << error;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,13 +58,12 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned int getTrianglesInMeshPart(const FBXMeshPart &meshPart, std::vector<int>& triangles) {
|
void getTrianglesInMeshPart(const FBXMeshPart &meshPart, std::vector<int>& triangles) {
|
||||||
// append all the triangles (and converted quads) from this mesh-part to triangles
|
// append all the triangles (and converted quads) from this mesh-part to triangles
|
||||||
std::vector<int> meshPartTriangles = meshPart.triangleIndices.toStdVector();
|
std::vector<int> meshPartTriangles = meshPart.triangleIndices.toStdVector();
|
||||||
triangles.insert(triangles.end(), meshPartTriangles.begin(), meshPartTriangles.end());
|
triangles.insert(triangles.end(), meshPartTriangles.begin(), meshPartTriangles.end());
|
||||||
|
|
||||||
// convert quads to triangles
|
// convert quads to triangles
|
||||||
unsigned int triangleCount = meshPart.triangleIndices.size() / 3;
|
|
||||||
unsigned int quadCount = meshPart.quadIndices.size() / 4;
|
unsigned int quadCount = meshPart.quadIndices.size() / 4;
|
||||||
for (unsigned int i = 0; i < quadCount; i++) {
|
for (unsigned int i = 0; i < quadCount; i++) {
|
||||||
unsigned int p0Index = meshPart.quadIndices[i * 4];
|
unsigned int p0Index = meshPart.quadIndices[i * 4];
|
||||||
|
@ -77,10 +77,7 @@ unsigned int getTrianglesInMeshPart(const FBXMeshPart &meshPart, std::vector<int
|
||||||
triangles.push_back(p0Index);
|
triangles.push_back(p0Index);
|
||||||
triangles.push_back(p2Index);
|
triangles.push_back(p2Index);
|
||||||
triangles.push_back(p3Index);
|
triangles.push_back(p3Index);
|
||||||
triangleCount += 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return triangleCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -177,41 +174,140 @@ AABox getAABoxForMeshPart(const FBXMesh& mesh, const FBXMeshPart &meshPart) {
|
||||||
return aaBox;
|
return aaBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TriangleEdge {
|
||||||
|
int indexA { -1 };
|
||||||
|
int indexB { -1 };
|
||||||
|
TriangleEdge() {}
|
||||||
|
TriangleEdge(int A, int B) : indexA(A), indexB(B) {}
|
||||||
|
bool operator==(const TriangleEdge& other) const {
|
||||||
|
return indexA == other.indexA && indexB == other.indexB;
|
||||||
|
}
|
||||||
|
void sortIndices() {
|
||||||
|
if (indexB < indexA) {
|
||||||
|
int t = indexA;
|
||||||
|
indexA = indexB;
|
||||||
|
indexB = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <>
|
||||||
|
struct hash<TriangleEdge> {
|
||||||
|
std::size_t operator()(const TriangleEdge& edge) const {
|
||||||
|
return (hash<int>()(edge.indexA) ^ (hash<int>()(edge.indexB) << 1));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns false if any edge has only one adjacent triangle
|
||||||
|
bool isClosedManifold(const std::vector<int>& triangles) {
|
||||||
|
using EdgeList = std::unordered_map<TriangleEdge, int>;
|
||||||
|
EdgeList edges;
|
||||||
|
|
||||||
|
// count the triangles for each edge
|
||||||
|
for (size_t i = 0; i < triangles.size(); i += 3) {
|
||||||
|
TriangleEdge edge;
|
||||||
|
for (int j = 0; j < 3; ++j) {
|
||||||
|
edge.indexA = triangles[(int)i + j];
|
||||||
|
edge.indexB = triangles[i + ((j + 1) % 3)];
|
||||||
|
edge.sortIndices();
|
||||||
|
|
||||||
|
EdgeList::iterator edgeEntry = edges.find(edge);
|
||||||
|
if (edgeEntry == edges.end()) {
|
||||||
|
edges.insert(std::pair<TriangleEdge, int>(edge, 1));
|
||||||
|
} else {
|
||||||
|
edgeEntry->second += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// scan for outside edge
|
||||||
|
for (auto& edgeEntry : edges) {
|
||||||
|
if (edgeEntry.second == 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void getConvexResults(VHACD::IVHACD* convexifier, FBXMesh& resultMesh) {
|
||||||
|
// Number of hulls for this input meshPart
|
||||||
|
unsigned int numHulls = convexifier->GetNConvexHulls();
|
||||||
|
qDebug() << " hulls =" << numHulls;
|
||||||
|
|
||||||
|
// create an output meshPart for each convex hull
|
||||||
|
for (unsigned int j = 0; j < numHulls; j++) {
|
||||||
|
VHACD::IVHACD::ConvexHull hull;
|
||||||
|
convexifier->GetConvexHull(j, hull);
|
||||||
|
|
||||||
|
resultMesh.parts.append(FBXMeshPart());
|
||||||
|
FBXMeshPart& resultMeshPart = resultMesh.parts.last();
|
||||||
|
|
||||||
|
int hullIndexStart = resultMesh.vertices.size();
|
||||||
|
for (unsigned int i = 0; i < hull.m_nPoints; i++) {
|
||||||
|
float x = hull.m_points[i * 3];
|
||||||
|
float y = hull.m_points[i * 3 + 1];
|
||||||
|
float z = hull.m_points[i * 3 + 2];
|
||||||
|
resultMesh.vertices.append(glm::vec3(x, y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < hull.m_nTriangles; i++) {
|
||||||
|
int index0 = hull.m_triangles[i * 3] + hullIndexStart;
|
||||||
|
int index1 = hull.m_triangles[i * 3 + 1] + hullIndexStart;
|
||||||
|
int index2 = hull.m_triangles[i * 3 + 2] + hullIndexStart;
|
||||||
|
resultMeshPart.triangleIndices.append(index0);
|
||||||
|
resultMeshPart.triangleIndices.append(index1);
|
||||||
|
resultMeshPart.triangleIndices.append(index2);
|
||||||
|
}
|
||||||
|
qDebug() << " hull" << j << " vertices =" << hull.m_nPoints
|
||||||
|
<< " triangles =" << hull.m_nTriangles
|
||||||
|
<< " FBXMeshVertices =" << resultMesh.vertices.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float computeDt(uint64_t start) {
|
||||||
|
return (float)(usecTimestampNow() - start) / 1.0e6f;
|
||||||
|
}
|
||||||
|
|
||||||
bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry,
|
bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry,
|
||||||
VHACD::IVHACD::Parameters params,
|
VHACD::IVHACD::Parameters params,
|
||||||
FBXGeometry& result,
|
FBXGeometry& result,
|
||||||
int startPartIndex,
|
|
||||||
int endPartIndex,
|
|
||||||
float minimumMeshSize, float maximumMeshSize) {
|
float minimumMeshSize, float maximumMeshSize) {
|
||||||
qDebug() << "num meshes =" << geometry.meshes.size();
|
qDebug() << "meshes =" << geometry.meshes.size();
|
||||||
|
|
||||||
// count the mesh-parts
|
// count the mesh-parts
|
||||||
int numParts = 0;
|
int numParts = 0;
|
||||||
foreach (const FBXMesh& mesh, geometry.meshes) {
|
foreach (const FBXMesh& mesh, geometry.meshes) {
|
||||||
numParts += mesh.parts.size();
|
numParts += mesh.parts.size();
|
||||||
}
|
}
|
||||||
|
qDebug() << "total parts =" << numParts;
|
||||||
|
|
||||||
VHACD::IVHACD * interfaceVHACD = VHACD::CreateVHACD();
|
VHACD::IVHACD * convexifier = VHACD::CreateVHACD();
|
||||||
|
|
||||||
if (startPartIndex < 0) {
|
|
||||||
startPartIndex = 0;
|
|
||||||
}
|
|
||||||
if (endPartIndex < 0) {
|
|
||||||
endPartIndex = numParts;
|
|
||||||
}
|
|
||||||
qDebug() << "num parts of interest =" << (endPartIndex - startPartIndex);
|
|
||||||
|
|
||||||
result.meshExtents.reset();
|
result.meshExtents.reset();
|
||||||
result.meshes.append(FBXMesh());
|
result.meshes.append(FBXMesh());
|
||||||
FBXMesh &resultMesh = result.meshes.last();
|
FBXMesh &resultMesh = result.meshes.last();
|
||||||
|
|
||||||
int meshIndex = 0;
|
int meshIndex = 0;
|
||||||
int partIndex = 0;
|
|
||||||
int validPartsFound = 0;
|
int validPartsFound = 0;
|
||||||
foreach (const FBXMesh& mesh, geometry.meshes) {
|
foreach (const FBXMesh& mesh, geometry.meshes) {
|
||||||
|
|
||||||
|
// find duplicate points
|
||||||
|
int numDupes = 0;
|
||||||
|
std::vector<int> dupeIndexMap;
|
||||||
|
dupeIndexMap.reserve(mesh.vertices.size());
|
||||||
|
for (int i = 0; i < mesh.vertices.size(); ++i) {
|
||||||
|
dupeIndexMap.push_back(i);
|
||||||
|
for (int j = 0; j < i; ++j) {
|
||||||
|
float distance = glm::distance(mesh.vertices[i], mesh.vertices[j]);
|
||||||
|
if (distance < 0.0001f) {
|
||||||
|
dupeIndexMap[i] = j;
|
||||||
|
++numDupes;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// each mesh has its own transform to move it to model-space
|
// each mesh has its own transform to move it to model-space
|
||||||
std::vector<glm::vec3> vertices;
|
std::vector<glm::vec3> vertices;
|
||||||
foreach (glm::vec3 vertex, mesh.vertices) {
|
foreach (glm::vec3 vertex, mesh.vertices) {
|
||||||
|
@ -224,87 +320,93 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry,
|
||||||
<< " vertices =" << numVertices;
|
<< " vertices =" << numVertices;
|
||||||
++meshIndex;
|
++meshIndex;
|
||||||
|
|
||||||
|
std::vector<int> openParts;
|
||||||
|
|
||||||
|
int partIndex = 0;
|
||||||
foreach (const FBXMeshPart &meshPart, mesh.parts) {
|
foreach (const FBXMeshPart &meshPart, mesh.parts) {
|
||||||
if (partIndex < startPartIndex || partIndex >= endPartIndex) {
|
std::vector<int> triangles;
|
||||||
|
getTrianglesInMeshPart(meshPart, triangles);
|
||||||
|
|
||||||
|
// only process meshes with triangles
|
||||||
|
if (triangles.size() <= 0) {
|
||||||
|
qDebug() << " skip part" << partIndex << "(zero triangles)";
|
||||||
++partIndex;
|
++partIndex;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> triangles;
|
// collapse dupe indices
|
||||||
unsigned int triangleCount = getTrianglesInMeshPart(meshPart, triangles);
|
for (auto& i : triangles) {
|
||||||
|
i = dupeIndexMap[i];
|
||||||
// only process meshes with triangles
|
|
||||||
if (triangles.size() <= 0) {
|
|
||||||
qDebug() << " part" << partIndex << ":";
|
|
||||||
qDebug() << " skip (zero triangles)";
|
|
||||||
++partIndex;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AABox aaBox = getAABoxForMeshPart(mesh, meshPart);
|
AABox aaBox = getAABoxForMeshPart(mesh, meshPart);
|
||||||
const float largestDimension = aaBox.getLargestDimension();
|
const float largestDimension = aaBox.getLargestDimension();
|
||||||
|
|
||||||
qDebug() << " part" << partIndex << ": "
|
|
||||||
<< " triangles =" << triangleCount
|
|
||||||
<< " largestDimension =" << largestDimension;
|
|
||||||
|
|
||||||
if (largestDimension < minimumMeshSize) {
|
if (largestDimension < minimumMeshSize) {
|
||||||
qDebug() << " skip (too small)";
|
qDebug() << " skip part" << partIndex << ": dimension =" << largestDimension << "(too small)";
|
||||||
++partIndex;
|
++partIndex;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maximumMeshSize > 0.0f && largestDimension > maximumMeshSize) {
|
if (maximumMeshSize > 0.0f && largestDimension > maximumMeshSize) {
|
||||||
qDebug() << " skip (too large)";
|
qDebug() << " skip part" << partIndex << ": dimension =" << largestDimension << "(too large)";
|
||||||
++partIndex;
|
++partIndex;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// figure out if the mesh is a closed manifold or not
|
||||||
|
bool closed = isClosedManifold(triangles);
|
||||||
|
if (closed) {
|
||||||
|
unsigned int triangleCount = triangles.size() / 3;
|
||||||
|
qDebug() << " process closed part" << partIndex << ": "
|
||||||
|
<< " triangles =" << triangleCount;
|
||||||
|
|
||||||
// compute approximate convex decomposition
|
// compute approximate convex decomposition
|
||||||
bool success = interfaceVHACD->Compute(&vertices[0].x, 3, (uint)numVertices, &triangles[0], 3, triangleCount, params);
|
bool success = convexifier->Compute(&vertices[0].x, 3, (uint)numVertices, &triangles[0], 3, triangleCount, params);
|
||||||
if (!success){
|
if (success) {
|
||||||
qDebug() << " failed to convexify";
|
getConvexResults(convexifier, resultMesh);
|
||||||
++partIndex;
|
} else {
|
||||||
continue;
|
qDebug() << " failed to convexify";
|
||||||
}
|
|
||||||
|
|
||||||
// Number of hulls for this input meshPart
|
|
||||||
unsigned int nConvexHulls = interfaceVHACD->GetNConvexHulls();
|
|
||||||
|
|
||||||
// create an output meshPart for each convex hull
|
|
||||||
for (unsigned int j = 0; j < nConvexHulls; j++) {
|
|
||||||
VHACD::IVHACD::ConvexHull hull;
|
|
||||||
interfaceVHACD->GetConvexHull(j, hull);
|
|
||||||
|
|
||||||
resultMesh.parts.append(FBXMeshPart());
|
|
||||||
FBXMeshPart &resultMeshPart = resultMesh.parts.last();
|
|
||||||
|
|
||||||
int hullIndexStart = resultMesh.vertices.size();
|
|
||||||
for (unsigned int i = 0; i < hull.m_nPoints; i++) {
|
|
||||||
float x = hull.m_points[i * 3];
|
|
||||||
float y = hull.m_points[i * 3 + 1];
|
|
||||||
float z = hull.m_points[i * 3 + 2];
|
|
||||||
resultMesh.vertices.append(glm::vec3(x, y, z));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < hull.m_nTriangles; i++) {
|
|
||||||
int index0 = hull.m_triangles[i * 3] + hullIndexStart;
|
|
||||||
int index1 = hull.m_triangles[i * 3 + 1] + hullIndexStart;
|
|
||||||
int index2 = hull.m_triangles[i * 3 + 2] + hullIndexStart;
|
|
||||||
resultMeshPart.triangleIndices.append(index0);
|
|
||||||
resultMeshPart.triangleIndices.append(index1);
|
|
||||||
resultMeshPart.triangleIndices.append(index2);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << " postpone open part" << partIndex;
|
||||||
|
openParts.push_back(partIndex);
|
||||||
}
|
}
|
||||||
++partIndex;
|
++partIndex;
|
||||||
++validPartsFound;
|
++validPartsFound;
|
||||||
}
|
}
|
||||||
|
if (! openParts.empty()) {
|
||||||
|
// combine open meshes in an attempt to produce a closed mesh
|
||||||
|
|
||||||
|
std::vector<int> triangles;
|
||||||
|
for (auto index : openParts) {
|
||||||
|
const FBXMeshPart &meshPart = mesh.parts[index];
|
||||||
|
getTrianglesInMeshPart(meshPart, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// collapse dupe indices
|
||||||
|
for (auto& i : triangles) {
|
||||||
|
i = dupeIndexMap[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// this time we don't care if the parts are close or not
|
||||||
|
unsigned int triangleCount = triangles.size() / 3;
|
||||||
|
qDebug() << " process remaining open parts =" << openParts.size() << ": "
|
||||||
|
<< " triangles =" << triangleCount;
|
||||||
|
|
||||||
|
// compute approximate convex decomposition
|
||||||
|
bool success = convexifier->Compute(&vertices[0].x, 3, (uint)numVertices, &triangles[0], 3, triangleCount, params);
|
||||||
|
if (success) {
|
||||||
|
getConvexResults(convexifier, resultMesh);
|
||||||
|
} else {
|
||||||
|
qDebug() << " failed to convexify";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//release memory
|
//release memory
|
||||||
interfaceVHACD->Clean();
|
convexifier->Clean();
|
||||||
interfaceVHACD->Release();
|
convexifier->Release();
|
||||||
|
|
||||||
return validPartsFound > 0;
|
return validPartsFound > 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ namespace vhacd {
|
||||||
bool computeVHACD(FBXGeometry& geometry,
|
bool computeVHACD(FBXGeometry& geometry,
|
||||||
VHACD::IVHACD::Parameters params,
|
VHACD::IVHACD::Parameters params,
|
||||||
FBXGeometry& result,
|
FBXGeometry& result,
|
||||||
int startPartIndex, int endPartIndex,
|
|
||||||
float minimumMeshSize, float maximumMeshSize);
|
float minimumMeshSize, float maximumMeshSize);
|
||||||
~VHACDUtil();
|
~VHACDUtil();
|
||||||
};
|
};
|
||||||
|
|
|
@ -356,7 +356,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
begin = std::chrono::high_resolution_clock::now();
|
begin = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
FBXGeometry result;
|
FBXGeometry result;
|
||||||
bool success = vUtil.computeVHACD(fbx, params, result, startMeshIndex, endMeshIndex, minimumMeshSize, maximumMeshSize);
|
bool success = vUtil.computeVHACD(fbx, params, result, minimumMeshSize, maximumMeshSize);
|
||||||
|
|
||||||
end = std::chrono::high_resolution_clock::now();
|
end = std::chrono::high_resolution_clock::now();
|
||||||
auto computeDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
|
auto computeDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
|
||||||
|
|
Loading…
Reference in a new issue