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