int Execute( int argc , char* argv[] )
{
    Reset< Real >();
    int i;
    int paramNum = sizeof(params)/sizeof(cmdLineReadable*);
    int commentNum=0;
    char **comments;

    comments = new char*[paramNum+7];
    for( i=0 ; i<paramNum+7 ; i++ ) comments[i] = new char[1024];

    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;
                fscanf( fp , " %f " , &f );
                xForm(i,j) = (Real)f;
            }
            fclose( fp );
        }
    }
    else xForm = XForm4x4< Real >::Identity();
    iXForm = xForm.inverse();

    DumpOutput2( comments[commentNum++] , "Running Screened Poisson Reconstruction (Version 6.13)\n" );
    char str[1024];
    for( int i=0 ; i<paramNum ; i++ )
        if( params[i]->set )
        {
            params[i]->writeValue( str );
            if( strlen( str ) ) DumpOutput2( comments[commentNum++] , "\t--%s %s\n" , params[i]->name , str );
            else                DumpOutput2( comments[commentNum++] , "\t--%s\n" , params[i]->name );
        }

    double t;
    double tt=PTime();
    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=PTime();
    int kernelDepth = KernelDepth.set ?  KernelDepth.value : Depth.value-2;
    if( kernelDepth>Depth.value )
    {
        fprintf( stderr,"[ERROR] %s can't be greater than %s: %d <= %d\n" , KernelDepth.name , Depth.name , KernelDepth.value , Depth.value );
        return EXIT_FAILURE;
    }

    double maxMemoryUsage;
    t=PTime() , tree.maxMemoryUsage=0;
    typename Octree< Real >::PointInfo* pointInfo = new typename Octree< Real >::PointInfo();
    typename Octree< Real >::NormalInfo* normalInfo = new typename Octree< Real >::NormalInfo();
    std::vector< Real >* kernelDensityWeights = new std::vector< Real >();
    std::vector< Real >* centerWeights = new std::vector< Real >();
    PointStream< float >* pointStream;
    char* ext = GetFileExtension( In.value );
    if     ( !strcasecmp( ext , "bnpts" ) ) pointStream = new BinaryPointStream< float >( In.value );
    else if( !strcasecmp( ext , "ply"   ) ) pointStream = new    PLYPointStream< float >( In.value );
    else                                    pointStream = new  ASCIIPointStream< float >( In.value );
    delete[] ext;
    int pointCount = tree.template SetTree< float >( pointStream , MinDepth.value , Depth.value , FullDepth.value , kernelDepth , Real(SamplesPerNode.value) , Scale.value , Confidence.set , NormalWeights.set , PointWeight.value , AdaptiveExponent.value , *pointInfo , *normalInfo , *kernelDensityWeights , *centerWeights , BoundaryType.value , xForm , Complete.set );
    if( !Density.set ) delete kernelDensityWeights , kernelDensityWeights = NULL;

    DumpOutput2( comments[commentNum++] , "#             Tree set in: %9.1f (s), %9.1f (MB)\n" , PTime()-t , tree.maxMemoryUsage );
    DumpOutput( "Input Points: %d\n" , pointCount );
    DumpOutput( "Leaves/Nodes: %d/%d\n" , tree.tree.leaves() , tree.tree.nodes() );
    DumpOutput( "Memory Usage: %.3f MB\n" , float( MemoryInfo::Usage() )/(1<<20) );

    maxMemoryUsage = tree.maxMemoryUsage;
    t=PTime() , tree.maxMemoryUsage=0;
    Pointer( Real ) constraints = tree.SetLaplacianConstraints( *normalInfo );
    delete normalInfo;
    DumpOutput2( comments[commentNum++] , "#      Constraints set in: %9.1f (s), %9.1f (MB)\n" , PTime()-t , tree.maxMemoryUsage );
    DumpOutput( "Memory Usage: %.3f MB\n" , float( MemoryInfo::Usage())/(1<<20) );
    maxMemoryUsage = std::max< double >( maxMemoryUsage , tree.maxMemoryUsage );

    t=PTime() , tree.maxMemoryUsage=0;
    Pointer( Real ) solution = tree.SolveSystem( *pointInfo , constraints , ShowResidual.set , Iters.value , MaxSolveDepth.value , CGDepth.value , CSSolverAccuracy.value );
    delete pointInfo;
    FreePointer( constraints );
    DumpOutput2( comments[commentNum++] , "# Linear system solved in: %9.1f (s), %9.1f (MB)\n" , PTime()-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=PTime();
    isoValue = tree.GetIsoValue( solution , *centerWeights );
    delete centerWeights;
    DumpOutput( "Got average in: %f\n" , PTime()-t );
    DumpOutput( "Iso-Value: %e\n" , isoValue );

    if( VoxelGrid.set )
    {
        double t = PTime();
        FILE* fp = fopen( VoxelGrid.value , "wb" );
        if( !fp ) fprintf( stderr , "Failed to open voxel file for writing: %s\n" , VoxelGrid.value );
        else
        {
            int res;
            Pointer( Real ) values = tree.Evaluate( solution , res , isoValue , VoxelDepth.value );
            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<res*res*res ; i++ ) fValues[i] = float( values[i] );
                fwrite( fValues , sizeof(float) , res*res*res , fp );
                delete[] fValues;
            }
            fclose( fp );
            DeletePointer( values );
        }
        DumpOutput( "Got voxel grid in: %f\n" , PTime()-t );
    }

    if( Out.set )
    {
        t = PTime() , tree.maxMemoryUsage = 0;
        tree.GetMCIsoSurface( kernelDensityWeights ? GetPointer( *kernelDensityWeights ) : NullPointer< Real >() , solution , isoValue , mesh , true , !NonManifold.set , PolygonMesh.set );
        if( PolygonMesh.set ) DumpOutput2( comments[commentNum++] , "#         Got polygons in: %9.1f (s), %9.1f (MB)\n" , PTime()-t , tree.maxMemoryUsage );
        else                  DumpOutput2( comments[commentNum++] , "#        Got triangles in: %9.1f (s), %9.1f (MB)\n" , PTime()-t , tree.maxMemoryUsage );
        maxMemoryUsage = std::max< double >( maxMemoryUsage , tree.maxMemoryUsage );
        DumpOutput2( comments[commentNum++],"#             Total Solve: %9.1f (s), %9.1f (MB)\n" , PTime()-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 , commentNum , iXForm );
            else            PlyWritePolygons( Out.value , &mesh , PLY_BINARY_NATIVE , comments , commentNum , iXForm );
        }
        DumpOutput( "Vertices / Polygons: %d / %d\n" , mesh.outOfCorePointCount()+mesh.inCorePoints.size() , mesh.polygonCount() );
    }
    FreePointer( solution );
    return 1;
}
Beispiel #2
0
int ExecuteMemory( int argc , char* argv[], std::vector< std::vector< float > > & positions, std::vector< std::vector< float > > & normals,
                   std::vector< std::vector< float > > & vertices, std::vector< std::vector< int > > & faces )
{
    argc = (int)argc;
    argv = argv;

    cmdLineParse( argc-1 , &argv[1] , sizeof(params)/sizeof(cmdLineReadable*) , params , 1 );

    typedef PlyVertex< Real > Vertex;
    bool OutputDensity = false;
    OutputDensity = OutputDensity;

    int i;
    int paramNum = sizeof(params)/sizeof(cmdLineReadable*);
    int commentNum=0;
    char **comments;

    comments = new char*[paramNum+7];
    for( i=0 ; i<paramNum+7 ; i++ ) comments[i] = new char[1024];

    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++ ) fscanf( fp , " %f " , &xForm( i , j ) );
            fclose( fp );
        }
    }
    else xForm = XForm4x4< Real >::Identity();
    iXForm = xForm.inverse();

    DumpOutput2( comments[commentNum++] , "Running Screened Poisson Reconstruction (Version 5.5)\n" , Degree );
    char str[1024];
    for( int i=0 ; i<paramNum ; i++ )
        if( params[i]->set )
        {
            params[i]->writeValue( str );
            if( strlen( str ) ) DumpOutput2( comments[commentNum++] , "\t--%s %s\n" , params[i]->name , str );
            else                DumpOutput2( comments[commentNum++] , "\t--%s\n" , params[i]->name );
        }

    double t;
    double tt=Time();
    Real isoValue = 0;

    Octree< Degree , false > tree;
    tree.threads = Threads.value;

    //if( !In.set )
    //{
    //	ShowUsage(argv[0]);
    //	return 0;
    //}

    if( !MaxSolveDepth.set ) MaxSolveDepth.value = Depth.value;
    if( SolverDivide.value<MinDepth.value )
    {
        fprintf( stderr , "[WARNING] %s must be at least as large as %s: %d>=%d\n" , SolverDivide.name , MinDepth.name , SolverDivide.value , MinDepth.value );
        SolverDivide.value = MinDepth.value;
    }
    if( IsoDivide.value<MinDepth.value )
    {
        fprintf( stderr , "[WARNING] %s must be at least as large as %s: %d>=%d\n" , IsoDivide.name , MinDepth.name , IsoDivide.value , IsoDivide.value );
        IsoDivide.value = MinDepth.value;
    }

    OctNode< TreeNodeData< false > , Real >::SetAllocator( MEMORY_ALLOCATOR_BLOCK_SIZE );

    t=Time();
    int kernelDepth = KernelDepth.set ?  KernelDepth.value : Depth.value-2;

    tree.setBSplineData( Depth.value , BoundaryType.value );
    if( kernelDepth>Depth.value )
    {
        fprintf( stderr,"[ERROR] %s can't be greater than %s: %d <= %d\n" , KernelDepth.name , Depth.name , KernelDepth.value , Depth.value );
        return EXIT_FAILURE;
    }

    double maxMemoryUsage;
    t=Time() , tree.maxMemoryUsage=0;

    //int pointCount = tree.setTree( In.value , Depth.value , MinDepth.value , kernelDepth , Real(SamplesPerNode.value) , Scale.value , Confidence.set , PointWeight.value , AdaptiveExponent.value , xForm );

    // Load data
    MemoryPointStream< Real >* ps = new MemoryPointStream< Real >( &positions, &normals );
    int pointCount = tree.setTreeMemory( ps , Depth.value , MinDepth.value , kernelDepth , Real(SamplesPerNode.value) , Scale.value , Confidence.set , PointWeight.value , AdaptiveExponent.value , xForm );

    tree.ClipTree();
    tree.finalize( IsoDivide.value );

    DumpOutput2( comments[commentNum++] , "#             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.tree.leaves() , tree.tree.nodes() );
    DumpOutput( "Memory Usage: %.3f MB\n" , float( MemoryInfo::Usage() )/(1<<20) );

    maxMemoryUsage = tree.maxMemoryUsage;
    t=Time() , tree.maxMemoryUsage=0;
    tree.SetLaplacianConstraints();
    DumpOutput2( comments[commentNum++] , "#      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;
    tree.LaplacianMatrixIteration( SolverDivide.value, ShowResidual.set , MinIters.value , SolverAccuracy.value , MaxSolveDepth.value , FixedIters.value );
    DumpOutput2( comments[commentNum++] , "# 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();
    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;
            Pointer( Real ) values = tree.GetSolutionGrid( res , isoValue , VoxelDepth.value );
            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<res*res*res ; i++ ) fValues[i] = float( values[i] );
                fwrite( fValues , sizeof(float) , res*res*res , fp );
                delete[] fValues;
            }
            fclose( fp );
            DeletePointer( values );
        }
        DumpOutput( "Got voxel grid in: %f\n" , Time()-t );
    }

    if( Out.set )
    {
        t = Time() , tree.maxMemoryUsage = 0;
        tree.GetMCIsoTriangles( isoValue , IsoDivide.value , &mesh , 0 , 1 , !NonManifold.set , PolygonMesh.set );
        if( PolygonMesh.set ) DumpOutput2( comments[commentNum++] , "#         Got polygons in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage );
        else                  DumpOutput2( comments[commentNum++] , "#        Got triangles in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage );
        maxMemoryUsage = std::max< double >( maxMemoryUsage , tree.maxMemoryUsage );
        DumpOutput2( comments[commentNum++],"#             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 , commentNum , iXForm );
        	else            PlyWritePolygons( Out.value , &mesh , PLY_BINARY_NATIVE , comments , commentNum , iXForm );
        }*/

        // Write to memory
        writeTriMesh(&mesh, vertices, faces);
    }

    return 1;
}
Beispiel #3
0
int Execute(int argc,char* argv[])
{
	int i;
	cmdLineString In,Out;
	cmdLineReadable Binary,Verbose,NoResetSamples,NoClipTree,Confidence,Manifold,PolygonMesh;
	cmdLineInt Depth(8),SolverDivide(8),IsoDivide(8),Refine(3);
	cmdLineInt KernelDepth;
	cmdLineFloat SamplesPerNode(1.0f),Scale(1.1f);
	char* paramNames[]=
	{
		"in","depth","out","refine","noResetSamples","noClipTree",
		"binary","solverDivide","isoDivide","scale","verbose",
		"kernelDepth","samplesPerNode","confidence","manifold","polygonMesh"
	};
	cmdLineReadable* params[]=
	{
		&In,&Depth,&Out,&Refine,&NoResetSamples,&NoClipTree,
		&Binary,&SolverDivide,&IsoDivide,&Scale,&Verbose,
		&KernelDepth,&SamplesPerNode,&Confidence,&Manifold,&PolygonMesh
	};
	int paramNum=sizeof(paramNames)/sizeof(char*);
	int commentNum=0;
	char **comments;

	comments=new char*[paramNum+7];
	for(i=0;i<paramNum+7;i++){comments[i]=new char[1024];}

	const char* Rev = "Rev: V2 ";
	const char* Date = "Date: 2006-11-09 (Thur, 09 Nov 2006) ";

	cmdLineParse(argc-1,&argv[1],paramNames,paramNum,params,0);

	if(Verbose.set){echoStdout=1;}

	DumpOutput2(comments[commentNum++],"Running Multi-Grid Octree Surface Reconstructor (degree %d). Version 3\n", Degree);
	if(In.set)				{DumpOutput2(comments[commentNum++],"\t--in %s\n",In.value);}
	if(Out.set)				{DumpOutput2(comments[commentNum++],"\t--out %s\n",Out.value);}
	if(Binary.set)			{DumpOutput2(comments[commentNum++],"\t--binary\n");}
	if(Depth.set)			{DumpOutput2(comments[commentNum++],"\t--depth %d\n",Depth.value);}
	if(SolverDivide.set)	{DumpOutput2(comments[commentNum++],"\t--solverDivide %d\n",SolverDivide.value);}
	if(IsoDivide.set)		{DumpOutput2(comments[commentNum++],"\t--isoDivide %d\n",IsoDivide.value);}
	if(Refine.set)			{DumpOutput2(comments[commentNum++],"\t--refine %d\n",Refine.value);}
	if(Scale.set)			{DumpOutput2(comments[commentNum++],"\t--scale %f\n",Scale.value);}
	if(KernelDepth.set)		{DumpOutput2(comments[commentNum++],"\t--kernelDepth %d\n",KernelDepth.value);}
	if(SamplesPerNode.set)	{DumpOutput2(comments[commentNum++],"\t--samplesPerNode %f\n",SamplesPerNode.value);}
	if(NoResetSamples.set)	{DumpOutput2(comments[commentNum++],"\t--noResetSamples\n");}
	if(NoClipTree.set)		{DumpOutput2(comments[commentNum++],"\t--noClipTree\n");}
	if(Confidence.set)		{DumpOutput2(comments[commentNum++],"\t--confidence\n");}
	if(Manifold.set)		{DumpOutput2(comments[commentNum++],"\t--manifold\n");}
	if(PolygonMesh.set)		{DumpOutput2(comments[commentNum++],"\t--polygonMesh\n");}

	double t;
	double tt=Time();
	Point3D<float> center;
	Real scale=1.0;
	Real isoValue=0;
	//////////////////////////////////
	// Fix courtesy of David Gallup //
	TreeNodeData::UseIndex = 1;     //
	//////////////////////////////////
	Octree<Degree> tree;
	PPolynomial<Degree> ReconstructionFunction=PPolynomial<Degree>::GaussianApproximation();

	center.coords[0]=center.coords[1]=center.coords[2]=0;
	if(!In.set || !Out.set)
	{
		ShowUsage(argv[0]);
		return 0;
	}
	
	TreeOctNode::SetAllocator(MEMORY_ALLOCATOR_BLOCK_SIZE);

	t=Time();
	int kernelDepth=Depth.value-2;
	if(KernelDepth.set){kernelDepth=KernelDepth.value;}

	tree.setFunctionData(ReconstructionFunction,Depth.value,0,Real(1.0)/(1<<Depth.value));
	DumpOutput("Function Data Set In: %lg\n",Time()-t);
	DumpOutput("Memory Usage: %.3f MB\n",float(MemoryInfo::Usage())/(1<<20));
	if(kernelDepth>Depth.value){
		fprintf(stderr,"KernelDepth can't be greater than Depth: %d <= %d\n",kernelDepth,Depth.value);
		return EXIT_FAILURE;
	}


	t=Time();
#if 1
	tree.setTree(In.value,Depth.value,Binary.set,kernelDepth,Real(SamplesPerNode.value),Scale.value,center,scale,!NoResetSamples.set,Confidence.set);
#else
if(Confidence.set){
	tree.setTree(In.value,Depth.value,Binary.set,kernelDepth,Real(SamplesPerNode.value),Scale.value,center,scale,!NoResetSamples.set,0,1);
}
else{
	tree.setTree(In.value,Depth.value,Binary.set,kernelDepth,Real(SamplesPerNode.value),Scale.value,center,scale,!NoResetSamples.set,0,0);
}
#endif
	DumpOutput2(comments[commentNum++],"#             Tree set in: %9.1f (s), %9.1f (MB)\n",Time()-t,tree.maxMemoryUsage);
	DumpOutput("Leaves/Nodes: %d/%d\n",tree.tree.leaves(),tree.tree.nodes());
	DumpOutput("   Tree Size: %.3f MB\n",float(sizeof(TreeOctNode)*tree.tree.nodes())/(1<<20));
	DumpOutput("Memory Usage: %.3f MB\n",float(MemoryInfo::Usage())/(1<<20));

	if(!NoClipTree.set){
		t=Time();
		tree.ClipTree();
		DumpOutput("Tree Clipped In: %lg\n",Time()-t);
		DumpOutput("Leaves/Nodes: %d/%d\n",tree.tree.leaves(),tree.tree.nodes());
		DumpOutput("   Tree Size: %.3f MB\n",float(sizeof(TreeOctNode)*tree.tree.nodes())/(1<<20));
	}

	t=Time();
	tree.finalize1(Refine.value);
	DumpOutput("Finalized 1 In: %lg\n",Time()-t);
	DumpOutput("Leaves/Nodes: %d/%d\n",tree.tree.leaves(),tree.tree.nodes());
	DumpOutput("Memory Usage: %.3f MB\n",float(MemoryInfo::Usage())/(1<<20));

	t=Time();
	tree.maxMemoryUsage=0;
	tree.SetLaplacianWeights();
	DumpOutput2(comments[commentNum++],"#Laplacian Weights Set In: %9.1f (s), %9.1f (MB)\n",Time()-t,tree.maxMemoryUsage);
	DumpOutput("Memory Usage: %.3f MB\n",float(MemoryInfo::Usage())/(1<<20));

	t=Time();
	tree.finalize2(Refine.value);
	DumpOutput("Finalized 2 In: %lg\n",Time()-t);
	DumpOutput("Leaves/Nodes: %d/%d\n",tree.tree.leaves(),tree.tree.nodes());
	DumpOutput("Memory Usage: %.3f MB\n",float(MemoryInfo::Usage())/(1<<20));

	tree.maxMemoryUsage=0;
	t=Time();
	tree.LaplacianMatrixIteration(SolverDivide.value);
	DumpOutput2(comments[commentNum++],"# 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));

	CoredVectorMeshData mesh;
	tree.maxMemoryUsage=0;
	t=Time();
	isoValue=tree.GetIsoValue();
	DumpOutput("Got average in: %f\n",Time()-t);
	DumpOutput("Iso-Value: %e\n",isoValue);
	DumpOutput("Memory Usage: %.3f MB\n",float(tree.MemoryUsage()));

	t=Time();
	if(IsoDivide.value) tree.GetMCIsoTriangles( isoValue , IsoDivide.value , &mesh , 0 , 1 , Manifold.set , PolygonMesh.set );
	else                tree.GetMCIsoTriangles( isoValue ,                   &mesh , 0 , 1 , Manifold.set , PolygonMesh.set );
	if( PolygonMesh.set ) DumpOutput2(comments[commentNum++],"#         Got Polygons in: %9.1f (s), %9.1f (MB)\n",Time()-t,tree.maxMemoryUsage);
	else                  DumpOutput2(comments[commentNum++],"#        Got Triangles in: %9.1f (s), %9.1f (MB)\n",Time()-t,tree.maxMemoryUsage);
	DumpOutput2(comments[commentNum++],"#              Total Time: %9.1f (s)\n",Time()-tt);
	PlyWritePolygons(Out.value,&mesh,PLY_BINARY_NATIVE,center,scale,comments,commentNum);

	return 1;
}