1 /****************************************************************************
2 * MeshLab                                                           o o     *
3 * A versatile mesh processing toolbox                             o     o   *
4 *                                                                _   O  _   *
5 * Copyright(C) 2005                                                \/)\/    *
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   History
25 $Log: samplefilter.cpp,v $
26 Revision 1.3  2006/11/29 00:59:20  cignoni
27 Cleaned plugins interface; changed useless help class into a plain string
28 
29 Revision 1.2  2006/11/27 06:57:21  cignoni
30 Wrong way of using the __DATE__ preprocessor symbol
31 
32 Revision 1.1  2006/09/25 09:24:39  e_cerisoli
33 add samplefilter
34 
35 ****************************************************************************/
36 
37 #include <math.h>
38 #include <stdlib.h>
39 #include <time.h>
40 
41 #include <common/interfaces.h>
42 #include <vcg/complex/algorithms/create/platonic.h>
43 
44 #include "filter_poisson.h"
45 #include "src/Geometry.h"
46 #include "src/PoissonParam.h"
47 
48 using namespace std;
49 using namespace vcg;
50 
51 int Execute2(PoissonParam &Par, vector<Point3D<float> > Pts, vector<Point3D<float> > Nor, 	CoredVectorMeshData &mesh, Point3D<float> &newCenter, float &newScale, vcg::CallBackPos *cb );
52 
53 
54 // Constructor usually performs only two simple tasks of filling the two lists
55 //  - typeList: with all the possible id of the filtering actions
56 //  - actionList with the corresponding actions. If you want to add icons to your filtering actions you can do here by construction the QActions accordingly
57 
PoissonPlugin()58 PoissonPlugin::PoissonPlugin()
59 {
60     typeList << FP_POISSON_RECON;
61 
62   foreach(FilterIDType tt , types())
63       actionList << new QAction(filterName(tt), this);
64 }
65 
66 // ST() must return the very short string describing each filtering action
67 // (this string is used also to define the menu entry)
filterName(FilterIDType filterId) const68  QString PoissonPlugin::filterName(FilterIDType filterId) const
69 {
70   switch(filterId) {
71   case FP_POISSON_RECON :  return QString("Surface Reconstruction: Poisson");
72         default : assert(0);
73     }
74     return QString("Error: Unknown Filter");
75 }
76 
77 // Info() must return the longer string describing each filtering action
78 // (this string is used in the About plugin dialog)
filterInfo(FilterIDType filterId) const79  QString PoissonPlugin::filterInfo(FilterIDType filterId) const
80 {
81   switch(filterId) {
82         case FP_POISSON_RECON :  return QString("Use the points and normal to build a surface using the Poisson Surface reconstruction approach.");
83         default : assert(0);
84     }
85     return QString("Error: Unknown Filter");
86 }
87 
88 // This function define the needed parameters for each filter. Return true if the filter has some parameters
89 // it is called every time, so you can set the default value of parameters according to the mesh
90 // For each parmeter you need to define,
91 // - the name of the parameter,
92 // - the string shown in the dialog
93 // - the default value
94 // - a possibly long string describing the meaning of that parameter (shown as a popup help in the dialog)
initParameterSet(QAction * action,MeshModel & m,RichParameterSet & parlst)95 void PoissonPlugin::initParameterSet(QAction *action,MeshModel &m, RichParameterSet & parlst)
96 //void PoissonPlugin::initParList(QAction *action, MeshModel &m, RichParameterSet &parlst)
97 {
98      switch(ID(action))	 {
99         case FP_POISSON_RECON :
100           //parlst.addParam(new RichBool ("RecomputeNormals",
101             //								false,
102             //								"Recompute normals",
103             //								"Do not use the current normals, but recompute them from scratch considering the vertices of the mesh as an unstructured point cloud.");
104           //parlst.addParam(new RichBool ("UseConfidence",
105             //								true,
106             //								"Use Quality",
107             //								"Use the per vertex quality as a confidence value\n");
108             parlst.addParam(new RichInt ("OctDepth",
109                                             6,
110                                             "Octree Depth",
111                                             "Set the depth of the Octree used for extracting the final surface. Suggested range 5..10. Higher numbers mean higher precision in the reconstruction but also higher processing times. Be patient.\n"));
112             parlst.addParam(new RichInt ("SolverDivide",
113                                             6,
114                                             "Solver Divide",
115                                             "This integer argument specifies the depth at which a block Gauss-Seidel solver is used to solve the Laplacian equation.\n"
116                                             "Using this parameter helps reduce the memory overhead at the cost of a small increase in reconstruction time. \n"
117                                             "In practice, the authors have found that for reconstructions of depth 9 or higher a subdivide depth of 7 or 8 can reduce the memory usage.\n"
118                                             "The default value is 8.\n"));
119             parlst.addParam(new RichFloat ("SamplesPerNode",
120                                             1.0,
121                                             "Samples per Node",
122                                             "This floating point value specifies the minimum number of sample points that should fall within an octree node as the octree\n"
123                                             "construction is adapted to sampling density. For noise-free samples, small values in the range [1.0 - 5.0] can be used.\n"
124                                             "For more noisy samples, larger values in the range [15.0 - 20.0] may be needed to provide a smoother, noise-reduced, reconstruction.\n"
125                                             "The default value is 1.0."));
126             parlst.addParam(new RichFloat ("Offset",
127                                              1.0,
128                                              "Surface offsetting",
129                                              "This floating point value specifies a correction value for the isosurface threshold that is chosen.\n"
130                                              "Values < 1 means internal offsetting, >1 external offsetting."
131                                              "Good values are in the range 0.5 .. 2.\n"
132                                              "The default value is 1.0 (no offsetting)."));
133 
134             break;
135    default: break; // do not add any parameter for the other filters
136   }
137 }
138 
139 // The Real Core Function doing the actual mesh processing.
140 // Move Vertex of a random quantity
applyFilter(QAction * filter,MeshDocument & md,RichParameterSet & par,vcg::CallBackPos * cb)141 bool PoissonPlugin::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos *cb)
142 {
143     MeshModel &m=*md.mm();
144   MeshModel &pm =*md.addNewMesh("","Poisson mesh");
145   vector<Point3D<float> > Pts(m.cm.vn);
146     vector<Point3D<float> > Nor(m.cm.vn);
147     CoredVectorMeshData mesh;
148 
149     if (m.hasDataMask(MeshModel::MM_WEDGTEXCOORD)){
150         m.clearDataMask(MeshModel::MM_WEDGTEXCOORD);
151     }
152     if (m.hasDataMask(MeshModel::MM_VERTTEXCOORD)){
153         m.clearDataMask(MeshModel::MM_VERTTEXCOORD);
154     }
155 
156     //Useless control on the normals. It can just avoid crashes derived from an importer setting up to [0.0f,0.0f,0.0f] the normal vectors of a mesh without per-vertex normal attribute.
157     int zeronrm = 0;
158     for(CMeshO::VertexIterator vi=m.cm.vert.begin(); vi!=m.cm.vert.end(); ++vi)
159     {
160         if(!(*vi).IsD())
161         {
162             if ((*vi).N() == Point3m(0.0f,0.0f,0.0f))
163                 ++zeronrm;
164         }
165     }
166 
167     if (zeronrm == m.cm.vn)
168     {
169         Log(GLLogStream::SYSTEM,"All the normal vectors are set to [0.0,0.0,0.0]. Poisson reconstruction filter requires a set of valid per-vertex normal. Filter will be aborted.");
170         this->errorMessage="All the normal vectors are set to [0.0,0.0,0.0]. Poisson reconstruction filter requires a set of valid per-vertex normal. Filter will be aborted.";
171         return false;
172     }
173 
174     int cnt=0;
175     for(CMeshO::VertexIterator vi=m.cm.vert.begin(); vi!=m.cm.vert.end(); ++vi)
176     if(!(*vi).IsD()){
177             (*vi).N().Normalize();
178             for(int ii=0;ii<3;++ii){
179                     Pts[cnt].coords[ii]=(*vi).P()[ii];
180                     Nor[cnt].coords[ii]=(*vi).N()[ii];
181             }
182             ++cnt;
183         }
184     assert(cnt==m.cm.vn);
185     // Log function dump textual info in the lower part of the MeshLab screen.
186     PoissonParam pp;
187     pp.Depth=par.getInt("OctDepth");
188     pp.SamplesPerNode = par.getFloat("SamplesPerNode");
189     pp.SolverDivide=par.getInt("SolverDivide");
190     pp.Offset = par.getFloat("Offset");
191     Point3D<float> center;
192     float scale;
193 
194     int ret= Execute2(pp, Pts, Nor, mesh,center,scale,cb);
195     mesh.resetIterator();
196     int vm = mesh.outOfCorePointCount()+mesh.inCorePoints.size();
197     int fm = mesh.triangleCount();
198 
199     Log("Successfully created a mesh of %i vert and %i faces",vm,fm);
200 
201     //m.cm.Clear();
202 
203     tri::Allocator<CMeshO>::AddVertices(pm.cm,vm);
204     tri::Allocator<CMeshO>::AddFaces(pm.cm,fm);
205 
206   Point3D<float> p;
207     int i;
208     for (i=0; i < int(mesh.inCorePoints.size()); i++){
209         p=mesh.inCorePoints[i];
210         pm.cm.vert[i].P()[0] = p.coords[0]*scale+center.coords[0];
211         pm.cm.vert[i].P()[1] = p.coords[1]*scale+center.coords[1];
212         pm.cm.vert[i].P()[2] = p.coords[2]*scale+center.coords[2];
213         }
214     for (int ii=0; ii < mesh.outOfCorePointCount(); ii++){
215         mesh.nextOutOfCorePoint(p);
216         pm.cm.vert[ii+i].P()[0] = p.coords[0]*scale+center.coords[0];
217         pm.cm.vert[ii+i].P()[1] = p.coords[1]*scale+center.coords[1];
218         pm.cm.vert[ii+i].P()[2] = p.coords[2]*scale+center.coords[2];
219     }
220 
221 TriangleIndex tIndex;
222 int inCoreFlag;
223 int nr_faces=mesh.triangleCount();
224 for (i=0; i < nr_faces; i++){
225         //
226         // create and fill a struct that the ply code can handle
227         //
228         mesh.nextTriangle(tIndex,inCoreFlag);
229         if(!(inCoreFlag & CoredMeshData::IN_CORE_FLAG[0])){tIndex.idx[0]+=int(mesh.inCorePoints.size());}
230         if(!(inCoreFlag & CoredMeshData::IN_CORE_FLAG[1])){tIndex.idx[1]+=int(mesh.inCorePoints.size());}
231         if(!(inCoreFlag & CoredMeshData::IN_CORE_FLAG[2])){tIndex.idx[2]+=int(mesh.inCorePoints.size());}
232         for(int j=0; j < 3; j++)
233         {
234             pm.cm.face[i].V(j) = &pm.cm.vert[tIndex.idx[j]];
235         }
236         //ply_put_element(ply, (void *) &ply_face);
237         //delete[] ply_face.vertices;
238     }  // for, write faces
239 
240 
241 //	for(int i=0;i<mesh.inCorePoints.size();++i){
242 //		mesh.triangles[i].idx[0]+=mesh.inCorePoints.size();
243 //		mesh.triangles[i].idx[1]+=mesh.inCorePoints.size();
244 //		mesh.triangles[i].idx[2]+=mesh.inCorePoints.size();
245 //		}
246 //	Build(m.cm,mesh.inCorePoints,mesh.triangles);
247     Log("Successfully created a mesh of %i faces",pm.cm.vn);
248 
249     pm.UpdateBoxAndNormals();
250     return true;
251 }
getClass(QAction * action)252  PoissonPlugin::FilterClass PoissonPlugin::getClass(QAction *action)
253 {
254   switch(ID(action))
255   {
256     case FP_POISSON_RECON :
257             return FilterClass (MeshFilterInterface::PointSet + MeshFilterInterface::Remeshing) ;
258     default: assert(0);
259   }
260   return FilterClass(0);
261 }
262 
263 
264 MESHLAB_PLUGIN_NAME_EXPORTER(PoissonPlugin)
265