diff --git a/application/application.pro b/application/application.pro index 42af7143..3508c978 100644 --- a/application/application.pro +++ b/application/application.pro @@ -265,6 +265,8 @@ SOURCES += ../dust3d/mesh/mesh_generator.cc HEADERS += ../dust3d/mesh/mesh_node.h HEADERS += ../dust3d/mesh/mesh_recombiner.h SOURCES += ../dust3d/mesh/mesh_recombiner.cc +HEADERS += ../dust3d/mesh/mesh_state.h +SOURCES += ../dust3d/mesh/mesh_state.cc HEADERS += ../dust3d/mesh/re_triangulator.h SOURCES += ../dust3d/mesh/re_triangulator.cc HEADERS += ../dust3d/mesh/resolve_triangle_source_node.h diff --git a/dust3d/base/object.h b/dust3d/base/object.h index d778b10f..33c887e1 100644 --- a/dust3d/base/object.h +++ b/dust3d/base/object.h @@ -63,6 +63,7 @@ public: std::vector> triangleAndQuads; std::vector> triangles; std::unordered_map, std::array>> partTriangleUvs; + std::vector, std::array>> seamTriangleUvs; std::vector triangleNormals; std::vector triangleColors; bool alphaEnabled = false; diff --git a/dust3d/mesh/mesh_generator.cc b/dust3d/mesh/mesh_generator.cc index 2c9ce2ee..7de066d3 100644 --- a/dust3d/mesh/mesh_generator.cc +++ b/dust3d/mesh/mesh_generator.cc @@ -900,7 +900,7 @@ std::unique_ptr MeshGenerator::combineComponentMesh(const st groupMeshes.emplace_back(std::make_tuple(std::move(stitchingMesh), CombineMode::Normal, String::join(stitchingComponents, ":"))); } } - mesh = combineMultipleMeshes(std::move(groupMeshes), true); + mesh = combineMultipleMeshes(std::move(groupMeshes)); ComponentPreview preview; if (mesh) mesh->fetch(preview.vertices, preview.triangles); @@ -917,7 +917,7 @@ std::unique_ptr MeshGenerator::combineComponentMesh(const st return mesh; } -std::unique_ptr MeshGenerator::combineMultipleMeshes(std::vector, CombineMode, std::string>>&& multipleMeshes, bool recombine) +std::unique_ptr MeshGenerator::combineMultipleMeshes(std::vector, CombineMode, std::string>>&& multipleMeshes) { std::unique_ptr mesh; std::string meshIdStrings; @@ -936,23 +936,20 @@ std::unique_ptr MeshGenerator::combineMultipleMeshes(std::ve 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); + if (nullptr != findCached->second.mesh) { + newMesh = std::make_unique(*findCached->second.mesh); } } else { newMesh = combineTwoMeshes(*mesh, *subMesh, - combinerMethod, - recombine); + combinerMethod); if (nullptr != newMesh) - m_cacheContext->cachedCombination.insert({ meshIdStrings, std::make_unique(*newMesh) }); + m_cacheContext->cachedCombination.insert({ meshIdStrings, GeneratedCombination { std::make_unique(*newMesh) } }); else - m_cacheContext->cachedCombination.insert({ meshIdStrings, nullptr }); + m_cacheContext->cachedCombination.insert({ meshIdStrings, GeneratedCombination {} }); } if (newMesh && !newMesh->isNull()) { mesh = std::move(newMesh); @@ -984,6 +981,8 @@ std::unique_ptr MeshGenerator::combineComponentChildGroupMes componentCache.sharedQuadEdges.insert(it); for (const auto& it : childComponentCache.partTriangleUvs) componentCache.partTriangleUvs.insert({ it.first, it.second }); + for (const auto& it : childComponentCache.seamTriangleUvs) + componentCache.seamTriangleUvs.push_back(it); for (const auto& it : childComponentCache.objectNodes) componentCache.objectNodes.push_back(it); for (const auto& it : childComponentCache.objectEdges) @@ -1001,8 +1000,7 @@ std::unique_ptr MeshGenerator::combineComponentChildGroupMes } std::unique_ptr MeshGenerator::combineTwoMeshes(const MeshCombiner::Mesh& first, const MeshCombiner::Mesh& second, - MeshCombiner::Method method, - bool recombine) + MeshCombiner::Method method) { if (first.isNull() || second.isNull()) return nullptr; @@ -1013,7 +1011,7 @@ std::unique_ptr MeshGenerator::combineTwoMeshes(const MeshCo &combinedVerticesSources)); if (nullptr == newMesh) return nullptr; - if (!newMesh->isNull() && recombine) { + if (!newMesh->isNull()) { MeshRecombiner recombiner; std::vector combinedVertices; std::vector> combinedFaces; @@ -1352,6 +1350,7 @@ void MeshGenerator::generate() m_object->nodes = componentCache.objectNodes; m_object->edges = componentCache.objectEdges; m_object->partTriangleUvs = componentCache.partTriangleUvs; + m_object->seamTriangleUvs = componentCache.seamTriangleUvs; m_nodeVertices = componentCache.objectNodeVertices; std::vector combinedVertices; diff --git a/dust3d/mesh/mesh_generator.h b/dust3d/mesh/mesh_generator.h index 6395fcbd..9967f027 100644 --- a/dust3d/mesh/mesh_generator.h +++ b/dust3d/mesh/mesh_generator.h @@ -73,6 +73,7 @@ public: std::unique_ptr mesh; std::set> sharedQuadEdges; std::unordered_map, std::array>> partTriangleUvs; + std::vector, std::array>> seamTriangleUvs; std::set noneSeamVertices; std::vector objectNodes; std::vector, std::pair>> objectEdges; @@ -82,6 +83,7 @@ public: mesh.reset(); sharedQuadEdges.clear(); partTriangleUvs.clear(); + seamTriangleUvs.clear(); noneSeamVertices.clear(); objectNodes.clear(); objectEdges.clear(); @@ -89,11 +91,16 @@ public: } }; + struct GeneratedCombination { + std::unique_ptr mesh; + std::vector, std::array>> seamTriangleUvs; + }; + struct GeneratedCacheContext { std::map components; std::map parts; std::map partMirrorIdMap; - std::map> cachedCombination; + std::map cachedCombination; }; struct ComponentPreview { @@ -158,13 +165,12 @@ private: void collectSharedQuadEdges(const std::vector& vertices, const std::vector>& faces, std::set>* sharedQuadEdges); std::unique_ptr combineTwoMeshes(const MeshCombiner::Mesh& first, const MeshCombiner::Mesh& second, - MeshCombiner::Method method, - bool recombine = true); + MeshCombiner::Method method); const std::map* findComponent(const std::string& componentIdString); CombineMode componentCombineMode(const std::map* component); std::unique_ptr combineComponentChildGroupMesh(const std::vector& componentIdStrings, GeneratedComponent& componentCache); - std::unique_ptr combineMultipleMeshes(std::vector, CombineMode, std::string>>&& multipleMeshes, bool recombine = true); + std::unique_ptr combineMultipleMeshes(std::vector, CombineMode, std::string>>&& multipleMeshes); std::unique_ptr combineStitchingMesh(const std::vector& partIdStrings, const std::vector& componentIdStrings, GeneratedComponent& componentCache); diff --git a/dust3d/mesh/mesh_recombiner.cc b/dust3d/mesh/mesh_recombiner.cc index 55808fcc..1449b13e 100644 --- a/dust3d/mesh/mesh_recombiner.cc +++ b/dust3d/mesh/mesh_recombiner.cc @@ -149,7 +149,7 @@ bool MeshRecombiner::recombine() } } std::map seamVertexToIslandMap; - size_t islands = splitSeamVerticesToIslands(seamLink, &seamVertexToIslandMap); + splitSeamVerticesToIslands(seamLink, &seamVertexToIslandMap); std::map, std::pair> edgesInSeamArea; for (size_t faceIndex = 0; faceIndex < (*m_faces).size(); ++faceIndex) { @@ -376,6 +376,22 @@ bool MeshRecombiner::bridge(const std::vector& first, const std::vector< void MeshRecombiner::fillPairs(const std::vector& small, const std::vector& large) { + std::vector, std::array>> bridgingFaceUvs; + + std::vector leftV(small.size(), 0.0); + double leftOffset = 0.0; + for (size_t j = 1; j < small.size(); ++j) { + leftOffset += ((*m_vertices)[small[j]] - (*m_vertices)[small[j - 1]]).length(); + leftV[j] = leftOffset; + } + + std::vector rightV(large.size(), 0.0); + double rightOffset = 0.0; + for (size_t j = 1; j < large.size(); ++j) { + rightOffset += ((*m_vertices)[large[j]] - (*m_vertices)[large[j - 1]]).length(); + rightV[j] = rightOffset; + } + size_t smallIndex = 0; size_t largeIndex = 0; while (smallIndex + 1 < small.size() || largeIndex + 1 < large.size()) { @@ -388,12 +404,18 @@ void MeshRecombiner::fillPairs(const std::vector& small, const std::vect m_regeneratedFaces.push_back({ small[smallIndex], small[smallIndex + 1], large[largeIndex] }); + bridgingFaceUvs.emplace_back(std::make_pair( + std::array { (*m_vertices)[small[smallIndex]], (*m_vertices)[small[smallIndex + 1]], (*m_vertices)[large[largeIndex]] }, + std::array { Vector2(0.0, leftV[smallIndex]), Vector2(0.0, leftV[smallIndex + 1]), Vector2(1.0, rightV[largeIndex]) })); ++smallIndex; continue; } m_regeneratedFaces.push_back({ large[largeIndex + 1], large[largeIndex], small[smallIndex] }); + bridgingFaceUvs.emplace_back(std::make_pair( + std::array { (*m_vertices)[large[largeIndex + 1]], (*m_vertices)[large[largeIndex]], (*m_vertices)[small[smallIndex]] }, + std::array { Vector2(1.0, rightV[largeIndex + 1]), Vector2(1.0, rightV[largeIndex]), Vector2(0.0, rightV[largeIndex]) })); ++largeIndex; continue; } @@ -401,6 +423,9 @@ void MeshRecombiner::fillPairs(const std::vector& small, const std::vect m_regeneratedFaces.push_back({ large[largeIndex + 1], large[largeIndex], small[smallIndex] }); + bridgingFaceUvs.emplace_back(std::make_pair( + std::array { (*m_vertices)[large[largeIndex + 1]], (*m_vertices)[large[largeIndex]], (*m_vertices)[small[smallIndex]] }, + std::array { Vector2(1.0, rightV[largeIndex + 1]), Vector2(1.0, rightV[largeIndex]), Vector2(0.0, rightV[largeIndex]) })); ++largeIndex; continue; } @@ -408,11 +433,16 @@ void MeshRecombiner::fillPairs(const std::vector& small, const std::vect m_regeneratedFaces.push_back({ small[smallIndex], small[smallIndex + 1], large[largeIndex] }); + bridgingFaceUvs.emplace_back(std::make_pair( + std::array { (*m_vertices)[small[smallIndex]], (*m_vertices)[small[smallIndex + 1]], (*m_vertices)[large[largeIndex]] }, + std::array { Vector2(0.0, leftV[smallIndex]), Vector2(0.0, leftV[smallIndex + 1]), Vector2(1.0, rightV[largeIndex]) })); ++smallIndex; continue; } break; } + + m_generatedBridgingTriangleUvs.emplace_back(bridgingFaceUvs); } void MeshRecombiner::removeReluctantVertices() @@ -438,4 +468,9 @@ void MeshRecombiner::removeReluctantVertices() m_regeneratedFaces = rearrangedFaces; } +const std::vector, std::array>>>& MeshRecombiner::generatedBridgingTriangleUvs() +{ + return m_generatedBridgingTriangleUvs; +} + } diff --git a/dust3d/mesh/mesh_recombiner.h b/dust3d/mesh/mesh_recombiner.h index a7bb6d8b..81a8e422 100644 --- a/dust3d/mesh/mesh_recombiner.h +++ b/dust3d/mesh/mesh_recombiner.h @@ -23,6 +23,7 @@ #ifndef DUST3D_MESH_MESH_RECOMBINER_H_ #define DUST3D_MESH_MESH_RECOMBINER_H_ +#include #include #include #include @@ -38,6 +39,7 @@ public: const std::vector& regeneratedVertices(); const std::vector>& regeneratedVerticesSourceIndices(); const std::vector>& regeneratedFaces(); + const std::vector, std::array>>>& generatedBridgingTriangleUvs(); bool recombine(); private: @@ -50,6 +52,7 @@ private: std::map, size_t> m_halfEdgeToFaceMap; std::map m_facesInSeamArea; std::set m_goodSeams; + std::vector, std::array>>> m_generatedBridgingTriangleUvs; bool addFaceToHalfEdgeToFaceMap(size_t faceIndex, std::map, size_t>& halfEdgeToFaceMap); diff --git a/dust3d/mesh/mesh_state.cc b/dust3d/mesh/mesh_state.cc new file mode 100644 index 00000000..24e0ec2b --- /dev/null +++ b/dust3d/mesh/mesh_state.cc @@ -0,0 +1,33 @@ +/* + * 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 + +namespace dust3d { + +void MeshState::fetch(std::vector& vertices, std::vector>& faces) const +{ + if (mesh) + mesh->fetch(vertices, faces); +} + +} diff --git a/dust3d/mesh/mesh_state.h b/dust3d/mesh/mesh_state.h new file mode 100644 index 00000000..ef232ef9 --- /dev/null +++ b/dust3d/mesh/mesh_state.h @@ -0,0 +1,42 @@ +/* + * 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_MESH_STATE_H_ +#define DUST3D_MESH_MESH_STATE_H_ + +#include +#include +#include + +namespace dust3d { + +class MeshState { +public: + std::unique_ptr mesh; + std::vector, std::array>> seamTriangleUvs; + + void fetch(std::vector& vertices, std::vector>& faces) const; +}; + +} + +#endif