1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2021, 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 <exception>
82 #include <set>
83 #include <memory>
84 #include <cctype>
85 
86 #include <assimp/DefaultIOStream.h>
87 #include <assimp/DefaultIOSystem.h>
88 
89 #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
90 #   include "PostProcessing/ValidateDataStructure.h"
91 #endif
92 
93 using namespace Assimp::Profiling;
94 using namespace Assimp::Formatter;
95 
96 namespace Assimp {
97     // ImporterRegistry.cpp
98     void GetImporterInstanceList(std::vector< BaseImporter* >& out);
99 	void DeleteImporterInstanceList(std::vector< BaseImporter* >& out);
100 
101     // PostStepRegistry.cpp
102     void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out);
103 }
104 
105 using namespace Assimp;
106 using namespace Assimp::Intern;
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 // ------------------------------------------------------------------------------------------------
operator new(size_t num_bytes)114 void* AllocateFromAssimpHeap::operator new ( size_t num_bytes)  {
115     return ::operator new(num_bytes);
116 }
117 
operator new(size_t num_bytes,const std::nothrow_t &)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 
operator delete(void * data)127 void AllocateFromAssimpHeap::operator delete ( void* data)  {
128     return ::operator delete(data);
129 }
130 
operator new[](size_t num_bytes)131 void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes)    {
132     return ::operator new[](num_bytes);
133 }
134 
operator new[](size_t num_bytes,const std::nothrow_t &)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 
operator delete[](void * data)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 = std::string();
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("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("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 nullptr 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 nullptr 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 = std::string();
391     pimpl->mException = std::exception_ptr();
392     ASSIMP_END_EXCEPTION_REGION(void);
393 }
394 
395 // ------------------------------------------------------------------------------------------------
396 // Get the current error string, if any
GetErrorString() const397 const char* Importer::GetErrorString() const {
398     ai_assert(nullptr != pimpl);
399 
400     // Must remain valid as long as ReadFile() or FreeFile() are not called
401     return pimpl->mErrorString.c_str();
402 }
403 
GetException() const404 const std::exception_ptr& Importer::GetException() const {
405     ai_assert(nullptr != pimpl);
406 
407     // Must remain valid as long as ReadFile() or FreeFile() are not called
408     return pimpl->mException;
409 }
410 
411 // ------------------------------------------------------------------------------------------------
412 // Enable extra-verbose mode
SetExtraVerbose(bool bDo)413 void Importer::SetExtraVerbose(bool bDo) {
414     ai_assert(nullptr != pimpl);
415 
416     pimpl->bExtraVerbose = bDo;
417 }
418 
419 // ------------------------------------------------------------------------------------------------
420 // Get the current scene
GetScene() const421 const aiScene* Importer::GetScene() const {
422     ai_assert(nullptr != pimpl);
423 
424     return pimpl->mScene;
425 }
426 
427 // ------------------------------------------------------------------------------------------------
428 // Orphan the current scene and return it.
GetOrphanedScene()429 aiScene* Importer::GetOrphanedScene() {
430     ai_assert(nullptr != pimpl);
431 
432     aiScene* s = pimpl->mScene;
433 
434     ASSIMP_BEGIN_EXCEPTION_REGION();
435     pimpl->mScene = nullptr;
436 
437     pimpl->mErrorString = std::string();
438     pimpl->mException = std::exception_ptr();
439     ASSIMP_END_EXCEPTION_REGION(aiScene*);
440 
441     return s;
442 }
443 
444 // ------------------------------------------------------------------------------------------------
445 // Validate post-processing flags
ValidateFlags(unsigned int pFlags) const446 bool Importer::ValidateFlags(unsigned int pFlags) const {
447     ASSIMP_BEGIN_EXCEPTION_REGION();
448     // run basic checks for mutually exclusive flags
449     if(!_ValidateFlags(pFlags)) {
450         return false;
451     }
452 
453     // ValidateDS does not anymore occur in the pp list, it plays an awesome extra role ...
454 #ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
455     if (pFlags & aiProcess_ValidateDataStructure) {
456         return false;
457     }
458 #endif
459     pFlags &= ~aiProcess_ValidateDataStructure;
460 
461     // Now iterate through all bits which are set in the flags and check whether we find at least
462     // one pp plugin which handles it.
463     for (unsigned int mask = 1; mask < (1u << (sizeof(unsigned int)*8-1));mask <<= 1) {
464 
465         if (pFlags & mask) {
466 
467             bool have = false;
468             for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++)   {
469                 if (pimpl->mPostProcessingSteps[a]-> IsActive(mask) ) {
470 
471                     have = true;
472                     break;
473                 }
474             }
475             if (!have) {
476                 return false;
477             }
478         }
479     }
480     ASSIMP_END_EXCEPTION_REGION(bool);
481     return true;
482 }
483 
484 // ------------------------------------------------------------------------------------------------
ReadFileFromMemory(const void * pBuffer,size_t pLength,unsigned int pFlags,const char * pHint)485 const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
486     size_t pLength,
487     unsigned int pFlags,
488     const char* pHint /*= ""*/) {
489     ai_assert(nullptr != pimpl);
490 
491     ASSIMP_BEGIN_EXCEPTION_REGION();
492     if (!pHint) {
493         pHint = "";
494     }
495 
496     if (!pBuffer || !pLength || strlen(pHint) > MaxLenHint ) {
497         pimpl->mErrorString = "Invalid parameters passed to ReadFileFromMemory()";
498         return nullptr;
499     }
500 
501     // prevent deletion of the previous IOHandler
502     IOSystem* io = pimpl->mIOHandler;
503     pimpl->mIOHandler = nullptr;
504 
505     SetIOHandler(new MemoryIOSystem((const uint8_t*)pBuffer,pLength,io));
506 
507     // read the file and recover the previous IOSystem
508     static const size_t BufSize(Importer::MaxLenHint + 28);
509     char fbuff[BufSize];
510     ai_snprintf(fbuff, BufSize, "%s.%s",AI_MEMORYIO_MAGIC_FILENAME,pHint);
511 
512     ReadFile(fbuff,pFlags);
513     SetIOHandler(io);
514 
515     ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString, pimpl->mException);
516     return pimpl->mScene;
517 }
518 
519 // ------------------------------------------------------------------------------------------------
WriteLogOpening(const std::string & file)520 void WriteLogOpening(const std::string& file) {
521 
522     ASSIMP_LOG_INFO("Load ", file);
523 
524     // print a full version dump. This is nice because we don't
525     // need to ask the authors of incoming bug reports for
526     // the library version they're using - a log dump is
527     // sufficient.
528     const unsigned int flags = aiGetCompileFlags();
529     std::stringstream stream;
530     stream << "Assimp " << aiGetVersionMajor() << "." << aiGetVersionMinor() << "." << aiGetVersionRevision() << " "
531 #if defined(ASSIMP_BUILD_ARCHITECTURE)
532            << ASSIMP_BUILD_ARCHITECTURE
533 #elif defined(_M_IX86) || defined(__x86_32__) || defined(__i386__)
534            << "x86"
535 #elif defined(_M_X64) || defined(__x86_64__)
536            << "amd64"
537 #elif defined(_M_IA64) || defined(__ia64__)
538            << "itanium"
539 #elif defined(__ppc__) || defined(__powerpc__)
540            << "ppc32"
541 #elif defined(__powerpc64__)
542            << "ppc64"
543 #elif defined(__arm__)
544            << "arm"
545 #else
546            << "<unknown architecture>"
547 #endif
548            << " "
549 #if defined(ASSIMP_BUILD_COMPILER)
550            << (ASSIMP_BUILD_COMPILER)
551 #elif defined(_MSC_VER)
552            << "msvc"
553 #elif defined(__GNUC__)
554            << "gcc"
555 #elif defined(__clang__)
556            << "clang"
557 #elif defined(__EMSCRIPTEN__)
558            << "emscripten"
559 #elif defined(__MINGW32__)
560            << "MinGW-w64 32bit"
561 #elif defined(__MINGW64__)
562            << "MinGW-w64 64bit"
563 #else
564            << "<unknown compiler>"
565 #endif
566 
567 #ifdef ASSIMP_BUILD_DEBUG
568            << " debug"
569 #endif
570 
571            << (flags & ASSIMP_CFLAGS_NOBOOST ? " noboost" : "")
572            << (flags & ASSIMP_CFLAGS_SHARED ? " shared" : "")
573            << (flags & ASSIMP_CFLAGS_SINGLETHREADED ? " singlethreaded" : "")
574            << (flags & ASSIMP_CFLAGS_DOUBLE_SUPPORT ? " double : " : "single : ");
575 
576     ASSIMP_LOG_DEBUG(stream.str());
577 }
578 
579 // ------------------------------------------------------------------------------------------------
580 // Reads the given file and returns its contents if successful.
ReadFile(const char * _pFile,unsigned int pFlags)581 const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) {
582     ai_assert(nullptr != pimpl);
583 
584     ASSIMP_BEGIN_EXCEPTION_REGION();
585     const std::string pFile(_pFile);
586 
587     // ----------------------------------------------------------------------
588     // Put a large try block around everything to catch all std::exception's
589     // that might be thrown by STL containers or by new().
590     // ImportErrorException's are throw by ourselves and caught elsewhere.
591     //-----------------------------------------------------------------------
592 
593     WriteLogOpening(pFile);
594 
595 #ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
596     try
597 #endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
598     {
599         // Check whether this Importer instance has already loaded
600         // a scene. In this case we need to delete the old one
601         if (pimpl->mScene)  {
602 
603             ASSIMP_LOG_DEBUG("(Deleting previous scene)");
604             FreeScene();
605         }
606 
607         // First check if the file is accessible at all
608         if( !pimpl->mIOHandler->Exists( pFile)) {
609 
610             pimpl->mErrorString = "Unable to open file \"" + pFile + "\".";
611             ASSIMP_LOG_ERROR(pimpl->mErrorString);
612             return nullptr;
613         }
614 
615         std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME, 0) ? new Profiler() : nullptr);
616         if (profiler) {
617             profiler->BeginRegion("total");
618         }
619 
620         // Find an worker class which can handle the file
621         BaseImporter* imp = nullptr;
622         SetPropertyInteger("importerIndex", -1);
623         for( unsigned int a = 0; a < pimpl->mImporter.size(); a++)  {
624 
625             if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, false)) {
626                 imp = pimpl->mImporter[a];
627                 SetPropertyInteger("importerIndex", a);
628                 break;
629             }
630         }
631 
632         if (!imp)   {
633             // not so bad yet ... try format auto detection.
634             const std::string::size_type s = pFile.find_last_of('.');
635             if (s != std::string::npos) {
636                 ASSIMP_LOG_INFO("File extension not known, trying signature-based detection");
637                 for( unsigned int a = 0; a < pimpl->mImporter.size(); a++)  {
638                     if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) {
639                         imp = pimpl->mImporter[a];
640                         SetPropertyInteger("importerIndex", a);
641                         break;
642                     }
643                 }
644             }
645             // Put a proper error message if no suitable importer was found
646             if( !imp)   {
647                 pimpl->mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\".";
648                 ASSIMP_LOG_ERROR(pimpl->mErrorString);
649                 return nullptr;
650             }
651         }
652 
653         // Get file size for progress handler
654         IOStream * fileIO = pimpl->mIOHandler->Open( pFile );
655         uint32_t fileSize = 0;
656         if (fileIO)
657         {
658             fileSize = static_cast<uint32_t>(fileIO->FileSize());
659             pimpl->mIOHandler->Close( fileIO );
660         }
661 
662         // Dispatch the reading to the worker class for this format
663         const aiImporterDesc *desc( imp->GetInfo() );
664         std::string ext( "unknown" );
665         if ( nullptr != desc ) {
666             ext = desc->mName;
667         }
668         ASSIMP_LOG_INFO("Found a matching importer for this file format: ", ext, "." );
669         pimpl->mProgressHandler->UpdateFileRead( 0, fileSize );
670 
671         if (profiler) {
672             profiler->BeginRegion("import");
673         }
674 
675         pimpl->mScene = imp->ReadFile( this, pFile, pimpl->mIOHandler);
676         pimpl->mProgressHandler->UpdateFileRead( fileSize, fileSize );
677 
678         if (profiler) {
679             profiler->EndRegion("import");
680         }
681 
682         SetPropertyString("sourceFilePath", pFile);
683 
684         // If successful, apply all active post processing steps to the imported data
685         if( pimpl->mScene)  {
686             if (!pimpl->mScene->mMetaData || !pimpl->mScene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT)) {
687                 if (!pimpl->mScene->mMetaData) {
688                     pimpl->mScene->mMetaData = new aiMetadata;
689                 }
690                 pimpl->mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT, aiString(ext));
691             }
692 
693 #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
694             // The ValidateDS process is an exception. It is executed first, even before ScenePreprocessor is called.
695             if (pFlags & aiProcess_ValidateDataStructure) {
696                 ValidateDSProcess ds;
697                 ds.ExecuteOnScene (this);
698                 if (!pimpl->mScene) {
699                     return nullptr;
700                 }
701             }
702 #endif // no validation
703 
704             // Preprocess the scene and prepare it for post-processing
705             if (profiler) {
706                 profiler->BeginRegion("preprocess");
707             }
708 
709             ScenePreprocessor pre(pimpl->mScene);
710             pre.ProcessScene();
711 
712             if (profiler) {
713                 profiler->EndRegion("preprocess");
714             }
715 
716             // Ensure that the validation process won't be called twice
717             ApplyPostProcessing(pFlags & (~aiProcess_ValidateDataStructure));
718         }
719         // if failed, extract the error string
720         else if( !pimpl->mScene) {
721             pimpl->mErrorString = imp->GetErrorText();
722             pimpl->mException = imp->GetException();
723         }
724 
725         // clear any data allocated by post-process steps
726         pimpl->mPPShared->Clean();
727 
728         if (profiler) {
729             profiler->EndRegion("total");
730         }
731     }
732 #ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
733     catch (std::exception &e) {
734 #if (defined _MSC_VER) &&   (defined _CPPRTTI)
735         // if we have RTTI get the full name of the exception that occurred
736         pimpl->mErrorString = std::string(typeid( e ).name()) + ": " + e.what();
737 #else
738         pimpl->mErrorString = std::string("std::exception: ") + e.what();
739 #endif
740 
741         ASSIMP_LOG_ERROR(pimpl->mErrorString);
742         delete pimpl->mScene; pimpl->mScene = nullptr;
743     }
744 #endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
745 
746     // either successful or failure - the pointer expresses it anyways
747     ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString, pimpl->mException);
748 
749     return pimpl->mScene;
750 }
751 
752 
753 // ------------------------------------------------------------------------------------------------
754 // Apply post-processing to the currently bound scene
ApplyPostProcessing(unsigned int pFlags)755 const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) {
756     ai_assert(nullptr != pimpl);
757 
758     ASSIMP_BEGIN_EXCEPTION_REGION();
759     // Return immediately if no scene is active
760     if (!pimpl->mScene) {
761         return nullptr;
762     }
763 
764     // If no flags are given, return the current scene with no further action
765     if (!pFlags) {
766         return pimpl->mScene;
767     }
768 
769     // In debug builds: run basic flag validation
770     ai_assert(_ValidateFlags(pFlags));
771     ASSIMP_LOG_INFO("Entering post processing pipeline");
772 
773 #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
774     // The ValidateDS process plays an exceptional role. It isn't contained in the global
775     // list of post-processing steps, so we need to call it manually.
776     if (pFlags & aiProcess_ValidateDataStructure) {
777         ValidateDSProcess ds;
778         ds.ExecuteOnScene (this);
779         if (!pimpl->mScene) {
780             return nullptr;
781         }
782     }
783 #endif // no validation
784 #ifdef ASSIMP_BUILD_DEBUG
785     if (pimpl->bExtraVerbose)
786     {
787 #ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
788         ASSIMP_LOG_ERROR("Verbose Import is not available due to build settings");
789 #endif  // no validation
790         pFlags |= aiProcess_ValidateDataStructure;
791     }
792 #else
793     if (pimpl->bExtraVerbose) {
794         ASSIMP_LOG_WARN("Not a debug build, ignoring extra verbose setting");
795     }
796 #endif // ! DEBUG
797 
798     std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME, 0) ? new Profiler() : nullptr);
799     for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++)   {
800         BaseProcess* process = pimpl->mPostProcessingSteps[a];
801         pimpl->mProgressHandler->UpdatePostProcess(static_cast<int>(a), static_cast<int>(pimpl->mPostProcessingSteps.size()) );
802         if( process->IsActive( pFlags)) {
803             if (profiler) {
804                 profiler->BeginRegion("postprocess");
805             }
806 
807             process->ExecuteOnScene ( this );
808 
809             if (profiler) {
810                 profiler->EndRegion("postprocess");
811             }
812         }
813         if( !pimpl->mScene) {
814             break;
815         }
816 #ifdef ASSIMP_BUILD_DEBUG
817 
818 #ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
819         continue;
820 #endif  // no validation
821 
822         // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step
823         if (pimpl->bExtraVerbose)   {
824             ASSIMP_LOG_DEBUG("Verbose Import: re-validating data structures");
825 
826             ValidateDSProcess ds;
827             ds.ExecuteOnScene (this);
828             if( !pimpl->mScene) {
829                 ASSIMP_LOG_ERROR("Verbose Import: failed to re-validate data structures");
830                 break;
831             }
832         }
833 #endif // ! DEBUG
834     }
835     pimpl->mProgressHandler->UpdatePostProcess( static_cast<int>(pimpl->mPostProcessingSteps.size()),
836         static_cast<int>(pimpl->mPostProcessingSteps.size()) );
837 
838     // update private scene flags
839     if( pimpl->mScene ) {
840       ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags;
841     }
842 
843     // clear any data allocated by post-process steps
844     pimpl->mPPShared->Clean();
845     ASSIMP_LOG_INFO("Leaving post processing pipeline");
846 
847     ASSIMP_END_EXCEPTION_REGION(const aiScene*);
848 
849     return pimpl->mScene;
850 }
851 
852 // ------------------------------------------------------------------------------------------------
ApplyCustomizedPostProcessing(BaseProcess * rootProcess,bool requestValidation)853 const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) {
854     ai_assert(nullptr != pimpl);
855 
856     ASSIMP_BEGIN_EXCEPTION_REGION();
857 
858     // Return immediately if no scene is active
859     if ( nullptr == pimpl->mScene ) {
860         return nullptr;
861     }
862 
863     // If no flags are given, return the current scene with no further action
864     if (nullptr == rootProcess) {
865         return pimpl->mScene;
866     }
867 
868     // In debug builds: run basic flag validation
869     ASSIMP_LOG_INFO( "Entering customized post processing pipeline" );
870 
871 #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
872     // The ValidateDS process plays an exceptional role. It isn't contained in the global
873     // list of post-processing steps, so we need to call it manually.
874     if ( requestValidation )
875     {
876         ValidateDSProcess ds;
877         ds.ExecuteOnScene( this );
878         if ( !pimpl->mScene ) {
879             return nullptr;
880         }
881     }
882 #endif // no validation
883 #ifdef ASSIMP_BUILD_DEBUG
884     if ( pimpl->bExtraVerbose )
885     {
886 #ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
887         ASSIMP_LOG_ERROR( "Verbose Import is not available due to build settings" );
888 #endif  // no validation
889     }
890 #else
891     if ( pimpl->bExtraVerbose ) {
892         ASSIMP_LOG_WARN( "Not a debug build, ignoring extra verbose setting" );
893     }
894 #endif // ! DEBUG
895 
896     std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME, 0) ? new Profiler() : nullptr);
897 
898     if ( profiler ) {
899         profiler->BeginRegion( "postprocess" );
900     }
901 
902     rootProcess->ExecuteOnScene( this );
903 
904     if ( profiler ) {
905         profiler->EndRegion( "postprocess" );
906     }
907 
908     // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step
909     if ( pimpl->bExtraVerbose || requestValidation  ) {
910         ASSIMP_LOG_DEBUG( "Verbose Import: revalidating data structures" );
911 
912         ValidateDSProcess ds;
913         ds.ExecuteOnScene( this );
914         if ( !pimpl->mScene ) {
915             ASSIMP_LOG_ERROR( "Verbose Import: failed to revalidate data structures" );
916         }
917     }
918 
919     // clear any data allocated by post-process steps
920     pimpl->mPPShared->Clean();
921     ASSIMP_LOG_INFO( "Leaving customized post processing pipeline" );
922 
923     ASSIMP_END_EXCEPTION_REGION( const aiScene* );
924 
925     return pimpl->mScene;
926 }
927 
928 // ------------------------------------------------------------------------------------------------
929 // Helper function to check whether an extension is supported by ASSIMP
IsExtensionSupported(const char * szExtension) const930 bool Importer::IsExtensionSupported(const char* szExtension) const {
931     return nullptr != GetImporter(szExtension);
932 }
933 
934 // ------------------------------------------------------------------------------------------------
GetImporterCount() const935 size_t Importer::GetImporterCount() const {
936     ai_assert(nullptr != pimpl);
937 
938     return pimpl->mImporter.size();
939 }
940 
941 // ------------------------------------------------------------------------------------------------
GetImporterInfo(size_t index) const942 const aiImporterDesc* Importer::GetImporterInfo(size_t index) const {
943     ai_assert(nullptr != pimpl);
944 
945     if (index >= pimpl->mImporter.size()) {
946         return nullptr;
947     }
948     return pimpl->mImporter[index]->GetInfo();
949 }
950 
951 
952 // ------------------------------------------------------------------------------------------------
GetImporter(size_t index) const953 BaseImporter* Importer::GetImporter (size_t index) const {
954     ai_assert(nullptr != pimpl);
955 
956     if (index >= pimpl->mImporter.size()) {
957         return nullptr;
958     }
959     return pimpl->mImporter[index];
960 }
961 
962 // ------------------------------------------------------------------------------------------------
963 // Find a loader plugin for a given file extension
GetImporter(const char * szExtension) const964 BaseImporter* Importer::GetImporter (const char* szExtension) const {
965     ai_assert(nullptr != pimpl);
966 
967     return GetImporter(GetImporterIndex(szExtension));
968 }
969 
970 // ------------------------------------------------------------------------------------------------
971 // Find a loader plugin for a given file extension
GetImporterIndex(const char * szExtension) const972 size_t Importer::GetImporterIndex (const char* szExtension) const {
973     ai_assert(nullptr != pimpl);
974     ai_assert(nullptr != szExtension);
975 
976     ASSIMP_BEGIN_EXCEPTION_REGION();
977 
978     // skip over wild-card and dot characters at string head --
979     for ( ; *szExtension == '*' || *szExtension == '.'; ++szExtension );
980 
981     std::string ext(szExtension);
982     if (ext.empty()) {
983         return static_cast<size_t>(-1);
984     }
985     ext = ai_tolower(ext);
986     std::set<std::string> str;
987     for (std::vector<BaseImporter*>::const_iterator i =  pimpl->mImporter.begin();i != pimpl->mImporter.end();++i)  {
988         str.clear();
989 
990         (*i)->GetExtensionList(str);
991         for (std::set<std::string>::const_iterator it = str.begin(); it != str.end(); ++it) {
992             if (ext == *it) {
993                 return std::distance(static_cast< std::vector<BaseImporter*>::const_iterator >(pimpl->mImporter.begin()), i);
994             }
995         }
996     }
997     ASSIMP_END_EXCEPTION_REGION(size_t);
998     return static_cast<size_t>(-1);
999 }
1000 
1001 // ------------------------------------------------------------------------------------------------
1002 // Helper function to build a list of all file extensions supported by ASSIMP
GetExtensionList(aiString & szOut) const1003 void Importer::GetExtensionList(aiString& szOut) const {
1004     ai_assert(nullptr != pimpl);
1005 
1006     ASSIMP_BEGIN_EXCEPTION_REGION();
1007     std::set<std::string> str;
1008     for (std::vector<BaseImporter*>::const_iterator i =  pimpl->mImporter.begin();i != pimpl->mImporter.end();++i)  {
1009         (*i)->GetExtensionList(str);
1010     }
1011 
1012 	// List can be empty
1013 	if( !str.empty() ) {
1014 		for (std::set<std::string>::const_iterator it = str.begin();; ) {
1015 			szOut.Append("*.");
1016 			szOut.Append((*it).c_str());
1017 
1018 			if (++it == str.end()) {
1019 				break;
1020 			}
1021 			szOut.Append(";");
1022 		}
1023 	}
1024     ASSIMP_END_EXCEPTION_REGION(void);
1025 }
1026 
1027 // ------------------------------------------------------------------------------------------------
1028 // Set a configuration property
SetPropertyInteger(const char * szName,int iValue)1029 bool Importer::SetPropertyInteger(const char* szName, int iValue) {
1030     ai_assert(nullptr != pimpl);
1031 
1032     bool existing;
1033     ASSIMP_BEGIN_EXCEPTION_REGION();
1034         existing = SetGenericProperty<int>(pimpl->mIntProperties, szName,iValue);
1035     ASSIMP_END_EXCEPTION_REGION(bool);
1036     return existing;
1037 }
1038 
1039 // ------------------------------------------------------------------------------------------------
1040 // Set a configuration property
SetPropertyFloat(const char * szName,ai_real iValue)1041 bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) {
1042     ai_assert(nullptr != pimpl);
1043 
1044     bool existing;
1045     ASSIMP_BEGIN_EXCEPTION_REGION();
1046         existing = SetGenericProperty<ai_real>(pimpl->mFloatProperties, szName,iValue);
1047     ASSIMP_END_EXCEPTION_REGION(bool);
1048     return existing;
1049 }
1050 
1051 // ------------------------------------------------------------------------------------------------
1052 // Set a configuration property
SetPropertyString(const char * szName,const std::string & value)1053 bool Importer::SetPropertyString(const char* szName, const std::string& value) {
1054     ai_assert(nullptr != pimpl);
1055 
1056     bool existing;
1057     ASSIMP_BEGIN_EXCEPTION_REGION();
1058         existing = SetGenericProperty<std::string>(pimpl->mStringProperties, szName,value);
1059     ASSIMP_END_EXCEPTION_REGION(bool);
1060     return existing;
1061 }
1062 
1063 // ------------------------------------------------------------------------------------------------
1064 // Set a configuration property
SetPropertyMatrix(const char * szName,const aiMatrix4x4 & value)1065 bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) {
1066     ai_assert(nullptr != pimpl);
1067 
1068     bool existing;
1069     ASSIMP_BEGIN_EXCEPTION_REGION();
1070         existing = SetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties, szName,value);
1071     ASSIMP_END_EXCEPTION_REGION(bool);
1072     return existing;
1073 }
1074 
1075 // ------------------------------------------------------------------------------------------------
1076 // Set a configuration property
SetPropertyPointer(const char * szName,void * value)1077 bool Importer::SetPropertyPointer(const char* szName, void* value) {
1078     ai_assert(nullptr != pimpl);
1079 
1080     bool existing;
1081     ASSIMP_BEGIN_EXCEPTION_REGION();
1082         existing = SetGenericProperty<void*>(pimpl->mPointerProperties, szName,value);
1083     ASSIMP_END_EXCEPTION_REGION(bool);
1084     return existing;
1085 }
1086 
1087 // ------------------------------------------------------------------------------------------------
1088 // Get a configuration property
GetPropertyInteger(const char * szName,int iErrorReturn) const1089 int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const {
1090     ai_assert(nullptr != pimpl);
1091 
1092     return GetGenericProperty<int>(pimpl->mIntProperties,szName,iErrorReturn);
1093 }
1094 
1095 // ------------------------------------------------------------------------------------------------
1096 // Get a configuration property
GetPropertyFloat(const char * szName,ai_real iErrorReturn) const1097 ai_real Importer::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const {
1098     ai_assert(nullptr != pimpl);
1099 
1100     return GetGenericProperty<ai_real>(pimpl->mFloatProperties,szName,iErrorReturn);
1101 }
1102 
1103 // ------------------------------------------------------------------------------------------------
1104 // Get a configuration property
GetPropertyString(const char * szName,const std::string & iErrorReturn) const1105 std::string Importer::GetPropertyString(const char* szName, const std::string& iErrorReturn /*= ""*/) const {
1106     ai_assert(nullptr != pimpl);
1107 
1108     return GetGenericProperty<std::string>(pimpl->mStringProperties,szName,iErrorReturn);
1109 }
1110 
1111 // ------------------------------------------------------------------------------------------------
1112 // Get a configuration property
GetPropertyMatrix(const char * szName,const aiMatrix4x4 & iErrorReturn) const1113 aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const {
1114     ai_assert(nullptr != pimpl);
1115 
1116     return GetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties,szName,iErrorReturn);
1117 }
1118 
1119 // ------------------------------------------------------------------------------------------------
1120 // Get a configuration property
GetPropertyPointer(const char * szName,void * iErrorReturn) const1121 void* Importer::GetPropertyPointer(const char* szName, void* iErrorReturn /*= nullptr*/) const {
1122     ai_assert(nullptr != pimpl);
1123 
1124     return GetGenericProperty<void*>(pimpl->mPointerProperties,szName,iErrorReturn);
1125 }
1126 
1127 // ------------------------------------------------------------------------------------------------
1128 // Get the memory requirements of a single node
1129 inline
AddNodeWeight(unsigned int & iScene,const aiNode * pcNode)1130 void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) {
1131     if ( nullptr == pcNode ) {
1132         return;
1133     }
1134     iScene += sizeof(aiNode);
1135     iScene += sizeof(unsigned int) * pcNode->mNumMeshes;
1136     iScene += sizeof(void*) * pcNode->mNumChildren;
1137 
1138     for (unsigned int i = 0; i < pcNode->mNumChildren;++i) {
1139         AddNodeWeight(iScene,pcNode->mChildren[i]);
1140     }
1141 }
1142 
1143 // ------------------------------------------------------------------------------------------------
1144 // Get the memory requirements of the scene
GetMemoryRequirements(aiMemoryInfo & in) const1145 void Importer::GetMemoryRequirements(aiMemoryInfo& in) const {
1146     ai_assert(nullptr != pimpl);
1147 
1148     in = aiMemoryInfo();
1149     aiScene* mScene = pimpl->mScene;
1150 
1151     // return if we have no scene loaded
1152     if (!mScene)
1153         return;
1154 
1155     in.total = sizeof(aiScene);
1156 
1157     // add all meshes
1158     for (unsigned int i = 0; i < mScene->mNumMeshes;++i) {
1159         in.meshes += sizeof(aiMesh);
1160         if (mScene->mMeshes[i]->HasPositions()) {
1161             in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
1162         }
1163 
1164         if (mScene->mMeshes[i]->HasNormals()) {
1165             in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
1166         }
1167 
1168         if (mScene->mMeshes[i]->HasTangentsAndBitangents()) {
1169             in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices * 2;
1170         }
1171 
1172         for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a) {
1173             if (mScene->mMeshes[i]->HasVertexColors(a)) {
1174                 in.meshes += sizeof(aiColor4D) * mScene->mMeshes[i]->mNumVertices;
1175             } else {
1176                 break;
1177             }
1178         }
1179         for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) {
1180             if (mScene->mMeshes[i]->HasTextureCoords(a)) {
1181                 in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
1182             } else {
1183                 break;
1184             }
1185         }
1186         if (mScene->mMeshes[i]->HasBones()) {
1187             in.meshes += sizeof(void*) * mScene->mMeshes[i]->mNumBones;
1188             for (unsigned int p = 0; p < mScene->mMeshes[i]->mNumBones;++p) {
1189                 in.meshes += sizeof(aiBone);
1190                 in.meshes += mScene->mMeshes[i]->mBones[p]->mNumWeights * sizeof(aiVertexWeight);
1191             }
1192         }
1193         in.meshes += (sizeof(aiFace) + 3 * sizeof(unsigned int))*mScene->mMeshes[i]->mNumFaces;
1194     }
1195     in.total += in.meshes;
1196 
1197     // add all embedded textures
1198     for (unsigned int i = 0; i < mScene->mNumTextures;++i) {
1199         const aiTexture* pc = mScene->mTextures[i];
1200         in.textures += sizeof(aiTexture);
1201         if (pc->mHeight) {
1202             in.textures += 4 * pc->mHeight * pc->mWidth;
1203         } else {
1204             in.textures += pc->mWidth;
1205         }
1206     }
1207     in.total += in.textures;
1208 
1209     // add all animations
1210     for (unsigned int i = 0; i < mScene->mNumAnimations;++i) {
1211         const aiAnimation* pc = mScene->mAnimations[i];
1212         in.animations += sizeof(aiAnimation);
1213 
1214         // add all bone anims
1215         for (unsigned int a = 0; a < pc->mNumChannels; ++a) {
1216             const aiNodeAnim* pc2 = pc->mChannels[a];
1217             in.animations += sizeof(aiNodeAnim);
1218             in.animations += pc2->mNumPositionKeys * sizeof(aiVectorKey);
1219             in.animations += pc2->mNumScalingKeys * sizeof(aiVectorKey);
1220             in.animations += pc2->mNumRotationKeys * sizeof(aiQuatKey);
1221         }
1222     }
1223     in.total += in.animations;
1224 
1225     // add all cameras and all lights
1226     in.total += in.cameras = sizeof(aiCamera) *  mScene->mNumCameras;
1227     in.total += in.lights  = sizeof(aiLight)  *  mScene->mNumLights;
1228 
1229     // add all nodes
1230     AddNodeWeight(in.nodes,mScene->mRootNode);
1231     in.total += in.nodes;
1232 
1233     // add all materials
1234     for (unsigned int i = 0; i < mScene->mNumMaterials;++i) {
1235         const aiMaterial* pc = mScene->mMaterials[i];
1236         in.materials += sizeof(aiMaterial);
1237         in.materials += pc->mNumAllocated * sizeof(void*);
1238 
1239         for (unsigned int a = 0; a < pc->mNumProperties;++a) {
1240             in.materials += pc->mProperties[a]->mDataLength;
1241         }
1242     }
1243 
1244     in.total += in.materials;
1245 }
1246