1 /*
2 Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
7 
8 Redistributions of source code must retain the above copyright notice, this list of
9 conditions and the following disclaimer. Redistributions in binary form must reproduce
10 the above copyright notice, this list of conditions and the following disclaimer
11 in the documentation and/or other materials provided with the distribution.
12 
13 Neither the name of the Johns Hopkins University nor the names of its contributors
14 may be used to endorse or promote products derived from this software without specific
15 prior written permission.
16 
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
18 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES
19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
20 SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
22 TO, PROCUREMENT OF SUBSTITUTE  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26 DAMAGE.
27 */
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <math.h>
32 #include <float.h>
33 #include <cmath>
34 #ifdef _WIN32
35 #include <Windows.h>
36 #include <Psapi.h>
37 #endif // _WIN32
38 #include "MyTime.h"
39 #include "MarchingCubes.h"
40 #include "Octree.h"
41 #include "SparseMatrix.h"
42 #include "CmdLineParser.h"
43 #include "PPolynomial.h"
44 #include "Ply.h"
45 #include "MemoryUsage.h"
46 #ifdef _OPENMP
47 #include "omp.h"
48 #endif // _OPENMP
49 
50 namespace {
51 
52 void DumpOutput( const char* format , ... );
53 void DumpOutput2( std::vector< char* >& comments , const char* format , ... );
54 #include "MultiGridOctreeData.h"
55 
56 #define DEFAULT_FULL_DEPTH 5
57 
58 #define XSTR(x) STR(x)
59 #define STR(x) #x
60 #if DEFAULT_FULL_DEPTH
61 // #pragma message ( "[WARNING] Setting default full depth to " XSTR(DEFAULT_FULL_DEPTH) )
62 #endif // DEFAULT_FULL_DEPTH
63 
64 #include <stdarg.h>
65 char* outputFile=NULL;
66 int echoStdout=0;
DumpOutput(const char * format,...)67 void DumpOutput( const char* format , ... )
68 {
69 	if( outputFile )
70 	{
71 		FILE* fp = fopen( outputFile , "a" );
72 		va_list args;
73 		va_start( args , format );
74 		vfprintf( fp , format , args );
75 		fclose( fp );
76 		va_end( args );
77 	}
78 	if( echoStdout )
79 	{
80 		va_list args;
81 		va_start( args , format );
82 		vprintf( format , args );
83 		va_end( args );
84 	}
85 }
DumpOutput2(std::vector<char * > & comments,const char * format,...)86 void DumpOutput2( std::vector< char* >& comments  , const char* format , ... )
87 {
88 	if( outputFile )
89 	{
90 		FILE* fp = fopen( outputFile , "a" );
91 		va_list args;
92 		va_start( args , format );
93 		vfprintf( fp , format , args );
94 		fclose( fp );
95 		va_end( args );
96 	}
97 	if( echoStdout )
98 	{
99 		va_list args;
100 		va_start( args , format );
101 		vprintf( format , args );
102 		va_end( args );
103 	}
104 	comments.push_back( new char[1024] );
105 	char* str = comments.back();
106 	va_list args;
107 	va_start( args , format );
108 	vsprintf( str , format , args );
109 	va_end( args );
110 	if( str[strlen(str)-1]=='\n' ) str[strlen(str)-1] = 0;
111 }
112 
113 
114 cmdLineString
115 	In( "in" ) ,
116 	Out( "out" ) ,
117 	VoxelGrid( "voxel" ) ,
118 	XForm( "xForm" );
119 
120 cmdLineReadable
121 #ifdef _WIN32
122 	Performance( "performance" ) ,
123 #endif // _WIN32
124 	Complete( "complete" ) ,
125 	ShowResidual( "showResidual" ) ,
126 	NoComments( "noComments" ) ,
127 	PolygonMesh( "polygonMesh" ) ,
128 	Confidence( "confidence" ) ,
129 	NormalWeights( "nWeights" ) ,
130 	NonManifold( "nonManifold" ) ,
131 	Dirichlet( "dirichlet" ) ,
132 	ASCII( "ascii" ) ,
133 	Density( "density" ) ,
134 	LinearFit( "linearFit" ) ,
135 	PrimalVoxel( "primalVoxel" ) ,
136 	Verbose( "verbose" ) ,
137 	Double( "double" );
138 
139 cmdLineInt
140 	Degree( "degree" , 2 ) ,
141 	Depth( "depth" , 8 ) ,
142 	CGDepth( "cgDepth" , 0 ) ,
143 	KernelDepth( "kernelDepth" ) ,
144 	AdaptiveExponent( "adaptiveExp" , 1 ) ,
145 	Iters( "iters" , 8 ) ,
146 	VoxelDepth( "voxelDepth" , -1 ) ,
147 	FullDepth( "fullDepth" , DEFAULT_FULL_DEPTH ) ,
148 	MinDepth( "minDepth" , 0 ) ,
149 	MaxSolveDepth( "maxSolveDepth" ) ,
150 	Threads( "threads" , omp_get_num_procs() );
151 
152 cmdLineFloat
153 	Color( "color" , 16.f ) ,
154 	SamplesPerNode( "samplesPerNode" , 1.5f ) ,
155 	Scale( "scale" , 1.1f ) ,
156 	CSSolverAccuracy( "cgAccuracy" , float(1e-3) ) ,
157 	PointWeight( "pointWeight" , 4.f );
158 
159 
160 cmdLineReadable* params[] =
161 {
162 	&In , &Degree , &Depth , &Out , &XForm ,
163 	&Scale , &Verbose , &CSSolverAccuracy , &NoComments , &Double ,
164 	&KernelDepth , &SamplesPerNode , &Confidence , &NormalWeights , &NonManifold , &PolygonMesh , &ASCII , &ShowResidual , &VoxelDepth ,
165 	&PointWeight , &VoxelGrid , &Threads , &MaxSolveDepth ,
166 	&AdaptiveExponent , &Dirichlet ,
167 	&Density ,
168 	&FullDepth ,
169 	&MinDepth ,
170 	&CGDepth , &Iters ,
171 	&Complete ,
172 	&Color ,
173 	&LinearFit ,
174 	&PrimalVoxel ,
175 #ifdef _WIN32
176 	&Performance ,
177 #endif // _WIN32
178 };
179 
180 
ShowUsage(char * ex)181 void ShowUsage(char* ex)
182 {
183 	printf( "Usage: %s\n" , ex );
184 	printf( "\t --%s  <input points>\n" , In.name );
185 
186 	printf( "\t[--%s <ouput triangle mesh>]\n" , Out.name );
187 	printf( "\t[--%s <ouput voxel grid>]\n" , VoxelGrid.name );
188 
189 	printf( "\t[--%s <b-spline degree>=%d]\n" , Degree.name , Degree.value );
190 
191 	printf( "\t[--%s <maximum reconstruction depth>=%d]\n" , Depth.name , Depth.value );
192 	printf( "\t\t Running at depth d corresponds to solving on a 2^d x 2^d x 2^d\n" );
193 	printf( "\t\t voxel grid.\n" );
194 
195 	printf( "\t[--%s <full depth>=%d]\n" , FullDepth.name , FullDepth.value );
196 	printf( "\t\t This flag specifies the depth up to which the octree should be complete.\n" );
197 
198 	printf( "\t[--%s <depth at which to extract the voxel grid>=<%s>]\n" , VoxelDepth.name , Depth.name );
199 
200 	printf( "\t[--%s <conjugate-gradients depth>=%d]\n" , CGDepth.name , CGDepth.value );
201 	printf( "\t\t The depth up to which a conjugate-gradients solver should be used.\n");
202 
203 	printf( "\t[--%s <scale factor>=%f]\n" , Scale.name , Scale.value );
204 	printf( "\t\t Specifies the factor of the bounding cube that the input\n" );
205 	printf( "\t\t samples should fit into.\n" );
206 
207 	printf( "\t[--%s <minimum number of samples per node>=%f]\n" , SamplesPerNode.name, SamplesPerNode.value );
208 	printf( "\t\t This parameter specifies the minimum number of points that\n" );
209 	printf( "\t\t should fall within an octree node.\n" );
210 
211 	printf( "\t[--%s <interpolation weight>=%f]\n" , PointWeight.name , PointWeight.value );
212 	printf( "\t\t This value specifies the weight that point interpolation constraints are\n" );
213 	printf( "\t\t given when defining the (screened) Poisson system.\n" );
214 
215 	printf( "\t[--%s <iterations>=%d]\n" , Iters.name , Iters.value );
216 	printf( "\t\t This flag specifies the (maximum if CG) number of solver iterations.\n" );
217 
218 	printf( "\t[--%s <pull factor>]\n" , Color.name );
219 	printf( "\t\t This flag specifies the pull factor for color interpolation\n" );
220 
221 #ifdef _OPENMP
222 	printf( "\t[--%s <num threads>=%d]\n" , Threads.name , Threads.value );
223 	printf( "\t\t This parameter specifies the number of threads across which\n" );
224 	printf( "\t\t the solver should be parallelized.\n" );
225 #endif // _OPENMP
226 
227 	printf( "\t[--%s]\n" , Confidence.name );
228 	printf( "\t\t If this flag is enabled, the size of a sample's normals is\n" );
229 	printf( "\t\t used as a confidence value, affecting the sample's\n" );
230 	printf( "\t\t constribution to the reconstruction process.\n" );
231 
232 	printf( "\t[--%s]\n" , NormalWeights.name );
233 	printf( "\t\t If this flag is enabled, the size of a sample's normals is\n" );
234 	printf( "\t\t used as to modulate the interpolation weight.\n" );
235 
236 #if 0
237 	printf( "\t[--%s]\n" , NonManifold.name );
238 	printf( "\t\t If this flag is enabled, the isosurface extraction does not add\n" );
239 	printf( "\t\t a planar polygon's barycenter in order to ensure that the output\n" );
240 	printf( "\t\t mesh is manifold.\n" );
241 #endif
242 
243 	printf( "\t[--%s]\n" , PolygonMesh.name);
244 	printf( "\t\t If this flag is enabled, the isosurface extraction returns polygons\n" );
245 	printf( "\t\t rather than triangles.\n" );
246 
247 #if 0
248 	printf( "\t[--%s <minimum depth>=%d]\n" , MinDepth.name , MinDepth.value );
249 	printf( "\t\t This flag specifies the coarsest depth at which the system is to be solved.\n" );
250 
251 	printf( "\t[--%s <cg solver accuracy>=%g]\n" , CSSolverAccuracy.name , CSSolverAccuracy.value );
252 	printf( "\t\t This flag specifies the accuracy cut-off to be used for CG.\n" );
253 
254 	printf( "\t[--%s <adaptive weighting exponent>=%d]\n", AdaptiveExponent.name , AdaptiveExponent.value );
255 	printf( "\t\t This flag specifies the exponent scale for the adaptive weighting.\n" );
256 
257 #ifdef _WIN32
258 	printf( "\t[--%s]\n" , Performance.name );
259 	printf( "\t\t If this flag is enabled, the running time and peak memory usage\n" );
260 	printf( "\t\t is output after the reconstruction.\n" );
261 #endif // _WIN32
262 #endif
263 	printf( "\t[--%s]\n" , Dirichlet.name);
264 	printf( "\t\t If this flag is enabled, Dirichlet boundary constraints are used for reconstruction.\n" );
265 
266 	printf( "\t[--%s]\n" , Density.name );
267 	printf( "\t\t If this flag is enabled, the sampling density is written out with the vertices.\n" );
268 
269 	printf( "\t[--%s]\n" , LinearFit.name );
270 	printf( "\t\t If this flag is enabled, the iso-surfacing will be performed using linear fitting.\n" );
271 
272 	printf( "\t[--%s]\n" , PrimalVoxel.name );
273 	printf( "\t\t If this flag is enabled, voxel sampling is performed at corners rather than centers.\n" );
274 
275 #if 0
276 	printf( "\t[--%s]\n" , ASCII.name );
277 	printf( "\t\t If this flag is enabled, the output file is written out in ASCII format.\n" );
278 
279 	printf( "\t[--%s]\n" , NoComments.name );
280 	printf( "\t\t If this flag is enabled, the output file will not include comments.\n" );
281 #endif
282 
283 	printf( "\t[--%s]\n" , Double.name );
284 	printf( "\t\t If this flag is enabled, the reconstruction will be performed with double-precision floats.\n" );
285 
286 	printf( "\t[--%s]\n" , Verbose.name );
287 	printf( "\t\t If this flag is enabled, the progress of the reconstructor will be output to STDOUT.\n" );
288 }
289 
ReadASCIIColor(FILE * fp)290 Point3D< unsigned char > ReadASCIIColor( FILE* fp )
291 {
292 	Point3D< unsigned char > c;
293 	if( fscanf( fp , " %c %c %c " , &c[0] , &c[1] , &c[2] )!=3 ) fprintf( stderr , "[ERROR] Failed to read color\n" ) , exit( 0 );
294 	return c;
295 }
296 
297 PlyProperty PlyColorProperties[]=
298 {
299 	{ "r"     , PLY_UCHAR , PLY_UCHAR , int( offsetof( Point3D< unsigned char > , coords[0] ) ) , 0 , 0 , 0 , 0 } ,
300 	{ "g"     , PLY_UCHAR , PLY_UCHAR , int( offsetof( Point3D< unsigned char > , coords[1] ) ) , 0 , 0 , 0 , 0 } ,
301 	{ "b"     , PLY_UCHAR , PLY_UCHAR , int( offsetof( Point3D< unsigned char > , coords[2] ) ) , 0 , 0 , 0 , 0 } ,
302 	{ "red"   , PLY_UCHAR , PLY_UCHAR , int( offsetof( Point3D< unsigned char > , coords[0] ) ) , 0 , 0 , 0 , 0 } ,
303 	{ "green" , PLY_UCHAR , PLY_UCHAR , int( offsetof( Point3D< unsigned char > , coords[1] ) ) , 0 , 0 , 0 , 0 } ,
304 	{ "blue"  , PLY_UCHAR , PLY_UCHAR , int( offsetof( Point3D< unsigned char > , coords[2] ) ) , 0 , 0 , 0 , 0 }
305 };
306 
ValidPlyColorProperties(const bool * props)307 bool ValidPlyColorProperties( const bool* props ){ return ( props[0] || props[3] ) && ( props[1] || props[4] ) && ( props[2] || props[5] ); }
308 
309 template< class Real , int Degree , class Vertex >
_Execute(int argc,char * argv[])310 int _Execute( int argc , char* argv[] )
311 {
312 	Reset< Real >();
313 	int paramNum = sizeof(params)/sizeof(cmdLineReadable*);
314 	std::vector< char* > comments;
315 
316 	if( Verbose.set ) echoStdout=1;
317 
318 	XForm4x4< Real > xForm , iXForm;
319 	if( XForm.set )
320 	{
321 		FILE* fp = fopen( XForm.value , "r" );
322 		if( !fp )
323 		{
324 			fprintf( stderr , "[WARNING] Could not read x-form from: %s\n" , XForm.value );
325 			xForm = XForm4x4< Real >::Identity();
326 		}
327 		else
328 		{
329 			for( int i=0 ; i<4 ; i++ ) for( int j=0 ; j<4 ; j++ )
330 			{
331 				float f;
332 				if( fscanf( fp , " %f " , &f )!=1 ) fprintf( stderr , "[ERROR] Execute: Failed to read xform\n" ) , exit( 0 );
333 				xForm(i,j) = (Real)f;
334 			}
335 			fclose( fp );
336 		}
337 	}
338 	else xForm = XForm4x4< Real >::Identity();
339 	iXForm = xForm.inverse();
340 
341 	DumpOutput2( comments , "Running Screened Poisson Reconstruction (Version 8.0)\n" );
342 	char str[1024];
343 	for( int i=0 ; i<paramNum ; i++ )
344 		if( params[i]->set )
345 		{
346 			params[i]->writeValue( str );
347 			if( strlen( str ) ) DumpOutput2( comments , "\t--%s %s\n" , params[i]->name , str );
348 			else                DumpOutput2( comments , "\t--%s\n" , params[i]->name );
349 		}
350 
351 	double t;
352 	double tt=Time();
353 	Real isoValue = 0;
354 
355 	Octree< Real > tree;
356 	tree.threads = Threads.value;
357 	if( !In.set )
358 	{
359 		ShowUsage( argv[0] );
360 		return 0;
361 	}
362 	if( !MaxSolveDepth.set ) MaxSolveDepth.value = Depth.value;
363 
364 	OctNode< TreeNodeData >::SetAllocator( MEMORY_ALLOCATOR_BLOCK_SIZE );
365 
366 	t=Time();
367 	int kernelDepth = KernelDepth.set ? KernelDepth.value : Depth.value-2;
368 	if( kernelDepth>Depth.value )
369 	{
370 		fprintf( stderr,"[WARNING] %s can't be greater than %s: %d <= %d\n" , KernelDepth.name , Depth.name , KernelDepth.value , Depth.value );
371 		kernelDepth = Depth.value;
372 	}
373 
374 	double maxMemoryUsage;
375 	t=Time() , tree.maxMemoryUsage=0;
376 	SparseNodeData< PointData< Real > , 0 >* pointInfo = new SparseNodeData< PointData< Real > , 0 >();
377 	SparseNodeData< Point3D< Real > , NORMAL_DEGREE >* normalInfo = new SparseNodeData< Point3D< Real > , NORMAL_DEGREE >();
378 	SparseNodeData< Real , WEIGHT_DEGREE >* densityWeights = new SparseNodeData< Real , WEIGHT_DEGREE >();
379 	SparseNodeData< Real , NORMAL_DEGREE >* nodeWeights = new SparseNodeData< Real , NORMAL_DEGREE >();
380 	int pointCount;
381 	typedef typename Octree< Real >::template ProjectiveData< Point3D< Real > > ProjectiveColor;
382 	SparseNodeData< ProjectiveColor , DATA_DEGREE >* colorData = NULL;
383 
384 	char* ext = GetFileExtension( In.value );
385 	if( Color.set && Color.value>0 )
386 	{
387 		colorData = new SparseNodeData< ProjectiveColor , DATA_DEGREE >();
388 		OrientedPointStreamWithData< float , Point3D< unsigned char > >* pointStream;
389 		if     ( !strcasecmp( ext , "bnpts" ) ) pointStream = new BinaryOrientedPointStreamWithData< float , Point3D< unsigned char > >( In.value );
390 		else if( !strcasecmp( ext , "ply"   ) ) pointStream = new    PLYOrientedPointStreamWithData< float , Point3D< unsigned char > >( In.value , PlyColorProperties , 6 , ValidPlyColorProperties );
391 		else                                    pointStream = new  ASCIIOrientedPointStreamWithData< float , Point3D< unsigned char > >( In.value , ReadASCIIColor );
392 		pointCount = tree.template SetTree< float , NORMAL_DEGREE , WEIGHT_DEGREE , DATA_DEGREE , Point3D< unsigned char > >( pointStream , MinDepth.value , Depth.value , FullDepth.value , kernelDepth , Real(SamplesPerNode.value) , Scale.value , Confidence.set , NormalWeights.set , PointWeight.value , AdaptiveExponent.value , *densityWeights , *pointInfo , *normalInfo , *nodeWeights , colorData , xForm , Dirichlet.set , Complete.set );
393 		delete pointStream;
394 
395 		for( const OctNode< TreeNodeData >* n = tree.tree().nextNode() ; n!=NULL ; n=tree.tree().nextNode( n ) )
396 		{
397 			int idx = colorData->index( n );
398 			if( idx>=0 ) colorData->data[idx] *= (Real)pow( Color.value , n->depth() );
399 		}
400 	}
401 	else
402 	{
403 		OrientedPointStream< float >* pointStream;
404 		if     ( !strcasecmp( ext , "bnpts" ) ) pointStream = new BinaryOrientedPointStream< float >( In.value );
405 		else if( !strcasecmp( ext , "ply"   ) ) pointStream = new    PLYOrientedPointStream< float >( In.value );
406 		else                                    pointStream = new  ASCIIOrientedPointStream< float >( In.value );
407 		pointCount = tree.template SetTree< float , NORMAL_DEGREE , WEIGHT_DEGREE , DATA_DEGREE , Point3D< unsigned char > >( pointStream , MinDepth.value , Depth.value , FullDepth.value , kernelDepth , Real(SamplesPerNode.value) , Scale.value , Confidence.set , NormalWeights.set , PointWeight.value , AdaptiveExponent.value , *densityWeights , *pointInfo , *normalInfo , *nodeWeights , colorData , xForm , Dirichlet.set , Complete.set );
408 		delete pointStream;
409 	}
410 	delete[] ext;
411 	if( !Density.set ) delete densityWeights , densityWeights = NULL;
412 	{
413 		std::vector< int > indexMap;
414 		if( NORMAL_DEGREE>Degree ) tree.template EnableMultigrid< NORMAL_DEGREE >( &indexMap );
415 		else                       tree.template EnableMultigrid<        Degree >( &indexMap );
416 		if( pointInfo ) pointInfo->remapIndices( indexMap );
417 		if( normalInfo ) normalInfo->remapIndices( indexMap );
418 		if( densityWeights ) densityWeights->remapIndices( indexMap );
419 		if( nodeWeights ) nodeWeights->remapIndices( indexMap );
420 		if( colorData ) colorData->remapIndices( indexMap );
421 	}
422 
423 	DumpOutput2( comments , "#             Tree set in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage );
424 	DumpOutput( "Input Points: %d\n" , pointCount );
425 	DumpOutput( "Leaves/Nodes: %d/%d\n" , tree.leaves() , tree.nodes() );
426 	DumpOutput( "Memory Usage: %.3f MB\n" , float( MemoryInfo::Usage() )/(1<<20) );
427 
428 	maxMemoryUsage = tree.maxMemoryUsage;
429 	t=Time() , tree.maxMemoryUsage=0;
430 	DenseNodeData< Real , Degree > constraints = tree.template SetLaplacianConstraints< Degree >( *normalInfo );
431 	delete normalInfo;
432 	DumpOutput2( comments , "#      Constraints set in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage );
433 	DumpOutput( "Memory Usage: %.3f MB\n" , float( MemoryInfo::Usage())/(1<<20) );
434 	maxMemoryUsage = std::max< double >( maxMemoryUsage , tree.maxMemoryUsage );
435 
436 	t=Time() , tree.maxMemoryUsage=0;
437 	DenseNodeData< Real , Degree > solution = tree.SolveSystem( *pointInfo , constraints , ShowResidual.set , Iters.value , MaxSolveDepth.value , CGDepth.value , CSSolverAccuracy.value );
438 	delete pointInfo;
439 	constraints.resize( 0 );
440 
441 	DumpOutput2( comments , "# Linear system solved in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage );
442 	DumpOutput( "Memory Usage: %.3f MB\n" , float( MemoryInfo::Usage() )/(1<<20) );
443 	maxMemoryUsage = std::max< double >( maxMemoryUsage , tree.maxMemoryUsage );
444 
445 	CoredFileMeshData< Vertex > mesh;
446 
447 	if( Verbose.set ) tree.maxMemoryUsage=0;
448 	t=Time();
449 	isoValue = tree.GetIsoValue( solution , *nodeWeights );
450 	delete nodeWeights;
451 	DumpOutput( "Got average in: %f\n" , Time()-t );
452 	DumpOutput( "Iso-Value: %e\n" , isoValue );
453 
454 	if( VoxelGrid.set )
455 	{
456 		double t = Time();
457 		FILE* fp = fopen( VoxelGrid.value , "wb" );
458 		if( !fp ) fprintf( stderr , "Failed to open voxel file for writing: %s\n" , VoxelGrid.value );
459 		else
460 		{
461 			int res = 0;
462 			Pointer( Real ) values = tree.Evaluate( solution , res , isoValue , VoxelDepth.value , PrimalVoxel.set );
463 			fwrite( &res , sizeof(int) , 1 , fp );
464 			if( sizeof(Real)==sizeof(float) ) fwrite( values , sizeof(float) , res*res*res , fp );
465 			else
466 			{
467 				float *fValues = new float[res*res*res];
468 				for( int i=0 ; i<res*res*res ; i++ ) fValues[i] = float( values[i] );
469 				fwrite( fValues , sizeof(float) , res*res*res , fp );
470 				delete[] fValues;
471 			}
472 			fclose( fp );
473 			DeletePointer( values );
474 		}
475 		DumpOutput( "Got voxel grid in: %f\n" , Time()-t );
476 	}
477 
478 	if( Out.set )
479 	{
480 		t = Time() , tree.maxMemoryUsage = 0;
481 		tree.template GetMCIsoSurface< Degree , WEIGHT_DEGREE , DATA_DEGREE >( densityWeights , colorData , solution , isoValue , mesh , !LinearFit.set , !NonManifold.set , PolygonMesh.set );
482 		if( PolygonMesh.set ) DumpOutput2( comments , "#         Got polygons in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage );
483 		else                  DumpOutput2( comments , "#        Got triangles in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage );
484 		maxMemoryUsage = std::max< double >( maxMemoryUsage , tree.maxMemoryUsage );
485 		DumpOutput2( comments , "#             Total Solve: %9.1f (s), %9.1f (MB)\n" , Time()-tt , maxMemoryUsage );
486 
487 		if( NoComments.set )
488 		{
489 			if( ASCII.set ) PlyWritePolygons( Out.value , &mesh , PLY_ASCII         , NULL , 0 , iXForm );
490 			else            PlyWritePolygons( Out.value , &mesh , PLY_BINARY_NATIVE , NULL , 0 , iXForm );
491 		}
492 		else
493 		{
494 			if( ASCII.set ) PlyWritePolygons( Out.value , &mesh , PLY_ASCII         , &comments[0] , (int)comments.size() , iXForm );
495 			else            PlyWritePolygons( Out.value , &mesh , PLY_BINARY_NATIVE , &comments[0] , (int)comments.size() , iXForm );
496 		}
497 		DumpOutput( "Vertices / Polygons: %d / %d\n" , mesh.outOfCorePointCount()+mesh.inCorePoints.size() , mesh.polygonCount() );
498 	}
499 	solution.resize( 0 );
500 	if( colorData ){ delete colorData ; colorData = NULL; }
501 	return 1;
502 }
503 
504 #ifdef _WIN32
to_seconds(const FILETIME & ft)505 inline double to_seconds( const FILETIME& ft )
506 {
507 	const double low_to_sec=100e-9; // 100 nanoseconds
508 	const double high_to_sec=low_to_sec*4294967296.0;
509 	return ft.dwLowDateTime*low_to_sec+ft.dwHighDateTime*high_to_sec;
510 }
511 #endif // _WIN32
512 
513 template< class Real , class Vertex >
Execute(int argc,char * argv[])514 int Execute( int argc , char* argv[] )
515 {
516 	switch( Degree.value )
517 	{
518 	case 1: return _Execute< Real , 1 , Vertex >( argc , argv );
519 	case 2: return _Execute< Real , 2 , Vertex >( argc , argv );
520 	case 3: return _Execute< Real , 3 , Vertex >( argc , argv );
521 	case 4: return _Execute< Real , 4 , Vertex >( argc , argv );
522 	default:
523 		fprintf( stderr , "[ERROR] Only B-Splines of degree 1 - 4 are supported" );
524 		return EXIT_FAILURE;
525 	}
526 }
527 
528 }  // namespace
529 
PoissonRecon(int argc,char * argv[])530 int PoissonRecon( int argc , char* argv[] )
531 {
532 #if defined(WIN32) && defined(MAX_MEMORY_GB)
533 	if( MAX_MEMORY_GB>0 )
534 	{
535 		SIZE_T peakMemory = 1;
536 		peakMemory <<= 30;
537 		peakMemory *= MAX_MEMORY_GB;
538 		printf( "Limiting memory usage to %.2f GB\n" , float( peakMemory>>30 ) );
539 		HANDLE h = CreateJobObject( NULL , NULL );
540 		AssignProcessToJobObject( h , GetCurrentProcess() );
541 
542 		JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
543 		jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_JOB_MEMORY;
544 		jeli.JobMemoryLimit = peakMemory;
545 		if( !SetInformationJobObject( h , JobObjectExtendedLimitInformation , &jeli , sizeof( jeli ) ) )
546 			fprintf( stderr , "Failed to set memory limit\n" );
547 	}
548 #endif // defined(WIN32) && defined(MAX_MEMORY_GB)
549 	double t = Time();
550 
551 	cmdLineParse( argc-1 , &argv[1] , sizeof(params)/sizeof(cmdLineReadable*) , params , 1 );
552 	if( Density.set )
553 		if( Color.set )
554 			if( Double.set ) Execute< double , PlyColorAndValueVertex< float > >( argc , argv );
555 			else             Execute< float  , PlyColorAndValueVertex< float > >( argc , argv );
556 		else
557 			if( Double.set ) Execute< double , PlyValueVertex< float > >( argc , argv );
558 			else             Execute< float  , PlyValueVertex< float > >( argc , argv );
559 	else
560 		if( Color.set )
561 			if( Double.set ) Execute< double , PlyColorVertex< float > >( argc , argv );
562 			else             Execute< float  , PlyColorVertex< float > >( argc , argv );
563 		else
564 			if( Double.set ) Execute< double , PlyVertex< float > >( argc , argv );
565 			else             Execute< float  , PlyVertex< float > >( argc , argv );
566 #ifdef _WIN32
567 	if( Performance.set )
568 	{
569 		HANDLE cur_thread=GetCurrentThread();
570 		FILETIME tcreat, texit, tkernel, tuser;
571 		if( GetThreadTimes( cur_thread , &tcreat , &texit , &tkernel , &tuser ) )
572 			printf( "Time (Wall/User/Kernel): %.2f / %.2f / %.2f\n" , Time()-t , to_seconds( tuser ) , to_seconds( tkernel ) );
573 		else printf( "Time: %.2f\n" , Time()-t );
574 		HANDLE h = GetCurrentProcess();
575 		PROCESS_MEMORY_COUNTERS pmc;
576 		if( GetProcessMemoryInfo( h , &pmc , sizeof(pmc) ) ) printf( "Peak Memory (MB): %d\n" , pmc.PeakWorkingSetSize>>20 );
577 	}
578 #endif // _WIN32
579 	return EXIT_SUCCESS;
580 }
581