Преглед изворни кода

- 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

aramis_acg пре 14 година
родитељ
комит
621bdef663

+ 2 - 106
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<const aiScene*, Assimp::Importer*> 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<CIOSystemWrapper*>(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

+ 105 - 0
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

+ 326 - 0
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<std::string, aiExportDataBlob*> 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<std::string> created;
+	std::vector< BlobEntry > blobs;
+};
+
+
+// --------------------------------------------------------------------------------------------
+BlobIOStream :: ~BlobIOStream() 
+{
+	creator->OnDestruct(file,this);
+	delete[] buffer;
+}
+
+	
+} // end Assimp
+
+#endif

+ 159 - 0
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<CIOSystemWrapper*>(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
+

+ 7 - 7
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<IOStream> 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

+ 2 - 0
code/Exceptional.h

@@ -65,6 +65,8 @@ public:
 private:
 };
 
+typedef DeadlyImportError DeadlyExportError;
+
 #ifdef _MSC_VER
 #	pragma warning(default : 4275)
 #endif

+ 38 - 80
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<IOSystem*> old = pimpl->mIOSystem;
+	boost::shared_ptr<IOSystem> old = pimpl->mIOSystem;
 
-	BlobIOSystem* blobio;
-	pimpl->mIOSystem = blobio = new BlobIOSystem();
+	BlobIOSystem* blobio = new BlobIOSystem();
+	pimpl->mIOSystem = boost::shared_ptr<IOSystem>( 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 
-{
-	return ::aiGetExportFormatCount();
-}
-
-// ------------------------------------------------------------------------------------------------
-const aiExportFormatDesc* Exporter :: aiGetExportFormatDescription( 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 )
+size_t Exporter :: GetExportFormatCount() const 
 {
-	return ::aiExportSceneEx(pScene,pFormatId,pFileName,NULL);
+	return ASSIMP_NUM_EXPORTERS;
 }
 
-
 // ------------------------------------------------------------------------------------------------
-ASSIMP_API aiReturn aiExportSceneEx( const aiScene* pScene, const char* pFormatId, const char* pFileName, const aiFileIO* pIO)
+const aiExportFormatDesc* Exporter :: GetExportFormatDescription( size_t pIndex ) const 
 {
-	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

+ 1 - 1
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.

+ 24 - 3
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<char*>( data ); }
+	~aiExportDataBlob() { delete static_cast<char*>( 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

+ 19 - 6
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:

+ 76 - 0
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

+ 41 - 0
test/unit/utExport.h

@@ -0,0 +1,41 @@
+#ifndef INCLUDED_UT_EXPORT_H
+#define INCLUDED_UT_EXPORT_H
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+
+#include <export.h>
+#include <export.hpp>
+
+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