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