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