From 0be42e485b69c34cc1632409272cc2290e8a805b Mon Sep 17 00:00:00 2001 From: Jeremy HU Date: Sun, 2 Oct 2022 04:19:34 +1100 Subject: [PATCH] Implement stitching line back closing --- application/sources/skeleton_document.h | 4 +- dust3d/base/vector3.h | 8 ++ dust3d/mesh/mesh_generator.cc | 19 ++- dust3d/mesh/stitch_mesh_builder.cc | 166 ++++++++++++++++++++---- dust3d/mesh/stitch_mesh_builder.h | 6 +- 5 files changed, 172 insertions(+), 31 deletions(-) diff --git a/application/sources/skeleton_document.h b/application/sources/skeleton_document.h index b91a80c1..328d6086 100644 --- a/application/sources/skeleton_document.h +++ b/application/sources/skeleton_document.h @@ -34,8 +34,8 @@ public: } void setRadius(float toRadius) { - if (toRadius < 0.005f) - toRadius = 0.005f; + if (toRadius < 0.001f) + toRadius = 0.001f; else if (toRadius > 1) toRadius = 1; radius = toRadius; diff --git a/dust3d/base/vector3.h b/dust3d/base/vector3.h index f36734e0..3a47a6f6 100644 --- a/dust3d/base/vector3.h +++ b/dust3d/base/vector3.h @@ -203,6 +203,14 @@ public: return *this; } + inline Vector3 &operator-=(const Vector3 &other) + { + m_data[0] -= other.x(); + m_data[1] -= other.y(); + m_data[2] -= other.z(); + return *this; + } + inline Vector3 &operator*=(double number) { m_data[0] *= number; diff --git a/dust3d/mesh/mesh_generator.cc b/dust3d/mesh/mesh_generator.cc index 57f9ede9..416e3568 100644 --- a/dust3d/mesh/mesh_generator.cc +++ b/dust3d/mesh/mesh_generator.cc @@ -460,13 +460,22 @@ std::unique_ptr MeshGenerator::combineStitchingMesh(const st std::vector orderedIndices; bool isCircle = false; - flattenLinks(builderNodeLinks, &orderedIndices, &isCircle); - std::vector orderedBuilderNodes(orderedIndices.size()); - for (size_t i = 0; i < orderedIndices.size(); ++i) - orderedBuilderNodes[i] = builderNodes[orderedIndices[i]]; + bool isClosing = false; + std::vector orderedBuilderNodes; + if (!builderNodeLinks.empty()) { + flattenLinks(builderNodeLinks, &orderedIndices, &isCircle); + orderedBuilderNodes.resize(orderedIndices.size()); + for (size_t i = 0; i < orderedIndices.size(); ++i) + orderedBuilderNodes[i] = builderNodes[orderedIndices[i]]; + } else { + orderedBuilderNodes.push_back(builderNodes[0]); + isClosing = true; + } + splines.emplace_back(StitchMeshBuilder::Spline { std::move(orderedBuilderNodes), - isCircle + isCircle, + isClosing }); } diff --git a/dust3d/mesh/stitch_mesh_builder.cc b/dust3d/mesh/stitch_mesh_builder.cc index 97a68dd2..90c17e65 100644 --- a/dust3d/mesh/stitch_mesh_builder.cc +++ b/dust3d/mesh/stitch_mesh_builder.cc @@ -20,6 +20,7 @@ * SOFTWARE. */ +#include #include #include @@ -41,12 +42,13 @@ const std::vector> &StitchMeshBuilder::generatedFaces() return m_generatedFaces; } -std::vector> StitchMeshBuilder::convertSplinesToStitchingPoints(const std::vector &splines) +void StitchMeshBuilder::interpolateSplinesToHaveEqualSizeOfNodesExceptClosingSplines() { - std::vector> stitchingPoints(splines.size()); - - for (size_t i = 0; i < splines.size(); ++i) { - const auto &spline = splines[i]; + std::vector interpolatedSplines = m_splines; + for (size_t i = 0; i < m_splines.size(); ++i) { + const auto &spline = m_splines[i]; + if (spline.isClosing) + continue; std::vector polyline(spline.nodes.size()); std::vector radiuses(spline.nodes.size()); for (size_t j = 0; j < spline.nodes.size(); ++j) { @@ -56,11 +58,27 @@ std::vector> StitchMeshBuilder::c std::vector segmentPoints; std::vector segmentRadiuses; splitPolylineToSegments(polyline, radiuses, m_targetSegments, &segmentPoints, &segmentRadiuses); - stitchingPoints[i].resize(segmentPoints.size()); - for (size_t k = 0; k < segmentPoints.size(); ++k) { + auto &interpolatedSpline = interpolatedSplines[i]; + interpolatedSpline.nodes.resize(segmentPoints.size()); + for (size_t j = 0; j < spline.nodes.size(); ++j) { + interpolatedSpline.nodes[j].origin = segmentPoints[j]; + interpolatedSpline.nodes[j].radius = segmentRadiuses[j]; + } + } + m_splines = std::move(interpolatedSplines); +} + +std::vector> StitchMeshBuilder::convertSplinesToStitchingPoints(const std::vector &splines) +{ + std::vector> stitchingPoints(splines.size()); + + for (size_t i = 0; i < splines.size(); ++i) { + const auto &spline = splines[i]; + stitchingPoints[i].resize(spline.nodes.size()); + for (size_t k = 0; k < spline.nodes.size(); ++k) { stitchingPoints[i][k].originVertex = m_generatedVertices.size(); - stitchingPoints[i][k].radius = segmentRadiuses[k]; - m_generatedVertices.push_back(segmentPoints[k]); + stitchingPoints[i][k].radius = spline.nodes[k].radius; + m_generatedVertices.push_back(spline.nodes[k].origin); m_generatedStitchingPoints.push_back(stitchingPoints[i][k]); } } @@ -159,8 +177,80 @@ void StitchMeshBuilder::generateMeshFromStitchingPoints(const std::vector sourcePositions(nodes.size()); + for (size_t i = 0; i < nodes.size(); ++i) { + sourcePositions[i] = nodes[i].origin; + sourceOrigin += sourcePositions[i]; + } + sourceOrigin /= nodes.size(); + Vector3 targetDirection = (targetPosition - sourceOrigin).normalized(); + std::vector sourceVectors(nodes.size()); + for (size_t i = 0; i < nodes.size(); ++i) { + sourceVectors[i] = (nodes[i].origin - sourceOrigin).normalized(); + } + std::vector interpolatedPositions(nodes.size()); + std::vector interpolatedRadiuses(nodes.size()); + for (size_t i = 0; i < nodes.size(); ++i) { + interpolatedPositions[i] = sourceOrigin + + sourceVectors[i].rotated(Vector3::crossProduct(sourceVectors[i], targetDirection), Math::Pi * 0.25) * + ((nodes[i].origin - sourceOrigin).length() + (nodes[i].origin - targetPosition).length() * 0.5) * 0.5; + interpolatedRadiuses[i] = nodes[i].radius * 0.5; + } + m_splines.back().nodes.resize(nodes.size()); + for (size_t i = 0; i < nodes.size(); ++i) { + m_splines.back().nodes[i].origin = interpolatedPositions[i]; + m_splines.back().nodes[i].radius = interpolatedRadiuses[i]; + } +} + +void StitchMeshBuilder::addQuadButMaybeTriangle(const std::vector &quadButMaybeTriangle) +{ + std::vector finalFace; + std::unordered_set used; + finalFace.reserve(4); + for (const auto &it: quadButMaybeTriangle) { + auto insertResult = used.insert(it); + if (insertResult.second) + finalFace.push_back(it); + } + m_generatedFaces.emplace_back(finalFace); +} + void StitchMeshBuilder::build() { + if (m_splines.empty()) + return; + + // Calculate target segments + m_targetSegments = 1; + for (const auto &it: m_splines) { + m_targetSegments = std::max(m_targetSegments, it.nodes.size() - 1); + } + + // TODO: Make sure there is no closing nodes in the middle + + // Interpolate splines to make sure every spline have equal size of nodes, except the closing splines + interpolateSplinesToHaveEqualSizeOfNodesExceptClosingSplines(); + + // Preprocess back closing + if (m_splines.back().isClosing) + interpolateSplinesForBackClosing(); + + // TODO: Preprocess front closing + + // Update to the real interploated segments + m_targetSegments = m_splines.back().nodes.size() - 1; + + // Generate one side faces std::vector> stitchingPoints = convertSplinesToStitchingPoints(m_splines); generateMeshFromStitchingPoints(stitchingPoints); @@ -179,29 +269,34 @@ void StitchMeshBuilder::build() for (auto &it: m_generatedNormals) it.normalize(); - // Move all vertices off distance at radius - for (size_t i = 0; i < m_generatedVertices.size(); ++i) { - m_generatedVertices[i] += m_generatedNormals[i] * m_generatedStitchingPoints[i].radius; - } - // Generate other side of faces auto oneSideVertexCount = m_generatedVertices.size(); m_generatedVertices.reserve(oneSideVertexCount * 2); for (size_t i = 0; i < oneSideVertexCount; ++i) { - m_generatedVertices.emplace_back(m_generatedVertices[i] - m_generatedNormals[i] * m_generatedStitchingPoints[i].radius * 2.0); + m_generatedVertices.emplace_back(m_generatedVertices[i]); } m_generatedFaces.reserve(m_generatedFaces.size() * 2); auto oneSideFaceCount = m_generatedFaces.size(); for (size_t i = 0; i < oneSideFaceCount; ++i) { auto quad = m_generatedFaces[i]; + // The following quad vertices should follow the order of 2,1,0,3 strictly, + // This is to allow 2,1,0 to be grouped as triangle in the later quad to triangles processing. + // If not follow this order, the front triangle and back triangle maybe cross over because of not be parallel. m_generatedFaces.emplace_back(std::vector { - oneSideVertexCount + quad[3], oneSideVertexCount + quad[2], oneSideVertexCount + quad[1], - oneSideVertexCount + quad[0] + oneSideVertexCount + quad[0], + oneSideVertexCount + quad[3] }); } + // Move all vertices off distance at radius + for (size_t i = 0; i < oneSideVertexCount; ++i) { + auto moveDistance = m_generatedNormals[i] * m_generatedStitchingPoints[i].radius; + m_generatedVertices[i] += moveDistance; + m_generatedVertices[i + oneSideVertexCount] -= moveDistance; + } + // Stitch layers for first stitching line for (size_t j = 1; j <= m_targetSegments; ++j) { size_t i = j - 1; @@ -215,14 +310,39 @@ void StitchMeshBuilder::build() // Stitch layers for last stitching line size_t offsetBetweenFirstAndLastBorder = oneSideVertexCount - (m_targetSegments + 1); - for (size_t j = 1; j <= m_targetSegments; ++j) { - size_t i = j - 1; + if (m_splines.back().isClosing) { + size_t half = m_targetSegments / 2; + for (size_t j = 1; j <= half; ++j) { + size_t i = j - 1; + addQuadButMaybeTriangle(std::vector { + oneSideVertexCount + j + offsetBetweenFirstAndLastBorder, + oneSideVertexCount + i + offsetBetweenFirstAndLastBorder, + oneSideVertexCount + (m_targetSegments - i) + offsetBetweenFirstAndLastBorder, + oneSideVertexCount + (m_targetSegments - j) + offsetBetweenFirstAndLastBorder + }); + addQuadButMaybeTriangle(std::vector { + (m_targetSegments - j) + offsetBetweenFirstAndLastBorder, + (m_targetSegments - i) + offsetBetweenFirstAndLastBorder, + i + offsetBetweenFirstAndLastBorder, + j + offsetBetweenFirstAndLastBorder + }); + } m_generatedFaces.emplace_back(std::vector { - oneSideVertexCount + j + offsetBetweenFirstAndLastBorder, - j + offsetBetweenFirstAndLastBorder, - i + offsetBetweenFirstAndLastBorder, - oneSideVertexCount + i + offsetBetweenFirstAndLastBorder + (m_targetSegments - 0) + offsetBetweenFirstAndLastBorder, + 0 + offsetBetweenFirstAndLastBorder, + oneSideVertexCount + 0 + offsetBetweenFirstAndLastBorder, + oneSideVertexCount + (m_targetSegments - 0) + offsetBetweenFirstAndLastBorder }); + } else { + for (size_t j = 1; j <= m_targetSegments; ++j) { + size_t i = j - 1; + m_generatedFaces.emplace_back(std::vector { + oneSideVertexCount + j + offsetBetweenFirstAndLastBorder, + j + offsetBetweenFirstAndLastBorder, + i + offsetBetweenFirstAndLastBorder, + oneSideVertexCount + i + offsetBetweenFirstAndLastBorder + }); + } } // Stitch layers for all head nodes of stitching lines diff --git a/dust3d/mesh/stitch_mesh_builder.h b/dust3d/mesh/stitch_mesh_builder.h index 1a411e12..cadc3964 100644 --- a/dust3d/mesh/stitch_mesh_builder.h +++ b/dust3d/mesh/stitch_mesh_builder.h @@ -41,6 +41,7 @@ public: { std::vector nodes; bool isCircle = false; + bool isClosing = false; }; StitchMeshBuilder(std::vector &&splines); @@ -60,8 +61,9 @@ private: std::vector m_generatedVertices; std::vector> m_generatedFaces; std::vector m_generatedNormals; - size_t m_targetSegments = 5; + size_t m_targetSegments = 15; + void interpolateSplinesToHaveEqualSizeOfNodesExceptClosingSplines(); std::vector> convertSplinesToStitchingPoints(const std::vector &splines); void generateMeshFromStitchingPoints(const std::vector> &stitchingPoints); void generateMeshFromStitchingPoints(const std::vector &a, @@ -72,6 +74,8 @@ private: std::vector *targetPoints, std::vector *targetRadiuses); double segmentsLength(const std::vector &segmentPoints); + void interpolateSplinesForBackClosing(); + void addQuadButMaybeTriangle(const std::vector &quadButMaybeTriangle); }; }