nuclear@0: /* nuclear@0: --------------------------------------------------------------------------- nuclear@0: Open Asset Import Library (assimp) nuclear@0: --------------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the following nuclear@0: conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: --------------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file Exporter.cpp nuclear@0: nuclear@0: Assimp export interface. While it's public interface bears many similarities nuclear@0: to the import interface (in fact, it is largely symmetric), the internal nuclear@0: implementations differs a lot. Exporters are considered stateless and are nuclear@0: simple callbacks which we maintain in a global list along with their nuclear@0: description strings. nuclear@0: nuclear@0: Here we implement only the C++ interface (Assimp::Exporter). nuclear@0: */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: nuclear@0: #ifndef ASSIMP_BUILD_NO_EXPORT nuclear@0: nuclear@0: #include "DefaultIOSystem.h" nuclear@0: #include "BlobIOSystem.h" nuclear@0: #include "SceneCombiner.h" nuclear@0: #include "BaseProcess.h" nuclear@0: #include "Importer.h" // need this for GetPostProcessingStepInstanceList() nuclear@0: nuclear@0: #include "MakeVerboseFormat.h" nuclear@0: #include "ConvertToLHProcess.h" nuclear@0: nuclear@0: namespace Assimp { nuclear@0: nuclear@0: // PostStepRegistry.cpp nuclear@0: void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out); nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Exporter worker function prototypes. Should not be necessary to #ifndef them, it's just a prototype nuclear@0: void ExportSceneCollada(const char*,IOSystem*, const aiScene*); nuclear@0: void ExportSceneObj(const char*,IOSystem*, const aiScene*); nuclear@0: void ExportSceneSTL(const char*,IOSystem*, const aiScene*); nuclear@0: void ExportScenePly(const char*,IOSystem*, const aiScene*); nuclear@0: void ExportScene3DS(const char*, IOSystem*, const aiScene*) {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // global array of all export formats which Assimp supports in its current build nuclear@0: Exporter::ExportFormatEntry gExporters[] = nuclear@0: { nuclear@0: #ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER nuclear@0: Exporter::ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada), nuclear@0: #endif nuclear@0: nuclear@0: #ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER nuclear@0: Exporter::ExportFormatEntry( "obj", "Wavefront OBJ format", "obj", &ExportSceneObj, nuclear@0: aiProcess_GenNormals | aiProcess_PreTransformVertices), nuclear@0: #endif nuclear@0: nuclear@0: #ifndef ASSIMP_BUILD_NO_STL_EXPORTER nuclear@0: Exporter::ExportFormatEntry( "stl", "Stereolithography", "stl" , &ExportSceneSTL, nuclear@0: aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices nuclear@0: ), nuclear@0: #endif nuclear@0: nuclear@0: #ifndef ASSIMP_BUILD_NO_PLY_EXPORTER nuclear@0: Exporter::ExportFormatEntry( "ply", "Stanford Polygon Library", "ply" , &ExportScenePly, nuclear@0: aiProcess_PreTransformVertices nuclear@0: ), nuclear@0: #endif nuclear@0: nuclear@0: //#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER nuclear@0: // ExportFormatEntry( "3ds", "Autodesk 3DS (legacy format)", "3ds" , &ExportScene3DS), nuclear@0: //#endif nuclear@0: }; nuclear@0: nuclear@0: #define ASSIMP_NUM_EXPORTERS (sizeof(gExporters)/sizeof(gExporters[0])) nuclear@0: nuclear@0: nuclear@0: class ExporterPimpl { nuclear@0: public: nuclear@0: nuclear@0: ExporterPimpl() nuclear@0: : blob() nuclear@0: , mIOSystem(new Assimp::DefaultIOSystem()) nuclear@0: , mIsDefaultIOHandler(true) nuclear@0: { nuclear@0: GetPostProcessingStepInstanceList(mPostProcessingSteps); nuclear@0: nuclear@0: // grab all builtin exporters nuclear@0: mExporters.resize(ASSIMP_NUM_EXPORTERS); nuclear@0: std::copy(gExporters,gExporters+ASSIMP_NUM_EXPORTERS,mExporters.begin()); nuclear@0: } nuclear@0: nuclear@0: ~ExporterPimpl() nuclear@0: { nuclear@0: delete blob; nuclear@0: nuclear@0: // Delete all post-processing plug-ins nuclear@0: for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++) { nuclear@0: delete mPostProcessingSteps[a]; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: public: nuclear@0: nuclear@0: aiExportDataBlob* blob; nuclear@0: boost::shared_ptr< Assimp::IOSystem > mIOSystem; nuclear@0: bool mIsDefaultIOHandler; nuclear@0: nuclear@0: /** Post processing steps we can apply at the imported data. */ nuclear@0: std::vector< BaseProcess* > mPostProcessingSteps; nuclear@0: nuclear@0: /** Last fatal export error */ nuclear@0: std::string mError; nuclear@0: nuclear@0: /** Exporters, this includes those registered using #Assimp::Exporter::RegisterExporter */ nuclear@0: std::vector mExporters; nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: } // end of namespace Assimp nuclear@0: nuclear@0: nuclear@0: nuclear@0: nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: Exporter :: Exporter() nuclear@0: : pimpl(new ExporterPimpl()) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: Exporter :: ~Exporter() nuclear@0: { nuclear@0: FreeBlob(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void Exporter :: SetIOHandler( IOSystem* pIOHandler) nuclear@0: { nuclear@0: pimpl->mIsDefaultIOHandler = !pIOHandler; nuclear@0: pimpl->mIOSystem.reset(pIOHandler); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: IOSystem* Exporter :: GetIOHandler() const nuclear@0: { nuclear@0: return pimpl->mIOSystem.get(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: bool Exporter :: IsDefaultIOHandler() const nuclear@0: { nuclear@0: return pimpl->mIsDefaultIOHandler; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: const aiExportDataBlob* Exporter :: ExportToBlob( const aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing ) nuclear@0: { nuclear@0: if (pimpl->blob) { nuclear@0: delete pimpl->blob; nuclear@0: pimpl->blob = NULL; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: boost::shared_ptr old = pimpl->mIOSystem; nuclear@0: nuclear@0: BlobIOSystem* blobio = new BlobIOSystem(); nuclear@0: pimpl->mIOSystem = boost::shared_ptr( blobio ); nuclear@0: nuclear@0: if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName())) { nuclear@0: pimpl->mIOSystem = old; nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: pimpl->blob = blobio->GetBlobChain(); nuclear@0: pimpl->mIOSystem = old; nuclear@0: nuclear@0: return pimpl->blob; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: aiReturn Exporter :: Export( const aiScene* pScene, const char* pFormatId, const char* pPath, unsigned int pPreprocessing ) nuclear@0: { nuclear@0: ASSIMP_BEGIN_EXCEPTION_REGION(); nuclear@0: nuclear@0: pimpl->mError = ""; nuclear@0: for (size_t i = 0; i < pimpl->mExporters.size(); ++i) { nuclear@0: const Exporter::ExportFormatEntry& exp = pimpl->mExporters[i]; nuclear@0: if (!strcmp(exp.mDescription.id,pFormatId)) { nuclear@0: nuclear@0: try { nuclear@0: nuclear@0: // Always create a full copy of the scene. We might optimize this one day, nuclear@0: // but for now it is the most pragmatic way. nuclear@0: aiScene* scenecopy_tmp; nuclear@0: SceneCombiner::CopyScene(&scenecopy_tmp,pScene); nuclear@0: nuclear@0: std::auto_ptr scenecopy(scenecopy_tmp); nuclear@0: const ScenePrivateData* const priv = ScenePriv(pScene); nuclear@0: nuclear@0: // steps that are not idempotent, i.e. we might need to run them again, usually to get back to the nuclear@0: // original state before the step was applied first. When checking which steps we don't need nuclear@0: // to run, those are excluded. nuclear@0: const unsigned int nonIdempotentSteps = aiProcess_FlipWindingOrder | aiProcess_FlipUVs | aiProcess_MakeLeftHanded; nuclear@0: nuclear@0: // Erase all pp steps that were already applied to this scene nuclear@0: unsigned int pp = (exp.mEnforcePP | pPreprocessing) & ~(priv nuclear@0: ? (priv->mPPStepsApplied & ~nonIdempotentSteps) nuclear@0: : 0u); nuclear@0: nuclear@0: // If no extra postprocessing was specified, and we obtained this scene from an nuclear@0: // Assimp importer, apply the reverse steps automatically. nuclear@0: if (!pPreprocessing && priv) { nuclear@0: pp |= (nonIdempotentSteps & priv->mPPStepsApplied); nuclear@0: } nuclear@0: nuclear@0: // If the input scene is not in verbose format, but there is at least postprocessing step that relies on it, nuclear@0: // we need to run the MakeVerboseFormat step first. nuclear@0: if (scenecopy->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { nuclear@0: nuclear@0: bool verbosify = false; nuclear@0: for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { nuclear@0: BaseProcess* const p = pimpl->mPostProcessingSteps[a]; nuclear@0: nuclear@0: if (p->IsActive(pp) && p->RequireVerboseFormat()) { nuclear@0: verbosify = true; nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (verbosify || (exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) { nuclear@0: DefaultLogger::get()->debug("export: Scene data not in verbose format, applying MakeVerboseFormat step first"); nuclear@0: nuclear@0: MakeVerboseFormatProcess proc; nuclear@0: proc.Execute(scenecopy.get()); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (pp) { nuclear@0: // the three 'conversion' steps need to be executed first because all other steps rely on the standard data layout nuclear@0: { nuclear@0: FlipWindingOrderProcess step; nuclear@0: if (step.IsActive(pp)) { nuclear@0: step.Execute(scenecopy.get()); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: { nuclear@0: FlipUVsProcess step; nuclear@0: if (step.IsActive(pp)) { nuclear@0: step.Execute(scenecopy.get()); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: { nuclear@0: MakeLeftHandedProcess step; nuclear@0: if (step.IsActive(pp)) { nuclear@0: step.Execute(scenecopy.get()); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // dispatch other processes nuclear@0: for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { nuclear@0: BaseProcess* const p = pimpl->mPostProcessingSteps[a]; nuclear@0: nuclear@0: if (p->IsActive(pp) nuclear@0: && !dynamic_cast(p) nuclear@0: && !dynamic_cast(p) nuclear@0: && !dynamic_cast(p)) { nuclear@0: nuclear@0: p->Execute(scenecopy.get()); nuclear@0: } nuclear@0: } nuclear@0: ScenePrivateData* const privOut = ScenePriv(scenecopy.get()); nuclear@0: ai_assert(privOut); nuclear@0: nuclear@0: privOut->mPPStepsApplied |= pp; nuclear@0: } nuclear@0: nuclear@0: exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get()); nuclear@0: } nuclear@0: catch (DeadlyExportError& err) { nuclear@0: pimpl->mError = err.what(); nuclear@0: return AI_FAILURE; nuclear@0: } nuclear@0: return AI_SUCCESS; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: pimpl->mError = std::string("Found no exporter to handle this file format: ") + pFormatId; nuclear@0: ASSIMP_END_EXCEPTION_REGION(aiReturn); nuclear@0: return AI_FAILURE; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: const char* Exporter :: GetErrorString() const nuclear@0: { nuclear@0: return pimpl->mError.c_str(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void Exporter :: FreeBlob( ) nuclear@0: { nuclear@0: delete pimpl->blob; nuclear@0: pimpl->blob = NULL; nuclear@0: nuclear@0: pimpl->mError = ""; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: const aiExportDataBlob* Exporter :: GetBlob() const nuclear@0: { nuclear@0: return pimpl->blob; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: const aiExportDataBlob* Exporter :: GetOrphanedBlob() const nuclear@0: { nuclear@0: const aiExportDataBlob* tmp = pimpl->blob; nuclear@0: pimpl->blob = NULL; nuclear@0: return tmp; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: size_t Exporter :: GetExportFormatCount() const nuclear@0: { nuclear@0: return pimpl->mExporters.size(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: const aiExportFormatDesc* Exporter :: GetExportFormatDescription( size_t pIndex ) const nuclear@0: { nuclear@0: if (pIndex >= GetExportFormatCount()) { nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: return &pimpl->mExporters[pIndex].mDescription; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: aiReturn Exporter :: RegisterExporter(const ExportFormatEntry& desc) nuclear@0: { nuclear@0: BOOST_FOREACH(const ExportFormatEntry& e, pimpl->mExporters) { nuclear@0: if (!strcmp(e.mDescription.id,desc.mDescription.id)) { nuclear@0: return aiReturn_FAILURE; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: pimpl->mExporters.push_back(desc); nuclear@0: return aiReturn_SUCCESS; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void Exporter :: UnregisterExporter(const char* id) nuclear@0: { nuclear@0: for(std::vector::iterator it = pimpl->mExporters.begin(); it != pimpl->mExporters.end(); ++it) { nuclear@0: if (!strcmp((*it).mDescription.id,id)) { nuclear@0: pimpl->mExporters.erase(it); nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: #endif // !ASSIMP_BUILD_NO_EXPORT