Sfoglia il codice sorgente

Merge branch 'master' into master

Kim Kulling 7 anni fa
parent
commit
f898f40486

+ 26 - 0
code/D3MFExporter.cpp

@@ -117,6 +117,7 @@ bool D3MFExporter::exportArchive( const char *file ) {
     if ( nullptr == m_zipArchive ) {
         return false;
     }
+
     ok |= exportContentTypes();
     ok |= export3DModel();
     ok |= exportRelations();
@@ -181,6 +182,8 @@ bool D3MFExporter::export3DModel() {
     mModelOutput << "<" << XmlTag::resources << ">";
     mModelOutput << std::endl;
 
+    writeMetaData();
+
     writeBaseMaterials();
 
     writeObjects();
@@ -209,6 +212,29 @@ void D3MFExporter::writeHeader() {
     mModelOutput << std::endl;
 }
 
+void D3MFExporter::writeMetaData() {
+    if ( nullptr == mScene->mMetaData ) {
+        return;
+    }
+
+    const unsigned int numMetaEntries( mScene->mMetaData->mNumProperties );
+    if ( 0 == numMetaEntries ) {
+        return;
+    }
+
+    const aiString *key;
+    const aiMetadataEntry *entry(nullptr);
+    for ( size_t i = 0; i < numMetaEntries; ++i ) {
+        mScene->mMetaData->Get( i, key, entry );
+        std::string k( key->C_Str() );
+        aiString value;
+        mScene->mMetaData->Get(  k, value );
+        mModelOutput << "<" << XmlTag::meta << " " << XmlTag::meta_name << "=\"" << key->C_Str() << "\">";
+        mModelOutput << value.C_Str();
+        mModelOutput << "</" << XmlTag::meta << ">" << std::endl;
+    }
+}
+
 void D3MFExporter::writeBaseMaterials() {
     mModelOutput << "<basematerials id=\"1\">\n";
     for ( size_t i = 0; i < mScene->mNumMaterials; ++i ) {

+ 1 - 0
code/D3MFExporter.h

@@ -76,6 +76,7 @@ public:
 
 protected:
     void writeHeader();
+    void writeMetaData();
     void writeBaseMaterials();
     void writeObjects();
     void writeMesh( aiMesh *mesh );

+ 36 - 3
code/D3MFImporter.cpp

@@ -102,6 +102,8 @@ public:
                 // 
             } else if ( nodeName == D3MF::XmlTag::basematerials ) {
                 ReadBaseMaterials();
+            } else if ( nodeName == D3MF::XmlTag::meta ) {
+                ReadMetadata();
             }
         }
 
@@ -109,19 +111,31 @@ public:
             scene->mRootNode->mName.Set( "3MF" );
         }
 
+        // import the metadata
+        if ( !mMetaData.empty() ) {
+            const size_t numMeta( mMetaData.size() );
+            scene->mMetaData = aiMetadata::Alloc( numMeta );
+            for ( size_t i = 0; i < numMeta; ++i ) {
+                aiString val( mMetaData[ i ].value );
+                scene->mMetaData->Set( i, mMetaData[ i ].name, val );
+            }
+        }
+
+        // import the meshes
         scene->mNumMeshes = static_cast<unsigned int>( mMeshes.size());
         scene->mMeshes = new aiMesh*[scene->mNumMeshes]();
-
         std::copy( mMeshes.begin(), mMeshes.end(), scene->mMeshes);
 
+        // import the materials
         scene->mNumMaterials = static_cast<unsigned int>( mMatArray.size() );
         if ( 0 != scene->mNumMaterials ) {
             scene->mMaterials = new aiMaterial*[ scene->mNumMaterials ];
             std::copy( mMatArray.begin(), mMatArray.end(), scene->mMaterials );
         }
+
+        // create the scenegraph
         scene->mRootNode->mNumChildren = static_cast<unsigned int>(children.size());
         scene->mRootNode->mChildren = new aiNode*[scene->mRootNode->mNumChildren]();
-
         std::copy(children.begin(), children.end(), scene->mRootNode->mChildren);
     }
 
@@ -180,6 +194,21 @@ private:
         return mesh;
     }
 
+    void ReadMetadata() {
+        const std::string name = xmlReader->getAttributeValue( D3MF::XmlTag::meta_name.c_str() );
+        xmlReader->read();
+        const std::string value = xmlReader->getNodeData();
+
+        if ( name.empty() ) {
+            return;
+        }
+
+        MetaEntry entry;
+        entry.name = name;
+        entry.value = value;
+        mMetaData.push_back( entry );
+    }
+
     void ImportVertices(aiMesh* mesh) {
         std::vector<aiVector3D> vertices;
         while(ReadToEndElement(D3MF::XmlTag::vertices)) {
@@ -371,8 +400,12 @@ private:
         return false;
     }
 
-
 private:
+    struct MetaEntry {
+        std::string name;
+        std::string value;
+    };
+    std::vector<MetaEntry> mMetaData;
     std::vector<aiMesh*> mMeshes;
     MatArray mMatArray;
     unsigned int mActiveMatGroup;

+ 56 - 68
code/DefaultLogger.cpp

@@ -45,7 +45,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *  @brief Implementation of DefaultLogger (and Logger)
  */
 
-
 // Default log streams
 #include "Win32DebugLogStream.h"
 #include "StdOStreamLogStream.h"
@@ -62,8 +61,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_SINGLETHREADED
 #   include <thread>
 #   include <mutex>
-
-std::mutex loggerMutex;
+    std::mutex loggerMutex;
 #endif
 
 namespace Assimp    {
@@ -76,22 +74,19 @@ static const unsigned int SeverityAll = Logger::Info | Logger::Err | Logger::War
 
 // ----------------------------------------------------------------------------------
 // Represents a log-stream + its error severity
-struct LogStreamInfo
-{
-    unsigned int m_uiErrorSeverity;
-    LogStream *m_pStream;
+struct LogStreamInfo {
+    unsigned int  m_uiErrorSeverity;
+    LogStream    *m_pStream;
 
     // Constructor
     LogStreamInfo( unsigned int uiErrorSev, LogStream *pStream ) :
         m_uiErrorSeverity( uiErrorSev ),
-        m_pStream( pStream )
-    {
+        m_pStream( pStream ) {
         // empty
     }
 
     // Destructor
-    ~LogStreamInfo()
-    {
+    ~LogStreamInfo() {
         delete m_pStream;
     }
 };
@@ -109,7 +104,7 @@ LogStream* LogStream::createDefaultStream(aiDefaultLogStream    streams,
 #ifdef WIN32
         return new Win32DebugLogStream();
 #else
-        return NULL;
+        return nullptr;
 #endif
 
         // Platform-independent default streams
@@ -118,7 +113,7 @@ LogStream* LogStream::createDefaultStream(aiDefaultLogStream    streams,
     case aiDefaultLogStream_STDOUT:
         return new StdOStreamLogStream(std::cout);
     case aiDefaultLogStream_FILE:
-        return (name && *name ? new FileLogStream(name,io) : NULL);
+        return (name && *name ? new FileLogStream(name,io) : nullptr );
     default:
         // We don't know this default log stream, so raise an assertion
         ai_assert(false);
@@ -134,34 +129,38 @@ LogStream* LogStream::createDefaultStream(aiDefaultLogStream    streams,
 Logger *DefaultLogger::create(const char* name /*= "AssimpLog.txt"*/,
     LogSeverity severity                       /*= NORMAL*/,
     unsigned int defStreams                    /*= aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE*/,
-    IOSystem* io                               /*= NULL*/)
-{
+    IOSystem* io                               /*= NULL*/) {
     // enter the mutex here to avoid concurrency problems
 #ifndef ASSIMP_BUILD_SINGLETHREADED
     std::lock_guard<std::mutex> lock(loggerMutex);
 #endif
 
-    if (m_pLogger && !isNullLogger() )
+    if ( m_pLogger && !isNullLogger() ) {
         delete m_pLogger;
+    }
 
     m_pLogger = new DefaultLogger( severity );
 
     // Attach default log streams
     // Stream the log to the MSVC debugger?
-    if (defStreams & aiDefaultLogStream_DEBUGGER)
-        m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_DEBUGGER));
+    if ( defStreams & aiDefaultLogStream_DEBUGGER ) {
+        m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_DEBUGGER ) );
+    }
 
     // Stream the log to COUT?
-    if (defStreams & aiDefaultLogStream_STDOUT)
-        m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_STDOUT));
+    if ( defStreams & aiDefaultLogStream_STDOUT ) {
+        m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDOUT ) );
+    }
 
     // Stream the log to CERR?
-    if (defStreams & aiDefaultLogStream_STDERR)
-         m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_STDERR));
+    if ( defStreams & aiDefaultLogStream_STDERR ) {
+        m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDERR ) );
+    }
 
     // Stream the log to a file
-    if (defStreams & aiDefaultLogStream_FILE && name && *name)
-        m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_FILE,name,io));
+    if ( defStreams & aiDefaultLogStream_FILE && name && *name ) {
+        m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_FILE, name, io ) );
+    }
 
     return m_pLogger;
 }
@@ -200,7 +199,6 @@ void Logger::warn(const char* message)  {
 
 // ----------------------------------------------------------------------------------
 void Logger::error(const char* message) {
-
     // SECURITY FIX: see above
     if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
         return;
@@ -209,23 +207,24 @@ void Logger::error(const char* message) {
 }
 
 // ----------------------------------------------------------------------------------
-void DefaultLogger::set( Logger *logger )
-{
+void DefaultLogger::set( Logger *logger ) {
     // enter the mutex here to avoid concurrency problems
 #ifndef ASSIMP_BUILD_SINGLETHREADED
     std::lock_guard<std::mutex> lock(loggerMutex);
 #endif
 
-    if (!logger)logger = &s_pNullLogger;
-    if (m_pLogger && !isNullLogger() )
+    if ( nullptr == logger ) {
+        logger = &s_pNullLogger;
+    }
+    if ( nullptr != m_pLogger && !isNullLogger() ) {
         delete m_pLogger;
+    }
 
     DefaultLogger::m_pLogger = logger;
 }
 
 // ----------------------------------------------------------------------------------
-bool DefaultLogger::isNullLogger()
-{
+bool DefaultLogger::isNullLogger() {
     return m_pLogger == &s_pNullLogger;
 }
 
@@ -236,8 +235,7 @@ Logger *DefaultLogger::get() {
 
 // ----------------------------------------------------------------------------------
 //  Kills the only instance
-void DefaultLogger::kill()
-{
+void DefaultLogger::kill() {
     // enter the mutex here to avoid concurrency problems
 #ifndef ASSIMP_BUILD_SINGLETHREADED
     std::lock_guard<std::mutex> lock(loggerMutex);
@@ -252,10 +250,10 @@ void DefaultLogger::kill()
 
 // ----------------------------------------------------------------------------------
 //  Debug message
-void DefaultLogger::OnDebug( const char* message )
-{
-	if ( m_Severity == Logger::NORMAL )
-		return;
+void DefaultLogger::OnDebug( const char* message ) {
+    if ( m_Severity == Logger::NORMAL ) {
+        return;
+    }
 
 	static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
 	char msg[Size];
@@ -266,8 +264,7 @@ void DefaultLogger::OnDebug( const char* message )
 
 // ----------------------------------------------------------------------------------
 //  Logs an info
-void DefaultLogger::OnInfo( const char* message )
-{
+void DefaultLogger::OnInfo( const char* message ){
 	static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
 	char msg[Size];
     ai_snprintf(msg, Size, "Info,  T%u: %s", GetThreadID(), message );
@@ -277,8 +274,7 @@ void DefaultLogger::OnInfo( const char* message )
 
 // ----------------------------------------------------------------------------------
 //  Logs a warning
-void DefaultLogger::OnWarn( const char* message )
-{
+void DefaultLogger::OnWarn( const char* message ) {
 	static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
 	char msg[Size];
 	ai_snprintf(msg, Size, "Warn,  T%u: %s", GetThreadID(), message );
@@ -288,8 +284,7 @@ void DefaultLogger::OnWarn( const char* message )
 
 // ----------------------------------------------------------------------------------
 //  Logs an error
-void DefaultLogger::OnError( const char* message )
-{
+void DefaultLogger::OnError( const char* message ) {
 	static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
 	char msg[ Size ];
     ai_snprintf(msg, Size, "Error, T%u: %s", GetThreadID(), message );
@@ -299,10 +294,10 @@ void DefaultLogger::OnError( const char* message )
 
 // ----------------------------------------------------------------------------------
 //  Will attach a new stream
-bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity )
-{
-    if (!pStream)
+bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) {
+    if ( nullptr == pStream ) {
         return false;
+    }
 
     if (0 == severity)  {
         severity = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging;
@@ -312,8 +307,7 @@ bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity )
         it != m_StreamArray.end();
         ++it )
     {
-        if ( (*it)->m_pStream == pStream )
-        {
+        if ( (*it)->m_pStream == pStream ) {
             (*it)->m_uiErrorSeverity |= severity;
             return true;
         }
@@ -326,34 +320,31 @@ bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity )
 
 // ----------------------------------------------------------------------------------
 //  Detach a stream
-bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity )
-{
-    if (!pStream)
+bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity ) {
+    if ( nullptr == pStream ) {
         return false;
+    }
 
     if (0 == severity)  {
         severity = SeverityAll;
     }
 
-    for ( StreamIt it = m_StreamArray.begin();
-        it != m_StreamArray.end();
-        ++it )
-    {
-        if ( (*it)->m_pStream == pStream )
-        {
+    bool res( false );
+    for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) {
+        if ( (*it)->m_pStream == pStream ) {
             (*it)->m_uiErrorSeverity &= ~severity;
-            if ( (*it)->m_uiErrorSeverity == 0 )
-            {
+            if ( (*it)->m_uiErrorSeverity == 0 ) {
                 // don't delete the underlying stream 'cause the caller gains ownership again
-                (**it).m_pStream = NULL;
+                (**it).m_pStream = nullptr;
                 delete *it;
                 m_StreamArray.erase( it );
+                res = true;
                 break;
             }
             return true;
         }
     }
-    return false;
+    return res;
 }
 
 // ----------------------------------------------------------------------------------
@@ -361,15 +352,13 @@ bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity )
 DefaultLogger::DefaultLogger(LogSeverity severity)
     :   Logger  ( severity )
     ,   noRepeatMsg (false)
-    ,   lastLen( 0 )
-{
+    ,   lastLen( 0 ) {
     lastMsg[0] = '\0';
 }
 
 // ----------------------------------------------------------------------------------
 //  Destructor
-DefaultLogger::~DefaultLogger()
-{
+DefaultLogger::~DefaultLogger() {
     for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) {
         // also frees the underlying stream, we are its owner.
         delete *it;
@@ -378,9 +367,8 @@ DefaultLogger::~DefaultLogger()
 
 // ----------------------------------------------------------------------------------
 //  Writes message to stream
-void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev )
-{
-    ai_assert(NULL != message);
+void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev ) {
+    ai_assert(nullptr != message);
 
     // Check whether this is a repeated message
     if (! ::strncmp( message,lastMsg, lastLen-1))

+ 2 - 1
code/Exporter.cpp

@@ -308,7 +308,8 @@ bool IsVerboseFormat(const aiScene* pScene) {
 }
 
 // ------------------------------------------------------------------------------------------------
-aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath, unsigned int pPreprocessing, const ExportProperties* pProperties) {
+aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath,
+        unsigned int pPreprocessing, const ExportProperties* pProperties) {
     ASSIMP_BEGIN_EXCEPTION_REGION();
 
     // when they create scenes from scratch, users will likely create them not in verbose

+ 7 - 1
code/SceneCombiner.cpp

@@ -55,6 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/SceneCombiner.h>
 #include <assimp/StringUtils.h>
 #include <assimp/fast_atof.h>
+#include <assimp/metadata.h>
 #include <assimp/Hash.h>
 #include "time.h"
 #include <assimp/DefaultLogger.hpp>
@@ -1001,7 +1002,12 @@ void SceneCombiner::CopyScene(aiScene** _dest,const aiScene* src,bool allocate)
         *_dest = new aiScene();
     }
     aiScene* dest = *_dest;
-    ai_assert(dest);
+    ai_assert(nullptr != dest);
+
+    // copy metadata
+    if ( nullptr != src->mMetaData ) {
+        dest->mMetaData = new aiMetadata( *src->mMetaData );
+    }
 
     // copy animations
     dest->mNumAnimations = src->mNumAnimations;

+ 81 - 17
include/assimp/metadata.h

@@ -67,6 +67,7 @@ typedef enum aiMetadataType {
     AI_DOUBLE     = 4,
     AI_AISTRING   = 5,
     AI_AIVECTOR3D = 6,
+    AI_META_MAX   = 7,
 
 #ifndef SWIG
     FORCE_32BIT = INT_MAX
@@ -130,42 +131,103 @@ struct aiMetadata {
      */
     aiMetadata()
     : mNumProperties(0)
-    , mKeys(NULL)
-    , mValues(NULL) {
+    , mKeys(nullptr)
+    , mValues(nullptr) {
         // empty
     }
 
+    aiMetadata( const aiMetadata &rhs )
+    : mNumProperties( rhs.mNumProperties )
+    , mKeys( nullptr )
+    , mValues( nullptr ) {
+        mKeys = new aiString[ mNumProperties ];
+        for ( unsigned int i = 0; i < mNumProperties; ++i ) {
+            mKeys[ i ] = rhs.mKeys[ i ];
+        }
+        mValues = new aiMetadataEntry[ mNumProperties ];
+        for ( unsigned int i = 0; i < mNumProperties; ++i ) {
+            mValues[ i ].mType = rhs.mValues[ i ].mType;
+            switch ( rhs.mValues[ i ].mType ) {
+            case AI_BOOL:
+                mValues[ i ].mData = new bool( rhs.mValues[i].mData );
+                break;
+            case AI_INT32: {
+                int32_t v;
+                ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( int32_t ) );
+                mValues[ i ].mData = new int32_t( v );
+                }
+                break;
+            case AI_UINT64: {
+                    uint64_t v;
+                    ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( uint64_t ) );
+                    mValues[ i ].mData = new  uint64_t( v );
+                }
+                break;
+            case AI_FLOAT: {
+                    float v;
+                    ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( float ) );
+                    mValues[ i ].mData = new float( v );
+                }
+                break;
+            case AI_DOUBLE: {
+                    double v;
+                    ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( double ) );
+                    mValues[ i ].mData = new double( v );
+                }
+                break;
+            case AI_AISTRING: {
+                    aiString v;
+                    rhs.Get<aiString>( mKeys[ i ], v );
+                    mValues[ i ].mData = new aiString( v );
+                }
+                break;
+            case AI_AIVECTOR3D: {
+                    aiVector3D v;
+                    rhs.Get<aiVector3D>( mKeys[ i ], v );
+                    mValues[ i ].mData = new aiVector3D( v );
+                }
+                break;
+#ifndef SWIG
+            case FORCE_32BIT:
+#endif
+            default:
+                break;
+            }
+
+        }
+    }
+
     /** 
      *  @brief The destructor.
      */
     ~aiMetadata() {
         delete [] mKeys;
-        mKeys = NULL;
+        mKeys = nullptr;
         if (mValues) {
             // Delete each metadata entry
             for (unsigned i=0; i<mNumProperties; ++i) {
                 void* data = mValues[i].mData;
                 switch (mValues[i].mType) {
                 case AI_BOOL:
-                    delete static_cast<bool*>(data);
+                    delete static_cast< bool* >( data );
                     break;
                 case AI_INT32:
-                    delete static_cast<int32_t*>(data);
+                    delete static_cast< int32_t* >( data );
                     break;
                 case AI_UINT64:
-                    delete static_cast<uint64_t*>(data);
+                    delete static_cast< uint64_t* >( data );
                     break;
                 case AI_FLOAT:
-                    delete static_cast<float*>(data);
+                    delete static_cast< float* >( data );
                     break;
                 case AI_DOUBLE:
-                    delete static_cast<double*>(data);
+                    delete static_cast< double* >( data );
                     break;
                 case AI_AISTRING:
-                    delete static_cast<aiString*>(data);
+                    delete static_cast< aiString* >( data );
                     break;
                 case AI_AIVECTOR3D:
-                    delete static_cast<aiVector3D*>(data);
+                    delete static_cast< aiVector3D* >( data );
                     break;
 #ifndef SWIG
                 case FORCE_32BIT:
@@ -177,7 +239,7 @@ struct aiMetadata {
 
             // Delete the metadata array
             delete [] mValues;
-            mValues = NULL;
+            mValues = nullptr;
         }
     }
 
@@ -208,8 +270,8 @@ struct aiMetadata {
     }
 
 	template<typename T>
-	inline void Add(const std::string& key, const T& value)
-	{
+	inline
+    void Add(const std::string& key, const T& value) {
 		aiString* new_keys = new aiString[mNumProperties + 1];
 		aiMetadataEntry* new_values = new aiMetadataEntry[mNumProperties + 1];
 
@@ -256,7 +318,7 @@ struct aiMetadata {
 
     template<typename T>
     inline 
-    bool Get( unsigned index, T& value ) {
+    bool Get( unsigned index, T& value ) const {
         // In range assertion
         if ( index >= mNumProperties ) {
             return false;
@@ -277,7 +339,7 @@ struct aiMetadata {
 
     template<typename T>
     inline 
-    bool Get( const aiString& key, T& value ) {
+    bool Get( const aiString& key, T& value ) const {
         // Search for the given key
         for ( unsigned int i = 0; i < mNumProperties; ++i ) {
             if ( mKeys[ i ] == key ) {
@@ -288,7 +350,8 @@ struct aiMetadata {
     }
 
     template<typename T>
-    inline bool Get( const std::string& key, T& value ) {
+    inline
+    bool Get( const std::string& key, T& value ) const {
         return Get(aiString(key), value);
     }
 
@@ -297,7 +360,8 @@ struct aiMetadata {
 	/// \param [out] pKey - pointer to the key value.
 	/// \param [out] pEntry - pointer to the entry: type and value.
 	/// \return false - if pIndex is out of range, else - true.
-	inline bool Get(size_t index, const aiString*& key, const aiMetadataEntry*& entry) {
+	inline
+    bool Get(size_t index, const aiString*& key, const aiMetadataEntry*& entry) const {
         if ( index >= mNumProperties ) {
             return false;
         }

+ 71 - 0
test/unit/utMetadata.cpp

@@ -181,3 +181,74 @@ TEST_F( utMetadata, get_set_aiVector3D_Test ) {
     EXPECT_TRUE( success );
 }
 
+TEST_F( utMetadata, copy_test ) {
+    m_data = aiMetadata::Alloc( AI_META_MAX );
+    bool bv = true;
+    m_data->Set( 0, "bool", bv );
+    int32_t i32v = -10;
+    m_data->Set( 1, "int32", i32v );
+    uint64_t ui64v = static_cast<uint64_t>( 10 );
+    m_data->Set( 2, "uint64", ui64v );
+    float fv = 1.0f;
+    m_data->Set( 3, "float", fv );
+    double dv = 2.0;
+    m_data->Set( 4, "double", dv );
+    const aiString strVal( std::string( "test" ) );
+    m_data->Set( 5, "aiString", strVal );
+    aiVector3D vecVal( 1, 2, 3 );
+    m_data->Set( 6, "aiVector3D", vecVal );
+
+    aiMetadata copy( *m_data );
+    EXPECT_EQ( 7, copy.mNumProperties );
+
+    // bool test
+    {
+        bool v;
+        EXPECT_TRUE( copy.Get( "bool", v ) );
+        EXPECT_EQ( bv, v );
+    }
+
+    // int32_t test
+    {
+        int32_t v;
+        bool ok = copy.Get( "int32", v );
+        EXPECT_TRUE( ok );
+        EXPECT_EQ( i32v, v );
+    }
+
+    // uint64_t test
+    {
+        uint64_t v;
+        bool ok = copy.Get( "uint64", v );
+        EXPECT_TRUE( ok );
+        EXPECT_EQ( ui64v, v );
+    }
+
+    // float test
+    {
+        float v;
+        EXPECT_TRUE( copy.Get( "float", v ) );
+        EXPECT_EQ( fv, v );
+    }
+
+    // double test
+    {
+        double v;
+        EXPECT_TRUE( copy.Get( "double", v ) );
+        EXPECT_EQ( dv, v );
+    }
+
+    // bool test
+    {
+        aiString v;
+        EXPECT_TRUE( copy.Get( "aiString", v ) );
+        EXPECT_EQ( strVal, v );
+    }
+
+    // bool test
+    {
+        aiVector3D v;
+        EXPECT_TRUE( copy.Get( "aiVector3D", v ) );
+        EXPECT_EQ( vecVal, v );
+    }
+}