From 1aa80ca8da66d9692dd34569aa8624e32b9c3c94 Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Mon, 25 May 2009 17:19:20 +0000 Subject: [PATCH] Adding Importer::ApplyPostProcessing(), uncommenting RegisterPPStep() and UnregisterPPStep(). git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@423 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/Importer.cpp | 243 ++++++++++++++++++++++++++++----------------- include/assimp.hpp | 34 ++++++- 2 files changed, 180 insertions(+), 97 deletions(-) diff --git a/code/Importer.cpp b/code/Importer.cpp index 2237ea116..69b1a94cb 100644 --- a/code/Importer.cpp +++ b/code/Importer.cpp @@ -39,21 +39,25 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file Implementation of the CPP-API class #Importer */ +/** @file Importer.cpp + * @brief Implementation of the CPP-API class #Importer + */ #include "AssimpPCH.h" +// ....................................................................................... /* Uncomment this line to prevent Assimp from catching unknown exceptions. * * Note that any Exception except ImportErrorException may lead to * undefined behaviour -> loaders could remain in an unusable state and * further imports with the same Importer instance could fail/crash/burn ... */ +// ....................................................................................... #define ASSIMP_CATCH_GLOBAL_EXCEPTIONS -// ======================================================================================= +// ....................................................................................... // Internal headers -// ======================================================================================= +// ....................................................................................... #include "BaseImporter.h" #include "BaseProcess.h" #include "DefaultIOStream.h" @@ -62,9 +66,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "ProcessHelper.h" #include "ScenePreprocessor.h" -// ======================================================================================= +// ....................................................................................... // Importers -// ======================================================================================= +// ....................................................................................... #ifndef AI_BUILD_NO_X_IMPORTER # include "XFileImporter.h" #endif @@ -149,17 +153,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef AI_BUILD_NO_3D_IMPORTER # include "UnrealLoader.h" #endif - - - - #ifndef AI_BUILD_NO_LWS_IMPORTER # include "LWSLoader.h" #endif -// ======================================================================================= +// ....................................................................................... // PostProcess-Steps -// ======================================================================================= +// ....................................................................................... #ifndef AI_BUILD_NO_CALCTANGENTS_PROCESS # include "CalcTangentsProcess.h" #endif @@ -230,11 +230,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 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 -// ======================================================================================= +// ....................................................................................... void* AllocateFromAssimpHeap::operator new ( size_t num_bytes) { return ::operator new(num_bytes); } @@ -272,7 +272,6 @@ Importer::Importer() // used more frequently than others should be at the beginning. // ---------------------------------------------------------------------------- pimpl->mImporter.reserve(25); - #if (!defined AI_BUILD_NO_X_IMPORTER) pimpl->mImporter.push_back( new XFileImporter()); #endif @@ -366,9 +365,7 @@ Importer::Importer() // of sequence it is executed. Steps that are added here are not // validated - as RegisterPPStep() does - all dependencies must be given. // ---------------------------------------------------------------------------- - pimpl->mPostProcessingSteps.reserve(25); - #if (!defined AI_BUILD_NO_REMOVEVC_PROCESS) pimpl->mPostProcessingSteps.push_back( new RemoveVCProcess()); #endif @@ -415,8 +412,10 @@ Importer::Importer() pimpl->mPostProcessingSteps.push_back( new GenFaceNormalsProcess()); #endif + // ......................................................................... // DON'T change the order of these five! pimpl->mPostProcessingSteps.push_back( new ComputeSpatialSortProcess()); + // ......................................................................... #if (!defined AI_BUILD_NO_GENVERTEXNORMALS_PROCESS) pimpl->mPostProcessingSteps.push_back( new GenVertexNormalsProcess()); @@ -428,7 +427,9 @@ Importer::Importer() pimpl->mPostProcessingSteps.push_back( new JoinVerticesProcess()); #endif + // ......................................................................... pimpl->mPostProcessingSteps.push_back( new DestroySpatialSortProcess()); + // ......................................................................... #if (!defined AI_BUILD_NO_SPLITLARGEMESHES_PROCESS) pimpl->mPostProcessingSteps.push_back( new SplitLargeMeshesProcess_Vertex()); @@ -449,12 +450,12 @@ Importer::Importer() pimpl->mPostProcessingSteps.push_back( new ImproveCacheLocalityProcess()); #endif - // Allocate a SharedPostProcessInfo object and store pointers to it - // in all post-process steps in the list. + // Allocate a SharedPostProcessInfo object and store pointers to it in all post-process steps in the list. pimpl->mPPShared = new SharedPostProcessInfo(); - for (std::vector::iterator it = pimpl->mPostProcessingSteps.begin(), - end = pimpl->mPostProcessingSteps.end(); it != end; ++it) - { + for (std::vector::iterator it = pimpl->mPostProcessingSteps.begin(); + it != pimpl->mPostProcessingSteps.end(); + ++it) { + (*it)->SetSharedData(pimpl->mPPShared); } } @@ -490,11 +491,22 @@ Importer::Importer(const Importer &other) { new(this) Importer(); - pimpl->mIntProperties = other.pimpl->mIntProperties; - pimpl->mFloatProperties = other.pimpl->mFloatProperties; + pimpl->mIntProperties = other.pimpl->mIntProperties; + pimpl->mFloatProperties = other.pimpl->mFloatProperties; pimpl->mStringProperties = other.pimpl->mStringProperties; } +// ------------------------------------------------------------------------------------------------ +// Register a custom post-processing step +aiReturn Importer::RegisterPPStep(BaseProcess* pImp) +{ + ai_assert(NULL != pImp); + + pimpl->mPostProcessingSteps.push_back(pImp); + DefaultLogger::get()->info("Registering custom post-processing step"); + return AI_SUCCESS; +} + // ------------------------------------------------------------------------------------------------ // Register a custom loader plugin aiReturn Importer::RegisterLoader(BaseImporter* pImp) @@ -503,16 +515,15 @@ aiReturn Importer::RegisterLoader(BaseImporter* pImp) // -------------------------------------------------------------------- // 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. + // This is absolutely OK, but we should warn the developer of the new + // loader that his code will probably never be called.s // -------------------------------------------------------------------- std::string st; pImp->GetExtensionList(st); #ifdef _DEBUG - const char* sz = ::strtok(const_cast(st.c_str()),";"); - while (sz) - { + const char* sz = ::strtok(const_cast(st.c_str()),";"); // evil + while (sz) { if (IsExtensionSupported(std::string(sz))) DefaultLogger::get()->warn(std::string( "The file extension " ) + sz + " is already in use"); @@ -540,7 +551,22 @@ aiReturn Importer::UnregisterLoader(BaseImporter* pImp) DefaultLogger::get()->info("Unregistering custom importer: " + st); return AI_SUCCESS; } - DefaultLogger::get()->warn("Unable to remove importer: importer object not found in table"); + DefaultLogger::get()->warn("Unable to remove custom importer: I can't find you ..."); + return AI_FAILURE; +} + +// ------------------------------------------------------------------------------------------------ +// Unregister a custom loader plugin +aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) +{ + ai_assert(NULL != pImp); + std::vector::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 .."); return AI_FAILURE; } @@ -582,11 +608,12 @@ bool Importer::IsDefaultIOHandler() // 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 may not be specified together"); + 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; @@ -675,8 +702,6 @@ bool Importer::ValidateFlags(unsigned int pFlags) // Reads the given file and returns its contents if successful. const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) { - // In debug builds: run a basic flag validation - ai_assert(_ValidateFlags(pFlags)); const std::string pFile(_pFile); // ---------------------------------------------------------------------- @@ -684,6 +709,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) // that might be thrown by STL containers or by new(). // ImportErrorException's are throw by ourselves and caught elsewhere. //----------------------------------------------------------------------- + #ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS try #endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS @@ -743,77 +769,31 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) pimpl->mScene = imp->ReadFile( pFile, pimpl->mIOHandler); // If successful, apply all active post processing steps to the imported data - if( pimpl->mScene) - { + if( pimpl->mScene) { + #ifndef AI_BUILD_NO_VALIDATEDS_PROCESS - // The ValidateDS process is an exception. It is executed first, - // even before ScenePreprocessor is called. + // 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) + if (!pimpl->mScene) { return NULL; + } } #endif // no validation - // Preprocess the scene + // Preprocess the scene and prepare it for post-processing ScenePreprocessor pre(pimpl->mScene); pre.ProcessScene(); - DefaultLogger::get()->info("Import successful, entering postprocessing-steps"); -#ifdef _DEBUG - if (pimpl->bExtraVerbose) - { -#ifndef AI_BUILD_NO_VALIDATEDS_PROCESS - - DefaultLogger::get()->error("Extra verbose mode not available, library" - " wasn't build with the ValidateDS-Step"); -#endif // no validation - - - pFlags |= aiProcess_ValidateDataStructure; - } -#else - if (pimpl->bExtraVerbose) - DefaultLogger::get()->warn("Not a debug build, ignoring extra verbose setting"); -#endif // ! DEBUG - for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) - { - BaseProcess* process = pimpl->mPostProcessingSteps[a]; - if( process->IsActive( pFlags)) - { - process->SetupProperties( this ); - process->ExecuteOnScene ( this ); - } - if( !pimpl->mScene) - break; -#ifdef _DEBUG - -#ifndef AI_BUILD_NO_VALIDATEDS_PROCESS - continue; -#endif // no validation - - // If the extra verbose mode is active execute the - // VaidateDataStructureStep again after each step - if (pimpl->bExtraVerbose) - { - DefaultLogger::get()->debug("Extra verbose: revalidating data structures"); - - ValidateDSProcess ds; - ds.ExecuteOnScene (this); - if( !pimpl->mScene) - { - DefaultLogger::get()->error("Extra verbose: failed to revalidate data structures"); - break; - } - } -#endif // ! DEBUG - } + // Ensure that the validation process won't be called twice + ApplyPostProcessing(pFlags & (~aiProcess_ValidateDataStructure)); } // if failed, extract the error string - else if( !pimpl->mScene) + else if( !pimpl->mScene) { pimpl->mErrorString = imp->GetErrorText(); + } // clear any data allocated by post-process steps pimpl->mPPShared->Clean(); @@ -837,6 +817,85 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) return pimpl->mScene; } +// ------------------------------------------------------------------------------------------------ +// Apply post-processing to the currently bound scene +const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) +{ + // 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 AI_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 _DEBUG + if (pimpl->bExtraVerbose) + { +#ifndef AI_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 + for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { + + BaseProcess* process = pimpl->mPostProcessingSteps[a]; + if( process->IsActive( pFlags)) { + + process->SetupProperties( this ); + process->ExecuteOnScene ( this ); + } + if( !pimpl->mScene) { + break; + } +#ifdef _DEBUG + +#ifndef AI_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 + } + + // clear any data allocated by post-process steps + pimpl->mPPShared->Clean(); + DefaultLogger::get()->info("Leaving post processing pipeline"); + return pimpl->mScene; +} + // ------------------------------------------------------------------------------------------------ // Helper function to check whether an extension is supported by ASSIMP bool Importer::IsExtensionSupported(const char* szExtension) diff --git a/include/assimp.hpp b/include/assimp.hpp index d494acccf..8f6fca490 100644 --- a/include/assimp.hpp +++ b/include/assimp.hpp @@ -164,18 +164,19 @@ public: */ aiReturn UnregisterLoader(BaseImporter* pImp); -#if 0 // ------------------------------------------------------------------- /** Registers a new post-process step. * + * At the moment, there's a small limitation: new post processing + * steps are added to end of the list, or in other words, executed + * last, after all built-in steps. * @param pImp Post-process step to be added. The Importer instance * takes ownership of the pointer, so it will be automatically * deleted with the Importer instance. - * @return AI_SUCCESS if the step has been added. + * @return AI_SUCCESS if the step has been added correctly. */ aiReturn RegisterPPStep(BaseProcess* pImp); - // ------------------------------------------------------------------- /** Unregisters a post-process step. * @@ -186,7 +187,7 @@ public: * if it has not yet been registered. */ aiReturn UnregisterPPStep(BaseProcess* pImp); -#endif + // ------------------------------------------------------------------- /** Set an integer configuration property. @@ -312,7 +313,9 @@ public: * @param pFile Path and filename to the file to be imported. * @param pFlags Optional post processing steps to be executed after * a successful import. Provide a bitwise combination of the - * #aiPostProcessSteps flags. + * #aiPostProcessSteps flags. If you wish to inspect the imported + * scene first in order to fine-tune your post-processing setup, + * consider to use #ApplyPostProcessing(). * @return A pointer to the imported data, NULL if the import failed. * The pointer to the scene remains in possession of the Importer * instance. Use GetOrphanedScene() to take ownership of it. @@ -323,6 +326,27 @@ public: */ const aiScene* ReadFile( const char* pFile, unsigned int pFlags); + // ------------------------------------------------------------------- + /** Apply post-processing to an already-imported scene. + * + * This is strictly equivalent to calling #ReadFile() with the same + * flags. However, you can use this separate function to inspect + * the imported scene first to fine-tune your post-processing setup. + * @param pFlags Provide a bitwise combination of the + * #aiPostProcessSteps flags. + * @return A pointer to the post-processed data. This is still the + * same as the pointer returned by #ReadFile(). However, if + * post-processing fails severly the scene could now be NULL. + * That's quite a rare case, post processing steps are not really + * designed to 'fail'. To be exact, the #aiProcess_ValidateDS + * flag is currently the only post processing step which can actually + * cause the scene to be reset to NULL. + * + * @note The method does nothing if no scene is currently bound + * to the #Importer instance. + */ + const aiScene* ApplyPostProcessing(unsigned int pFlags); + // ------------------------------------------------------------------- /** @brief Reads the given file and returns its contents if successful. *