/* Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Johns Hopkins University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #ifdef _WIN32 #include #include #endif // _WIN32 #include "MyTime.h" #include "MarchingCubes.h" #include "Octree.h" #include "SparseMatrix.h" #include "CmdLineParser.h" #include "PPolynomial.h" #include "Ply.h" #include "MemoryUsage.h" #ifdef _OPENMP #include "omp.h" #endif // _OPENMP namespace { void DumpOutput( const char* format , ... ); void DumpOutput2( std::vector< char* >& comments , const char* format , ... ); #include "MultiGridOctreeData.h" #define DEFAULT_FULL_DEPTH 5 #define XSTR(x) STR(x) #define STR(x) #x #if DEFAULT_FULL_DEPTH // #pragma message ( "[WARNING] Setting default full depth to " XSTR(DEFAULT_FULL_DEPTH) ) #endif // DEFAULT_FULL_DEPTH #include char* outputFile=NULL; int echoStdout=0; void DumpOutput( const char* format , ... ) { if( outputFile ) { FILE* fp = fopen( outputFile , "a" ); va_list args; va_start( args , format ); vfprintf( fp , format , args ); fclose( fp ); va_end( args ); } if( echoStdout ) { va_list args; va_start( args , format ); vprintf( format , args ); va_end( args ); } } void DumpOutput2( std::vector< char* >& comments , const char* format , ... ) { if( outputFile ) { FILE* fp = fopen( outputFile , "a" ); va_list args; va_start( args , format ); vfprintf( fp , format , args ); fclose( fp ); va_end( args ); } if( echoStdout ) { va_list args; va_start( args , format ); vprintf( format , args ); va_end( args ); } comments.push_back( new char[1024] ); char* str = comments.back(); va_list args; va_start( args , format ); vsprintf( str , format , args ); va_end( args ); if( str[strlen(str)-1]=='\n' ) str[strlen(str)-1] = 0; } cmdLineString In( "in" ) , Out( "out" ) , VoxelGrid( "voxel" ) , XForm( "xForm" ); cmdLineReadable #ifdef _WIN32 Performance( "performance" ) , #endif // _WIN32 Complete( "complete" ) , ShowResidual( "showResidual" ) , NoComments( "noComments" ) , PolygonMesh( "polygonMesh" ) , Confidence( "confidence" ) , NormalWeights( "nWeights" ) , NonManifold( "nonManifold" ) , Dirichlet( "dirichlet" ) , ASCII( "ascii" ) , Density( "density" ) , LinearFit( "linearFit" ) , PrimalVoxel( "primalVoxel" ) , Verbose( "verbose" ) , Double( "double" ); cmdLineInt Degree( "degree" , 2 ) , Depth( "depth" , 8 ) , CGDepth( "cgDepth" , 0 ) , KernelDepth( "kernelDepth" ) , AdaptiveExponent( "adaptiveExp" , 1 ) , Iters( "iters" , 8 ) , VoxelDepth( "voxelDepth" , -1 ) , FullDepth( "fullDepth" , DEFAULT_FULL_DEPTH ) , MinDepth( "minDepth" , 0 ) , MaxSolveDepth( "maxSolveDepth" ) , Threads( "threads" , omp_get_num_procs() ); cmdLineFloat Color( "color" , 16.f ) , SamplesPerNode( "samplesPerNode" , 1.5f ) , Scale( "scale" , 1.1f ) , CSSolverAccuracy( "cgAccuracy" , float(1e-3) ) , PointWeight( "pointWeight" , 4.f ); cmdLineReadable* params[] = { &In , &Degree , &Depth , &Out , &XForm , &Scale , &Verbose , &CSSolverAccuracy , &NoComments , &Double , &KernelDepth , &SamplesPerNode , &Confidence , &NormalWeights , &NonManifold , &PolygonMesh , &ASCII , &ShowResidual , &VoxelDepth , &PointWeight , &VoxelGrid , &Threads , &MaxSolveDepth , &AdaptiveExponent , &Dirichlet , &Density , &FullDepth , &MinDepth , &CGDepth , &Iters , &Complete , &Color , &LinearFit , &PrimalVoxel , #ifdef _WIN32 &Performance , #endif // _WIN32 }; void ShowUsage(char* ex) { printf( "Usage: %s\n" , ex ); printf( "\t --%s \n" , In.name ); printf( "\t[--%s ]\n" , Out.name ); printf( "\t[--%s ]\n" , VoxelGrid.name ); printf( "\t[--%s =%d]\n" , Degree.name , Degree.value ); printf( "\t[--%s =%d]\n" , Depth.name , Depth.value ); printf( "\t\t Running at depth d corresponds to solving on a 2^d x 2^d x 2^d\n" ); printf( "\t\t voxel grid.\n" ); printf( "\t[--%s =%d]\n" , FullDepth.name , FullDepth.value ); printf( "\t\t This flag specifies the depth up to which the octree should be complete.\n" ); printf( "\t[--%s =<%s>]\n" , VoxelDepth.name , Depth.name ); printf( "\t[--%s =%d]\n" , CGDepth.name , CGDepth.value ); printf( "\t\t The depth up to which a conjugate-gradients solver should be used.\n"); printf( "\t[--%s =%f]\n" , Scale.name , Scale.value ); printf( "\t\t Specifies the factor of the bounding cube that the input\n" ); printf( "\t\t samples should fit into.\n" ); printf( "\t[--%s =%f]\n" , SamplesPerNode.name, SamplesPerNode.value ); printf( "\t\t This parameter specifies the minimum number of points that\n" ); printf( "\t\t should fall within an octree node.\n" ); printf( "\t[--%s =%f]\n" , PointWeight.name , PointWeight.value ); printf( "\t\t This value specifies the weight that point interpolation constraints are\n" ); printf( "\t\t given when defining the (screened) Poisson system.\n" ); printf( "\t[--%s =%d]\n" , Iters.name , Iters.value ); printf( "\t\t This flag specifies the (maximum if CG) number of solver iterations.\n" ); printf( "\t[--%s ]\n" , Color.name ); printf( "\t\t This flag specifies the pull factor for color interpolation\n" ); #ifdef _OPENMP printf( "\t[--%s =%d]\n" , Threads.name , Threads.value ); printf( "\t\t This parameter specifies the number of threads across which\n" ); printf( "\t\t the solver should be parallelized.\n" ); #endif // _OPENMP printf( "\t[--%s]\n" , Confidence.name ); printf( "\t\t If this flag is enabled, the size of a sample's normals is\n" ); printf( "\t\t used as a confidence value, affecting the sample's\n" ); printf( "\t\t constribution to the reconstruction process.\n" ); printf( "\t[--%s]\n" , NormalWeights.name ); printf( "\t\t If this flag is enabled, the size of a sample's normals is\n" ); printf( "\t\t used as to modulate the interpolation weight.\n" ); #if 0 printf( "\t[--%s]\n" , NonManifold.name ); printf( "\t\t If this flag is enabled, the isosurface extraction does not add\n" ); printf( "\t\t a planar polygon's barycenter in order to ensure that the output\n" ); printf( "\t\t mesh is manifold.\n" ); #endif printf( "\t[--%s]\n" , PolygonMesh.name); printf( "\t\t If this flag is enabled, the isosurface extraction returns polygons\n" ); printf( "\t\t rather than triangles.\n" ); #if 0 printf( "\t[--%s =%d]\n" , MinDepth.name , MinDepth.value ); printf( "\t\t This flag specifies the coarsest depth at which the system is to be solved.\n" ); printf( "\t[--%s =%g]\n" , CSSolverAccuracy.name , CSSolverAccuracy.value ); printf( "\t\t This flag specifies the accuracy cut-off to be used for CG.\n" ); printf( "\t[--%s =%d]\n", AdaptiveExponent.name , AdaptiveExponent.value ); printf( "\t\t This flag specifies the exponent scale for the adaptive weighting.\n" ); #ifdef _WIN32 printf( "\t[--%s]\n" , Performance.name ); printf( "\t\t If this flag is enabled, the running time and peak memory usage\n" ); printf( "\t\t is output after the reconstruction.\n" ); #endif // _WIN32 #endif printf( "\t[--%s]\n" , Dirichlet.name); printf( "\t\t If this flag is enabled, Dirichlet boundary constraints are used for reconstruction.\n" ); printf( "\t[--%s]\n" , Density.name ); printf( "\t\t If this flag is enabled, the sampling density is written out with the vertices.\n" ); printf( "\t[--%s]\n" , LinearFit.name ); printf( "\t\t If this flag is enabled, the iso-surfacing will be performed using linear fitting.\n" ); printf( "\t[--%s]\n" , PrimalVoxel.name ); printf( "\t\t If this flag is enabled, voxel sampling is performed at corners rather than centers.\n" ); #if 0 printf( "\t[--%s]\n" , ASCII.name ); printf( "\t\t If this flag is enabled, the output file is written out in ASCII format.\n" ); printf( "\t[--%s]\n" , NoComments.name ); printf( "\t\t If this flag is enabled, the output file will not include comments.\n" ); #endif printf( "\t[--%s]\n" , Double.name ); printf( "\t\t If this flag is enabled, the reconstruction will be performed with double-precision floats.\n" ); printf( "\t[--%s]\n" , Verbose.name ); printf( "\t\t If this flag is enabled, the progress of the reconstructor will be output to STDOUT.\n" ); } Point3D< unsigned char > ReadASCIIColor( FILE* fp ) { Point3D< unsigned char > c; if( fscanf( fp , " %c %c %c " , &c[0] , &c[1] , &c[2] )!=3 ) fprintf( stderr , "[ERROR] Failed to read color\n" ) , exit( 0 ); return c; } PlyProperty PlyColorProperties[]= { { "r" , PLY_UCHAR , PLY_UCHAR , int( offsetof( Point3D< unsigned char > , coords[0] ) ) , 0 , 0 , 0 , 0 } , { "g" , PLY_UCHAR , PLY_UCHAR , int( offsetof( Point3D< unsigned char > , coords[1] ) ) , 0 , 0 , 0 , 0 } , { "b" , PLY_UCHAR , PLY_UCHAR , int( offsetof( Point3D< unsigned char > , coords[2] ) ) , 0 , 0 , 0 , 0 } , { "red" , PLY_UCHAR , PLY_UCHAR , int( offsetof( Point3D< unsigned char > , coords[0] ) ) , 0 , 0 , 0 , 0 } , { "green" , PLY_UCHAR , PLY_UCHAR , int( offsetof( Point3D< unsigned char > , coords[1] ) ) , 0 , 0 , 0 , 0 } , { "blue" , PLY_UCHAR , PLY_UCHAR , int( offsetof( Point3D< unsigned char > , coords[2] ) ) , 0 , 0 , 0 , 0 } }; bool ValidPlyColorProperties( const bool* props ){ return ( props[0] || props[3] ) && ( props[1] || props[4] ) && ( props[2] || props[5] ); } template< class Real , int Degree , class Vertex > int _Execute( int argc , char* argv[] ) { Reset< Real >(); int paramNum = sizeof(params)/sizeof(cmdLineReadable*); std::vector< char* > comments; if( Verbose.set ) echoStdout=1; XForm4x4< Real > xForm , iXForm; if( XForm.set ) { FILE* fp = fopen( XForm.value , "r" ); if( !fp ) { fprintf( stderr , "[WARNING] Could not read x-form from: %s\n" , XForm.value ); xForm = XForm4x4< Real >::Identity(); } else { for( int i=0 ; i<4 ; i++ ) for( int j=0 ; j<4 ; j++ ) { float f; if( fscanf( fp , " %f " , &f )!=1 ) fprintf( stderr , "[ERROR] Execute: Failed to read xform\n" ) , exit( 0 ); xForm(i,j) = (Real)f; } fclose( fp ); } } else xForm = XForm4x4< Real >::Identity(); iXForm = xForm.inverse(); DumpOutput2( comments , "Running Screened Poisson Reconstruction (Version 8.0)\n" ); char str[1024]; for( int i=0 ; iset ) { params[i]->writeValue( str ); if( strlen( str ) ) DumpOutput2( comments , "\t--%s %s\n" , params[i]->name , str ); else DumpOutput2( comments , "\t--%s\n" , params[i]->name ); } double t; double tt=Time(); Real isoValue = 0; Octree< Real > tree; tree.threads = Threads.value; if( !In.set ) { ShowUsage( argv[0] ); return 0; } if( !MaxSolveDepth.set ) MaxSolveDepth.value = Depth.value; OctNode< TreeNodeData >::SetAllocator( MEMORY_ALLOCATOR_BLOCK_SIZE ); t=Time(); int kernelDepth = KernelDepth.set ? KernelDepth.value : Depth.value-2; if( kernelDepth>Depth.value ) { fprintf( stderr,"[WARNING] %s can't be greater than %s: %d <= %d\n" , KernelDepth.name , Depth.name , KernelDepth.value , Depth.value ); kernelDepth = Depth.value; } double maxMemoryUsage; t=Time() , tree.maxMemoryUsage=0; SparseNodeData< PointData< Real > , 0 >* pointInfo = new SparseNodeData< PointData< Real > , 0 >(); SparseNodeData< Point3D< Real > , NORMAL_DEGREE >* normalInfo = new SparseNodeData< Point3D< Real > , NORMAL_DEGREE >(); SparseNodeData< Real , WEIGHT_DEGREE >* densityWeights = new SparseNodeData< Real , WEIGHT_DEGREE >(); SparseNodeData< Real , NORMAL_DEGREE >* nodeWeights = new SparseNodeData< Real , NORMAL_DEGREE >(); int pointCount; typedef typename Octree< Real >::template ProjectiveData< Point3D< Real > > ProjectiveColor; SparseNodeData< ProjectiveColor , DATA_DEGREE >* colorData = NULL; char* ext = GetFileExtension( In.value ); if( Color.set && Color.value>0 ) { colorData = new SparseNodeData< ProjectiveColor , DATA_DEGREE >(); OrientedPointStreamWithData< float , Point3D< unsigned char > >* pointStream; if ( !strcasecmp( ext , "bnpts" ) ) pointStream = new BinaryOrientedPointStreamWithData< float , Point3D< unsigned char > >( In.value ); else if( !strcasecmp( ext , "ply" ) ) pointStream = new PLYOrientedPointStreamWithData< float , Point3D< unsigned char > >( In.value , PlyColorProperties , 6 , ValidPlyColorProperties ); else pointStream = new ASCIIOrientedPointStreamWithData< float , Point3D< unsigned char > >( In.value , ReadASCIIColor ); 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 ); delete pointStream; for( const OctNode< TreeNodeData >* n = tree.tree().nextNode() ; n!=NULL ; n=tree.tree().nextNode( n ) ) { int idx = colorData->index( n ); if( idx>=0 ) colorData->data[idx] *= (Real)pow( Color.value , n->depth() ); } } else { OrientedPointStream< float >* pointStream; if ( !strcasecmp( ext , "bnpts" ) ) pointStream = new BinaryOrientedPointStream< float >( In.value ); else if( !strcasecmp( ext , "ply" ) ) pointStream = new PLYOrientedPointStream< float >( In.value ); else pointStream = new ASCIIOrientedPointStream< float >( In.value ); 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 ); delete pointStream; } delete[] ext; if( !Density.set ) delete densityWeights , densityWeights = NULL; { std::vector< int > indexMap; if( NORMAL_DEGREE>Degree ) tree.template EnableMultigrid< NORMAL_DEGREE >( &indexMap ); else tree.template EnableMultigrid< Degree >( &indexMap ); if( pointInfo ) pointInfo->remapIndices( indexMap ); if( normalInfo ) normalInfo->remapIndices( indexMap ); if( densityWeights ) densityWeights->remapIndices( indexMap ); if( nodeWeights ) nodeWeights->remapIndices( indexMap ); if( colorData ) colorData->remapIndices( indexMap ); } DumpOutput2( comments , "# Tree set in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage ); DumpOutput( "Input Points: %d\n" , pointCount ); DumpOutput( "Leaves/Nodes: %d/%d\n" , tree.leaves() , tree.nodes() ); DumpOutput( "Memory Usage: %.3f MB\n" , float( MemoryInfo::Usage() )/(1<<20) ); maxMemoryUsage = tree.maxMemoryUsage; t=Time() , tree.maxMemoryUsage=0; DenseNodeData< Real , Degree > constraints = tree.template SetLaplacianConstraints< Degree >( *normalInfo ); delete normalInfo; DumpOutput2( comments , "# Constraints set in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage ); DumpOutput( "Memory Usage: %.3f MB\n" , float( MemoryInfo::Usage())/(1<<20) ); maxMemoryUsage = std::max< double >( maxMemoryUsage , tree.maxMemoryUsage ); t=Time() , tree.maxMemoryUsage=0; DenseNodeData< Real , Degree > solution = tree.SolveSystem( *pointInfo , constraints , ShowResidual.set , Iters.value , MaxSolveDepth.value , CGDepth.value , CSSolverAccuracy.value ); delete pointInfo; constraints.resize( 0 ); DumpOutput2( comments , "# Linear system solved in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage ); DumpOutput( "Memory Usage: %.3f MB\n" , float( MemoryInfo::Usage() )/(1<<20) ); maxMemoryUsage = std::max< double >( maxMemoryUsage , tree.maxMemoryUsage ); CoredFileMeshData< Vertex > mesh; if( Verbose.set ) tree.maxMemoryUsage=0; t=Time(); isoValue = tree.GetIsoValue( solution , *nodeWeights ); delete nodeWeights; DumpOutput( "Got average in: %f\n" , Time()-t ); DumpOutput( "Iso-Value: %e\n" , isoValue ); if( VoxelGrid.set ) { double t = Time(); FILE* fp = fopen( VoxelGrid.value , "wb" ); if( !fp ) fprintf( stderr , "Failed to open voxel file for writing: %s\n" , VoxelGrid.value ); else { int res = 0; Pointer( Real ) values = tree.Evaluate( solution , res , isoValue , VoxelDepth.value , PrimalVoxel.set ); fwrite( &res , sizeof(int) , 1 , fp ); if( sizeof(Real)==sizeof(float) ) fwrite( values , sizeof(float) , res*res*res , fp ); else { float *fValues = new float[res*res*res]; for( int i=0 ; i( densityWeights , colorData , solution , isoValue , mesh , !LinearFit.set , !NonManifold.set , PolygonMesh.set ); if( PolygonMesh.set ) DumpOutput2( comments , "# Got polygons in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage ); else DumpOutput2( comments , "# Got triangles in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage ); maxMemoryUsage = std::max< double >( maxMemoryUsage , tree.maxMemoryUsage ); DumpOutput2( comments , "# Total Solve: %9.1f (s), %9.1f (MB)\n" , Time()-tt , maxMemoryUsage ); if( NoComments.set ) { if( ASCII.set ) PlyWritePolygons( Out.value , &mesh , PLY_ASCII , NULL , 0 , iXForm ); else PlyWritePolygons( Out.value , &mesh , PLY_BINARY_NATIVE , NULL , 0 , iXForm ); } else { if( ASCII.set ) PlyWritePolygons( Out.value , &mesh , PLY_ASCII , &comments[0] , (int)comments.size() , iXForm ); else PlyWritePolygons( Out.value , &mesh , PLY_BINARY_NATIVE , &comments[0] , (int)comments.size() , iXForm ); } DumpOutput( "Vertices / Polygons: %d / %d\n" , mesh.outOfCorePointCount()+mesh.inCorePoints.size() , mesh.polygonCount() ); } solution.resize( 0 ); if( colorData ){ delete colorData ; colorData = NULL; } return 1; } #ifdef _WIN32 inline double to_seconds( const FILETIME& ft ) { const double low_to_sec=100e-9; // 100 nanoseconds const double high_to_sec=low_to_sec*4294967296.0; return ft.dwLowDateTime*low_to_sec+ft.dwHighDateTime*high_to_sec; } #endif // _WIN32 template< class Real , class Vertex > int Execute( int argc , char* argv[] ) { switch( Degree.value ) { case 1: return _Execute< Real , 1 , Vertex >( argc , argv ); case 2: return _Execute< Real , 2 , Vertex >( argc , argv ); case 3: return _Execute< Real , 3 , Vertex >( argc , argv ); case 4: return _Execute< Real , 4 , Vertex >( argc , argv ); default: fprintf( stderr , "[ERROR] Only B-Splines of degree 1 - 4 are supported" ); return EXIT_FAILURE; } } } // namespace int PoissonRecon( int argc , char* argv[] ) { #if defined(WIN32) && defined(MAX_MEMORY_GB) if( MAX_MEMORY_GB>0 ) { SIZE_T peakMemory = 1; peakMemory <<= 30; peakMemory *= MAX_MEMORY_GB; printf( "Limiting memory usage to %.2f GB\n" , float( peakMemory>>30 ) ); HANDLE h = CreateJobObject( NULL , NULL ); AssignProcessToJobObject( h , GetCurrentProcess() ); JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 }; jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_JOB_MEMORY; jeli.JobMemoryLimit = peakMemory; if( !SetInformationJobObject( h , JobObjectExtendedLimitInformation , &jeli , sizeof( jeli ) ) ) fprintf( stderr , "Failed to set memory limit\n" ); } #endif // defined(WIN32) && defined(MAX_MEMORY_GB) double t = Time(); cmdLineParse( argc-1 , &argv[1] , sizeof(params)/sizeof(cmdLineReadable*) , params , 1 ); if( Density.set ) if( Color.set ) if( Double.set ) Execute< double , PlyColorAndValueVertex< float > >( argc , argv ); else Execute< float , PlyColorAndValueVertex< float > >( argc , argv ); else if( Double.set ) Execute< double , PlyValueVertex< float > >( argc , argv ); else Execute< float , PlyValueVertex< float > >( argc , argv ); else if( Color.set ) if( Double.set ) Execute< double , PlyColorVertex< float > >( argc , argv ); else Execute< float , PlyColorVertex< float > >( argc , argv ); else if( Double.set ) Execute< double , PlyVertex< float > >( argc , argv ); else Execute< float , PlyVertex< float > >( argc , argv ); #ifdef _WIN32 if( Performance.set ) { HANDLE cur_thread=GetCurrentThread(); FILETIME tcreat, texit, tkernel, tuser; if( GetThreadTimes( cur_thread , &tcreat , &texit , &tkernel , &tuser ) ) printf( "Time (Wall/User/Kernel): %.2f / %.2f / %.2f\n" , Time()-t , to_seconds( tuser ) , to_seconds( tkernel ) ); else printf( "Time: %.2f\n" , Time()-t ); HANDLE h = GetCurrentProcess(); PROCESS_MEMORY_COUNTERS pmc; if( GetProcessMemoryInfo( h , &pmc , sizeof(pmc) ) ) printf( "Peak Memory (MB): %d\n" , pmc.PeakWorkingSetSize>>20 ); } #endif // _WIN32 return EXIT_SUCCESS; }