mirror of
https://github.com/lucaspalomodevelop/meshlab.git
synced 2026-03-14 16:44:37 +00:00
fixed texture defragmentation crashes with non-manifold edges and zero-area faces
This commit is contained in:
parent
42aec03b3f
commit
8634eaefe6
@ -71,6 +71,13 @@ struct FF {
|
||||
int e[3]; // opposite edge index
|
||||
};
|
||||
|
||||
inline bool IsEdgeManifold3D(Mesh& m, const MeshFace& f, int i, Mesh::PerFaceAttributeHandle<FF>& 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<FF> Get3DFaceAdjacencyAttribute(Mesh& m);
|
||||
inline Mesh::PerFaceAttributeHandle<TexCoordStorage> GetWedgeTexCoordStorageAttribute(Mesh& m);
|
||||
inline Mesh::PerMeshAttributeHandle<BoundaryInfo> GetBoundaryInfoAttribute(Mesh& m);
|
||||
|
||||
@ -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<int, RegionID> VertexRID;
|
||||
|
||||
Mesh& m = graph->mesh;
|
||||
|
||||
int numExtraVertices = 0;
|
||||
std::map<VertexRID, int> remap;
|
||||
|
||||
tri::UpdateFlags<Mesh>::VertexClearV(m);
|
||||
for (auto& c : graph->charts) {
|
||||
std::set<Mesh::VertexPointer> 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<Mesh>::AddVertices(m, numExtraVertices);
|
||||
|
||||
tri::UpdateFlags<Mesh>::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<Mesh>::VertexClearV(m);
|
||||
for (auto& c : graph->charts) {
|
||||
std::set<Mesh::VertexPointer> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
*
|
||||
|
||||
@ -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<double>::LargestOutline2(outline2Vec);
|
||||
if (tri::OutlineUtil<double>::Outline2Area(outline2Vec[i]) < 0)
|
||||
for (int i = 0; i < outline2Vec.size(); ++i) {
|
||||
double outlineArea = tri::OutlineUtil<double>::Outline2Area(outline2Vec[i]);
|
||||
if (outlineArea < 0)
|
||||
tri::OutlineUtil<double>::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];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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<SeamHandle> 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<Mesh::FacePointer> faces;
|
||||
std::vector<int> 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<Mesh>::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<Mesh::VertexPointer>& 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<Mesh::VertexPointer> ComputeVerticesWithinOffsetThresh
|
||||
std::vector<Mesh::FacePointer> faces;
|
||||
std::vector<int> 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<Mesh::VertexPointer> 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<Mesh>::SplitNonManifoldVertex(sd.shell, 0.3))
|
||||
;
|
||||
ensure(tri::Clean<Mesh>::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<Mesh::VertexPointer>& 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -65,12 +65,12 @@ struct SeamData {
|
||||
std::vector<int> vertexinda;
|
||||
std::vector<int> vertexindb;
|
||||
|
||||
std::unordered_set<Mesh::VertexPointer> seamVertices;
|
||||
std::unordered_set<Mesh::FacePointer> vfTopologyFaceSet;
|
||||
|
||||
std::map<Mesh::VertexPointer, Mesh::VertexPointer> mrep;
|
||||
std::map<SeamMesh::VertexPointer, std::vector<Mesh::VertexPointer>> evec;
|
||||
|
||||
typedef std::pair<std::vector<Mesh::FacePointer>, std::vector<int>> FanInfo;
|
||||
std::map<Mesh::VertexPointer, FanInfo> vfmap;
|
||||
|
||||
std::unordered_set<Mesh::VertexPointer> verticesWithinThreshold;
|
||||
std::unordered_set<Mesh::FacePointer> optimizationArea;
|
||||
std::vector<vcg::Point2d> texcoordoptVert;
|
||||
|
||||
@ -135,7 +135,7 @@ void ExtractUVCoordinates(ClusteredSeamHandle csh, std::vector<Point2d>& 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<Mesh>::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<SeamMesh>::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<SeamMesh>::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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Point2d>& uva, std::vector<Point2d>& uvb, const std::unordered_set<RegionID>& a);
|
||||
|
||||
void BuildSeamMesh(Mesh& m, SeamMesh& seamMesh);
|
||||
void BuildSeamMesh(Mesh& m, SeamMesh& seamMesh, GraphHandle graph);
|
||||
std::vector<SeamHandle> GenerateSeams(SeamMesh& seamMesh);
|
||||
std::vector<ClusteredSeamHandle> ClusterSeamsByChartId(const std::vector<SeamHandle>& seams);
|
||||
ClusteredSeamHandle Flatten(const std::vector<ClusteredSeamHandle>& cshVec);
|
||||
|
||||
@ -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]);
|
||||
|
||||
@ -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<std::string, QVariant> FilterTextureDefragPlugin::applyFilter(
|
||||
switch(ID(filter)) {
|
||||
case FP_TEXTURE_DEFRAG:
|
||||
{
|
||||
if (vcg::tri::Clean<CMeshO>::CountNonManifoldEdgeFF(md.mm()->cm) > 0 ||
|
||||
vcg::tri::Clean<CMeshO>::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<CMeshO>::RemoveZeroAreaFace(mm.cm);
|
||||
tri::Clean<CMeshO>::RemoveDuplicateVertex(mm.cm);
|
||||
tri::Allocator<CMeshO>::CompactEveryVector(mm.cm);
|
||||
|
||||
tri::UpdateTopology<CMeshO>::FaceFace(mm.cm);
|
||||
if (tri::Clean<CMeshO>::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<std::string, QVariant> FilterTextureDefragPlugin::applyFilter(
|
||||
QDir wd = QDir::current();
|
||||
QDir::setCurrent(path);
|
||||
|
||||
tri::Allocator<CMeshO>::CompactEveryVector(mm.cm);
|
||||
|
||||
// build mesh object
|
||||
Mesh defragMesh;
|
||||
auto fi = tri::Allocator<Mesh>::AddFaces(defragMesh, mm.cm.FN());
|
||||
@ -252,8 +256,8 @@ std::map<std::string, QVariant> FilterTextureDefragPlugin::applyFilter(
|
||||
// build textureobjecthandle object
|
||||
TextureObjectHandle textureObject = std::make_shared<TextureObject>();
|
||||
|
||||
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<std::string, QVariant> 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<std::string, QVariant> FilterTextureDefragPlugin::applyFilter(
|
||||
ScaleTextureCoordinatesToImage(defragMesh, textureObject);
|
||||
|
||||
// setup proxy mesh
|
||||
{
|
||||
Compute3DFaceAdjacencyAttribute(defragMesh);
|
||||
|
||||
CutAlongSeams(defragMesh);
|
||||
tri::Allocator<Mesh>::CompactEveryVector(defragMesh);
|
||||
|
||||
tri::UpdateTopology<Mesh>::FaceFace(defragMesh);
|
||||
while (tri::Clean<Mesh>::SplitNonManifoldVertex(defragMesh, 0))
|
||||
;
|
||||
tri::UpdateTopology<Mesh>::VertexFace(defragMesh);
|
||||
|
||||
tri::Allocator<Mesh>::CompactEveryVector(defragMesh);
|
||||
}
|
||||
|
||||
ComputeWedgeTexCoordStorageAttribute(defragMesh);
|
||||
Compute3DFaceAdjacencyAttribute(defragMesh);
|
||||
CutAlongSeams(defragMesh);
|
||||
|
||||
GraphHandle graph = ComputeGraph(defragMesh, textureObject);
|
||||
|
||||
while (tri::Clean<Mesh>::SplitNonManifoldVertex(defragMesh, 0))
|
||||
;
|
||||
tri::Allocator<Mesh>::CompactEveryVector(defragMesh);
|
||||
|
||||
DisconnectCharts(graph);
|
||||
tri::UpdateTopology<Mesh>::FaceFace(defragMesh);
|
||||
tri::UpdateTopology<Mesh>::VertexFace(defragMesh);
|
||||
|
||||
|
||||
ComputeWedgeTexCoordStorageAttribute(defragMesh);
|
||||
|
||||
std::map<RegionID, bool> flipped;
|
||||
for (auto& c : graph->charts)
|
||||
flipped[c.first] = c.second->UVFlipped();
|
||||
@ -323,7 +325,7 @@ std::map<std::string, QVariant> FilterTextureDefragPlugin::applyFilter(
|
||||
}
|
||||
}
|
||||
|
||||
cb(70, "Packing new atlas...");
|
||||
cb(70, "Packing atlas...");
|
||||
// clear texture coordinates of empty-area charts
|
||||
std::vector<ChartHandle> chartsToPack;
|
||||
for (auto& entry : graph->charts) {
|
||||
@ -341,6 +343,16 @@ std::map<std::string, QVariant> 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<TextureSize> texszVec;
|
||||
int npacked = Pack(chartsToPack, textureObject, texszVec);
|
||||
|
||||
@ -374,7 +386,7 @@ std::map<std::string, QVariant> 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<std::string, QVariant> FilterTextureDefragPlugin::applyFilter(
|
||||
default:
|
||||
wrongActionCalled(filter);
|
||||
}
|
||||
|
||||
|
||||
return std::map<std::string, QVariant>();
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user