2
0
Эх сурвалжийг харах

Merge pull request #2810 from MalcolmTyrrell/ModellerMetaData

Modeller meta data
Kim Kulling 5 жил өмнө
parent
commit
016115628e

+ 157 - 125
code/Common/Importer.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2019, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -78,6 +76,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/TinyFormatter.h>
 #include <assimp/Exceptional.h>
 #include <assimp/Profiler.h>
+#include <assimp/commonMetaData.h>
+
 #include <set>
 #include <memory>
 #include <cctype>
@@ -119,7 +119,7 @@ void* AllocateFromAssimpHeap::operator new ( size_t num_bytes, const std::nothro
         return AllocateFromAssimpHeap::operator new( num_bytes );
     }
     catch( ... )    {
-        return NULL;
+        return nullptr;
     }
 }
 
@@ -134,9 +134,8 @@ void* AllocateFromAssimpHeap::operator new[] ( size_t 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;
+    } catch( ... )    {
+        return nullptr;
     }
 }
 
@@ -148,7 +147,7 @@ void AllocateFromAssimpHeap::operator delete[] ( void* data)    {
 // Importer constructor.
 Importer::Importer()
  : pimpl( new ImporterPimpl ) {
-    pimpl->mScene = NULL;
+    pimpl->mScene = nullptr;
     pimpl->mErrorString = "";
 
     // Allocate a default IO handler
@@ -174,14 +173,14 @@ Importer::Importer()
 
 // ------------------------------------------------------------------------------------------------
 // Destructor of Importer
-Importer::~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++)
+    for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); ++a ) {
         delete pimpl->mPostProcessingSteps[a];
+    }
 
     // Delete the assigned IO and progress handler
     delete pimpl->mIOHandler;
@@ -199,9 +198,9 @@ Importer::~Importer()
 
 // ------------------------------------------------------------------------------------------------
 // Register a custom post-processing step
-aiReturn Importer::RegisterPPStep(BaseProcess* pImp)
-{
-    ai_assert(NULL != pImp);
+aiReturn Importer::RegisterPPStep(BaseProcess* pImp) {
+    ai_assert( nullptr != pImp );
+    
     ASSIMP_BEGIN_EXCEPTION_REGION();
 
         pimpl->mPostProcessingSteps.push_back(pImp);
@@ -213,9 +212,9 @@ aiReturn Importer::RegisterPPStep(BaseProcess* pImp)
 
 // ------------------------------------------------------------------------------------------------
 // Register a custom loader plugin
-aiReturn Importer::RegisterLoader(BaseImporter* pImp)
-{
-    ai_assert(NULL != pImp);
+aiReturn Importer::RegisterLoader(BaseImporter* pImp) {
+    ai_assert(nullptr != pImp);
+    
     ASSIMP_BEGIN_EXCEPTION_REGION();
 
     // --------------------------------------------------------------------
@@ -242,13 +241,13 @@ aiReturn Importer::RegisterLoader(BaseImporter* pImp)
     pimpl->mImporter.push_back(pImp);
     ASSIMP_LOG_INFO_F("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)
-{
+aiReturn Importer::UnregisterLoader(BaseImporter* pImp) {
     if(!pImp) {
         // unregistering a NULL importer is no problem for us ... really!
         return AI_SUCCESS;
@@ -265,13 +264,13 @@ aiReturn Importer::UnregisterLoader(BaseImporter* pImp)
     }
     ASSIMP_LOG_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)
-{
+aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) {
     if(!pImp) {
         // unregistering a NULL ppstep is no problem for us ... really!
         return AI_SUCCESS;
@@ -288,24 +287,22 @@ aiReturn Importer::UnregisterPPStep(BaseProcess* pImp)
     }
     ASSIMP_LOG_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)
-{
+void Importer::SetIOHandler( IOSystem* pIOHandler) {
+    ai_assert(nullptr != pimpl);
+    
     ASSIMP_BEGIN_EXCEPTION_REGION();
     // If the new handler is zero, allocate a default IO implementation.
-    if (!pIOHandler)
-    {
+    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)
-    {
+    } else if (pimpl->mIOHandler != pIOHandler) { // Otherwise register the custom handler
         delete pimpl->mIOHandler;
         pimpl->mIOHandler = pIOHandler;
         pimpl->mIsDefaultHandler = false;
@@ -316,29 +313,32 @@ void Importer::SetIOHandler( IOSystem* pIOHandler)
 // ------------------------------------------------------------------------------------------------
 // Get the currently set IO handler
 IOSystem* Importer::GetIOHandler() const {
+    ai_assert(nullptr != pimpl);
+    
     return pimpl->mIOHandler;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Check whether a custom IO handler is currently set
 bool Importer::IsDefaultIOHandler() const {
+    ai_assert(nullptr != pimpl);
+    
     return pimpl->mIsDefaultHandler;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Supplies a custom progress handler to get regular callbacks during importing
 void Importer::SetProgressHandler ( ProgressHandler* pHandler ) {
+    ai_assert(nullptr != pimpl);
+    
     ASSIMP_BEGIN_EXCEPTION_REGION();
+    
     // If the new handler is zero, allocate a default implementation.
-    if (!pHandler)
-    {
+    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)
-    {
+    } else if (pimpl->mProgressHandler != pHandler) { // Otherwise register the custom handler
         delete pimpl->mProgressHandler;
         pimpl->mProgressHandler = pHandler;
         pimpl->mIsDefaultProgressHandler = false;
@@ -349,19 +349,22 @@ void Importer::SetProgressHandler ( ProgressHandler* pHandler ) {
 // ------------------------------------------------------------------------------------------------
 // Get the currently set progress handler
 ProgressHandler* Importer::GetProgressHandler() const {
+    ai_assert(nullptr != pimpl);
+    
     return pimpl->mProgressHandler;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Check whether a custom progress handler is currently set
 bool Importer::IsDefaultProgressHandler() const {
+    ai_assert(nullptr != pimpl);
+    
     return pimpl->mIsDefaultProgressHandler;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Validate post process step flags
-bool _ValidateFlags(unsigned int pFlags)
-{
+bool _ValidateFlags(unsigned int pFlags) {
     if (pFlags & aiProcess_GenSmoothNormals && pFlags & aiProcess_GenNormals)   {
         ASSIMP_LOG_ERROR("#aiProcess_GenSmoothNormals and #aiProcess_GenNormals are incompatible");
         return false;
@@ -375,12 +378,13 @@ bool _ValidateFlags(unsigned int pFlags)
 
 // ------------------------------------------------------------------------------------------------
 // Free the current scene
-void Importer::FreeScene( )
-{
+void Importer::FreeScene( ) {
+    ai_assert(nullptr != pimpl);
+    
     ASSIMP_BEGIN_EXCEPTION_REGION();
 
     delete pimpl->mScene;
-    pimpl->mScene = NULL;
+    pimpl->mScene = nullptr;
 
     pimpl->mErrorString = "";
     ASSIMP_END_EXCEPTION_REGION(void);
@@ -388,44 +392,48 @@ void Importer::FreeScene( )
 
 // ------------------------------------------------------------------------------------------------
 // Get the current error string, if any
-const char* Importer::GetErrorString() const
-{
-     /* Must remain valid as long as ReadFile() or FreeFile() are not called */
+const char* Importer::GetErrorString() const {
+    ai_assert(nullptr != pimpl);
+    
+    // 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)
-{
+void Importer::SetExtraVerbose(bool bDo) {
+    ai_assert(nullptr != pimpl);
+    
     pimpl->bExtraVerbose = bDo;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Get the current scene
-const aiScene* Importer::GetScene() const
-{
+const aiScene* Importer::GetScene() const {
+    ai_assert(nullptr != pimpl);
+    
     return pimpl->mScene;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Orphan the current scene and return it.
-aiScene* Importer::GetOrphanedScene()
-{
+aiScene* Importer::GetOrphanedScene() {
+    ai_assert(nullptr != pimpl);
+    
     aiScene* s = pimpl->mScene;
 
     ASSIMP_BEGIN_EXCEPTION_REGION();
-    pimpl->mScene = NULL;
+    pimpl->mScene = nullptr;
 
-    pimpl->mErrorString = ""; /* reset error string */
+    pimpl->mErrorString = ""; // reset error string
     ASSIMP_END_EXCEPTION_REGION(aiScene*);
+    
     return s;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Validate post-processing flags
-bool Importer::ValidateFlags(unsigned int pFlags) const
-{
+bool Importer::ValidateFlags(unsigned int pFlags) const {
     ASSIMP_BEGIN_EXCEPTION_REGION();
     // run basic checks for mutually exclusive flags
     if(!_ValidateFlags(pFlags)) {
@@ -467,8 +475,9 @@ bool Importer::ValidateFlags(unsigned int pFlags) const
 const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
     size_t pLength,
     unsigned int pFlags,
-    const char* pHint /*= ""*/)
-{
+    const char* pHint /*= ""*/) {
+    ai_assert(nullptr != pimpl);
+    
     ASSIMP_BEGIN_EXCEPTION_REGION();
     if (!pHint) {
         pHint = "";
@@ -476,12 +485,12 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
 
     if (!pBuffer || !pLength || strlen(pHint) > MaxLenHint ) {
         pimpl->mErrorString = "Invalid parameters passed to ReadFileFromMemory()";
-        return NULL;
+        return nullptr;
     }
 
     // prevent deletion of the previous IOHandler
     IOSystem* io = pimpl->mIOHandler;
-    pimpl->mIOHandler = NULL;
+    pimpl->mIOHandler = nullptr;
 
     SetIOHandler(new MemoryIOSystem((const uint8_t*)pBuffer,pLength,io));
 
@@ -498,8 +507,8 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
 }
 
 // ------------------------------------------------------------------------------------------------
-void WriteLogOpening(const std::string& file)
-{
+void WriteLogOpening(const std::string& file) {
+    
     ASSIMP_LOG_INFO_F("Load ", file);
 
     // print a full version dump. This is nice because we don't
@@ -550,8 +559,9 @@ void WriteLogOpening(const std::string& file)
 
 // ------------------------------------------------------------------------------------------------
 // Reads the given file and returns its contents if successful.
-const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
-{
+const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) {
+    ai_assert(nullptr != pimpl);
+    
     ASSIMP_BEGIN_EXCEPTION_REGION();
     const std::string pFile(_pFile);
 
@@ -580,7 +590,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
 
             pimpl->mErrorString = "Unable to open file \"" + pFile + "\".";
             ASSIMP_LOG_ERROR(pimpl->mErrorString);
-            return NULL;
+            return nullptr;
         }
 
         std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL);
@@ -589,7 +599,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
         }
 
         // Find an worker class which can handle the file
-        BaseImporter* imp = NULL;
+        BaseImporter* imp = nullptr;
         SetPropertyInteger("importerIndex", -1);
         for( unsigned int a = 0; a < pimpl->mImporter.size(); a++)  {
 
@@ -617,7 +627,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
             if( !imp)   {
                 pimpl->mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\".";
                 ASSIMP_LOG_ERROR(pimpl->mErrorString);
-                return NULL;
+                return nullptr;
             }
         }
 
@@ -633,7 +643,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
         // Dispatch the reading to the worker class for this format
         const aiImporterDesc *desc( imp->GetInfo() );
         std::string ext( "unknown" );
-        if ( NULL != desc ) {
+        if ( nullptr != desc ) {
             ext = desc->mName;
         }
         ASSIMP_LOG_INFO("Found a matching importer for this file format: " + ext + "." );
@@ -654,15 +664,20 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
 
         // If successful, apply all active post processing steps to the imported data
         if( pimpl->mScene)  {
+            if (!pimpl->mScene->mMetaData || !pimpl->mScene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT)) {
+                if (!pimpl->mScene->mMetaData) {
+                    pimpl->mScene->mMetaData = new aiMetadata;
+                }
+                pimpl->mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT, aiString(ext));
+            }
 
 #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)
-            {
+            if (pFlags & aiProcess_ValidateDataStructure) {
                 ValidateDSProcess ds;
                 ds.ExecuteOnScene (this);
                 if (!pimpl->mScene) {
-                    return NULL;
+                    return nullptr;
                 }
             }
 #endif // no validation
@@ -695,8 +710,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
         }
     }
 #ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
-    catch (std::exception &e)
-    {
+    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();
@@ -705,24 +719,26 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
 #endif
 
         ASSIMP_LOG_ERROR(pimpl->mErrorString);
-        delete pimpl->mScene; pimpl->mScene = NULL;
+        delete pimpl->mScene; pimpl->mScene = nullptr;
     }
 #endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
 
     // either successful or failure - the pointer expresses it anyways
     ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString);
+    
     return pimpl->mScene;
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // Apply post-processing to the currently bound scene
-const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
-{
+const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) {
+    ai_assert(nullptr != pimpl);
+    
     ASSIMP_BEGIN_EXCEPTION_REGION();
     // Return immediately if no scene is active
     if (!pimpl->mScene) {
-        return NULL;
+        return nullptr;
     }
 
     // If no flags are given, return the current scene with no further action
@@ -737,12 +753,11 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
 #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)
-    {
+    if (pFlags & aiProcess_ValidateDataStructure) {
         ValidateDSProcess ds;
         ds.ExecuteOnScene (this);
         if (!pimpl->mScene) {
-            return NULL;
+            return nullptr;
         }
     }
 #endif // no validation
@@ -762,11 +777,9 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
 
     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");
             }
@@ -803,24 +816,28 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
         static_cast<int>(pimpl->mPostProcessingSteps.size()) );
 
     // update private scene flags
-    if( pimpl->mScene )
+    if( pimpl->mScene ) {
       ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags;
+    }
 
     // clear any data allocated by post-process steps
     pimpl->mPPShared->Clean();
     ASSIMP_LOG_INFO("Leaving post processing pipeline");
 
     ASSIMP_END_EXCEPTION_REGION(const aiScene*);
+    
     return pimpl->mScene;
 }
 
 // ------------------------------------------------------------------------------------------------
 const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) {
+    ai_assert(nullptr != pimpl);
+    
     ASSIMP_BEGIN_EXCEPTION_REGION();
 
     // Return immediately if no scene is active
-    if ( NULL == pimpl->mScene ) {
-        return NULL;
+    if ( nullptr == pimpl->mScene ) {
+        return nullptr;
     }
 
     // If no flags are given, return the current scene with no further action
@@ -839,7 +856,7 @@ const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess
         ValidateDSProcess ds;
         ds.ExecuteOnScene( this );
         if ( !pimpl->mScene ) {
-            return NULL;
+            return nullptr;
         }
     }
 #endif // no validation
@@ -890,46 +907,50 @@ const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess
 
 // ------------------------------------------------------------------------------------------------
 // Helper function to check whether an extension is supported by ASSIMP
-bool Importer::IsExtensionSupported(const char* szExtension) const
-{
+bool Importer::IsExtensionSupported(const char* szExtension) const {
     return nullptr != GetImporter(szExtension);
 }
 
 // ------------------------------------------------------------------------------------------------
-size_t Importer::GetImporterCount() const
-{
+size_t Importer::GetImporterCount() const {
+    ai_assert(nullptr != pimpl);
+    
     return pimpl->mImporter.size();
 }
 
 // ------------------------------------------------------------------------------------------------
-const aiImporterDesc* Importer::GetImporterInfo(size_t index) const
-{
+const aiImporterDesc* Importer::GetImporterInfo(size_t index) const {
+    ai_assert(nullptr != pimpl);
+    
     if (index >= pimpl->mImporter.size()) {
-        return NULL;
+        return nullptr;
     }
     return pimpl->mImporter[index]->GetInfo();
 }
 
 
 // ------------------------------------------------------------------------------------------------
-BaseImporter* Importer::GetImporter (size_t index) const
-{
+BaseImporter* Importer::GetImporter (size_t index) const {
+    ai_assert(nullptr != pimpl);
+    
     if (index >= pimpl->mImporter.size()) {
-        return NULL;
+        return nullptr;
     }
     return pimpl->mImporter[index];
 }
 
 // ------------------------------------------------------------------------------------------------
 // Find a loader plugin for a given file extension
-BaseImporter* Importer::GetImporter (const char* szExtension) const
-{
+BaseImporter* Importer::GetImporter (const char* szExtension) const {
+    ai_assert(nullptr != pimpl);
+    
     return GetImporter(GetImporterIndex(szExtension));
 }
 
 // ------------------------------------------------------------------------------------------------
 // Find a loader plugin for a given file extension
 size_t Importer::GetImporterIndex (const char* szExtension) const {
+    ai_assert(nullptr != pimpl);
     ai_assert(nullptr != szExtension);
 
     ASSIMP_BEGIN_EXCEPTION_REGION();
@@ -960,8 +981,9 @@ size_t Importer::GetImporterIndex (const char* szExtension) const {
 
 // ------------------------------------------------------------------------------------------------
 // Helper function to build a list of all file extensions supported by ASSIMP
-void Importer::GetExtensionList(aiString& szOut) const
-{
+void Importer::GetExtensionList(aiString& szOut) const {
+    ai_assert(nullptr != pimpl);
+    
     ASSIMP_BEGIN_EXCEPTION_REGION();
     std::set<std::string> str;
     for (std::vector<BaseImporter*>::const_iterator i =  pimpl->mImporter.begin();i != pimpl->mImporter.end();++i)  {
@@ -985,8 +1007,9 @@ void Importer::GetExtensionList(aiString& szOut) const
 
 // ------------------------------------------------------------------------------------------------
 // Set a configuration property
-bool Importer::SetPropertyInteger(const char* szName, int iValue)
-{
+bool Importer::SetPropertyInteger(const char* szName, int iValue) {
+    ai_assert(nullptr != pimpl);
+    
     bool existing;
     ASSIMP_BEGIN_EXCEPTION_REGION();
         existing = SetGenericProperty<int>(pimpl->mIntProperties, szName,iValue);
@@ -996,8 +1019,9 @@ bool Importer::SetPropertyInteger(const char* szName, int iValue)
 
 // ------------------------------------------------------------------------------------------------
 // Set a configuration property
-bool Importer::SetPropertyFloat(const char* szName, ai_real iValue)
-{
+bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) {
+    ai_assert(nullptr != pimpl);
+    
     bool existing;
     ASSIMP_BEGIN_EXCEPTION_REGION();
         existing = SetGenericProperty<ai_real>(pimpl->mFloatProperties, szName,iValue);
@@ -1007,8 +1031,9 @@ bool Importer::SetPropertyFloat(const char* szName, ai_real iValue)
 
 // ------------------------------------------------------------------------------------------------
 // Set a configuration property
-bool Importer::SetPropertyString(const char* szName, const std::string& value)
-{
+bool Importer::SetPropertyString(const char* szName, const std::string& value) {
+    ai_assert(nullptr != pimpl);
+    
     bool existing;
     ASSIMP_BEGIN_EXCEPTION_REGION();
         existing = SetGenericProperty<std::string>(pimpl->mStringProperties, szName,value);
@@ -1018,8 +1043,9 @@ bool Importer::SetPropertyString(const char* szName, const std::string& value)
 
 // ------------------------------------------------------------------------------------------------
 // Set a configuration property
-bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value)
-{
+bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) {
+    ai_assert(nullptr != pimpl);
+    
     bool existing;
     ASSIMP_BEGIN_EXCEPTION_REGION();
         existing = SetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties, szName,value);
@@ -1029,40 +1055,43 @@ bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value)
 
 // ------------------------------------------------------------------------------------------------
 // Get a configuration property
-int Importer::GetPropertyInteger(const char* szName,
-    int iErrorReturn /*= 0xffffffff*/) const
-{
+int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const {
+    ai_assert(nullptr != pimpl);
+    
     return GetGenericProperty<int>(pimpl->mIntProperties,szName,iErrorReturn);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Get a configuration property
-ai_real Importer::GetPropertyFloat(const char* szName,
-    ai_real iErrorReturn /*= 10e10*/) const
-{
+ai_real Importer::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const {
+    ai_assert(nullptr != pimpl);
+    
     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
-{
+const std::string Importer::GetPropertyString(const char* szName, const std::string& iErrorReturn /*= ""*/) const {
+    ai_assert(nullptr != pimpl);
+    
     return GetGenericProperty<std::string>(pimpl->mStringProperties,szName,iErrorReturn);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Get a configuration property
-const aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName,
-    const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const
-{
+const aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const {
+    ai_assert(nullptr != pimpl);
+    
     return GetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties,szName,iErrorReturn);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Get the memory requirements of a single node
-inline void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode)
-{
+inline 
+void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) {
+    if ( nullptr == pcNode ) {
+        return;
+    }
     iScene += sizeof(aiNode);
     iScene += sizeof(unsigned int) * pcNode->mNumMeshes;
     iScene += sizeof(void*) * pcNode->mNumChildren;
@@ -1074,8 +1103,9 @@ inline void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode)
 
 // ------------------------------------------------------------------------------------------------
 // Get the memory requirements of the scene
-void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
-{
+void Importer::GetMemoryRequirements(aiMemoryInfo& in) const {
+    ai_assert(nullptr != pimpl);
+    
     in = aiMemoryInfo();
     aiScene* mScene = pimpl->mScene;
 
@@ -1087,8 +1117,7 @@ void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
     in.total = sizeof(aiScene);
 
     // add all meshes
-    for (unsigned int i = 0; i < mScene->mNumMeshes;++i)
-    {
+    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;
@@ -1105,14 +1134,16 @@ void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
         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;
             }
-            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;
             }
-            else break;
         }
         if (mScene->mMeshes[i]->HasBones()) {
             in.meshes += sizeof(void*) * mScene->mMeshes[i]->mNumBones;
@@ -1131,8 +1162,9 @@ void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
         in.textures += sizeof(aiTexture);
         if (pc->mHeight) {
             in.textures += 4 * pc->mHeight * pc->mWidth;
+        } else {
+            in.textures += pc->mWidth;
         }
-        else in.textures += pc->mWidth;
     }
     in.total += in.textures;
 

+ 10 - 1
code/FBX/FBXConverter.cpp

@@ -60,6 +60,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/scene.h>
 
 #include <assimp/CreateAnimMesh.h>
+#include <assimp/commonMetaData.h>
+#include <assimp/StringUtils.h>
 
 #include <tuple>
 #include <memory>
@@ -3603,7 +3605,9 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
                 return;
             }
 
-            out->mMetaData = aiMetadata::Alloc(15);
+            const bool hasGenerator = !doc.Creator().empty();
+
+            out->mMetaData = aiMetadata::Alloc(16 + (hasGenerator ? 1 : 0));
             out->mMetaData->Set(0, "UpAxis", doc.GlobalSettings().UpAxis());
             out->mMetaData->Set(1, "UpAxisSign", doc.GlobalSettings().UpAxisSign());
             out->mMetaData->Set(2, "FrontAxis", doc.GlobalSettings().FrontAxis());
@@ -3619,6 +3623,11 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
             out->mMetaData->Set(12, "TimeSpanStart", doc.GlobalSettings().TimeSpanStart());
             out->mMetaData->Set(13, "TimeSpanStop", doc.GlobalSettings().TimeSpanStop());
             out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate());
+            out->mMetaData->Set(15, AI_METADATA_SOURCE_FORMAT_VERSION, aiString(to_string(doc.FBXVersion())));
+            if (hasGenerator)
+            {
+                out->mMetaData->Set(16, AI_METADATA_SOURCE_GENERATOR, aiString(doc.Creator()));
+            }
         }
 
         void FBXConverter::TransferDataToScene()

+ 2 - 0
code/FBX/FBXConverter.h

@@ -421,6 +421,8 @@ private:
         double& minTime,
         Model::RotOrder order);
 
+    // ------------------------------------------------------------------------------------------------
+    // Copy global geometric data and some information about the source asset into scene metadata.
     void ConvertGlobalSettings();
 
     // ------------------------------------------------------------------------------------------------

+ 21 - 1
code/glTF/glTFImporter.cpp

@@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/ai_assert.h>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/importerdesc.h>
+#include <assimp/commonMetaData.h>
 
 #include <memory>
 
@@ -697,6 +698,25 @@ void glTFImporter::ImportEmbeddedTextures(glTF::Asset& r)
     }
 }
 
+void glTFImporter::ImportCommonMetadata(glTF::Asset& a)
+{
+    ai_assert(mScene->mMetaData == nullptr);
+    const bool hasVersion = !a.asset.version.empty();
+    const bool hasGenerator = !a.asset.generator.empty();
+    if (hasVersion || hasGenerator)
+    {
+        mScene->mMetaData = new aiMetadata;
+        if (hasVersion)
+        {
+            mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version));
+        }
+        if (hasGenerator)
+        {
+            mScene->mMetaData->Add(AI_METADATA_SOURCE_GENERATOR, aiString(a.asset.generator));
+        }
+    }
+}
+
 void glTFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
 {
     // clean all member arrays
@@ -723,7 +743,7 @@ void glTFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOS
     ImportLights(asset);
 
     ImportNodes(asset);
-
+    ImportCommonMetadata(asset);
 
     if (pScene->mNumMeshes == 0) {
         pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;

+ 1 - 1
code/glTF/glTFImporter.h

@@ -83,7 +83,7 @@ private:
     void ImportCameras(glTF::Asset& a);
     void ImportLights(glTF::Asset& a);
     void ImportNodes(glTF::Asset& a);
-
+    void ImportCommonMetadata(glTF::Asset& a);
 };
 
 } // Namespace assimp

+ 21 - 0
code/glTF2/glTF2Importer.cpp

@@ -55,6 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/scene.h>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/Importer.hpp>
+#include <assimp/commonMetaData.h>
 
 #include <memory>
 #include <unordered_map>
@@ -1301,6 +1302,24 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) {
 	}
 }
 
+void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) {
+    ai_assert(mScene->mMetaData == nullptr);
+    const bool hasVersion = !a.asset.version.empty();
+    const bool hasGenerator = !a.asset.generator.empty();
+    if (hasVersion || hasGenerator)
+    {
+        mScene->mMetaData = new aiMetadata;
+        if (hasVersion)
+        {
+            mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version));
+        }
+        if (hasGenerator)
+        {
+            mScene->mMetaData->Add(AI_METADATA_SOURCE_GENERATOR, aiString(a.asset.generator));
+        }
+    }
+}
+
 void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
 	// clean all member arrays
 	meshOffsets.clear();
@@ -1328,6 +1347,8 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO
 
 	ImportAnimations(asset);
 
+    ImportCommonMetadata(asset);
+
 	if (pScene->mNumMeshes == 0) {
 		pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
 	}

+ 1 - 0
code/glTF2/glTF2Importer.h

@@ -84,6 +84,7 @@ private:
     void ImportLights(glTF2::Asset& a);
     void ImportNodes(glTF2::Asset& a);
     void ImportAnimations(glTF2::Asset& a);
+    void ImportCommonMetadata(glTF2::Asset& a);
 };
 
 } // Namespace assimp

+ 63 - 0
include/assimp/commonMetaData.h

@@ -0,0 +1,63 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 commonMetaData.h
+ *  @brief Defines a set of common scene metadata keys.
+ */
+#pragma once
+#ifndef AI_COMMONMETADATA_H_INC
+#define AI_COMMONMETADATA_H_INC
+
+/// Scene metadata holding the name of the importer which loaded the source asset.
+/// This is always present if the scene was created from an imported asset.
+#define AI_METADATA_SOURCE_FORMAT "SourceAsset_Format"
+
+/// Scene metadata holding the version of the source asset as a string, if available.
+/// Not all formats add this metadata.
+#define AI_METADATA_SOURCE_FORMAT_VERSION "SourceAsset_FormatVersion"
+
+/// Scene metadata holding the name of the software which generated the source asset, if available.
+/// Not all formats add this metadata.
+#define AI_METADATA_SOURCE_GENERATOR "SourceAsset_Generator"
+
+#endif

+ 19 - 2
include/assimp/metadata.h

@@ -286,8 +286,8 @@ struct aiMetadata {
 			new_values[i] = mValues[i];
 		}
 
-		delete mKeys;
-		delete mValues;
+		delete[] mKeys;
+		delete[] mValues;
 
 		mKeys = new_keys;
 		mValues = new_values;
@@ -377,6 +377,23 @@ struct aiMetadata {
 		return true;
 	}
 
+    /// Check whether there is a metadata entry for the given key.
+    /// \param [in] Key - the key value value to check for.
+    inline
+    bool HasKey(const char* key) {
+        if ( nullptr == key ) {
+            return false;
+        }
+        
+        // Search for the given key
+        for (unsigned int i = 0; i < mNumProperties; ++i) {
+            if ( 0 == strncmp(mKeys[i].C_Str(), key, mKeys[i].length ) ) {
+                return true;
+            }
+        }
+        return false;
+    }
+
 #endif // __cplusplus
 
 };

+ 27 - 0
test/unit/utFBXImporterExporter.cpp

@@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/material.h>
 #include <assimp/scene.h>
 #include <assimp/types.h>
+#include <assimp/commonMetaData.h>
 
 using namespace Assimp;
 
@@ -283,3 +284,29 @@ TEST_F(utFBXImporterExporter, importOrphantEmbeddedTextureTest) {
     ASSERT_TRUE(scene->mTextures[0]->pcData);
     ASSERT_EQ(9026u, scene->mTextures[0]->mWidth) << "FBX ASCII base64 compression used for a texture.";
 }
+
+TEST_F(utFBXImporterExporter, sceneMetadata) {
+    Assimp::Importer importer;
+    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/global_settings.fbx",
+        aiProcess_ValidateDataStructure);
+    ASSERT_NE(scene, nullptr);
+    ASSERT_NE(scene->mMetaData, nullptr);
+    {
+        ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT));
+        aiString format;
+        ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, format));
+        ASSERT_EQ(strcmp(format.C_Str(), "Autodesk FBX Importer"), 0);
+    }
+    {
+        ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT_VERSION));
+        aiString version;
+        ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, version));
+        ASSERT_EQ(strcmp(version.C_Str(), "7400"), 0);
+    }
+    {
+        ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_GENERATOR));
+        aiString generator;
+        ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_GENERATOR, generator));
+        ASSERT_EQ(strncmp(generator.C_Str(), "Blender", 7), 0);
+    }
+}

+ 28 - 0
test/unit/utglTF2ImportExport.cpp

@@ -45,6 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/Exporter.hpp>
 #include <assimp/postprocess.h>
 #include <assimp/scene.h>
+#include <assimp/commonMetaData.h>
+
 #include <array>
 
 using namespace Assimp;
@@ -436,3 +438,29 @@ TEST_F(utglTF2ImportExport, error_string_preserved) {
 }
 
 #endif // ASSIMP_BUILD_NO_EXPORT
+
+TEST_F(utglTF2ImportExport, sceneMetadata) {
+    Assimp::Importer importer;
+    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf",
+        aiProcess_ValidateDataStructure);
+    ASSERT_NE(scene, nullptr);
+    ASSERT_NE(scene->mMetaData, nullptr);
+    {
+        ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT));
+        aiString format;
+        ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, format));
+        ASSERT_EQ(strcmp(format.C_Str(), "glTF2 Importer"), 0);
+    }
+    {
+        ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT_VERSION));
+        aiString version;
+        ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, version));
+        ASSERT_EQ(strcmp(version.C_Str(), "2.0"), 0);
+    }
+    {
+        ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_GENERATOR));
+        aiString generator;
+        ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_GENERATOR, generator));
+        ASSERT_EQ(strcmp(generator.C_Str(), "COLLADA2GLTF"), 0);
+    }
+}

+ 26 - 0
test/unit/utglTFImportExport.cpp

@@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/postprocess.h>
 
 #include <assimp/scene.h>
+#include <assimp/commonMetaData.h>
 
 using namespace Assimp;
 
@@ -85,3 +86,28 @@ TEST_F(utglTFImportExport, incorrect_vertex_arrays) {
     EXPECT_EQ(scene->mMeshes[7]->mNumVertices, 35u);
     EXPECT_EQ(scene->mMeshes[7]->mNumFaces, 17u);
 }
+
+TEST_F(utglTFImportExport, sceneMetadata) {
+    Assimp::Importer importer;
+    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF/TwoBoxes/TwoBoxes.gltf", aiProcess_ValidateDataStructure);
+    ASSERT_TRUE(scene);
+    ASSERT_TRUE(scene->mMetaData);
+    {
+        ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT));
+        aiString format;
+        ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, format));
+        ASSERT_EQ(strcmp(format.C_Str(), "glTF Importer"), 0);
+    }
+    {
+        ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT_VERSION));
+        aiString version;
+        ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, version));
+        ASSERT_EQ(strcmp(version.C_Str(), "1.0"), 0);
+    }
+    {
+        ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_GENERATOR));
+        aiString generator;
+        ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_GENERATOR, generator));
+        ASSERT_EQ(strncmp(generator.C_Str(), "collada2gltf", 12), 0);
+    }
+}