Add a simple mesh hole fixer
parent
0c7a07c7b1
commit
5426b1afcc
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue