Add a simple mesh hole fixer

master
huxingyi 2020-04-14 21:20:19 +09:30
parent 0c7a07c7b1
commit 5426b1afcc
7 changed files with 163 additions and 4 deletions

View File

@ -135,6 +135,9 @@ include(thirdparty/qtsingleapplication/src/qtsingleapplication.pri)
INCLUDEPATH += src INCLUDEPATH += src
SOURCES += src/fixholes.cpp
HEADERS += src/fixholes.h
SOURCES += src/toonline.cpp SOURCES += src/toonline.cpp
HEADERS += src/toonline.h HEADERS += src/toonline.h

143
src/fixholes.cpp Normal file
View File

@ -0,0 +1,143 @@
#include <unordered_set>
#include <set>
#include <QDebug>
#include <simpleuv/triangulate.h>
#include "fixholes.h"
#include "util.h"
static void buildEdgeToFaceMap(const std::vector<std::vector<size_t>> &faces, std::map<std::pair<size_t, size_t>, 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<QVector3D> &verticies, std::vector<std::vector<size_t>> &faces)
{
std::map<std::pair<size_t, size_t>, size_t> edgeToFaceMap;
buildEdgeToFaceMap(faces, edgeToFaceMap);
std::map<size_t, std::vector<size_t>> holeVertexLink;
std::vector<size_t> 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<std::pair<std::vector<size_t>, double>> holeRings;
auto findRing = [&](size_t startVertex) {
while (true) {
bool foundRing = false;
std::vector<size_t> ring;
std::unordered_set<size_t> visited;
std::set<std::pair<size_t, size_t>> 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<simpleuv::Vertex> 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<simpleuv::Face> 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<std::vector<size_t>> addedFaces;
for (const auto &it: newFaces) {
faces.push_back(std::vector<size_t> {it.indices[0], it.indices[1], it.indices[2]});
//addedFaces.push_back(std::vector<size_t> {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();
}

8
src/fixholes.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef DUST3D_FIX_HOLES_H
#define DUST3D_FIX_HOLES_H
#include <QVector3D>
#include <vector>
void fixHoles(const std::vector<QVector3D> &verticies, std::vector<std::vector<size_t>> &faces);
#endif

View File

@ -25,6 +25,7 @@
#include "meshstroketifier.h" #include "meshstroketifier.h"
#include "fileforever.h" #include "fileforever.h"
#include "snapshotxml.h" #include "snapshotxml.h"
#include "fixholes.h"
MeshGenerator::MeshGenerator(Snapshot *snapshot) : MeshGenerator::MeshGenerator(Snapshot *snapshot) :
m_snapshot(snapshot) m_snapshot(snapshot)
@ -1219,10 +1220,12 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const QString &component
if (isManifold(newTriangles)) { if (isManifold(newTriangles)) {
mesh = new MeshCombiner::Mesh(newVertices, newTriangles, disableSelfIntersectionTest); mesh = new MeshCombiner::Mesh(newVertices, newTriangles, disableSelfIntersectionTest);
} else { } else {
fixHoles(newVertices, newTriangles);
disableSelfIntersectionTest = true; disableSelfIntersectionTest = true;
mesh = new MeshCombiner::Mesh(newVertices, newTriangles, disableSelfIntersectionTest); mesh = new MeshCombiner::Mesh(newVertices, newTriangles, disableSelfIntersectionTest);
} }
} else { } else {
fixHoles(newVertices, newTriangles);
mesh = new MeshCombiner::Mesh(newVertices, newTriangles, disableSelfIntersectionTest); mesh = new MeshCombiner::Mesh(newVertices, newTriangles, disableSelfIntersectionTest);
} }
if (nullptr != mesh) { if (nullptr != mesh) {

View File

@ -48,7 +48,8 @@ bool triangulateFacesWithoutKeepVertices(std::vector<QVector3D> &vertices, const
if (angle >= 1.0 && angle <= 179.0) { if (angle >= 1.0 && angle <= 179.0) {
bool isEar = true; bool isEar = true;
for (size_t x = 0; x < fillRing.size() - 3; ++x) { 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)) { if (pointInTriangle(enter, cone, leave, fourth)) {
isEar = false; isEar = false;
break; break;

View File

@ -232,7 +232,7 @@ void recoverQuads(const std::vector<QVector3D> &vertices, const std::vector<std:
if (sharedQuadEdges.find(pair) != sharedQuadEdges.end()) { if (sharedQuadEdges.find(pair) != sharedQuadEdges.end()) {
auto oppositeEdge = triangleEdgeMap.find(std::make_pair(edge.first.second, edge.first.first)); auto oppositeEdge = triangleEdgeMap.find(std::make_pair(edge.first.second, edge.first.first));
if (oppositeEdge == triangleEdgeMap.end()) { if (oppositeEdge == triangleEdgeMap.end()) {
qDebug() << "Find opposite edge failed"; //qDebug() << "Find opposite edge failed";
} else { } else {
if (unionedFaces.find(oppositeEdge->second.first) == unionedFaces.end()) { if (unionedFaces.find(oppositeEdge->second.first) == unionedFaces.end()) {
unionedFaces.insert(edge.second.first); unionedFaces.insert(edge.second.first);
@ -300,7 +300,7 @@ size_t weldSeam(const std::vector<QVector3D> &sourceVertices, const std::vector<
auto oppositeEdge = std::make_pair(edge.second, edge.first); auto oppositeEdge = std::make_pair(edge.second, edge.first);
auto findOppositeFace = triangleEdgeMap.find(oppositeEdge); auto findOppositeFace = triangleEdgeMap.find(oppositeEdge);
if (findOppositeFace == triangleEdgeMap.end()) { if (findOppositeFace == triangleEdgeMap.end()) {
qDebug() << "Find opposite edge failed"; //qDebug() << "Find opposite edge failed";
continue; continue;
} }
int oppositeFaceIndex = findOppositeFace->second.first; int oppositeFaceIndex = findOppositeFace->second.first;

View File

@ -81,7 +81,8 @@ void triangulate(const std::vector<Vertex> &vertices, std::vector<Face> &faces,
if (angle >= 1.0 && angle <= 179.0) { if (angle >= 1.0 && angle <= 179.0) {
bool isEar = true; bool isEar = true;
for (size_t x = 0; x < fillRing.size() - 3; ++x) { 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)) { if (pointInTriangle(enter, cone, leave, fourth)) {
isEar = false; isEar = false;
break; break;