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