/* Copyright (c) 2013, Michael Kazhdan 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 "PreProcessor.h" #define NEW_CHUNKS #define DISABLE_PARALLELIZATION // -80,-130,40 #include #include #include #include #include #include #include "MyMiscellany.h" #include "CmdLineParser.h" #include "Geometry.h" #include "Ply.h" #include "PointStream.h" #include "PointStreamData.h" cmdLineParameter< char* > In( "in" ) , Out( "out" ); cmdLineParameter< float > Width( "width" , -1.f ) , PadRadius( "radius" , 0.f ); cmdLineParameterArray< float , 6 > BoundingBox( "bBox" ); cmdLineReadable ASCII( "ascii" ) , Verbose( "verbose" ); cmdLineReadable* params[] = { &In , &Out , &Width , &PadRadius , &ASCII , &Verbose , &BoundingBox , NULL }; void ShowUsage( char* ex ) { printf( "Usage: %s\n" , ex ); printf( "\t --%s \n" , In.name ); printf( "\t[--%s ]\n" , Out.name ); printf( "\t[--%s =%f]\n" , Width.name , Width.value ); printf( "\t[--%s =%f]\n" , PadRadius.name , PadRadius.value ); printf( "\t[--%s ]\n" , BoundingBox.name ); printf( "\t[--%s]\n" , ASCII.name ); printf( "\t[--%s]\n" , Verbose.name ); } void PrintBoundingBox( Point< float , 3 > min , Point< float , 3 > max ) { printf( "[" ); for( unsigned int d=0 ; d<3 ; d++ ) printf( " %f" , min[d] ); printf( " ] [" ); for( unsigned int d=0 ; d<3 ; d++ ) printf( " %f" , max[d] ); printf( " ]" ); } template< typename ... VertexData > void WritePoints( const char *fileName , int ft , const std::vector< PlyVertexWithData< float , 3 , MultiPointStreamData< float , VertexData ... > > > &vertices , const std::vector< std::string > &comments ) { typedef PlyVertexWithData< float , 3 , MultiPointStreamData< float , VertexData ... > > Vertex; typedef MultiPointStreamData< float , VertexData ... > StreamData; PLYOutputPointStreamWithData< float , 3 , StreamData > pointStream( fileName , vertices.size() , ft , StreamData::PlyWriteProperties() , StreamData::PlyWriteNum ); for( size_t i=0 ; i void WriteMesh( const char *fileName , int ft , const std::vector< PlyVertexWithData< float , 3 , MultiPointStreamData< float , VertexData ... > > > &vertices , const std::vector< std::vector< long long > > &polygons , const std::vector< std::string > &comments ) { typedef PlyVertexWithData< float , 3 , MultiPointStreamData< float , VertexData ... > > Vertex; if( vertices.size()>std::numeric_limits< int >::max() ) { if( vertices.size()>std::numeric_limits< unsigned int >::max() ) ERROR_OUT( "more vertices than can be indexed by an unsigned int: %llu" , (unsigned long long)vertices.size() ); WARN( "more vertices than can be indexed by an int, using unsigned int instead: %llu" , (unsigned long long)vertices.size() ); std::vector< std::vector< unsigned int > > outPolygons; outPolygons.resize( polygons.size() ); for( size_t i=0 ; i( fileName , vertices , outPolygons , Vertex::PlyWriteProperties() , Vertex::PlyWriteNum , ft , comments ) ) ERROR_OUT( "Could not write mesh to: " , fileName ); } else { std::vector< std::vector< int > > outPolygons; outPolygons.resize( polygons.size() ); for( size_t i=0 ; i( fileName , vertices , outPolygons , Vertex::PlyWriteProperties() , Vertex::PlyWriteNum , ft , comments ) ) ERROR_OUT( "Could not write mesh to: " , fileName ); } } template< typename Vertex > void GetBoundingBox( const std::vector< Vertex > &vertices , Point< float , 3 > &min , Point< float , 3 > &max ) { min = max = vertices[0].point; for( size_t i=0 ; i( min[d] , vertices[i].point[d] ); max[d] = std::max< float >( max[d] , vertices[i].point[d] ); } } template< typename Vertex > void GetSubPoints( const std::vector< Vertex > &vertices , Point< float , 3 > min , Point< float , 3 > max , std::vector< Vertex > &subVertices ) { subVertices.resize( 0 ); for( size_t i=0 ; i=max[d] ) inside = false; if( inside ) subVertices.push_back( vertices[i] ); } } template< typename Vertex > void GetSubVertices( const std::vector< Vertex > &vertices , std::vector< std::vector< long long > > &polygons , std::vector< Vertex > &subVertices ) { subVertices.resize( 0 ); long long count = 0; std::unordered_map< long long , long long > vMap; for( size_t i=0 ; i void GetSubMesh( const std::vector< Vertex > &vertices , const std::vector< std::vector< long long > > &polygons , Point< float , 3 > min , Point< float , 3 > max , std::vector< Vertex > &subVertices , std::vector< std::vector< long long > > &subPolygons ) { subPolygons.resize( 0 ); for( size_t i=0 ; i center; for( size_t j=0 ; j=max[d] ) inside = false; if( inside ) subPolygons.push_back( polygons[i] ); } GetSubVertices( vertices , subPolygons , subVertices ); } template< typename ... VertexData > void Execute( void ) { Timer timer; double t = Time(); typedef PlyVertexWithData< float , 3 , MultiPointStreamData< float , VertexData ... > > Vertex; std::vector< Vertex > vertices; std::vector< std::vector< long long > > polygons; int ft; std::vector< std::string > comments; PlyReadPolygons< Vertex >( In.value , vertices , polygons , Vertex::PlyReadProperties() , Vertex::PlyReadNum , ft , comments ); printf( "Vertices / Polygons: %llu / %llu\n" , (unsigned long long)vertices.size() , (unsigned long long)polygons.size() ); printf( "Time (Wall/CPU): %.2f / %.2f\n" , timer.wallTime() , timer.cpuTime() ); printf( "Peak Memory (MB): %d\n" , MemoryInfo::PeakMemoryUsageMB() ); Point< float , 3 > min , max; GetBoundingBox( vertices , min , max ); PrintBoundingBox( min , max ) ; printf( "\n" ); float width = Width.value; if( BoundingBox.set ) { Point< float , 3 > min( BoundingBox.values[0] , BoundingBox.values[1] , BoundingBox.values[2] ); Point< float , 3 > max( BoundingBox.values[4] , BoundingBox.values[5] , BoundingBox.values[6] ); auto InBoundingBox = [&]( Point< float , 3 > p ) { return p[0]>=min[0] && p[0]=min[1] && p[1]=min[2] && p[2] > _polygons; Timer timer; #ifdef NEW_CHUNKS { size_t polygonCount = 0; for( size_t i=0 ; i center; for( int j=0 ; j center; for( int j=0 ; j _vertices; GetSubVertices( vertices , _polygons , _vertices ); if( Verbose.set ) { printf( "\t\t%s\n" , Out.value ); printf( "\t\t\tVertices / Polygons: %llu / %llu\n" , (unsigned long long)_vertices.size() , (unsigned long long)_polygons.size() ); } WriteMesh( Out.value , ASCII.set ? PLY_ASCII : ft , _vertices , _polygons , comments ); } else WARN( "no polygons in bounding box" ); } else { std::vector< Vertex > _vertices; Timer timer; #ifdef NEW_CHUNKS { size_t vertexCount = 0; for( size_t i=0 ; i0 ) { float radius = PadRadius.value * width; size_t vCount=0 , pCount=0; for( unsigned int d=0 ; d<3 ; d++ ) min[d] -= width/10000.f , max[d] += width/10000.f; int begin[] = { (int)floor( min[0]/width ) , (int)floor( min[1]/width ) , (int)floor( min[2]/width ) }; int end [] = { (int)ceil ( max[0]/width ) , (int)ceil ( max[1]/width ) , (int)ceil ( max[2]/width ) }; int size [] = { end[0]-begin[0] , end[1]-begin[1] , end[2]-begin[2] }; struct Range{ int begin[3] , end[3]; }; auto SetRange = [&]( Point< float , 3 > p , Range &range ) { for( int d=0 ; d<3 ; d++ ) { range.begin[d] = (int)floor( (p[d]-radius)/width ) , range.end[d] = (int)ceil( (p[d]+radius)/width ); if( range.begin[d]==range.end[d] ) range.end[d]++; } }; auto Index1D = [&]( int x , int y , int z ) { x -= begin[0] , y -= begin[1] , z -= begin[2]; return x + y*size[0] + z*size[0]*size[1]; }; auto Index3D = [&]( int idx , int &x , int &y , int &z ) { x = idx % size[0]; idx /= size[0]; y = idx % size[1]; idx /= size[1]; z = idx % size[2]; x += begin[0] , y += begin[1] , z += begin[2]; }; if( polygons.size() ) { std::vector< std::vector< std::vector< long long > > > _polygons( size[0]*size[1]*size[2] ); Range range; Timer timer; #ifdef NEW_CHUNKS { std::vector< size_t > polygonCounts( size[0]*size[1]*size[2] , 0 ); for( size_t i=0 ; i center; for( int j=0 ; j center; for( int j=0 ; j _vertices; GetSubVertices( vertices , _polygons[i] , _vertices ); std::stringstream stream; int x , y , z; Index3D( i , x , y , z ); stream << Out.value << "." << x << "." << y << "." << z << ".ply"; Point< float , 3 > min , max; min = Point< float , 3 >( x+0 , y+0 , z+0 ) * width; max = Point< float , 3 >( x+1 , y+1 , z+1 ) * width; if( Verbose.set ) { static std::mutex mutex; std::lock_guard< std::mutex > lock( mutex ); printf( "\t\t%s\n" , stream.str().c_str() ); printf( "\t\t\tVertices / Polygons: %llu / %llu\n" , (unsigned long long)_vertices.size() , (unsigned long long)_polygons[i].size() ); printf( "\t\t\t" ) ; PrintBoundingBox( min , max ) ; printf( "\n" ); } WriteMesh( stream.str().c_str() , ASCII.set ? PLY_ASCII : ft , _vertices , _polygons[i] , comments ); vCount += _vertices.size() , pCount += _polygons[i].size(); } } } else { std::vector< std::vector< Vertex > > _vertices( size[0]*size[1]*size[2] ); Range range; Timer timer; #ifdef NEW_CHUNKS { std::vector< size_t > vertexCounts( size[0]*size[1]*size[2] , 0 ); for( size_t i=0 ; i min , max; min = Point< float , 3 >( x+0 , y+0 , z+0 ) * width; max = Point< float , 3 >( x+1 , y+1 , z+1 ) * width; if( Verbose.set ) { static std::mutex mutex; std::lock_guard< std::mutex > lock( mutex ); printf( "\t\t%s\n" , stream.str().c_str() ); printf( "\t\t\tPoints: %llu\n" , (unsigned long long)_vertices[i].size() ); printf( "\t\t\t" ) ; PrintBoundingBox( min , max ) ; printf( "\n" ); } WritePoints( stream.str().c_str() , ASCII.set ? PLY_ASCII : ft , _vertices[i] , comments ); vCount += _vertices[i].size(); } } } if( !radius ) { if( polygons.size() ) { if( pCount!=polygons.size() ) WARN( "polygon counts don't match: " , polygons.size() , " != " , pCount ); } else { if( vCount!=vertices.size() ) WARN( "vertex counts don't match:" , vertices.size() , " != " , vCount ); } } } else { if( Out.set ) { if( polygons.size() ) WriteMesh( Out.value , ASCII.set ? PLY_ASCII : ft , vertices , polygons , comments ); else WritePoints( Out.value , ASCII.set ? PLY_ASCII : ft , vertices , comments ); } } } int main( int argc , char* argv[] ) { cmdLineParse( argc-1 , &argv[1] , params ); #ifdef USE_SEG_FAULT_HANDLER WARN( "using seg-fault handler" ); StackTracer::exec = argv[0]; signal( SIGSEGV , SignalHandler ); #endif // USE_SEG_FAULT_HANDLER #ifdef ARRAY_DEBUG WARN( "Array debugging enabled" ); #endif // ARRAY_DEBUG if( !In.set ) { ShowUsage( argv[0] ); return EXIT_FAILURE; } Timer timer; typedef MultiPointStreamData< float , PointStreamValue< float > , PointStreamNormal< float , 3 > , PointStreamColor< float > > VertexData; typedef PlyVertexWithData< float , 3 , VertexData > Vertex; bool readFlags[ Vertex::PlyReadNum ]; if( !PlyReadHeader( In.value , Vertex::PlyReadProperties() , Vertex::PlyReadNum , readFlags ) ) ERROR_OUT( "Failed to read ply header: " , In.value ); bool hasValue = VertexData::ValidPlyReadProperties< 0 >( readFlags + 3 ); bool hasNormal = VertexData::ValidPlyReadProperties< 1 >( readFlags + 3 ); bool hasColor = VertexData::ValidPlyReadProperties< 2 >( readFlags + 3 ); if( hasValue ) if( hasColor ) if( hasNormal ) Execute< PointStreamValue< float > , PointStreamNormal< float , 3 > , PointStreamColor< float > >(); else Execute< PointStreamValue< float > , PointStreamColor< float > >(); else if( hasNormal ) Execute< PointStreamValue< float > , PointStreamNormal< float , 3 > >(); else Execute< PointStreamValue< float > >(); else if( hasColor ) if( hasNormal ) Execute< PointStreamNormal< float , 3 > , PointStreamColor< float > >(); else Execute< PointStreamColor< float > >(); else if( hasNormal ) Execute< PointStreamNormal< float , 3 > >(); else Execute< >(); printf( "Time (Wall/CPU): %.2f / %.2f\n" , timer.wallTime() , timer.cpuTime() ); printf( "Peak Memory (MB): %d\n" , MemoryInfo::PeakMemoryUsageMB() ); return EXIT_SUCCESS; }