dust3d/src/gridmeshbuilder.cpp

449 lines
17 KiB
C++

#include <queue>
#include "gridmeshbuilder.h"
#include "cyclefinder.h"
#include "regionfiller.h"
#include "util.h"
size_t GridMeshBuilder::addNode(const QVector3D &position, float radius)
{
size_t newNodeIndex = m_nodes.size();
Node node;
node.position = position;
node.radius = radius;
node.source = newNodeIndex;
m_nodes.push_back(node);
return newNodeIndex;
}
size_t GridMeshBuilder::addEdge(size_t firstNodeIndex, size_t secondNodeIndex)
{
size_t newEdgeIndex = m_edges.size();
Edge edge;
edge.firstNodeIndex = firstNodeIndex;
edge.secondNodeIndex = secondNodeIndex;
m_edges.push_back(edge);
m_nodes[firstNodeIndex].neighborIndices.push_back(secondNodeIndex);
m_nodes[secondNodeIndex].neighborIndices.push_back(firstNodeIndex);
return newEdgeIndex;
}
const std::vector<QVector3D> &GridMeshBuilder::getGeneratedPositions()
{
return m_generatedPositions;
}
const std::vector<size_t> &GridMeshBuilder::getGeneratedSources()
{
return m_generatedSources;
}
const std::vector<std::vector<size_t>> &GridMeshBuilder::getGeneratedFaces()
{
return m_generatedFaces;
}
void GridMeshBuilder::splitCycleToPolylines(const std::vector<size_t> &cycle,
std::vector<std::vector<size_t>> *polylines)
{
if (cycle.size() < 3) {
qDebug() << "Invalid cycle size:" << cycle.size();
return;
}
//qDebug() << "Cycle:" << cycle;
std::vector<size_t> cornerIndices;
for (size_t i = 0; i < cycle.size(); ++i) {
size_t h = (i + cycle.size() - 1) % cycle.size();
size_t j = (i + 1) % cycle.size();
QVector3D hi = m_nodeVertices[cycle[i]].position - m_nodeVertices[cycle[h]].position;
QVector3D ij = m_nodeVertices[cycle[j]].position - m_nodeVertices[cycle[i]].position;
auto angle = degreesBetweenVectors(hi, ij);
//qDebug() << "angle[" << i << "]:" << angle;
if (angle >= m_polylineAngleChangeThreshold)
cornerIndices.push_back(i);
}
if (cornerIndices.size() < 3) {
qDebug() << "Invalid corners:" << cornerIndices.size();
return;
}
for (size_t m = 0; m < cornerIndices.size(); ++m) {
size_t n = (m + 1) % cornerIndices.size();
std::vector<size_t> polyline;
size_t i = cornerIndices[m];
size_t j = cornerIndices[n];
for (size_t p = i; p != j; p = (p + 1) % cycle.size())
polyline.push_back(cycle[p]);
polyline.push_back(cycle[j]);
//qDebug() << "Polyline[m" << m << "n" << n << "i" << i << "j" << j << "]:" << polyline;
polylines->push_back(polyline);
}
}
void GridMeshBuilder::prepareNodeVertices()
{
m_nodeVertices.resize(m_nodes.size());
m_nodePositions.resize(m_nodes.size());
for (size_t i = 0; i < m_nodes.size(); ++i) {
RegionFiller::Node node;
node.position = m_nodes[i].position;
node.radius = m_nodes[i].radius;
node.source = i;
m_nodeVertices[i] = node;
m_nodePositions[i] = node.position;
}
}
void GridMeshBuilder::generateFaces()
{
auto createEdgeKey = [](size_t v1, size_t v2) {
if (v1 > v2)
std::swap(v1, v2);
return std::make_pair(v1, v2);
};
std::map<std::pair<size_t, size_t>, std::vector<size_t>> edgeToCandidateFaceMap;
m_generatedVertices = m_nodeVertices;
std::vector<std::vector<size_t>> candidateFaces;
std::vector<size_t> candidateFaceSourceCycles;
m_generatedFaces.clear();
auto addCandidateFaces = [&](const std::vector<std::vector<size_t>> &newFaces, size_t sourceCycle) {
for (const auto &face: newFaces) {
size_t candidateFaceIndex = candidateFaces.size();
candidateFaces.push_back(face);
candidateFaceSourceCycles.push_back(sourceCycle);
for (size_t i = 0; i < face.size(); ++i) {
size_t j = (i + 1) % face.size();
auto edgeKey = createEdgeKey(face[i], face[j]);
edgeToCandidateFaceMap[edgeKey].push_back(candidateFaceIndex);
}
}
};
for (size_t i = 0; i < m_cycles.size(); ++i) {
const auto &it = m_cycles[i];
std::vector<std::vector<size_t>> polylines;
splitCycleToPolylines(it, &polylines);
if (polylines.size() < 3) {
std::vector<std::vector<size_t>> faces;
faces.push_back(it);
addCandidateFaces(faces, i);
continue;
}
RegionFiller regionFiller(&m_generatedVertices, &polylines);
if (regionFiller.fill()) {
m_generatedVertices = regionFiller.getOldAndNewVertices();
} else {
regionFiller.fillWithoutPartition();
}
auto newFaces = regionFiller.getNewFaces();
addCandidateFaces(newFaces, i);
}
if (candidateFaces.empty())
return;
std::set<size_t> visitedFaceIndices;
std::queue<size_t> waitFaceIndices;
waitFaceIndices.push(0);
while (!waitFaceIndices.empty()) {
auto faceIndex = waitFaceIndices.front();
waitFaceIndices.pop();
if (visitedFaceIndices.find(faceIndex) != visitedFaceIndices.end())
continue;
visitedFaceIndices.insert(faceIndex);
const auto &face = candidateFaces[faceIndex];
if (face.size() < 3) {
qDebug() << "Invalid face, edges:" << face.size();
continue;
}
bool shouldReverse = false;
size_t checkIndex = 0;
for (size_t i = 0; i < face.size(); ++i) {
size_t j = (i + 1) % face.size();
if (m_halfEdgeMap.find(std::make_pair(face[i], face[j])) != m_halfEdgeMap.end() ||
m_halfEdgeMap.find(std::make_pair(face[j], face[i])) != m_halfEdgeMap.end()) {
checkIndex = i;
break;
}
}
size_t nextOfCheckIndex = (checkIndex + 1) % face.size();
std::pair<size_t, size_t> edge = std::make_pair(face[checkIndex], face[nextOfCheckIndex]);
if (m_halfEdgeMap.find(edge) != m_halfEdgeMap.end()) {
std::pair<size_t, size_t> oppositeEdge = std::make_pair(face[nextOfCheckIndex], face[checkIndex]);
if (m_halfEdgeMap.find(oppositeEdge) != m_halfEdgeMap.end()) {
qDebug() << "Too many face share one edge, should not happen";
continue;
}
shouldReverse = true;
}
auto finalFace = face;
if (shouldReverse) {
std::reverse(finalFace.begin(), finalFace.end());
}
size_t finalFaceIndex = m_generatedFaces.size();
m_generatedFaces.push_back(finalFace);
m_generatedFaceSourceCycles.push_back(candidateFaceSourceCycles[faceIndex]);
for (size_t i = 0; i < finalFace.size(); ++i) {
size_t j = (i + 1) % finalFace.size();
auto insertResult = m_halfEdgeMap.insert({std::make_pair(finalFace[i], finalFace[j]), finalFaceIndex});
if (!insertResult.second) {
qDebug() << "Should not happend, half edge conflicts";
}
auto edgeKey = createEdgeKey(finalFace[i], finalFace[j]);
auto findCandidates = edgeToCandidateFaceMap.find(edgeKey);
if (findCandidates == edgeToCandidateFaceMap.end())
continue;
for (const auto &candidateFaceIndex: findCandidates->second) {
if (visitedFaceIndices.find(candidateFaceIndex) != visitedFaceIndices.end())
continue;
waitFaceIndices.push(candidateFaceIndex);
}
}
}
}
void GridMeshBuilder::calculateNormals()
{
std::vector<std::vector<QVector3D>> vertexNormals(m_generatedVertices.size());
for (const auto &face: m_generatedFaces) {
QVector3D sumOfNormals;
for (size_t i = 0; i < face.size(); ++i) {
size_t h = (i + face.size() - 1) % face.size();
size_t j = (i + 1) % face.size();
QVector3D vh = m_generatedVertices[face[h]].position;
QVector3D vi = m_generatedVertices[face[i]].position;
QVector3D vj = m_generatedVertices[face[j]].position;
sumOfNormals += QVector3D::normal(vj - vi, vh - vi);
}
QVector3D faceNormal = sumOfNormals.normalized();
for (size_t i = 0; i < face.size(); ++i) {
vertexNormals[face[i]].push_back(faceNormal);
}
}
m_nodeNormals.resize(m_generatedVertices.size());
for (size_t i = 0; i < m_nodeNormals.size(); ++i) {
const auto &normals = vertexNormals[i];
if (normals.empty())
continue;
m_nodeNormals[i] = std::accumulate(normals.begin(), normals.end(), QVector3D()).normalized();
}
}
void GridMeshBuilder::removeBigRingFaces()
{
if (m_generatedFaces.size() != m_generatedFaceSourceCycles.size()) {
qDebug() << "Generated source cycles invalid";
return;
}
auto maxBigRingSize = m_maxBigRingSize;
if (m_subdived)
maxBigRingSize *= 2;
std::set<size_t> invalidCycles;
for (size_t faceIndex = 0; faceIndex < m_generatedFaces.size(); ++faceIndex) {
const auto &face = m_generatedFaces[faceIndex];
size_t sourceCycle = m_generatedFaceSourceCycles[faceIndex];
size_t oppositeCycles = 0;
for (size_t i = 0; i < face.size(); ++i) {
size_t j = (i + 1) % face.size();
auto oppositeEdge = std::make_pair(face[j], face[i]);
auto findOpposite = m_halfEdgeMap.find(oppositeEdge);
if (findOpposite == m_halfEdgeMap.end())
continue;
size_t oppositeFaceIndex = findOpposite->second;
size_t oppositeFaceSourceCycle = m_generatedFaceSourceCycles[oppositeFaceIndex];
if (sourceCycle == oppositeFaceSourceCycle)
continue;
++oppositeCycles;
}
if (oppositeCycles > maxBigRingSize)
invalidCycles.insert(sourceCycle);
}
if (invalidCycles.empty())
return;
auto oldFaces = m_generatedFaces;
m_halfEdgeMap.clear();
m_generatedFaces.clear();
for (size_t faceIndex = 0; faceIndex < oldFaces.size(); ++faceIndex) {
size_t sourceCycle = m_generatedFaceSourceCycles[faceIndex];
if (invalidCycles.find(sourceCycle) != invalidCycles.end())
continue;
const auto &face = oldFaces[faceIndex];
for (size_t i = 0; i < face.size(); ++i) {
size_t j = (i + 1) % face.size();
auto edge = std::make_pair(face[i], face[j]);
m_halfEdgeMap.insert({edge, m_generatedFaces.size()});
}
m_generatedFaces.push_back(face);
}
}
void GridMeshBuilder::extrude()
{
removeBigRingFaces();
calculateNormals();
bool hasHalfEdge = false;
for (const auto &halfEdge: m_halfEdgeMap) {
auto oppositeHalfEdge = std::make_pair(halfEdge.first.second, halfEdge.first.first);
if (m_halfEdgeMap.find(oppositeHalfEdge) != m_halfEdgeMap.end())
continue;
hasHalfEdge = true;
break;
}
if (m_generatedVertices.empty())
return;
m_generatedPositions.resize(m_generatedVertices.size() * 2);
m_generatedSources.resize(m_generatedPositions.size(), 0);
for (size_t i = 0; i < m_generatedVertices.size(); ++i) {
const auto &vertex = m_generatedVertices[i];
const auto &normal = m_nodeNormals[i];
m_generatedPositions[i] = vertex.position;
m_generatedPositions[i] += normal * vertex.radius;
m_generatedSources[i] = m_nodes[vertex.source].source;
size_t j = m_generatedVertices.size() + i;
m_generatedPositions[j] = vertex.position;
m_generatedPositions[j] -= normal * vertex.radius;
m_generatedSources[j] = m_generatedSources[i];
}
bool pickSecondMesh = false;
// The outter faces should have longer edges
float sumOfFirstMeshEdgeLength = 0;
float sumOfSecondMeshEdgeLength = 0;
for (size_t i = 0; i < m_generatedFaces.size(); ++i) {
const auto &face = m_generatedFaces[i];
for (size_t m = 0; m < face.size(); ++m) {
size_t n = (m + 1) % face.size();
sumOfFirstMeshEdgeLength += (m_generatedPositions[face[m]] - m_generatedPositions[face[n]]).length();
sumOfSecondMeshEdgeLength += (m_generatedPositions[m_generatedVertices.size() + face[m]] - m_generatedPositions[m_generatedVertices.size() + face[n]]).length();
}
}
if (sumOfFirstMeshEdgeLength < sumOfSecondMeshEdgeLength)
pickSecondMesh = true;
size_t faceNumPerLayer = m_generatedFaces.size();
if (hasHalfEdge) {
m_generatedFaces.resize(faceNumPerLayer * 2);
for (size_t i = faceNumPerLayer; i < m_generatedFaces.size(); ++i) {
auto &face = m_generatedFaces[i];
face = m_generatedFaces[i - faceNumPerLayer];
for (auto &it: face)
it += m_generatedVertices.size();
std::reverse(face.begin(), face.end());
}
for (const auto &halfEdge: m_halfEdgeMap) {
auto oppositeHalfEdge = std::make_pair(halfEdge.first.second, halfEdge.first.first);
if (m_halfEdgeMap.find(oppositeHalfEdge) != m_halfEdgeMap.end())
continue;
std::vector<size_t> face = {
oppositeHalfEdge.first,
oppositeHalfEdge.second,
halfEdge.first.first + m_generatedVertices.size(),
halfEdge.first.second + m_generatedVertices.size()
};
m_generatedFaces.push_back(face);
}
} else {
if (pickSecondMesh) {
for (auto &face: m_generatedFaces) {
for (auto &it: face)
it += m_generatedVertices.size();
std::reverse(face.begin(), face.end());
}
}
}
}
void GridMeshBuilder::applyModifiers()
{
std::vector<Node> oldNodes = m_nodes;
std::vector<Edge> oldEdges = m_edges;
m_edges.clear();
float distance2Threshold = m_meshTargetEdgeSize * m_meshTargetEdgeSize;
std::set<std::pair<size_t, size_t>> visitedEdges;
for (const auto &oldEdge: oldEdges) {
auto key = std::make_pair(oldEdge.firstNodeIndex, oldEdge.secondNodeIndex);
if (visitedEdges.find(key) != visitedEdges.end())
continue;
visitedEdges.insert(std::make_pair(oldEdge.firstNodeIndex, oldEdge.secondNodeIndex));
visitedEdges.insert(std::make_pair(oldEdge.secondNodeIndex, oldEdge.firstNodeIndex));
const auto &oldStartNode = oldNodes[oldEdge.firstNodeIndex];
const auto &oldStopNode = oldNodes[oldEdge.secondNodeIndex];
if ((oldStartNode.position - oldStopNode.position).lengthSquared() <= distance2Threshold) {
m_edges.push_back(oldEdge);
continue;
}
auto oldEdgeLength = (oldStartNode.position - oldStopNode.position).length();
size_t newInsertNum = oldEdgeLength / m_meshTargetEdgeSize;
if (newInsertNum < 1)
newInsertNum = 1;
if (newInsertNum > 100)
continue;
float stepFactor = 1.0 / (newInsertNum + 1);
float factor = stepFactor;
std::vector<size_t> edgeNodeIndices;
edgeNodeIndices.push_back(oldEdge.firstNodeIndex);
for (size_t i = 0; i < newInsertNum && factor < 1.0; factor += stepFactor, ++i) {
float firstFactor = 1.0 - factor;
Node newNode;
newNode.position = oldStartNode.position * firstFactor + oldStopNode.position * factor;
newNode.radius = oldStartNode.radius * firstFactor + oldStopNode.radius * factor;
if (firstFactor >= 0.5) {
newNode.source = oldEdge.firstNodeIndex;
} else {
newNode.source = oldEdge.secondNodeIndex;
}
edgeNodeIndices.push_back(m_nodes.size());
m_nodes.push_back(newNode);
}
edgeNodeIndices.push_back(oldEdge.secondNodeIndex);
for (size_t i = 1; i < edgeNodeIndices.size(); ++i) {
size_t h = i - 1;
m_edges.push_back(Edge {edgeNodeIndices[h], edgeNodeIndices[i]});
}
}
}
void GridMeshBuilder::setSubdived(bool subdived)
{
m_subdived = subdived;
}
bool GridMeshBuilder::build()
{
if (m_subdived)
applyModifiers();
prepareNodeVertices();
findCycles();
if (m_cycles.empty())
return false;
generateFaces();
extrude();
return true;
}
void GridMeshBuilder::findCycles()
{
std::vector<std::pair<size_t, size_t>> edges(m_edges.size());
for (size_t i = 0; i < m_edges.size(); ++i) {
const auto &source = m_edges[i];
edges[i] = std::make_pair(source.firstNodeIndex, source.secondNodeIndex);
}
CycleFinder cycleFinder(m_nodePositions, edges);
cycleFinder.find();
m_cycles = cycleFinder.getCycles();
}