Radium Engine  1.5.20
Loading...
Searching...
No Matches
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
14const std::string plyExt( "ply" );
15
16namespace Ra {
17namespace IO {
18
19using namespace Core::Utils; // log
20using namespace Core::Asset; // Filedata
21
22TinyPlyFileLoader::TinyPlyFileLoader() {}
23
24TinyPlyFileLoader::~TinyPlyFileLoader() {}
25
26std::vector<std::string> TinyPlyFileLoader::getFileExtensions() const {
27 return std::vector<std::string>( { "*." + plyExt } );
28}
29
30bool 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
35struct 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
58inline 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
74struct 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
79template <typename DataType>
80void 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
90template <typename DataType>
91void 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
101template <typename ContainerType>
102void 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
119FileData* TinyPlyFileLoader::loadFile( const std::string& filename ) {
120
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 ) {
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
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
300std::string TinyPlyFileLoader::name() const {
301 return "TinyPly";
302}
303} // namespace IO
304} // namespace Ra
T any_of(T... args)
T clock(T... args)
T compare(T... args)
T count(T... args)
T data(T... args)
T emplace_back(T... args)
T endl(T... args)
T gbump(T... args)
T gptr(T... args)
T move(T... args)
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:3
STL namespace.
T replace(T... args)
T reserve(T... args)
T reset(T... args)
T resize(T... args)
T setg(T... args)
T size(T... args)
T to_string(T... args)