diff --git a/application/application.pro b/application/application.pro index 36844107..59fe90de 100644 --- a/application/application.pro +++ b/application/application.pro @@ -283,6 +283,8 @@ HEADERS += ../dust3d/mesh/resolve_triangle_tangent.h SOURCES += ../dust3d/mesh/resolve_triangle_tangent.cc 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 diff --git a/application/resources/model-screwdriver.ds3 b/application/resources/model-screwdriver.ds3 index 63b199f0..241fba3d 100644 Binary files a/application/resources/model-screwdriver.ds3 and b/application/resources/model-screwdriver.ds3 differ diff --git a/application/sources/material_previews_generator.cc b/application/sources/material_previews_generator.cc index 0cdf2e51..03c523fd 100644 --- a/application/sources/material_previews_generator.cc +++ b/application/sources/material_previews_generator.cc @@ -48,7 +48,7 @@ void MaterialPreviewsGenerator::generate() std::vector partIds; dust3d::Ds3FileReader ds3Reader((const std::uint8_t *)fileData.data(), fileData.size()); - for (int i = 0; i < ds3Reader.items().size(); ++i) { + for (int i = 0; i < (int)ds3Reader.items().size(); ++i) { dust3d::Ds3ReaderItem item = ds3Reader.items().at(i); if (item.type == "model") { std::vector data; diff --git a/dust3d/base/part_target.h b/dust3d/base/part_target.h index 61b06073..ebd511d8 100644 --- a/dust3d/base/part_target.h +++ b/dust3d/base/part_target.h @@ -32,6 +32,7 @@ enum class PartTarget { Model = 0, CutFace, + StitchingLine, Count }; PartTarget PartTargetFromString(const char *targetString); @@ -43,6 +44,8 @@ PartTarget PartTargetFromString(const char *targetString) return PartTarget::Model; \ if (target == "CutFace") \ return PartTarget::CutFace; \ + if (target == "StitchingLine") \ + return PartTarget::StitchingLine; \ return PartTarget::Model; \ } const char *PartTargetToString(PartTarget target); @@ -54,6 +57,8 @@ const char *PartTargetToString(PartTarget target) return "Model"; \ case PartTarget::CutFace: \ return "CutFace"; \ + case PartTarget::StitchingLine: \ + return "StitchingLine"; \ default: \ return "Model"; \ } \ @@ -67,6 +72,8 @@ std::string PartTargetToDispName(PartTarget target) return std::string("Model"); \ case PartTarget::CutFace: \ return std::string("Cut Face"); \ + case PartTarget::StitchingLine: \ + return std::string("Stitching Line"); \ default: \ return std::string("Model"); \ } \ diff --git a/dust3d/mesh/mesh_combiner.cc b/dust3d/mesh/mesh_combiner.cc index c55e2a09..eacb37d9 100644 --- a/dust3d/mesh/mesh_combiner.cc +++ b/dust3d/mesh/mesh_combiner.cc @@ -31,31 +31,28 @@ namespace dust3d MeshCombiner::Mesh::Mesh(const std::vector &vertices, const std::vector> &faces) { - m_vertices = new std::vector(vertices); - m_triangles = new std::vector>; - triangulate(vertices, faces, m_triangles); - m_solidMesh = new SolidMesh; - m_solidMesh->setVertices(m_vertices); - m_solidMesh->setTriangles(m_triangles); + m_vertices = std::make_unique>(vertices); + m_triangles = std::make_unique>>(); + triangulate(vertices, faces, m_triangles.get()); + m_solidMesh = std::make_unique(); + m_solidMesh->setVertices(m_vertices.get()); + m_solidMesh->setTriangles(m_triangles.get()); m_solidMesh->prepare(); } MeshCombiner::Mesh::Mesh(const Mesh &other) { - m_vertices = new std::vector; - m_triangles = new std::vector>; + m_vertices = std::make_unique>(); + m_triangles = std::make_unique>>(); other.fetch(*m_vertices, *m_triangles); - m_solidMesh = new SolidMesh; - m_solidMesh->setVertices(m_vertices); - m_solidMesh->setTriangles(m_triangles); + m_solidMesh = std::make_unique(); + m_solidMesh->setVertices(m_vertices.get()); + m_solidMesh->setTriangles(m_triangles.get()); m_solidMesh->prepare(); } MeshCombiner::Mesh::~Mesh() { - delete m_solidMesh; - delete m_vertices; - delete m_triangles; } void MeshCombiner::Mesh::fetch(std::vector &vertices, std::vector> &faces) const @@ -72,19 +69,13 @@ bool MeshCombiner::Mesh::isNull() const return nullptr == m_vertices || m_vertices->empty(); } -bool MeshCombiner::Mesh::isCombinable() const -{ - return true; -} - MeshCombiner::Mesh *MeshCombiner::combine(const Mesh &firstMesh, const Mesh &secondMesh, Method method, std::vector> *combinedVerticesComeFrom) { - if (firstMesh.isNull() || !firstMesh.isCombinable() || - secondMesh.isNull() || !secondMesh.isCombinable()) + if (firstMesh.isNull() || secondMesh.isNull()) return nullptr; - SolidMeshBooleanOperation booleanOperation(firstMesh.m_solidMesh, secondMesh.m_solidMesh); + SolidMeshBooleanOperation booleanOperation(firstMesh.m_solidMesh.get(), secondMesh.m_solidMesh.get()); if (!booleanOperation.combine()) return nullptr; @@ -96,14 +87,14 @@ MeshCombiner::Mesh *MeshCombiner::combine(const Mesh &firstMesh, const Mesh &sec if (nullptr == vertices) return; for (const auto &point: *vertices) { - auto insertResult = verticesSourceMap.insert({{point.x(), point.y(), point.z()}, + verticesSourceMap.insert({{point.x(), point.y(), point.z()}, {source, vertexIndex}}); ++vertexIndex; } }; if (nullptr != combinedVerticesComeFrom) { - addToSourceMap(firstMesh.m_solidMesh, Source::First); - addToSourceMap(secondMesh.m_solidMesh, Source::Second); + addToSourceMap(firstMesh.m_solidMesh.get(), Source::First); + addToSourceMap(secondMesh.m_solidMesh.get(), Source::Second); } std::vector> resultTriangles; diff --git a/dust3d/mesh/mesh_combiner.h b/dust3d/mesh/mesh_combiner.h index 9f0c380e..68f76d5a 100644 --- a/dust3d/mesh/mesh_combiner.h +++ b/dust3d/mesh/mesh_combiner.h @@ -24,6 +24,7 @@ #define DUST3D_MESH_MESH_COMBINER_H_ #include +#include #include #include @@ -55,14 +56,13 @@ public: ~Mesh(); void fetch(std::vector &vertices, std::vector> &faces) const; bool isNull() const; - bool isCombinable() const; friend MeshCombiner; private: - SolidMesh *m_solidMesh = nullptr; - std::vector *m_vertices = nullptr; - std::vector> *m_triangles = nullptr; + std::unique_ptr m_solidMesh; + std::unique_ptr> m_vertices; + std::unique_ptr>> m_triangles; }; static Mesh *combine(const Mesh &firstMesh, const Mesh &secondMesh, Method method, diff --git a/dust3d/mesh/mesh_generator.cc b/dust3d/mesh/mesh_generator.cc index d6274fe1..6824fe07 100644 --- a/dust3d/mesh/mesh_generator.cc +++ b/dust3d/mesh/mesh_generator.cc @@ -375,7 +375,7 @@ void MeshGenerator::cutFaceStringToCutTemplate(const std::string &cutFaceString, } } -MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdString, bool *hasError, bool *retryable, bool addIntermediateNodes) +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()) { @@ -473,7 +473,6 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri partCache.previewVertices.clear(); partCache.isSuccessful = false; partCache.joined = (target == PartTarget::Model && !isDisabled); - partCache.releaseMeshes(); struct NodeInfo { @@ -545,7 +544,6 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri bool buildSucceed = false; std::map nodeIdStringToIndexMap; std::map nodeIndexToIdStringMap; - StrokeModifier *strokeModifier = nullptr; auto addNodeToPartCache = [&](const std::string &nodeIdString, const NodeInfo &nodeInfo) { ObjectNode objectNode; @@ -575,7 +573,7 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri }); }; - strokeModifier = new StrokeModifier; + auto strokeModifier = std::make_unique(); if (smooth) strokeModifier->enableSmooth(); @@ -626,7 +624,7 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri std::vector sourceNodeIndices; - StrokeMeshBuilder *strokeMeshBuilder = new StrokeMeshBuilder; + auto strokeMeshBuilder = std::make_unique(); strokeMeshBuilder->setDeformThickness(deformThickness); strokeMeshBuilder->setDeformWidth(deformWidth); @@ -680,14 +678,11 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri partCache.objectNodeVertices.push_back({position, {partIdString, nodeIdString}}); } - delete strokeMeshBuilder; - strokeMeshBuilder = nullptr; - bool hasMeshError = false; - MeshCombiner::Mesh *mesh = nullptr; + std::unique_ptr mesh; if (buildSucceed) { - mesh = new MeshCombiner::Mesh(partCache.vertices, partCache.faces); + mesh = std::make_unique(partCache.vertices, partCache.faces); if (mesh->isNull()) { hasMeshError = true; } @@ -698,7 +693,6 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri std::vector partPreviewVertices; Color partPreviewColor = partColor; if (nullptr != mesh) { - partCache.mesh = new MeshCombiner::Mesh(*mesh); mesh->fetch(partPreviewVertices, partCache.previewTriangles); partCache.previewVertices = partPreviewVertices; partCache.isSuccessful = true; @@ -747,21 +741,16 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri } } - delete strokeModifier; - if (mesh && mesh->isNull()) { - delete mesh; - mesh = nullptr; + mesh.reset(); } if (isDisabled) { - delete mesh; - mesh = nullptr; + mesh.reset(); } if (target != PartTarget::Model) { - delete mesh; - mesh = nullptr; + mesh.reset(); } if (hasMeshError && target == PartTarget::Model) { @@ -820,9 +809,9 @@ std::string MeshGenerator::componentColorName(const std::map MeshGenerator::combineComponentMesh(const std::string &componentIdString, CombineMode *combineMode) { - MeshCombiner::Mesh *mesh = nullptr; + std::unique_ptr mesh; Uuid componentId; const std::map *component = &m_snapshot->rootComponent; @@ -842,7 +831,7 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &compo if (m_cacheEnabled) { if (m_dirtyComponentIds.find(componentIdString) == m_dirtyComponentIds.end()) { if (nullptr != componentCache.mesh) - return new MeshCombiner::Mesh(*componentCache.mesh); + return std::make_unique(*componentCache.mesh); } } @@ -851,7 +840,7 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &compo componentCache.objectNodes.clear(); componentCache.objectEdges.clear(); componentCache.objectNodeVertices.clear(); - componentCache.releaseMeshes(); + componentCache.mesh.reset(); std::string linkDataType = String::valueOrEmpty(*component, "linkDataType"); if ("partId" == linkDataType) { @@ -860,8 +849,7 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &compo bool retryable = true; mesh = combinePartMesh(partIdString, &hasError, &retryable, m_interpolationEnabled); if (hasError) { - delete mesh; - mesh = nullptr; + mesh.reset(); if (retryable && m_interpolationEnabled) { hasError = false; mesh = combinePartMesh(partIdString, &hasError, &retryable, false); @@ -870,7 +858,6 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &compo m_isSuccessful = false; } } - const auto &partCache = m_cacheContext->parts[partIdString]; for (const auto &vertex: partCache.vertices) componentCache.noneSeamVertices.insert(vertex); @@ -882,19 +869,13 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &compo for (const auto &it: partCache.objectNodeVertices) componentCache.objectNodeVertices.push_back(it); } else { - std::vector>>> combineGroups; - // Firstly, group by combine mode + std::vector>> combineGroups; int currentGroupIndex = -1; auto lastCombineMode = CombineMode::Count; - bool foundColorSolubilitySetting = false; for (const auto &childIdString: String::split(String::valueOrEmpty(*component, "children"), ',')) { if (childIdString.empty()) continue; const auto &child = findComponent(childIdString); - std::string colorName = componentColorName(child); - if (colorName == "+") { - foundColorSolubilitySetting = true; - } auto combineMode = componentCombineMode(child); if (lastCombineMode != combineMode || lastCombineMode == CombineMode::Inversion) { combineGroups.push_back({combineMode, {}}); @@ -904,162 +885,87 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &compo if (-1 == currentGroupIndex) { continue; } - combineGroups[currentGroupIndex].second.push_back({childIdString, colorName}); + combineGroups[currentGroupIndex].second.push_back(childIdString); } - // Secondly, sub group by color - std::vector> groupMeshes; + std::vector, CombineMode, std::string>> groupMeshes; for (const auto &group: combineGroups) { - std::set used; - std::vector> componentIdStrings; - int currentSubGroupIndex = -1; - auto lastColorName = std::string(); - for (size_t i = 0; i < group.second.size(); ++i) { - if (used.find(i) != used.end()) - continue; - //const auto &colorName = group.second[i].second; - const std::string colorName = "white"; // Force to use the same color = deactivate combine by color - if (lastColorName != colorName || lastColorName.empty()) { - componentIdStrings.push_back({}); - ++currentSubGroupIndex; - lastColorName = colorName; - } - if (-1 == currentSubGroupIndex) { - continue; - } - used.insert(i); - componentIdStrings[currentSubGroupIndex].push_back(group.second[i].first); - if (colorName.empty()) - continue; - for (size_t j = i + 1; j < group.second.size(); ++j) { - if (used.find(j) != used.end()) - continue; - const auto &otherColorName = group.second[j].second; - if (otherColorName.empty()) - continue; - if (otherColorName != colorName) - continue; - used.insert(j); - componentIdStrings[currentSubGroupIndex].push_back(group.second[j].first); - } - } - std::vector> multipleMeshes; - std::vector subGroupMeshIdStringList; - for (const auto &it: componentIdStrings) { - std::vector componentChildGroupIdStringList; - for (const auto &componentChildGroupIdString: it) - componentChildGroupIdStringList.push_back(componentChildGroupIdString); - MeshCombiner::Mesh *childMesh = combineComponentChildGroupMesh(it, componentCache); - if (nullptr == childMesh) - continue; - if (childMesh->isNull()) { - delete childMesh; - continue; - } - std::string componentChildGroupIdStringListString = String::join(componentChildGroupIdStringList, "|"); - subGroupMeshIdStringList.push_back(componentChildGroupIdStringListString); - multipleMeshes.push_back(std::make_tuple(childMesh, CombineMode::Normal, componentChildGroupIdStringListString)); - } - MeshCombiner::Mesh *subGroupMesh = combineMultipleMeshes(multipleMeshes, true/*foundColorSolubilitySetting*/); - if (nullptr == subGroupMesh) + auto childMesh = combineComponentChildGroupMesh(group.second, componentCache); + if (nullptr == childMesh || childMesh->isNull()) continue; - groupMeshes.push_back(std::make_tuple(subGroupMesh, group.first, String::join(subGroupMeshIdStringList, "&"))); + groupMeshes.emplace_back(std::make_tuple(std::move(childMesh), group.first, String::join(group.second, "|"))); } - mesh = combineMultipleMeshes(groupMeshes, true); + mesh = combineMultipleMeshes(std::move(groupMeshes), true); } if (nullptr != mesh) - componentCache.mesh = new MeshCombiner::Mesh(*mesh); + componentCache.mesh = std::make_unique(*mesh); if (nullptr != mesh && mesh->isNull()) { - delete mesh; - mesh = nullptr; - } - - if (componentId.isNull()) { - // Prepare cloth collision shap - if (nullptr != mesh && !mesh->isNull()) { - m_clothCollisionVertices.clear(); - m_clothCollisionTriangles.clear(); - mesh->fetch(m_clothCollisionVertices, m_clothCollisionTriangles); - } else { - // TODO: when no body is valid, may add ground plane as collision shape - // ... ... - } + mesh.reset(); } return mesh; } -MeshCombiner::Mesh *MeshGenerator::combineMultipleMeshes(const std::vector> &multipleMeshes, bool recombine) +std::unique_ptr MeshGenerator::combineMultipleMeshes(std::vector, CombineMode, std::string>> &&multipleMeshes, bool recombine) { - MeshCombiner::Mesh *mesh = nullptr; + std::unique_ptr mesh; std::string meshIdStrings; - for (const auto &it: multipleMeshes) { + for (auto &it: multipleMeshes) { + auto subMesh = std::move(std::get<0>(it)); const auto &childCombineMode = std::get<1>(it); - MeshCombiner::Mesh *subMesh = std::get<0>(it); const std::string &subMeshIdString = std::get<2>(it); if (nullptr == subMesh || subMesh->isNull()) { - delete subMesh; - continue; - } - if (!subMesh->isCombinable()) { - // TODO: Collect vertices - delete subMesh; continue; } if (nullptr == mesh) { - mesh = subMesh; + mesh = std::move(subMesh); meshIdStrings = subMeshIdString; + continue; + } + auto combinerMethod = childCombineMode == CombineMode::Inversion ? + MeshCombiner::Method::Diff : MeshCombiner::Method::Union; + auto combinerMethodString = combinerMethod == MeshCombiner::Method::Union ? + "+" : "-"; + meshIdStrings += combinerMethodString + subMeshIdString; + if (recombine) + meshIdStrings += "!"; + std::unique_ptr newMesh; + auto findCached = m_cacheContext->cachedCombination.find(meshIdStrings); + if (findCached != m_cacheContext->cachedCombination.end()) { + if (nullptr != findCached->second) { + newMesh = std::make_unique(*findCached->second); + } } else { - auto combinerMethod = childCombineMode == CombineMode::Inversion ? - MeshCombiner::Method::Diff : MeshCombiner::Method::Union; - auto combinerMethodString = combinerMethod == MeshCombiner::Method::Union ? - "+" : "-"; - meshIdStrings += combinerMethodString + subMeshIdString; - if (recombine) - meshIdStrings += "!"; - MeshCombiner::Mesh *newMesh = nullptr; - auto findCached = m_cacheContext->cachedCombination.find(meshIdStrings); - if (findCached != m_cacheContext->cachedCombination.end()) { - if (nullptr != findCached->second) { - newMesh = new MeshCombiner::Mesh(*findCached->second); - } - } else { - newMesh = combineTwoMeshes(*mesh, - *subMesh, - combinerMethod, - recombine); - delete subMesh; - if (nullptr != newMesh) - m_cacheContext->cachedCombination.insert({meshIdStrings, new MeshCombiner::Mesh(*newMesh)}); - else - m_cacheContext->cachedCombination.insert({meshIdStrings, nullptr}); - } - if (newMesh && !newMesh->isNull()) { - delete mesh; - mesh = newMesh; - } else { - m_isSuccessful = false; - delete newMesh; - } + newMesh = combineTwoMeshes(*mesh, + *subMesh, + combinerMethod, + recombine); + if (nullptr != newMesh) + m_cacheContext->cachedCombination.insert({meshIdStrings, std::make_unique(*newMesh)}); + else + m_cacheContext->cachedCombination.insert({meshIdStrings, nullptr}); + } + if (newMesh && !newMesh->isNull()) { + mesh = std::move(newMesh); + } else { + m_isSuccessful = false; } } if (nullptr != mesh && mesh->isNull()) { - delete mesh; - mesh = nullptr; + mesh.reset(); } return mesh; } -MeshCombiner::Mesh *MeshGenerator::combineComponentChildGroupMesh(const std::vector &componentIdStrings, GeneratedComponent &componentCache) +std::unique_ptr MeshGenerator::combineComponentChildGroupMesh(const std::vector &componentIdStrings, GeneratedComponent &componentCache) { - std::vector> multipleMeshes; + std::vector, CombineMode, std::string>> multipleMeshes; for (const auto &childIdString: componentIdStrings) { CombineMode childCombineMode = CombineMode::Normal; - MeshCombiner::Mesh *subMesh = combineComponentMesh(childIdString, &childCombineMode); + std::unique_ptr subMesh = combineComponentMesh(childIdString, &childCombineMode); if (CombineMode::Uncombined == childCombineMode) { - delete subMesh; continue; } @@ -1076,31 +982,25 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentChildGroupMesh(const std::vec componentCache.objectNodeVertices.push_back(it); if (nullptr == subMesh || subMesh->isNull()) { - delete subMesh; - continue; - } - - if (!subMesh->isCombinable()) { - componentCache.incombinableMeshes.push_back(subMesh); continue; } - multipleMeshes.push_back(std::make_tuple(subMesh, childCombineMode, childIdString)); + multipleMeshes.emplace_back(std::make_tuple(std::move(subMesh), childCombineMode, childIdString)); } - return combineMultipleMeshes(multipleMeshes); + return combineMultipleMeshes(std::move(multipleMeshes)); } -MeshCombiner::Mesh *MeshGenerator::combineTwoMeshes(const MeshCombiner::Mesh &first, const MeshCombiner::Mesh &second, +std::unique_ptr MeshGenerator::combineTwoMeshes(const MeshCombiner::Mesh &first, const MeshCombiner::Mesh &second, MeshCombiner::Method method, bool recombine) { if (first.isNull() || second.isNull()) return nullptr; std::vector> combinedVerticesSources; - MeshCombiner::Mesh *newMesh = MeshCombiner::combine(first, + auto newMesh = std::unique_ptr(MeshCombiner::combine(first, second, method, - &combinedVerticesSources); + &combinedVerticesSources)); if (nullptr == newMesh) return nullptr; if (!newMesh->isNull() && recombine) { @@ -1112,18 +1012,14 @@ MeshCombiner::Mesh *MeshGenerator::combineTwoMeshes(const MeshCombiner::Mesh &fi recombiner.setFaces(&combinedFaces); if (recombiner.recombine()) { if (isWatertight(recombiner.regeneratedFaces())) { - MeshCombiner::Mesh *reMesh = new MeshCombiner::Mesh(recombiner.regeneratedVertices(), recombiner.regeneratedFaces()); - if (!reMesh->isNull() && reMesh->isCombinable()) { - delete newMesh; - newMesh = reMesh; - } else { - delete reMesh; + auto reMesh = std::make_unique(recombiner.regeneratedVertices(), recombiner.regeneratedFaces()); + if (!reMesh->isNull()) { + newMesh = std::move(reMesh); } } } } if (newMesh->isNull()) { - delete newMesh; return nullptr; } return newMesh; @@ -1245,23 +1141,6 @@ void MeshGenerator::postprocessObject(Object *object) object->setTriangleVertexNormals(triangleVertexNormals); } -void MeshGenerator::collectIncombinableComponentMeshes(const std::string &componentIdString) -{ - const auto &component = findComponent(componentIdString); - if (CombineMode::Uncombined == componentCombineMode(component)) - return; - const auto &componentCache = m_cacheContext->components[componentIdString]; - for (const auto &mesh: componentCache.incombinableMeshes) { - m_isSuccessful = false; - collectIncombinableMesh(mesh, componentCache); - } - for (const auto &childIdString: String::split(String::valueOrEmpty(*component, "children"), ',')) { - if (childIdString.empty()) - continue; - collectIncombinableComponentMeshes(childIdString); - } -} - void MeshGenerator::collectIncombinableMesh(const MeshCombiner::Mesh *mesh, const GeneratedComponent &componentCache) { if (nullptr == mesh) @@ -1302,7 +1181,7 @@ void MeshGenerator::collectUncombinedComponent(const std::string &componentIdStr m_object->edges.insert(m_object->edges.end(), componentCache.objectEdges.begin(), componentCache.objectEdges.end()); m_nodeVertices.insert(m_nodeVertices.end(), componentCache.objectNodeVertices.begin(), componentCache.objectNodeVertices.end()); - collectIncombinableMesh(componentCache.mesh, componentCache); + collectIncombinableMesh(componentCache.mesh.get(), componentCache); return; } for (const auto &childIdString: String::split(String::valueOrEmpty(*component, "children"), ',')) { @@ -1459,7 +1338,6 @@ void MeshGenerator::generate() } m_cacheContext->partMirrorIdMap.erase(mirrorFrom); } - it->second.releaseMeshes(); it = m_cacheContext->parts.erase(it); continue; } @@ -1469,13 +1347,11 @@ void MeshGenerator::generate() if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) { for (auto combinationIt = m_cacheContext->cachedCombination.begin(); combinationIt != m_cacheContext->cachedCombination.end(); ) { if (std::string::npos != combinationIt->first.find(it->first)) { - delete combinationIt->second; combinationIt = m_cacheContext->cachedCombination.erase(combinationIt); continue; } combinationIt++; } - it->second.releaseMeshes(); it = m_cacheContext->components.erase(it); continue; } @@ -1489,7 +1365,6 @@ void MeshGenerator::generate() for (const auto &dirtyComponentId: m_dirtyComponentIds) { for (auto combinationIt = m_cacheContext->cachedCombination.begin(); combinationIt != m_cacheContext->cachedCombination.end(); ) { if (std::string::npos != combinationIt->first.find(dirtyComponentId)) { - delete combinationIt->second; combinationIt = m_cacheContext->cachedCombination.erase(combinationIt); continue; } @@ -1533,13 +1408,10 @@ void MeshGenerator::generate() // Recursively check uncombined components collectUncombinedComponent(to_string(Uuid())); - collectIncombinableComponentMeshes(to_string(Uuid())); collectErroredParts(); postprocessObject(m_object); - delete combinedMesh; - if (needDeleteCacheContext) { delete m_cacheContext; m_cacheContext = nullptr; diff --git a/dust3d/mesh/mesh_generator.h b/dust3d/mesh/mesh_generator.h index 43db8be0..786f6a53 100644 --- a/dust3d/mesh/mesh_generator.h +++ b/dust3d/mesh/mesh_generator.h @@ -40,19 +40,8 @@ class MeshGenerator { public: - class GeneratedPart + struct GeneratedPart { - public: - ~GeneratedPart() - { - releaseMeshes(); - }; - void releaseMeshes() - { - delete mesh; - mesh = nullptr; - } - MeshCombiner::Mesh *mesh = nullptr; std::vector vertices; std::vector> faces; std::vector objectNodes; @@ -64,23 +53,9 @@ public: bool joined = true; }; - class GeneratedComponent + struct GeneratedComponent { - public: - ~GeneratedComponent() - { - releaseMeshes(); - }; - void releaseMeshes() - { - delete mesh; - mesh = nullptr; - for (auto &it: incombinableMeshes) - delete it; - incombinableMeshes.clear(); - } - MeshCombiner::Mesh *mesh = nullptr; - std::vector incombinableMeshes; + std::unique_ptr mesh; std::set> sharedQuadEdges; std::set noneSeamVertices; std::vector objectNodes; @@ -88,27 +63,16 @@ public: std::vector>> objectNodeVertices; }; - class GeneratedCacheContext + struct GeneratedCacheContext { - public: - ~GeneratedCacheContext() - { - for (auto &it: cachedCombination) - delete it.second; - for (auto &it: parts) - it.second.releaseMeshes(); - for (auto &it: components) - it.second.releaseMeshes(); - } std::map components; std::map parts; std::map partMirrorIdMap; - std::map cachedCombination; + std::map> cachedCombination; }; - class PartPreview + struct PartPreview { - public: std::vector cutTemplate; std::vector vertices; @@ -156,25 +120,22 @@ private: bool m_cacheEnabled = false; float m_smoothShadingThresholdAngleDegrees = 60; uint64_t m_id = 0; - std::vector m_clothCollisionVertices; - std::vector> m_clothCollisionTriangles; bool m_weldEnabled = true; bool m_interpolationEnabled = true; void collectParts(); - void collectIncombinableComponentMeshes(const std::string &componentIdString); void collectIncombinableMesh(const MeshCombiner::Mesh *mesh, const GeneratedComponent &componentCache); bool checkIsComponentDirty(const std::string &componentIdString); bool checkIsPartDirty(const std::string &partIdString); bool checkIsPartDependencyDirty(const std::string &partIdString); void checkDirtyFlags(); - MeshCombiner::Mesh *combinePartMesh(const std::string &partIdString, bool *hasError, bool *retryable, bool addIntermediateNodes=true); - MeshCombiner::Mesh *combineComponentMesh(const std::string &componentIdString, CombineMode *combineMode); + std::unique_ptr combinePartMesh(const std::string &partIdString, bool *hasError, bool *retryable, bool addIntermediateNodes=true); + std::unique_ptr combineComponentMesh(const std::string &componentIdString, CombineMode *combineMode); void makeXmirror(const std::vector &sourceVertices, const std::vector> &sourceFaces, std::vector *destVertices, std::vector> *destFaces); void collectSharedQuadEdges(const std::vector &vertices, const std::vector> &faces, std::set> *sharedQuadEdges); - MeshCombiner::Mesh *combineTwoMeshes(const MeshCombiner::Mesh &first, const MeshCombiner::Mesh &second, + std::unique_ptr combineTwoMeshes(const MeshCombiner::Mesh &first, const MeshCombiner::Mesh &second, MeshCombiner::Method method, bool recombine=true); void generateSmoothTriangleVertexNormals(const std::vector &vertices, const std::vector> &triangles, @@ -182,9 +143,9 @@ private: std::vector> *triangleVertexNormals); const std::map *findComponent(const std::string &componentIdString); CombineMode componentCombineMode(const std::map *component); - MeshCombiner::Mesh *combineComponentChildGroupMesh(const std::vector &componentIdStrings, + std::unique_ptr combineComponentChildGroupMesh(const std::vector &componentIdStrings, GeneratedComponent &componentCache); - MeshCombiner::Mesh *combineMultipleMeshes(const std::vector> &multipleMeshes, bool recombine=true); + std::unique_ptr combineMultipleMeshes(std::vector, CombineMode, std::string>> &&multipleMeshes, bool recombine=true); std::string componentColorName(const std::map *component); void collectUncombinedComponent(const std::string &componentIdString); void cutFaceStringToCutTemplate(const std::string &cutFaceString, std::vector &cutTemplate); diff --git a/dust3d/mesh/stitch_mesh_builder.cc b/dust3d/mesh/stitch_mesh_builder.cc new file mode 100644 index 00000000..cb430eba --- /dev/null +++ b/dust3d/mesh/stitch_mesh_builder.cc @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2016-2022 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 + +namespace dust3d +{ + +StitchMeshBuilder::StitchMeshBuilder(std::vector &&splines) +{ + m_splines = std::move(splines); +} + +const std::vector &StitchMeshBuilder::generatedVertices() +{ + return m_generatedVertices; +} + +const std::vector> &StitchMeshBuilder::generatedFaces() +{ + return m_generatedFaces; +} + +std::vector> StitchMeshBuilder::convertSplinesToStitchingPoints(const std::vector &splines) +{ + std::vector> stitchingPoints(splines.size()); + + size_t targetSegments = 5; // FIXME: decide target segments by ... + for (size_t i = 0; i < splines.size(); ++i) { + const auto &spline = splines[i]; + std::vector polyline(spline.nodes.size()); + for (size_t j = 0; j < spline.nodes.size(); ++j) + polyline[j] = spline.nodes[j].origin; + auto segmentPoints = splitPolylineToSegments(polyline, targetSegments); + stitchingPoints[i].resize(segmentPoints.size()); + for (size_t k = 0; k < segmentPoints.size(); ++k) { + stitchingPoints[i][k].originVertex = m_generatedVertices.size(); + // TODO: stitchingPoints[i][k].radius = ... + m_generatedVertices.emplace_back(segmentPoints[k]); + } + } + + return stitchingPoints; +} + +double StitchMeshBuilder::segmentsLength(const std::vector &segmentPoints) +{ + double totalLength = 0.0; + for (size_t j = 1; j < segmentPoints.size(); ++j) { + size_t i = j - 1; + totalLength += (segmentPoints[i] - segmentPoints[j]).length(); + } + return totalLength; +} + +std::vector StitchMeshBuilder::splitPolylineToSegments(const std::vector &polyline, + size_t targetSegments) +{ + std::vector segmentPoints; + if (polyline.size() < 2) + return segmentPoints; + double totalLength = segmentsLength(polyline); + if (Math::isZero(totalLength)) { + Vector3 middle = (polyline.front() + polyline.back()) * 0.5; + for (size_t i = 0; i < targetSegments + 1; ++i) { + segmentPoints.push_back(middle); + } + return segmentPoints; + } + double segmentLength = totalLength / targetSegments; + double wantLength = segmentLength; + segmentPoints.push_back(polyline.front()); + for (size_t j = 1; j < polyline.size(); ++j) { + size_t i = j - 1; + double lineLength = (polyline[j] - polyline[i]).length(); + Vector3 polylineStart = polyline[i]; + while (true) { + if (lineLength < wantLength) { + wantLength -= lineLength; + break; + } + Vector3 newPosition = (polylineStart * (lineLength - wantLength) + polyline[j] * wantLength) / lineLength; + segmentPoints.push_back(newPosition); + lineLength -= wantLength; + polylineStart = newPosition; + wantLength = segmentLength; + } + } + if (segmentPoints.size() < targetSegments + 1) { + segmentPoints.push_back(polyline.back()); + } else { + segmentPoints.back() = polyline.back(); + } + return segmentPoints; +} + +void StitchMeshBuilder::generateMeshFromStitchingPoints(const std::vector> &stitchingPoints) +{ + for (size_t j = 1; j < stitchingPoints.size(); ++j) { + size_t i = j - 1; + generateMeshFromStitchingPoints(stitchingPoints[i], stitchingPoints[j]); + } +} + +void StitchMeshBuilder::generateMeshFromStitchingPoints(const std::vector &a, + const std::vector &b) +{ + if (a.size() != b.size()) { + dust3dDebug << "Unmatch sizes of stitching points, a:" << a.size() << "b:" << b.size(); + return; + } + if (a.size() < 2) { + dust3dDebug << "Expected at least two stitching points, current:" << a.size(); + return; + } + for (size_t j = 1; j < a.size(); ++j) { + size_t i = j - 1; + m_generatedFaces.emplace_back(std::vector { + a[i].originVertex, + b[i].originVertex, + b[j].originVertex, + a[j].originVertex + }); + } +} + +void StitchMeshBuilder::build() +{ + std::vector> stitchingPoints = convertSplinesToStitchingPoints(m_splines); + generateMeshFromStitchingPoints(stitchingPoints); +} + +} diff --git a/dust3d/mesh/stitch_mesh_builder.h b/dust3d/mesh/stitch_mesh_builder.h new file mode 100644 index 00000000..49eb2c19 --- /dev/null +++ b/dust3d/mesh/stitch_mesh_builder.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016-2022 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_STITCH_MESH_BUILDER_H_ +#define DUST3D_MESH_STITCH_MESH_BUILDER_H_ + +#include + +namespace dust3d +{ + +class StitchMeshBuilder +{ +public: + struct Node + { + Vector3 origin; + double radius; + }; + + struct Spline + { + std::vector nodes; + bool isCircle = false; + }; + + StitchMeshBuilder(std::vector &&splines); + void build(); + const std::vector &generatedVertices(); + const std::vector> &generatedFaces(); + +private: + struct StitchingPoint + { + size_t originVertex; + double radius; + }; + + std::vector m_splines; + std::vector m_generatedVertices; + std::vector> m_generatedFaces; + + std::vector> convertSplinesToStitchingPoints(const std::vector &splines); + void generateMeshFromStitchingPoints(const std::vector> &stitchingPoints); + void generateMeshFromStitchingPoints(const std::vector &a, + const std::vector &b); + std::vector splitPolylineToSegments(const std::vector &polyline, + size_t targetSegments); + double segmentsLength(const std::vector &segmentPoints); +}; + +} + +#endif