From c614da251a360c5cfe461060c0a27d367bf7a8f5 Mon Sep 17 00:00:00 2001 From: Jeremy HU Date: Wed, 19 Oct 2022 20:22:17 +1100 Subject: [PATCH] Remove stroke mesh builder --- application/application.pro | 4 - dust3d/mesh/mesh_generator.cc | 343 ------------ dust3d/mesh/mesh_generator.h | 1 - dust3d/mesh/stroke_mesh_builder.cc | 810 ----------------------------- dust3d/mesh/stroke_mesh_builder.h | 141 ----- dust3d/mesh/stroke_modifier.cc | 302 ----------- dust3d/mesh/stroke_modifier.h | 76 --- 7 files changed, 1677 deletions(-) delete mode 100644 dust3d/mesh/stroke_mesh_builder.cc delete mode 100644 dust3d/mesh/stroke_mesh_builder.h delete mode 100644 dust3d/mesh/stroke_modifier.cc delete mode 100644 dust3d/mesh/stroke_modifier.h diff --git a/application/application.pro b/application/application.pro index e00c4842..85ce6af9 100644 --- a/application/application.pro +++ b/application/application.pro @@ -300,10 +300,6 @@ HEADERS += ../dust3d/mesh/smooth_normal.h SOURCES += ../dust3d/mesh/smooth_normal.cc HEADERS += ../dust3d/mesh/stitch_mesh_builder.h SOURCES += ../dust3d/mesh/stitch_mesh_builder.cc -HEADERS += ../dust3d/mesh/stroke_mesh_builder.h -SOURCES += ../dust3d/mesh/stroke_mesh_builder.cc -HEADERS += ../dust3d/mesh/stroke_modifier.h -SOURCES += ../dust3d/mesh/stroke_modifier.cc HEADERS += ../dust3d/mesh/triangulate.h SOURCES += ../dust3d/mesh/triangulate.cc HEADERS += ../dust3d/mesh/trim_vertices.h diff --git a/dust3d/mesh/mesh_generator.cc b/dust3d/mesh/mesh_generator.cc index e528664e..8a9d21b6 100644 --- a/dust3d/mesh/mesh_generator.cc +++ b/dust3d/mesh/mesh_generator.cc @@ -33,8 +33,6 @@ #include #include #include -#include -#include #include #include #include @@ -737,347 +735,6 @@ std::unique_ptr MeshGenerator::combinePartMesh(const std::st return mesh; } -/* -std::unique_ptr MeshGenerator::combinePartMesh(const std::string &partIdString, bool *hasError, bool *retryable, bool addIntermediateNodes) -{ - auto findPart = m_snapshot->parts.find(partIdString); - if (findPart == m_snapshot->parts.end()) { - return nullptr; - } - - Uuid partId = Uuid(partIdString); - auto &part = findPart->second; - - *retryable = true; - - bool isDisabled = String::isTrue(String::valueOrEmpty(part, "disabled")); - std::string __mirroredByPartId = String::valueOrEmpty(part, "__mirroredByPartId"); - std::string __mirrorFromPartId = String::valueOrEmpty(part, "__mirrorFromPartId"); - bool subdived = String::isTrue(String::valueOrEmpty(part, "subdived")); - bool rounded = String::isTrue(String::valueOrEmpty(part, "rounded")); - bool chamfered = String::isTrue(String::valueOrEmpty(part, "chamfered")); - bool countershaded = String::isTrue(String::valueOrEmpty(part, "countershaded")); - bool smooth = String::isTrue(String::valueOrEmpty(part, "smooth")); - std::string colorString = String::valueOrEmpty(part, "color"); - Color partColor = colorString.empty() ? m_defaultPartColor : Color(colorString); - float deformThickness = 1.0; - float deformWidth = 1.0; - float cutRotation = 0.0; - float hollowThickness = 0.0; - auto target = PartTargetFromString(String::valueOrEmpty(part, "target").c_str()); - auto base = PartBaseFromString(String::valueOrEmpty(part, "base").c_str()); - - std::string searchPartIdString = __mirrorFromPartId.empty() ? partIdString : __mirrorFromPartId; - - std::string cutFaceString = String::valueOrEmpty(part, "cutFace"); - std::vector cutTemplate; - cutFaceStringToCutTemplate(cutFaceString, cutTemplate); - if (chamfered) - chamferFace(&cutTemplate); - - std::string cutRotationString = String::valueOrEmpty(part, "cutRotation"); - if (!cutRotationString.empty()) { - cutRotation = String::toFloat(cutRotationString); - } - - std::string hollowThicknessString = String::valueOrEmpty(part, "hollowThickness"); - if (!hollowThicknessString.empty()) { - hollowThickness = String::toFloat(hollowThicknessString); - } - - std::string thicknessString = String::valueOrEmpty(part, "deformThickness"); - if (!thicknessString.empty()) { - deformThickness = String::toFloat(thicknessString); - } - - std::string widthString = String::valueOrEmpty(part, "deformWidth"); - if (!widthString.empty()) { - deformWidth = String::toFloat(widthString); - } - - bool deformUnified = String::isTrue(String::valueOrEmpty(part, "deformUnified")); - - Uuid materialId; - std::string materialIdString = String::valueOrEmpty(part, "materialId"); - if (!materialIdString.empty()) - materialId = Uuid(materialIdString); - - float colorSolubility = 0; - std::string colorSolubilityString = String::valueOrEmpty(part, "colorSolubility"); - if (!colorSolubilityString.empty()) - colorSolubility = String::toFloat(colorSolubilityString); - - float metalness = 0; - std::string metalnessString = String::valueOrEmpty(part, "metallic"); - if (!metalnessString.empty()) - metalness = String::toFloat(metalnessString); - - float roughness = 1.0; - std::string roughnessString = String::valueOrEmpty(part, "roughness"); - if (!roughnessString.empty()) - roughness = String::toFloat(roughnessString); - - Uuid fillMeshFileId; - std::string fillMeshString = String::valueOrEmpty(part, "fillMesh"); - if (!fillMeshString.empty()) { - fillMeshFileId = Uuid(fillMeshString); - if (!fillMeshFileId.isNull()) { - *retryable = false; - } - } - - auto &partCache = m_cacheContext->parts[partIdString]; - partCache.objectNodes.clear(); - partCache.objectEdges.clear(); - partCache.objectNodeVertices.clear(); - partCache.vertices.clear(); - partCache.faces.clear(); - partCache.color = partColor; - partCache.metalness = metalness; - partCache.roughness = roughness; - partCache.isSuccessful = false; - partCache.joined = (target == PartTarget::Model && !isDisabled); - - struct NodeInfo - { - float radius = 0; - Vector3 position; - bool hasCutFaceSettings = false; - float cutRotation = 0.0; - std::string cutFace; - }; - std::map nodeInfos; - for (const auto &nodeIdString: m_partNodeIds[searchPartIdString]) { - auto findNode = m_snapshot->nodes.find(nodeIdString); - if (findNode == m_snapshot->nodes.end()) { - continue; - } - auto &node = findNode->second; - - float radius = String::toFloat(String::valueOrEmpty(node, "radius")); - float x = (String::toFloat(String::valueOrEmpty(node, "x")) - m_mainProfileMiddleX); - float y = (m_mainProfileMiddleY - String::toFloat(String::valueOrEmpty(node, "y"))); - float z = (m_sideProfileMiddleX - String::toFloat(String::valueOrEmpty(node, "z"))); - - bool hasCutFaceSettings = false; - float cutRotation = 0.0; - std::string cutFace; - - const auto &cutFaceIt = node.find("cutFace"); - if (cutFaceIt != node.end()) { - cutFace = cutFaceIt->second; - hasCutFaceSettings = true; - const auto &cutRotationIt = node.find("cutRotation"); - if (cutRotationIt != node.end()) { - cutRotation = String::toFloat(cutRotationIt->second); - } - } - - auto &nodeInfo = nodeInfos[nodeIdString]; - nodeInfo.position = Vector3(x, y, z); - nodeInfo.radius = radius; - nodeInfo.hasCutFaceSettings = hasCutFaceSettings; - nodeInfo.cutRotation = cutRotation; - nodeInfo.cutFace = cutFace; - } - - std::set> edges; - for (const auto &edgeIdString: m_partEdgeIds[searchPartIdString]) { - auto findEdge = m_snapshot->edges.find(edgeIdString); - if (findEdge == m_snapshot->edges.end()) { - continue; - } - auto &edge = findEdge->second; - - std::string fromNodeIdString = String::valueOrEmpty(edge, "from"); - std::string toNodeIdString = String::valueOrEmpty(edge, "to"); - - const auto &findFromNodeInfo = nodeInfos.find(fromNodeIdString); - if (findFromNodeInfo == nodeInfos.end()) { - continue; - } - - const auto &findToNodeInfo = nodeInfos.find(toNodeIdString); - if (findToNodeInfo == nodeInfos.end()) { - continue; - } - - edges.insert({fromNodeIdString, toNodeIdString}); - } - - bool buildSucceed = false; - std::map nodeIdStringToIndexMap; - std::map nodeIndexToIdStringMap; - - auto addNodeToPartCache = [&](const std::string &nodeIdString, const NodeInfo &nodeInfo) { - ObjectNode objectNode; - objectNode.partId = Uuid(partIdString); - objectNode.nodeId = Uuid(nodeIdString); - objectNode.origin = nodeInfo.position; - objectNode.radius = nodeInfo.radius; - objectNode.color = partColor; - objectNode.materialId = materialId; - objectNode.countershaded = countershaded; - objectNode.colorSolubility = colorSolubility; - objectNode.metalness = metalness; - objectNode.roughness = roughness; - if (!__mirroredByPartId.empty()) - objectNode.mirroredByPartId = Uuid(__mirroredByPartId); - if (!__mirrorFromPartId.empty()) { - objectNode.mirrorFromPartId = Uuid(__mirrorFromPartId); - objectNode.origin.setX(-nodeInfo.position.x()); - } - objectNode.joined = partCache.joined; - partCache.objectNodes.push_back(objectNode); - }; - auto addEdgeToPartCache = [&](const std::string &firstNodeIdString, const std::string &secondNodeIdString) { - partCache.objectEdges.push_back({ - {Uuid(partIdString), Uuid(firstNodeIdString)}, - {Uuid(partIdString), Uuid(secondNodeIdString)} - }); - }; - - auto strokeModifier = std::make_unique(); - - if (smooth) - strokeModifier->enableSmooth(); - if (addIntermediateNodes) - strokeModifier->enableIntermediateAddition(); - - for (const auto &nodeIt: nodeInfos) { - const auto &nodeIdString = nodeIt.first; - const auto &nodeInfo = nodeIt.second; - size_t nodeIndex = 0; - if (nodeInfo.hasCutFaceSettings) { - std::vector nodeCutTemplate; - cutFaceStringToCutTemplate(nodeInfo.cutFace, nodeCutTemplate); - if (chamfered) - chamferFace(&nodeCutTemplate); - nodeIndex = strokeModifier->addNode(nodeInfo.position, nodeInfo.radius, nodeCutTemplate, nodeInfo.cutRotation); - } else { - nodeIndex = strokeModifier->addNode(nodeInfo.position, nodeInfo.radius, cutTemplate, cutRotation); - } - nodeIdStringToIndexMap[nodeIdString] = nodeIndex; - nodeIndexToIdStringMap[nodeIndex] = nodeIdString; - } - - for (const auto &edgeIt: edges) { - const std::string &fromNodeIdString = edgeIt.first; - const std::string &toNodeIdString = edgeIt.second; - - auto findFromNodeIndex = nodeIdStringToIndexMap.find(fromNodeIdString); - if (findFromNodeIndex == nodeIdStringToIndexMap.end()) { - continue; - } - - auto findToNodeIndex = nodeIdStringToIndexMap.find(toNodeIdString); - if (findToNodeIndex == nodeIdStringToIndexMap.end()) { - continue; - } - - strokeModifier->addEdge(findFromNodeIndex->second, findToNodeIndex->second); - } - - if (subdived) - strokeModifier->subdivide(); - - if (rounded) - strokeModifier->roundEnd(); - - strokeModifier->finalize(); - - std::vector sourceNodeIndices; - - auto strokeMeshBuilder = std::make_unique(); - - strokeMeshBuilder->setDeformThickness(deformThickness); - strokeMeshBuilder->setDeformWidth(deformWidth); - strokeMeshBuilder->setDeformUnified(deformUnified); - strokeMeshBuilder->setHollowThickness(hollowThickness); - if (PartBase::YZ == base) { - strokeMeshBuilder->enableBaseNormalOnX(false); - } else if (PartBase::Average == base) { - strokeMeshBuilder->enableBaseNormalAverage(true); - } else if (PartBase::XY == base) { - strokeMeshBuilder->enableBaseNormalOnZ(false); - } else if (PartBase::ZX == base) { - strokeMeshBuilder->enableBaseNormalOnY(false); - } - - for (const auto &node: strokeModifier->nodes()) { - auto nodeIndex = strokeMeshBuilder->addNode(node.position, node.radius, node.cutTemplate, node.cutRotation); - strokeMeshBuilder->setNodeOriginInfo(nodeIndex, node.nearOriginNodeIndex, node.farOriginNodeIndex); - } - for (const auto &edge: strokeModifier->edges()) - strokeMeshBuilder->addEdge(edge.firstNodeIndex, edge.secondNodeIndex); - - for (const auto &nodeIt: nodeInfos) { - const auto &nodeIdString = nodeIt.first; - const auto &nodeInfo = nodeIt.second; - addNodeToPartCache(nodeIdString, nodeInfo); - } - - for (const auto &edgeIt: edges) { - const std::string &fromNodeIdString = edgeIt.first; - const std::string &toNodeIdString = edgeIt.second; - addEdgeToPartCache(fromNodeIdString, toNodeIdString); - } - - buildSucceed = strokeMeshBuilder->build(); - - partCache.vertices = strokeMeshBuilder->generatedVertices(); - partCache.faces = strokeMeshBuilder->generatedFaces(); - if (!__mirrorFromPartId.empty()) { - for (auto &it: partCache.vertices) - it.setX(-it.x()); - for (auto &it: partCache.faces) - std::reverse(it.begin(), it.end()); - } - sourceNodeIndices = strokeMeshBuilder->generatedVerticesSourceNodeIndices(); - for (size_t i = 0; i < partCache.vertices.size(); ++i) { - const auto &position = partCache.vertices[i]; - const auto &source = strokeMeshBuilder->generatedVerticesSourceNodeIndices()[i]; - size_t nodeIndex = strokeModifier->nodes()[source].originNodeIndex; - const auto &nodeIdString = nodeIndexToIdStringMap[nodeIndex]; - partCache.objectNodeVertices.push_back({position, {partIdString, nodeIdString}}); - } - - bool hasMeshError = false; - std::unique_ptr mesh; - - if (buildSucceed) { - mesh = std::make_unique(partCache.vertices, partCache.faces); - if (mesh->isNull()) { - hasMeshError = true; - } - } else { - hasMeshError = true; - } - - if (nullptr != mesh) { - partCache.isSuccessful = true; - } - - if (mesh && mesh->isNull()) { - mesh.reset(); - } - - //if (isDisabled) { - // mesh.reset(); - //} - - if (target != PartTarget::Model) { - mesh.reset(); - } - - if (hasMeshError && target == PartTarget::Model) { - *hasError = true; - } - - return mesh; -} -*/ - const std::map* MeshGenerator::findComponent(const std::string& componentIdString) { const std::map* component = &m_snapshot->rootComponent; diff --git a/dust3d/mesh/mesh_generator.h b/dust3d/mesh/mesh_generator.h index ec30c317..00665add 100644 --- a/dust3d/mesh/mesh_generator.h +++ b/dust3d/mesh/mesh_generator.h @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include diff --git a/dust3d/mesh/stroke_mesh_builder.cc b/dust3d/mesh/stroke_mesh_builder.cc deleted file mode 100644 index 16999e6a..00000000 --- a/dust3d/mesh/stroke_mesh_builder.cc +++ /dev/null @@ -1,810 +0,0 @@ -/* - * Copyright (c) 2016-2021 Jeremy HU . All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace dust3d { - -size_t StrokeMeshBuilder::Node::nextOrNeighborOtherThan(size_t neighborIndex) const -{ - if (this->next != neighborIndex && this->next != this->index) - return this->next; - for (const auto& it : this->neighbors) { - if (it != neighborIndex) - return it; - } - return this->index; -} - -void StrokeMeshBuilder::enableBaseNormalOnX(bool enabled) -{ - m_baseNormalOnX = enabled; -} - -void StrokeMeshBuilder::enableBaseNormalOnY(bool enabled) -{ - m_baseNormalOnY = enabled; -} - -void StrokeMeshBuilder::enableBaseNormalOnZ(bool enabled) -{ - m_baseNormalOnZ = enabled; -} - -void StrokeMeshBuilder::enableBaseNormalAverage(bool enabled) -{ - m_baseNormalAverageEnabled = enabled; -} - -void StrokeMeshBuilder::setDeformThickness(float thickness) -{ - m_deformThickness = std::max((float)0.01, thickness); -} - -void StrokeMeshBuilder::setDeformWidth(float width) -{ - m_deformWidth = std::max((float)0.01, width); -} - -void StrokeMeshBuilder::setDeformUnified(bool unified) -{ - m_deformUnified = unified; -} - -void StrokeMeshBuilder::setHollowThickness(float hollowThickness) -{ - m_hollowThickness = hollowThickness; -} - -void StrokeMeshBuilder::setNodeOriginInfo(size_t nodeIndex, int nearOriginNodeIndex, int farOriginNodeIndex) -{ - auto& node = m_nodes[nodeIndex]; - node.nearOriginNodeIndex = nearOriginNodeIndex; - node.farOriginNodeIndex = farOriginNodeIndex; -} - -const std::vector& StrokeMeshBuilder::generatedVertices() -{ - return m_generatedVertices; -} - -const std::vector>& StrokeMeshBuilder::generatedFaces() -{ - return m_generatedFaces; -} - -const std::vector& StrokeMeshBuilder::generatedVerticesSourceNodeIndices() -{ - return m_generatedVerticesSourceNodeIndices; -} - -const std::vector& StrokeMeshBuilder::nodes() const -{ - return m_nodes; -} - -const std::vector& StrokeMeshBuilder::nodeIndices() const -{ - return m_nodeIndices; -} - -const Vector3& StrokeMeshBuilder::nodeTraverseDirection(size_t nodeIndex) const -{ - return m_nodes[nodeIndex].traverseDirection; -} - -const Vector3& StrokeMeshBuilder::nodeBaseNormal(size_t nodeIndex) const -{ - return m_nodes[nodeIndex].baseNormal; -} - -size_t StrokeMeshBuilder::nodeTraverseOrder(size_t nodeIndex) const -{ - return m_nodes[nodeIndex].traverseOrder; -} - -size_t StrokeMeshBuilder::addNode(const Vector3& position, float radius, const std::vector& cutTemplate, float cutRotation) -{ - size_t nodeIndex = m_nodes.size(); - - Node node; - node.position = position; - node.radius = radius; - node.cutTemplate = cutTemplate; - node.cutRotation = cutRotation; - node.next = nodeIndex; - node.index = nodeIndex; - - m_nodes.push_back(node); - - return nodeIndex; -} - -void StrokeMeshBuilder::addEdge(size_t firstNodeIndex, - size_t secondNodeIndex) -{ - auto& fromNode = m_nodes[firstNodeIndex]; - fromNode.next = secondNodeIndex; - fromNode.neighbors.push_back(secondNodeIndex); - - auto& toNode = m_nodes[secondNodeIndex]; - toNode.neighbors.push_back(firstNodeIndex); -} - -Vector3 StrokeMeshBuilder::calculateBaseNormalFromTraverseDirection(const Vector3& traverseDirection) -{ - const std::vector axisList = { - Vector3 { 1, 0, 0 }, - Vector3 { 0, 1, 0 }, - Vector3 { 0, 0, 1 }, - }; - float maxDot = -1; - size_t nearAxisIndex = 0; - bool reversed = false; - for (size_t i = 0; i < axisList.size(); ++i) { - const auto axis = axisList[i]; - auto dot = Vector3::dotProduct(axis, traverseDirection); - auto positiveDot = abs(dot); - if (positiveDot >= maxDot) { - reversed = dot < 0; - maxDot = positiveDot; - nearAxisIndex = i; - } - } - // axisList[nearAxisIndex] align with the traverse direction, - // So we pick the next axis to do cross product with traverse direction - const auto& choosenAxis = axisList[(nearAxisIndex + 1) % 3]; - auto baseNormal = Vector3::crossProduct(traverseDirection, choosenAxis).normalized(); - return reversed ? -baseNormal : baseNormal; -} - -std::vector StrokeMeshBuilder::makeCut(const Vector3& cutCenter, - float radius, - const std::vector& cutTemplate, - const Vector3& cutNormal, - const Vector3& baseNormal) -{ - std::vector resultCut; - Vector3 u = Vector3::crossProduct(cutNormal, baseNormal).normalized(); - Vector3 v = Vector3::crossProduct(u, cutNormal).normalized(); - auto uFactor = u * radius; - auto vFactor = v * radius; - for (const auto& t : cutTemplate) { - resultCut.push_back(cutCenter + (uFactor * t.x() + vFactor * t.y())); - } - return resultCut; -} - -void StrokeMeshBuilder::insertCutVertices(const std::vector& cut, - std::vector* vertices, - size_t nodeIndex, - const Vector3& cutNormal) -{ - size_t indexInCut = 0; - for (const auto& position : cut) { - size_t vertexIndex = m_generatedVertices.size(); - m_generatedVertices.push_back(position); - m_generatedVerticesSourceNodeIndices.push_back(nodeIndex); - m_generatedVerticesCutDirects.push_back(cutNormal); - - GeneratedVertexInfo info; - info.orderInCut = indexInCut; - info.cutSize = cut.size(); - m_generatedVerticesInfos.push_back(info); - - vertices->push_back(vertexIndex); - - ++indexInCut; - } -} - -std::vector StrokeMeshBuilder::edgeloopFlipped(const std::vector& edgeLoop) -{ - auto reversed = edgeLoop; - std::reverse(reversed.begin(), reversed.end()); - std::rotate(reversed.rbegin(), reversed.rbegin() + 1, reversed.rend()); - return reversed; -} - -void StrokeMeshBuilder::buildMesh() -{ - if (1 == m_nodes.size()) { - const Node& node = m_nodes[0]; - int subdivideTimes = (int)(node.cutTemplate.size() / 4) - 1; - if (subdivideTimes < 0) - subdivideTimes = 0; - buildBoxMesh(node.position, node.radius, subdivideTimes, m_generatedVertices, m_generatedFaces); - m_generatedVerticesSourceNodeIndices.resize(m_generatedVertices.size(), 0); - m_generatedVerticesCutDirects.resize(m_generatedVertices.size(), node.traverseDirection); - return; - } - - for (size_t i = 0; i < m_nodeIndices.size(); ++i) { - auto& node = m_nodes[m_nodeIndices[i]]; - if (!Math::isZero(node.cutRotation)) { - float degree = node.cutRotation * 180; - node.baseNormal = node.baseNormal.rotated(node.traverseDirection, Math::radiansFromDegrees(degree)); - } - auto cutVertices = makeCut(node.position, node.radius, node.cutTemplate, - node.traverseDirection, node.baseNormal); - std::vector cut; - insertCutVertices(cutVertices, &cut, node.index, node.traverseDirection); - m_cuts.push_back(cut); - } -} - -void StrokeMeshBuilder::interpolateCutEdges() -{ - if (m_cuts.empty()) - return; - - size_t bigCutIndex = 0; - double maxRadius = 0.0; - for (size_t i = 0; i < m_cuts.size(); ++i) { - size_t j = (i + 1) % m_cuts.size(); - if (m_cuts[i].size() != m_cuts[j].size()) - return; - const auto& nodeIndex = m_generatedVerticesSourceNodeIndices[m_cuts[i][0]]; - if (m_nodes[nodeIndex].radius > maxRadius) { - maxRadius = m_nodes[nodeIndex].radius; - bigCutIndex = i; - } - } - - const auto& cut = m_cuts[bigCutIndex]; - double sumOfLegnth = 0; - std::vector edgeLengths; - edgeLengths.reserve(cut.size()); - for (size_t i = 0; i < cut.size(); ++i) { - size_t j = (i + 1) % cut.size(); - double length = (m_generatedVertices[cut[i]] - m_generatedVertices[cut[j]]).length(); - edgeLengths.push_back(length); - sumOfLegnth += length; - } - double targetLength = 1.2 * sumOfLegnth / cut.size(); - std::vector> newCuts(m_cuts.size()); - for (size_t index = 0; index < cut.size(); ++index) { - size_t nextIndex = (index + 1) % cut.size(); - for (size_t cutIndex = 0; cutIndex < m_cuts.size(); ++cutIndex) { - newCuts[cutIndex].push_back(m_cuts[cutIndex][index]); - } - const auto& oldEdgeLength = edgeLengths[index]; - if (targetLength >= oldEdgeLength) - continue; - size_t newInsertNum = oldEdgeLength / targetLength; - if (newInsertNum < 1) - newInsertNum = 1; - if (newInsertNum > 100) - continue; - float stepFactor = 1.0 / (newInsertNum + 1); - float factor = stepFactor; - for (size_t i = 0; i < newInsertNum && factor < 1.0; factor += stepFactor, ++i) { - float firstFactor = 1.0 - factor; - for (size_t cutIndex = 0; cutIndex < m_cuts.size(); ++cutIndex) { - auto newPosition = m_generatedVertices[m_cuts[cutIndex][index]] * firstFactor + m_generatedVertices[m_cuts[cutIndex][nextIndex]] * factor; - newCuts[cutIndex].push_back(m_generatedVertices.size()); - m_generatedVertices.push_back(newPosition); - const auto& oldIndex = m_cuts[cutIndex][index]; - m_generatedVerticesCutDirects.push_back(m_generatedVerticesCutDirects[oldIndex]); - m_generatedVerticesSourceNodeIndices.push_back(m_generatedVerticesSourceNodeIndices[oldIndex]); - m_generatedVerticesInfos.push_back(m_generatedVerticesInfos[oldIndex]); - } - } - } - - m_cuts = newCuts; -} - -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 = m_cuts[h]; - auto reversedCutI = edgeloopFlipped(m_cuts[i]); - std::vector, Vector3>> edgeLoops = { - { cutH, -nodeH.traverseDirection }, - { reversedCutI, nodeI.traverseDirection }, - }; - HoleStitcher stitcher; - stitcher.setVertices(&m_generatedVertices); - stitcher.stitch(edgeLoops); - for (const auto& face : stitcher.newlyGeneratedFaces()) { - m_generatedFaces.push_back(face); - } - } - - // Fill endpoints - if (!m_isRing) { - if (m_cuts.size() < 2) - return; - if (!Math::isZero(m_hollowThickness)) { - // Generate mesh for hollow - size_t startVertexIndex = m_generatedVertices.size(); - for (size_t i = 0; i < startVertexIndex; ++i) { - const auto& position = m_generatedVertices[i]; - const auto& node = m_nodes[m_generatedVerticesSourceNodeIndices[i]]; - auto ray = position - node.position; - - auto newPosition = position - ray * m_hollowThickness; - m_generatedVertices.push_back(newPosition); - m_generatedVerticesCutDirects.push_back(m_generatedVerticesCutDirects[i]); - m_generatedVerticesSourceNodeIndices.push_back(m_generatedVerticesSourceNodeIndices[i]); - m_generatedVerticesInfos.push_back(m_generatedVerticesInfos[i]); - } - - size_t oldFaceNum = m_generatedFaces.size(); - for (size_t i = 0; i < oldFaceNum; ++i) { - auto newFace = m_generatedFaces[i]; - std::reverse(newFace.begin(), newFace.end()); - for (auto& it : newFace) - it += startVertexIndex; - m_generatedFaces.push_back(newFace); - } - - 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(); - std::vector quad; - quad.push_back(cut[i]); - quad.push_back(cut[j]); - quad.push_back(startVertexIndex + cut[j]); - quad.push_back(startVertexIndex + cut[i]); - m_generatedFaces.push_back(quad); - } - } - } else { - 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(); - isotropicTriangulate(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])); - } - } - } -} - -void StrokeMeshBuilder::reviseTraverseDirections() -{ - std::vector> revised; - for (size_t i = 0; i < m_nodeIndices.size(); ++i) { - const auto& node = m_nodes[m_nodeIndices[i]]; - if (-1 != node.nearOriginNodeIndex && -1 != node.farOriginNodeIndex) { - const auto& nearOriginNode = m_nodes[node.nearOriginNodeIndex]; - const auto& farOriginNode = m_nodes[node.farOriginNodeIndex]; - float nearDistance = (node.position - nearOriginNode.position).length(); - float farDistance = (node.position - farOriginNode.position).length(); - float totalDistance = nearDistance + farDistance; - float distanceFactor = nearDistance / totalDistance; - const Vector3* revisedNearCutNormal = nullptr; - const Vector3* revisedFarCutNormal = nullptr; - if (distanceFactor <= 0.5) { - revisedNearCutNormal = &nearOriginNode.traverseDirection; - revisedFarCutNormal = &node.traverseDirection; - } else { - distanceFactor = (1.0 - distanceFactor); - revisedNearCutNormal = &farOriginNode.traverseDirection; - revisedFarCutNormal = &node.traverseDirection; - } - distanceFactor *= 1.75; - Vector3 newTraverseDirection; - if (Vector3::dotProduct(*revisedNearCutNormal, *revisedFarCutNormal) <= 0) - newTraverseDirection = (*revisedNearCutNormal * (1.0 - distanceFactor) - *revisedFarCutNormal * distanceFactor).normalized(); - else - newTraverseDirection = (*revisedNearCutNormal * (1.0 - distanceFactor) + *revisedFarCutNormal * distanceFactor).normalized(); - if (Vector3::dotProduct(newTraverseDirection, node.traverseDirection) <= 0) - newTraverseDirection = -newTraverseDirection; - revised.push_back({ node.index, newTraverseDirection }); - } - } - for (const auto& it : revised) - m_nodes[it.first].traverseDirection = it.second; -} - -void StrokeMeshBuilder::localAverageBaseNormals() -{ - std::vector> averaged; - for (size_t i = 0; i < m_nodeIndices.size(); ++i) { - size_t h, j; - if (m_isRing) { - h = (i + m_nodeIndices.size() - 1) % m_nodeIndices.size(); - j = (i + 1) % m_nodeIndices.size(); - } else { - h = i > 0 ? i - 1 : i; - j = i + 1 < m_nodeIndices.size() ? i + 1 : i; - } - const auto& nodeH = m_nodes[m_nodeIndices[h]]; - const auto& nodeI = m_nodes[m_nodeIndices[i]]; - const auto& nodeJ = m_nodes[m_nodeIndices[j]]; - averaged.push_back({ nodeI.index, - (nodeH.baseNormal + nodeI.baseNormal + nodeJ.baseNormal).normalized() }); - } - for (const auto& it : averaged) - m_nodes[it.first].baseNormal = it.second; -} - -void StrokeMeshBuilder::unifyBaseNormals() -{ - for (size_t i = 1; i < m_nodeIndices.size(); ++i) { - size_t h = m_nodeIndices[i - 1]; - const auto& nodeH = m_nodes[m_nodeIndices[h]]; - auto& nodeI = m_nodes[m_nodeIndices[i]]; - if (Vector3::dotProduct(nodeI.baseNormal, nodeH.baseNormal) < 0) - nodeI.baseNormal = -nodeI.baseNormal; - } -} - -void StrokeMeshBuilder::reviseNodeBaseNormal(Node& node) -{ - Vector3 orientedBaseNormal = Vector3::dotProduct(node.traverseDirection, node.baseNormal) >= 0 ? node.baseNormal : -node.baseNormal; - if (orientedBaseNormal.isZero()) { - orientedBaseNormal = calculateBaseNormalFromTraverseDirection(node.traverseDirection); - } - node.baseNormal = orientedBaseNormal; -} - -bool StrokeMeshBuilder::prepare() -{ - if (m_nodes.empty()) - return false; - - if (1 == m_nodes.size()) { - auto& node = m_nodes[0]; - node.traverseOrder = 0; - node.traverseDirection = Vector3(0, 1, 0); - node.baseNormal = Vector3(0, 0, 1); - return true; - } - - m_nodeIndices = sortedNodeIndices(&m_isRing); - - if (m_nodeIndices.empty()) - return false; - - std::vector edgeDirections; - for (size_t i = 0; i < m_nodeIndices.size(); ++i) { - m_nodes[m_nodeIndices[i]].traverseOrder = i; - size_t j; - if (m_isRing) { - j = (i + 1) % m_nodeIndices.size(); - } else { - j = i + 1 < m_nodeIndices.size() ? i + 1 : i; - } - edgeDirections.push_back((m_nodes[m_nodeIndices[j]].position - m_nodes[m_nodeIndices[i]].position).normalized()); - } - - for (size_t i = 0; i < m_nodeIndices.size(); ++i) { - size_t h; - if (m_isRing) { - h = (i + m_nodeIndices.size() - 1) % m_nodeIndices.size(); - } else { - h = i > 0 ? i - 1 : i; - } - m_nodes[m_nodeIndices[i]].traverseDirection = (edgeDirections[h] + edgeDirections[i]).normalized(); - } - reviseTraverseDirections(); - - // Base plane constraints - if (!m_baseNormalOnX || !m_baseNormalOnY || !m_baseNormalOnZ) { - for (auto& it : edgeDirections) { - if (!m_baseNormalOnX) - it.setX(0); - if (!m_baseNormalOnY) - it.setY(0); - if (!m_baseNormalOnZ) - it.setZ(0); - } - } - std::vector validBaseNormalPosArray; - for (size_t i = m_isRing ? 0 : 1; i < m_nodeIndices.size(); ++i) { - size_t h = (i + m_nodeIndices.size() - 1) % m_nodeIndices.size(); - // >15 degrees && < 165 degrees - if (abs(Vector3::dotProduct(edgeDirections[h], edgeDirections[i])) < 0.966) { - auto baseNormal = Vector3::crossProduct(edgeDirections[h], edgeDirections[i]); - if (!baseNormal.isZero()) { - if (!validBaseNormalPosArray.empty()) { - const auto& lastNode = m_nodes[m_nodeIndices[validBaseNormalPosArray[validBaseNormalPosArray.size() - 1]]]; - if (Vector3::dotProduct(lastNode.baseNormal, baseNormal) < 0) - baseNormal = -baseNormal; - } - auto& node = m_nodes[m_nodeIndices[i]]; - node.baseNormal = baseNormal; - validBaseNormalPosArray.push_back(i); - continue; - } - } - } - if (validBaseNormalPosArray.empty()) { - Vector3 baseNormal; - for (size_t i = 0; i < m_nodeIndices.size(); ++i) { - const auto& node = m_nodes[m_nodeIndices[i]]; - baseNormal += calculateBaseNormalFromTraverseDirection(node.traverseDirection); - } - baseNormal.normalize(); - for (size_t i = 0; i < m_nodeIndices.size(); ++i) { - auto& node = m_nodes[m_nodeIndices[i]]; - node.baseNormal = baseNormal; - } - } else if (1 == validBaseNormalPosArray.size()) { - auto baseNormal = m_nodes[m_nodeIndices[validBaseNormalPosArray[0]]].baseNormal; - for (size_t i = 0; i < m_nodeIndices.size(); ++i) { - auto& node = m_nodes[m_nodeIndices[i]]; - node.baseNormal = baseNormal; - } - } else { - if (!m_isRing) { - auto prePos = validBaseNormalPosArray[0]; - const auto& preNode = m_nodes[m_nodeIndices[prePos]]; - auto preBaseNormal = preNode.baseNormal; - auto afterPos = validBaseNormalPosArray[validBaseNormalPosArray.size() - 1]; - const auto& afterNode = m_nodes[m_nodeIndices[afterPos]]; - auto afterBaseNormal = afterNode.baseNormal; - for (size_t i = 0; i < prePos; ++i) { - auto& node = m_nodes[m_nodeIndices[i]]; - node.baseNormal = preBaseNormal; - } - for (size_t i = afterPos + 1; i < m_nodeIndices.size(); ++i) { - auto& node = m_nodes[m_nodeIndices[i]]; - node.baseNormal = afterBaseNormal; - } - } - auto updateInBetweenBaseNormal = [this](const Node& nodeU, const Node& nodeV, Node& updateNode) { - float distanceU = (updateNode.position - nodeU.position).length(); - float distanceV = (updateNode.position - nodeV.position).length(); - float totalDistance = distanceU + distanceV; - float factorU = 1.0 - distanceU / totalDistance; - float factorV = 1.0 - factorU; - auto baseNormal = (nodeU.baseNormal * factorU + nodeV.baseNormal * factorV).normalized(); - updateNode.baseNormal = baseNormal; - }; - for (size_t k = m_isRing ? 0 : 1; k < validBaseNormalPosArray.size(); ++k) { - size_t u = validBaseNormalPosArray[(k + validBaseNormalPosArray.size() - 1) % validBaseNormalPosArray.size()]; - size_t v = validBaseNormalPosArray[k]; - const auto& nodeU = m_nodes[m_nodeIndices[u]]; - const auto& nodeV = m_nodes[m_nodeIndices[v]]; - for (size_t i = (u + 1) % m_nodeIndices.size(); - i != v; - i = (i + 1) % m_nodeIndices.size()) { - auto& node = m_nodes[m_nodeIndices[i]]; - updateInBetweenBaseNormal(nodeU, nodeV, node); - } - } - if (m_baseNormalAverageEnabled) { - Vector3 baseNormal; - for (size_t i = 0; i < m_nodeIndices.size(); ++i) { - const auto& node = m_nodes[m_nodeIndices[i]]; - baseNormal += node.baseNormal; - } - baseNormal.normalize(); - for (size_t i = 0; i < m_nodeIndices.size(); ++i) { - auto& node = m_nodes[m_nodeIndices[i]]; - node.baseNormal = baseNormal; - } - } else { - unifyBaseNormals(); - localAverageBaseNormals(); - for (size_t i = 0; i < m_nodeIndices.size(); ++i) { - reviseNodeBaseNormal(m_nodes[m_nodeIndices[i]]); - } - unifyBaseNormals(); - } - } - - return true; -} - -std::vector StrokeMeshBuilder::sortedNodeIndices(bool* isRing) -{ - std::vector nodeIndices; - - size_t startingNodeIndex = 0; - if (!calculateStartingNodeIndex(&startingNodeIndex, isRing)) - return nodeIndices; - - size_t fromNodeIndex = startingNodeIndex; - std::unordered_set visited; - auto nodeIndex = fromNodeIndex; - while (true) { - if (visited.find(nodeIndex) != visited.end()) - break; - visited.insert(nodeIndex); - nodeIndices.push_back(nodeIndex); - const auto& node = m_nodes[nodeIndex]; - size_t neighborIndex = node.nextOrNeighborOtherThan(fromNodeIndex); - if (neighborIndex == nodeIndex) - break; - fromNodeIndex = nodeIndex; - nodeIndex = neighborIndex; - }; - - return nodeIndices; -} - -bool StrokeMeshBuilder::calculateStartingNodeIndex(size_t* startingNodeIndex, - bool* isRing) -{ - if (m_nodes.empty()) - return false; - - if (1 == m_nodes.size()) { - *startingNodeIndex = 0; - *isRing = false; - return true; - } - - auto findNearestNodeWithWorldCenter = [&](const std::vector& nodeIndices) { - std::vector> dist2Array; - for (const auto& i : nodeIndices) { - dist2Array.push_back({ i, (float)m_nodes[i].position.lengthSquared() }); - } - return std::min_element(dist2Array.begin(), dist2Array.end(), - [](const std::pair& first, - const std::pair& second) { - return first.second < second.second; - }) - ->first; - }; - - auto findEndpointNodeIndices = [&]() { - std::vector endpointIndices; - for (const auto& it : m_nodes) { - if (1 == it.neighbors.size()) - endpointIndices.push_back(it.index); - } - return endpointIndices; - }; - - auto endpointIndices = findEndpointNodeIndices(); - if (2 != endpointIndices.size()) { - // Invalid endpoint count, there must be a ring, choose the node which is nearest with world center - std::vector nodeIndices(m_nodes.size()); - for (size_t i = 0; i < m_nodes.size(); ++i) { - if (2 != m_nodes[i].neighbors.size()) - return false; - nodeIndices[i] = i; - } - *startingNodeIndex = findNearestNodeWithWorldCenter(nodeIndices); - *isRing = true; - return true; - } - - auto countAlignedDirections = [&](size_t nodeIndex) { - size_t alignedCount = 0; - size_t fromNodeIndex = nodeIndex; - std::unordered_set visited; - while (true) { - if (visited.find(nodeIndex) != visited.end()) - break; - visited.insert(nodeIndex); - const auto& node = m_nodes[nodeIndex]; - size_t neighborIndex = node.nextOrNeighborOtherThan(fromNodeIndex); - if (neighborIndex == nodeIndex) - break; - if (node.next == neighborIndex) - ++alignedCount; - fromNodeIndex = nodeIndex; - nodeIndex = neighborIndex; - }; - return alignedCount; - }; - - auto chooseStartingEndpointByAlignedDirections = [&](const std::vector& endpointIndices) { - std::vector> alignedDirections(endpointIndices.size()); - for (size_t i = 0; i < endpointIndices.size(); ++i) { - auto nodeIndex = endpointIndices[i]; - alignedDirections[i] = { nodeIndex, countAlignedDirections(nodeIndex) }; - } - std::sort(alignedDirections.begin(), alignedDirections.end(), [](const std::pair& first, const std::pair& second) { - return first.second > second.second; - }); - if (alignedDirections[0].second > alignedDirections[1].second) - return alignedDirections[0].first; - std::vector nodeIndices = { alignedDirections[0].first, alignedDirections[1].first }; - return findNearestNodeWithWorldCenter(nodeIndices); - }; - - *startingNodeIndex = chooseStartingEndpointByAlignedDirections(endpointIndices); - *isRing = false; - return true; -} - -Vector3 StrokeMeshBuilder::calculateDeformPosition(const Vector3& vertexPosition, const Vector3& ray, const Vector3& deformNormal, float deformFactor) -{ - Vector3 revisedNormal = Vector3::dotProduct(ray, deformNormal) < 0.0 ? -deformNormal : deformNormal; - Vector3 projectRayOnRevisedNormal = revisedNormal * (Vector3::dotProduct(ray, revisedNormal) / revisedNormal.lengthSquared()); - auto scaledProjct = projectRayOnRevisedNormal * deformFactor; - return vertexPosition + (scaledProjct - projectRayOnRevisedNormal); -} - -void StrokeMeshBuilder::applyDeform() -{ - float maxRadius = 0.0; - if (m_deformUnified) { - for (const auto& node : m_nodes) { - if (node.radius > maxRadius) - maxRadius = node.radius; - } - } - for (size_t i = 0; i < m_generatedVertices.size(); ++i) { - auto& position = m_generatedVertices[i]; - const auto& node = m_nodes[m_generatedVerticesSourceNodeIndices[i]]; - const auto& cutDirect = m_generatedVerticesCutDirects[i]; - auto ray = position - node.position; - Vector3 sum; - size_t count = 0; - float deformUnifyFactor = m_deformUnified ? maxRadius / node.radius : 1.0; - if (!Math::isEqual(m_deformThickness, (float)1.0)) { - auto deformedPosition = calculateDeformPosition(position, ray, node.baseNormal, m_deformThickness * deformUnifyFactor); - sum += deformedPosition; - ++count; - } - if (!Math::isEqual(m_deformWidth, (float)1.0)) { - auto deformedPosition = calculateDeformPosition(position, ray, Vector3::crossProduct(node.baseNormal, cutDirect), m_deformWidth * deformUnifyFactor); - sum += deformedPosition; - ++count; - } - if (count > 0) - position = sum / count; - } -} - -bool StrokeMeshBuilder::buildBaseNormalsOnly() -{ - return prepare(); -} - -bool StrokeMeshBuilder::build() -{ - if (!prepare()) - return false; - - buildMesh(); - applyDeform(); - interpolateCutEdges(); - stitchCuts(); - return true; -} - -} diff --git a/dust3d/mesh/stroke_mesh_builder.h b/dust3d/mesh/stroke_mesh_builder.h deleted file mode 100644 index e459e04a..00000000 --- a/dust3d/mesh/stroke_mesh_builder.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2016-2021 Jeremy HU . All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef DUST3D_MESH_STOKE_MESH_BUILDER_H_ -#define DUST3D_MESH_STOKE_MESH_BUILDER_H_ - -#include -#include -#include -#include -#include - -namespace dust3d { - -class StrokeMeshBuilder { -public: - struct CutFaceTransform { - Vector3 translation; - float scale; - Vector3 uFactor; - Vector3 vFactor; - bool reverse = false; - }; - - struct Node { - float radius; - Vector3 position; - std::vector cutTemplate; - float cutRotation; - int nearOriginNodeIndex = -1; - int farOriginNodeIndex = -1; - - size_t index; - std::vector neighbors; - size_t next; - Vector3 cutNormal; - Vector3 traverseDirection; - Vector3 baseNormal; - size_t traverseOrder; - - size_t nextOrNeighborOtherThan(size_t neighborIndex) const; - }; - - size_t addNode(const Vector3& position, float radius, const std::vector& cutTemplate, float cutRotation); - void addEdge(size_t firstNodeIndex, size_t secondNodeIndex); - void setNodeOriginInfo(size_t nodeIndex, int nearOriginNodeIndex, int farOriginNodeIndex); - void setDeformThickness(float thickness); - void setDeformWidth(float width); - void setDeformUnified(bool unified); - void setHollowThickness(float hollowThickness); - void enableBaseNormalOnX(bool enabled); - void enableBaseNormalOnY(bool enabled); - void enableBaseNormalOnZ(bool enabled); - void enableBaseNormalAverage(bool enabled); - bool buildBaseNormalsOnly(); - const std::vector& nodes() const; - const std::vector& nodeIndices() const; - const Vector3& nodeTraverseDirection(size_t nodeIndex) const; - const Vector3& nodeBaseNormal(size_t nodeIndex) const; - size_t nodeTraverseOrder(size_t nodeIndex) const; - bool build(); - const std::vector& generatedVertices(); - const std::vector>& generatedFaces(); - const std::vector& generatedVerticesSourceNodeIndices(); - - static Vector3 calculateDeformPosition(const Vector3& vertexPosition, const Vector3& ray, const Vector3& deformNormal, float deformFactor); - static Vector3 calculateBaseNormalFromTraverseDirection(const Vector3& traverseDirection); - -private: - struct GeneratedVertexInfo { - size_t orderInCut; - size_t cutSize; - }; - - std::vector m_nodes; - float m_deformThickness = 1.0f; - float m_deformWidth = 1.0f; - float m_cutRotation = 0.0f; - bool m_baseNormalOnX = true; - bool m_baseNormalOnY = true; - bool m_baseNormalOnZ = true; - bool m_baseNormalAverageEnabled = false; - float m_hollowThickness = 0.0f; - bool m_deformUnified = false; - - bool m_isRing = false; - std::vector m_nodeIndices; - std::vector m_generatedVertices; - std::vector m_generatedVerticesCutDirects; - std::vector m_generatedVerticesSourceNodeIndices; - std::vector m_generatedVerticesInfos; - std::vector> m_generatedFaces; - - std::vector> m_cuts; - - bool prepare(); - std::vector makeCut(const Vector3& cutCenter, - float radius, - const std::vector& cutTemplate, - const Vector3& cutNormal, - const Vector3& baseNormal); - void insertCutVertices(const std::vector& cut, - std::vector* vertices, - size_t nodeIndex, - const Vector3& cutNormal); - void buildMesh(); - std::vector sortedNodeIndices(bool* isRing); - bool calculateStartingNodeIndex(size_t* startingNodeIndex, - bool* isRing); - void reviseTraverseDirections(); - void localAverageBaseNormals(); - void unifyBaseNormals(); - std::vector edgeloopFlipped(const std::vector& edgeLoop); - void reviseNodeBaseNormal(Node& node); - void applyDeform(); - void interpolateCutEdges(); - void stitchCuts(); -}; - -} - -#endif diff --git a/dust3d/mesh/stroke_modifier.cc b/dust3d/mesh/stroke_modifier.cc deleted file mode 100644 index c25b211b..00000000 --- a/dust3d/mesh/stroke_modifier.cc +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (c) 2016-2021 Jeremy HU . All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include - -namespace dust3d { - -void StrokeModifier::enableIntermediateAddition() -{ - m_intermediateAdditionEnabled = true; -} - -void StrokeModifier::enableSmooth() -{ - m_smooth = true; -} - -size_t StrokeModifier::addNode(const Vector3& position, float radius, const std::vector& cutTemplate, float cutRotation) -{ - size_t nodeIndex = m_nodes.size(); - - Node node; - node.isOriginal = true; - node.position = position; - node.radius = radius; - node.cutTemplate = cutTemplate; - node.cutRotation = cutRotation; - node.originNodeIndex = nodeIndex; - m_nodes.push_back(node); - - return nodeIndex; -} - -size_t StrokeModifier::addEdge(size_t firstNodeIndex, size_t secondNodeIndex) -{ - size_t edgeIndex = m_edges.size(); - - Edge edge; - edge.firstNodeIndex = firstNodeIndex; - edge.secondNodeIndex = secondNodeIndex; - m_edges.push_back(edge); - - return edgeIndex; -} - -void StrokeModifier::createIntermediateNode(const Node& firstNode, const Node& secondNode, float factor, Node* resultNode) -{ - float firstFactor = 1.0 - factor; - resultNode->position = firstNode.position * firstFactor + secondNode.position * factor; - resultNode->radius = firstNode.radius * firstFactor + secondNode.radius * factor; - if (factor <= 0.5) { - resultNode->originNodeIndex = firstNode.originNodeIndex; - resultNode->nearOriginNodeIndex = firstNode.originNodeIndex; - resultNode->farOriginNodeIndex = secondNode.originNodeIndex; - resultNode->cutRotation = firstNode.cutRotation; - resultNode->cutTemplate = firstNode.cutTemplate; - } else { - resultNode->originNodeIndex = secondNode.originNodeIndex; - resultNode->nearOriginNodeIndex = secondNode.originNodeIndex; - resultNode->farOriginNodeIndex = firstNode.originNodeIndex; - resultNode->cutRotation = secondNode.cutRotation; - resultNode->cutTemplate = secondNode.cutTemplate; - } -} - -void StrokeModifier::subdivide() -{ - for (auto& node : m_nodes) { - subdivideFace(&node.cutTemplate); - } -} - -void StrokeModifier::subdivideFace(std::vector* face) -{ - auto oldFace = *face; - face->resize(oldFace.size() * 2); - for (size_t i = 0, n = 0; i < oldFace.size(); ++i) { - size_t h = (i + oldFace.size() - 1) % oldFace.size(); - size_t j = (i + 1) % oldFace.size(); - (*face)[n++] = oldFace[h] * 0.125 + oldFace[i] * 0.75 + oldFace[j] * 0.125; - (*face)[n++] = (oldFace[i] + oldFace[j]) * 0.5; - } -} - -float StrokeModifier::averageCutTemplateEdgeLength(const std::vector& cutTemplate) -{ - if (cutTemplate.empty()) - return 0; - - float sum = 0; - for (size_t i = 0; i < cutTemplate.size(); ++i) { - size_t j = (i + 1) % cutTemplate.size(); - sum += (cutTemplate[i] - cutTemplate[j]).length(); - } - return sum / cutTemplate.size(); -} - -void StrokeModifier::roundEnd() -{ - std::map> neighbors; - for (const auto& edge : m_edges) { - neighbors[edge.firstNodeIndex].push_back(edge.secondNodeIndex); - neighbors[edge.secondNodeIndex].push_back(edge.firstNodeIndex); - } - for (const auto& it : neighbors) { - if (1 == it.second.size()) { - const Node& currentNode = m_nodes[it.first]; - const Node& neighborNode = m_nodes[it.second[0]]; - Node endNode; - endNode.radius = currentNode.radius * 0.5; - endNode.position = currentNode.position + (currentNode.position - neighborNode.position).normalized() * endNode.radius; - endNode.cutTemplate = currentNode.cutTemplate; - endNode.cutRotation = currentNode.cutRotation; - endNode.originNodeIndex = currentNode.originNodeIndex; - size_t endNodeIndex = m_nodes.size(); - m_nodes.push_back(endNode); - addEdge(endNode.originNodeIndex, endNodeIndex); - } - } -} - -void StrokeModifier::createIntermediateCutTemplateEdges(std::vector& cutTemplate, float averageCutTemplateLength) -{ - std::vector newCutTemplate; - auto pointCount = cutTemplate.size(); - float targetLength = averageCutTemplateLength * 1.2; - for (size_t index = 0; index < pointCount; ++index) { - size_t nextIndex = (index + 1) % pointCount; - newCutTemplate.push_back(cutTemplate[index]); - float oldEdgeLength = (cutTemplate[index] - cutTemplate[nextIndex]).length(); - if (targetLength >= oldEdgeLength) - continue; - size_t newInsertNum = oldEdgeLength / targetLength; - if (newInsertNum < 1) - newInsertNum = 1; - if (newInsertNum > 100) - continue; - float stepFactor = 1.0 / (newInsertNum + 1); - float factor = stepFactor; - for (size_t i = 0; i < newInsertNum && factor < 1.0; factor += stepFactor, ++i) { - float firstFactor = 1.0 - factor; - newCutTemplate.push_back(cutTemplate[index] * firstFactor + cutTemplate[nextIndex] * factor); - } - } - cutTemplate = newCutTemplate; -} - -void StrokeModifier::finalize() -{ - if (!m_intermediateAdditionEnabled) - return; - - for (auto& node : m_nodes) { - node.averageCutTemplateLength = averageCutTemplateEdgeLength(node.cutTemplate); - createIntermediateCutTemplateEdges(node.cutTemplate, node.averageCutTemplateLength); - } - - auto oldEdges = m_edges; - m_edges.clear(); - for (const auto& edge : oldEdges) { - const Node& firstNode = m_nodes[edge.firstNodeIndex]; - const Node& secondNode = m_nodes[edge.secondNodeIndex]; - auto firstAverageCutTemplateEdgeLength = firstNode.averageCutTemplateLength * firstNode.radius; - auto secondAverageCutTemplateEdgeLength = secondNode.averageCutTemplateLength * secondNode.radius; - float targetEdgeLength = (firstAverageCutTemplateEdgeLength + secondAverageCutTemplateEdgeLength) * 0.5; - float currentEdgeLength = (firstNode.position - secondNode.position).length(); - if (targetEdgeLength >= currentEdgeLength) { - addEdge(edge.firstNodeIndex, edge.secondNodeIndex); - continue; - } - size_t newInsertNum = currentEdgeLength / targetEdgeLength; - if (newInsertNum < 1) - newInsertNum = 1; - if (newInsertNum > 100) { - addEdge(edge.firstNodeIndex, edge.secondNodeIndex); - continue; - } - float stepFactor = 1.0 / (newInsertNum + 1); - std::vector nodeIndices; - nodeIndices.push_back(edge.firstNodeIndex); - float factor = stepFactor; - for (size_t i = 0; i < newInsertNum && factor < 1.0; factor += stepFactor, ++i) { - Node intermediateNode; - const Node& firstNode = m_nodes[edge.firstNodeIndex]; - const Node& secondNode = m_nodes[edge.secondNodeIndex]; - createIntermediateNode(firstNode, secondNode, factor, &intermediateNode); - size_t intermedidateNodeIndex = m_nodes.size(); - nodeIndices.push_back(intermedidateNodeIndex); - m_nodes.push_back(intermediateNode); - } - nodeIndices.push_back(edge.secondNodeIndex); - for (size_t i = 1; i < nodeIndices.size(); ++i) { - addEdge(nodeIndices[i - 1], nodeIndices[i]); - } - } - - if (m_smooth) - smooth(); -} - -void StrokeModifier::smooth() -{ - std::unordered_map> neighborMap; - for (const auto& edge : m_edges) { - neighborMap[edge.firstNodeIndex].push_back(edge.secondNodeIndex); - neighborMap[edge.secondNodeIndex].push_back(edge.firstNodeIndex); - } - - int startEndpoint = 0; - for (const auto& edge : m_edges) { - auto findNeighbor = neighborMap.find(edge.firstNodeIndex); - if (findNeighbor == neighborMap.end()) - continue; - if (1 != findNeighbor->second.size()) { - auto findNeighborNeighbor = neighborMap.find(edge.secondNodeIndex); - if (findNeighborNeighbor == neighborMap.end()) - continue; - if (1 != findNeighborNeighbor->second.size()) - continue; - startEndpoint = edge.secondNodeIndex; - } else { - startEndpoint = edge.firstNodeIndex; - } - break; - } - if (-1 == startEndpoint) - return; - - int loopIndex = startEndpoint; - int previousIndex = -1; - bool isRing = false; - std::vector loop; - while (-1 != loopIndex) { - loop.push_back(loopIndex); - auto findNeighbor = neighborMap.find(loopIndex); - if (findNeighbor == neighborMap.end()) - return; - int nextIndex = -1; - for (const auto& index : findNeighbor->second) { - if (index == previousIndex) - continue; - if (index == startEndpoint) { - isRing = true; - break; - } - nextIndex = index; - break; - } - previousIndex = loopIndex; - loopIndex = nextIndex; - } - - CentripetalCatmullRomSpline spline(isRing); - for (size_t i = 0; i < loop.size(); ++i) { - const auto& nodeIndex = loop[i]; - const auto& node = m_nodes[nodeIndex]; - bool isKnot = node.originNodeIndex == nodeIndex; - spline.addPoint((int)nodeIndex, node.position, isKnot); - } - if (!spline.interpolate()) - return; - for (const auto& it : spline.splineNodes()) { - if (-1 == it.source) - continue; - auto& node = m_nodes[it.source]; - node.position = it.position; - } -} - -const std::vector& StrokeModifier::nodes() const -{ - return m_nodes; -} - -const std::vector& StrokeModifier::edges() const -{ - return m_edges; -} - -} diff --git a/dust3d/mesh/stroke_modifier.h b/dust3d/mesh/stroke_modifier.h deleted file mode 100644 index 4d2c898f..00000000 --- a/dust3d/mesh/stroke_modifier.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2016-2021 Jeremy HU . All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef DUST3D_MESH_STROKE_MODIFIER_H_ -#define DUST3D_MESH_STROKE_MODIFIER_H_ - -#include -#include - -namespace dust3d { - -class StrokeModifier { -public: - struct Node { - bool isOriginal = false; - Vector3 position; - float radius = 0.0; - std::vector cutTemplate; - float cutRotation = 0.0; - int nearOriginNodeIndex = -1; - int farOriginNodeIndex = -1; - int originNodeIndex = 0; - float averageCutTemplateLength; - }; - - struct Edge { - size_t firstNodeIndex; - size_t secondNodeIndex; - }; - - size_t addNode(const Vector3& position, float radius, const std::vector& cutTemplate, float cutRotation); - size_t addEdge(size_t firstNodeIndex, size_t secondNodeIndex); - void subdivide(); - void roundEnd(); - void enableIntermediateAddition(); - void enableSmooth(); - const std::vector& nodes() const; - const std::vector& edges() const; - void finalize(); - - static void subdivideFace(std::vector* face); - -private: - std::vector m_nodes; - std::vector m_edges; - bool m_intermediateAdditionEnabled = false; - bool m_smooth = false; - - void createIntermediateNode(const Node& firstNode, const Node& secondNode, float factor, Node* resultNode); - float averageCutTemplateEdgeLength(const std::vector& cutTemplate); - void createIntermediateCutTemplateEdges(std::vector& cutTemplate, float averageCutTemplateLength); - void smooth(); -}; - -} - -#endif