From 8634eaefe63ecc429791ffa211d79616d7a36161 Mon Sep 17 00:00:00 2001 From: Andrea Maggiordomo Date: Tue, 8 Jun 2021 17:58:21 +0200 Subject: [PATCH] fixed texture defragmentation crashes with non-manifold edges and zero-area faces --- .../src/mesh_attribute.h | 7 ++ .../TextureDefragmentation/src/mesh_graph.cpp | 77 +++++++++++++++- .../TextureDefragmentation/src/mesh_graph.h | 5 + .../TextureDefragmentation/src/packing.cpp | 36 ++++---- .../src/seam_remover.cpp | 91 ++++++++++++------- .../TextureDefragmentation/src/seam_remover.h | 6 +- .../TextureDefragmentation/src/seams.cpp | 26 +++--- .../TextureDefragmentation/src/seams.h | 2 +- .../src/texture_object.cpp | 1 + .../filter_texture_defragmentation.cpp | 76 +++++++++------- 10 files changed, 223 insertions(+), 104 deletions(-) diff --git a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/mesh_attribute.h b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/mesh_attribute.h index f8788089d..b96c93fd5 100644 --- a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/mesh_attribute.h +++ b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/mesh_attribute.h @@ -71,6 +71,13 @@ struct FF { int e[3]; // opposite edge index }; +inline bool IsEdgeManifold3D(Mesh& m, const MeshFace& f, int i, Mesh::PerFaceAttributeHandle& ffadj) +{ + const MeshFace& ff = m.face[ffadj[f].f[i]]; // opposite face + int ffi = ffadj[f].e[i]; // opposite index + return tri::Index(m, f) == ffadj[ff].f[ffi]; +} + inline Mesh::PerFaceAttributeHandle Get3DFaceAdjacencyAttribute(Mesh& m); inline Mesh::PerFaceAttributeHandle GetWedgeTexCoordStorageAttribute(Mesh& m); inline Mesh::PerMeshAttributeHandle GetBoundaryInfoAttribute(Mesh& m); diff --git a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/mesh_graph.cpp b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/mesh_graph.cpp index 3a37c9a29..87854e81d 100644 --- a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/mesh_graph.cpp +++ b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/mesh_graph.cpp @@ -386,9 +386,11 @@ GraphHandle ComputeGraph(Mesh &m, TextureObjectHandle textureObject) RegionID regionId = f.id; graph->GetChart_Insert(regionId)->AddFace(&f); for (int i = 0; i < f.VN(); ++i) { - RegionID adjId = m.face[ffadj[f].f[i]].id; - if (regionId != adjId) { - (graph->GetChart_Insert(regionId)->adj).insert(graph->GetChart_Insert(adjId)); + if (IsEdgeManifold3D(m, f, i, ffadj)) { + RegionID adjId = m.face[ffadj[f].f[i]].id; + if (regionId != adjId) { + (graph->GetChart_Insert(regionId)->adj).insert(graph->GetChart_Insert(adjId)); + } } } } @@ -396,4 +398,73 @@ GraphHandle ComputeGraph(Mesh &m, TextureObjectHandle textureObject) return graph; } +void DisconnectCharts(GraphHandle graph) +{ + typedef std::pair VertexRID; + Mesh& m = graph->mesh; + + int numExtraVertices = 0; + std::map remap; + + tri::UpdateFlags::VertexClearV(m); + for (auto& c : graph->charts) { + std::set vset; + for (auto fptr : c.second->fpVec) { + for (int i = 0; i < 3; ++i) { + vset.insert(fptr->V(i)); + } + } + for (auto vp : vset) { + if (vp->IsV()) { + numExtraVertices++; + remap[std::make_pair(tri::Index(m, vp), c.first)] = -1; + } + vp->SetV(); + } + } + + auto vi = tri::Allocator::AddVertices(m, numExtraVertices); + + tri::UpdateFlags::VertexClearV(m); + + for (auto& entry : remap) { + VertexRID vrid = entry.first; + vi->ImportData(m.vert[vrid.first]); + m.vert[vrid.first].SetV(); + + ensure(entry.second == -1); + entry.second = tri::Index(m, *vi); + vi++; + } + int updated = 0; + int iters = 0; + for (auto& c : graph->charts) { + for (auto fptr : c.second->fpVec) { + for (int i = 0; i < 3; ++i) { + VertexRID vrid = std::make_pair(tri::Index(m, fptr->V(i)), c.first); + iters++; + if (fptr->V(i)->IsV() && remap.count(vrid) > 0) { + int vind = remap[vrid]; + fptr->V(i) = &m.vert[vind]; + updated++; + } + } + } + } + + // safety check + tri::UpdateFlags::VertexClearV(m); + for (auto& c : graph->charts) { + std::set vset; + for (auto fptr : c.second->fpVec) { + for (int i = 0; i < 3; ++i) { + vset.insert(fptr->V(i)); + } + } + for (auto vp : vset) { + ensure(!vp->IsV()); + vp->SetV(); + } + } +} diff --git a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/mesh_graph.h b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/mesh_graph.h index e0722e8c7..0855186a1 100644 --- a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/mesh_graph.h +++ b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/mesh_graph.h @@ -107,6 +107,11 @@ void CopyToMesh(FaceGroup& fg, Mesh& m); * to determine chart adjacency relations */ GraphHandle ComputeGraph(Mesh &m, TextureObjectHandle textureObject); +/* This function ensures that the vertices referenced by each chart are unique + * to the chart. Necessary because non-manifold vertices adjacent to + * non-manifold edges cannot be split by the VCG's SplitNonManifoldVertices() */ +void DisconnectCharts(GraphHandle graph); + /* * MeshGraph class * diff --git a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/packing.cpp b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/packing.cpp index efbc30ed0..effb1ec96 100644 --- a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/packing.cpp +++ b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/packing.cpp @@ -204,30 +204,28 @@ Outline2d ExtractOutline2d(FaceGroup& chart) } } - int i; vcg::Box2d box = chart.UVBox(); - bool useChartBBAsOutline = false; - std::size_t maxsz = 0; - for (std::size_t i = 0; i < outline2Vec.size(); ++i) { - maxsz = std::max(maxsz, outline2Vec[i].size()); - } + int outlineIndex = -1; + double largestArea = 0; - if (maxsz == 0) { - useChartBBAsOutline = true; - } else { - i = (outline2Vec.size() == 1) ? 0 : tri::OutlineUtil::LargestOutline2(outline2Vec); - if (tri::OutlineUtil::Outline2Area(outline2Vec[i]) < 0) + for (int i = 0; i < outline2Vec.size(); ++i) { + double outlineArea = tri::OutlineUtil::Outline2Area(outline2Vec[i]); + if (outlineArea < 0) tri::OutlineUtil::ReverseOutline2(outline2Vec[i]); - vcg::Box2d outlineBox; - for (const auto& p : outline2Vec[i]) - outlineBox.Add(p); - if (outlineBox.DimX() < box.DimX() || outlineBox.DimY() < box.DimY()) - useChartBBAsOutline = true; + if (std::abs(outlineArea) >= largestArea) { + vcg::Box2d outlineBox; + for (const auto& p : outline2Vec[i]) + outlineBox.Add(p); + if (outlineBox.DimX() >= box.DimX() && outlineBox.DimY() >= box.DimY()) { + outlineIndex = i; + largestArea = std::abs(outlineArea); + } + } } - if (useChartBBAsOutline) { - LOG_WARN << "Failed to compute outline, falling back to uv bounding box for chart " << chart.id; + if (outlineIndex == -1) { + LOG_WARN << "Outline not bounding, falling back to UV bounding box for chart " << chart.id; outline.clear(); outline.push_back(Point2d(box.min.X(), box.min.Y())); outline.push_back(Point2d(box.max.X(), box.min.Y())); @@ -235,7 +233,7 @@ Outline2d ExtractOutline2d(FaceGroup& chart) outline.push_back(Point2d(box.min.X(), box.max.Y())); return outline; } else { - return outline2Vec[i]; + return outline2Vec[outlineIndex]; } } diff --git a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/seam_remover.cpp b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/seam_remover.cpp index d8a8b3dbc..d491c6988 100644 --- a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/seam_remover.cpp +++ b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/seam_remover.cpp @@ -268,7 +268,7 @@ AlgoStateHandle InitializeState(GraphHandle graph, const AlgoParameters& algoPar state->inputUVBorderLength = 0; state->currentUVBorderLength = 0; - BuildSeamMesh(graph->mesh, state->sm); + BuildSeamMesh(graph->mesh, state->sm, graph); std::vector seams = GenerateSeams(state->sm); // disconnecting seams are (initially) clustered by chart adjacency @@ -392,15 +392,9 @@ void GreedyOptimization(GraphHandle graph, AlgoStateHandle state, const AlgoPara } } } - PrintStateInfo(state, graph, params); - LogExecutionStats(); - - Mesh shell; - LOG_INFO << "Atlas energy after optimization is " << ARAP::ComputeEnergyFromStoredWedgeTC(graph->mesh, nullptr, nullptr); - } void Finalize(GraphHandle graph, int *vndup) @@ -684,6 +678,11 @@ static OffsetMap AlignAndMerge(ClusteredSeamHandle csh, SeamData& sd, const Matc Mesh::VertexPointer v0b = edge.fb->V0(edge.eb); Mesh::VertexPointer v1b = edge.fb->V1(edge.eb); + sd.seamVertices.insert(v0a); + sd.seamVertices.insert(v1a); + sd.seamVertices.insert(v0b); + sd.seamVertices.insert(v1b); + if (v0a->P() != edge.V(0)->P()) std::swap(v0a, v1a); if (v0b->P() != edge.V(0)->P()) @@ -707,9 +706,11 @@ static OffsetMap AlignAndMerge(ClusteredSeamHandle csh, SeamData& sd, const Matc } } - // for each vertex merged, store its original vfadj fan - for (auto& entry : sd.mrep) { - face::VFStarVF(entry.first, sd.vfmap[entry.first].first, sd.vfmap[entry.first].second); + for (auto vp : sd.seamVertices) { + std::vector faces; + std::vector indices; + face::VFStarVF(vp, faces, indices); + sd.vfTopologyFaceSet.insert(faces.begin(), faces.end()); } // update vertex references @@ -726,8 +727,7 @@ static OffsetMap AlignAndMerge(ClusteredSeamHandle csh, SeamData& sd, const Matc } } - // update topologies. face-face is trivial, for vertex-face we - // merge the VF lists + // update topologies. face-face is trivial // face-face for (SeamHandle sh : csh->seams) { @@ -740,19 +740,20 @@ static OffsetMap AlignAndMerge(ClusteredSeamHandle csh, SeamData& sd, const Matc } } - //tri::UpdateTopology::VertexFace(graph->mesh); + { + for (Mesh::VertexPointer vp : sd.seamVertices) { + vp->VFp() = 0; + vp->VFi() = 0; + } - // iterate over emap, and concatenate vf adjacencies - // ugly... essentially we have a list of vfadj fans stored in vfmap, - // and we concatenate the end of a fan with the beginning of the next - // note that we only change the data stored in the faces (i.e. the list) and - // never touch the data stored on the vertices - for (auto& entry : sd.evec) { - if (entry.second.size() > 1) { - std::vector& verts = entry.second; - for (unsigned i = 0; i < entry.second.size() - 1; ++i) { - sd.vfmap[verts[i]].first.back()->VFp(sd.vfmap[verts[i]].second.back()) = sd.vfmap[verts[i+1]].first.front(); - sd.vfmap[verts[i]].first.back()->VFi(sd.vfmap[verts[i]].second.back()) = sd.vfmap[verts[i+1]].second.front(); + for (Mesh::FacePointer fptr : sd.vfTopologyFaceSet) { + for (int i = 0; i < 3; ++i) { + if (sd.seamVertices.find(fptr->V(i)) != sd.seamVertices.end()) { + (*fptr).VFp(i) = (*fptr).V(i)->VFp(); + (*fptr).VFi(i) = (*fptr).V(i)->VFi(); + (*fptr).V(i)->VFp() = &(*fptr); + (*fptr).V(i)->VFi() = i; + } } } } @@ -789,12 +790,21 @@ static void ComputeOptimizationArea(SeamData& sd, Mesh& mesh, OffsetMap& om) sd.verticesWithinThreshold = ComputeVerticesWithinOffsetThreshold(mesh, om, sd); sd.optimizationArea.clear(); + auto ffadj = Get3DFaceAdjacencyAttribute(mesh); for (auto fptr : fpvec) { + bool addFace = false; + bool edgeManifold = true; for (int i = 0; i < 3; ++i) { - if (sd.verticesWithinThreshold.find(fptr->V(i)) != sd.verticesWithinThreshold.end()) { - sd.optimizationArea.insert(fptr); - break; - } + edgeManifold &= IsEdgeManifold3D(mesh, *fptr, i, ffadj); + if (sd.verticesWithinThreshold.find(fptr->V(i)) != sd.verticesWithinThreshold.end()) + addFace = true; + } + if (addFace && edgeManifold) + sd.optimizationArea.insert(fptr); + if (addFace && !edgeManifold) { + sd.verticesWithinThreshold.erase(fptr->V(0)); + sd.verticesWithinThreshold.erase(fptr->V(1)); + sd.verticesWithinThreshold.erase(fptr->V(2)); } } @@ -859,6 +869,7 @@ static std::unordered_set ComputeVerticesWithinOffsetThresh std::vector faces; std::vector indices; face::VFStarVF(node.first, faces, indices); + for (unsigned i = 0; i < faces.size(); ++i) { if(faces[i]->id != sd.a->id && faces[i]->id != sd.b->id){ LOG_ERR << "issue at face " << tri::Index(m, faces[i]); @@ -882,7 +893,7 @@ static std::unordered_set ComputeVerticesWithinOffsetThresh Mesh::VertexPointer v2 = faces[i]->V2(indices[i]); double d2 = dist[node.first] - EdgeLengthUV(*faces[i], e2); - if (d2 >= 0 && (dist.find(v2) == dist.end() || dist[v2] < d2)) { + if (d2 >= 0 && (dist.find(v2) == dist.end() || dist[v2] < d2)) { dist[v2] = d2; h.push_back(std::make_pair(v2, d2)); std::push_heap(h.begin(), h.end(), cmp); @@ -1142,6 +1153,7 @@ static CheckStatus OptimizeChart(SeamData& sd, GraphHandle graph, bool fixInters // split non manifold vertices while (tri::Clean::SplitNonManifoldVertex(sd.shell, 0.3)) ; + ensure(tri::Clean::CountNonManifoldEdgeFF(sd.shell) == 0); CutAlongSeams(sd.shell); @@ -1449,12 +1461,21 @@ static void RejectMove(const SeamData& sd, AlgoStateHandle state, GraphHandle gr // restore vertex-face topology // iterate over emap, and split the lists according to the original topology // recall that we never touched any vertex topology attribute - for (auto& entry : sd.evec) { - //ensure(entry.second.size() > 1); not true for self seams at the cone vertex - const std::vector& verts = entry.second; - for (unsigned i = 0; i < entry.second.size() - 1; ++i) { - sd.vfmap.at(verts[i]).first.back()->VFp(sd.vfmap.at(verts[i]).second.back()) = 0; - sd.vfmap.at(verts[i]).first.back()->VFi(sd.vfmap.at(verts[i]).second.back()) = 0; + { + for (Mesh::VertexPointer vp : sd.seamVertices) { + vp->VFp() = 0; + vp->VFi() = 0; + } + + for (Mesh::FacePointer fptr : sd.vfTopologyFaceSet) { + for (int i = 0; i < 3; ++i) { + if (sd.seamVertices.find(fptr->V(i)) != sd.seamVertices.end()) { + (*fptr).VFp(i) = (*fptr).V(i)->VFp(); + (*fptr).VFi(i) = (*fptr).V(i)->VFi(); + (*fptr).V(i)->VFp() = &(*fptr); + (*fptr).V(i)->VFi() = i; + } + } } } diff --git a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/seam_remover.h b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/seam_remover.h index 969a1830d..944dd4da9 100644 --- a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/seam_remover.h +++ b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/seam_remover.h @@ -65,12 +65,12 @@ struct SeamData { std::vector vertexinda; std::vector vertexindb; + std::unordered_set seamVertices; + std::unordered_set vfTopologyFaceSet; + std::map mrep; std::map> evec; - typedef std::pair, std::vector> FanInfo; - std::map vfmap; - std::unordered_set verticesWithinThreshold; std::unordered_set optimizationArea; std::vector texcoordoptVert; diff --git a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/seams.cpp b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/seams.cpp index 9ddaabef2..ad60bc509 100644 --- a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/seams.cpp +++ b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/seams.cpp @@ -135,7 +135,7 @@ void ExtractUVCoordinates(ClusteredSeamHandle csh, std::vector& uva, st } } -void BuildSeamMesh(Mesh& m, SeamMesh& seamMesh) +void BuildSeamMesh(Mesh& m, SeamMesh& seamMesh, GraphHandle graph) { seamMesh.Clear(); @@ -145,18 +145,22 @@ void BuildSeamMesh(Mesh& m, SeamMesh& seamMesh) tri::UpdateFlags::FaceClearFaceEdgeS(m); for (auto& f : m.face) { for (int i = 0; i < 3; ++i) { - if (face::IsBorder(f, i) && f.IsFaceEdgeS(i) == false){ + if (IsEdgeManifold3D(m, f, i, ffadj) && face::IsBorder(f, i) && f.IsFaceEdgeS(i) == false) { PosF pa(&f, i); PosF pb = GetDualPos(m, pa, ffadj); - if (pa.F()->id > pb.F()->id) - std::swap(pa, pb); - auto ei = tri::Allocator::AddEdge(seamMesh, pa.V()->P(), pa.VFlip()->P()); - ei->fa = pa.F(); - ei->ea = pa.E(); - ei->fb = pb.F(); - ei->eb = pb.E(); - pa.F()->SetFaceEdgeS(pa.E()); - pb.F()->SetFaceEdgeS(pb.E()); + ChartHandle ca = graph->GetChart(pa.F()->id); + ChartHandle cb = graph->GetChart(pb.F()->id); + if (ca == cb || ca->adj.count(cb) > 0) { + if (pa.F()->id > pb.F()->id) + std::swap(pa, pb); + auto ei = tri::Allocator::AddEdge(seamMesh, pa.V()->P(), pa.VFlip()->P()); + ei->fa = pa.F(); + ei->ea = pa.E(); + ei->fb = pb.F(); + ei->eb = pb.E(); + pa.F()->SetFaceEdgeS(pa.E()); + pb.F()->SetFaceEdgeS(pb.E()); + } } } } diff --git a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/seams.h b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/seams.h index db028990c..8183040b8 100644 --- a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/seams.h +++ b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/seams.h @@ -57,7 +57,7 @@ double ComputeSeamLength3D(SeamHandle sh); // a is a set of ids that logically describe one side of the seam (whose coordinates are inserted in uva) void ExtractUVCoordinates(ClusteredSeamHandle csh, std::vector& uva, std::vector& uvb, const std::unordered_set& a); -void BuildSeamMesh(Mesh& m, SeamMesh& seamMesh); +void BuildSeamMesh(Mesh& m, SeamMesh& seamMesh, GraphHandle graph); std::vector GenerateSeams(SeamMesh& seamMesh); std::vector ClusterSeamsByChartId(const std::vector& seams); ClusteredSeamHandle Flatten(const std::vector& cshVec); diff --git a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/texture_object.cpp b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/texture_object.cpp index 23066bd18..ef917ea2e 100644 --- a/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/texture_object.cpp +++ b/src/meshlabplugins/filter_texture_defragmentation/TextureDefragmentation/src/texture_object.cpp @@ -86,6 +86,7 @@ void TextureObject::Bind(int i) glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img.width(), img.height(), GL_BGRA, GL_UNSIGNED_BYTE, img.constBits()); glGenerateMipmap(GL_TEXTURE_2D); CheckGLError(); + Mirror(img); } else { glBindTexture(GL_TEXTURE_2D, texNameVec[i]); diff --git a/src/meshlabplugins/filter_texture_defragmentation/filter_texture_defragmentation.cpp b/src/meshlabplugins/filter_texture_defragmentation/filter_texture_defragmentation.cpp index e4691d00c..fd3aacdc9 100644 --- a/src/meshlabplugins/filter_texture_defragmentation/filter_texture_defragmentation.cpp +++ b/src/meshlabplugins/filter_texture_defragmentation/filter_texture_defragmentation.cpp @@ -52,11 +52,11 @@ FilterTextureDefragPlugin::FilterTextureDefragPlugin() typeList = { FP_TEXTURE_DEFRAG, }; - + for(ActionIDType tt: types()) actionList.push_back(new QAction(filterName(tt), this)); - LOG_INIT(logging::Level::Warning); + LOG_INIT(logging::Level::Error); LOG_SET_THREAD_NAME("TextureDefrag"); } @@ -171,6 +171,14 @@ RichParameterList FilterTextureDefragPlugin::initParameterList(const QAction *ac 0.025, "Global ARAP distortion tolerance", "Global ARAP distortion tolerance when merging a seam. If the global atlas energy is higher than this value, the operation is reverted.")); + parlst.addParam(RichDynamicFloat( + "uvReductionLimit", + 0.0, + 0.0, + 100.0, + "UV Length Target (percentage)", + "Target UV length as percentage of the input length. The algorithm halts if the target UV length has be en reached, or if no futher " + "seams can be merged.")); parlst.addParam(RichFloat( "offsetFactor", 5.0, @@ -201,19 +209,17 @@ std::map FilterTextureDefragPlugin::applyFilter( switch(ID(filter)) { case FP_TEXTURE_DEFRAG: { - if (vcg::tri::Clean::CountNonManifoldEdgeFF(md.mm()->cm) > 0 || - vcg::tri::Clean::CountNonManifoldVertexFF(md.mm()->cm) > 0) { - throw MLException("Mesh has non-manifold vertices and/or faces. Texture map defragmentation filter requires manifoldness."); - } + cb(0, "Initializing layer..."); MeshModel& mm = *(md.addNewMesh(md.mm()->cm, "texdefrag_" + currentModel.label())); mm.updateDataMask(¤tModel); - for (const std::string& txtname : currentModel.cm.textures){ - mm.addTexture(txtname, currentModel.getTexture(txtname)); - } QString path = currentModel.pathName(); + tri::Clean::RemoveZeroAreaFace(mm.cm); + tri::Clean::RemoveDuplicateVertex(mm.cm); + tri::Allocator::CompactEveryVector(mm.cm); + tri::UpdateTopology::FaceFace(mm.cm); if (tri::Clean::CountNonManifoldEdgeFF(mm.cm) > 0) log(GLLogStream::Levels::WARNING, "Texture Defragmentation: mesh has non-manifold edges, seam topology may be unreliable"); @@ -222,8 +228,6 @@ std::map FilterTextureDefragPlugin::applyFilter( QDir wd = QDir::current(); QDir::setCurrent(path); - tri::Allocator::CompactEveryVector(mm.cm); - // build mesh object Mesh defragMesh; auto fi = tri::Allocator::AddFaces(defragMesh, mm.cm.FN()); @@ -252,8 +256,8 @@ std::map FilterTextureDefragPlugin::applyFilter( // build textureobjecthandle object TextureObjectHandle textureObject = std::make_shared(); - for (const string& textureName : mm.cm.textures) { - textureObject->AddImage(mm.getTexture(textureName)); + for (const string& textureName : currentModel.cm.textures) { + textureObject->AddImage(currentModel.getTexture(textureName)); } AlgoParameters ap; @@ -262,7 +266,7 @@ std::map FilterTextureDefragPlugin::applyFilter( ap.boundaryTolerance = par.getFloat("boundaryTolerance"); ap.distortionTolerance = par.getFloat("distortionTolerance"); ap.globalDistortionThreshold = par.getFloat("globalDistortionTolerance"); - ap.UVBorderLengthReduction = 0.0; + ap.UVBorderLengthReduction = par.getFloat("uvReductionLimit") / 100.0f; ap.offsetFactor = par.getFloat("offsetFactor"); ap.timelimit = par.getFloat("timelimit"); @@ -273,24 +277,22 @@ std::map FilterTextureDefragPlugin::applyFilter( ScaleTextureCoordinatesToImage(defragMesh, textureObject); // setup proxy mesh - { - Compute3DFaceAdjacencyAttribute(defragMesh); - - CutAlongSeams(defragMesh); - tri::Allocator::CompactEveryVector(defragMesh); - - tri::UpdateTopology::FaceFace(defragMesh); - while (tri::Clean::SplitNonManifoldVertex(defragMesh, 0)) - ; - tri::UpdateTopology::VertexFace(defragMesh); - - tri::Allocator::CompactEveryVector(defragMesh); - } - - ComputeWedgeTexCoordStorageAttribute(defragMesh); + Compute3DFaceAdjacencyAttribute(defragMesh); + CutAlongSeams(defragMesh); GraphHandle graph = ComputeGraph(defragMesh, textureObject); + while (tri::Clean::SplitNonManifoldVertex(defragMesh, 0)) + ; + tri::Allocator::CompactEveryVector(defragMesh); + + DisconnectCharts(graph); + tri::UpdateTopology::FaceFace(defragMesh); + tri::UpdateTopology::VertexFace(defragMesh); + + + ComputeWedgeTexCoordStorageAttribute(defragMesh); + std::map flipped; for (auto& c : graph->charts) flipped[c.first] = c.second->UVFlipped(); @@ -323,7 +325,7 @@ std::map FilterTextureDefragPlugin::applyFilter( } } - cb(70, "Packing new atlas..."); + cb(70, "Packing atlas..."); // clear texture coordinates of empty-area charts std::vector chartsToPack; for (auto& entry : graph->charts) { @@ -341,6 +343,16 @@ std::map FilterTextureDefragPlugin::applyFilter( } } + // Set non-manifold edges as border, otherwise outline extraction fails + for (auto& f : graph->mesh.face) { + for (int i = 0; i < 3; ++i) { + if (!face::IsManifold(f, i)) { + f.FFp(i) = &f; + f.FFi(i) = i; + } + } + } + std::vector texszVec; int npacked = Pack(chartsToPack, textureObject, texszVec); @@ -374,7 +386,7 @@ std::map FilterTextureDefragPlugin::applyFilter( mm.clearTextures(); const char *imageFormat = "png"; - QString textureBase = QFileInfo(currentModel.fullName()).baseName() + "_optimized_texture_"; + QString textureBase = mm.label() + "_optimized_texture_"; for (unsigned i = 0; i < newTextures.size(); ++i) { QString tname = textureBase + QString(std::to_string(i).c_str()) + "." + imageFormat; mm.addTexture(tname.toStdString(), *newTextures[i]); @@ -390,7 +402,7 @@ std::map FilterTextureDefragPlugin::applyFilter( default: wrongActionCalled(filter); } - + return std::map(); }