added clean option in isotropic remeshing and updated the remeshing example
This commit is contained in:
parent
87f8ba9b33
commit
cfe695ece3
|
@ -41,57 +41,79 @@ class MyEdge;
|
||||||
class MyFace;
|
class MyFace;
|
||||||
class MyVertex;
|
class MyVertex;
|
||||||
struct MyUsedTypes : public UsedTypes< Use<MyVertex> ::AsVertexType,
|
struct MyUsedTypes : public UsedTypes< Use<MyVertex> ::AsVertexType,
|
||||||
Use<MyEdge> ::AsEdgeType,
|
Use<MyEdge> ::AsEdgeType,
|
||||||
Use<MyFace> ::AsFaceType>{};
|
Use<MyFace> ::AsFaceType>{};
|
||||||
|
|
||||||
class MyVertex : public Vertex<MyUsedTypes, vertex::Coord3f, vertex::Normal3f, vertex::VFAdj, vertex::Qualityf, vertex::BitFlags >{};
|
class MyVertex : public Vertex<MyUsedTypes, vertex::Coord3f, vertex::Normal3f, vertex::VFAdj, vertex::Qualityf, vertex::BitFlags, vertex::Mark>{};
|
||||||
class MyFace : public Face< MyUsedTypes, face::Mark, face::VertexRef, face::VFAdj, face::FFAdj, face::Normal3f, face::BitFlags > {};
|
class MyFace : public Face< MyUsedTypes, face::Mark, face::VertexRef, face::VFAdj, face::FFAdj, face::Normal3f, face::BitFlags > {};
|
||||||
class MyEdge : public Edge<MyUsedTypes>{};
|
class MyEdge : public Edge<MyUsedTypes>{};
|
||||||
class MyMesh : public tri::TriMesh< vector<MyVertex>, vector<MyFace> , vector<MyEdge> > {};
|
class MyMesh : public tri::TriMesh< vector<MyVertex>, vector<MyFace> , vector<MyEdge> > {};
|
||||||
|
|
||||||
int main( int argc, char **argv )
|
int main( int argc, char **argv )
|
||||||
{
|
{
|
||||||
MyMesh original,toremesh;
|
MyMesh original,toremesh;
|
||||||
if(argc<2)
|
if(argc<2)
|
||||||
{
|
{
|
||||||
printf("Usage: trimesh_remesh <filename> [targetLen [iterNum] ]");
|
printf("Usage: trimesh_remesh <filename> [[targetLen (bbox perc)] [iterNum] [creaseAngle] [maxSurfDist (bbox perc)]]");
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tri::io::Importer<MyMesh>::Open(original,argv[1])!=0)
|
if(tri::io::Importer<MyMesh>::Open(original,argv[1])!=0)
|
||||||
{
|
{
|
||||||
printf("Error reading file %s\n",argv[1]);
|
printf("Error reading file %s\n",argv[1]);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
float targetLenPerc=.2f;
|
float targetLenPerc=.2f;
|
||||||
int iterNum=20;
|
int iterNum=20;
|
||||||
if(argc>=3) targetLenPerc = atof(argv[2]);
|
float creaseAngle = 30.f;
|
||||||
if(argc>=4) iterNum = atoi(argv[3]);
|
float maxSurfDistPerc = 0.001f;
|
||||||
|
if(argc>=3) targetLenPerc = atof(argv[2]);
|
||||||
// Mesh cleaning
|
if(argc>=4) iterNum = atoi(argv[3]);
|
||||||
tri::Clean<MyMesh>::RemoveDuplicateVertex(original);
|
if(argc>=5) creaseAngle = atof(argv[4]);
|
||||||
tri::Clean<MyMesh>::RemoveUnreferencedVertex(original);
|
if(argc>=6) maxSurfDistPerc = atof(argv[5]);
|
||||||
Allocator<MyMesh>::CompactEveryVector(original);
|
|
||||||
tri::UpdateNormal<MyMesh>::PerVertexNormalizedPerFaceNormalized(original);
|
|
||||||
tri::UpdateBounding<MyMesh>::Box(original);
|
// Mesh cleaning
|
||||||
|
tri::Clean<MyMesh>::RemoveUnreferencedVertex(original);
|
||||||
vcg::tri::Append<MyMesh,MyMesh>::MeshCopy(toremesh,original);
|
Allocator<MyMesh>::CompactEveryVector(original);
|
||||||
tri::UpdateNormal<MyMesh>::PerVertexNormalizedPerFaceNormalized(toremesh);
|
|
||||||
tri::UpdateBounding<MyMesh>::Box(toremesh);
|
|
||||||
|
tri::UpdateNormal<MyMesh>::PerVertexNormalizedPerFaceNormalized(original);
|
||||||
tri::UpdateTopology<MyMesh>::FaceFace(toremesh);
|
tri::UpdateBounding<MyMesh>::Box(original);
|
||||||
tri::MeshAssert<MyMesh>::FFTwoManifoldEdge(toremesh);
|
|
||||||
float lengthThr = targetLenPerc*(original.bbox.Diag()/100.f);
|
vcg::tri::Append<MyMesh,MyMesh>::MeshCopy(toremesh,original);
|
||||||
printf("Length Thr: %8.3f ~ %4.2f %% on %5.3f\n",lengthThr,targetLenPerc,original.bbox.Diag());
|
tri::UpdateNormal<MyMesh>::PerVertexNormalizedPerFaceNormalized(toremesh);
|
||||||
|
tri::UpdateBounding<MyMesh>::Box(toremesh);
|
||||||
IsotropicRemeshing<MyMesh>::Params params;
|
|
||||||
params.SetTargetLen(lengthThr);
|
tri::UpdateTopology<MyMesh>::FaceFace(toremesh);
|
||||||
params.SetFeatureAngleDeg(10);
|
float lengthThr = targetLenPerc*(original.bbox.Diag()/100.f);
|
||||||
params.iter=iterNum;
|
float maxSurfDist = maxSurfDistPerc*(original.bbox.Diag()/100.f);
|
||||||
printf(" Input mesh %8i v %8i f\n",toremesh.VN(),toremesh.FN());
|
printf("Length Thr: %8.3f ~ %4.2f %% on %5.3f\n",lengthThr,targetLenPerc,original.bbox.Diag());
|
||||||
IsotropicRemeshing<MyMesh>::Do(toremesh, original, params);
|
|
||||||
vcg::tri::io::ExporterPLY<MyMesh>::Save(toremesh, "remesh.ply");
|
IsotropicRemeshing<MyMesh>::Params params;
|
||||||
printf("Output mesh %8i v %8i f\n",toremesh.VN(),toremesh.FN());
|
params.SetTargetLen(lengthThr);
|
||||||
|
params.SetFeatureAngleDeg(creaseAngle);
|
||||||
return 0;
|
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<MyMesh>::Do(toremesh, original, params);
|
||||||
|
vcg::tri::io::ExporterPLY<MyMesh>::Save(toremesh, "remesh.ply");
|
||||||
|
printf("Output mesh %8i v %8i f\n",toremesh.VN(),toremesh.FN());
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,15 +23,16 @@
|
||||||
#ifndef _VCG_ISOTROPICREMESHING_H
|
#ifndef _VCG_ISOTROPICREMESHING_H
|
||||||
#define _VCG_ISOTROPICREMESHING_H
|
#define _VCG_ISOTROPICREMESHING_H
|
||||||
|
|
||||||
#include<vcg/complex/algorithms/update/quality.h>
|
#include <vcg/complex/algorithms/update/quality.h>
|
||||||
#include<vcg/complex/algorithms/update/curvature.h>
|
#include <vcg/complex/algorithms/update/curvature.h>
|
||||||
#include<vcg/complex/algorithms/update/normal.h>
|
#include <vcg/complex/algorithms/update/normal.h>
|
||||||
#include<vcg/complex/algorithms/refine.h>
|
#include <vcg/complex/algorithms/refine.h>
|
||||||
#include<vcg/complex/algorithms/stat.h>
|
#include <vcg/complex/algorithms/stat.h>
|
||||||
#include<vcg/complex/algorithms/smooth.h>
|
#include <vcg/complex/algorithms/smooth.h>
|
||||||
#include<vcg/complex/algorithms/local_optimization/tri_edge_collapse.h>
|
#include <vcg/complex/algorithms/local_optimization/tri_edge_collapse.h>
|
||||||
#include<vcg/space/index/spatial_hashing.h>
|
#include <vcg/space/index/spatial_hashing.h>
|
||||||
|
#include <vcg/complex/append.h>
|
||||||
|
#include <vcg/complex/allocate.h>
|
||||||
#include <wrap/io_trimesh/export.h>
|
#include <wrap/io_trimesh/export.h>
|
||||||
|
|
||||||
namespace vcg {
|
namespace vcg {
|
||||||
|
@ -42,6 +43,7 @@ class IsotropicRemeshing
|
||||||
public:
|
public:
|
||||||
typedef TRI_MESH_TYPE MeshType;
|
typedef TRI_MESH_TYPE MeshType;
|
||||||
typedef typename MeshType::FaceType FaceType;
|
typedef typename MeshType::FaceType FaceType;
|
||||||
|
typedef typename MeshType::FacePointer FacePointer;
|
||||||
typedef typename FaceType::VertexType VertexType;
|
typedef typename FaceType::VertexType VertexType;
|
||||||
typedef typename FaceType::VertexPointer VertexPointer;
|
typedef typename FaceType::VertexPointer VertexPointer;
|
||||||
typedef typename VertexType::ScalarType ScalarType;
|
typedef typename VertexType::ScalarType ScalarType;
|
||||||
|
@ -85,6 +87,7 @@ public:
|
||||||
bool smoothFlag=true;
|
bool smoothFlag=true;
|
||||||
bool projectFlag=true;
|
bool projectFlag=true;
|
||||||
bool selectedOnly = false;
|
bool selectedOnly = false;
|
||||||
|
bool cleanFlag = true;
|
||||||
|
|
||||||
bool userSelectedCreases = false;
|
bool userSelectedCreases = false;
|
||||||
bool surfDistCheck = true;
|
bool surfDistCheck = true;
|
||||||
|
@ -111,6 +114,7 @@ public:
|
||||||
|
|
||||||
} Params;
|
} Params;
|
||||||
|
|
||||||
|
private:
|
||||||
static void debug_crease (MeshType & toRemesh, std::string prepend, int i)
|
static void debug_crease (MeshType & toRemesh, std::string prepend, int i)
|
||||||
{
|
{
|
||||||
ForEachVertex(toRemesh, [] (VertexType & v) {
|
ForEachVertex(toRemesh, [] (VertexType & v) {
|
||||||
|
@ -136,6 +140,88 @@ public:
|
||||||
vcg::tri::io::Exporter<MeshType>::Save(toRemesh, prepend.c_str(), vcg::tri::io::Mask::IOM_ALL);
|
vcg::tri::io::Exporter<MeshType>::Save(toRemesh, prepend.c_str(), vcg::tri::io::Mask::IOM_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void removeColinearFaces(MeshType & m, Params & params)
|
||||||
|
{
|
||||||
|
vcg::tri::UpdateTopology<MeshType>::FaceFace(m);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
// MeshType projectMesh;
|
||||||
|
// vcg::tri::Append<MeshType, MeshType>::MeshCopy(projectMesh, m);
|
||||||
|
// vcg::tri::UpdateBounding<MeshType>::Box(projectMesh);
|
||||||
|
// vcg::tri::UpdateNormal<MeshType>::PerVertexNormalizedPerFace(projectMesh);
|
||||||
|
|
||||||
|
// StaticGrid grid;
|
||||||
|
// grid.Set(projectMesh.face.begin(), projectMesh.face.end());
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
vcg::tri::UpdateTopology<MeshType>::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<FaceType>(f, longestIdx)) {
|
||||||
|
|
||||||
|
// Check if EdgeFlipping improves quality
|
||||||
|
FacePointer g = f.FFp(longestIdx); int k = f.FFi(longestIdx);
|
||||||
|
vcg::Triangle3<ScalarType> 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<FaceType>(f, longestIdx);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cleanMesh(MeshType & m, Params & params)
|
||||||
|
{
|
||||||
|
vcg::tri::Clean<MeshType>::RemoveDuplicateFace(m);
|
||||||
|
vcg::tri::Clean<MeshType>::RemoveUnreferencedVertex(m);
|
||||||
|
vcg::tri::Allocator<MeshType>::CompactEveryVector(m);
|
||||||
|
|
||||||
|
vcg::tri::UpdateTopology<MeshType>::FaceFace(m);
|
||||||
|
removeColinearFaces(m, params);
|
||||||
|
vcg::tri::UpdateTopology<MeshType>::FaceFace(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
static void Do(MeshType &toRemesh, Params & params, vcg::CallBackPos * cb=0)
|
static void Do(MeshType &toRemesh, Params & params, vcg::CallBackPos * cb=0)
|
||||||
{
|
{
|
||||||
MeshType toProjectCopy;
|
MeshType toProjectCopy;
|
||||||
|
@ -151,6 +237,7 @@ public:
|
||||||
assert(&toRemesh != &toProject);
|
assert(&toRemesh != &toProject);
|
||||||
params.stat.Reset();
|
params.stat.Reset();
|
||||||
|
|
||||||
|
|
||||||
tri::UpdateBounding<MeshType>::Box(toRemesh);
|
tri::UpdateBounding<MeshType>::Box(toRemesh);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -161,6 +248,9 @@ public:
|
||||||
params.grid.Set(toProject.face.begin(), toProject.face.end());
|
params.grid.Set(toProject.face.begin(), toProject.face.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (params.cleanFlag)
|
||||||
|
cleanMesh(toRemesh, params);
|
||||||
|
|
||||||
tri::UpdateTopology<MeshType>::FaceFace(toRemesh);
|
tri::UpdateTopology<MeshType>::FaceFace(toRemesh);
|
||||||
tri::UpdateFlags<MeshType>::VertexBorderFromFaceAdj(toRemesh);
|
tri::UpdateFlags<MeshType>::VertexBorderFromFaceAdj(toRemesh);
|
||||||
tri::UpdateTopology<MeshType>::VertexFace(toRemesh);
|
tri::UpdateTopology<MeshType>::VertexFace(toRemesh);
|
||||||
|
@ -173,7 +263,6 @@ public:
|
||||||
for(int i=0; i < params.iter; ++i)
|
for(int i=0; i < params.iter; ++i)
|
||||||
{
|
{
|
||||||
// params.stat.Reset();
|
// params.stat.Reset();
|
||||||
|
|
||||||
if(cb) cb(100*i/params.iter, "Remeshing");
|
if(cb) cb(100*i/params.iter, "Remeshing");
|
||||||
|
|
||||||
if(params.splitFlag)
|
if(params.splitFlag)
|
||||||
|
@ -1023,6 +1112,7 @@ private:
|
||||||
{
|
{
|
||||||
bp = VertexPair(pi.VFlip(), pi.V());
|
bp = VertexPair(pi.VFlip(), pi.V());
|
||||||
Collapser::Do(m, bp, mp, true);
|
Collapser::Do(m, bp, mp, true);
|
||||||
|
++params.stat.collapseNum;
|
||||||
++count;
|
++count;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue