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