From 621bdef663c0eb30788ca29ace0c872e668a8b13 Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Tue, 8 Mar 2011 16:09:54 +0000 Subject: [PATCH] - rework exporter interface to be based primarily on the existing IOSystem. - implement ExportToBlob/aiExportToBlob via a custom IOSystem implementation. - split exporter C and C++ interfaces. + test cases for the exporter interface git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@913 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/Assimp.cpp | 108 +----------- code/AssimpCExport.cpp | 105 ++++++++++++ code/BlobIOSystem.h | 326 +++++++++++++++++++++++++++++++++++++ code/CInterfaceIOWrapper.h | 159 ++++++++++++++++++ code/ColladaExporter.cpp | 14 +- code/Exceptional.h | 2 + code/Exporter.cpp | 118 +++++--------- include/IOSystem.h | 2 +- include/export.h | 27 ++- include/export.hpp | 25 ++- test/unit/utExport.cpp | 76 +++++++++ test/unit/utExport.h | 41 +++++ 12 files changed, 800 insertions(+), 203 deletions(-) create mode 100644 code/AssimpCExport.cpp create mode 100644 code/BlobIOSystem.h create mode 100644 code/CInterfaceIOWrapper.h create mode 100644 test/unit/utExport.cpp create mode 100644 test/unit/utExport.h diff --git a/code/Assimp.cpp b/code/Assimp.cpp index d349e9822..38d3b1a0e 100644 --- a/code/Assimp.cpp +++ b/code/Assimp.cpp @@ -44,9 +44,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AssimpPCH.h" #include "../include/assimp.h" -#include "../include/aiFileIO.h" #include "GenericProperty.h" +#include "CInterfaceIOWrapper.h" // ------------------------------------------------------------------------------------------------ #ifdef AI_C_THREADSAFE @@ -58,6 +58,7 @@ using namespace Assimp; namespace Assimp { + /** Stores the importer objects for all active import processes */ typedef std::map ImporterMap; @@ -101,113 +102,8 @@ static boost::mutex gMutex; static boost::mutex gLogStreamMutex; #endif -class CIOSystemWrapper; -class CIOStreamWrapper; -// ------------------------------------------------------------------------------------------------ -// Custom IOStream implementation for the C-API -class CIOStreamWrapper : public IOStream -{ - friend class CIOSystemWrapper; -public: - CIOStreamWrapper(aiFile* pFile) - : mFile(pFile) - {} - - // ................................................................... - size_t Read(void* pvBuffer, - size_t pSize, - size_t pCount - ){ - // need to typecast here as C has no void* - return mFile->ReadProc(mFile,(char*)pvBuffer,pSize,pCount); - } - - // ................................................................... - size_t Write(const void* pvBuffer, - size_t pSize, - size_t pCount - ){ - // need to typecast here as C has no void* - return mFile->WriteProc(mFile,(const char*)pvBuffer,pSize,pCount); - } - - // ................................................................... - aiReturn Seek(size_t pOffset, - aiOrigin pOrigin - ){ - return mFile->SeekProc(mFile,pOffset,pOrigin); - } - - // ................................................................... - size_t Tell(void) const { - return mFile->TellProc(mFile); - } - - // ................................................................... - size_t FileSize() const { - return mFile->FileSizeProc(mFile); - } - - // ................................................................... - void Flush () { - return mFile->FlushProc(mFile); - } - -private: - aiFile* mFile; -}; - -// ------------------------------------------------------------------------------------------------ -// Custom IOStream implementation for the C-API -class CIOSystemWrapper : public IOSystem -{ -public: - CIOSystemWrapper(aiFileIO* pFile) - : mFileSystem(pFile) - {} - - // ................................................................... - bool Exists( const char* pFile) const { - CIOSystemWrapper* pip = const_cast(this); - IOStream* p = pip->Open(pFile); - if (p){ - pip->Close(p); - return true; - } - return false; - } - - // ................................................................... - char getOsSeparator() const { -#ifndef _WIN32 - return '/'; -#else - return '\\'; -#endif - } - - // ................................................................... - IOStream* Open(const char* pFile,const char* pMode = "rb") { - aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile,pMode); - if (!p) { - return NULL; - } - return new CIOStreamWrapper(p); - } - - // ................................................................... - void Close( IOStream* pFile) { - if (!pFile) { - return; - } - mFileSystem->CloseProc(mFileSystem,((CIOStreamWrapper*) pFile)->mFile); - delete pFile; - } -private: - aiFileIO* mFileSystem; -}; // ------------------------------------------------------------------------------------------------ // Custom LogStream implementation for the C-API diff --git a/code/AssimpCExport.cpp b/code/AssimpCExport.cpp new file mode 100644 index 000000000..f2b8d64ec --- /dev/null +++ b/code/AssimpCExport.cpp @@ -0,0 +1,105 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (ASSIMP) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 AssimpCExport.cpp +Assimp C export interface. See Exporter.cpp for some notes. +*/ + +#include "AssimpPCH.h" + +#ifndef ASSIMP_BUILD_NO_EXPORT +#include "CInterfaceIOWrapper.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API size_t aiGetExportFormatCount(void) +{ + return Exporter().GetExportFormatCount(); +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API const aiExportFormatDesc* aiGetExportFormatDescription( size_t pIndex) +{ + return Exporter().GetExportFormatDescription(pIndex); +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiReturn aiExportScene( const aiScene* pScene, const char* pFormatId, const char* pFileName ) +{ + return ::aiExportSceneEx(pScene,pFormatId,pFileName,NULL); +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiReturn aiExportSceneEx( const aiScene* pScene, const char* pFormatId, const char* pFileName, aiFileIO* pIO) +{ + Exporter exp; + + if (pIO) { + exp.SetIOHandler(new CIOSystemWrapper(pIO)); + } + return exp.Export(pScene,pFormatId,pFileName); +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const aiScene* pScene, const char* pFormatId ) +{ + Exporter exp; + if (!exp.ExportToBlob(pScene,pFormatId)) { + return NULL; + } + const aiExportDataBlob* blob = exp.GetOrphanedBlob(); + ai_assert(blob); + + return blob; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API C_STRUCT void aiReleaseExportData( const aiExportDataBlob* pData ) +{ + delete pData; +} + +#endif // !ASSIMP_BUILD_NO_EXPORT diff --git a/code/BlobIOSystem.h b/code/BlobIOSystem.h new file mode 100644 index 000000000..b1dac8e05 --- /dev/null +++ b/code/BlobIOSystem.h @@ -0,0 +1,326 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (ASSIMP) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Provides cheat implementations for IOSystem and IOStream to + * redirect exporter output to a blob chain.*/ + +#ifndef AI_BLOBIOSYSTEM_H_INCLUDED +#define AI_BLOBIOSYSTEM_H_INCLUDED + +namespace Assimp { + class BlobIOSystem; + +// -------------------------------------------------------------------------------------------- +/** Redirect IOStream to a blob */ +// -------------------------------------------------------------------------------------------- +class BlobIOStream : public IOStream +{ +public: + + BlobIOStream(BlobIOSystem* creator, const std::string& file, size_t initial = 4096) + : file_size() + , cursor() + , buffer() + , creator(creator) + , file(file) + , cur_size() + , initial(initial) + { + } + + + virtual ~BlobIOStream(); + +public: + + // ------------------------------------------------------------------- + aiExportDataBlob* GetBlob() + { + aiExportDataBlob* blob = new aiExportDataBlob(); + blob->size = file_size; + blob->data = buffer; + + buffer = NULL; + + return blob; + } + + +public: + + + // ------------------------------------------------------------------- + virtual size_t Read(void* pvBuffer, + size_t pSize, + size_t pCount) + { + return 0; + } + + // ------------------------------------------------------------------- + virtual size_t Write(const void* pvBuffer, + size_t pSize, + size_t pCount) + { + pSize *= pCount; + if (cursor + pSize > cur_size) { + Grow(cursor + pSize); + } + + memcpy(buffer+cursor, pvBuffer, pSize); + cursor += pSize; + + file_size = std::max(file_size,cursor); + return pCount; + } + + // ------------------------------------------------------------------- + virtual aiReturn Seek(size_t pOffset, + aiOrigin pOrigin) + { + switch(pOrigin) + { + case aiOrigin_CUR: + cursor += pOffset; + + case aiOrigin_END: + cursor = file_size - pOffset; + + case aiOrigin_SET: + cursor = pOffset; + break; + + default: + return AI_FAILURE; + } + + if (cursor > file_size) { + Grow(cursor); + } + + file_size = std::max(cursor,file_size); + return AI_SUCCESS; + } + + // ------------------------------------------------------------------- + virtual size_t Tell() const + { + return cursor; + } + + // ------------------------------------------------------------------- + virtual size_t FileSize() const + { + return file_size; + } + + // ------------------------------------------------------------------- + virtual void Flush() + { + // ignore + } + + + +private: + + // ------------------------------------------------------------------- + void Grow(size_t need = 0) + { + // 1.5 and phi are very heap-friendly growth factors (the first + // allows for frequent re-use of heap blocks, the second + // forms a fibonacci sequence with similar characteristics - + // since this heavily depends on the heap implementation + // and other factors as well, i'll just go with 1.5 since + // it is quicker to compute). + size_t new_size = std::max(initial, std::max( need, cur_size+(cur_size>>1) )); + + const uint8_t* const old = buffer; + buffer = new uint8_t[new_size]; + + if (old) { + memcpy(buffer,old,cur_size); + delete[] old; + } + + cur_size = new_size; + } + +private: + + uint8_t* buffer; + size_t cur_size,file_size, cursor, initial; + + const std::string file; + BlobIOSystem* const creator; +}; + + +#define AI_BLOBIO_MAGIC "$blobfile" + +// -------------------------------------------------------------------------------------------- +/** Redirect IOSystem to a blob */ +// -------------------------------------------------------------------------------------------- +class BlobIOSystem : public IOSystem +{ + + friend class BlobIOStream; + typedef std::pair BlobEntry; + +public: + + BlobIOSystem() + { + } + + virtual ~BlobIOSystem() + { + BOOST_FOREACH(BlobEntry& blobby, blobs) { + delete blobby.second; + } + } + +public: + + // ------------------------------------------------------------------- + const char* GetMagicFileName() const + { + return AI_BLOBIO_MAGIC; + } + + + // ------------------------------------------------------------------- + aiExportDataBlob* GetBlobChain() + { + // one must be the master + aiExportDataBlob* master = NULL, *cur; + BOOST_FOREACH(const BlobEntry& blobby, blobs) { + if (blobby.first == AI_BLOBIO_MAGIC) { + master = blobby.second; + break; + } + } + if (!master) { + DefaultLogger::get()->error("BlobIOSystem: no data written or master file was not closed properly."); + return NULL; + } + + master->name.Set(""); + + cur = master; + BOOST_FOREACH(const BlobEntry& blobby, blobs) { + if (blobby.second == master) { + continue; + } + + cur->next = blobby.second; + cur = cur->next; + + // extract the file extension from the file written + const std::string::size_type s = blobby.first.find_first_of('.'); + cur->name.Set(s == std::string::npos ? blobby.first : blobby.first.substr(s+1)); + } + + // give up blob ownership + blobs.clear(); + return master; + } + +public: + + // ------------------------------------------------------------------- + virtual bool Exists( const char* pFile) const { + return created.find(std::string(pFile)) != created.end(); + } + + + // ------------------------------------------------------------------- + virtual char getOsSeparator() const { + return '/'; + } + + + // ------------------------------------------------------------------- + virtual IOStream* Open(const char* pFile, + const char* pMode) + { + if (pMode[0] != 'w') { + return NULL; + } + + created.insert(std::string(pFile)); + return new BlobIOStream(this,std::string(pFile)); + } + + // ------------------------------------------------------------------- + virtual void Close( IOStream* pFile) + { + delete pFile; + } + +private: + + // ------------------------------------------------------------------- + void OnDestruct(const std::string& filename, BlobIOStream* child) + { + // we don't know in which the files are closed, so we + // can't reliably say that the first must be the master + // file ... + blobs.push_back( BlobEntry(filename,child->GetBlob()) ); + } + +private: + std::set created; + std::vector< BlobEntry > blobs; +}; + + +// -------------------------------------------------------------------------------------------- +BlobIOStream :: ~BlobIOStream() +{ + creator->OnDestruct(file,this); + delete[] buffer; +} + + +} // end Assimp + +#endif diff --git a/code/CInterfaceIOWrapper.h b/code/CInterfaceIOWrapper.h new file mode 100644 index 000000000..a32bd9c11 --- /dev/null +++ b/code/CInterfaceIOWrapper.h @@ -0,0 +1,159 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (ASSIMP) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 aiFileIO -> IOSystem wrapper*/ + +#ifndef AI_CIOSYSTEM_H_INCLUDED +#define AI_CIOSYSTEM_H_INCLUDED + +#include "../include/aiFileIO.h" + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +// Custom IOStream implementation for the C-API +class CIOStreamWrapper : public IOStream +{ + friend class CIOSystemWrapper; +public: + + CIOStreamWrapper(aiFile* pFile) + : mFile(pFile) + {} + + // ................................................................... + size_t Read(void* pvBuffer, + size_t pSize, + size_t pCount + ){ + // need to typecast here as C has no void* + return mFile->ReadProc(mFile,(char*)pvBuffer,pSize,pCount); + } + + // ................................................................... + size_t Write(const void* pvBuffer, + size_t pSize, + size_t pCount + ){ + // need to typecast here as C has no void* + return mFile->WriteProc(mFile,(const char*)pvBuffer,pSize,pCount); + } + + // ................................................................... + aiReturn Seek(size_t pOffset, + aiOrigin pOrigin + ){ + return mFile->SeekProc(mFile,pOffset,pOrigin); + } + + // ................................................................... + size_t Tell(void) const { + return mFile->TellProc(mFile); + } + + // ................................................................... + size_t FileSize() const { + return mFile->FileSizeProc(mFile); + } + + // ................................................................... + void Flush () { + return mFile->FlushProc(mFile); + } + +private: + aiFile* mFile; +}; + +// ------------------------------------------------------------------------------------------------ +// Custom IOStream implementation for the C-API +class CIOSystemWrapper : public IOSystem +{ +public: + CIOSystemWrapper(aiFileIO* pFile) + : mFileSystem(pFile) + {} + + // ................................................................... + bool Exists( const char* pFile) const { + CIOSystemWrapper* pip = const_cast(this); + IOStream* p = pip->Open(pFile); + if (p){ + pip->Close(p); + return true; + } + return false; + } + + // ................................................................... + char getOsSeparator() const { +#ifndef _WIN32 + return '/'; +#else + return '\\'; +#endif + } + + // ................................................................... + IOStream* Open(const char* pFile,const char* pMode = "rb") { + aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile,pMode); + if (!p) { + return NULL; + } + return new CIOStreamWrapper(p); + } + + // ................................................................... + void Close( IOStream* pFile) { + if (!pFile) { + return; + } + mFileSystem->CloseProc(mFileSystem,((CIOStreamWrapper*) pFile)->mFile); + delete pFile; + } +private: + aiFileIO* mFileSystem; +}; + +} + +#endif + diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index 6fcac6928..196441e84 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -46,18 +46,18 @@ using namespace Assimp; namespace Assimp { - // ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ // Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp -void ExportSceneCollada( aiExportDataBlob* pBlob, const aiScene* pScene) +void ExportSceneCollada(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene) { // invoke the exporter ColladaExporter iDoTheExportThing( pScene); - // we're still here - export successfully completed. Load result into given blob - pBlob->size = iDoTheExportThing.mOutput.tellp(); - pBlob->data = new char[pBlob->size]; - iDoTheExportThing.mOutput.seekg( 0); - iDoTheExportThing.mOutput.read( (char*) pBlob->data, pBlob->size); + // we're still here - export successfully completed. Write result to the given IOSYstem + boost::scoped_ptr outfile (pIOSystem->Open(pFile,"wt")); + + // XXX maybe use a small wrapper around IOStream that behaves like std::stringstream in order to avoid the extra copy. + outfile->Write( iDoTheExportThing.mOutput.str().c_str(), iDoTheExportThing.mOutput.tellp(),1); } } // end of namespace Assimp diff --git a/code/Exceptional.h b/code/Exceptional.h index 95fd03f79..46c355a34 100644 --- a/code/Exceptional.h +++ b/code/Exceptional.h @@ -65,6 +65,8 @@ public: private: }; +typedef DeadlyImportError DeadlyExportError; + #ifdef _MSC_VER # pragma warning(default : 4275) #endif diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 4129c3311..d5a6f3e6d 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -46,24 +46,26 @@ to the import interface (in fact, it is largely symmetric), the internal implementations differs a lot. Exporters are considered stateless and are simple callbacks which we maintain in a global list along with their description strings. + +Here we implement only the C++ interface (Assimp::Exporter). */ #include "AssimpPCH.h" #ifndef ASSIMP_BUILD_NO_EXPORT -#include "DefaultIOStream.h" #include "DefaultIOSystem.h" +#include "BlobIOSystem.h" namespace Assimp { // ------------------------------------------------------------------------------------------------ // Exporter worker function prototypes. Should not be necessary to #ifndef them, it's just a prototype -void ExportSceneCollada(aiExportDataBlob*, const aiScene*); -void ExportScene3DS(aiExportDataBlob*, const aiScene*) { } +void ExportSceneCollada(const char*,IOSystem*, const aiScene*); +void ExportScene3DS(const char*, IOSystem*, const aiScene*) {} /// Function pointer type of a Export worker function -typedef void (*fpExportFunc)(aiExportDataBlob*, const aiScene*); +typedef void (*fpExportFunc)(const char*,IOSystem*,const aiScene*); // ------------------------------------------------------------------------------------------------ /// Internal description of an Assimp export format option @@ -76,10 +78,11 @@ struct ExportFormatEntry fpExportFunc mExportFunction; // Constructor to fill all entries - ExportFormatEntry( const char* pId, const char* pDesc, fpExportFunc pFunction) + ExportFormatEntry( const char* pId, const char* pDesc, const char* pExtension, fpExportFunc pFunction) { mDescription.id = pId; mDescription.description = pDesc; + mDescription.fileExtension = pExtension; mExportFunction = pFunction; } }; @@ -89,11 +92,11 @@ struct ExportFormatEntry ExportFormatEntry gExporters[] = { #ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER - ExportFormatEntry( "collada", "Collada .dae", &ExportSceneCollada), + ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada), #endif #ifndef ASSIMP_BUILD_NO_3DS_EXPORTER - ExportFormatEntry( "3ds", "Autodesk .3ds", &ExportScene3DS), + ExportFormatEntry( "3ds", "Autodesk 3DS (legacy format)", "3ds" , &ExportScene3DS), #endif }; @@ -121,6 +124,8 @@ public: bool mIsDefaultIOHandler; }; +#define ASSIMP_NUM_EXPORTERS (sizeof(gExporters)/sizeof(gExporters[0])) + } // end of namespace Assimp @@ -174,30 +179,43 @@ const aiExportDataBlob* Exporter :: ExportToBlob( const aiScene* pScene, const pimpl->blob = NULL; } - /* TODO (ALEX) - boost::shared_ptr old = pimpl->mIOSystem; + boost::shared_ptr old = pimpl->mIOSystem; - BlobIOSystem* blobio; - pimpl->mIOSystem = blobio = new BlobIOSystem(); + BlobIOSystem* blobio = new BlobIOSystem(); + pimpl->mIOSystem = boost::shared_ptr( blobio ); - if (AI_SUCCESS != Export(pScene,pFormatId)) { + if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName())) { pimpl->mIOSystem = old; return NULL; } - pimpl->blob = blobio->GetBlob(); + pimpl->blob = blobio->GetBlobChain(); pimpl->mIOSystem = old; return pimpl->blob; - */ - return NULL; } // ------------------------------------------------------------------------------------------------ aiReturn Exporter :: Export( const aiScene* pScene, const char* pFormatId, const char* pPath ) { + ASSIMP_BEGIN_EXCEPTION_REGION(); + for (size_t i = 0; i < ASSIMP_NUM_EXPORTERS; ++i) { + if (!strcmp(gExporters[i].mDescription.id,pFormatId)) { + + try { + gExporters[i].mExportFunction(pPath,pimpl->mIOSystem.get(),pScene); + } + catch (DeadlyExportError& err) { + // XXX what to do with the error message? Maybe introduce extra member to hold it, similar to Assimp.Importer + DefaultLogger::get()->error(err.what()); + return AI_FAILURE; + } + return AI_SUCCESS; + } + } + ASSIMP_END_EXCEPTION_REGION(aiReturn); return AI_FAILURE; } @@ -219,80 +237,20 @@ const aiExportDataBlob* Exporter :: GetOrphanedBlob() const // ------------------------------------------------------------------------------------------------ -size_t Exporter :: aiGetExportFormatCount() const +size_t Exporter :: GetExportFormatCount() const { - return ::aiGetExportFormatCount(); + return ASSIMP_NUM_EXPORTERS; } // ------------------------------------------------------------------------------------------------ -const aiExportFormatDesc* Exporter :: aiGetExportFormatDescription( size_t pIndex ) const +const aiExportFormatDesc* Exporter :: GetExportFormatDescription( size_t pIndex ) const { - return ::aiGetExportFormatDescription(pIndex); -} - - - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API size_t aiGetExportFormatCount(void) -{ - return sizeof( Assimp::gExporters) / sizeof( Assimp::ExportFormatEntry); -} - - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API const aiExportFormatDesc* aiGetExportFormatDescription( size_t pIndex) -{ - if( pIndex >= aiGetExportFormatCount() ) - return NULL; - - return &Assimp::gExporters[pIndex].mDescription; -} - - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API aiReturn aiExportScene( const aiScene* pScene, const char* pFormatId, const char* pFileName ) -{ - return ::aiExportSceneEx(pScene,pFormatId,pFileName,NULL); -} - - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API aiReturn aiExportSceneEx( const aiScene* pScene, const char* pFormatId, const char* pFileName, const aiFileIO* pIO) -{ - return AI_FAILURE; -} - - - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const aiScene* pScene, const char* pFormatId ) -{ - // find a suitable exporter - const Assimp::ExportFormatEntry* exporter = NULL; - for( size_t a = 0; a < aiGetExportFormatCount(); ++a ) - { - if( strcmp( Assimp::gExporters[a].mDescription.id, pFormatId) == 0 ) - exporter = Assimp::gExporters + a; - } - - if( !exporter ) - return NULL; - - aiExportDataBlob* blob = new aiExportDataBlob; - exporter->mExportFunction( blob, pScene); - if( !blob->data || blob->size == 0 ) - { - delete blob; + if (pIndex >= ASSIMP_NUM_EXPORTERS) { return NULL; } - return blob; + return &gExporters[pIndex].mDescription; } -// ------------------------------------------------------------------------------------------------ -ASSIMP_API C_STRUCT void aiReleaseExportData( const aiExportDataBlob* pData ) -{ - delete pData; -} #endif // !ASSIMP_BUILD_NO_EXPORT diff --git a/include/IOSystem.h b/include/IOSystem.h index cae49e2bc..faf30dcd5 100644 --- a/include/IOSystem.h +++ b/include/IOSystem.h @@ -149,7 +149,7 @@ public: /** @brief Compares two paths and check whether the point to * identical files. * - * The dummy implementation of this virtual performs a + * The dummy implementation of this virtual member performs a * case-insensitive comparison of the given strings. The default IO * system implementation uses OS mechanisms to convert relative into * absolute paths, so the result can be trusted. diff --git a/include/export.h b/include/export.h index 86b9d3d29..8dbf4cce1 100644 --- a/include/export.h +++ b/include/export.h @@ -91,6 +91,10 @@ ASSIMP_API const C_STRUCT aiExportFormatDesc* aiGetExportFormatDescription( size * exported scene. The memory referred by this structure is owned by Assimp. Use aiReleaseExportedFile() * to free its resources. Don't try to free the memory on your side - it will crash for most build configurations * due to conflicting heaps. +* +* Blobs can be nested - each blob may reference another blob, which may in turn reference another blob and so on. +* This is used when exporters write more than one output file for a given #aiScene. See the remarks for +* #aiExportDataBlob::name for more information. */ struct aiExportDataBlob #ifdef __cplusplus @@ -103,11 +107,28 @@ struct aiExportDataBlob /// The data. void* data; + /** Name of the blob. An empty string always + indicates the first (and primary) blob, + which contains the actual file data. + Any other blobs are auxiliary files produced + by exporters (i.e. material files). Existence + of such files depends on the file format. Most + formats don't split assets across multiple files. + + If used, blob names usually contain the file + extension that should be used when writing + the data to disc. + */ + aiString name; + + /** Pointer to the next blob in the chain or NULL if there is none. */ + aiExportDataBlob * next; + #ifdef __cplusplus /// Default constructor - aiExportDataBlob() { size = 0; data = NULL; } + aiExportDataBlob() { size = 0; data = next = NULL; } /// Releases the data - ~aiExportDataBlob() { delete static_cast( data ); } + ~aiExportDataBlob() { delete static_cast( data ); delete next; } #endif // __cplusplus }; @@ -137,7 +158,7 @@ ASSIMP_API aiReturn aiExportScene( const C_STRUCT aiScene* pScene, const char* p * #aiExportSceneToBlob is provided as convienience function to export to memory buffers. * @return a status code indicating the result of the export */ -ASSIMP_API aiReturn aiExportSceneEx( const C_STRUCT aiScene* pScene, const char* pFormatId, const char* pFileName, const C_STRUCT aiFileIO* pIO ); +ASSIMP_API aiReturn aiExportSceneEx( const C_STRUCT aiScene* pScene, const char* pFormatId, const char* pFileName, C_STRUCT aiFileIO* pIO ); // -------------------------------------------------------------------------------- /** Exports the given scene to a chosen file format. Returns the exported data as a binary blob which diff --git a/include/export.hpp b/include/export.hpp index 2a807ae52..083ec0538 100644 --- a/include/export.hpp +++ b/include/export.hpp @@ -59,6 +59,16 @@ namespace Assimp { * * The interface is modelled after the importer interface and mostly * symmetric. The same rules for threading etc. apply. + * + * In a nutshell, there are two export interfaces: #Export, which writes the + * output file(s) either to the regular file system or to a user-supplied + * #IOSystem, and #ExportToBlob which returns a linked list of memory + * buffers (blob), each referring to one output file (in most cases + * there will be only one output file of course, but this extra complexity is + * needed since Assimp aims at supporting a wide range of file formats). + * + * #ExportToBlob is especially useful if you intend to work + * with the data in-memory. */ class ASSIMP_API Exporter // TODO: causes good ol' base class has no dll interface warning @@ -77,9 +87,11 @@ public: // ------------------------------------------------------------------- /** Supplies a custom IO handler to the exporter to use to open and - * access files. If you need the exporter to use custion IO logic to - * access the files, you need to supply a custom implementation of - * IOSystem and IOFile to the exporter. + * access files. + * + * If you need #Export to use custom IO logic to access the files, + * you need to supply a custom implementation of IOSystem and + * IOFile to the exporter. * * #Exporter takes ownership of the object and will destroy it * afterwards. The previously assigned handler will be deleted. @@ -124,7 +136,8 @@ public: * export formats are available. * @return the exported data or NULL in case of error. * @note If the Exporter instance did already hold a blob from - * a previous call to #ExportToBlob, it will be disposed. */ + * a previous call to #ExportToBlob, it will be disposed. + * Any IO handlers set via #SetIOHandler are ignored here.*/ const aiExportDataBlob* ExportToBlob( const aiScene* pScene, const char* pFormatId ); @@ -155,7 +168,7 @@ public: /** Returns the number of export file formats available in the current * Assimp build. Use #Exporter::GetExportFormatDescription to * retrieve infos of a specific export format */ - size_t aiGetExportFormatCount() const; + size_t GetExportFormatCount() const; // ------------------------------------------------------------------- @@ -166,7 +179,7 @@ public: * for. Valid range is 0 to #Exporter::GetExportFormatCount * @return A description of that specific export format. * NULL if pIndex is out of range. */ - const aiExportFormatDesc* aiGetExportFormatDescription( size_t pIndex ) const; + const aiExportFormatDesc* GetExportFormatDescription( size_t pIndex ) const; protected: diff --git a/test/unit/utExport.cpp b/test/unit/utExport.cpp new file mode 100644 index 000000000..ec5088510 --- /dev/null +++ b/test/unit/utExport.cpp @@ -0,0 +1,76 @@ + +#include "UnitTestPCH.h" +#include "utExport.h" + +#ifndef ASSIMP_BUILD_NO_EXPORT + +CPPUNIT_TEST_SUITE_REGISTRATION (ExporterTest); + +void ExporterTest :: setUp (void) +{ + + ex = new Assimp::Exporter(); + im = new Assimp::Importer(); + + pTest = im->ReadFile("../../test/models/X/test.x",0); +} + + +void ExporterTest :: tearDown (void) +{ + delete ex; + delete im; +} + + +void ExporterTest :: testExportToFile (void) +{ + const char* file = "unittest_output.dae"; + CPPUNIT_ASSERT_EQUAL(AI_SUCCESS,ex->Export(pTest,"collada",file)); + + // check if we can read it again + CPPUNIT_ASSERT(im->ReadFile(file,0)); +} + + +void ExporterTest :: testExportToBlob (void) +{ + const aiExportDataBlob* blob = ex->ExportToBlob(pTest,"collada"); + CPPUNIT_ASSERT(blob); + CPPUNIT_ASSERT(blob->data); + CPPUNIT_ASSERT(blob->size > 0); + CPPUNIT_ASSERT(!blob->name.length); + + // XXX test chained blobs (i.e. obj file with accompanying mtl script) + + // check if we can read it again + CPPUNIT_ASSERT(im->ReadFileFromMemory(blob->data,blob->size,0,"dae")); +} + + +void ExporterTest :: testCppExportInterface (void) +{ + CPPUNIT_ASSERT(ex->GetExportFormatCount() > 0); + for(size_t i = 0; i < ex->GetExportFormatCount(); ++i) { + const aiExportFormatDesc* const desc = ex->GetExportFormatDescription(i); + CPPUNIT_ASSERT(desc); + CPPUNIT_ASSERT(desc->description && strlen(desc->description)); + CPPUNIT_ASSERT(desc->fileExtension && strlen(desc->fileExtension)); + CPPUNIT_ASSERT(desc->id && strlen(desc->id)); + } + + CPPUNIT_ASSERT(ex->IsDefaultIOHandler()); +} + + +void ExporterTest :: testCExportInterface (void) +{ + CPPUNIT_ASSERT(aiGetExportFormatCount() > 0); + for(size_t i = 0; i < aiGetExportFormatCount(); ++i) { + const aiExportFormatDesc* const desc = aiGetExportFormatDescription(i); + CPPUNIT_ASSERT(desc); + // rest has aleady been validated by testCppExportInterface + } +} + +#endif diff --git a/test/unit/utExport.h b/test/unit/utExport.h new file mode 100644 index 000000000..2d43f92ef --- /dev/null +++ b/test/unit/utExport.h @@ -0,0 +1,41 @@ +#ifndef INCLUDED_UT_EXPORT_H +#define INCLUDED_UT_EXPORT_H + +#ifndef ASSIMP_BUILD_NO_EXPORT + +#include +#include + +using namespace Assimp; + +class ExporterTest : public CPPUNIT_NS :: TestFixture +{ + CPPUNIT_TEST_SUITE (ExporterTest); + CPPUNIT_TEST (testExportToFile); + CPPUNIT_TEST (testExportToBlob); + CPPUNIT_TEST (testCppExportInterface); + CPPUNIT_TEST (testCExportInterface); + CPPUNIT_TEST_SUITE_END (); + + public: + void setUp (void); + void tearDown (void); + + protected: + + void testExportToFile (void); + void testExportToBlob (void); + void testCppExportInterface (void); + void testCExportInterface (void); + + private: + + + const aiScene* pTest; + Assimp::Exporter* ex; + Assimp::Importer* im; +}; + +#endif + +#endif