/* Header for PLY polygon files. - Greg Turk, March 1994 A PLY file contains a single polygonal _object_. An object is composed of lists of _elements_. Typical elements are vertices, faces, edges and materials. Each type of element for a given object has one or more _properties_ associated with the element type. For instance, a vertex element may have as properties three floating-point values x,y,z and three unsigned chars for red, green and blue. --------------------------------------------------------------- Copyright (c) 1994 The Board of Trustees of The Leland Stanford Junior University. All rights reserved. Permission to use, copy, modify and distribute this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice and this permission notice appear in all copies of this software and that you do not sell the software. THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef __PLY_H__ #define __PLY_H__ #include #include #include "PlyFile.h" #include "Geometry.h" template< class Real > int PLYType( void ); template<> inline int PLYType< int >( void ){ return PLY_INT ; } template<> inline int PLYType< char >( void ){ return PLY_CHAR ; } template<> inline int PLYType< unsigned char >( void ){ return PLY_UCHAR ; } template<> inline int PLYType< float >( void ){ return PLY_FLOAT ; } template<> inline int PLYType< double >( void ){ return PLY_DOUBLE; } template< class Real > inline int PLYType( void ){ ERROR_OUT( "Unrecognized type" ); } template< typename Integer > struct PLYTraits{ static const std::string name; }; template<> const std::string PLYTraits< int >::name="int"; template<> const std::string PLYTraits< unsigned int >::name="unsigned int"; template<> const std::string PLYTraits< long >::name="long"; template<> const std::string PLYTraits< unsigned long >::name="unsigned long"; template<> const std::string PLYTraits< long long >::name="long long"; template<> const std::string PLYTraits< unsigned long long >::name="unsigned long long"; template< typename Index > struct PlyFace { unsigned int nr_vertices; Index *vertices; int segment; static PlyProperty face_props[]; }; template<> PlyProperty PlyFace< int >::face_props[] = { PlyProperty( "vertex_indices" , PLY_INT , PLY_INT , offsetof( PlyFace , vertices ) , 1 , PLY_INT , PLY_INT , offsetof( PlyFace , nr_vertices ) ) }; template<> PlyProperty PlyFace< unsigned int >::face_props[] = { PlyProperty( "vertex_indices" , PLY_UINT , PLY_UINT , offsetof( PlyFace , vertices ) , 1 , PLY_INT , PLY_INT , offsetof( PlyFace , nr_vertices ) ) }; template<> PlyProperty PlyFace< long long >::face_props[] = { PlyProperty( "vertex_indices" , PLY_LONGLONG , PLY_LONGLONG , offsetof( PlyFace , vertices ) , 1 , PLY_INT , PLY_INT , offsetof( PlyFace , nr_vertices ) ) }; template<> PlyProperty PlyFace< unsigned long long >::face_props[] = { PlyProperty( "vertex_indices" , PLY_ULONGLONG , PLY_ULONGLONG , offsetof( PlyFace , vertices ) , 1 , PLY_INT , PLY_INT , offsetof( PlyFace , nr_vertices ) ) }; struct RGBColor { unsigned char c[3]; unsigned char& operator[]( int idx ) { return c[idx]; } unsigned char operator[]( int idx ) const { return c[idx]; } RGBColor( void ){ c[0] = c[1] = c[2] = 0; } RGBColor( const RGBColor& rgb ){ memcpy( c , rgb.c , sizeof(unsigned char) * 3 ); } RGBColor& operator = ( const RGBColor& rgb ){ memcpy( c , rgb.c , sizeof(unsigned char) * 3 ) ; return *this; } }; /////////////// // PlyVertex // /////////////// template< typename _Real , int Dim , typename _RealOnDisk=float > class PlyVertex { public: typedef _Real Real; PlyVertex& operator += ( const PlyVertex& p ){ point += p.point ; return *this; } PlyVertex& operator -= ( const PlyVertex& p ){ point -= p.point ; return *this; } PlyVertex& operator *= ( Real s ) { point *= s ; return *this; } PlyVertex& operator /= ( Real s ) { point /= s ; return *this; } PlyVertex operator + ( const PlyVertex& p ) const { return PlyVertex( point + p.point ); } PlyVertex operator - ( const PlyVertex& p ) const { return PlyVertex( point - p.point ); } PlyVertex operator * ( Real s ) const { return PlyVertex( point * s ); } PlyVertex operator / ( Real s ) const { return PlyVertex( point / s ); } const static int PlyReadNum = Dim; const static int PlyWriteNum = Dim; static const PlyProperty* PlyReadProperties( void ){ return _PlyProperties; } static const PlyProperty* PlyWriteProperties( void ){ return _PlyProperties; } Point< Real , Dim > point; PlyVertex( void ) {} PlyVertex( Point< Real , Dim > p ) : point(p) { } struct Transform { Transform( void ){} Transform( const XForm< Real , Dim+1 >& xForm ) : _pointXForm(xForm) { } PlyVertex operator() ( const PlyVertex& p ) const { PlyVertex _p; _p.point = _pointXForm * p.point; return _p; } protected: XForm< Real , Dim+1 > _pointXForm; }; protected: static const PlyProperty _PlyProperties[]; }; template<> const PlyProperty PlyVertex< float , 2 , float >::_PlyProperties[] = { PlyProperty( "x" , PLY_FLOAT , PLY_FLOAT , int( offsetof( PlyVertex , point.coords[0] ) ) , 0 , 0 , 0 , 0 ) , PlyProperty( "y" , PLY_FLOAT , PLY_FLOAT , int( offsetof( PlyVertex , point.coords[1] ) ) , 0 , 0 , 0 , 0 ) , }; template<> const PlyProperty PlyVertex< double , 2 , float >::_PlyProperties[] = { PlyProperty( "x" , PLY_FLOAT , PLY_DOUBLE , int( offsetof( PlyVertex , point.coords[0] ) ) , 0 , 0 , 0 , 0 ) , PlyProperty( "y" , PLY_FLOAT , PLY_DOUBLE , int( offsetof( PlyVertex , point.coords[1] ) ) , 0 , 0 , 0 , 0 ) , }; template<> const PlyProperty PlyVertex< float , 2 , double >::_PlyProperties[] = { PlyProperty( "x" , PLY_DOUBLE , PLY_FLOAT , int( offsetof( PlyVertex , point.coords[0] ) ) , 0 , 0 , 0 , 0 ) , PlyProperty( "y" , PLY_DOUBLE , PLY_FLOAT , int( offsetof( PlyVertex , point.coords[1] ) ) , 0 , 0 , 0 , 0 ) , }; template<> const PlyProperty PlyVertex< double , 2 , double >::_PlyProperties[] = { PlyProperty( "x" , PLY_DOUBLE , PLY_DOUBLE , int( offsetof( PlyVertex , point.coords[0] ) ) , 0 , 0 , 0 , 0 ) , PlyProperty( "y" , PLY_DOUBLE , PLY_DOUBLE , int( offsetof( PlyVertex , point.coords[1] ) ) , 0 , 0 , 0 , 0 ) , }; template<> const PlyProperty PlyVertex< float , 3 , float >::_PlyProperties[] = { PlyProperty( "x" , PLY_FLOAT , PLY_FLOAT , int( offsetof( PlyVertex , point.coords[0] ) ) , 0 , 0 , 0 , 0 ) , PlyProperty( "y" , PLY_FLOAT , PLY_FLOAT , int( offsetof( PlyVertex , point.coords[1] ) ) , 0 , 0 , 0 , 0 ) , PlyProperty( "z" , PLY_FLOAT , PLY_FLOAT , int( offsetof( PlyVertex , point.coords[2] ) ) , 0 , 0 , 0 , 0 ) , }; template<> const PlyProperty PlyVertex< double , 3 , float >::_PlyProperties[] = { PlyProperty( "x" , PLY_FLOAT , PLY_DOUBLE , int( offsetof( PlyVertex , point.coords[0] ) ) , 0 , 0 , 0 , 0 ) , PlyProperty( "y" , PLY_FLOAT , PLY_DOUBLE , int( offsetof( PlyVertex , point.coords[1] ) ) , 0 , 0 , 0 , 0 ) , PlyProperty( "z" , PLY_FLOAT , PLY_DOUBLE , int( offsetof( PlyVertex , point.coords[2] ) ) , 0 , 0 , 0 , 0 ) , }; template<> const PlyProperty PlyVertex< float , 3 , double >::_PlyProperties[] = { PlyProperty( "x" , PLY_DOUBLE , PLY_FLOAT , int( offsetof( PlyVertex , point.coords[0] ) ) , 0 , 0 , 0 , 0 ) , PlyProperty( "y" , PLY_DOUBLE , PLY_FLOAT , int( offsetof( PlyVertex , point.coords[1] ) ) , 0 , 0 , 0 , 0 ) , PlyProperty( "z" , PLY_DOUBLE , PLY_FLOAT , int( offsetof( PlyVertex , point.coords[2] ) ) , 0 , 0 , 0 , 0 ) , }; template<> const PlyProperty PlyVertex< double , 3 , double >::_PlyProperties[] = { PlyProperty( "x" , PLY_DOUBLE , PLY_DOUBLE , int( offsetof( PlyVertex , point.coords[0] ) ) , 0 , 0 , 0 , 0 ) , PlyProperty( "y" , PLY_DOUBLE , PLY_DOUBLE , int( offsetof( PlyVertex , point.coords[1] ) ) , 0 , 0 , 0 , 0 ) , PlyProperty( "z" , PLY_DOUBLE , PLY_DOUBLE , int( offsetof( PlyVertex , point.coords[2] ) ) , 0 , 0 , 0 , 0 ) , }; /////////////////////// // PlyVertexWithData // /////////////////////// template< typename _Real , int Dim , typename Data , typename _RealOnDisk=float > class PlyVertexWithData { public: typedef _Real Real; PlyVertexWithData& operator += ( const PlyVertexWithData& p ){ point += p.point , data += p.data ; return *this; } PlyVertexWithData& operator -= ( const PlyVertexWithData& p ){ point -= p.point , data -= p.data ; return *this; } PlyVertexWithData& operator *= ( Real s ) { point *= s , data *= s ; return *this; } PlyVertexWithData& operator /= ( Real s ) { point /= s , data /= s ; return *this; } PlyVertexWithData operator + ( const PlyVertexWithData& p ) const { return PlyVertexWithData( point + p.point , data + p.data ); } PlyVertexWithData operator - ( const PlyVertexWithData& p ) const { return PlyVertexWithData( point - p.point , data - p.data ); } PlyVertexWithData operator * ( Real s ) const { return PlyVertexWithData( point * s , data * s ); } PlyVertexWithData operator / ( Real s ) const { return PlyVertexWithData( point / s , data / s ); } const static int PlyReadNum = Data::PlyReadNum + Dim; const static int PlyWriteNum = Data::PlyWriteNum + Dim; static const PlyProperty* PlyReadProperties( void ){ _SetReadProperties() ; return _PlyReadProperties; } static const PlyProperty* PlyWriteProperties( void ){ _SetWriteProperties() ; return _PlyWriteProperties; } Point< Real , Dim > point; Data data; PlyVertexWithData( void ) {} PlyVertexWithData( Point< Real , Dim > p , Data d ) : point(p) , data(d) { } struct Transform { Transform( void ){} Transform( const XForm< Real , Dim+1 >& xForm ) : _pointXForm(xForm) , _dataXForm(xForm) { } PlyVertexWithData operator() ( const PlyVertexWithData& p ) const { PlyVertexWithData _p; _p.point = _pointXForm * p.point; _p.data = _dataXForm( p.data ); return _p; } protected: XForm< Real , Dim+1 > _pointXForm; typename Data::Transform _dataXForm; }; protected: static void _SetReadProperties( void ); static void _SetWriteProperties( void ); static PlyProperty _PlyReadProperties[]; static PlyProperty _PlyWriteProperties[]; }; template< typename Real , int Dim , typename Data , typename RealOnDisk > PlyProperty PlyVertexWithData< Real , Dim , Data , RealOnDisk >::_PlyReadProperties[ PlyReadNum ]; template< typename Real , int Dim , typename Data , typename RealOnDisk > PlyProperty PlyVertexWithData< Real , Dim , Data , RealOnDisk >::_PlyWriteProperties[ PlyWriteNum ]; template< typename Real , int Dim , typename Data , typename RealOnDisk > void PlyVertexWithData< Real , Dim , Data , RealOnDisk >::_SetReadProperties( void ) { { const PlyProperty * ReadProps = PlyVertex< Real , Dim , RealOnDisk >::PlyReadProperties(); for( int d=0 ; d::PlyReadNum ; d++ ) _PlyReadProperties[d] = ReadProps[d]; } { const PlyProperty * ReadProps = Data::PlyReadProperties(); for( int d=0 ; d::PlyReadNum ] = ReadProps[d]; _PlyReadProperties[d+PlyVertex< Real , Dim , RealOnDisk >::PlyReadNum ].offset += (int)offsetof( PlyVertexWithData , data ); } } } template< typename Real , int Dim , typename Data , typename RealOnDisk > void PlyVertexWithData< Real , Dim , Data , RealOnDisk >::_SetWriteProperties( void ) { { const PlyProperty * WriteProps = PlyVertex< Real , Dim , RealOnDisk >::PlyWriteProperties(); for( int d=0 ; d::PlyWriteNum ; d++ ) _PlyWriteProperties[d] = WriteProps[d]; } { const PlyProperty * WriteProps = Data::PlyWriteProperties(); for( int d=0 ; d::PlyWriteNum ] = WriteProps[d]; _PlyWriteProperties[d+PlyVertex< Real , Dim , RealOnDisk >::PlyWriteNum ].offset += (int)offsetof( PlyVertexWithData , data ); } } } template< class Vertex , typename Index , class Real , int Dim , typename OutputIndex=int > int PlyWritePolygons( const char* fileName , CoredMeshData< Vertex , Index >* mesh , int file_type , const Point< float , Dim >& translate , float scale , const std::vector< std::string >& comments , XForm< Real , Dim+1 > xForm=XForm< Real , Dim+1 >::Identity() ); template< class Vertex , typename Index , class Real , int Dim , typename OutputIndex=int > int PlyWritePolygons( const char* fileName , CoredMeshData< Vertex , Index >* mesh , int file_type , const std::vector< std::string >& comments , XForm< Real , Dim+1 > xForm=XForm< Real , Dim+1 >::Identity() ); inline bool PlyReadHeader( char* fileName , const PlyProperty* properties , int propertyNum , bool* readFlags , int& file_type ) { std::vector< std::string > elist; float version; PlyFile *ply = PlyFile::Read( fileName , elist , file_type , version ); if( !ply ) return false; for( int i=0 ; iget_property( elist[i].c_str() , &properties[j] )!=0; delete ply; return true; } inline bool PlyReadHeader( char* fileName , const PlyProperty* properties , int propertyNum , bool* readFlags ) { int file_type; return PlyReadHeader( fileName , properties , propertyNum , readFlags , file_type ); } template< class Vertex , typename Index > int PlyReadPolygons( const char* fileName, std::vector< Vertex >& vertices , std::vector >& polygons , const PlyProperty* properties , int propertyNum , int& file_type , std::vector< std::string > &comments , bool* readFlags=NULL ); template< class Vertex , typename Index > int PlyWritePolygons( const char* fileName , const std::vector< Vertex > &vertices , const std::vector< std::vector< Index > > &polygons , const PlyProperty* properties , int propertyNum , int file_type , const std::vector< std::string > &comments ); template< class Vertex , typename Index > int PlyWritePolygons( const char* fileName , const std::vector< Vertex > &vertices , const std::vector< std::vector< Index > > &polygons , const PlyProperty *properties , int propertyNum , int file_type , const std::vector< std::string > &comments ) { size_t nr_vertices = vertices.size(); size_t nr_faces = polygons.size(); float version; std::vector< std::string > elem_names = { std::string( "vertex" ) , std::string( "face" ) }; PlyFile *ply = PlyFile::Write( fileName , elem_names , file_type , version ); if (!ply){return 0;} // // describe vertex and face properties // ply->element_count( "vertex", nr_vertices ); for( int i=0 ; idescribe_property( "vertex" , &properties[i] ); ply->element_count( "face" , nr_faces ); ply->describe_property( "face" , PlyFace< Index >::face_props ); // Write in the comments for( int i=0 ; iput_comment( comments[i] ); ply->header_complete(); // write vertices ply->put_element_setup( elem_names[0] ); for( size_t i=0 ; iput_element( (void *)&vertices[i] ); // write faces PlyFace< Index > ply_face; int maxFaceVerts=3; ply_face.nr_vertices = 3; ply_face.vertices = new Index[3]; ply->put_element_setup( elem_names[1] ); for( size_t i=0 ; imaxFaceVerts ) { delete[] ply_face.vertices; maxFaceVerts = (int)polygons[i].size(); ply_face.vertices=new Index[ maxFaceVerts ]; } ply_face.nr_vertices = (int)polygons[i].size(); for( size_t j=0 ; jput_element( (void *)&ply_face ); } delete[] ply_face.vertices; delete ply; return 1; } template< class Vertex , typename Index > int PlyReadPolygons ( const char *fileName , std::vector< Vertex > &vertices , std::vector< std::vector< Index > > &polygons , const PlyProperty *properties , int propertyNum , int &file_type , std::vector< std::string > &comments , bool *readFlags ) { std::vector< std::string > elist = { std::string( "vertex" ) , std::string( "face" ) }; float version; PlyFile *ply = PlyFile::Read( fileName , elist , file_type , version ); if(!ply) return 0; comments.reserve( comments.size() + ply->comments.size() ); for( int i=0 ; icomments.size() ; i++ ) comments.push_back( ply->comments[i] ); for( int i=0 ; i plist = ply->get_element_description( elem_name , num_elems ); if( !plist.size() ) { delete ply; return 0; } if( elem_name=="vertex" ) { for( int i=0 ; iget_property( elem_name , &properties[i] ); if( readFlags ) readFlags[i] = (hasProperty!=0); } vertices.resize( num_elems ); for( size_t j=0 ; jget_element( (void *)&vertices[j] ); } else if( elem_name=="face" ) { ply->get_property( elem_name , PlyFace< Index >::face_props ); polygons.resize( num_elems ); for( size_t j=0 ; j ply_face; ply->get_element( (void *)&ply_face ); polygons[j].resize( ply_face.nr_vertices ); for( unsigned int k=0 ; kget_other_element( elem_name , num_elems ); for( int j=0 ; j int PlyWritePolygons( const char* fileName , CoredMeshData< Vertex , Index >* mesh , int file_type , const Point< float , Dim >& translate , float scale , const std::vector< std::string > &comments , XForm< Real , Dim+1 > xForm ) { if( mesh->outOfCorePointCount()+mesh->inCorePoints.size()>(size_t)std::numeric_limits::max() ) { if( std::is_same< Index , OutputIndex >::value ) ERROR_OUT( "more vertices than can be represented using " , PLYTraits< Index >::name ); WARN( "more vertices than can be represented using " , PLYTraits< OutputIndex >::name , " using " , PLYTraits< Index >::name , " instead" ); return PlyWritePolygons< Vertex , Index , Real , Dim , Index >( fileName , mesh , file_type , translate , scale , comments , xForm ); } size_t nr_vertices = mesh->outOfCorePointCount()+mesh->inCorePoints.size(); size_t nr_faces = mesh->polygonCount(); float version; std::vector< std::string > elem_names = { std::string( "vertex" ) , std::string( "face" ) }; PlyFile *ply = PlyFile::Write( fileName , elem_names , file_type , version ); if( !ply ) return 0; mesh->resetIterator(); // // describe vertex and face properties // ply->element_count( "vertex" , nr_vertices ); for( int i=0 ; idescribe_property( "vertex" , &Vertex::Properties[i] ); ply->element_count( "face" , nr_faces ); ply->describe_property( "face" , PlyFace< OutputIndex >::face_props ); // Write in the comments for( int i=0 ; iput_comment( comments[i] ); ply->header_complete(); // write vertices ply->put_element_setup( "vertex" ); for( size_t i=0 ; iinCorePoints.size() ; i++ ) { Vertex vertex = xForm * ( mesh->inCorePoints[i] * scale + translate ); ply->put_element( (void *)&vertex ); } for( size_t i=0; ioutOfCorePointCount() ; i++ ) { Vertex vertex; mesh->nextOutOfCorePoint( vertex ); vertex = xForm * ( vertex * scale + translate ); ply->put_element( (void *)&vertex ); } // for, write vertices // write faces std::vector< CoredVertexIndex< Index > > polygon; ply->put_element_setup( "face" ); for( size_t i=0 ; i ply_face; mesh->nextPolygon( polygon ); ply_face.nr_vertices = int( polygon.size() ); ply_face.vertices = new OutputIndex[ polygon.size() ]; for( int j=0 ; jinCorePoints.size() ); ply->put_element( (void *)&ply_face ); delete[] ply_face.vertices; } // for, write faces delete ply; return 1; } template< class Vertex , typename Index , class Real , int Dim , typename OutputIndex > int PlyWritePolygons( const char* fileName , CoredMeshData< Vertex , Index >* mesh , int file_type , const std::vector< std::string > &comments , XForm< Real , Dim+1 > xForm ) { if( mesh->outOfCorePointCount()+mesh->inCorePoints.size()>(size_t)std::numeric_limits::max() ) { if( std::is_same< Index , OutputIndex >::value ) ERROR_OUT( "more vertices than can be represented using " , PLYTraits< Index >::name ); WARN( "more vertices than can be represented using " , PLYTraits< OutputIndex >::name , " using " , PLYTraits< Index >::name , " instead" ); return PlyWritePolygons< Vertex , Index , Real , Dim , Index >( fileName , mesh , file_type , comments , xForm ); } size_t nr_vertices = mesh->outOfCorePointCount()+mesh->inCorePoints.size(); size_t nr_faces = mesh->polygonCount(); float version; std::vector< std::string > elem_names = { std::string( "vertex" ) , std::string( "face" ) }; PlyFile *ply = PlyFile::Write( fileName , elem_names , file_type , version ); if( !ply ) return 0; mesh->resetIterator(); // // describe vertex and face properties // ply->element_count( "vertex" , nr_vertices ); typename Vertex::Transform _xForm( xForm ); const PlyProperty* PlyWriteProperties = Vertex::PlyWriteProperties(); for( int i=0 ; idescribe_property( "vertex" , &PlyWriteProperties[i] ); ply->element_count( "face" , nr_faces ); ply->describe_property( "face" , PlyFace< OutputIndex >::face_props ); // Write in the comments for( int i=0 ; iput_comment( comments[i] ); ply->header_complete(); // write vertices ply->put_element_setup( "vertex" ); for( size_t i=0 ; iinCorePoints.size() ; i++ ) { Vertex vertex = _xForm( mesh->inCorePoints[i] ); ply->put_element( (void *)&vertex ); } for( size_t i=0; ioutOfCorePointCount() ; i++ ) { Vertex vertex; mesh->nextOutOfCorePoint( vertex ); vertex = _xForm( vertex ); ply->put_element( (void *)&vertex ); } // for, write vertices // write faces std::vector< CoredVertexIndex< Index > > polygon; ply->put_element_setup( "face" ); for( size_t i=0 ; i ply_face; mesh->nextPolygon( polygon ); ply_face.nr_vertices = int( polygon.size() ); ply_face.vertices = new OutputIndex[ polygon.size() ]; for( int j=0 ; jinCorePoints.size() ); ply->put_element( (void *)&ply_face ); delete[] ply_face.vertices; } // for, write faces delete ply; return 1; } inline int PlyDefaultFileType( void ){ return PLY_ASCII; } #endif /* !__PLY_H__ */