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