#include "readPLY.h" #include #include #include #include #include #include "tinyply.h" #include "read_file_binary.h" #include "FileMemoryStream.h" namespace igl { template IGL_INLINE bool _tinyply_buffer_to_matrix( tinyply::PlyData & D, Eigen::PlainObjectBase & M, size_t rows, size_t cols ) { Eigen::Map< Eigen::Matrix > _map( reinterpret_cast( D.buffer.get()), rows, cols ); M = _map.template cast(); return true; } template IGL_INLINE bool tinyply_buffer_to_matrix( tinyply::PlyData & D, Eigen::PlainObjectBase & M, size_t rows, size_t cols ) { switch(D.t) { case tinyply::Type::INT8 : return _tinyply_buffer_to_matrix(D, M,rows,cols); case tinyply::Type::UINT8 : return _tinyply_buffer_to_matrix(D, M,rows,cols); case tinyply::Type::INT16 : return _tinyply_buffer_to_matrix(D, M,rows,cols); case tinyply::Type::UINT16 : return _tinyply_buffer_to_matrix(D, M,rows,cols); case tinyply::Type::INT32 : return _tinyply_buffer_to_matrix(D, M,rows,cols); case tinyply::Type::UINT32 : return _tinyply_buffer_to_matrix(D, M,rows,cols); case tinyply::Type::FLOAT32 : return _tinyply_buffer_to_matrix(D, M,rows,cols); case tinyply::Type::FLOAT64 : return _tinyply_buffer_to_matrix(D, M,rows,cols); default: return false; } } template IGL_INLINE bool _tinyply_tristrips_to_trifaces( tinyply::PlyData & D, Eigen::PlainObjectBase & M, size_t el, size_t el_len ) { Eigen::Map< Eigen::Matrix > _map( reinterpret_cast( D.buffer.get()), el, el_len ); // to make it more interesting, triangles in triangle strip can be separated by negative index elements // 1. count all triangles size_t triangles=0; // TODO: it's possible to optimize this , i suppose for(size_t i=0; i=0 && _map(i,j+1)>=0 && _map(i,j+2)>=0) triangles++; } // 2. convert triangles to faces, skipping over the negative indeces, indicating separate strips M.resize(triangles, 3); size_t k=0; for(size_t i=0; i=0 && _map(i,j+1)>=0 && _map(i,j+2)>=0) { // consequtive faces on the same strip have to be flip-flopped, to preserve orientation M( k,0 ) = static_cast( _map(i, j ) ); M( k,1 ) = static_cast( _map(i, j+1+flip ) ); M( k,2 ) = static_cast( _map(i, j+1+(flip^1) ) ); k++; flip ^= 1; } else { // reset flip on new strip start flip = 0; } } } assert(k==triangles); return true; } template IGL_INLINE bool tinyply_tristrips_to_faces( tinyply::PlyData & D, Eigen::PlainObjectBase & M, size_t el, size_t el_len ) { switch(D.t) { case tinyply::Type::INT8 : return _tinyply_tristrips_to_trifaces(D, M,el,el_len); case tinyply::Type::UINT8 : return _tinyply_tristrips_to_trifaces(D, M,el,el_len); case tinyply::Type::INT16 : return _tinyply_tristrips_to_trifaces(D, M,el,el_len); case tinyply::Type::UINT16 : return _tinyply_tristrips_to_trifaces(D, M,el,el_len); case tinyply::Type::INT32 : return _tinyply_tristrips_to_trifaces(D, M,el,el_len); case tinyply::Type::UINT32 : return _tinyply_tristrips_to_trifaces(D, M,el,el_len); case tinyply::Type::FLOAT32 : return _tinyply_tristrips_to_trifaces(D, M,el,el_len); case tinyply::Type::FLOAT64 : return _tinyply_tristrips_to_trifaces(D, M,el,el_len); default: return false; } } template < typename DerivedV, typename DerivedF, typename DerivedE, typename DerivedN, typename DerivedUV, typename DerivedVD, typename DerivedFD, typename DerivedED > IGL_INLINE bool readPLY( FILE *fp, Eigen::PlainObjectBase & V, Eigen::PlainObjectBase & F, Eigen::PlainObjectBase & E, Eigen::PlainObjectBase & N, Eigen::PlainObjectBase & UV, Eigen::PlainObjectBase & VD, std::vector & Vheader, Eigen::PlainObjectBase & FD, std::vector & Fheader, Eigen::PlainObjectBase & ED, std::vector & Eheader, std::vector & comments ) { // buffer the whole file in memory // then read from memory buffer try { std::vector fileBufferBytes; // read_file_binary will call fclose read_file_binary(fp,fileBufferBytes); FileMemoryStream stream((char*)fileBufferBytes.data(), fileBufferBytes.size()); return readPLY(stream,V,F,E,N,UV,VD,Vheader,FD,Fheader,ED,Eheader,comments); } catch(const std::exception& e) { std::cerr << "ReadPLY error: " << e.what() << std::endl; } fclose(fp); return false; } template < typename DerivedV, typename DerivedF > IGL_INLINE bool readPLY( FILE *fp, Eigen::PlainObjectBase & V, Eigen::PlainObjectBase & F ) { Eigen::MatrixXd N,UV,VD,FD,ED; Eigen::MatrixXi E; std::vector Vheader,Eheader,Fheader,comments; return readPLY(fp,V,F,E,N,UV,VD,Vheader,FD,Fheader,ED,Eheader,comments); } template < typename DerivedV, typename DerivedF, typename DerivedE > IGL_INLINE bool readPLY( FILE *fp, Eigen::PlainObjectBase & V, Eigen::PlainObjectBase & F, Eigen::PlainObjectBase & E ) { Eigen::MatrixXd N,UV,VD,FD,ED; std::vector Vheader,Eheader,Fheader,comments; return readPLY(fp,V,F,E,N,UV,VD,Vheader,FD,Fheader,ED,Eheader,comments); } template < typename DerivedV, typename DerivedF, typename DerivedE, typename DerivedN, typename DerivedUV, typename DerivedVD, typename DerivedFD, typename DerivedED > IGL_INLINE bool readPLY( std::istream & ply_stream, Eigen::PlainObjectBase & V, Eigen::PlainObjectBase & F, Eigen::PlainObjectBase & E, Eigen::PlainObjectBase & N, Eigen::PlainObjectBase & UV, Eigen::PlainObjectBase & VD, std::vector & Vheader, Eigen::PlainObjectBase & FD, std::vector & Fheader, Eigen::PlainObjectBase & ED, std::vector & Eheader, std::vector & comments ) { tinyply::PlyFile file; file.parse_header(ply_stream); std::set vertex_std{ "x","y","z", "nx","ny","nz", "u","v", "texture_u", "texture_v", "s", "t"}; std::set face_std { "vertex_index", "vertex_indices"}; std::set edge_std { "vertex1", "vertex2"}; //non-standard edge indexes // Tinyply treats parsed data as untyped byte buffers. std::shared_ptr vertices, normals, faces, texcoords, edges; // Some ply files contain tristrips instead of faces std::shared_ptr tristrips; std::shared_ptr _vertex_data; std::vector _vertex_header; std::shared_ptr _face_data; std::vector _face_header; std::shared_ptr _edge_data; std::vector _edge_header; for (auto c : file.get_comments()) comments.push_back(c); for (auto e : file.get_elements()) { if(e.name == "vertex" ) // found a vertex { for (auto p : e.properties) { if(vertex_std.find(p.name) == vertex_std.end()) { _vertex_header.push_back(p.name); } } } else if(e.name == "face" ) // found face { for (auto p : e.properties) { if(face_std.find(p.name) == face_std.end()) { _face_header.push_back(p.name); } } } else if(e.name == "edge" ) // found edge { for (auto p : e.properties) { if(edge_std.find(p.name) == edge_std.end()) { _edge_header.push_back(p.name); } } } // skip the unknown entries } // The header information can be used to programmatically extract properties on elements // known to exist in the header prior to reading the data. For brevity of this sample, properties // like vertex position are hard-coded: try { vertices = file.request_properties_from_element("vertex", { "x", "y", "z" }); } catch (const std::exception & ) { } try { normals = file.request_properties_from_element("vertex", { "nx", "ny", "nz" }); } catch (const std::exception & ) { } //Try texture coordinates with several names try { //texture_u texture_v are the names used by meshlab to store textures texcoords = file.request_properties_from_element("vertex", { "texture_u", "texture_v" }); } catch (const std::exception & ) { } if (!texcoords) { try { //u v are the naive names texcoords = file.request_properties_from_element("vertex", { "u", "v" }); } catch (const std::exception & ) { } } if (!texcoords) { try { //s t were the names used by blender and the previous libigl PLY reader. texcoords = file.request_properties_from_element("vertex", { "s", "t" }); } catch (const std::exception & ) { } } // Providing a list size hint (the last argument) is a 2x performance improvement. If you have // arbitrary ply files, it is best to leave this 0. try { faces = file.request_properties_from_element( "face", { "vertex_indices" }, 0); } catch (const std::exception & ) { } if (!faces) { try { // alternative name of the elements faces = file.request_properties_from_element( "face", { "vertex_index" },0); } catch (const std::exception & ) { } } if (!faces) { try { // try using tristrips tristrips = file.request_properties_from_element( "tristrips", { "vertex_indices" }, 0); } catch (const std::exception & ) { } if (!tristrips) { try { // alternative name of the elements tristrips = file.request_properties_from_element( "tristrips", { "vertex_index" }, 0); } catch (const std::exception & ) { } } } try { edges = file.request_properties_from_element("edge", { "vertex1", "vertex2" }); } catch (const std::exception & ) { } if(! _vertex_header.empty()) _vertex_data = file.request_properties_from_element( "vertex", _vertex_header); if(! _face_header.empty()) _face_data = file.request_properties_from_element( "face", _face_header); if(! _edge_header.empty()) _edge_data = file.request_properties_from_element( "edge", _edge_header); // Parse the geometry data file.read(ply_stream); if (!vertices || !tinyply_buffer_to_matrix(*vertices,V,vertices->count,3) ) { V.resize(0,0); } if (!normals || !tinyply_buffer_to_matrix(*normals,N,normals->count,3) ) { N.resize(0,0); } if (!texcoords || !tinyply_buffer_to_matrix(*texcoords,UV,texcoords->count,2) ) { UV.resize(0,0); } //HACK: Unfortunately, tinyply doesn't store list size as a separate variable if (!faces || !tinyply_buffer_to_matrix(*faces, F, faces->count, faces->count==0?0:faces->buffer.size_bytes()/(tinyply::PropertyTable[faces->t].stride*faces->count) )) { if(tristrips) { // need to convert to faces // code based on blender importer for ply // converting triangle strips into triangles // tinyply supports tristrips of the same length only size_t el_count = tristrips->buffer.size_bytes()/(tinyply::PropertyTable[tristrips->t].stride*tristrips->count); // all strips should have tristrips->count elements if(!tinyply_tristrips_to_faces(*tristrips, F , tristrips->count, el_count)) F.resize(0,0); } else { F.resize(0,0); } } if(!edges || !tinyply_buffer_to_matrix(*edges,E, edges->count,2)) { E.resize(0,0); } /// convert vertex data: Vheader=_vertex_header; if(_vertex_header.empty()) { VD.resize(0,0); } else { VD.resize(vertices->count,_vertex_header.size()); tinyply_buffer_to_matrix(*_vertex_data, VD, vertices->count, _vertex_header.size()); } /// convert face data: Fheader=_face_header; if(_face_header.empty()) { FD.resize(0,0); } else { FD.resize(faces->count, _face_header.size()); tinyply_buffer_to_matrix(*_face_data, FD, faces->count, _face_header.size()); } /// convert edge data: Eheader=_edge_header; if(_edge_header.empty()) { ED.resize(0,0); } else { ED.resize(_edge_data->count, _edge_header.size()); tinyply_buffer_to_matrix(*_edge_data, ED, _edge_data->count, _edge_header.size()); } return true; } template < typename DerivedV, typename DerivedF, typename DerivedE, typename DerivedN, typename DerivedUV, typename DerivedVD, typename DerivedFD, typename DerivedED > IGL_INLINE bool readPLY( const std::string& ply_file, Eigen::PlainObjectBase & V, Eigen::PlainObjectBase & F, Eigen::PlainObjectBase & E, Eigen::PlainObjectBase & N, Eigen::PlainObjectBase & UV, Eigen::PlainObjectBase & VD, std::vector & VDheader, Eigen::PlainObjectBase & FD, std::vector & FDheader, Eigen::PlainObjectBase & ED, std::vector & EDheader, std::vector & comments ) { std::ifstream ply_stream(ply_file, std::ios::binary); if (ply_stream.fail()) { std::cerr << "ReadPLY: Error opening file " << ply_file << std::endl; return false; } try { return readPLY(ply_stream, V, F, E, N, UV, VD, VDheader, FD,FDheader, ED, EDheader, comments ); } catch (const std::exception& e) { std::cerr << "ReadPLY error: " << ply_file << e.what() << std::endl; } return false; } template < typename DerivedV, typename DerivedF, typename DerivedE, typename DerivedN, typename DerivedUV, typename DerivedD > IGL_INLINE bool readPLY( const std::string & filename, Eigen::PlainObjectBase & V, Eigen::PlainObjectBase & F, Eigen::PlainObjectBase & E, Eigen::PlainObjectBase & N, Eigen::PlainObjectBase & UV, Eigen::PlainObjectBase & VD, std::vector & Vheader ) { Eigen::MatrixXd FD,ED; std::vector Fheader,Eheader; std::vector comments; return readPLY(filename,V,F,E,N,UV,VD,Vheader,FD,Fheader,ED,Eheader,comments); } template < typename DerivedV, typename DerivedF, typename DerivedE, typename DerivedN, typename DerivedUV > IGL_INLINE bool readPLY( const std::string & filename, Eigen::PlainObjectBase & V, Eigen::PlainObjectBase & F, Eigen::PlainObjectBase & E, Eigen::PlainObjectBase & N, Eigen::PlainObjectBase & UV ) { Eigen::MatrixXd VD,FD,ED; std::vector Vheader,Fheader,Eheader; std::vector comments; return readPLY(filename,V,F,E, N,UV,VD,Vheader,FD,Fheader,ED,Eheader,comments); } template < typename DerivedV, typename DerivedF, typename DerivedN, typename DerivedUV > IGL_INLINE bool readPLY( const std::string & filename, Eigen::PlainObjectBase & V, Eigen::PlainObjectBase & F, Eigen::PlainObjectBase & N, Eigen::PlainObjectBase & UV ) { Eigen::MatrixXd VD,FD,ED; Eigen::MatrixXi E; std::vector Vheader,Fheader,Eheader; std::vector comments; return readPLY(filename,V,F,E, N,UV,VD,Vheader,FD,Fheader,ED,Eheader,comments); } template < typename DerivedV, typename DerivedF > IGL_INLINE bool readPLY( const std::string & filename, Eigen::PlainObjectBase & V, Eigen::PlainObjectBase & F ) { Eigen::MatrixXd N,UV; Eigen::MatrixXd VD,FD,ED; Eigen::MatrixXi E; std::vector Vheader,Fheader,Eheader; std::vector comments; return readPLY(filename,V,F,E,N,UV,VD,Vheader,FD,Fheader,ED,Eheader,comments); } template < typename DerivedV, typename DerivedF, typename DerivedE > IGL_INLINE bool readPLY( const std::string & filename, Eigen::PlainObjectBase & V, Eigen::PlainObjectBase & F, Eigen::PlainObjectBase & E ) { Eigen::MatrixXd N,UV; Eigen::MatrixXd VD,FD,ED; std::vector Vheader,Fheader,Eheader; std::vector comments; return readPLY(filename,V,F,E,N,UV,VD,Vheader,FD,Fheader,ED,Eheader,comments); } } //igl namespace #ifdef IGL_STATIC_LIBRARY // Explicit template instantiation // generated by autoexplicit.sh template bool igl::readPLY, Eigen::Matrix >(FILE*, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); template bool igl::readPLY, Eigen::Matrix >(FILE*, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); template bool igl::readPLY, Eigen::Matrix >(FILE*, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); template bool igl::readPLY, Eigen::Matrix >(FILE*, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); template bool igl::readPLY, Eigen::Matrix >(FILE*, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); template bool igl::readPLY, Eigen::Matrix >(FILE*, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); template bool igl::readPLY, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); template bool igl::readPLY, Eigen::Matrix >(FILE*, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); template bool igl::readPLY, Eigen::Matrix >(FILE*, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); template bool igl::readPLY, Eigen::Matrix >(FILE*, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); template bool igl::readPLY, Eigen::Matrix >(FILE*, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); template bool igl::readPLY, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, std::vector, std::allocator >, std::allocator, std::allocator > > >&, Eigen::PlainObjectBase >&, std::vector, std::allocator >, std::allocator, std::allocator > > >&, Eigen::PlainObjectBase >&, std::vector, std::allocator >, std::allocator, std::allocator > > >&, std::vector, std::allocator >, std::allocator, std::allocator > > >&); template bool igl::readPLY, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, std::vector, std::allocator >, std::allocator, std::allocator > > >&, Eigen::PlainObjectBase >&, std::vector, std::allocator >, std::allocator, std::allocator > > >&, Eigen::PlainObjectBase >&, std::vector, std::allocator >, std::allocator, std::allocator > > >&, std::vector, std::allocator >, std::allocator, std::allocator > > >&); #endif