#include #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 &GridMeshBuilder::getGeneratedPositions() { return m_generatedPositions; } const std::vector &GridMeshBuilder::getGeneratedSources() { return m_generatedSources; } const std::vector> &GridMeshBuilder::getGeneratedFaces() { return m_generatedFaces; } void GridMeshBuilder::splitCycleToPolylines(const std::vector &cycle, std::vector> *polylines) { if (cycle.size() < 3) { qDebug() << "Invalid cycle size:" << cycle.size(); return; } //qDebug() << "Cycle:" << cycle; std::vector 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 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::vector> edgeToCandidateFaceMap; m_generatedVertices = m_nodeVertices; std::vector> candidateFaces; std::vector candidateFaceSourceCycles; m_generatedFaces.clear(); auto addCandidateFaces = [&](const std::vector> &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> polylines; splitCycleToPolylines(it, &polylines); if (polylines.size() < 3) { std::vector> 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 visitedFaceIndices; std::queue 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 edge = std::make_pair(face[checkIndex], face[nextOfCheckIndex]); if (m_halfEdgeMap.find(edge) != m_halfEdgeMap.end()) { std::pair 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> 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 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 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 oldNodes = m_nodes; std::vector oldEdges = m_edges; m_edges.clear(); float distance2Threshold = m_meshTargetEdgeSize * m_meshTargetEdgeSize; std::set> 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 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> 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(); }