vcglib/apps/sample/polygonmesh_quadsimpl/polygonmesh_quadsimpl.cpp

460 lines
12 KiB
C++

/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004-2016 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#include <sstream>
#include <vcg/complex/complex.h>
#include <vcg/complex/algorithms/clean.h>
#include <wrap/io_trimesh/import.h>
#include <wrap/io_trimesh/export_off.h>
#include <vcg/complex/algorithms/polygon_support.h>
#include <vcg/complex/algorithms/local_optimization/quad_diag_collapse.h>
#include <vcg/complex/algorithms/update/fitmaps.h>
using namespace vcg;
using namespace std;
// forward declarations
class CFace;
class CVertex;
class CHEdge;
class CEdge;
class MyPolyVertex;
struct CUsedTypes: public vcg::UsedTypes< vcg::Use<CVertex>::AsVertexType, vcg::Use<CFace>::AsFaceType >{};
// Mesh of triangles
class CVertex : public Vertex<
CUsedTypes,
vertex::BitFlags,
vertex::Coord3f,
vertex::Normal3f,
vertex::VFAdj,
vertex::Mark,
vcg::vertex::Curvaturef,
vcg::vertex::CurvatureDirf,
vertex::Color4b,
vertex::Qualityf
>{};
class CFace : public Face<
CUsedTypes,
face::VertexRef,
face::Normal3f,
face::BitFlags,
face::FFAdj,
face::VFAdj,
face::Mark,
face::EdgePlane
> {};
class CMesh : public vcg::tri::TriMesh< vector<CVertex>, vector<CFace> > {};
// Poly mesh
class MyPolyFace;
class MyPolyVertex;
struct PolyUsedTypes: public vcg::UsedTypes<
vcg::Use<MyPolyVertex> ::AsVertexType,
vcg::Use<CEdge> ::AsEdgeType,
vcg::Use<CHEdge> ::AsHEdgeType,
vcg::Use<MyPolyFace> ::AsFaceType
>{};
class MyPolyVertex:public Vertex<
PolyUsedTypes,
vertex::Coord3f,
vertex::Normal3f,
vertex::Mark,
vertex::BitFlags,
vertex::VHAdj,
vertex::VFAdj
>{};
class CEdge : public Edge<PolyUsedTypes>{};
class CHEdge : public HEdge<
PolyUsedTypes,
hedge::BitFlags,
hedge::HFAdj,
hedge::HOppAdj,
hedge::HNextAdj,
hedge::HVAdj,
hedge::HPrevAdj,
hedge::Mark
>{};
class MyPolyFace:public Face<
PolyUsedTypes,
face::PolyInfo,
face::PFVAdj,
face::PFFAdj,
face::PFHAdj,
face::BitFlags,
face::Normal3f,
face::Mark
> {};
class MyPolyMesh: public tri::TriMesh<
std::vector<MyPolyVertex>,
std::vector<MyPolyFace>,
std::vector<CHEdge>,
std::vector<CEdge>
>{};
/*!
* \brief Collapse operation for adaptive simplification using fitmaps
*
*/
class MyCollapseAdaptive: public vcg::tri::QuadDiagonalCollapse< MyPolyMesh, MyCollapseAdaptive, CMesh , vcg::tri::VertReg<MyPolyMesh> ,vcg::tri::FitmapsCollapse<MyPolyMesh, CMesh> , vcg::tri::FitmapsCollapse<MyPolyMesh, CMesh> >
{
public:
typedef vcg::tri::QuadDiagonalCollapse< MyPolyMesh, MyCollapseAdaptive, CMesh , vcg::tri::VertReg<MyPolyMesh>, vcg::tri::FitmapsCollapse<MyPolyMesh, CMesh> , vcg::tri::FitmapsCollapse<MyPolyMesh, CMesh> > constructor;
MyCollapseAdaptive(HEdgePointer he, int mark):constructor(he,mark){}
};
/*!
* \brief Collapse for uniform simplification
*
*/
class MyCollapse: public vcg::tri::QuadDiagonalCollapseBase< MyPolyMesh, MyCollapse, CMesh , vcg::tri::VertReg<MyPolyMesh> >
{
public:
typedef vcg::tri::QuadDiagonalCollapseBase< MyPolyMesh, MyCollapse, CMesh , vcg::tri::VertReg<MyPolyMesh> > constructor;
MyCollapse(HEdgePointer he, int mark):constructor(he,mark){}
};
typedef CMesh::FaceType TriFaceType;
typedef vcg::GridStaticPtr<CMesh::FaceType, TriFaceType::ScalarType> GRID;
typedef CMesh::PerVertexAttributeHandle<float> Fitmap_attr;
/*! Initializes the grid for smoothing and fitmaps
*
* \param m Reference mesh
*
*/
void initGrid(CMesh & m)
{
GRID* grid = new GRID();
vcg::tri::UpdateBounding<CMesh>::Box(m);
vcg::tri::UpdateEdges<CMesh>::Set(m);
grid->Set(m.face.begin(), m.face.end());
// grid->ShowStats(stdout);
MyCollapse::grid() = grid;
MyCollapseAdaptive::grid() = grid;
}
/*! Initializes the heap of operations on a mesh
*
* \param m Mesh
* \param loc
* \param Adaptive Specifies if simplificaton will be adaptive
*
*/
void init_heap(MyPolyMesh &m, vcg::LocalOptimization<MyPolyMesh> &loc, bool adaptive)
{
if(adaptive)
MyCollapseAdaptive::Init(m, loc.h);
else
MyCollapse::Init(m,loc.h);
std::make_heap(loc.h.begin(),loc.h.end());
if(!loc.h.empty())
loc.currMetric=loc.h.front().pri;
}
/*! Read fitmaps values from a file and loads them into a mesh
*
* \param m Mesh
* \param fn Name of the file to read
*
*/
bool read_fitmaps(CMesh &m, const char *fn)
{
ifstream fitmaps;
fitmaps.open(fn);
if(! fitmaps.is_open())
return false;
Fitmap_attr S_Fit = tri::Allocator<CMesh>::GetPerVertexAttribute<float>(m,"S-Fitmap");
Fitmap_attr M_Fit = tri::Allocator<CMesh>::GetPerVertexAttribute<float>(m,"M-Fitmap");
int index;
float S_fit, M_fit;
do
{
fitmaps >> index >> S_fit >> M_fit;
S_Fit[m.vert[index]] = S_fit;
M_Fit[m.vert[index]] = M_fit;
}while(fitmaps.good());
bool eof = fitmaps.eof();
fitmaps.close();
return eof;
}
/*! Writes fitmaps values into a file
*
* \param m Mesh
* \param fn Name of the file to write
*
*/
bool store_fitmaps(CMesh &m, const char *fn)
{
ofstream fitmaps;
fitmaps.open(fn);
if(! fitmaps.is_open())
return false;
Fitmap_attr S_Fit = tri::Allocator<CMesh>::GetPerVertexAttribute<float>(m,"S-Fitmap");
Fitmap_attr M_Fit = tri::Allocator<CMesh>::GetPerVertexAttribute<float>(m,"M-Fitmap");
for(unsigned int i =0; i< m.vert.size(); i++)
{
if( !(m.vert[i].IsD()) )
{
fitmaps << i << " " << S_Fit[m.vert[i]] << " " << M_Fit[m.vert[i]] << endl;
if(!fitmaps.good())
{
fitmaps.close();
return false;
}
}
}
fitmaps.close();
return true;
}
/*! Load fitmaps for a mesh, computing them or reading values from a file
*
* \param m Mesh
* \param fn Name of the mesh file
*
*/
void load_fitmaps(CMesh &m, char* fn)
{
Fitmap_attr S_Fit = tri::Allocator<CMesh>::AddPerVertexAttribute<float> (m, string("S-Fitmap"));
Fitmap_attr M_Fit = tri::Allocator<CMesh>::AddPerVertexAttribute<float> (m, string("M-Fitmap"));
string filename(fn);
int found = filename.find_last_of("/");
string name = filename.substr(found+1);
string suffix = ".fmp";
if( !read_fitmaps( m, (name + suffix).c_str()) )
{
tri::Fitmaps<CMesh>::computeSFitmap(m);
for(CMesh::VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
S_Fit[vi] = vi->Q();
tri::Fitmaps<CMesh>::computeMFitmap(m, 5);
for(CMesh::VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
M_Fit[vi] = vi->Q();
store_fitmaps(m, ( name + suffix).c_str());
}
}
/*! Load a mesh, from a file
*
* \param m Mesh that will be filled with data from a file
* \param fn Name of the mesh file
* \param loadFitmaps Specifies if fitmaps have to be loaded
*
*/
void loadMesh(CMesh & m, char* fn, bool loadFitmaps = false)
{
int ret = vcg::tri::io::Importer<CMesh>::Open(m,fn);
if(ret != 0)
{
cerr << "Error reading file " << fn << endl;
exit(1);
}
tri::Clean<CMesh>::RemoveDegenerateFace(m);
tri::Clean<CMesh>::RemoveDuplicateFace(m);
tri::Clean<CMesh>::RemoveDuplicateVertex(m);
tri::Clean<CMesh>::RemoveUnreferencedVertex(m);
tri::UpdateTopology<CMesh>::FaceFace(m);
tri::Clean<CMesh>::RemoveNonManifoldFace(m);
tri::UpdateTopology<CMesh>::FaceFace(m);
tri::Clean<CMesh>::RemoveNonManifoldVertex(m);
tri::UpdateTopology<CMesh>::FaceFace(m);
// update bounding box
vcg::tri::UpdateBounding<CMesh>::Box (m);
// update Normals
vcg::tri::UpdateNormals<CMesh>::PerVertexNormalizedPerFace(m);
vcg::tri::UpdateNormals<CMesh>::PerFaceNormalized(m);
if(loadFitmaps)
load_fitmaps(m,fn);
}
int main(int argc, char *argv[]) {
// HE mesh
MyPolyMesh pm;
// Tri meshes
CMesh mesh,refMesh;
char* meshfile = NULL;
char* trimeshfile = NULL;
char* outfile = "output.off";
int faces;
bool adaptive = false;
if(argc < 2)
{
cerr << "Usage: " << argv[0] << " -meshfile filename [-trimeshfile filename] -faces num_faces [-adaptive] [-outfile filename]" << endl;
}
for(int i=1; i< argc; i++)
{
string arg = string(argv[i]);
if ( arg == "-meshfile")
meshfile = argv[++i];
else if (arg == "-trimeshfile")
trimeshfile = argv[++i];
else if (arg == "-faces")
{
stringstream myStream(argv[++i], stringstream::in | stringstream::out);
myStream >> faces;
}
else if (arg == "-outfile")
outfile = argv[++i];
else if (arg == "-adaptive")
adaptive = true;
}
if( !meshfile)
{
cerr << "Missing mesh filename" << endl;
exit(1);
}
if(faces < 0)
{
cerr << "Missing faces number" << endl;
exit(1);
}
// Load the mesh to simplify
loadMesh(mesh, meshfile);
// Load the reference mesh
if(trimeshfile)
loadMesh(refMesh, trimeshfile, adaptive);
else
loadMesh(refMesh, meshfile, adaptive);
initGrid(refMesh);
MyCollapse::refMesh() = &refMesh;
MyCollapseAdaptive::refMesh() = &refMesh;
vcg::tri::PolygonSupport<CMesh,MyPolyMesh>::ImportFromTriMesh(pm,mesh);
vcg::tri::UpdateHalfEdges<MyPolyMesh>::FromIndexed(pm);
// After loading check mesh consistency
assert(vcg::tri::UpdateHalfEdges<MyPolyMesh>::CheckConsistency(pm));
HalfedgeQuadClean<MyPolyMesh>::remove_singlets(pm);
HalfedgeQuadClean<MyPolyMesh>::remove_doublets(pm);
vcg::LocalOptimization<MyPolyMesh> loc(pm);
init_heap(pm, loc, adaptive);
loc.HeapSimplexRatio = 9;
loc.SetTargetSimplices(faces);
// Perform simplification
loc.DoOptimization();
assert(vcg::tri::UpdateHalfEdges<MyPolyMesh>::CheckConsistency(pm));
vcg::tri::UpdateIndexed<MyPolyMesh>::FromHalfEdges(pm );
int ret = tri::io::ExporterOFF<MyPolyMesh>::Save(pm, outfile, tri::io::Mask::IOM_BITPOLYGONAL );
if(ret != 0 )
{
cerr << "Error saving file" << endl;
exit(1);
}
cout << "Simplification ended successfully!" << endl;
}