From cfe695ece30698c59e8676b9ce5250584f4adace Mon Sep 17 00:00:00 2001 From: "T.Alderighi" Date: Fri, 17 Jan 2020 13:56:13 +0100 Subject: [PATCH] added clean option in isotropic remeshing and updated the remeshing example --- .../trimesh_remeshing/trimesh_remeshing.cpp | 112 +++++++++++------- vcg/complex/algorithms/isotropic_remeshing.h | 110 +++++++++++++++-- 2 files changed, 167 insertions(+), 55 deletions(-) diff --git a/apps/sample/trimesh_remeshing/trimesh_remeshing.cpp b/apps/sample/trimesh_remeshing/trimesh_remeshing.cpp index cbf7f300..895e83f5 100644 --- a/apps/sample/trimesh_remeshing/trimesh_remeshing.cpp +++ b/apps/sample/trimesh_remeshing/trimesh_remeshing.cpp @@ -41,57 +41,79 @@ class MyEdge; class MyFace; class MyVertex; struct MyUsedTypes : public UsedTypes< Use ::AsVertexType, - Use ::AsEdgeType, - Use ::AsFaceType>{}; + Use ::AsEdgeType, + Use ::AsFaceType>{}; -class MyVertex : public Vertex{}; +class MyVertex : public Vertex{}; class MyFace : public Face< MyUsedTypes, face::Mark, face::VertexRef, face::VFAdj, face::FFAdj, face::Normal3f, face::BitFlags > {}; class MyEdge : public Edge{}; class MyMesh : public tri::TriMesh< vector, vector , vector > {}; int main( int argc, char **argv ) { - MyMesh original,toremesh; - if(argc<2) - { - printf("Usage: trimesh_remesh [targetLen [iterNum] ]"); - exit(0); - } + MyMesh original,toremesh; + if(argc<2) + { + printf("Usage: trimesh_remesh [[targetLen (bbox perc)] [iterNum] [creaseAngle] [maxSurfDist (bbox perc)]]"); + exit(0); + } - if(tri::io::Importer::Open(original,argv[1])!=0) - { - printf("Error reading file %s\n",argv[1]); - exit(0); - } - float targetLenPerc=.2f; - int iterNum=20; - if(argc>=3) targetLenPerc = atof(argv[2]); - if(argc>=4) iterNum = atoi(argv[3]); - - // Mesh cleaning - tri::Clean::RemoveDuplicateVertex(original); - tri::Clean::RemoveUnreferencedVertex(original); - Allocator::CompactEveryVector(original); - tri::UpdateNormal::PerVertexNormalizedPerFaceNormalized(original); - tri::UpdateBounding::Box(original); - - vcg::tri::Append::MeshCopy(toremesh,original); - tri::UpdateNormal::PerVertexNormalizedPerFaceNormalized(toremesh); - tri::UpdateBounding::Box(toremesh); - - tri::UpdateTopology::FaceFace(toremesh); - tri::MeshAssert::FFTwoManifoldEdge(toremesh); - float lengthThr = targetLenPerc*(original.bbox.Diag()/100.f); - printf("Length Thr: %8.3f ~ %4.2f %% on %5.3f\n",lengthThr,targetLenPerc,original.bbox.Diag()); - - IsotropicRemeshing::Params params; - params.SetTargetLen(lengthThr); - params.SetFeatureAngleDeg(10); - params.iter=iterNum; - printf(" Input mesh %8i v %8i f\n",toremesh.VN(),toremesh.FN()); - IsotropicRemeshing::Do(toremesh, original, params); - vcg::tri::io::ExporterPLY::Save(toremesh, "remesh.ply"); - printf("Output mesh %8i v %8i f\n",toremesh.VN(),toremesh.FN()); - - return 0; + if(tri::io::Importer::Open(original,argv[1])!=0) + { + printf("Error reading file %s\n",argv[1]); + exit(0); + } + float targetLenPerc=.2f; + int iterNum=20; + float creaseAngle = 30.f; + float maxSurfDistPerc = 0.001f; + if(argc>=3) targetLenPerc = atof(argv[2]); + if(argc>=4) iterNum = atoi(argv[3]); + if(argc>=5) creaseAngle = atof(argv[4]); + if(argc>=6) maxSurfDistPerc = atof(argv[5]); + + + // Mesh cleaning + tri::Clean::RemoveUnreferencedVertex(original); + Allocator::CompactEveryVector(original); + + + tri::UpdateNormal::PerVertexNormalizedPerFaceNormalized(original); + tri::UpdateBounding::Box(original); + + vcg::tri::Append::MeshCopy(toremesh,original); + tri::UpdateNormal::PerVertexNormalizedPerFaceNormalized(toremesh); + tri::UpdateBounding::Box(toremesh); + + tri::UpdateTopology::FaceFace(toremesh); + float lengthThr = targetLenPerc*(original.bbox.Diag()/100.f); + float maxSurfDist = maxSurfDistPerc*(original.bbox.Diag()/100.f); + printf("Length Thr: %8.3f ~ %4.2f %% on %5.3f\n",lengthThr,targetLenPerc,original.bbox.Diag()); + + IsotropicRemeshing::Params params; + params.SetTargetLen(lengthThr); + params.SetFeatureAngleDeg(creaseAngle); + params.iter=iterNum; + + if (maxSurfDistPerc != 0) + { + params.surfDistCheck = true; + params.maxSurfDist = maxSurfDist; + } + else + { + params.surfDistCheck = false; + } + + params.cleanFlag = true; + params.userSelectedCreases = false; + + + + printf(" Input mesh %8i v %8i f\n",toremesh.VN(),toremesh.FN()); + IsotropicRemeshing::Do(toremesh, original, params); + vcg::tri::io::ExporterPLY::Save(toremesh, "remesh.ply"); + printf("Output mesh %8i v %8i f\n",toremesh.VN(),toremesh.FN()); + + return 0; } diff --git a/vcg/complex/algorithms/isotropic_remeshing.h b/vcg/complex/algorithms/isotropic_remeshing.h index ac4b8f00..0d3b443d 100644 --- a/vcg/complex/algorithms/isotropic_remeshing.h +++ b/vcg/complex/algorithms/isotropic_remeshing.h @@ -23,15 +23,16 @@ #ifndef _VCG_ISOTROPICREMESHING_H #define _VCG_ISOTROPICREMESHING_H -#include -#include -#include -#include -#include -#include -#include -#include - +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include namespace vcg { @@ -42,6 +43,7 @@ class IsotropicRemeshing public: typedef TRI_MESH_TYPE MeshType; typedef typename MeshType::FaceType FaceType; + typedef typename MeshType::FacePointer FacePointer; typedef typename FaceType::VertexType VertexType; typedef typename FaceType::VertexPointer VertexPointer; typedef typename VertexType::ScalarType ScalarType; @@ -85,6 +87,7 @@ public: bool smoothFlag=true; bool projectFlag=true; bool selectedOnly = false; + bool cleanFlag = true; bool userSelectedCreases = false; bool surfDistCheck = true; @@ -111,6 +114,7 @@ public: } Params; +private: static void debug_crease (MeshType & toRemesh, std::string prepend, int i) { ForEachVertex(toRemesh, [] (VertexType & v) { @@ -136,6 +140,88 @@ public: vcg::tri::io::Exporter::Save(toRemesh, prepend.c_str(), vcg::tri::io::Mask::IOM_ALL); } + static void removeColinearFaces(MeshType & m, Params & params) + { + vcg::tri::UpdateTopology::FaceFace(m); + + int count = 0; + +// MeshType projectMesh; +// vcg::tri::Append::MeshCopy(projectMesh, m); +// vcg::tri::UpdateBounding::Box(projectMesh); +// vcg::tri::UpdateNormal::PerVertexNormalizedPerFace(projectMesh); + +// StaticGrid grid; +// grid.Set(projectMesh.face.begin(), projectMesh.face.end()); + + do + { + vcg::tri::UpdateTopology::FaceFace(m); + vcg::tri::UnMarkAll(m); + + count = 0; + for (size_t i = 0; i < size_t(m.FN()); ++i) + { + FaceType & f = m.face[i]; + + ScalarType quality = vcg::QualityRadii(f.cP(0), f.cP(1), f.cP(2)); + + if (quality <= 0.00000001) + { + //find longest edge + double edges[3]; + edges[0] = vcg::Distance(f.cP(0), f.cP(1)); + edges[1] = vcg::Distance(f.cP(1), f.cP(2)); + edges[2] = vcg::Distance(f.cP(2), f.cP(0)); + int longestIdx = std::find(edges, edges+3, std::max(std::max(edges[0], edges[1]), edges[2])) - (edges); + + if (vcg::tri::IsMarked(m, f.V2(longestIdx))) + continue; + + + auto f1 = f.cFFp(longestIdx); + vcg::tri::Mark(m,f.V2(longestIdx)); + if (!vcg::face::IsBorder(f, longestIdx) && vcg::face::IsManifold(f, longestIdx) && vcg::face::checkFlipEdgeNotManifold(f, longestIdx)) { + + // Check if EdgeFlipping improves quality + FacePointer g = f.FFp(longestIdx); int k = f.FFi(longestIdx); + vcg::Triangle3 t1(f.P(longestIdx), f.P1(longestIdx), f.P2(longestIdx)), t2(g->P(k), g->P1(k), g->P2(k)), + t3(f.P(longestIdx), g->P2(k), f.P2(longestIdx)), t4(g->P(k), f.P2(longestIdx), g->P2(k)); + + if ( std::min( QualityFace(t1), QualityFace(t2) ) <= std::min( QualityFace(t3), QualityFace(t4) )) + { + ScalarType dist; + CoordType closest; + auto fp0 = vcg::tri::GetClosestFaceBase(*params.mProject, params.grid, vcg::Barycenter(t3), 0.000001, dist, closest); + if (fp0 == NULL) + continue; + + auto fp1 = vcg::tri::GetClosestFaceBase(*params.mProject, params.grid, vcg::Barycenter(t4), 0.000001, dist, closest); + if (fp1 == NULL) + continue; + + vcg::face::FlipEdgeNotManifold(f, longestIdx); + ++count; + } + } + } + } + } while (count); + } + + static void cleanMesh(MeshType & m, Params & params) + { + vcg::tri::Clean::RemoveDuplicateFace(m); + vcg::tri::Clean::RemoveUnreferencedVertex(m); + vcg::tri::Allocator::CompactEveryVector(m); + + vcg::tri::UpdateTopology::FaceFace(m); + removeColinearFaces(m, params); + vcg::tri::UpdateTopology::FaceFace(m); + } + +public: + static void Do(MeshType &toRemesh, Params & params, vcg::CallBackPos * cb=0) { MeshType toProjectCopy; @@ -151,6 +237,7 @@ public: assert(&toRemesh != &toProject); params.stat.Reset(); + tri::UpdateBounding::Box(toRemesh); { @@ -161,6 +248,9 @@ public: params.grid.Set(toProject.face.begin(), toProject.face.end()); } + if (params.cleanFlag) + cleanMesh(toRemesh, params); + tri::UpdateTopology::FaceFace(toRemesh); tri::UpdateFlags::VertexBorderFromFaceAdj(toRemesh); tri::UpdateTopology::VertexFace(toRemesh); @@ -173,7 +263,6 @@ public: for(int i=0; i < params.iter; ++i) { // params.stat.Reset(); - if(cb) cb(100*i/params.iter, "Remeshing"); if(params.splitFlag) @@ -1023,6 +1112,7 @@ private: { bp = VertexPair(pi.VFlip(), pi.V()); Collapser::Do(m, bp, mp, true); + ++params.stat.collapseNum; ++count; break; }