1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5
6 Copyright (c) 2006-2019, assimp team
7
8 All rights reserved.
9
10 Redistribution and use of this software in source and binary forms,
11 with or without modification, are permitted provided that the following
12 conditions are met:
13
14 * Redistributions of source code must retain the above
15 copyright notice, this list of conditions and the
16 following disclaimer.
17
18 * Redistributions in binary form must reproduce the above
19 copyright notice, this list of conditions and the
20 following disclaimer in the documentation and/or other
21 materials provided with the distribution.
22
23 * Neither the name of the assimp team, nor the names of its
24 contributors may be used to endorse or promote products
25 derived from this software without specific prior
26 written permission of the assimp team.
27
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 ---------------------------------------------------------------------------
40 */
41
42 /** @file Importer.cpp
43 * @brief Implementation of the CPP-API class #Importer
44 */
45
46 #include <assimp/version.h>
47 #include <assimp/config.h>
48 #include <assimp/importerdesc.h>
49
50 // ------------------------------------------------------------------------------------------------
51 /* Uncomment this line to prevent Assimp from catching unknown exceptions.
52 *
53 * Note that any Exception except DeadlyImportError may lead to
54 * undefined behaviour -> loaders could remain in an unusable state and
55 * further imports with the same Importer instance could fail/crash/burn ...
56 */
57 // ------------------------------------------------------------------------------------------------
58 #ifndef ASSIMP_BUILD_DEBUG
59 # define ASSIMP_CATCH_GLOBAL_EXCEPTIONS
60 #endif
61
62 // ------------------------------------------------------------------------------------------------
63 // Internal headers
64 // ------------------------------------------------------------------------------------------------
65 #include "Common/Importer.h"
66 #include "Common/BaseProcess.h"
67 #include "Common/DefaultProgressHandler.h"
68 #include "PostProcessing/ProcessHelper.h"
69 #include "Common/ScenePreprocessor.h"
70 #include "Common/ScenePrivate.h"
71
72 #include <assimp/BaseImporter.h>
73 #include <assimp/GenericProperty.h>
74 #include <assimp/MemoryIOWrapper.h>
75 #include <assimp/Profiler.h>
76 #include <assimp/TinyFormatter.h>
77 #include <assimp/Exceptional.h>
78 #include <assimp/Profiler.h>
79 #include <assimp/commonMetaData.h>
80
81 #include <set>
82 #include <memory>
83 #include <cctype>
84
85 #include <assimp/DefaultIOStream.h>
86 #include <assimp/DefaultIOSystem.h>
87
88 #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
89 # include "PostProcessing/ValidateDataStructure.h"
90 #endif
91
92 using namespace Assimp::Profiling;
93 using namespace Assimp::Formatter;
94
95 namespace Assimp {
96 // ImporterRegistry.cpp
97 void GetImporterInstanceList(std::vector< BaseImporter* >& out);
98 void DeleteImporterInstanceList(std::vector< BaseImporter* >& out);
99
100 // PostStepRegistry.cpp
101 void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out);
102 }
103
104 using namespace Assimp;
105 //using namespace Assimp::Intern;
106
107 /*
108 // ------------------------------------------------------------------------------------------------
109 // Intern::AllocateFromAssimpHeap serves as abstract base class. It overrides
110 // new and delete (and their array counterparts) of public API classes (e.g. Logger) to
111 // utilize our DLL heap.
112 // See http://www.gotw.ca/publications/mill15.htm
113 // ------------------------------------------------------------------------------------------------
114 void* AllocateFromAssimpHeap::operator new ( size_t num_bytes) {
115 return ::operator new(num_bytes);
116 }
117
118 void* AllocateFromAssimpHeap::operator new ( size_t num_bytes, const std::nothrow_t& ) throw() {
119 try {
120 return AllocateFromAssimpHeap::operator new( num_bytes );
121 }
122 catch( ... ) {
123 return nullptr;
124 }
125 }
126
127 void AllocateFromAssimpHeap::operator delete ( void* data) {
128 return ::operator delete(data);
129 }
130
131 void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes) {
132 return ::operator new[](num_bytes);
133 }
134
135 void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes, const std::nothrow_t& ) throw() {
136 try {
137 return AllocateFromAssimpHeap::operator new[]( num_bytes );
138 } catch( ... ) {
139 return nullptr;
140 }
141 }
142
143 void AllocateFromAssimpHeap::operator delete[] ( void* data) {
144 return ::operator delete[](data);
145 }
146 */
147 // ------------------------------------------------------------------------------------------------
148 // Importer constructor.
Importer()149 Importer::Importer()
150 : pimpl( new ImporterPimpl ) {
151 pimpl->mScene = nullptr;
152 pimpl->mErrorString = "";
153
154 // Allocate a default IO handler
155 pimpl->mIOHandler = new DefaultIOSystem;
156 pimpl->mIsDefaultHandler = true;
157 pimpl->bExtraVerbose = false; // disable extra verbose mode by default
158
159 pimpl->mProgressHandler = new DefaultProgressHandler();
160 pimpl->mIsDefaultProgressHandler = true;
161
162 GetImporterInstanceList(pimpl->mImporter);
163 GetPostProcessingStepInstanceList(pimpl->mPostProcessingSteps);
164
165 // Allocate a SharedPostProcessInfo object and store pointers to it in all post-process steps in the list.
166 pimpl->mPPShared = new SharedPostProcessInfo();
167 for (std::vector<BaseProcess*>::iterator it = pimpl->mPostProcessingSteps.begin();
168 it != pimpl->mPostProcessingSteps.end();
169 ++it) {
170
171 (*it)->SetSharedData(pimpl->mPPShared);
172 }
173 }
174
175 // ------------------------------------------------------------------------------------------------
176 // Destructor of Importer
~Importer()177 Importer::~Importer() {
178 // Delete all import plugins
179 DeleteImporterInstanceList(pimpl->mImporter);
180
181 // Delete all post-processing plug-ins
182 for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); ++a ) {
183 delete pimpl->mPostProcessingSteps[a];
184 }
185
186 // Delete the assigned IO and progress handler
187 delete pimpl->mIOHandler;
188 delete pimpl->mProgressHandler;
189
190 // Kill imported scene. Destructor's should do that recursively
191 delete pimpl->mScene;
192
193 // Delete shared post-processing data
194 delete pimpl->mPPShared;
195
196 // and finally the pimpl itself
197 delete pimpl;
198 }
199
200 // ------------------------------------------------------------------------------------------------
201 // Register a custom post-processing step
RegisterPPStep(BaseProcess * pImp)202 aiReturn Importer::RegisterPPStep(BaseProcess* pImp) {
203 ai_assert( nullptr != pImp );
204
205 ASSIMP_BEGIN_EXCEPTION_REGION();
206
207 pimpl->mPostProcessingSteps.push_back(pImp);
208 ASSIMP_LOG_INFO("Registering custom post-processing step");
209
210 ASSIMP_END_EXCEPTION_REGION(aiReturn);
211 return AI_SUCCESS;
212 }
213
214 // ------------------------------------------------------------------------------------------------
215 // Register a custom loader plugin
RegisterLoader(BaseImporter * pImp)216 aiReturn Importer::RegisterLoader(BaseImporter* pImp) {
217 ai_assert(nullptr != pImp);
218
219 ASSIMP_BEGIN_EXCEPTION_REGION();
220
221 // --------------------------------------------------------------------
222 // Check whether we would have two loaders for the same file extension
223 // This is absolutely OK, but we should warn the developer of the new
224 // loader that his code will probably never be called if the first
225 // loader is a bit too lazy in his file checking.
226 // --------------------------------------------------------------------
227 std::set<std::string> st;
228 std::string baked;
229 pImp->GetExtensionList(st);
230
231 for(std::set<std::string>::const_iterator it = st.begin(); it != st.end(); ++it) {
232
233 #ifdef ASSIMP_BUILD_DEBUG
234 if (IsExtensionSupported(*it)) {
235 ASSIMP_LOG_WARN_F("The file extension ", *it, " is already in use");
236 }
237 #endif
238 baked += *it;
239 }
240
241 // add the loader
242 pimpl->mImporter.push_back(pImp);
243 ASSIMP_LOG_INFO_F("Registering custom importer for these file extensions: ", baked);
244 ASSIMP_END_EXCEPTION_REGION(aiReturn);
245
246 return AI_SUCCESS;
247 }
248
249 // ------------------------------------------------------------------------------------------------
250 // Unregister a custom loader plugin
UnregisterLoader(BaseImporter * pImp)251 aiReturn Importer::UnregisterLoader(BaseImporter* pImp) {
252 if(!pImp) {
253 // unregistering a NULL importer is no problem for us ... really!
254 return AI_SUCCESS;
255 }
256
257 ASSIMP_BEGIN_EXCEPTION_REGION();
258 std::vector<BaseImporter*>::iterator it = std::find(pimpl->mImporter.begin(),
259 pimpl->mImporter.end(),pImp);
260
261 if (it != pimpl->mImporter.end()) {
262 pimpl->mImporter.erase(it);
263 ASSIMP_LOG_INFO("Unregistering custom importer: ");
264 return AI_SUCCESS;
265 }
266 ASSIMP_LOG_WARN("Unable to remove custom importer: I can't find you ...");
267 ASSIMP_END_EXCEPTION_REGION(aiReturn);
268
269 return AI_FAILURE;
270 }
271
272 // ------------------------------------------------------------------------------------------------
273 // Unregister a custom loader plugin
UnregisterPPStep(BaseProcess * pImp)274 aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) {
275 if(!pImp) {
276 // unregistering a NULL ppstep is no problem for us ... really!
277 return AI_SUCCESS;
278 }
279
280 ASSIMP_BEGIN_EXCEPTION_REGION();
281 std::vector<BaseProcess*>::iterator it = std::find(pimpl->mPostProcessingSteps.begin(),
282 pimpl->mPostProcessingSteps.end(),pImp);
283
284 if (it != pimpl->mPostProcessingSteps.end()) {
285 pimpl->mPostProcessingSteps.erase(it);
286 ASSIMP_LOG_INFO("Unregistering custom post-processing step");
287 return AI_SUCCESS;
288 }
289 ASSIMP_LOG_WARN("Unable to remove custom post-processing step: I can't find you ..");
290 ASSIMP_END_EXCEPTION_REGION(aiReturn);
291
292 return AI_FAILURE;
293 }
294
295 // ------------------------------------------------------------------------------------------------
296 // Supplies a custom IO handler to the importer to open and access files.
SetIOHandler(IOSystem * pIOHandler)297 void Importer::SetIOHandler( IOSystem* pIOHandler) {
298 ai_assert(nullptr != pimpl);
299
300 ASSIMP_BEGIN_EXCEPTION_REGION();
301 // If the new handler is zero, allocate a default IO implementation.
302 if (!pIOHandler) {
303 // Release pointer in the possession of the caller
304 pimpl->mIOHandler = new DefaultIOSystem();
305 pimpl->mIsDefaultHandler = true;
306 } else if (pimpl->mIOHandler != pIOHandler) { // Otherwise register the custom handler
307 delete pimpl->mIOHandler;
308 pimpl->mIOHandler = pIOHandler;
309 pimpl->mIsDefaultHandler = false;
310 }
311 ASSIMP_END_EXCEPTION_REGION(void);
312 }
313
314 // ------------------------------------------------------------------------------------------------
315 // Get the currently set IO handler
GetIOHandler() const316 IOSystem* Importer::GetIOHandler() const {
317 ai_assert(nullptr != pimpl);
318
319 return pimpl->mIOHandler;
320 }
321
322 // ------------------------------------------------------------------------------------------------
323 // Check whether a custom IO handler is currently set
IsDefaultIOHandler() const324 bool Importer::IsDefaultIOHandler() const {
325 ai_assert(nullptr != pimpl);
326
327 return pimpl->mIsDefaultHandler;
328 }
329
330 // ------------------------------------------------------------------------------------------------
331 // Supplies a custom progress handler to get regular callbacks during importing
SetProgressHandler(ProgressHandler * pHandler)332 void Importer::SetProgressHandler ( ProgressHandler* pHandler ) {
333 ai_assert(nullptr != pimpl);
334
335 ASSIMP_BEGIN_EXCEPTION_REGION();
336
337 // If the new handler is zero, allocate a default implementation.
338 if (!pHandler) {
339 // Release pointer in the possession of the caller
340 pimpl->mProgressHandler = new DefaultProgressHandler();
341 pimpl->mIsDefaultProgressHandler = true;
342 } else if (pimpl->mProgressHandler != pHandler) { // Otherwise register the custom handler
343 delete pimpl->mProgressHandler;
344 pimpl->mProgressHandler = pHandler;
345 pimpl->mIsDefaultProgressHandler = false;
346 }
347 ASSIMP_END_EXCEPTION_REGION(void);
348 }
349
350 // ------------------------------------------------------------------------------------------------
351 // Get the currently set progress handler
GetProgressHandler() const352 ProgressHandler* Importer::GetProgressHandler() const {
353 ai_assert(nullptr != pimpl);
354
355 return pimpl->mProgressHandler;
356 }
357
358 // ------------------------------------------------------------------------------------------------
359 // Check whether a custom progress handler is currently set
IsDefaultProgressHandler() const360 bool Importer::IsDefaultProgressHandler() const {
361 ai_assert(nullptr != pimpl);
362
363 return pimpl->mIsDefaultProgressHandler;
364 }
365
366 // ------------------------------------------------------------------------------------------------
367 // Validate post process step flags
_ValidateFlags(unsigned int pFlags)368 bool _ValidateFlags(unsigned int pFlags) {
369 if (pFlags & aiProcess_GenSmoothNormals && pFlags & aiProcess_GenNormals) {
370 ASSIMP_LOG_ERROR("#aiProcess_GenSmoothNormals and #aiProcess_GenNormals are incompatible");
371 return false;
372 }
373 if (pFlags & aiProcess_OptimizeGraph && pFlags & aiProcess_PreTransformVertices) {
374 ASSIMP_LOG_ERROR("#aiProcess_OptimizeGraph and #aiProcess_PreTransformVertices are incompatible");
375 return false;
376 }
377 return true;
378 }
379
380 // ------------------------------------------------------------------------------------------------
381 // Free the current scene
FreeScene()382 void Importer::FreeScene( ) {
383 ai_assert(nullptr != pimpl);
384
385 ASSIMP_BEGIN_EXCEPTION_REGION();
386
387 delete pimpl->mScene;
388 pimpl->mScene = nullptr;
389
390 pimpl->mErrorString = "";
391 ASSIMP_END_EXCEPTION_REGION(void);
392 }
393
394 // ------------------------------------------------------------------------------------------------
395 // Get the current error string, if any
GetErrorString() const396 const char* Importer::GetErrorString() const {
397 ai_assert(nullptr != pimpl);
398
399 // Must remain valid as long as ReadFile() or FreeFile() are not called
400 return pimpl->mErrorString.c_str();
401 }
402
403 // ------------------------------------------------------------------------------------------------
404 // Enable extra-verbose mode
SetExtraVerbose(bool bDo)405 void Importer::SetExtraVerbose(bool bDo) {
406 ai_assert(nullptr != pimpl);
407
408 pimpl->bExtraVerbose = bDo;
409 }
410
411 // ------------------------------------------------------------------------------------------------
412 // Get the current scene
GetScene() const413 const aiScene* Importer::GetScene() const {
414 ai_assert(nullptr != pimpl);
415
416 return pimpl->mScene;
417 }
418
419 // ------------------------------------------------------------------------------------------------
420 // Orphan the current scene and return it.
GetOrphanedScene()421 aiScene* Importer::GetOrphanedScene() {
422 ai_assert(nullptr != pimpl);
423
424 aiScene* s = pimpl->mScene;
425
426 ASSIMP_BEGIN_EXCEPTION_REGION();
427 pimpl->mScene = nullptr;
428
429 pimpl->mErrorString = ""; // reset error string
430 ASSIMP_END_EXCEPTION_REGION(aiScene*);
431
432 return s;
433 }
434
435 // ------------------------------------------------------------------------------------------------
436 // Validate post-processing flags
ValidateFlags(unsigned int pFlags) const437 bool Importer::ValidateFlags(unsigned int pFlags) const {
438 ASSIMP_BEGIN_EXCEPTION_REGION();
439 // run basic checks for mutually exclusive flags
440 if(!_ValidateFlags(pFlags)) {
441 return false;
442 }
443
444 // ValidateDS does not anymore occur in the pp list, it plays an awesome extra role ...
445 #ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
446 if (pFlags & aiProcess_ValidateDataStructure) {
447 return false;
448 }
449 #endif
450 pFlags &= ~aiProcess_ValidateDataStructure;
451
452 // Now iterate through all bits which are set in the flags and check whether we find at least
453 // one pp plugin which handles it.
454 for (unsigned int mask = 1; mask < (1u << (sizeof(unsigned int)*8-1));mask <<= 1) {
455
456 if (pFlags & mask) {
457
458 bool have = false;
459 for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
460 if (pimpl->mPostProcessingSteps[a]-> IsActive(mask) ) {
461
462 have = true;
463 break;
464 }
465 }
466 if (!have) {
467 return false;
468 }
469 }
470 }
471 ASSIMP_END_EXCEPTION_REGION(bool);
472 return true;
473 }
474
475 // ------------------------------------------------------------------------------------------------
ReadFileFromMemory(const void * pBuffer,size_t pLength,unsigned int pFlags,const char * pHint)476 const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
477 size_t pLength,
478 unsigned int pFlags,
479 const char* pHint /*= ""*/) {
480 ai_assert(nullptr != pimpl);
481
482 ASSIMP_BEGIN_EXCEPTION_REGION();
483 if (!pHint) {
484 pHint = "";
485 }
486
487 if (!pBuffer || !pLength || strlen(pHint) > MaxLenHint ) {
488 pimpl->mErrorString = "Invalid parameters passed to ReadFileFromMemory()";
489 return nullptr;
490 }
491
492 // prevent deletion of the previous IOHandler
493 IOSystem* io = pimpl->mIOHandler;
494 pimpl->mIOHandler = nullptr;
495
496 SetIOHandler(new MemoryIOSystem((const uint8_t*)pBuffer,pLength,io));
497
498 // read the file and recover the previous IOSystem
499 static const size_t BufSize(Importer::MaxLenHint + 28);
500 char fbuff[BufSize];
501 ai_snprintf(fbuff, BufSize, "%s.%s",AI_MEMORYIO_MAGIC_FILENAME,pHint);
502
503 ReadFile(fbuff,pFlags);
504 SetIOHandler(io);
505
506 ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString);
507 return pimpl->mScene;
508 }
509
510 // ------------------------------------------------------------------------------------------------
WriteLogOpening(const std::string & file)511 void WriteLogOpening(const std::string& file) {
512
513 ASSIMP_LOG_INFO_F("Load ", file);
514
515 // print a full version dump. This is nice because we don't
516 // need to ask the authors of incoming bug reports for
517 // the library version they're using - a log dump is
518 // sufficient.
519 const unsigned int flags( aiGetCompileFlags() );
520 std::stringstream stream;
521 stream << "Assimp " << aiGetVersionMajor() << "." << aiGetVersionMinor() << "." << aiGetVersionRevision() << " "
522 #if defined(ASSIMP_BUILD_ARCHITECTURE)
523 << ASSIMP_BUILD_ARCHITECTURE
524 #elif defined(_M_IX86) || defined(__x86_32__) || defined(__i386__)
525 << "x86"
526 #elif defined(_M_X64) || defined(__x86_64__)
527 << "amd64"
528 #elif defined(_M_IA64) || defined(__ia64__)
529 << "itanium"
530 #elif defined(__ppc__) || defined(__powerpc__)
531 << "ppc32"
532 #elif defined(__powerpc64__)
533 << "ppc64"
534 #elif defined(__arm__)
535 << "arm"
536 #else
537 << "<unknown architecture>"
538 #endif
539 << " "
540 #if defined(ASSIMP_BUILD_COMPILER)
541 << ( ASSIMP_BUILD_COMPILER )
542 #elif defined(_MSC_VER)
543 << "msvc"
544 #elif defined(__GNUC__)
545 << "gcc"
546 #else
547 << "<unknown compiler>"
548 #endif
549
550 #ifdef ASSIMP_BUILD_DEBUG
551 << " debug"
552 #endif
553
554 << (flags & ASSIMP_CFLAGS_NOBOOST ? " noboost" : "")
555 << (flags & ASSIMP_CFLAGS_SHARED ? " shared" : "")
556 << (flags & ASSIMP_CFLAGS_SINGLETHREADED ? " singlethreaded" : "");
557
558 ASSIMP_LOG_DEBUG(stream.str());
559 }
560
561 // ------------------------------------------------------------------------------------------------
562 // Reads the given file and returns its contents if successful.
ReadFile(const char * _pFile,unsigned int pFlags)563 const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) {
564 ai_assert(nullptr != pimpl);
565
566 ASSIMP_BEGIN_EXCEPTION_REGION();
567 const std::string pFile(_pFile);
568
569 // ----------------------------------------------------------------------
570 // Put a large try block around everything to catch all std::exception's
571 // that might be thrown by STL containers or by new().
572 // ImportErrorException's are throw by ourselves and caught elsewhere.
573 //-----------------------------------------------------------------------
574
575 WriteLogOpening(pFile);
576
577 #ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
578 try
579 #endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
580 {
581 // Check whether this Importer instance has already loaded
582 // a scene. In this case we need to delete the old one
583 if (pimpl->mScene) {
584
585 ASSIMP_LOG_DEBUG("(Deleting previous scene)");
586 FreeScene();
587 }
588
589 // First check if the file is accessible at all
590 if( !pimpl->mIOHandler->Exists( pFile)) {
591
592 pimpl->mErrorString = "Unable to open file \"" + pFile + "\".";
593 ASSIMP_LOG_ERROR(pimpl->mErrorString);
594 return nullptr;
595 }
596
597 std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL);
598 if (profiler) {
599 profiler->BeginRegion("total");
600 }
601
602 // Find an worker class which can handle the file
603 BaseImporter* imp = nullptr;
604 SetPropertyInteger("importerIndex", -1);
605 for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) {
606
607 if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, false)) {
608 imp = pimpl->mImporter[a];
609 SetPropertyInteger("importerIndex", a);
610 break;
611 }
612 }
613
614 if (!imp) {
615 // not so bad yet ... try format auto detection.
616 const std::string::size_type s = pFile.find_last_of('.');
617 if (s != std::string::npos) {
618 ASSIMP_LOG_INFO("File extension not known, trying signature-based detection");
619 for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) {
620 if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) {
621 imp = pimpl->mImporter[a];
622 SetPropertyInteger("importerIndex", a);
623 break;
624 }
625 }
626 }
627 // Put a proper error message if no suitable importer was found
628 if( !imp) {
629 pimpl->mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\".";
630 ASSIMP_LOG_ERROR(pimpl->mErrorString);
631 return nullptr;
632 }
633 }
634
635 // Get file size for progress handler
636 IOStream * fileIO = pimpl->mIOHandler->Open( pFile );
637 uint32_t fileSize = 0;
638 if (fileIO)
639 {
640 fileSize = static_cast<uint32_t>(fileIO->FileSize());
641 pimpl->mIOHandler->Close( fileIO );
642 }
643
644 // Dispatch the reading to the worker class for this format
645 const aiImporterDesc *desc( imp->GetInfo() );
646 std::string ext( "unknown" );
647 if ( nullptr != desc ) {
648 ext = desc->mName;
649 }
650 ASSIMP_LOG_INFO("Found a matching importer for this file format: " + ext + "." );
651 pimpl->mProgressHandler->UpdateFileRead( 0, fileSize );
652
653 if (profiler) {
654 profiler->BeginRegion("import");
655 }
656
657 pimpl->mScene = imp->ReadFile( this, pFile, pimpl->mIOHandler);
658 pimpl->mProgressHandler->UpdateFileRead( fileSize, fileSize );
659
660 if (profiler) {
661 profiler->EndRegion("import");
662 }
663
664 SetPropertyString("sourceFilePath", pFile);
665
666 // If successful, apply all active post processing steps to the imported data
667 if( pimpl->mScene) {
668 if (!pimpl->mScene->mMetaData || !pimpl->mScene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT)) {
669 if (!pimpl->mScene->mMetaData) {
670 pimpl->mScene->mMetaData = new aiMetadata;
671 }
672 pimpl->mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT, aiString(ext));
673 }
674
675 #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
676 // The ValidateDS process is an exception. It is executed first, even before ScenePreprocessor is called.
677 if (pFlags & aiProcess_ValidateDataStructure) {
678 ValidateDSProcess ds;
679 ds.ExecuteOnScene (this);
680 if (!pimpl->mScene) {
681 return nullptr;
682 }
683 }
684 #endif // no validation
685
686 // Preprocess the scene and prepare it for post-processing
687 if (profiler) {
688 profiler->BeginRegion("preprocess");
689 }
690
691 ScenePreprocessor pre(pimpl->mScene);
692 pre.ProcessScene();
693
694 if (profiler) {
695 profiler->EndRegion("preprocess");
696 }
697
698 // Ensure that the validation process won't be called twice
699 ApplyPostProcessing(pFlags & (~aiProcess_ValidateDataStructure));
700 }
701 // if failed, extract the error string
702 else if( !pimpl->mScene) {
703 pimpl->mErrorString = imp->GetErrorText();
704 }
705
706 // clear any data allocated by post-process steps
707 pimpl->mPPShared->Clean();
708
709 if (profiler) {
710 profiler->EndRegion("total");
711 }
712 }
713 #ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
714 catch (std::exception &e) {
715 #if (defined _MSC_VER) && (defined _CPPRTTI)
716 // if we have RTTI get the full name of the exception that occurred
717 pimpl->mErrorString = std::string(typeid( e ).name()) + ": " + e.what();
718 #else
719 pimpl->mErrorString = std::string("std::exception: ") + e.what();
720 #endif
721
722 ASSIMP_LOG_ERROR(pimpl->mErrorString);
723 delete pimpl->mScene; pimpl->mScene = nullptr;
724 }
725 #endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
726
727 // either successful or failure - the pointer expresses it anyways
728 ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString);
729
730 return pimpl->mScene;
731 }
732
733
734 // ------------------------------------------------------------------------------------------------
735 // Apply post-processing to the currently bound scene
ApplyPostProcessing(unsigned int pFlags)736 const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) {
737 ai_assert(nullptr != pimpl);
738
739 ASSIMP_BEGIN_EXCEPTION_REGION();
740 // Return immediately if no scene is active
741 if (!pimpl->mScene) {
742 return nullptr;
743 }
744
745 // If no flags are given, return the current scene with no further action
746 if (!pFlags) {
747 return pimpl->mScene;
748 }
749
750 // In debug builds: run basic flag validation
751 ai_assert(_ValidateFlags(pFlags));
752 ASSIMP_LOG_INFO("Entering post processing pipeline");
753
754 #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
755 // The ValidateDS process plays an exceptional role. It isn't contained in the global
756 // list of post-processing steps, so we need to call it manually.
757 if (pFlags & aiProcess_ValidateDataStructure) {
758 ValidateDSProcess ds;
759 ds.ExecuteOnScene (this);
760 if (!pimpl->mScene) {
761 return nullptr;
762 }
763 }
764 #endif // no validation
765 #ifdef ASSIMP_BUILD_DEBUG
766 if (pimpl->bExtraVerbose)
767 {
768 #ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
769 ASSIMP_LOG_ERROR("Verbose Import is not available due to build settings");
770 #endif // no validation
771 pFlags |= aiProcess_ValidateDataStructure;
772 }
773 #else
774 if (pimpl->bExtraVerbose) {
775 ASSIMP_LOG_WARN("Not a debug build, ignoring extra verbose setting");
776 }
777 #endif // ! DEBUG
778
779 std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL);
780 for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
781 BaseProcess* process = pimpl->mPostProcessingSteps[a];
782 pimpl->mProgressHandler->UpdatePostProcess(static_cast<int>(a), static_cast<int>(pimpl->mPostProcessingSteps.size()) );
783 if( process->IsActive( pFlags)) {
784 if (profiler) {
785 profiler->BeginRegion("postprocess");
786 }
787
788 process->ExecuteOnScene ( this );
789
790 if (profiler) {
791 profiler->EndRegion("postprocess");
792 }
793 }
794 if( !pimpl->mScene) {
795 break;
796 }
797 #ifdef ASSIMP_BUILD_DEBUG
798
799 #ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
800 continue;
801 #endif // no validation
802
803 // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step
804 if (pimpl->bExtraVerbose) {
805 ASSIMP_LOG_DEBUG("Verbose Import: re-validating data structures");
806
807 ValidateDSProcess ds;
808 ds.ExecuteOnScene (this);
809 if( !pimpl->mScene) {
810 ASSIMP_LOG_ERROR("Verbose Import: failed to re-validate data structures");
811 break;
812 }
813 }
814 #endif // ! DEBUG
815 }
816 pimpl->mProgressHandler->UpdatePostProcess( static_cast<int>(pimpl->mPostProcessingSteps.size()),
817 static_cast<int>(pimpl->mPostProcessingSteps.size()) );
818
819 // update private scene flags
820 if( pimpl->mScene ) {
821 ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags;
822 }
823
824 // clear any data allocated by post-process steps
825 pimpl->mPPShared->Clean();
826 ASSIMP_LOG_INFO("Leaving post processing pipeline");
827
828 ASSIMP_END_EXCEPTION_REGION(const aiScene*);
829
830 return pimpl->mScene;
831 }
832
833 // ------------------------------------------------------------------------------------------------
ApplyCustomizedPostProcessing(BaseProcess * rootProcess,bool requestValidation)834 const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) {
835 ai_assert(nullptr != pimpl);
836
837 ASSIMP_BEGIN_EXCEPTION_REGION();
838
839 // Return immediately if no scene is active
840 if ( nullptr == pimpl->mScene ) {
841 return nullptr;
842 }
843
844 // If no flags are given, return the current scene with no further action
845 if ( NULL == rootProcess ) {
846 return pimpl->mScene;
847 }
848
849 // In debug builds: run basic flag validation
850 ASSIMP_LOG_INFO( "Entering customized post processing pipeline" );
851
852 #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
853 // The ValidateDS process plays an exceptional role. It isn't contained in the global
854 // list of post-processing steps, so we need to call it manually.
855 if ( requestValidation )
856 {
857 ValidateDSProcess ds;
858 ds.ExecuteOnScene( this );
859 if ( !pimpl->mScene ) {
860 return nullptr;
861 }
862 }
863 #endif // no validation
864 #ifdef ASSIMP_BUILD_DEBUG
865 if ( pimpl->bExtraVerbose )
866 {
867 #ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
868 ASSIMP_LOG_ERROR( "Verbose Import is not available due to build settings" );
869 #endif // no validation
870 }
871 #else
872 if ( pimpl->bExtraVerbose ) {
873 ASSIMP_LOG_WARN( "Not a debug build, ignoring extra verbose setting" );
874 }
875 #endif // ! DEBUG
876
877 std::unique_ptr<Profiler> profiler( GetPropertyInteger( AI_CONFIG_GLOB_MEASURE_TIME, 0 ) ? new Profiler() : NULL );
878
879 if ( profiler ) {
880 profiler->BeginRegion( "postprocess" );
881 }
882
883 rootProcess->ExecuteOnScene( this );
884
885 if ( profiler ) {
886 profiler->EndRegion( "postprocess" );
887 }
888
889 // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step
890 if ( pimpl->bExtraVerbose || requestValidation ) {
891 ASSIMP_LOG_DEBUG( "Verbose Import: revalidating data structures" );
892
893 ValidateDSProcess ds;
894 ds.ExecuteOnScene( this );
895 if ( !pimpl->mScene ) {
896 ASSIMP_LOG_ERROR( "Verbose Import: failed to revalidate data structures" );
897 }
898 }
899
900 // clear any data allocated by post-process steps
901 pimpl->mPPShared->Clean();
902 ASSIMP_LOG_INFO( "Leaving customized post processing pipeline" );
903
904 ASSIMP_END_EXCEPTION_REGION( const aiScene* );
905
906 return pimpl->mScene;
907 }
908
909 // ------------------------------------------------------------------------------------------------
910 // Helper function to check whether an extension is supported by ASSIMP
IsExtensionSupported(const char * szExtension) const911 bool Importer::IsExtensionSupported(const char* szExtension) const {
912 return nullptr != GetImporter(szExtension);
913 }
914
915 // ------------------------------------------------------------------------------------------------
GetImporterCount() const916 size_t Importer::GetImporterCount() const {
917 ai_assert(nullptr != pimpl);
918
919 return pimpl->mImporter.size();
920 }
921
922 // ------------------------------------------------------------------------------------------------
GetImporterInfo(size_t index) const923 const aiImporterDesc* Importer::GetImporterInfo(size_t index) const {
924 ai_assert(nullptr != pimpl);
925
926 if (index >= pimpl->mImporter.size()) {
927 return nullptr;
928 }
929 return pimpl->mImporter[index]->GetInfo();
930 }
931
932
933 // ------------------------------------------------------------------------------------------------
GetImporter(size_t index) const934 BaseImporter* Importer::GetImporter (size_t index) const {
935 ai_assert(nullptr != pimpl);
936
937 if (index >= pimpl->mImporter.size()) {
938 return nullptr;
939 }
940 return pimpl->mImporter[index];
941 }
942
943 // ------------------------------------------------------------------------------------------------
944 // Find a loader plugin for a given file extension
GetImporter(const char * szExtension) const945 BaseImporter* Importer::GetImporter (const char* szExtension) const {
946 ai_assert(nullptr != pimpl);
947
948 return GetImporter(GetImporterIndex(szExtension));
949 }
950
951 // ------------------------------------------------------------------------------------------------
952 // Find a loader plugin for a given file extension
GetImporterIndex(const char * szExtension) const953 size_t Importer::GetImporterIndex (const char* szExtension) const {
954 ai_assert(nullptr != pimpl);
955 ai_assert(nullptr != szExtension);
956
957 ASSIMP_BEGIN_EXCEPTION_REGION();
958
959 // skip over wildcard and dot characters at string head --
960 for ( ; *szExtension == '*' || *szExtension == '.'; ++szExtension );
961
962 std::string ext(szExtension);
963 if (ext.empty()) {
964 return static_cast<size_t>(-1);
965 }
966 std::transform( ext.begin(), ext.end(), ext.begin(), ToLower<char> );
967
968 std::set<std::string> str;
969 for (std::vector<BaseImporter*>::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) {
970 str.clear();
971
972 (*i)->GetExtensionList(str);
973 for (std::set<std::string>::const_iterator it = str.begin(); it != str.end(); ++it) {
974 if (ext == *it) {
975 return std::distance(static_cast< std::vector<BaseImporter*>::const_iterator >(pimpl->mImporter.begin()), i);
976 }
977 }
978 }
979 ASSIMP_END_EXCEPTION_REGION(size_t);
980 return static_cast<size_t>(-1);
981 }
982
983 // ------------------------------------------------------------------------------------------------
984 // Helper function to build a list of all file extensions supported by ASSIMP
GetExtensionList(aiString & szOut) const985 void Importer::GetExtensionList(aiString& szOut) const {
986 ai_assert(nullptr != pimpl);
987
988 ASSIMP_BEGIN_EXCEPTION_REGION();
989 std::set<std::string> str;
990 for (std::vector<BaseImporter*>::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) {
991 (*i)->GetExtensionList(str);
992 }
993
994 // List can be empty
995 if( !str.empty() ) {
996 for (std::set<std::string>::const_iterator it = str.begin();; ) {
997 szOut.Append("*.");
998 szOut.Append((*it).c_str());
999
1000 if (++it == str.end()) {
1001 break;
1002 }
1003 szOut.Append(";");
1004 }
1005 }
1006 ASSIMP_END_EXCEPTION_REGION(void);
1007 }
1008
1009 // ------------------------------------------------------------------------------------------------
1010 // Set a configuration property
SetPropertyInteger(const char * szName,int iValue)1011 bool Importer::SetPropertyInteger(const char* szName, int iValue) {
1012 ai_assert(nullptr != pimpl);
1013
1014 bool existing;
1015 ASSIMP_BEGIN_EXCEPTION_REGION();
1016 existing = SetGenericProperty<int>(pimpl->mIntProperties, szName,iValue);
1017 ASSIMP_END_EXCEPTION_REGION(bool);
1018 return existing;
1019 }
1020
1021 // ------------------------------------------------------------------------------------------------
1022 // Set a configuration property
SetPropertyFloat(const char * szName,ai_real iValue)1023 bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) {
1024 ai_assert(nullptr != pimpl);
1025
1026 bool existing;
1027 ASSIMP_BEGIN_EXCEPTION_REGION();
1028 existing = SetGenericProperty<ai_real>(pimpl->mFloatProperties, szName,iValue);
1029 ASSIMP_END_EXCEPTION_REGION(bool);
1030 return existing;
1031 }
1032
1033 // ------------------------------------------------------------------------------------------------
1034 // Set a configuration property
SetPropertyString(const char * szName,const std::string & value)1035 bool Importer::SetPropertyString(const char* szName, const std::string& value) {
1036 ai_assert(nullptr != pimpl);
1037
1038 bool existing;
1039 ASSIMP_BEGIN_EXCEPTION_REGION();
1040 existing = SetGenericProperty<std::string>(pimpl->mStringProperties, szName,value);
1041 ASSIMP_END_EXCEPTION_REGION(bool);
1042 return existing;
1043 }
1044
1045 // ------------------------------------------------------------------------------------------------
1046 // Set a configuration property
SetPropertyMatrix(const char * szName,const aiMatrix4x4 & value)1047 bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) {
1048 ai_assert(nullptr != pimpl);
1049
1050 bool existing;
1051 ASSIMP_BEGIN_EXCEPTION_REGION();
1052 existing = SetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties, szName,value);
1053 ASSIMP_END_EXCEPTION_REGION(bool);
1054 return existing;
1055 }
1056
1057 // ------------------------------------------------------------------------------------------------
1058 // Get a configuration property
GetPropertyInteger(const char * szName,int iErrorReturn) const1059 int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const {
1060 ai_assert(nullptr != pimpl);
1061
1062 return GetGenericProperty<int>(pimpl->mIntProperties,szName,iErrorReturn);
1063 }
1064
1065 // ------------------------------------------------------------------------------------------------
1066 // Get a configuration property
GetPropertyFloat(const char * szName,ai_real iErrorReturn) const1067 ai_real Importer::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const {
1068 ai_assert(nullptr != pimpl);
1069
1070 return GetGenericProperty<ai_real>(pimpl->mFloatProperties,szName,iErrorReturn);
1071 }
1072
1073 // ------------------------------------------------------------------------------------------------
1074 // Get a configuration property
GetPropertyString(const char * szName,const std::string & iErrorReturn) const1075 const std::string Importer::GetPropertyString(const char* szName, const std::string& iErrorReturn /*= ""*/) const {
1076 ai_assert(nullptr != pimpl);
1077
1078 return GetGenericProperty<std::string>(pimpl->mStringProperties,szName,iErrorReturn);
1079 }
1080
1081 // ------------------------------------------------------------------------------------------------
1082 // Get a configuration property
GetPropertyMatrix(const char * szName,const aiMatrix4x4 & iErrorReturn) const1083 const aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const {
1084 ai_assert(nullptr != pimpl);
1085
1086 return GetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties,szName,iErrorReturn);
1087 }
1088
1089 // ------------------------------------------------------------------------------------------------
1090 // Get the memory requirements of a single node
1091 inline
AddNodeWeight(unsigned int & iScene,const aiNode * pcNode)1092 void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) {
1093 if ( nullptr == pcNode ) {
1094 return;
1095 }
1096 iScene += sizeof(aiNode);
1097 iScene += sizeof(unsigned int) * pcNode->mNumMeshes;
1098 iScene += sizeof(void*) * pcNode->mNumChildren;
1099
1100 for (unsigned int i = 0; i < pcNode->mNumChildren;++i) {
1101 AddNodeWeight(iScene,pcNode->mChildren[i]);
1102 }
1103 }
1104
1105 // ------------------------------------------------------------------------------------------------
1106 // Get the memory requirements of the scene
GetMemoryRequirements(aiMemoryInfo & in) const1107 void Importer::GetMemoryRequirements(aiMemoryInfo& in) const {
1108 ai_assert(nullptr != pimpl);
1109
1110 in = aiMemoryInfo();
1111 aiScene* mScene = pimpl->mScene;
1112
1113 // return if we have no scene loaded
1114 if (!pimpl->mScene)
1115 return;
1116
1117
1118 in.total = sizeof(aiScene);
1119
1120 // add all meshes
1121 for (unsigned int i = 0; i < mScene->mNumMeshes;++i) {
1122 in.meshes += sizeof(aiMesh);
1123 if (mScene->mMeshes[i]->HasPositions()) {
1124 in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
1125 }
1126
1127 if (mScene->mMeshes[i]->HasNormals()) {
1128 in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
1129 }
1130
1131 if (mScene->mMeshes[i]->HasTangentsAndBitangents()) {
1132 in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices * 2;
1133 }
1134
1135 for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a) {
1136 if (mScene->mMeshes[i]->HasVertexColors(a)) {
1137 in.meshes += sizeof(aiColor4D) * mScene->mMeshes[i]->mNumVertices;
1138 } else {
1139 break;
1140 }
1141 }
1142 for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) {
1143 if (mScene->mMeshes[i]->HasTextureCoords(a)) {
1144 in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
1145 } else {
1146 break;
1147 }
1148 }
1149 if (mScene->mMeshes[i]->HasBones()) {
1150 in.meshes += sizeof(void*) * mScene->mMeshes[i]->mNumBones;
1151 for (unsigned int p = 0; p < mScene->mMeshes[i]->mNumBones;++p) {
1152 in.meshes += sizeof(aiBone);
1153 in.meshes += mScene->mMeshes[i]->mBones[p]->mNumWeights * sizeof(aiVertexWeight);
1154 }
1155 }
1156 in.meshes += (sizeof(aiFace) + 3 * sizeof(unsigned int))*mScene->mMeshes[i]->mNumFaces;
1157 }
1158 in.total += in.meshes;
1159
1160 // add all embedded textures
1161 for (unsigned int i = 0; i < mScene->mNumTextures;++i) {
1162 const aiTexture* pc = mScene->mTextures[i];
1163 in.textures += sizeof(aiTexture);
1164 if (pc->mHeight) {
1165 in.textures += 4 * pc->mHeight * pc->mWidth;
1166 } else {
1167 in.textures += pc->mWidth;
1168 }
1169 }
1170 in.total += in.textures;
1171
1172 // add all animations
1173 for (unsigned int i = 0; i < mScene->mNumAnimations;++i) {
1174 const aiAnimation* pc = mScene->mAnimations[i];
1175 in.animations += sizeof(aiAnimation);
1176
1177 // add all bone anims
1178 for (unsigned int a = 0; a < pc->mNumChannels; ++a) {
1179 const aiNodeAnim* pc2 = pc->mChannels[i];
1180 in.animations += sizeof(aiNodeAnim);
1181 in.animations += pc2->mNumPositionKeys * sizeof(aiVectorKey);
1182 in.animations += pc2->mNumScalingKeys * sizeof(aiVectorKey);
1183 in.animations += pc2->mNumRotationKeys * sizeof(aiQuatKey);
1184 }
1185 }
1186 in.total += in.animations;
1187
1188 // add all cameras and all lights
1189 in.total += in.cameras = sizeof(aiCamera) * mScene->mNumCameras;
1190 in.total += in.lights = sizeof(aiLight) * mScene->mNumLights;
1191
1192 // add all nodes
1193 AddNodeWeight(in.nodes,mScene->mRootNode);
1194 in.total += in.nodes;
1195
1196 // add all materials
1197 for (unsigned int i = 0; i < mScene->mNumMaterials;++i) {
1198 const aiMaterial* pc = mScene->mMaterials[i];
1199 in.materials += sizeof(aiMaterial);
1200 in.materials += pc->mNumAllocated * sizeof(void*);
1201
1202 for (unsigned int a = 0; a < pc->mNumProperties;++a) {
1203 in.materials += pc->mProperties[a]->mDataLength;
1204 }
1205 }
1206 in.total += in.materials;
1207 }
1208