1 /****************************************************************************
2 * VCGLib                                                            o o     *
3 * Visual and Computer Graphics Library                            o     o   *
4 *                                                                _   O  _   *
5 * Copyright(C) 2004-2016                                           \/)\/    *
6 * Visual Computing Lab                                            /\/|      *
7 * ISTI - Italian National Research Council                           |      *
8 *                                                                    \      *
9 * All rights reserved.                                                      *
10 *                                                                           *
11 * This program is free software; you can redistribute it and/or modify      *
12 * it under the terms of the GNU General Public License as published by      *
13 * the Free Software Foundation; either version 2 of the License, or         *
14 * (at your option) any later version.                                       *
15 *                                                                           *
16 * This program is distributed in the hope that it will be useful,           *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
20 * for more details.                                                         *
21 *                                                                           *
22 ****************************************************************************/
23 
24 #ifndef __PLYMC_H__
25 #define __PLYMC_H__
26 
27 #ifndef WIN32
28 #define _int64 long long
29 #define __int64 long long
30 #define __cdecl
31 #endif
32 
33 #include <cstdio>
34 #include <time.h>
35 #include <float.h>
36 #include <math.h>
37 
38 #include <vcg/complex/complex.h>
39 
40 #include <vcg/math/histogram.h>
41 #include <vcg/complex/algorithms/geodesic.h>
42 #include <wrap/io_trimesh/import.h>
43 #include <wrap/io_trimesh/export_ply.h>
44 //#include <wrap/ply/plystuff.h>
45 
46 #include <vcg/complex/algorithms/create/marching_cubes.h>
47 #include <vcg/complex/algorithms/create/mc_trivial_walker.h>
48 
49 // local optimization
50 #include <vcg/complex/algorithms/local_optimization.h>
51 #include <vcg/complex/algorithms/local_optimization/tri_edge_collapse.h>
52 #include <vcg/complex/algorithms/local_optimization/tri_edge_collapse_quadric.h>
53 
54 #include <stdarg.h>
55 #include "volume.h"
56 #include "tri_edge_collapse_mc.h"
57 namespace vcg {
58 namespace tri {
59 
60 // Simple prototype for later use...
61 template<class MeshType>
62 void MCSimplify( MeshType &m, float perc, bool preserveBB=true, vcg::CallBackPos *cb=0);
63 
64 
65 /** Surface Reconstruction
66  *
67  *  To allow the managment of a very large set of meshes to be merged,
68  *  it is templated on a MeshProvider class that is able to provide the meshes to be merged.
69  *  IT is the surface reconstrction algorithm that have been used for a long time inside the ISTI-Visual Computer Lab.
70  *  It is mostly a variant of the Curless et al. e.g. a volumetric approach with some original weighting schemes,"
71  *  a different expansion rule, and another approach to hole filling through volume dilation/relaxations.
72  */
73 
74 template < class SMesh, class MeshProvider>
75 class PlyMC
76 {
77 public:
78 
79   typedef typename SMesh::FaceIterator     SFaceIterator;
80   typedef typename SMesh::VertexIterator   SVertexIterator;
81 
82   class MCVertex;
83   class MCEdge;
84   class MCFace;
85 
86   class MCUsedTypes: public vcg::UsedTypes <  vcg::Use<MCVertex>::template AsVertexType,
87                                               vcg::Use<MCEdge  >::template AsEdgeType,
88                                               vcg::Use<MCFace  >::template AsFaceType >{};
89 
90   class MCVertex  : public Vertex< MCUsedTypes, vertex::Coord3f, vertex::Color4b, vertex::Mark, vertex::VFAdj, vertex::BitFlags, vertex::Qualityf>{};
91   class MCEdge : public Edge<MCUsedTypes,edge::VertexRef>{};
92   class MCFace    : public Face< MCUsedTypes, face::InfoOcf, face::VertexRef, face::FFAdjOcf, face::VFAdjOcf, face::BitFlags> {};
93   class MCMesh	: public vcg::tri::TriMesh< std::vector< MCVertex>, face::vector_ocf< MCFace > > {};
94 
95 
96   //******************************************
97   //typedef Voxel<float> Voxelf;
98   typedef Voxelfc Voxelf;
99   //******************************************
100 
101   class Parameter
102   {
103   public:
Parameter()104     Parameter()
105     {
106       NCell=10000;
107       WideNum= 3;
108       WideSize=0;
109       VoxSize=0;
110       IPosS=Point3i(0,0,0);  // SubVolume Start
111       IPosE=Point3i(0,0,0);  // SubVolume End
112       IPosB=Point3i(0,0,0);  // SubVolume to restart from in lexicographic order (useful for crashes)
113       IPos=Point3i(0,0,0);
114       IDiv=Point3i(1,1,1);
115       VerboseLevel=0;
116       SliceNum=1;
117       FillThr=12;
118       ExpAngleDeg=30;
119       SmoothNum=1;
120       RefillNum=1;
121       IntraSmoothFlag = false;
122       QualitySmoothAbs = 0.0f; //  0 means un-setted value.
123       QualitySmoothVox = 3.0f; // expressed in voxel
124       OffsetFlag=false;
125       OffsetThr=-3;
126       GeodesicQualityFlag=true;
127       PLYFileQualityFlag=false;
128       FullyPreprocessedFlag=false;
129       SaveVolumeFlag=false;
130       SafeBorder=1;
131       CleaningFlag=false;
132       SimplificationFlag=false;
133       VertSplatFlag=false;
134       MergeColor=false;
135       basename = "plymcout";
136     }
137 
138     int NCell;
139     int WideNum;
140     float WideSize;
141     float VoxSize;
142     Point3i IPosS;  // SubVolume Start
143     Point3i IPosE;  // SubVolume End
144     Point3i IPosB;  // SubVolume to restart from in lexicographic order (useful for crashes)
145     Point3i IPos;
146     Point3i IDiv;
147     int VerboseLevel;
148     int SliceNum;
149     int FillThr;
150     float ExpAngleDeg;
151     int SmoothNum;
152     int RefillNum;
153     bool IntraSmoothFlag;
154     float QualitySmoothAbs; //  0 means un-setted value.
155     float QualitySmoothVox; // expressed in voxel
156     bool OffsetFlag;
157     float OffsetThr;
158     bool GeodesicQualityFlag;
159     bool PLYFileQualityFlag;
160     bool FullyPreprocessedFlag;
161     bool CleaningFlag;
162     bool SaveVolumeFlag;
163     int SafeBorder;
164     bool SimplificationFlag;
165     bool VertSplatFlag;
166     bool MergeColor;
167     std::string basename;
168     std::vector<std::string> OutNameVec;
169     std::vector<std::string> OutNameSimpVec;
170   }; //end Parameter class
171 
172   /// PLYMC Data
173   MeshProvider MP;
174   Parameter p;
175   Volume<Voxelf> VV;
176   char errorMessage[1024];
177 
178 /// PLYMC Methods
179 
InitMesh(SMesh & m,const char * filename,Matrix44f Tr)180   bool InitMesh(SMesh &m, const char *filename, Matrix44f Tr)
181   {
182     int loadmask;
183     int ret = tri::io::Importer<SMesh>::Open(m,filename,loadmask);
184     if(ret)
185     {
186       printf("Error: unabe to open mesh '%s'",filename);
187       return false;
188     }
189 
190     if(p.VertSplatFlag)
191     {
192       if(!(loadmask & tri::io::Mask::IOM_VERTNORMAL))
193       {
194         if(m.FN()==0)
195         {
196           sprintf(errorMessage,"%sError: mesh has not per vertex normals\n",errorMessage);
197           return false;
198         }
199         else
200         {
201           tri::Clean<SMesh>::RemoveUnreferencedVertex(m);
202           tri::Allocator<SMesh>::CompactEveryVector(m);
203           tri::UpdateNormal<SMesh>::PerVertexNormalizedPerFaceNormalized(m);
204         }
205       }
206       tri::UpdateNormal<SMesh>::NormalizePerVertex(m);
207       int badNormalCnt=0;
208       for(SVertexIterator vi=m.vert.begin(); vi!=m.vert.end();++vi)
209         if(math::Abs(SquaredNorm((*vi).N())-1.0)>0.0001)
210         {
211           badNormalCnt++;
212           tri::Allocator<SMesh>::DeleteVertex(m,*vi);
213         }
214       tri::Allocator<SMesh>::CompactEveryVector(m);
215        if(badNormalCnt > m.VN()/10)
216         {
217           sprintf(errorMessage,"%sError: mesh has null normals\n",errorMessage);
218           return false;
219         }
220 
221       if(!(loadmask & tri::io::Mask::IOM_VERTQUALITY))
222         tri::UpdateQuality<SMesh>::VertexConstant(m,0);
223       tri::UpdateNormal<SMesh>::PerVertexMatrix(m,Tr);
224     }
225     else // processing for triangle meshes
226     {
227       if(!p.FullyPreprocessedFlag)
228       {
229         if(p.CleaningFlag){
230           int dup = tri::Clean<SMesh>::RemoveDuplicateVertex(m);
231           int unref =  tri::Clean<SMesh>::RemoveUnreferencedVertex(m);
232           printf("Removed %i duplicates and %i unref",dup,unref);
233         }
234 
235         tri::UpdateNormal<SMesh>::PerVertexNormalizedPerFaceNormalized(m);
236         if(p.GeodesicQualityFlag) {
237           tri::UpdateTopology<SMesh>::VertexFace(m);
238           tri::UpdateFlags<SMesh>::FaceBorderFromVF(m);
239           tri::Geodesic<SMesh>::DistanceFromBorder(m);
240         }
241       }
242 
243       tri::UpdatePosition<SMesh>::Matrix(m,Tr,true);
244       tri::UpdateBounding<SMesh>::Box(m);
245       printf("Init Mesh %s (%ivn,%ifn)\n",filename,m.vn,m.fn);
246     }
247     for(SVertexIterator vi=m.vert.begin(); vi!=m.vert.end();++vi)
248       VV.Interize((*vi).P());
249     return true;
250   }
251 
252   // This function add a mesh (or a point cloud to the volume)
253 // the point cloud MUST have normalized vertex normals.
AddMeshToVolumeM(SMesh & m,std::string meshname,const double w)254     bool AddMeshToVolumeM(SMesh &m, std::string meshname, const double w )
255     {
256       tri::RequireCompactness(m);
257       if(!m.bbox.Collide(VV.SubBoxSafe)) return false;
258       size_t found =meshname.find_last_of("/\\");
259       std::string shortname = meshname.substr(found+1);
260 
261       Volume <Voxelf> B;
262       B.Init(VV);
263 
264       bool res=false;
265       double quality=0;
266 
267       // Now add the mesh to the volume
268       if(!p.VertSplatFlag)
269       {
270         float minq=std::numeric_limits<float>::max(), maxq=-std::numeric_limits<float>::max();
271             // Calcolo range qualita geodesica PER FACCIA come media di quelle per vertice
272             for(SFaceIterator fi=m.face.begin(); fi!=m.face.end();++fi){
273                 (*fi).Q()=((*fi).V(0)->Q()+(*fi).V(1)->Q()+(*fi).V(2)->Q())/3.0f;
274                 minq=std::min((*fi).Q(),minq);
275                 maxq=std::max((*fi).Q(),maxq);
276             }
277 
278             // La qualita' e' inizialmente espressa come distanza assoluta dal bordo della mesh
279             printf("Q [%4.2f  %4.2f] \n",minq,maxq);
280             bool closed=false;
281             if(minq==maxq) closed=true;  // se la mesh e' chiusa la  ComputeGeodesicQuality mette la qualita a zero ovunque
282             // Classical approach: scan each face
283             int tt0=clock();
284             printf("---- Face Rasterization");
285             for(SFaceIterator fi=m.face.begin(); fi!=m.face.end();++fi)
286                 {
287                     if(closed || (p.PLYFileQualityFlag==false && p.GeodesicQualityFlag==false)) quality=1.0;
288                     else quality=w*(*fi).Q();
289                     if(quality)
290                             res |= B.ScanFace((*fi).V(0)->P(),(*fi).V(1)->P(),(*fi).V(2)->P(),quality,(*fi).N());
291                 }
292             printf(" : %li\n",clock()-tt0);
293 
294     } else
295     {	// Splat approach add only the vertices to the volume
296         printf("Vertex Splatting\n");
297         for(SVertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
298                 {
299                     if(p.PLYFileQualityFlag==false) quality=1.0;
300                     else quality=w*(*vi).Q();
301                     if(quality)
302                         res |= B.SplatVert((*vi).P(),quality,(*vi).N(),(*vi).C());
303                 }
304     }
305     if(!res) return false;
306 
307     int vstp=0;
308     if(p.VerboseLevel>0) {
309       B.SlicedPPM(shortname.c_str(),std::string(SFormat("%02i",vstp)).c_str(),p.SliceNum	);
310       B.SlicedPPMQ(shortname.c_str(),std::string(SFormat("%02i",vstp)).c_str(),p.SliceNum	);
311       vstp++;
312     }
313     for(int i=0;i<p.WideNum;++i) {
314     B.Expand(math::ToRad(p.ExpAngleDeg));
315         if(p.VerboseLevel>1) B.SlicedPPM(shortname.c_str(),SFormat("%02ie",vstp++),p.SliceNum	);
316         B.Refill(p.FillThr);
317         if(p.VerboseLevel>1) B.SlicedPPM(shortname.c_str(),SFormat("%02if",vstp++),p.SliceNum	);
318         if(p.IntraSmoothFlag)
319         {
320             Volume <Voxelf> SM;
321             SM.Init(VV);
322             SM.CopySmooth(B,1,p.QualitySmoothAbs);
323             B=SM;
324             if(p.VerboseLevel>1) B.SlicedPPM(shortname.c_str(),SFormat("%02is",vstp++),p.SliceNum	);
325 //			if(VerboseLevel>1) B.SlicedPPMQ(shortname,SFormat("%02is",vstp),SliceNum	);
326         }
327     }
328     if(p.SmoothNum>0)
329         {
330             Volume <Voxelf> SM;
331             SM.Init(VV);
332             SM.CopySmooth(B,1,p.QualitySmoothAbs);
333             B=SM;
334             if(p.VerboseLevel>1) B.SlicedPPM(shortname.c_str(),SFormat("%02isf",vstp++),p.SliceNum	);
335         }
336     VV.Merge(B);
337     if(p.VerboseLevel>0) VV.SlicedPPMQ(std::string("merge_").c_str(),shortname.c_str(),p.SliceNum	);
338     return true;
339 }
340 
341 bool Process(vcg::CallBackPos *cb=0)
342 {
343   sprintf(errorMessage,"");
344   printf("bbox scanning...\n"); fflush(stdout);
345   Matrix44f Id; Id.SetIdentity();
346   MP.InitBBox();
347   printf("Completed BBox Scanning                   \n");
348   Box3f fullb = MP.fullBB();
349   assert (!fullb.IsNull());
350   assert (!fullb.IsEmpty());
351   // Calcolo gridsize
352   Point3f voxdim;
353   fullb.Offset(fullb.Diag() * 0.1 );
354 
355   int saveMask=0;
356   saveMask|=tri::io::Mask::IOM_VERTQUALITY;
357   if(p.MergeColor) saveMask |= tri::io::Mask::IOM_VERTCOLOR ;
358 
359   voxdim = fullb.max - fullb.min;
360 
361   // if kcell==0 the number of cells is computed starting from required voxel size;
362   __int64 cells;
363   if(p.NCell>0) cells = (__int64)(p.NCell)*(__int64)(1000);
364   else cells = (__int64)(voxdim[0]/p.VoxSize) * (__int64)(voxdim[1]/p.VoxSize) *(__int64)(voxdim[2]/p.VoxSize) ;
365 
366   {
367     Volume<Voxelf> B; // local to this small block
368 
369     Box3f fullbf; fullbf.Import(fullb);
370     B.Init(cells,fullbf,p.IDiv,p.IPosS);
371     B.Dump(stdout);
372     if(p.WideSize>0) p.WideNum=p.WideSize/B.voxel.Norm();
373 
374     // Now the volume has been determined; the quality threshold in absolute units can be computed
375     if(p.QualitySmoothAbs==0)
376       p.QualitySmoothAbs= p.QualitySmoothVox * B.voxel.Norm();
377   }
378 
379 
380   int TotAdd=0,TotMC=0,TotSav=0; // partial timings counter
381 
382   for(p.IPos[0]=p.IPosS[0];p.IPos[0]<=p.IPosE[0];++p.IPos[0])
383     for(p.IPos[1]=p.IPosS[1];p.IPos[1]<=p.IPosE[1];++p.IPos[1])
384       for(p.IPos[2]=p.IPosS[2];p.IPos[2]<=p.IPosE[2];++p.IPos[2])
385         if((p.IPos[2]+(p.IPos[1]*p.IDiv[2])+(p.IPos[0]*p.IDiv[2]*p.IDiv[1])) >=
386            (p.IPosB[2]+(p.IPosB[1]*p.IDiv[2])+(p.IPosB[0]*p.IDiv[2]*p.IDiv[1]))) // skip until IPos >= IPosB
387         {
388           printf("----------- SubBlock %2i %2i %2i ----------\n",p.IPos[0],p.IPos[1],p.IPos[2]);
389           //Volume<Voxelf> B;
390           int t0=clock();
391 
392           Box3f fullbf; fullbf.Import(fullb);
393 
394           VV.Init(cells,fullbf,p.IDiv,p.IPos);
395           printf("\n\n --------------- Allocated subcells. %i\n",VV.Allocated());
396 
397           std::string filename=p.basename;
398           if(p.IDiv!=Point3i(1,1,1))
399           {
400             std::string subvoltag;
401             VV.GetSubVolumeTag(subvoltag);
402             filename+=subvoltag;
403           }
404           /********** Grande loop di scansione di tutte le mesh *********/
405           bool res=false;
406           if(!cb) printf("Step 1: Converting meshes into volume\n");
407           for(int i=0;i<MP.size();++i)
408           {
409             Box3f bbb= MP.bb(i);
410             /**********************/
411             if(cb) cb((i+1)/MP.size(),"Step 1: Converting meshes into volume");
412             /**********************/
413             // if bbox of mesh #i is part of the subblock, then process it
414             if(bbb.Collide(VV.SubBoxSafe))
415             {
416               SMesh *sm;
417               if(!MP.Find(i,sm) )
418               {
419                 res = InitMesh(*sm,MP.MeshName(i).c_str(),MP.Tr(i));
420                 if(!res)
421                 {
422                   sprintf(errorMessage,"%sFailed Init of mesh %s\n",errorMessage,MP.MeshName(i).c_str());
423                   return false ;
424                 }
425               }
426               res |= AddMeshToVolumeM(*sm, MP.MeshName(i),MP.W(i));
427             }
428           }
429 
430           //B.Normalize(1);
431           printf("End Scanning\n");
432           if(p.OffsetFlag)
433           {
434             VV.Offset(p.OffsetThr);
435             if (p.VerboseLevel>0)
436             {
437               VV.SlicedPPM("finaloff","__",p.SliceNum);
438               VV.SlicedPPMQ("finaloff","__",p.SliceNum);
439             }
440           }
441           //if(p.VerboseLevel>1) VV.SlicedPPM(filename.c_str(),SFormat("_%02im",i),p.SliceNum	);
442 
443           for(int i=0;i<p.RefillNum;++i)
444           {
445             VV.Refill(3,6);
446             if(p.VerboseLevel>1) VV.SlicedPPM(filename.c_str(),SFormat("_%02imsr",i),p.SliceNum	);
447             //if(VerboseLevel>1) VV.SlicedPPMQ(filename,SFormat("_%02ips",i++),SliceNum	);
448           }
449 
450           for(int i=0;i<p.SmoothNum;++i)
451           {
452             Volume <Voxelf> SM;
453             SM.Init(VV);
454             printf("%2i/%2i: ",i,p.SmoothNum);
455             SM.CopySmooth(VV,1,p.QualitySmoothAbs);
456             VV=SM;
457             VV.Refill(3,6);
458             if(p.VerboseLevel>1) VV.SlicedPPM(filename.c_str(),SFormat("_%02ims",i),p.SliceNum	);
459           }
460 
461           int t1=clock();  //--------
462           TotAdd+=t1-t0;
463           printf("Extracting surface...\r");
464           if (p.VerboseLevel>0)
465           {
466             VV.SlicedPPM("final","__",p.SliceNum);
467             VV.SlicedPPMQ("final","__",p.SliceNum);
468           }
469           MCMesh me;
470           if(res)
471           {
472             typedef vcg::tri::TrivialWalker<MCMesh, Volume <Voxelf> >	  Walker;
473             typedef vcg::tri::MarchingCubes<MCMesh, Walker>             MarchingCubes;
474 
475             Walker walker;
476             MarchingCubes	mc(me, walker);
477             /**********************/
478             if(cb) cb(50,"Step 2: Marching Cube...");
479             else printf("Step 2: Marching Cube...\n");
480             /**********************/
481             walker.SetExtractionBox(VV.SubPartSafe);
482             walker.BuildMesh(me,VV,mc,0);
483 
484             typename MCMesh::VertexIterator vi;
485             Box3f bbb; bbb.Import(VV.SubPart);
486             for(vi=me.vert.begin();vi!=me.vert.end();++vi)
487             {
488               if(!bbb.IsIn((*vi).P()))
489                 vcg::tri::Allocator< MCMesh >::DeleteVertex(me,*vi);
490               VV.DeInterize((*vi).P());
491             }
492             for (typename MCMesh::FaceIterator fi = me.face.begin(); fi != me.face.end(); ++fi)
493             {
494               if((*fi).V(0)->IsD() || (*fi).V(1)->IsD() || (*fi).V(2)->IsD() )
495                 vcg::tri::Allocator< MCMesh >::DeleteFace(me,*fi);
496               else std::swap((*fi).V1(0), (*fi).V2(0));
497             }
498 
499             int t2=clock();  //--------
500             TotMC+=t2-t1;
501             if(me.vn >0 || me.fn >0)
502             {
503               p.OutNameVec.push_back(filename+std::string(".ply"));
504               tri::io::ExporterPLY<MCMesh>::Save(me,p.OutNameVec.back().c_str(),saveMask);
505               if(p.SimplificationFlag)
506               {
507                 /**********************/
508                 if(cb) cb(50,"Step 3: Simplify mesh...");
509                 else printf("Step 3: Simplify mesh...\n");
510                 /**********************/
511                 p.OutNameSimpVec.push_back(filename+std::string(".d.ply"));
512                 me.face.EnableVFAdjacency();
513                 MCSimplify<MCMesh>(me, VV.voxel[0]/4.0);
514                 tri::Allocator<MCMesh>::CompactFaceVector(me);
515                 me.face.EnableFFAdjacency();
516                 tri::Clean<MCMesh>::RemoveTVertexByFlip(me,20,true);
517                 tri::Clean<MCMesh>::RemoveFaceFoldByFlip(me);
518                 tri::io::ExporterPLY<MCMesh>::Save(me,p.OutNameSimpVec.back().c_str(),saveMask);
519               }
520             }
521             int t3=clock();  //--------
522             TotSav+=t3-t2;
523 
524           }
525 
526           printf("Mesh Saved '%s':  %8d vertices, %8d faces                   \n",(filename+std::string(".ply")).c_str(),me.vn,me.fn);
527           printf("Adding Meshes %8i\n",TotAdd);
528           printf("MC            %8i\n",TotMC);
529           printf("Saving        %8i\n",TotSav);
530           printf("Total         %8i\n",TotAdd+TotMC+TotSav);
531         }
532         else
533         {
534           printf("----------- skipping SubBlock %2i %2i %2i ----------\n",p.IPos[0],p.IPos[1],p.IPos[2]);
535         }
536   return true;
537 }
538 
539 
540 }; //end PlyMC class
541 
542 
543 template < class MeshType, class VertexPair>
544          class PlyMCTriEdgeCollapse: public MCTriEdgeCollapse< MeshType, VertexPair, PlyMCTriEdgeCollapse<MeshType,VertexPair> > {
545                         public:
546                         typedef  MCTriEdgeCollapse< MeshType, VertexPair, PlyMCTriEdgeCollapse  > MCTEC;
PlyMCTriEdgeCollapse(const VertexPair & p,int i,BaseParameterClass * pp)547             inline PlyMCTriEdgeCollapse(  const VertexPair &p, int i, BaseParameterClass *pp) :MCTEC(p,i,pp){}
548  };
549 
550 
551 
552 template<   class MeshType>
MCSimplify(MeshType & m,float absoluteError,bool preserveBB,vcg::CallBackPos * cb)553 void MCSimplify( MeshType &m, float absoluteError, bool preserveBB, vcg::CallBackPos *cb)
554 {
555 
556   typedef PlyMCTriEdgeCollapse<MeshType,BasicVertexPair<typename MeshType::VertexType> > MyColl;
557   typedef typename MeshType::FaceIterator FaceIterator;
558   typedef typename MeshType::CoordType CoordType;
559 
560 
561     tri::UpdateBounding<MeshType>::Box(m);
562     tri::UpdateTopology<MeshType>::VertexFace(m);
563     TriEdgeCollapseMCParameter pp;
564     pp.bb.Import(m.bbox);
565     pp.preserveBBox=preserveBB;
566     vcg::LocalOptimization<MeshType> DeciSession(m,&pp);
567     if(absoluteError==0)
568     {
569       // guess the mc side.
570       // In a MC mesh the vertices are on the egdes of the cells. and the edges are (mostly) on face of the cells.
571       // If you have  2 vert over the same face xy they share z
572 
573       std::vector<float> ZSet;
574       for(FaceIterator fi = m.face.begin();fi!=m.face.end();++fi)
575         if(!(*fi).IsD())
576         {
577          CoordType v0=(*fi).V(0)->P();
578          CoordType v1=(*fi).V(1)->P();
579          CoordType v2=(*fi).V(2)->P();
580          if(v0[2]==v1[2] && v0[1]!=v1[1] && v0[0]!=v1[0]) ZSet.push_back(v0[2]);
581          if(v0[2]==v2[2] && v0[1]!=v1[1] && v2[0]!=v2[0]) ZSet.push_back(v0[2]);
582          if(v1[2]==v2[2] && v1[1]!=v1[1] && v2[0]!=v2[0]) ZSet.push_back(v0[2]);
583          if(ZSet.size()>100) break;
584        }
585       std::sort(ZSet.begin(),ZSet.end());
586       std::vector<float>::iterator lastV = std::unique(ZSet.begin(),ZSet.end());
587       ZSet.resize(lastV-ZSet.begin());
588       float Delta=0;
589       for(size_t i = 0; i< ZSet.size()-1;++i)
590       {
591         Delta = std::max(ZSet[i+1]-ZSet[i],Delta);
592 //        qDebug("%f",Delta);
593       }
594       absoluteError= Delta/4.0f;
595     }
596 //    qDebug("Simplifying at absoluteError=%f",absoluteError);
597 
598     float TargetError = absoluteError;
599     char buf[1024];
600     DeciSession.template Init< MyColl > ();
601 
602     pp.areaThr=TargetError*TargetError;
603     DeciSession.SetTimeBudget(1.0f);
604     if(TargetError < std::numeric_limits<float>::max() ) DeciSession.SetTargetMetric(TargetError);
605     while(DeciSession.DoOptimization() && DeciSession.currMetric < TargetError)
606       {
607         sprintf(buf,"Simplyfing %7i err %9g \r",m.fn,DeciSession.currMetric);
608         if (cb) cb(int(100.0f*DeciSession.currMetric/TargetError),buf);
609       }
610 }
611 
612 
613 } // end namespace tri
614 } // end namespace vcg
615 
616 #endif
617