| /* |
| --------------------------------------------------------------------------- |
| Open Asset Import Library (assimp) |
| --------------------------------------------------------------------------- |
| |
| Copyright (c) 2006-2017, assimp team |
| |
| |
| All rights reserved. |
| |
| Redistribution and use of this software in source and binary forms, |
| with or without modification, are permitted provided that the following |
| conditions are met: |
| |
| * Redistributions of source code must retain the above |
| copyright notice, this list of conditions and the |
| following disclaimer. |
| |
| * Redistributions in binary form must reproduce the above |
| copyright notice, this list of conditions and the |
| following disclaimer in the documentation and/or other |
| materials provided with the distribution. |
| |
| * Neither the name of the assimp team, nor the names of its |
| contributors may be used to endorse or promote products |
| derived from this software without specific prior |
| written permission of the assimp team. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| --------------------------------------------------------------------------- |
| */ |
| |
| /** @file Importer.cpp |
| * @brief Implementation of the CPP-API class #Importer |
| */ |
| |
| #include <assimp/version.h> |
| #include <assimp/config.h> |
| #include <assimp/importerdesc.h> |
| |
| // ------------------------------------------------------------------------------------------------ |
| /* Uncomment this line to prevent Assimp from catching unknown exceptions. |
| * |
| * Note that any Exception except DeadlyImportError may lead to |
| * undefined behaviour -> loaders could remain in an unusable state and |
| * further imports with the same Importer instance could fail/crash/burn ... |
| */ |
| // ------------------------------------------------------------------------------------------------ |
| #ifndef ASSIMP_BUILD_DEBUG |
| # define ASSIMP_CATCH_GLOBAL_EXCEPTIONS |
| #endif |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Internal headers |
| // ------------------------------------------------------------------------------------------------ |
| #include "Importer.h" |
| #include "BaseImporter.h" |
| #include "BaseProcess.h" |
| |
| #include "DefaultProgressHandler.h" |
| #include "GenericProperty.h" |
| #include "ProcessHelper.h" |
| #include "ScenePreprocessor.h" |
| #include "ScenePrivate.h" |
| #include "MemoryIOWrapper.h" |
| #include "Profiler.h" |
| #include "TinyFormatter.h" |
| #include "Exceptional.h" |
| #include "Profiler.h" |
| #include <set> |
| #include <memory> |
| #include <cctype> |
| |
| #include <assimp/DefaultIOStream.h> |
| #include <assimp/DefaultIOSystem.h> |
| |
| #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS |
| # include "ValidateDataStructure.h" |
| #endif |
| |
| using namespace Assimp::Profiling; |
| using namespace Assimp::Formatter; |
| |
| namespace Assimp { |
| // ImporterRegistry.cpp |
| void GetImporterInstanceList(std::vector< BaseImporter* >& out); |
| void DeleteImporterInstanceList(std::vector< BaseImporter* >& out); |
| |
| // PostStepRegistry.cpp |
| void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out); |
| } |
| |
| using namespace Assimp; |
| using namespace Assimp::Intern; |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Intern::AllocateFromAssimpHeap serves as abstract base class. It overrides |
| // new and delete (and their array counterparts) of public API classes (e.g. Logger) to |
| // utilize our DLL heap. |
| // See http://www.gotw.ca/publications/mill15.htm |
| // ------------------------------------------------------------------------------------------------ |
| void* AllocateFromAssimpHeap::operator new ( size_t num_bytes) { |
| return ::operator new(num_bytes); |
| } |
| |
| void* AllocateFromAssimpHeap::operator new ( size_t num_bytes, const std::nothrow_t& ) throw() { |
| try { |
| return AllocateFromAssimpHeap::operator new( num_bytes ); |
| } |
| catch( ... ) { |
| return NULL; |
| } |
| } |
| |
| void AllocateFromAssimpHeap::operator delete ( void* data) { |
| return ::operator delete(data); |
| } |
| |
| void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes) { |
| return ::operator new[](num_bytes); |
| } |
| |
| void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes, const std::nothrow_t& ) throw() { |
| try { |
| return AllocateFromAssimpHeap::operator new[]( num_bytes ); |
| } |
| catch( ... ) { |
| return NULL; |
| } |
| } |
| |
| void AllocateFromAssimpHeap::operator delete[] ( void* data) { |
| return ::operator delete[](data); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Importer constructor. |
| Importer::Importer() |
| : pimpl( NULL ) { |
| // allocate the pimpl first |
| pimpl = new ImporterPimpl(); |
| |
| pimpl->mScene = NULL; |
| pimpl->mErrorString = ""; |
| |
| // Allocate a default IO handler |
| pimpl->mIOHandler = new DefaultIOSystem; |
| pimpl->mIsDefaultHandler = true; |
| pimpl->bExtraVerbose = false; // disable extra verbose mode by default |
| |
| pimpl->mProgressHandler = new DefaultProgressHandler(); |
| pimpl->mIsDefaultProgressHandler = true; |
| |
| GetImporterInstanceList(pimpl->mImporter); |
| GetPostProcessingStepInstanceList(pimpl->mPostProcessingSteps); |
| |
| // Allocate a SharedPostProcessInfo object and store pointers to it in all post-process steps in the list. |
| pimpl->mPPShared = new SharedPostProcessInfo(); |
| for (std::vector<BaseProcess*>::iterator it = pimpl->mPostProcessingSteps.begin(); |
| it != pimpl->mPostProcessingSteps.end(); |
| ++it) { |
| |
| (*it)->SetSharedData(pimpl->mPPShared); |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Destructor of Importer |
| Importer::~Importer() |
| { |
| // Delete all import plugins |
| DeleteImporterInstanceList(pimpl->mImporter); |
| |
| // Delete all post-processing plug-ins |
| for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) |
| delete pimpl->mPostProcessingSteps[a]; |
| |
| // Delete the assigned IO and progress handler |
| delete pimpl->mIOHandler; |
| delete pimpl->mProgressHandler; |
| |
| // Kill imported scene. Destructors should do that recursivly |
| delete pimpl->mScene; |
| |
| // Delete shared post-processing data |
| delete pimpl->mPPShared; |
| |
| // and finally the pimpl itself |
| delete pimpl; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Copy constructor - copies the config of another Importer, not the scene |
| Importer::Importer(const Importer &other) |
| : pimpl(NULL) { |
| new(this) Importer(); |
| |
| pimpl->mIntProperties = other.pimpl->mIntProperties; |
| pimpl->mFloatProperties = other.pimpl->mFloatProperties; |
| pimpl->mStringProperties = other.pimpl->mStringProperties; |
| pimpl->mMatrixProperties = other.pimpl->mMatrixProperties; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Register a custom post-processing step |
| aiReturn Importer::RegisterPPStep(BaseProcess* pImp) |
| { |
| ai_assert(NULL != pImp); |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| |
| pimpl->mPostProcessingSteps.push_back(pImp); |
| DefaultLogger::get()->info("Registering custom post-processing step"); |
| |
| ASSIMP_END_EXCEPTION_REGION(aiReturn); |
| return AI_SUCCESS; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Register a custom loader plugin |
| aiReturn Importer::RegisterLoader(BaseImporter* pImp) |
| { |
| ai_assert(NULL != pImp); |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| |
| // -------------------------------------------------------------------- |
| // Check whether we would have two loaders for the same file extension |
| // This is absolutely OK, but we should warn the developer of the new |
| // loader that his code will probably never be called if the first |
| // loader is a bit too lazy in his file checking. |
| // -------------------------------------------------------------------- |
| std::set<std::string> st; |
| std::string baked; |
| pImp->GetExtensionList(st); |
| |
| for(std::set<std::string>::const_iterator it = st.begin(); it != st.end(); ++it) { |
| |
| #ifdef ASSIMP_BUILD_DEBUG |
| if (IsExtensionSupported(*it)) { |
| DefaultLogger::get()->warn("The file extension " + *it + " is already in use"); |
| } |
| #endif |
| baked += *it; |
| } |
| |
| // add the loader |
| pimpl->mImporter.push_back(pImp); |
| DefaultLogger::get()->info("Registering custom importer for these file extensions: " + baked); |
| ASSIMP_END_EXCEPTION_REGION(aiReturn); |
| return AI_SUCCESS; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Unregister a custom loader plugin |
| aiReturn Importer::UnregisterLoader(BaseImporter* pImp) |
| { |
| if(!pImp) { |
| // unregistering a NULL importer is no problem for us ... really! |
| return AI_SUCCESS; |
| } |
| |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| std::vector<BaseImporter*>::iterator it = std::find(pimpl->mImporter.begin(), |
| pimpl->mImporter.end(),pImp); |
| |
| if (it != pimpl->mImporter.end()) { |
| pimpl->mImporter.erase(it); |
| DefaultLogger::get()->info("Unregistering custom importer: "); |
| return AI_SUCCESS; |
| } |
| DefaultLogger::get()->warn("Unable to remove custom importer: I can't find you ..."); |
| ASSIMP_END_EXCEPTION_REGION(aiReturn); |
| return AI_FAILURE; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Unregister a custom loader plugin |
| aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) |
| { |
| if(!pImp) { |
| // unregistering a NULL ppstep is no problem for us ... really! |
| return AI_SUCCESS; |
| } |
| |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| std::vector<BaseProcess*>::iterator it = std::find(pimpl->mPostProcessingSteps.begin(), |
| pimpl->mPostProcessingSteps.end(),pImp); |
| |
| if (it != pimpl->mPostProcessingSteps.end()) { |
| pimpl->mPostProcessingSteps.erase(it); |
| DefaultLogger::get()->info("Unregistering custom post-processing step"); |
| return AI_SUCCESS; |
| } |
| DefaultLogger::get()->warn("Unable to remove custom post-processing step: I can't find you .."); |
| ASSIMP_END_EXCEPTION_REGION(aiReturn); |
| return AI_FAILURE; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Supplies a custom IO handler to the importer to open and access files. |
| void Importer::SetIOHandler( IOSystem* pIOHandler) |
| { |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| // If the new handler is zero, allocate a default IO implementation. |
| if (!pIOHandler) |
| { |
| // Release pointer in the possession of the caller |
| pimpl->mIOHandler = new DefaultIOSystem(); |
| pimpl->mIsDefaultHandler = true; |
| } |
| // Otherwise register the custom handler |
| else if (pimpl->mIOHandler != pIOHandler) |
| { |
| delete pimpl->mIOHandler; |
| pimpl->mIOHandler = pIOHandler; |
| pimpl->mIsDefaultHandler = false; |
| } |
| ASSIMP_END_EXCEPTION_REGION(void); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Get the currently set IO handler |
| IOSystem* Importer::GetIOHandler() const |
| { |
| return pimpl->mIOHandler; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Check whether a custom IO handler is currently set |
| bool Importer::IsDefaultIOHandler() const |
| { |
| return pimpl->mIsDefaultHandler; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Supplies a custom progress handler to get regular callbacks during importing |
| void Importer::SetProgressHandler ( ProgressHandler* pHandler ) |
| { |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| // If the new handler is zero, allocate a default implementation. |
| if (!pHandler) |
| { |
| // Release pointer in the possession of the caller |
| pimpl->mProgressHandler = new DefaultProgressHandler(); |
| pimpl->mIsDefaultProgressHandler = true; |
| } |
| // Otherwise register the custom handler |
| else if (pimpl->mProgressHandler != pHandler) |
| { |
| delete pimpl->mProgressHandler; |
| pimpl->mProgressHandler = pHandler; |
| pimpl->mIsDefaultProgressHandler = false; |
| } |
| ASSIMP_END_EXCEPTION_REGION(void); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Get the currently set progress handler |
| ProgressHandler* Importer::GetProgressHandler() const |
| { |
| return pimpl->mProgressHandler; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Check whether a custom progress handler is currently set |
| bool Importer::IsDefaultProgressHandler() const |
| { |
| return pimpl->mIsDefaultProgressHandler; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Validate post process step flags |
| bool _ValidateFlags(unsigned int pFlags) |
| { |
| if (pFlags & aiProcess_GenSmoothNormals && pFlags & aiProcess_GenNormals) { |
| DefaultLogger::get()->error("#aiProcess_GenSmoothNormals and #aiProcess_GenNormals are incompatible"); |
| return false; |
| } |
| if (pFlags & aiProcess_OptimizeGraph && pFlags & aiProcess_PreTransformVertices) { |
| DefaultLogger::get()->error("#aiProcess_OptimizeGraph and #aiProcess_PreTransformVertices are incompatible"); |
| return false; |
| } |
| return true; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Free the current scene |
| void Importer::FreeScene( ) |
| { |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| delete pimpl->mScene; |
| pimpl->mScene = NULL; |
| |
| pimpl->mErrorString = ""; |
| ASSIMP_END_EXCEPTION_REGION(void); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Get the current error string, if any |
| const char* Importer::GetErrorString() const |
| { |
| /* Must remain valid as long as ReadFile() or FreeFile() are not called */ |
| return pimpl->mErrorString.c_str(); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Enable extra-verbose mode |
| void Importer::SetExtraVerbose(bool bDo) |
| { |
| pimpl->bExtraVerbose = bDo; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Get the current scene |
| const aiScene* Importer::GetScene() const |
| { |
| return pimpl->mScene; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Orphan the current scene and return it. |
| aiScene* Importer::GetOrphanedScene() |
| { |
| aiScene* s = pimpl->mScene; |
| |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| pimpl->mScene = NULL; |
| |
| pimpl->mErrorString = ""; /* reset error string */ |
| ASSIMP_END_EXCEPTION_REGION(aiScene*); |
| return s; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Validate post-processing flags |
| bool Importer::ValidateFlags(unsigned int pFlags) const |
| { |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| // run basic checks for mutually exclusive flags |
| if(!_ValidateFlags(pFlags)) { |
| return false; |
| } |
| |
| // ValidateDS does not anymore occur in the pp list, it plays an awesome extra role ... |
| #ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS |
| if (pFlags & aiProcess_ValidateDataStructure) { |
| return false; |
| } |
| #endif |
| pFlags &= ~aiProcess_ValidateDataStructure; |
| |
| // Now iterate through all bits which are set in the flags and check whether we find at least |
| // one pp plugin which handles it. |
| for (unsigned int mask = 1; mask < (1u << (sizeof(unsigned int)*8-1));mask <<= 1) { |
| |
| if (pFlags & mask) { |
| |
| bool have = false; |
| for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { |
| if (pimpl->mPostProcessingSteps[a]-> IsActive(mask) ) { |
| |
| have = true; |
| break; |
| } |
| } |
| if (!have) { |
| return false; |
| } |
| } |
| } |
| ASSIMP_END_EXCEPTION_REGION(bool); |
| return true; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| const aiScene* Importer::ReadFileFromMemory( const void* pBuffer, |
| size_t pLength, |
| unsigned int pFlags, |
| const char* pHint /*= ""*/) |
| { |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| if (!pHint) { |
| pHint = ""; |
| } |
| |
| if (!pBuffer || !pLength || strlen(pHint) > MaxLenHint ) { |
| pimpl->mErrorString = "Invalid parameters passed to ReadFileFromMemory()"; |
| return NULL; |
| } |
| |
| // prevent deletion of the previous IOHandler |
| IOSystem* io = pimpl->mIOHandler; |
| pimpl->mIOHandler = NULL; |
| |
| SetIOHandler(new MemoryIOSystem((const uint8_t*)pBuffer,pLength)); |
| |
| // read the file and recover the previous IOSystem |
| static const size_t BufferSize(Importer::MaxLenHint + 28); |
| char fbuff[ BufferSize ]; |
| ai_snprintf(fbuff, BufferSize, "%s.%s",AI_MEMORYIO_MAGIC_FILENAME,pHint); |
| |
| ReadFile(fbuff,pFlags); |
| SetIOHandler(io); |
| |
| ASSIMP_END_EXCEPTION_REGION(const aiScene*); |
| return pimpl->mScene; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void WriteLogOpening(const std::string& file) |
| { |
| Logger* l = DefaultLogger::get(); |
| if (!l) { |
| return; |
| } |
| l->info("Load " + file); |
| |
| // print a full version dump. This is nice because we don't |
| // need to ask the authors of incoming bug reports for |
| // the library version they're using - a log dump is |
| // sufficient. |
| const unsigned int flags = aiGetCompileFlags(); |
| l->debug(format() |
| << "Assimp " |
| << aiGetVersionMajor() |
| << "." |
| << aiGetVersionMinor() |
| << "." |
| << aiGetVersionRevision() |
| |
| << " " |
| #if defined(ASSIMP_BUILD_ARCHITECTURE) |
| << ASSIMP_BUILD_ARCHITECTURE |
| #elif defined(_M_IX86) || defined(__x86_32__) || defined(__i386__) |
| << "x86" |
| #elif defined(_M_X64) || defined(__x86_64__) |
| << "amd64" |
| #elif defined(_M_IA64) || defined(__ia64__) |
| << "itanium" |
| #elif defined(__ppc__) || defined(__powerpc__) |
| << "ppc32" |
| #elif defined(__powerpc64__) |
| << "ppc64" |
| #elif defined(__arm__) |
| << "arm" |
| #else |
| << "<unknown architecture>" |
| #endif |
| |
| << " " |
| #if defined(ASSIMP_BUILD_COMPILER) |
| << ASSIMP_BUILD_COMPILER |
| #elif defined(_MSC_VER) |
| << "msvc" |
| #elif defined(__GNUC__) |
| << "gcc" |
| #else |
| << "<unknown compiler>" |
| #endif |
| |
| #ifdef ASSIMP_BUILD_DEBUG |
| << " debug" |
| #endif |
| |
| << (flags & ASSIMP_CFLAGS_NOBOOST ? " noboost" : "") |
| << (flags & ASSIMP_CFLAGS_SHARED ? " shared" : "") |
| << (flags & ASSIMP_CFLAGS_SINGLETHREADED ? " singlethreaded" : "") |
| ); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Reads the given file and returns its contents if successful. |
| const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) |
| { |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| const std::string pFile(_pFile); |
| |
| // ---------------------------------------------------------------------- |
| // Put a large try block around everything to catch all std::exception's |
| // that might be thrown by STL containers or by new(). |
| // ImportErrorException's are throw by ourselves and caught elsewhere. |
| //----------------------------------------------------------------------- |
| |
| WriteLogOpening(pFile); |
| |
| #ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS |
| try |
| #endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS |
| { |
| // Check whether this Importer instance has already loaded |
| // a scene. In this case we need to delete the old one |
| if (pimpl->mScene) { |
| |
| DefaultLogger::get()->debug("(Deleting previous scene)"); |
| FreeScene(); |
| } |
| |
| // First check if the file is accessible at all |
| if( !pimpl->mIOHandler->Exists( pFile)) { |
| |
| pimpl->mErrorString = "Unable to open file \"" + pFile + "\"."; |
| DefaultLogger::get()->error(pimpl->mErrorString); |
| return NULL; |
| } |
| |
| std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL); |
| if (profiler) { |
| profiler->BeginRegion("total"); |
| } |
| |
| // Find an worker class which can handle the file |
| BaseImporter* imp = NULL; |
| for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { |
| |
| if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, false)) { |
| imp = pimpl->mImporter[a]; |
| break; |
| } |
| } |
| |
| if (!imp) { |
| // not so bad yet ... try format auto detection. |
| const std::string::size_type s = pFile.find_last_of('.'); |
| if (s != std::string::npos) { |
| DefaultLogger::get()->info("File extension not known, trying signature-based detection"); |
| for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { |
| |
| if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) { |
| imp = pimpl->mImporter[a]; |
| break; |
| } |
| } |
| } |
| // Put a proper error message if no suitable importer was found |
| if( !imp) { |
| pimpl->mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\"."; |
| DefaultLogger::get()->error(pimpl->mErrorString); |
| return NULL; |
| } |
| } |
| |
| // Get file size for progress handler |
| IOStream * fileIO = pimpl->mIOHandler->Open( pFile ); |
| uint32_t fileSize = 0; |
| if (fileIO) |
| { |
| fileSize = static_cast<uint32_t>(fileIO->FileSize()); |
| pimpl->mIOHandler->Close( fileIO ); |
| } |
| |
| // Dispatch the reading to the worker class for this format |
| const aiImporterDesc *desc( imp->GetInfo() ); |
| std::string ext( "unknown" ); |
| if ( NULL != desc ) { |
| ext = desc->mName; |
| } |
| DefaultLogger::get()->info("Found a matching importer for this file format: " + ext + "." ); |
| pimpl->mProgressHandler->UpdateFileRead( 0, fileSize ); |
| |
| if (profiler) { |
| profiler->BeginRegion("import"); |
| } |
| |
| pimpl->mScene = imp->ReadFile( this, pFile, pimpl->mIOHandler); |
| pimpl->mProgressHandler->UpdateFileRead( fileSize, fileSize ); |
| |
| if (profiler) { |
| profiler->EndRegion("import"); |
| } |
| |
| // If successful, apply all active post processing steps to the imported data |
| if( pimpl->mScene) { |
| |
| #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS |
| // The ValidateDS process is an exception. It is executed first, even before ScenePreprocessor is called. |
| if (pFlags & aiProcess_ValidateDataStructure) |
| { |
| ValidateDSProcess ds; |
| ds.ExecuteOnScene (this); |
| if (!pimpl->mScene) { |
| return NULL; |
| } |
| } |
| #endif // no validation |
| |
| // Preprocess the scene and prepare it for post-processing |
| if (profiler) { |
| profiler->BeginRegion("preprocess"); |
| } |
| |
| ScenePreprocessor pre(pimpl->mScene); |
| pre.ProcessScene(); |
| |
| if (profiler) { |
| profiler->EndRegion("preprocess"); |
| } |
| |
| // Ensure that the validation process won't be called twice |
| ApplyPostProcessing(pFlags & (~aiProcess_ValidateDataStructure)); |
| } |
| // if failed, extract the error string |
| else if( !pimpl->mScene) { |
| pimpl->mErrorString = imp->GetErrorText(); |
| } |
| |
| // clear any data allocated by post-process steps |
| pimpl->mPPShared->Clean(); |
| |
| if (profiler) { |
| profiler->EndRegion("total"); |
| } |
| } |
| #ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS |
| catch (std::exception &e) |
| { |
| #if (defined _MSC_VER) && (defined _CPPRTTI) |
| // if we have RTTI get the full name of the exception that occurred |
| pimpl->mErrorString = std::string(typeid( e ).name()) + ": " + e.what(); |
| #else |
| pimpl->mErrorString = std::string("std::exception: ") + e.what(); |
| #endif |
| |
| DefaultLogger::get()->error(pimpl->mErrorString); |
| delete pimpl->mScene; pimpl->mScene = NULL; |
| } |
| #endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS |
| |
| // either successful or failure - the pointer expresses it anyways |
| ASSIMP_END_EXCEPTION_REGION(const aiScene*); |
| return pimpl->mScene; |
| } |
| |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Apply post-processing to the currently bound scene |
| const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) |
| { |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| // Return immediately if no scene is active |
| if (!pimpl->mScene) { |
| return NULL; |
| } |
| |
| // If no flags are given, return the current scene with no further action |
| if (!pFlags) { |
| return pimpl->mScene; |
| } |
| |
| // In debug builds: run basic flag validation |
| ai_assert(_ValidateFlags(pFlags)); |
| DefaultLogger::get()->info("Entering post processing pipeline"); |
| |
| #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS |
| // The ValidateDS process plays an exceptional role. It isn't contained in the global |
| // list of post-processing steps, so we need to call it manually. |
| if (pFlags & aiProcess_ValidateDataStructure) |
| { |
| ValidateDSProcess ds; |
| ds.ExecuteOnScene (this); |
| if (!pimpl->mScene) { |
| return NULL; |
| } |
| } |
| #endif // no validation |
| #ifdef ASSIMP_BUILD_DEBUG |
| if (pimpl->bExtraVerbose) |
| { |
| #ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS |
| DefaultLogger::get()->error("Verbose Import is not available due to build settings"); |
| #endif // no validation |
| pFlags |= aiProcess_ValidateDataStructure; |
| } |
| #else |
| if (pimpl->bExtraVerbose) { |
| DefaultLogger::get()->warn("Not a debug build, ignoring extra verbose setting"); |
| } |
| #endif // ! DEBUG |
| |
| std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL); |
| for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { |
| |
| BaseProcess* process = pimpl->mPostProcessingSteps[a]; |
| pimpl->mProgressHandler->UpdatePostProcess(static_cast<int>(a), static_cast<int>(pimpl->mPostProcessingSteps.size()) ); |
| if( process->IsActive( pFlags)) { |
| |
| if (profiler) { |
| profiler->BeginRegion("postprocess"); |
| } |
| |
| process->ExecuteOnScene ( this ); |
| |
| if (profiler) { |
| profiler->EndRegion("postprocess"); |
| } |
| } |
| if( !pimpl->mScene) { |
| break; |
| } |
| #ifdef ASSIMP_BUILD_DEBUG |
| |
| #ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS |
| continue; |
| #endif // no validation |
| |
| // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step |
| if (pimpl->bExtraVerbose) { |
| DefaultLogger::get()->debug("Verbose Import: revalidating data structures"); |
| |
| ValidateDSProcess ds; |
| ds.ExecuteOnScene (this); |
| if( !pimpl->mScene) { |
| DefaultLogger::get()->error("Verbose Import: failed to revalidate data structures"); |
| break; |
| } |
| } |
| #endif // ! DEBUG |
| } |
| pimpl->mProgressHandler->UpdatePostProcess( static_cast<int>(pimpl->mPostProcessingSteps.size()), static_cast<int>(pimpl->mPostProcessingSteps.size()) ); |
| |
| // update private scene flags |
| if( pimpl->mScene ) |
| ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags; |
| |
| // clear any data allocated by post-process steps |
| pimpl->mPPShared->Clean(); |
| DefaultLogger::get()->info("Leaving post processing pipeline"); |
| |
| ASSIMP_END_EXCEPTION_REGION(const aiScene*); |
| return pimpl->mScene; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) { |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| |
| // Return immediately if no scene is active |
| if ( NULL == pimpl->mScene ) { |
| return NULL; |
| } |
| |
| // If no flags are given, return the current scene with no further action |
| if ( NULL == rootProcess ) { |
| return pimpl->mScene; |
| } |
| |
| // In debug builds: run basic flag validation |
| DefaultLogger::get()->info( "Entering customized post processing pipeline" ); |
| |
| #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS |
| // The ValidateDS process plays an exceptional role. It isn't contained in the global |
| // list of post-processing steps, so we need to call it manually. |
| if ( requestValidation ) |
| { |
| ValidateDSProcess ds; |
| ds.ExecuteOnScene( this ); |
| if ( !pimpl->mScene ) { |
| return NULL; |
| } |
| } |
| #endif // no validation |
| #ifdef ASSIMP_BUILD_DEBUG |
| if ( pimpl->bExtraVerbose ) |
| { |
| #ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS |
| DefaultLogger::get()->error( "Verbose Import is not available due to build settings" ); |
| #endif // no validation |
| } |
| #else |
| if ( pimpl->bExtraVerbose ) { |
| DefaultLogger::get()->warn( "Not a debug build, ignoring extra verbose setting" ); |
| } |
| #endif // ! DEBUG |
| |
| std::unique_ptr<Profiler> profiler( GetPropertyInteger( AI_CONFIG_GLOB_MEASURE_TIME, 0 ) ? new Profiler() : NULL ); |
| |
| if ( profiler ) { |
| profiler->BeginRegion( "postprocess" ); |
| } |
| |
| rootProcess->ExecuteOnScene( this ); |
| |
| if ( profiler ) { |
| profiler->EndRegion( "postprocess" ); |
| } |
| |
| // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step |
| if ( pimpl->bExtraVerbose || requestValidation ) { |
| DefaultLogger::get()->debug( "Verbose Import: revalidating data structures" ); |
| |
| ValidateDSProcess ds; |
| ds.ExecuteOnScene( this ); |
| if ( !pimpl->mScene ) { |
| DefaultLogger::get()->error( "Verbose Import: failed to revalidate data structures" ); |
| } |
| } |
| |
| // clear any data allocated by post-process steps |
| pimpl->mPPShared->Clean(); |
| DefaultLogger::get()->info( "Leaving customized post processing pipeline" ); |
| |
| ASSIMP_END_EXCEPTION_REGION( const aiScene* ); |
| |
| return pimpl->mScene; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Helper function to check whether an extension is supported by ASSIMP |
| bool Importer::IsExtensionSupported(const char* szExtension) const |
| { |
| return NULL != GetImporter(szExtension); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| size_t Importer::GetImporterCount() const |
| { |
| return pimpl->mImporter.size(); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| const aiImporterDesc* Importer::GetImporterInfo(size_t index) const |
| { |
| if (index >= pimpl->mImporter.size()) { |
| return NULL; |
| } |
| return pimpl->mImporter[index]->GetInfo(); |
| } |
| |
| |
| // ------------------------------------------------------------------------------------------------ |
| BaseImporter* Importer::GetImporter (size_t index) const |
| { |
| if (index >= pimpl->mImporter.size()) { |
| return NULL; |
| } |
| return pimpl->mImporter[index]; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Find a loader plugin for a given file extension |
| BaseImporter* Importer::GetImporter (const char* szExtension) const |
| { |
| return GetImporter(GetImporterIndex(szExtension)); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Find a loader plugin for a given file extension |
| size_t Importer::GetImporterIndex (const char* szExtension) const |
| { |
| ai_assert(szExtension); |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| |
| // skip over wildcard and dot characters at string head -- |
| for(;*szExtension == '*' || *szExtension == '.'; ++szExtension); |
| |
| std::string ext(szExtension); |
| if (ext.empty()) { |
| return static_cast<size_t>(-1); |
| } |
| std::transform(ext.begin(),ext.end(), ext.begin(), tolower); |
| |
| std::set<std::string> str; |
| for (std::vector<BaseImporter*>::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) { |
| str.clear(); |
| |
| (*i)->GetExtensionList(str); |
| for (std::set<std::string>::const_iterator it = str.begin(); it != str.end(); ++it) { |
| if (ext == *it) { |
| return std::distance(static_cast< std::vector<BaseImporter*>::const_iterator >(pimpl->mImporter.begin()), i); |
| } |
| } |
| } |
| ASSIMP_END_EXCEPTION_REGION(size_t); |
| return static_cast<size_t>(-1); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Helper function to build a list of all file extensions supported by ASSIMP |
| void Importer::GetExtensionList(aiString& szOut) const |
| { |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| std::set<std::string> str; |
| for (std::vector<BaseImporter*>::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) { |
| (*i)->GetExtensionList(str); |
| } |
| |
| for (std::set<std::string>::const_iterator it = str.begin();; ) { |
| szOut.Append("*."); |
| szOut.Append((*it).c_str()); |
| |
| if (++it == str.end()) { |
| break; |
| } |
| szOut.Append(";"); |
| } |
| ASSIMP_END_EXCEPTION_REGION(void); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Set a configuration property |
| bool Importer::SetPropertyInteger(const char* szName, int iValue) |
| { |
| bool existing; |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| existing = SetGenericProperty<int>(pimpl->mIntProperties, szName,iValue); |
| ASSIMP_END_EXCEPTION_REGION(bool); |
| return existing; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Set a configuration property |
| bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) |
| { |
| bool exising; |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| exising = SetGenericProperty<ai_real>(pimpl->mFloatProperties, szName,iValue); |
| ASSIMP_END_EXCEPTION_REGION(bool); |
| return exising; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Set a configuration property |
| bool Importer::SetPropertyString(const char* szName, const std::string& value) |
| { |
| bool exising; |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| exising = SetGenericProperty<std::string>(pimpl->mStringProperties, szName,value); |
| ASSIMP_END_EXCEPTION_REGION(bool); |
| return exising; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Set a configuration property |
| bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) |
| { |
| bool exising; |
| ASSIMP_BEGIN_EXCEPTION_REGION(); |
| exising = SetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties, szName,value); |
| ASSIMP_END_EXCEPTION_REGION(bool); |
| return exising; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Get a configuration property |
| int Importer::GetPropertyInteger(const char* szName, |
| int iErrorReturn /*= 0xffffffff*/) const |
| { |
| return GetGenericProperty<int>(pimpl->mIntProperties,szName,iErrorReturn); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Get a configuration property |
| ai_real Importer::GetPropertyFloat(const char* szName, |
| ai_real iErrorReturn /*= 10e10*/) const |
| { |
| return GetGenericProperty<ai_real>(pimpl->mFloatProperties,szName,iErrorReturn); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Get a configuration property |
| const std::string Importer::GetPropertyString(const char* szName, |
| const std::string& iErrorReturn /*= ""*/) const |
| { |
| return GetGenericProperty<std::string>(pimpl->mStringProperties,szName,iErrorReturn); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Get a configuration property |
| const aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, |
| const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const |
| { |
| return GetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties,szName,iErrorReturn); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Get the memory requirements of a single node |
| inline void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) |
| { |
| iScene += sizeof(aiNode); |
| iScene += sizeof(unsigned int) * pcNode->mNumMeshes; |
| iScene += sizeof(void*) * pcNode->mNumChildren; |
| |
| for (unsigned int i = 0; i < pcNode->mNumChildren;++i) { |
| AddNodeWeight(iScene,pcNode->mChildren[i]); |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Get the memory requirements of the scene |
| void Importer::GetMemoryRequirements(aiMemoryInfo& in) const |
| { |
| in = aiMemoryInfo(); |
| aiScene* mScene = pimpl->mScene; |
| |
| // return if we have no scene loaded |
| if (!pimpl->mScene) |
| return; |
| |
| |
| in.total = sizeof(aiScene); |
| |
| // add all meshes |
| for (unsigned int i = 0; i < mScene->mNumMeshes;++i) |
| { |
| in.meshes += sizeof(aiMesh); |
| if (mScene->mMeshes[i]->HasPositions()) { |
| in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; |
| } |
| |
| if (mScene->mMeshes[i]->HasNormals()) { |
| in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; |
| } |
| |
| if (mScene->mMeshes[i]->HasTangentsAndBitangents()) { |
| in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices * 2; |
| } |
| |
| for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a) { |
| if (mScene->mMeshes[i]->HasVertexColors(a)) { |
| in.meshes += sizeof(aiColor4D) * mScene->mMeshes[i]->mNumVertices; |
| } |
| else break; |
| } |
| for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) { |
| if (mScene->mMeshes[i]->HasTextureCoords(a)) { |
| in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; |
| } |
| else break; |
| } |
| if (mScene->mMeshes[i]->HasBones()) { |
| in.meshes += sizeof(void*) * mScene->mMeshes[i]->mNumBones; |
| for (unsigned int p = 0; p < mScene->mMeshes[i]->mNumBones;++p) { |
| in.meshes += sizeof(aiBone); |
| in.meshes += mScene->mMeshes[i]->mBones[p]->mNumWeights * sizeof(aiVertexWeight); |
| } |
| } |
| in.meshes += (sizeof(aiFace) + 3 * sizeof(unsigned int))*mScene->mMeshes[i]->mNumFaces; |
| } |
| in.total += in.meshes; |
| |
| // add all embedded textures |
| for (unsigned int i = 0; i < mScene->mNumTextures;++i) { |
| const aiTexture* pc = mScene->mTextures[i]; |
| in.textures += sizeof(aiTexture); |
| if (pc->mHeight) { |
| in.textures += 4 * pc->mHeight * pc->mWidth; |
| } |
| else in.textures += pc->mWidth; |
| } |
| in.total += in.textures; |
| |
| // add all animations |
| for (unsigned int i = 0; i < mScene->mNumAnimations;++i) { |
| const aiAnimation* pc = mScene->mAnimations[i]; |
| in.animations += sizeof(aiAnimation); |
| |
| // add all bone anims |
| for (unsigned int a = 0; a < pc->mNumChannels; ++a) { |
| const aiNodeAnim* pc2 = pc->mChannels[i]; |
| in.animations += sizeof(aiNodeAnim); |
| in.animations += pc2->mNumPositionKeys * sizeof(aiVectorKey); |
| in.animations += pc2->mNumScalingKeys * sizeof(aiVectorKey); |
| in.animations += pc2->mNumRotationKeys * sizeof(aiQuatKey); |
| } |
| } |
| in.total += in.animations; |
| |
| // add all cameras and all lights |
| in.total += in.cameras = sizeof(aiCamera) * mScene->mNumCameras; |
| in.total += in.lights = sizeof(aiLight) * mScene->mNumLights; |
| |
| // add all nodes |
| AddNodeWeight(in.nodes,mScene->mRootNode); |
| in.total += in.nodes; |
| |
| // add all materials |
| for (unsigned int i = 0; i < mScene->mNumMaterials;++i) { |
| const aiMaterial* pc = mScene->mMaterials[i]; |
| in.materials += sizeof(aiMaterial); |
| in.materials += pc->mNumAllocated * sizeof(void*); |
| |
| for (unsigned int a = 0; a < pc->mNumProperties;++a) { |
| in.materials += pc->mProperties[a]->mDataLength; |
| } |
| } |
| in.total += in.materials; |
| } |