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