Loading [MathJax]/extensions/TeX/AMSmath.js
Radium Engine  1.5.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
TinyPlyFileLoader.cpp
1 #include <IO/TinyPlyLoader/TinyPlyFileLoader.hpp>
2 
3 #include <Core/Asset/FileData.hpp>
4 #include <Core/Containers/VectorArray.hpp>
5 #include <Core/Geometry/StandardAttribNames.hpp>
6 #include <Core/Utils/Attribs.hpp>
7 
8 #include <tinyply.h>
9 
10 #include <fstream>
11 #include <iostream>
12 #include <string>
13 
14 const std::string plyExt( "ply" );
15 
16 namespace Ra {
17 namespace IO {
18 
19 using namespace Core::Utils; // log
20 using namespace Core::Asset; // Filedata
21 
22 TinyPlyFileLoader::TinyPlyFileLoader() {}
23 
24 TinyPlyFileLoader::~TinyPlyFileLoader() {}
25 
26 std::vector<std::string> TinyPlyFileLoader::getFileExtensions() const {
27  return std::vector<std::string>( { "*." + plyExt } );
28 }
29 
30 bool TinyPlyFileLoader::handleFileExtension( const std::string& extension ) const {
31  return extension.compare( plyExt ) == 0;
32 }
33 // from https://github.com/ddiakopoulos/tinyply/blob/master/source/example-utils.hpp
34 
35 struct memory_buffer : public std::streambuf {
36  char* p_start { nullptr };
37  char* p_end { nullptr };
38  size_t size;
39 
40  memory_buffer( char const* first_elem, size_t size_ ) :
41  p_start( const_cast<char*>( first_elem ) ), p_end( p_start + size_ ), size( size_ ) {
42  setg( p_start, p_start, p_end );
43  }
44 
45  pos_type seekoff( off_type off, std::ios_base::seekdir dir, std::ios_base::openmode ) override {
46  if ( dir == std::ios_base::cur )
47  gbump( static_cast<int>( off ) );
48  else
49  setg( p_start, ( dir == std::ios_base::beg ? p_start : p_end ) + off, p_end );
50  return gptr() - p_start;
51  }
52 
53  pos_type seekpos( pos_type pos, std::ios_base::openmode which ) override {
54  return seekoff( pos, std::ios_base::beg, which );
55  }
56 };
57 
58 inline std::vector<uint8_t> read_file_binary( const std::string& pathToFile ) {
59  std::ifstream file( pathToFile, std::ios::binary );
60  std::vector<uint8_t> fileBufferBytes;
61 
62  if ( file.is_open() ) {
63  file.seekg( 0, std::ios::end );
64  size_t sizeBytes = file.tellg();
65  file.seekg( 0, std::ios::beg );
66  fileBufferBytes.resize( sizeBytes );
67  if ( file.read( (char*)fileBufferBytes.data(), sizeBytes ) ) return fileBufferBytes;
68  }
69  else
70  throw std::runtime_error( "could not open binary ifstream to path " + pathToFile );
71  return fileBufferBytes;
72 }
73 
74 struct memory_stream : virtual memory_buffer, public std::istream {
75  memory_stream( char const* first_elem, size_t size_ ) :
76  memory_buffer( first_elem, size_ ), std::istream( static_cast<std::streambuf*>( this ) ) {}
77 };
78 
79 template <typename DataType>
80 void copyArrayToContainer( const uint8_t* buffer,
81  Ra::Core::Vector1Array& container,
82  size_t count ) {
83  auto data = reinterpret_cast<const DataType*>( buffer );
84  container.reserve( count );
85  for ( size_t i = 0; i < count; ++i ) {
86  container.emplace_back( data[i] );
87  }
88 }
89 
90 template <typename DataType>
91 void copyArrayToContainer( const uint8_t* buffer,
92  Ra::Core::Vector3Array& container,
93  size_t count ) {
94  auto data = reinterpret_cast<const DataType*>( buffer );
95  container.reserve( count );
96  for ( size_t i = 0; i < count; ++i ) {
97  container.emplace_back( data[i * 3 + 0], data[i * 3 + 1], data[i * 3 + 2] );
98  }
99 }
100 
101 template <typename ContainerType>
102 void copyBufferToContainer( const std::shared_ptr<tinyply::PlyData>& buffer,
103  ContainerType& container ) {
104  if ( buffer && buffer->count != 0 ) {
105  switch ( buffer->t ) {
106  case tinyply::Type::FLOAT32: {
107  copyArrayToContainer<float>( buffer->buffer.get(), container, buffer->count );
108  } break;
109  case tinyply::Type::FLOAT64: {
110  copyArrayToContainer<double>( buffer->buffer.get(), container, buffer->count );
111  } break;
112  default:
113  LOG( logWARNING ) << "[TinyPLY] copyBufferToContainer - unsupported buffer type ..."
114  << tinyply::PropertyTable[buffer->t].str;
115  }
116  }
117 }
118 
119 FileData* TinyPlyFileLoader::loadFile( const std::string& filename ) {
120 
121  std::unique_ptr<std::istream> file_stream;
122  std::vector<uint8_t> byte_buffer;
123 
124  // from https://github.com/ddiakopoulos/tinyply/blob/master/source/example.cpp
125  // For most files < 1gb, pre-loading the entire file upfront and wrapping it into a
126  // stream is a net win for parsing speed, about 40% faster.
127  const bool preload_into_memory = true;
128  if ( preload_into_memory ) {
129  byte_buffer = read_file_binary( filename );
130  file_stream.reset( new memory_stream( (char*)byte_buffer.data(), byte_buffer.size() ) );
131  }
132  else { file_stream.reset( new std::ifstream( filename, std::ios::binary ) ); }
133 
134  if ( !file_stream || file_stream->fail() ) {
135  LOG( logINFO ) << "[TinyPLY] Could not open file [" << filename << "] Aborting"
136  << std::endl;
137  return nullptr;
138  }
139 
140  // Parse the ASCII header fields
141  tinyply::PlyFile file;
142  file.parse_header( *file_stream );
143 
144  auto elements = file.get_elements();
145  if ( std::any_of( elements.begin(), elements.end(), []( const auto& e ) -> bool {
146  return e.name == "face" && e.size != 0;
147  } ) ) {
148  // Mesh found. Let the other loaders handle it
149  LOG( logINFO ) << "[TinyPLY] Faces found. Aborting" << std::endl;
150  return nullptr;
151  }
152 
153  // we are now sure to have a point-cloud
154  FileData* fileData = new FileData( filename );
155  fileData->setVerbose( true );
156 
157  if ( !fileData->isInitialized() ) {
158  delete fileData;
159  LOG( logINFO ) << "[TinyPLY] Filedata cannot be initialized...";
160  return nullptr;
161  }
162 
163  if ( fileData->isVerbose() ) {
164  LOG( logINFO ) << "[TinyPLY] File Loading begin...";
165  LOG( logINFO ) << "....................................................................";
166  for ( auto c : file.get_comments() )
167  LOG( logINFO ) << "Comment: " << c;
168  for ( auto e : file.get_elements() ) {
169  LOG( logINFO ) << "element - " << e.name << " (" << e.size << ")";
170  for ( auto p : e.properties )
171  LOG( logINFO ) << "\tproperty - " << p.name << " ("
172  << tinyply::PropertyTable[p.propertyType].str << ")";
173  }
174  LOG( logINFO ) << "....................................................................";
175  }
176 
177  auto initBuffer = [&file]( const std::string& elementKey,
178  const std::vector<std::string> propertyKeys ) {
179  std::shared_ptr<tinyply::PlyData> ret;
180  try {
181  ret = file.request_properties_from_element( elementKey, propertyKeys );
182  }
183  catch ( const std::exception& e ) {
184  ret = nullptr;
185  LOG( logINFO ) << "[TinyPLY] " << e.what();
186  }
187  return ret;
188  };
189 
190  auto startTime { std::clock() };
191 
192  // a unique name is required by the component messaging system
193  static int nameId { 0 };
194  auto geomData = std::make_unique<GeometryData>( "PC_" + std::to_string( ++nameId ),
195  GeometryData::POINT_CLOUD );
196  geomData->setFrame( Core::Transform::Identity() );
197 
199  auto vertBuffer { initBuffer( "vertex", { "x", "y", "z" } ) };
200  // if there is no vertex prop, or their count is 0, then quit.
201  if ( !vertBuffer || vertBuffer->count == 0 ) {
202  delete fileData;
203  LOG( logINFO ) << "[TinyPLY] No vertex found";
204  return nullptr;
205  }
208  auto normalBuffer { initBuffer( "vertex", { "nx", "ny", "nz" } ) };
209  auto alphaBuffer { initBuffer( "vertex", { "alpha" } ) };
210  auto colorBuffer { initBuffer( "vertex", { "red", "green", "blue" } ) };
211 
213  std::vector<std::pair<std::string, std::shared_ptr<tinyply::PlyData>>> allAttributes;
214  const std::set<std::string> usedAttributes {
215  "x", "y", "z", "nx", "ny", "nz", "alpha", "red", "green", "blue" };
216  for ( const auto& e : file.get_elements() ) {
217  if ( e.name == "vertex" ) {
218  for ( const auto& p : e.properties ) {
219  bool exists = usedAttributes.find( p.name ) != usedAttributes.end();
220  if ( !exists ) {
221  allAttributes.emplace_back( p.name, initBuffer( "vertex", { p.name } ) );
222  }
223  }
224  break;
225  }
226  }
227 
228  // read requested buffers (and only those) from file content
229  file.read( *file_stream );
230  {
231  auto unlocker = geomData->getGeometry().vertexAttribs().getScopedLockState();
232  copyBufferToContainer( vertBuffer, geomData->getGeometry().verticesWithLock() );
233  copyBufferToContainer( normalBuffer, geomData->getGeometry().normalsWithLock() );
234  }
235 
236  auto& vertexAttribs = geomData->getGeometry().vertexAttribs();
237 
238  size_t colorCount = colorBuffer ? colorBuffer->count : 0;
239  if ( colorCount != 0 ) {
240  auto handle = vertexAttribs.addAttrib<Core::Vector4>(
241  Ra::Core::Geometry::getAttribName( Ra::Core::Geometry::MeshAttrib::VERTEX_COLOR ) );
242  auto& container = vertexAttribs.getDataWithLock( handle );
243 
244  if ( alphaBuffer && alphaBuffer->count == colorCount ) {
245  uint8_t* al = alphaBuffer->buffer.get();
246  uint8_t* colors = colorBuffer->buffer.get();
247  for ( size_t i = 0; i < colorCount; ++i, al++, colors += 3 ) {
248  container.emplace_back( Scalar( colors[0] ) / 255_ra,
249  Scalar( colors[1] ) / 255_ra,
250  Scalar( colors[2] ) / 255_ra,
251  Scalar( *al ) / 255_ra );
252  }
253  }
254  else {
255  uint8_t* colors = colorBuffer->buffer.get();
256  for ( size_t i = 0; i < colorCount; ++i, colors += 3 ) {
257  container.emplace_back( Scalar( colors[0] ) / 255_ra,
258  Scalar( colors[1] ) / 255_ra,
259  Scalar( colors[2] ) / 255_ra,
260  1_ra );
261  }
262  }
263  vertexAttribs.unlock( handle );
264  }
265 
267  for ( const auto& a : allAttributes ) {
268  // For now manage scalar properties only
269  if ( a.second->isList ) {
270  LOG( logWARNING ) << "[TinyPLY] unmanaged vector attribute " << a.first;
271  continue;
272  }
274  auto attribName { a.first };
275  std::replace( attribName.begin(), attribName.end(), '-', '_' );
276  LOG( logINFO ) << "[TinyPLY] Adding custom attrib with name " << attribName << " (was "
277  << a.first << ")";
278  auto handle = vertexAttribs.addAttrib<Scalar>( attribName );
279  auto& container = vertexAttribs.getDataWithLock( handle );
280  copyBufferToContainer( a.second, container );
281  vertexAttribs.unlock( handle );
282  }
283 
284  fileData->m_geometryData.clear();
285  fileData->m_geometryData.reserve( 1 );
286  fileData->m_geometryData.push_back( std::move( geomData ) );
287 
288  fileData->m_loadingTime = ( std::clock() - startTime ) / Scalar( CLOCKS_PER_SEC );
289 
290  if ( fileData->isVerbose() ) {
291  LOG( logINFO ) << "[TinyPLY] File Loading end.";
292  fileData->displayInfo();
293  }
294 
295  fileData->m_processed = true;
296 
297  return fileData;
298 }
299 
300 std::string TinyPlyFileLoader::name() const {
301  return "TinyPly";
302 }
303 } // namespace IO
304 } // namespace Ra
Definition: Cage.cpp:3