diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index b89e81ce6..950793f44 100644 --- a/code/D3MFExporter.cpp +++ b/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 << "" << std::endl; + } +} + void D3MFExporter::writeBaseMaterials() { mModelOutput << "\n"; for ( size_t i = 0; i < mScene->mNumMaterials; ++i ) { diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h index b553e0ab5..110862b99 100644 --- a/code/D3MFExporter.h +++ b/code/D3MFExporter.h @@ -76,6 +76,7 @@ public: protected: void writeHeader(); + void writeMetaData(); void writeBaseMaterials(); void writeObjects(); void writeMesh( aiMesh *mesh ); diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 764f0d28b..535652502 100644 --- a/code/D3MFImporter.cpp +++ b/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( mMeshes.size()); scene->mMeshes = new aiMesh*[scene->mNumMeshes](); - std::copy( mMeshes.begin(), mMeshes.end(), scene->mMeshes); + // import the materials scene->mNumMaterials = static_cast( 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(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 vertices; while(ReadToEndElement(D3MF::XmlTag::vertices)) { @@ -371,8 +400,12 @@ private: return false; } - private: + struct MetaEntry { + std::string name; + std::string value; + }; + std::vector mMetaData; std::vector mMeshes; MatArray mMatArray; unsigned int mActiveMatGroup; diff --git a/code/DefaultLogger.cpp b/code/DefaultLogger.cpp index ad101174c..2871a4131 100644 --- a/code/DefaultLogger.cpp +++ b/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 # include - -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 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 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 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)) diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 4951c70ef..49523e658 100644 --- a/code/Exporter.cpp +++ b/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 diff --git a/code/SceneCombiner.cpp b/code/SceneCombiner.cpp index 8647bd1ba..f67ef547c 100644 --- a/code/SceneCombiner.cpp +++ b/code/SceneCombiner.cpp @@ -55,6 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include "time.h" #include @@ -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; diff --git a/include/assimp/metadata.h b/include/assimp/metadata.h index 2dba61abc..70a604de9 100644 --- a/include/assimp/metadata.h +++ b/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( mKeys[ i ], v ); + mValues[ i ].mData = new aiString( v ); + } + break; + case AI_AIVECTOR3D: { + aiVector3D v; + rhs.Get( 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(data); + delete static_cast< bool* >( data ); break; case AI_INT32: - delete static_cast(data); + delete static_cast< int32_t* >( data ); break; case AI_UINT64: - delete static_cast(data); + delete static_cast< uint64_t* >( data ); break; case AI_FLOAT: - delete static_cast(data); + delete static_cast< float* >( data ); break; case AI_DOUBLE: - delete static_cast(data); + delete static_cast< double* >( data ); break; case AI_AISTRING: - delete static_cast(data); + delete static_cast< aiString* >( data ); break; case AI_AIVECTOR3D: - delete static_cast(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 - 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 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 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 - 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; } diff --git a/test/unit/utMetadata.cpp b/test/unit/utMetadata.cpp index 7fda143b7..42f632bce 100644 --- a/test/unit/utMetadata.cpp +++ b/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( 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 ); + } +}