1/*
2---------------------------------------------------------------------------
3Open Asset Import Library (assimp)
4---------------------------------------------------------------------------
5
6Copyright (c) 2006-2017, assimp team
7
8
9All rights reserved.
10
11Redistribution and use of this software in source and binary forms,
12with or without modification, are permitted provided that the following
13conditions are met:
14
15* Redistributions of source code must retain the above
16 copyright notice, this list of conditions and the
17 following disclaimer.
18
19* Redistributions in binary form must reproduce the above
20 copyright notice, this list of conditions and the
21 following disclaimer in the documentation and/or other
22 materials provided with the distribution.
23
24* Neither the name of the assimp team, nor the names of its
25 contributors may be used to endorse or promote products
26 derived from this software without specific prior
27 written permission of the assimp team.
28
29THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40---------------------------------------------------------------------------
41*/
42
43/** @file Importer.cpp
44 * @brief Implementation of the CPP-API class #Importer
45 */
46
47#include <assimp/version.h>
48#include <assimp/config.h>
49#include <assimp/importerdesc.h>
50
51// ------------------------------------------------------------------------------------------------
52/* Uncomment this line to prevent Assimp from catching unknown exceptions.
53 *
54 * Note that any Exception except DeadlyImportError may lead to
55 * undefined behaviour -> loaders could remain in an unusable state and
56 * further imports with the same Importer instance could fail/crash/burn ...
57 */
58// ------------------------------------------------------------------------------------------------
59#ifndef ASSIMP_BUILD_DEBUG
60# define ASSIMP_CATCH_GLOBAL_EXCEPTIONS
61#endif
62
63// ------------------------------------------------------------------------------------------------
64// Internal headers
65// ------------------------------------------------------------------------------------------------
66#include "Importer.h"
67#include "BaseImporter.h"
68#include "BaseProcess.h"
69
70#include "DefaultProgressHandler.h"
71#include "GenericProperty.h"
72#include "ProcessHelper.h"
73#include "ScenePreprocessor.h"
74#include "ScenePrivate.h"
75#include "MemoryIOWrapper.h"
76#include "Profiler.h"
77#include "TinyFormatter.h"
78#include "Exceptional.h"
79#include "Profiler.h"
80#include <set>
81#include <memory>
82#include <cctype>
83
84#include <assimp/DefaultIOStream.h>
85#include <assimp/DefaultIOSystem.h>
86
87#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
88# include "ValidateDataStructure.h"
89#endif
90
91using namespace Assimp::Profiling;
92using namespace Assimp::Formatter;
93
94namespace Assimp {
95 // ImporterRegistry.cpp
96 void GetImporterInstanceList(std::vector< BaseImporter* >& out);
97 void DeleteImporterInstanceList(std::vector< BaseImporter* >& out);
98
99 // PostStepRegistry.cpp
100 void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out);
101}
102
103using namespace Assimp;
104using namespace Assimp::Intern;
105
106// ------------------------------------------------------------------------------------------------
107// Intern::AllocateFromAssimpHeap serves as abstract base class. It overrides
108// new and delete (and their array counterparts) of public API classes (e.g. Logger) to
109// utilize our DLL heap.
110// See http://www.gotw.ca/publications/mill15.htm
111// ------------------------------------------------------------------------------------------------
112void* AllocateFromAssimpHeap::operator new ( size_t num_bytes) {
113 return ::operator new(num_bytes);
114}
115
116void* AllocateFromAssimpHeap::operator new ( size_t num_bytes, const std::nothrow_t& ) throw() {
117 try {
118 return AllocateFromAssimpHeap::operator new( num_bytes );
119 }
120 catch( ... ) {
121 return NULL;
122 }
123}
124
125void AllocateFromAssimpHeap::operator delete ( void* data) {
126 return ::operator delete(data);
127}
128
129void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes) {
130 return ::operator new[](num_bytes);
131}
132
133void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes, const std::nothrow_t& ) throw() {
134 try {
135 return AllocateFromAssimpHeap::operator new[]( num_bytes );
136 }
137 catch( ... ) {
138 return NULL;
139 }
140}
141
142void AllocateFromAssimpHeap::operator delete[] ( void* data) {
143 return ::operator delete[](data);
144}
145
146// ------------------------------------------------------------------------------------------------
147// Importer constructor.
148Importer::Importer()
149 : pimpl( NULL ) {
150 // allocate the pimpl first
151 pimpl = new ImporterPimpl();
152
153 pimpl->mScene = NULL;
154 pimpl->mErrorString = "";
155
156 // Allocate a default IO handler
157 pimpl->mIOHandler = new DefaultIOSystem;
158 pimpl->mIsDefaultHandler = true;
159 pimpl->bExtraVerbose = false; // disable extra verbose mode by default
160
161 pimpl->mProgressHandler = new DefaultProgressHandler();
162 pimpl->mIsDefaultProgressHandler = true;
163
164 GetImporterInstanceList(pimpl->mImporter);
165 GetPostProcessingStepInstanceList(pimpl->mPostProcessingSteps);
166
167 // Allocate a SharedPostProcessInfo object and store pointers to it in all post-process steps in the list.
168 pimpl->mPPShared = new SharedPostProcessInfo();
169 for (std::vector<BaseProcess*>::iterator it = pimpl->mPostProcessingSteps.begin();
170 it != pimpl->mPostProcessingSteps.end();
171 ++it) {
172
173 (*it)->SetSharedData(pimpl->mPPShared);
174 }
175}
176
177// ------------------------------------------------------------------------------------------------
178// Destructor of Importer
179Importer::~Importer()
180{
181 // Delete all import plugins
182 DeleteImporterInstanceList(pimpl->mImporter);
183
184 // Delete all post-processing plug-ins
185 for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++)
186 delete pimpl->mPostProcessingSteps[a];
187
188 // Delete the assigned IO and progress handler
189 delete pimpl->mIOHandler;
190 delete pimpl->mProgressHandler;
191
192 // Kill imported scene. Destructors should do that recursivly
193 delete pimpl->mScene;
194
195 // Delete shared post-processing data
196 delete pimpl->mPPShared;
197
198 // and finally the pimpl itself
199 delete pimpl;
200}
201
202// ------------------------------------------------------------------------------------------------
203// Copy constructor - copies the config of another Importer, not the scene
204Importer::Importer(const Importer &other)
205 : pimpl(NULL) {
206 new(this) Importer();
207
208 pimpl->mIntProperties = other.pimpl->mIntProperties;
209 pimpl->mFloatProperties = other.pimpl->mFloatProperties;
210 pimpl->mStringProperties = other.pimpl->mStringProperties;
211 pimpl->mMatrixProperties = other.pimpl->mMatrixProperties;
212}
213
214// ------------------------------------------------------------------------------------------------
215// Register a custom post-processing step
216aiReturn Importer::RegisterPPStep(BaseProcess* pImp)
217{
218 ai_assert(NULL != pImp);
219 ASSIMP_BEGIN_EXCEPTION_REGION();
220
221 pimpl->mPostProcessingSteps.push_back(pImp);
222 DefaultLogger::get()->info("Registering custom post-processing step");
223
224 ASSIMP_END_EXCEPTION_REGION(aiReturn);
225 return AI_SUCCESS;
226}
227
228// ------------------------------------------------------------------------------------------------
229// Register a custom loader plugin
230aiReturn Importer::RegisterLoader(BaseImporter* pImp)
231{
232 ai_assert(NULL != pImp);
233 ASSIMP_BEGIN_EXCEPTION_REGION();
234
235 // --------------------------------------------------------------------
236 // Check whether we would have two loaders for the same file extension
237 // This is absolutely OK, but we should warn the developer of the new
238 // loader that his code will probably never be called if the first
239 // loader is a bit too lazy in his file checking.
240 // --------------------------------------------------------------------
241 std::set<std::string> st;
242 std::string baked;
243 pImp->GetExtensionList(st);
244
245 for(std::set<std::string>::const_iterator it = st.begin(); it != st.end(); ++it) {
246
247#ifdef ASSIMP_BUILD_DEBUG
248 if (IsExtensionSupported(*it)) {
249 DefaultLogger::get()->warn("The file extension " + *it + " is already in use");
250 }
251#endif
252 baked += *it;
253 }
254
255 // add the loader
256 pimpl->mImporter.push_back(pImp);
257 DefaultLogger::get()->info("Registering custom importer for these file extensions: " + baked);
258 ASSIMP_END_EXCEPTION_REGION(aiReturn);
259 return AI_SUCCESS;
260}
261
262// ------------------------------------------------------------------------------------------------
263// Unregister a custom loader plugin
264aiReturn Importer::UnregisterLoader(BaseImporter* pImp)
265{
266 if(!pImp) {
267 // unregistering a NULL importer is no problem for us ... really!
268 return AI_SUCCESS;
269 }
270
271 ASSIMP_BEGIN_EXCEPTION_REGION();
272 std::vector<BaseImporter*>::iterator it = std::find(pimpl->mImporter.begin(),
273 pimpl->mImporter.end(),pImp);
274
275 if (it != pimpl->mImporter.end()) {
276 pimpl->mImporter.erase(it);
277 DefaultLogger::get()->info("Unregistering custom importer: ");
278 return AI_SUCCESS;
279 }
280 DefaultLogger::get()->warn("Unable to remove custom importer: I can't find you ...");
281 ASSIMP_END_EXCEPTION_REGION(aiReturn);
282 return AI_FAILURE;
283}
284
285// ------------------------------------------------------------------------------------------------
286// Unregister a custom loader plugin
287aiReturn 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.
310void 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
332IOSystem* Importer::GetIOHandler() const
333{
334 return pimpl->mIOHandler;
335}
336
337// ------------------------------------------------------------------------------------------------
338// Check whether a custom IO handler is currently set
339bool Importer::IsDefaultIOHandler() const
340{
341 return pimpl->mIsDefaultHandler;
342}
343
344// ------------------------------------------------------------------------------------------------
345// Supplies a custom progress handler to get regular callbacks during importing
346void 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
368ProgressHandler* Importer::GetProgressHandler() const
369{
370 return pimpl->mProgressHandler;
371}
372
373// ------------------------------------------------------------------------------------------------
374// Check whether a custom progress handler is currently set
375bool Importer::IsDefaultProgressHandler() const
376{
377 return pimpl->mIsDefaultProgressHandler;
378}
379
380// ------------------------------------------------------------------------------------------------
381// Validate post process step flags
382bool _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
397void 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
409const 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
417void Importer::SetExtraVerbose(bool bDo)
418{
419 pimpl->bExtraVerbose = bDo;
420}
421
422// ------------------------------------------------------------------------------------------------
423// Get the current scene
424const aiScene* Importer::GetScene() const
425{
426 return pimpl->mScene;
427}
428
429// ------------------------------------------------------------------------------------------------
430// Orphan the current scene and return it.
431aiScene* 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
445bool 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// ------------------------------------------------------------------------------------------------
485const 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// ------------------------------------------------------------------------------------------------
519void 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.
582const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
583{
584 ASSIMP_BEGIN_EXCEPTION_REGION();
585 const std::string pFile(_pFile);
586
587 // ----------------------------------------------------------------------
588 // Put a large try block around everything to catch all std::exception's
589 // that might be thrown by STL containers or by new().
590 // ImportErrorException's are throw by ourselves and caught elsewhere.
591 //-----------------------------------------------------------------------
592
593 WriteLogOpening(pFile);
594
595#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
596 try
597#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
598 {
599 // Check whether this Importer instance has already loaded
600 // a scene. In this case we need to delete the old one
601 if (pimpl->mScene) {
602
603 DefaultLogger::get()->debug("(Deleting previous scene)");
604 FreeScene();
605 }
606
607 // First check if the file is accessible at all
608 if( !pimpl->mIOHandler->Exists( pFile)) {
609
610 pimpl->mErrorString = "Unable to open file \"" + pFile + "\".";
611 DefaultLogger::get()->error(pimpl->mErrorString);
612 return NULL;
613 }
614
615 std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL);
616 if (profiler) {
617 profiler->BeginRegion("total");
618 }
619
620 // Find an worker class which can handle the file
621 BaseImporter* imp = NULL;
622 for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) {
623
624 if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, false)) {
625 imp = pimpl->mImporter[a];
626 break;
627 }
628 }
629
630 if (!imp) {
631 // not so bad yet ... try format auto detection.
632 const std::string::size_type s = pFile.find_last_of('.');
633 if (s != std::string::npos) {
634 DefaultLogger::get()->info("File extension not known, trying signature-based detection");
635 for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) {
636
637 if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) {
638 imp = pimpl->mImporter[a];
639 break;
640 }
641 }
642 }
643 // Put a proper error message if no suitable importer was found
644 if( !imp) {
645 pimpl->mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\".";
646 DefaultLogger::get()->error(pimpl->mErrorString);
647 return NULL;
648 }
649 }
650
651 // Get file size for progress handler
652 IOStream * fileIO = pimpl->mIOHandler->Open( pFile );
653 uint32_t fileSize = 0;
654 if (fileIO)
655 {
656 fileSize = static_cast<uint32_t>(fileIO->FileSize());
657 pimpl->mIOHandler->Close( fileIO );
658 }
659
660 // Dispatch the reading to the worker class for this format
661 const aiImporterDesc *desc( imp->GetInfo() );
662 std::string ext( "unknown" );
663 if ( NULL != desc ) {
664 ext = desc->mName;
665 }
666 DefaultLogger::get()->info("Found a matching importer for this file format: " + ext + "." );
667 pimpl->mProgressHandler->UpdateFileRead( 0, fileSize );
668
669 if (profiler) {
670 profiler->BeginRegion("import");
671 }
672
673 pimpl->mScene = imp->ReadFile( this, pFile, pimpl->mIOHandler);
674 pimpl->mProgressHandler->UpdateFileRead( fileSize, fileSize );
675
676 if (profiler) {
677 profiler->EndRegion("import");
678 }
679
680 // If successful, apply all active post processing steps to the imported data
681 if( pimpl->mScene) {
682
683#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
684 // The ValidateDS process is an exception. It is executed first, even before ScenePreprocessor is called.
685 if (pFlags & aiProcess_ValidateDataStructure)
686 {
687 ValidateDSProcess ds;
688 ds.ExecuteOnScene (this);
689 if (!pimpl->mScene) {
690 return NULL;
691 }
692 }
693#endif // no validation
694
695 // Preprocess the scene and prepare it for post-processing
696 if (profiler) {
697 profiler->BeginRegion("preprocess");
698 }
699
700 ScenePreprocessor pre(pimpl->mScene);
701 pre.ProcessScene();
702
703 if (profiler) {
704 profiler->EndRegion("preprocess");
705 }
706
707 // Ensure that the validation process won't be called twice
708 ApplyPostProcessing(pFlags & (~aiProcess_ValidateDataStructure));
709 }
710 // if failed, extract the error string
711 else if( !pimpl->mScene) {
712 pimpl->mErrorString = imp->GetErrorText();
713 }
714
715 // clear any data allocated by post-process steps
716 pimpl->mPPShared->Clean();
717
718 if (profiler) {
719 profiler->EndRegion("total");
720 }
721 }
722#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
723 catch (std::exception &e)
724 {
725#if (defined _MSC_VER) && (defined _CPPRTTI)
726 // if we have RTTI get the full name of the exception that occurred
727 pimpl->mErrorString = std::string(typeid( e ).name()) + ": " + e.what();
728#else
729 pimpl->mErrorString = std::string("std::exception: ") + e.what();
730#endif
731
732 DefaultLogger::get()->error(pimpl->mErrorString);
733 delete pimpl->mScene; pimpl->mScene = NULL;
734 }
735#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
736
737 // either successful or failure - the pointer expresses it anyways
738 ASSIMP_END_EXCEPTION_REGION(const aiScene*);
739 return pimpl->mScene;
740}
741
742
743// ------------------------------------------------------------------------------------------------
744// Apply post-processing to the currently bound scene
745const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
746{
747 ASSIMP_BEGIN_EXCEPTION_REGION();
748 // Return immediately if no scene is active
749 if (!pimpl->mScene) {
750 return NULL;
751 }
752
753 // If no flags are given, return the current scene with no further action
754 if (!pFlags) {
755 return pimpl->mScene;
756 }
757
758 // In debug builds: run basic flag validation
759 ai_assert(_ValidateFlags(pFlags));
760 DefaultLogger::get()->info("Entering post processing pipeline");
761
762#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
763 // The ValidateDS process plays an exceptional role. It isn't contained in the global
764 // list of post-processing steps, so we need to call it manually.
765 if (pFlags & aiProcess_ValidateDataStructure)
766 {
767 ValidateDSProcess ds;
768 ds.ExecuteOnScene (this);
769 if (!pimpl->mScene) {
770 return NULL;
771 }
772 }
773#endif // no validation
774#ifdef ASSIMP_BUILD_DEBUG
775 if (pimpl->bExtraVerbose)
776 {
777#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
778 DefaultLogger::get()->error("Verbose Import is not available due to build settings");
779#endif // no validation
780 pFlags |= aiProcess_ValidateDataStructure;
781 }
782#else
783 if (pimpl->bExtraVerbose) {
784 DefaultLogger::get()->warn("Not a debug build, ignoring extra verbose setting");
785 }
786#endif // ! DEBUG
787
788 std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL);
789 for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
790
791 BaseProcess* process = pimpl->mPostProcessingSteps[a];
792 pimpl->mProgressHandler->UpdatePostProcess(static_cast<int>(a), static_cast<int>(pimpl->mPostProcessingSteps.size()) );
793 if( process->IsActive( pFlags)) {
794
795 if (profiler) {
796 profiler->BeginRegion("postprocess");
797 }
798
799 process->ExecuteOnScene ( this );
800
801 if (profiler) {
802 profiler->EndRegion("postprocess");
803 }
804 }
805 if( !pimpl->mScene) {
806 break;
807 }
808#ifdef ASSIMP_BUILD_DEBUG
809
810#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
811 continue;
812#endif // no validation
813
814 // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step
815 if (pimpl->bExtraVerbose) {
816 DefaultLogger::get()->debug("Verbose Import: revalidating data structures");
817
818 ValidateDSProcess ds;
819 ds.ExecuteOnScene (this);
820 if( !pimpl->mScene) {
821 DefaultLogger::get()->error("Verbose Import: failed to revalidate data structures");
822 break;
823 }
824 }
825#endif // ! DEBUG
826 }
827 pimpl->mProgressHandler->UpdatePostProcess( static_cast<int>(pimpl->mPostProcessingSteps.size()), static_cast<int>(pimpl->mPostProcessingSteps.size()) );
828
829 // update private scene flags
830 if( pimpl->mScene )
831 ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags;
832
833 // clear any data allocated by post-process steps
834 pimpl->mPPShared->Clean();
835 DefaultLogger::get()->info("Leaving post processing pipeline");
836
837 ASSIMP_END_EXCEPTION_REGION(const aiScene*);
838 return pimpl->mScene;
839}
840
841// ------------------------------------------------------------------------------------------------
842const 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
917bool Importer::IsExtensionSupported(const char* szExtension) const
918{
919 return NULL != GetImporter(szExtension);
920}
921
922// ------------------------------------------------------------------------------------------------
923size_t Importer::GetImporterCount() const
924{
925 return pimpl->mImporter.size();
926}
927
928// ------------------------------------------------------------------------------------------------
929const 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// ------------------------------------------------------------------------------------------------
939BaseImporter* 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
949BaseImporter* 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
956size_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
987void 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
1009bool 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
1020bool Importer::SetPropertyFloat(const char* szName, ai_real iValue)
1021{
1022 bool exising;
1023 ASSIMP_BEGIN_EXCEPTION_REGION();
1024 exising = SetGenericProperty<ai_real>(pimpl->mFloatProperties, szName,iValue);
1025 ASSIMP_END_EXCEPTION_REGION(bool);
1026 return exising;
1027}
1028
1029// ------------------------------------------------------------------------------------------------
1030// Set a configuration property
1031bool 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
1042bool 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
1053int 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
1061ai_real Importer::GetPropertyFloat(const char* szName,
1062 ai_real iErrorReturn /*= 10e10*/) const
1063{
1064 return GetGenericProperty<ai_real>(pimpl->mFloatProperties,szName,iErrorReturn);
1065}
1066
1067// ------------------------------------------------------------------------------------------------
1068// Get a configuration property
1069const 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
1077const 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
1085inline 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
1098void 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