1 | /* |
2 | --------------------------------------------------------------------------- |
3 | Open Asset Import Library (assimp) |
4 | --------------------------------------------------------------------------- |
5 | |
6 | Copyright (c) 2006-2019, assimp team |
7 | |
8 | |
9 | |
10 | All rights reserved. |
11 | |
12 | Redistribution and use of this software in source and binary forms, |
13 | with or without modification, are permitted provided that the following |
14 | conditions are met: |
15 | |
16 | * Redistributions of source code must retain the above |
17 | copyright notice, this list of conditions and the |
18 | following disclaimer. |
19 | |
20 | * Redistributions in binary form must reproduce the above |
21 | copyright notice, this list of conditions and the |
22 | following disclaimer in the documentation and/or other |
23 | materials provided with the distribution. |
24 | |
25 | * Neither the name of the assimp team, nor the names of its |
26 | contributors may be used to endorse or promote products |
27 | derived from this software without specific prior |
28 | written permission of the assimp team. |
29 | |
30 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
31 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
32 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
33 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
34 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
35 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
36 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
37 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
38 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
39 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
40 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
41 | --------------------------------------------------------------------------- |
42 | */ |
43 | |
44 | /** @file BaseImporter.cpp |
45 | * @brief Implementation of BaseImporter |
46 | */ |
47 | |
48 | #include <assimp/BaseImporter.h> |
49 | #include <assimp/ParsingUtils.h> |
50 | #include "FileSystemFilter.h" |
51 | #include "Importer.h" |
52 | #include <assimp/ByteSwapper.h> |
53 | #include <assimp/scene.h> |
54 | #include <assimp/Importer.hpp> |
55 | #include <assimp/postprocess.h> |
56 | #include <assimp/importerdesc.h> |
57 | |
58 | #include <ios> |
59 | #include <list> |
60 | #include <memory> |
61 | #include <sstream> |
62 | #include <cctype> |
63 | |
64 | using namespace Assimp; |
65 | |
66 | // ------------------------------------------------------------------------------------------------ |
67 | // Constructor to be privately used by Importer |
68 | BaseImporter::BaseImporter() AI_NO_EXCEPT |
69 | : m_progress() { |
70 | // nothing to do here |
71 | } |
72 | |
73 | // ------------------------------------------------------------------------------------------------ |
74 | // Destructor, private as well |
75 | BaseImporter::~BaseImporter() { |
76 | // nothing to do here |
77 | } |
78 | |
79 | void BaseImporter::UpdateImporterScale( Importer* pImp ) |
80 | { |
81 | ai_assert(pImp != nullptr); |
82 | ai_assert(importerScale != 0.0); |
83 | ai_assert(fileScale != 0.0); |
84 | |
85 | double activeScale = importerScale * fileScale; |
86 | |
87 | // Set active scaling |
88 | pImp->SetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, fValue: static_cast<float>( activeScale) ); |
89 | |
90 | ASSIMP_LOG_DEBUG_F("UpdateImporterScale scale set: %f" , activeScale ); |
91 | } |
92 | |
93 | // ------------------------------------------------------------------------------------------------ |
94 | // Imports the given file and returns the imported data. |
95 | aiScene* BaseImporter::ReadFile(Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) { |
96 | |
97 | |
98 | m_progress = pImp->GetProgressHandler(); |
99 | if (nullptr == m_progress) { |
100 | return nullptr; |
101 | } |
102 | |
103 | ai_assert(m_progress); |
104 | |
105 | // Gather configuration properties for this run |
106 | SetupProperties( pImp ); |
107 | |
108 | // Construct a file system filter to improve our success ratio at reading external files |
109 | FileSystemFilter filter(pFile,pIOHandler); |
110 | |
111 | // create a scene object to hold the data |
112 | std::unique_ptr<aiScene> sc(new aiScene()); |
113 | |
114 | // dispatch importing |
115 | try |
116 | { |
117 | InternReadFile( pFile, pScene: sc.get(), pIOHandler: &filter); |
118 | |
119 | // Calculate import scale hook - required because pImp not available anywhere else |
120 | // passes scale into ScaleProcess |
121 | UpdateImporterScale(pImp); |
122 | |
123 | |
124 | } catch( const std::exception& err ) { |
125 | // extract error description |
126 | m_ErrorText = err.what(); |
127 | ASSIMP_LOG_ERROR(m_ErrorText); |
128 | return nullptr; |
129 | } |
130 | |
131 | // return what we gathered from the import. |
132 | return sc.release(); |
133 | } |
134 | |
135 | // ------------------------------------------------------------------------------------------------ |
136 | void BaseImporter::SetupProperties(const Importer* pImp) |
137 | { |
138 | // the default implementation does nothing |
139 | } |
140 | |
141 | // ------------------------------------------------------------------------------------------------ |
142 | void BaseImporter::GetExtensionList(std::set<std::string>& extensions) { |
143 | const aiImporterDesc* desc = GetInfo(); |
144 | ai_assert(desc != nullptr); |
145 | |
146 | const char* ext = desc->mFileExtensions; |
147 | ai_assert(ext != nullptr ); |
148 | |
149 | const char* last = ext; |
150 | do { |
151 | if (!*ext || *ext == ' ') { |
152 | extensions.insert(x: std::string(last,ext-last)); |
153 | ai_assert(ext-last > 0); |
154 | last = ext; |
155 | while(*last == ' ') { |
156 | ++last; |
157 | } |
158 | } |
159 | } |
160 | while(*ext++); |
161 | } |
162 | |
163 | // ------------------------------------------------------------------------------------------------ |
164 | /*static*/ bool BaseImporter::( IOSystem* pIOHandler, |
165 | const std::string& pFile, |
166 | const char** tokens, |
167 | unsigned int numTokens, |
168 | unsigned int searchBytes /* = 200 */, |
169 | bool tokensSol /* false */, |
170 | bool noAlphaBeforeTokens /* false */) |
171 | { |
172 | ai_assert( nullptr != tokens ); |
173 | ai_assert( 0 != numTokens ); |
174 | ai_assert( 0 != searchBytes); |
175 | |
176 | if ( nullptr == pIOHandler ) { |
177 | return false; |
178 | } |
179 | |
180 | std::unique_ptr<IOStream> pStream (pIOHandler->Open(pFile)); |
181 | if (pStream.get() ) { |
182 | // read 200 characters from the file |
183 | std::unique_ptr<char[]> _buffer (new char[searchBytes+1 /* for the '\0' */]); |
184 | char *buffer( _buffer.get() ); |
185 | const size_t read( pStream->Read(pvBuffer: buffer,pSize: 1,pCount: searchBytes) ); |
186 | if( 0 == read ) { |
187 | return false; |
188 | } |
189 | |
190 | for( size_t i = 0; i < read; ++i ) { |
191 | buffer[ i ] = static_cast<char>( ::tolower( c: buffer[ i ] ) ); |
192 | } |
193 | |
194 | // It is not a proper handling of unicode files here ... |
195 | // ehm ... but it works in most cases. |
196 | char* cur = buffer,*cur2 = buffer,*end = &buffer[read]; |
197 | while (cur != end) { |
198 | if( *cur ) { |
199 | *cur2++ = *cur; |
200 | } |
201 | ++cur; |
202 | } |
203 | *cur2 = '\0'; |
204 | |
205 | std::string token; |
206 | for (unsigned int i = 0; i < numTokens; ++i ) { |
207 | ai_assert( nullptr != tokens[i] ); |
208 | const size_t len( strlen( s: tokens[ i ] ) ); |
209 | token.clear(); |
210 | const char *ptr( tokens[ i ] ); |
211 | for ( size_t tokIdx = 0; tokIdx < len; ++tokIdx ) { |
212 | token.push_back( c: static_cast<char>( tolower( c: *ptr ) ) ); |
213 | ++ptr; |
214 | } |
215 | const char* r = strstr( haystack: buffer, needle: token.c_str() ); |
216 | if( !r ) { |
217 | continue; |
218 | } |
219 | // We need to make sure that we didn't accidentially identify the end of another token as our token, |
220 | // e.g. in a previous version the "gltf " present in some gltf files was detected as "f " |
221 | if (noAlphaBeforeTokens && (r != buffer && isalpha(r[-1]))) { |
222 | continue; |
223 | } |
224 | // We got a match, either we don't care where it is, or it happens to |
225 | // be in the beginning of the file / line |
226 | if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') { |
227 | ASSIMP_LOG_DEBUG_F( "Found positive match for header keyword: " , tokens[i] ); |
228 | return true; |
229 | } |
230 | } |
231 | } |
232 | |
233 | return false; |
234 | } |
235 | |
236 | // ------------------------------------------------------------------------------------------------ |
237 | // Simple check for file extension |
238 | /*static*/ bool BaseImporter::SimpleExtensionCheck (const std::string& pFile, |
239 | const char* ext0, |
240 | const char* ext1, |
241 | const char* ext2) |
242 | { |
243 | std::string::size_type pos = pFile.find_last_of(c: '.'); |
244 | |
245 | // no file extension - can't read |
246 | if( pos == std::string::npos) |
247 | return false; |
248 | |
249 | const char* ext_real = & pFile[ pos+1 ]; |
250 | if( !ASSIMP_stricmp(s1: ext_real,s2: ext0) ) |
251 | return true; |
252 | |
253 | // check for other, optional, file extensions |
254 | if (ext1 && !ASSIMP_stricmp(s1: ext_real,s2: ext1)) |
255 | return true; |
256 | |
257 | if (ext2 && !ASSIMP_stricmp(s1: ext_real,s2: ext2)) |
258 | return true; |
259 | |
260 | return false; |
261 | } |
262 | |
263 | // ------------------------------------------------------------------------------------------------ |
264 | // Get file extension from path |
265 | std::string BaseImporter::GetExtension( const std::string& file ) { |
266 | std::string::size_type pos = file.find_last_of(c: '.'); |
267 | |
268 | // no file extension at all |
269 | if (pos == std::string::npos) { |
270 | return "" ; |
271 | } |
272 | |
273 | |
274 | // thanks to Andy Maloney for the hint |
275 | std::string ret = file.substr( pos: pos + 1 ); |
276 | std::transform( first: ret.begin(), last: ret.end(), result: ret.begin(), unary_op: ToLower<char>); |
277 | |
278 | return ret; |
279 | } |
280 | |
281 | // ------------------------------------------------------------------------------------------------ |
282 | // Check for magic bytes at the beginning of the file. |
283 | /* static */ bool BaseImporter::CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile, |
284 | const void* _magic, unsigned int num, unsigned int offset, unsigned int size) |
285 | { |
286 | ai_assert( size <= 16 ); |
287 | ai_assert( _magic ); |
288 | |
289 | if (!pIOHandler) { |
290 | return false; |
291 | } |
292 | union { |
293 | const char* magic; |
294 | const uint16_t* magic_u16; |
295 | const uint32_t* magic_u32; |
296 | }; |
297 | magic = reinterpret_cast<const char*>(_magic); |
298 | std::unique_ptr<IOStream> pStream (pIOHandler->Open(pFile)); |
299 | if (pStream.get() ) { |
300 | |
301 | // skip to offset |
302 | pStream->Seek(pOffset: offset,pOrigin: aiOrigin_SET); |
303 | |
304 | // read 'size' characters from the file |
305 | union { |
306 | char data[16]; |
307 | uint16_t data_u16[8]; |
308 | uint32_t data_u32[4]; |
309 | }; |
310 | if(size != pStream->Read(pvBuffer: data,pSize: 1,pCount: size)) { |
311 | return false; |
312 | } |
313 | |
314 | for (unsigned int i = 0; i < num; ++i) { |
315 | // also check against big endian versions of tokens with size 2,4 |
316 | // that's just for convenience, the chance that we cause conflicts |
317 | // is quite low and it can save some lines and prevent nasty bugs |
318 | if (2 == size) { |
319 | uint16_t rev = *magic_u16; |
320 | ByteSwap::Swap(fOut: &rev); |
321 | if (data_u16[0] == *magic_u16 || data_u16[0] == rev) { |
322 | return true; |
323 | } |
324 | } |
325 | else if (4 == size) { |
326 | uint32_t rev = *magic_u32; |
327 | ByteSwap::Swap(fOut: &rev); |
328 | if (data_u32[0] == *magic_u32 || data_u32[0] == rev) { |
329 | return true; |
330 | } |
331 | } |
332 | else { |
333 | // any length ... just compare |
334 | if(!memcmp(s1: magic,s2: data,n: size)) { |
335 | return true; |
336 | } |
337 | } |
338 | magic += size; |
339 | } |
340 | } |
341 | return false; |
342 | } |
343 | |
344 | #ifdef ASSIMP_USE_HUNTER |
345 | # include <utf8/utf8.h> |
346 | #else |
347 | # include "../contrib/utf8cpp/source/utf8.h" |
348 | #endif |
349 | |
350 | // ------------------------------------------------------------------------------------------------ |
351 | // Convert to UTF8 data |
352 | void BaseImporter::ConvertToUTF8(std::vector<char>& data) |
353 | { |
354 | //ConversionResult result; |
355 | if(data.size() < 8) { |
356 | throw DeadlyImportError("File is too small" ); |
357 | } |
358 | |
359 | // UTF 8 with BOM |
360 | if((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) { |
361 | ASSIMP_LOG_DEBUG("Found UTF-8 BOM ..." ); |
362 | |
363 | std::copy(first: data.begin()+3,last: data.end(),result: data.begin()); |
364 | data.resize(new_size: data.size()-3); |
365 | return; |
366 | } |
367 | |
368 | |
369 | // UTF 32 BE with BOM |
370 | if(*((uint32_t*)&data.front()) == 0xFFFE0000) { |
371 | |
372 | // swap the endianness .. |
373 | for(uint32_t* p = (uint32_t*)&data.front(), *end = (uint32_t*)&data.back(); p <= end; ++p) { |
374 | AI_SWAP4P(p); |
375 | } |
376 | } |
377 | |
378 | // UTF 32 LE with BOM |
379 | if(*((uint32_t*)&data.front()) == 0x0000FFFE) { |
380 | ASSIMP_LOG_DEBUG("Found UTF-32 BOM ..." ); |
381 | |
382 | std::vector<char> output; |
383 | int *ptr = (int*)&data[ 0 ]; |
384 | int *end = ptr + ( data.size() / sizeof(int) ) +1; |
385 | utf8::utf32to8( start: ptr, end, result: back_inserter(x&: output)); |
386 | return; |
387 | } |
388 | |
389 | // UTF 16 BE with BOM |
390 | if(*((uint16_t*)&data.front()) == 0xFFFE) { |
391 | |
392 | // swap the endianness .. |
393 | for(uint16_t* p = (uint16_t*)&data.front(), *end = (uint16_t*)&data.back(); p <= end; ++p) { |
394 | ByteSwap::Swap2(szOut: p); |
395 | } |
396 | } |
397 | |
398 | // UTF 16 LE with BOM |
399 | if(*((uint16_t*)&data.front()) == 0xFEFF) { |
400 | ASSIMP_LOG_DEBUG("Found UTF-16 BOM ..." ); |
401 | |
402 | std::vector<unsigned char> output; |
403 | utf8::utf16to8(start: data.begin(), end: data.end(), result: back_inserter(x&: output)); |
404 | return; |
405 | } |
406 | } |
407 | |
408 | // ------------------------------------------------------------------------------------------------ |
409 | // Convert to UTF8 data to ISO-8859-1 |
410 | void BaseImporter::ConvertUTF8toISO8859_1(std::string& data) |
411 | { |
412 | size_t size = data.size(); |
413 | size_t i = 0, j = 0; |
414 | |
415 | while(i < size) { |
416 | if ((unsigned char) data[i] < (size_t) 0x80) { |
417 | data[j] = data[i]; |
418 | } else if(i < size - 1) { |
419 | if((unsigned char) data[i] == 0xC2) { |
420 | data[j] = data[++i]; |
421 | } else if((unsigned char) data[i] == 0xC3) { |
422 | data[j] = ((unsigned char) data[++i] + 0x40); |
423 | } else { |
424 | std::stringstream stream; |
425 | stream << "UTF8 code " << std::hex << data[i] << data[i + 1] << " can not be converted into ISA-8859-1." ; |
426 | ASSIMP_LOG_ERROR( stream.str() ); |
427 | |
428 | data[j++] = data[i++]; |
429 | data[j] = data[i]; |
430 | } |
431 | } else { |
432 | ASSIMP_LOG_ERROR("UTF8 code but only one character remaining" ); |
433 | |
434 | data[j] = data[i]; |
435 | } |
436 | |
437 | i++; j++; |
438 | } |
439 | |
440 | data.resize(n: j); |
441 | } |
442 | |
443 | // ------------------------------------------------------------------------------------------------ |
444 | void BaseImporter::TextFileToBuffer(IOStream* stream, |
445 | std::vector<char>& data, |
446 | TextFileMode mode) |
447 | { |
448 | ai_assert(nullptr != stream); |
449 | |
450 | const size_t fileSize = stream->FileSize(); |
451 | if (mode == FORBID_EMPTY) { |
452 | if(!fileSize) { |
453 | throw DeadlyImportError("File is empty" ); |
454 | } |
455 | } |
456 | |
457 | data.reserve(n: fileSize+1); |
458 | data.resize(new_size: fileSize); |
459 | if(fileSize > 0) { |
460 | if(fileSize != stream->Read( pvBuffer: &data[0], pSize: 1, pCount: fileSize)) { |
461 | throw DeadlyImportError("File read error" ); |
462 | } |
463 | |
464 | ConvertToUTF8(data); |
465 | } |
466 | |
467 | // append a binary zero to simplify string parsing |
468 | data.push_back(x: 0); |
469 | } |
470 | |
471 | // ------------------------------------------------------------------------------------------------ |
472 | namespace Assimp { |
473 | // Represents an import request |
474 | struct LoadRequest { |
475 | LoadRequest(const std::string& _file, unsigned int _flags,const BatchLoader::PropertyMap* _map, unsigned int _id) |
476 | : file(_file) |
477 | , flags(_flags) |
478 | , refCnt(1) |
479 | , scene(NULL) |
480 | , loaded(false) |
481 | , id(_id) { |
482 | if ( _map ) { |
483 | map = *_map; |
484 | } |
485 | } |
486 | |
487 | bool operator== ( const std::string& f ) const { |
488 | return file == f; |
489 | } |
490 | |
491 | const std::string file; |
492 | unsigned int flags; |
493 | unsigned int refCnt; |
494 | aiScene *scene; |
495 | bool loaded; |
496 | BatchLoader::PropertyMap map; |
497 | unsigned int id; |
498 | }; |
499 | } |
500 | |
501 | // ------------------------------------------------------------------------------------------------ |
502 | // BatchLoader::pimpl data structure |
503 | struct Assimp::BatchData { |
504 | BatchData( IOSystem* pIO, bool validate ) |
505 | : pIOSystem( pIO ) |
506 | , pImporter( nullptr ) |
507 | , next_id(0xffff) |
508 | , validate( validate ) { |
509 | ai_assert( nullptr != pIO ); |
510 | |
511 | pImporter = new Importer(); |
512 | pImporter->SetIOHandler( pIO ); |
513 | } |
514 | |
515 | ~BatchData() { |
516 | pImporter->SetIOHandler( nullptr ); /* get pointer back into our possession */ |
517 | delete pImporter; |
518 | } |
519 | |
520 | // IO system to be used for all imports |
521 | IOSystem* pIOSystem; |
522 | |
523 | // Importer used to load all meshes |
524 | Importer* pImporter; |
525 | |
526 | // List of all imports |
527 | std::list<LoadRequest> requests; |
528 | |
529 | // Base path |
530 | std::string pathBase; |
531 | |
532 | // Id for next item |
533 | unsigned int next_id; |
534 | |
535 | // Validation enabled state |
536 | bool validate; |
537 | }; |
538 | |
539 | typedef std::list<LoadRequest>::iterator LoadReqIt; |
540 | |
541 | // ------------------------------------------------------------------------------------------------ |
542 | BatchLoader::BatchLoader(IOSystem* pIO, bool validate ) { |
543 | ai_assert(nullptr != pIO); |
544 | |
545 | m_data = new BatchData( pIO, validate ); |
546 | } |
547 | |
548 | // ------------------------------------------------------------------------------------------------ |
549 | BatchLoader::~BatchLoader() |
550 | { |
551 | // delete all scenes what have not been polled by the user |
552 | for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) { |
553 | delete (*it).scene; |
554 | } |
555 | delete m_data; |
556 | } |
557 | |
558 | // ------------------------------------------------------------------------------------------------ |
559 | void BatchLoader::setValidation( bool enabled ) { |
560 | m_data->validate = enabled; |
561 | } |
562 | |
563 | // ------------------------------------------------------------------------------------------------ |
564 | bool BatchLoader::getValidation() const { |
565 | return m_data->validate; |
566 | } |
567 | |
568 | // ------------------------------------------------------------------------------------------------ |
569 | unsigned int BatchLoader::AddLoadRequest(const std::string& file, |
570 | unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/) |
571 | { |
572 | ai_assert(!file.empty()); |
573 | |
574 | // check whether we have this loading request already |
575 | for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) { |
576 | // Call IOSystem's path comparison function here |
577 | if ( m_data->pIOSystem->ComparePaths(one: (*it).file,second: file)) { |
578 | if (map) { |
579 | if ( !( ( *it ).map == *map ) ) { |
580 | continue; |
581 | } |
582 | } |
583 | else if ( !( *it ).map.empty() ) { |
584 | continue; |
585 | } |
586 | |
587 | (*it).refCnt++; |
588 | return (*it).id; |
589 | } |
590 | } |
591 | |
592 | // no, we don't have it. So add it to the queue ... |
593 | m_data->requests.push_back(x: LoadRequest(file,steps,map, m_data->next_id)); |
594 | return m_data->next_id++; |
595 | } |
596 | |
597 | // ------------------------------------------------------------------------------------------------ |
598 | aiScene* BatchLoader::GetImport( unsigned int which ) |
599 | { |
600 | for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) { |
601 | if ((*it).id == which && (*it).loaded) { |
602 | aiScene* sc = (*it).scene; |
603 | if (!(--(*it).refCnt)) { |
604 | m_data->requests.erase(position: it); |
605 | } |
606 | return sc; |
607 | } |
608 | } |
609 | return nullptr; |
610 | } |
611 | |
612 | |
613 | |
614 | // ------------------------------------------------------------------------------------------------ |
615 | void BatchLoader::LoadAll() |
616 | { |
617 | // no threaded implementation for the moment |
618 | for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) { |
619 | // force validation in debug builds |
620 | unsigned int pp = (*it).flags; |
621 | if ( m_data->validate ) { |
622 | pp |= aiProcess_ValidateDataStructure; |
623 | } |
624 | |
625 | // setup config properties if necessary |
626 | ImporterPimpl* pimpl = m_data->pImporter->Pimpl(); |
627 | pimpl->mFloatProperties = (*it).map.floats; |
628 | pimpl->mIntProperties = (*it).map.ints; |
629 | pimpl->mStringProperties = (*it).map.strings; |
630 | pimpl->mMatrixProperties = (*it).map.matrices; |
631 | |
632 | if (!DefaultLogger::isNullLogger()) |
633 | { |
634 | ASSIMP_LOG_INFO("%%% BEGIN EXTERNAL FILE %%%" ); |
635 | ASSIMP_LOG_INFO_F("File: " , (*it).file); |
636 | } |
637 | m_data->pImporter->ReadFile(pFile: (*it).file,pFlags: pp); |
638 | (*it).scene = m_data->pImporter->GetOrphanedScene(); |
639 | (*it).loaded = true; |
640 | |
641 | ASSIMP_LOG_INFO("%%% END EXTERNAL FILE %%%" ); |
642 | } |
643 | } |
644 | |