diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index b21c2ee01e..43d820b6dd 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include "VHACDUtil.h" @@ -49,7 +50,7 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { reSortFBXGeometryMeshes(result); } catch (const QString& error) { - qDebug() << "Error reading " << filename << ": " << error; + qDebug() << "Error reading" << filename << ":" << error; return false; } @@ -57,13 +58,12 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { } -unsigned int getTrianglesInMeshPart(const FBXMeshPart &meshPart, std::vector& triangles) { +void getTrianglesInMeshPart(const FBXMeshPart &meshPart, std::vector& triangles) { // append all the triangles (and converted quads) from this mesh-part to triangles std::vector meshPartTriangles = meshPart.triangleIndices.toStdVector(); triangles.insert(triangles.end(), meshPartTriangles.begin(), meshPartTriangles.end()); // convert quads to triangles - unsigned int triangleCount = meshPart.triangleIndices.size() / 3; unsigned int quadCount = meshPart.quadIndices.size() / 4; for (unsigned int i = 0; i < quadCount; i++) { unsigned int p0Index = meshPart.quadIndices[i * 4]; @@ -77,10 +77,7 @@ unsigned int getTrianglesInMeshPart(const FBXMeshPart &meshPart, std::vector + struct hash { + std::size_t operator()(const TriangleEdge& edge) const { + return (hash()(edge.indexA) ^ (hash()(edge.indexB) << 1)); + } + }; +} + +// returns false if any edge has only one adjacent triangle +bool isClosedManifold(const std::vector& triangles) { + using EdgeList = std::unordered_map; + 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(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, VHACD::IVHACD::Parameters params, FBXGeometry& result, - int startPartIndex, - int endPartIndex, float minimumMeshSize, float maximumMeshSize) { - qDebug() << "num meshes =" << geometry.meshes.size(); + qDebug() << "meshes =" << geometry.meshes.size(); // count the mesh-parts int numParts = 0; foreach (const FBXMesh& mesh, geometry.meshes) { numParts += mesh.parts.size(); } + qDebug() << "total parts =" << numParts; - VHACD::IVHACD * interfaceVHACD = VHACD::CreateVHACD(); - - if (startPartIndex < 0) { - startPartIndex = 0; - } - if (endPartIndex < 0) { - endPartIndex = numParts; - } - qDebug() << "num parts of interest =" << (endPartIndex - startPartIndex); + VHACD::IVHACD * convexifier = VHACD::CreateVHACD(); result.meshExtents.reset(); result.meshes.append(FBXMesh()); FBXMesh &resultMesh = result.meshes.last(); int meshIndex = 0; - int partIndex = 0; int validPartsFound = 0; foreach (const FBXMesh& mesh, geometry.meshes) { + // find duplicate points + int numDupes = 0; + std::vector 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 std::vector vertices; foreach (glm::vec3 vertex, mesh.vertices) { @@ -224,87 +320,93 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, << " vertices =" << numVertices; ++meshIndex; + std::vector openParts; + + int partIndex = 0; foreach (const FBXMeshPart &meshPart, mesh.parts) { - if (partIndex < startPartIndex || partIndex >= endPartIndex) { + std::vector triangles; + getTrianglesInMeshPart(meshPart, triangles); + + // only process meshes with triangles + if (triangles.size() <= 0) { + qDebug() << " skip part" << partIndex << "(zero triangles)"; ++partIndex; continue; } - std::vector triangles; - unsigned int triangleCount = getTrianglesInMeshPart(meshPart, triangles); - - // only process meshes with triangles - if (triangles.size() <= 0) { - qDebug() << " part" << partIndex << ":"; - qDebug() << " skip (zero triangles)"; - ++partIndex; - continue; + // collapse dupe indices + for (auto& i : triangles) { + i = dupeIndexMap[i]; } AABox aaBox = getAABoxForMeshPart(mesh, meshPart); const float largestDimension = aaBox.getLargestDimension(); - qDebug() << " part" << partIndex << ": " - << " triangles =" << triangleCount - << " largestDimension =" << largestDimension; - if (largestDimension < minimumMeshSize) { - qDebug() << " skip (too small)"; + qDebug() << " skip part" << partIndex << ": dimension =" << largestDimension << "(too small)"; ++partIndex; continue; } if (maximumMeshSize > 0.0f && largestDimension > maximumMeshSize) { - qDebug() << " skip (too large)"; + qDebug() << " skip part" << partIndex << ": dimension =" << largestDimension << "(too large)"; ++partIndex; 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 - bool success = interfaceVHACD->Compute(&vertices[0].x, 3, (uint)numVertices, &triangles[0], 3, triangleCount, params); - if (!success){ - qDebug() << " failed to convexify"; - ++partIndex; - continue; - } - - // 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); + // 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"; } + } else { + qDebug() << " postpone open part" << partIndex; + openParts.push_back(partIndex); } ++partIndex; ++validPartsFound; } + if (! openParts.empty()) { + // combine open meshes in an attempt to produce a closed mesh + + std::vector 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 - interfaceVHACD->Clean(); - interfaceVHACD->Release(); + convexifier->Clean(); + convexifier->Release(); return validPartsFound > 0; } diff --git a/tools/vhacd-util/src/VHACDUtil.h b/tools/vhacd-util/src/VHACDUtil.h index f394301839..62d1779946 100644 --- a/tools/vhacd-util/src/VHACDUtil.h +++ b/tools/vhacd-util/src/VHACDUtil.h @@ -34,7 +34,6 @@ namespace vhacd { bool computeVHACD(FBXGeometry& geometry, VHACD::IVHACD::Parameters params, FBXGeometry& result, - int startPartIndex, int endPartIndex, float minimumMeshSize, float maximumMeshSize); ~VHACDUtil(); }; diff --git a/tools/vhacd-util/src/VHACDUtilApp.cpp b/tools/vhacd-util/src/VHACDUtilApp.cpp index 81eb6b399d..b04bf8e43e 100644 --- a/tools/vhacd-util/src/VHACDUtilApp.cpp +++ b/tools/vhacd-util/src/VHACDUtilApp.cpp @@ -356,7 +356,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : begin = std::chrono::high_resolution_clock::now(); 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(); auto computeDuration = std::chrono::duration_cast(end - begin).count();