2020-04-14 11:50:19 +00:00
|
|
|
#include <unordered_set>
|
|
|
|
#include <set>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <simpleuv/triangulate.h>
|
2020-04-14 21:57:01 +00:00
|
|
|
#include <unordered_set>
|
|
|
|
#include <unordered_map>
|
2020-04-14 11:50:19 +00:00
|
|
|
#include "fixholes.h"
|
|
|
|
#include "util.h"
|
|
|
|
|
2020-04-14 21:57:01 +00:00
|
|
|
void fixHoles(const std::vector<QVector3D> &verticies, std::vector<std::vector<size_t>> &faces)
|
2020-04-14 11:50:19 +00:00
|
|
|
{
|
2020-04-14 21:57:01 +00:00
|
|
|
std::map<std::pair<size_t, size_t>, std::unordered_set<size_t>> edgeToFaceSetMap;
|
|
|
|
|
2020-04-14 11:50:19 +00:00
|
|
|
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();
|
2020-04-14 21:57:01 +00:00
|
|
|
edgeToFaceSetMap[{face[i], face[j]}].insert(index);
|
2020-04-14 11:50:19 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-14 21:57:01 +00:00
|
|
|
|
|
|
|
std::unordered_set<size_t> invalidFaces;
|
|
|
|
|
2020-04-14 11:50:19 +00:00
|
|
|
std::map<std::pair<size_t, size_t>, size_t> edgeToFaceMap;
|
2020-04-14 21:57:01 +00:00
|
|
|
for (const auto &it: edgeToFaceSetMap) {
|
|
|
|
if (1 == it.second.size()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (const auto &loopFaceIndex: it.second) {
|
|
|
|
invalidFaces.insert(loopFaceIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const auto &it: edgeToFaceSetMap) {
|
|
|
|
if (1 == it.second.size()) {
|
|
|
|
size_t index = *it.second.begin();
|
|
|
|
if (invalidFaces.find(index) == invalidFaces.end()) {
|
|
|
|
edgeToFaceMap.insert({it.first, index});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-04-14 11:50:19 +00:00
|
|
|
|
2020-04-14 23:03:23 +00:00
|
|
|
std::unordered_set<size_t> newInvalidFaces;
|
|
|
|
for (size_t index = 0; index < faces.size(); ++index) {
|
|
|
|
if (invalidFaces.find(index) != invalidFaces.end())
|
|
|
|
continue;
|
|
|
|
const auto &face = faces[index];
|
|
|
|
size_t validNeighborNum = 0;
|
|
|
|
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())
|
|
|
|
++validNeighborNum;
|
|
|
|
}
|
|
|
|
if (validNeighborNum < face.size() - validNeighborNum) {
|
|
|
|
invalidFaces.insert(index);
|
|
|
|
newInvalidFaces.insert(index);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto &index: newInvalidFaces) {
|
|
|
|
const auto &face = faces[index];
|
|
|
|
for (size_t i = 0; i < face.size(); i++) {
|
|
|
|
size_t j = (i + 1) % face.size();
|
|
|
|
edgeToFaceMap.erase({face[i], face[j]});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-14 11:50:19 +00:00
|
|
|
std::map<size_t, std::vector<size_t>> holeVertexLink;
|
|
|
|
std::vector<size_t> startVertices;
|
2020-04-14 21:57:01 +00:00
|
|
|
for (size_t index = 0; index < faces.size(); ++index) {
|
|
|
|
if (invalidFaces.find(index) != invalidFaces.end())
|
|
|
|
continue;
|
|
|
|
const auto &face = faces[index];
|
2020-04-14 11:50:19 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-04-15 07:00:04 +00:00
|
|
|
//saveAsObj("fixholes_input.obj", verticies, faces);
|
2020-04-14 11:50:19 +00:00
|
|
|
//std::vector<std::vector<size_t>> addedFaces;
|
2020-04-14 21:57:01 +00:00
|
|
|
//std::vector<std::vector<size_t>> removedFaces;
|
|
|
|
std::vector<std::vector<size_t>> fixedFaces;
|
|
|
|
for (size_t i = 0; i < faces.size(); ++i) {
|
|
|
|
if (invalidFaces.find(i) != invalidFaces.end()) {
|
|
|
|
//removedFaces.push_back(faces[i]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
fixedFaces.push_back(faces[i]);
|
|
|
|
}
|
|
|
|
//saveAsObj("fixholes_output_without_newfaces.obj", verticies, fixedFaces);
|
2020-04-14 11:50:19 +00:00
|
|
|
for (const auto &it: newFaces) {
|
2020-04-14 21:57:01 +00:00
|
|
|
fixedFaces.push_back(std::vector<size_t> {it.indices[0], it.indices[1], it.indices[2]});
|
2020-04-14 11:50:19 +00:00
|
|
|
//addedFaces.push_back(std::vector<size_t> {it.indices[0], it.indices[1], it.indices[2]});
|
|
|
|
}
|
2020-04-14 21:57:01 +00:00
|
|
|
//saveAsObj("fixholes_output.obj", verticies, fixedFaces);
|
2020-04-14 11:50:19 +00:00
|
|
|
//saveAsObj("fixholes_added.obj", verticies, addedFaces);
|
2020-04-14 21:57:01 +00:00
|
|
|
//saveAsObj("fixholes_removed.obj", verticies, removedFaces);
|
|
|
|
|
|
|
|
faces = fixedFaces;
|
2020-04-14 11:50:19 +00:00
|
|
|
|
2020-04-14 21:57:01 +00:00
|
|
|
//qDebug() << "fixHoles holeRings:" << holeRings.size() << "newFaces:" << newFaces.size() << "removedFaces:" << removedFaces.size();
|
2020-04-14 11:50:19 +00:00
|
|
|
}
|