diff --git a/dust3d.pro b/dust3d.pro index 8aa4f7c4..f60d87d2 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -135,6 +135,9 @@ include(thirdparty/qtsingleapplication/src/qtsingleapplication.pri) INCLUDEPATH += src +SOURCES += src/fixholes.cpp +HEADERS += src/fixholes.h + SOURCES += src/toonline.cpp HEADERS += src/toonline.h diff --git a/src/fixholes.cpp b/src/fixholes.cpp new file mode 100644 index 00000000..be7db33d --- /dev/null +++ b/src/fixholes.cpp @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include "fixholes.h" +#include "util.h" + +static void buildEdgeToFaceMap(const std::vector> &faces, std::map, size_t> &edgeToFaceMap) +{ + edgeToFaceMap.clear(); + for (decltype(faces.size()) index = 0; index < faces.size(); ++index) { + const auto &face = faces[index]; + for (size_t i = 0; i < face.size(); i++) { + size_t j = (i + 1) % face.size(); + edgeToFaceMap[{face[i], face[j]}] = index; + } + } +} + +void fixHoles(const std::vector &verticies, std::vector> &faces) +{ + std::map, size_t> edgeToFaceMap; + buildEdgeToFaceMap(faces, edgeToFaceMap); + + std::map> holeVertexLink; + std::vector startVertices; + for (const auto &face: faces) { + for (size_t i = 0; i < face.size(); i++) { + size_t j = (i + 1) % face.size(); + auto findOppositeFaceResult = edgeToFaceMap.find({face[j], face[i]}); + if (findOppositeFaceResult != edgeToFaceMap.end()) + continue; + holeVertexLink[face[j]].push_back(face[i]); + startVertices.push_back(face[j]); + } + } + + std::vector, double>> holeRings; + auto findRing = [&](size_t startVertex) { + while (true) { + bool foundRing = false; + std::vector ring; + std::unordered_set visited; + std::set> visitedPath; + double ringLength = 0; + while (!foundRing) { + ring.clear(); + visited.clear(); + //ringLength = 0; + auto first = startVertex; + auto index = first; + auto prev = first; + ring.push_back(first); + visited.insert(first); + while (true) { + auto findLinkResult = holeVertexLink.find(index); + if (findLinkResult == holeVertexLink.end()) { + //if (index != first) + // qDebug() << "fixHoles Search ring failed, index:" << index << "first:" << first; + return false; + } + for (const auto &item: findLinkResult->second) { + if (item == first) { + foundRing = true; + break; + } + } + if (foundRing) + break; + if (findLinkResult->second.size() > 1) { + bool foundNewPath = false; + for (const auto &item: findLinkResult->second) { + if (visitedPath.find({prev, item}) == visitedPath.end()) { + index = item; + foundNewPath = true; + break; + } + } + if (!foundNewPath) { + //qDebug() << "fixHoles No new path to try"; + return false; + } + visitedPath.insert({prev, index}); + } else { + index = *findLinkResult->second.begin(); + } + if (visited.find(index) != visited.end()) { + while (index != *ring.begin()) + ring.erase(ring.begin()); + foundRing = true; + break; + } + ring.push_back(index); + visited.insert(index); + //ringLength += (verticies[index] - verticies[prev]).length(); + prev = index; + } + } + if (ring.size() < 3) { + //qDebug() << "fixHoles Ring too short, size:" << ring.size(); + return false; + } + holeRings.push_back({ring, ringLength}); + for (size_t i = 0; i < ring.size(); ++i) { + size_t j = (i + 1) % ring.size(); + auto findLinkResult = holeVertexLink.find(ring[i]); + for (auto it = findLinkResult->second.begin(); it != findLinkResult->second.end(); ++it) { + if (*it == ring[j]) { + findLinkResult->second.erase(it); + if (findLinkResult->second.empty()) + holeVertexLink.erase(ring[i]); + break; + } + } + } + return true; + } + return false; + }; + for (const auto &startVertex: startVertices) + findRing(startVertex); + + std::vector simpleuvVertices(verticies.size()); + for (size_t i = 0; i < simpleuvVertices.size(); ++i) { + const auto &src = verticies[i]; + simpleuvVertices[i] = simpleuv::Vertex {{src.x(), src.y(), src.z()}}; + } + std::vector newFaces; + for (size_t i = 0; i < holeRings.size(); ++i) { + simpleuv::triangulate(simpleuvVertices, newFaces, holeRings[i].first); + } + + //saveAsObj("fixholes_input.obj", verticies, faces); + //std::vector> addedFaces; + for (const auto &it: newFaces) { + faces.push_back(std::vector {it.indices[0], it.indices[1], it.indices[2]}); + //addedFaces.push_back(std::vector {it.indices[0], it.indices[1], it.indices[2]}); + } + //saveAsObj("fixholes_output.obj", verticies, faces); + //saveAsObj("fixholes_added.obj", verticies, addedFaces); + + qDebug() << "fixHoles holeRings:" << holeRings.size() << "newFaces:" << newFaces.size(); +} diff --git a/src/fixholes.h b/src/fixholes.h new file mode 100644 index 00000000..fe099701 --- /dev/null +++ b/src/fixholes.h @@ -0,0 +1,8 @@ +#ifndef DUST3D_FIX_HOLES_H +#define DUST3D_FIX_HOLES_H +#include +#include + +void fixHoles(const std::vector &verticies, std::vector> &faces); + +#endif diff --git a/src/meshgenerator.cpp b/src/meshgenerator.cpp index 94ccdb77..6f702015 100644 --- a/src/meshgenerator.cpp +++ b/src/meshgenerator.cpp @@ -25,6 +25,7 @@ #include "meshstroketifier.h" #include "fileforever.h" #include "snapshotxml.h" +#include "fixholes.h" MeshGenerator::MeshGenerator(Snapshot *snapshot) : m_snapshot(snapshot) @@ -1219,10 +1220,12 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const QString &component if (isManifold(newTriangles)) { mesh = new MeshCombiner::Mesh(newVertices, newTriangles, disableSelfIntersectionTest); } else { + fixHoles(newVertices, newTriangles); disableSelfIntersectionTest = true; mesh = new MeshCombiner::Mesh(newVertices, newTriangles, disableSelfIntersectionTest); } } else { + fixHoles(newVertices, newTriangles); mesh = new MeshCombiner::Mesh(newVertices, newTriangles, disableSelfIntersectionTest); } if (nullptr != mesh) { diff --git a/src/triangulatefaces.cpp b/src/triangulatefaces.cpp index 843c9b58..65d0d34a 100644 --- a/src/triangulatefaces.cpp +++ b/src/triangulatefaces.cpp @@ -48,7 +48,8 @@ bool triangulateFacesWithoutKeepVertices(std::vector &vertices, const if (angle >= 1.0 && angle <= 179.0) { bool isEar = true; for (size_t x = 0; x < fillRing.size() - 3; ++x) { - auto fourth = vertices[(i + 3 + k) % fillRing.size()]; + auto h = (i + 3 + x) % fillRing.size(); + auto fourth = vertexToEigenVector3d(vertices[fillRing[h]]); if (pointInTriangle(enter, cone, leave, fourth)) { isEar = false; break; diff --git a/src/util.cpp b/src/util.cpp index 7e0b9e0a..b150bcf1 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -232,7 +232,7 @@ void recoverQuads(const std::vector &vertices, const std::vectorsecond.first) == unionedFaces.end()) { unionedFaces.insert(edge.second.first); @@ -300,7 +300,7 @@ size_t weldSeam(const std::vector &sourceVertices, const std::vector< auto oppositeEdge = std::make_pair(edge.second, edge.first); auto findOppositeFace = triangleEdgeMap.find(oppositeEdge); if (findOppositeFace == triangleEdgeMap.end()) { - qDebug() << "Find opposite edge failed"; + //qDebug() << "Find opposite edge failed"; continue; } int oppositeFaceIndex = findOppositeFace->second.first; diff --git a/thirdparty/simpleuv/simpleuv/triangulate.cpp b/thirdparty/simpleuv/simpleuv/triangulate.cpp index 1e4b7ea9..ca889fbb 100644 --- a/thirdparty/simpleuv/simpleuv/triangulate.cpp +++ b/thirdparty/simpleuv/simpleuv/triangulate.cpp @@ -81,7 +81,8 @@ void triangulate(const std::vector &vertices, std::vector &faces, if (angle >= 1.0 && angle <= 179.0) { bool isEar = true; for (size_t x = 0; x < fillRing.size() - 3; ++x) { - auto fourth = vertexToEigenVector3d(vertices[(i + 3 + k) % fillRing.size()]); + auto h = (i + 3 + x) % fillRing.size(); + auto fourth = vertexToEigenVector3d(vertices[fillRing[h]]); if (pointInTriangle(enter, cone, leave, fourth)) { isEar = false; break;