diff --git a/dust3d.pro b/dust3d.pro index 6d8a0e7e..37b10757 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -538,6 +538,9 @@ HEADERS += src/fileforever.h SOURCES += src/partpreviewimagesgenerator.cpp HEADERS += src/partpreviewimagesgenerator.h +SOURCES += src/remeshhole.cpp +HEADERS += src/remeshhole.h + SOURCES += src/main.cpp HEADERS += src/version.h diff --git a/src/remeshhole.cpp b/src/remeshhole.cpp new file mode 100644 index 00000000..ea943455 --- /dev/null +++ b/src/remeshhole.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "remeshhole.h" + +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef Kernel::Point_3 Point; +typedef CGAL::Surface_mesh Mesh; +typedef boost::graph_traits::halfedge_descriptor halfedge_descriptor; +typedef boost::graph_traits::edge_descriptor edge_descriptor; +typedef boost::graph_traits::vertex_iterator vertex_iterator; +typedef boost::graph_traits::vertex_descriptor vertex_descriptor; + +struct halfedge2edge +{ + halfedge2edge(const Mesh& m, std::vector& edges) + : m_mesh(m), m_edges(edges) + {} + void operator()(const halfedge_descriptor& h) const + { + m_edges.push_back(edge(h, m_mesh)); + } + const Mesh& m_mesh; + std::vector& m_edges; +}; + +void remeshHole(std::vector &vertices, + const std::vector &hole, + std::vector> &newFaces) +{ + if (hole.empty()) + return; + + Mesh mesh; + + double targetEdgeLength = 0; + for (size_t i = 1; i < hole.size(); ++i) { + targetEdgeLength += (vertices[hole[i - 1]] - vertices[hole[i]]).length(); + } + targetEdgeLength /= hole.size(); + + std::vector::Vertex_index> meshFace; + std::vector originalIndices; + originalIndices.reserve(hole.size()); + for (const auto &v: hole) { + originalIndices.push_back(v); + const auto &position = vertices[v]; + meshFace.push_back(mesh.add_vertex(Point(position.x(), position.y(), position.z()))); + } + mesh.add_face(meshFace); + + std::vector border; + + CGAL::Polygon_mesh_processing::triangulate_faces(mesh); + + CGAL::Polygon_mesh_processing::border_halfedges(faces(mesh), + mesh, + boost::make_function_output_iterator(halfedge2edge(mesh, border))); + + auto ecm = mesh.add_property_map("ecm").first; + for (edge_descriptor e: border) + ecm[e] = true; + + Mesh::Property_map meshPropertyMap; + bool created; + boost::tie(meshPropertyMap, created) = mesh.add_property_map("v:source", 0); + + size_t vertexIndex = 0; + for (auto vertexIt = mesh.vertices_begin(); vertexIt != mesh.vertices_end(); vertexIt++) { + meshPropertyMap[*vertexIt] = originalIndices[vertexIndex++]; + } + + unsigned int nb_iter = 3; + + CGAL::Polygon_mesh_processing::isotropic_remeshing(faces(mesh), + targetEdgeLength, + mesh, + CGAL::Polygon_mesh_processing::parameters::number_of_iterations(nb_iter) + .protect_constraints(true) + .edge_is_constrained_map(ecm)); + + for (auto vertexIt = mesh.vertices_begin() + vertexIndex; vertexIt != mesh.vertices_end(); vertexIt++) { + auto point = mesh.point(*vertexIt); + originalIndices.push_back(vertices.size()); + vertices.push_back(QVector3D( + CGAL::to_double(point.x()), + CGAL::to_double(point.y()), + CGAL::to_double(point.z()) + )); + meshPropertyMap[*vertexIt] = originalIndices[vertexIndex++]; + } + + for (const auto &faceIt: mesh.faces()) { + CGAL::Vertex_around_face_iterator vbegin, vend; + std::vector faceIndices; + for (boost::tie(vbegin, vend) = CGAL::vertices_around_face(mesh.halfedge(faceIt), mesh); + vbegin != vend; + ++vbegin) { + faceIndices.push_back(meshPropertyMap[*vbegin]); + } + newFaces.push_back(faceIndices); + } +} diff --git a/src/remeshhole.h b/src/remeshhole.h new file mode 100644 index 00000000..4a3e11b6 --- /dev/null +++ b/src/remeshhole.h @@ -0,0 +1,9 @@ +#ifndef DUST3D_REMESH_HOLE_H +#define DUST3D_REMESH_HOLE_H +#include + +void remeshHole(std::vector &vertices, + const std::vector &hole, + std::vector> &newFaces); + +#endif diff --git a/src/strokemeshbuilder.cpp b/src/strokemeshbuilder.cpp index 0a48a734..2a3846d8 100644 --- a/src/strokemeshbuilder.cpp +++ b/src/strokemeshbuilder.cpp @@ -5,6 +5,7 @@ #include "meshstitcher.h" #include "util.h" #include "boxmesh.h" +#include "remeshhole.h" size_t StrokeMeshBuilder::Node::nextOrNeighborOtherThan(size_t neighborIndex) const { @@ -230,7 +231,6 @@ void StrokeMeshBuilder::buildMesh() return; } - std::vector> cuts; for (size_t i = 0; i < m_nodeIndices.size(); ++i) { auto &node = m_nodes[m_nodeIndices[i]]; if (!qFuzzyIsNull(node.cutRotation)) { @@ -243,16 +243,18 @@ void StrokeMeshBuilder::buildMesh() node.traverseDirection, node.baseNormal); std::vector cut; insertCutVertices(cutVertices, &cut, node.index, node.traverseDirection); - cuts.push_back(cut); + m_cuts.push_back(cut); } - - // Stich cuts +} + +void StrokeMeshBuilder::stitchCuts() +{ for (size_t i = m_isRing ? 0 : 1; i < m_nodeIndices.size(); ++i) { size_t h = (i + m_nodeIndices.size() - 1) % m_nodeIndices.size(); const auto &nodeH = m_nodes[m_nodeIndices[h]]; const auto &nodeI = m_nodes[m_nodeIndices[i]]; - const auto &cutH = cuts[h]; - auto reversedCutI = edgeloopFlipped(cuts[i]); + const auto &cutH = m_cuts[h]; + auto reversedCutI = edgeloopFlipped(m_cuts[i]); std::vector, QVector3D>> edgeLoops = { {cutH, -nodeH.traverseDirection}, {reversedCutI, nodeI.traverseDirection}, @@ -267,7 +269,7 @@ void StrokeMeshBuilder::buildMesh() // Fill endpoints if (!m_isRing) { - if (cuts.size() < 2) + if (m_cuts.size() < 2) return; if (!qFuzzyIsNull(m_hollowThickness)) { // Generate mesh for hollow @@ -293,8 +295,8 @@ void StrokeMeshBuilder::buildMesh() m_generatedFaces.push_back(newFace); } - std::vector> revisedCuts = {cuts[0], - edgeloopFlipped(cuts[cuts.size() - 1])}; + std::vector> revisedCuts = {m_cuts[0], + edgeloopFlipped(m_cuts[m_cuts.size() - 1])}; for (const auto &cut: revisedCuts) { for (size_t i = 0; i < cut.size(); ++i) { size_t j = (i + 1) % cut.size(); @@ -307,8 +309,28 @@ void StrokeMeshBuilder::buildMesh() } } } else { - m_generatedFaces.push_back(cuts[0]); - m_generatedFaces.push_back(edgeloopFlipped(cuts[cuts.size() - 1])); + if (m_cuts[0].size() <= 4) { + m_generatedFaces.push_back(m_cuts[0]); + m_generatedFaces.push_back(edgeloopFlipped(m_cuts[m_cuts.size() - 1])); + } else { + auto remeshAndAddCut = [&](const std::vector &inputFace) { + std::vector> remeshedFaces; + size_t oldVertexCount = m_generatedVertices.size(); + remeshHole(m_generatedVertices, + inputFace, + remeshedFaces); + size_t oldIndex = inputFace[0]; + for (size_t i = oldVertexCount; i < m_generatedVertices.size(); ++i) { + m_generatedVerticesCutDirects.push_back(m_generatedVerticesCutDirects[oldIndex]); + m_generatedVerticesSourceNodeIndices.push_back(m_generatedVerticesSourceNodeIndices[oldIndex]); + m_generatedVerticesInfos.push_back(m_generatedVerticesInfos[oldIndex]); + } + for (const auto &it: remeshedFaces) + m_generatedFaces.push_back(it); + }; + remeshAndAddCut(m_cuts[0]); + remeshAndAddCut(edgeloopFlipped(m_cuts[m_cuts.size() - 1])); + } } } } @@ -722,5 +744,6 @@ bool StrokeMeshBuilder::build() buildMesh(); applyDeform(); + stitchCuts(); return true; } diff --git a/src/strokemeshbuilder.h b/src/strokemeshbuilder.h index 6dc5a200..56c98483 100644 --- a/src/strokemeshbuilder.h +++ b/src/strokemeshbuilder.h @@ -96,6 +96,8 @@ private: std::vector m_generatedVerticesInfos; std::vector> m_generatedFaces; + std::vector> m_cuts; + bool prepare(); std::vector makeCut(const QVector3D &cutCenter, float radius, @@ -116,6 +118,7 @@ private: std::vector edgeloopFlipped(const std::vector &edgeLoop); void reviseNodeBaseNormal(Node &node); void applyDeform(); + void stitchCuts(); }; #endif