diff --git a/code/AssimpCExport.cpp b/code/AssimpCExport.cpp index 26df1861b..90e327668 100644 --- a/code/AssimpCExport.cpp +++ b/code/AssimpCExport.cpp @@ -47,6 +47,7 @@ Assimp C export interface. See Exporter.cpp for some notes. #ifndef ASSIMP_BUILD_NO_EXPORT #include "CInterfaceIOWrapper.h" +#include "SceneCombiner.h" using namespace Assimp; @@ -63,6 +64,15 @@ ASSIMP_API const aiExportFormatDesc* aiGetExportFormatDescription( size_t pIndex return Exporter().GetExportFormatDescription(pIndex); } +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiCopyScene(const aiScene* pIn, aiScene** pOut) +{ + if (!pOut || !pIn) { + return; + } + + SceneCombiner::CopyScene(pOut,pIn,false); +} // ------------------------------------------------------------------------------------------------ ASSIMP_API aiReturn aiExportScene( const aiScene* pScene, const char* pFormatId, const char* pFileName, unsigned int pPreprocessing ) @@ -97,7 +107,7 @@ ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const aiScene* } // ------------------------------------------------------------------------------------------------ -ASSIMP_API C_STRUCT void aiReleaseExportData( const aiExportDataBlob* pData ) +ASSIMP_API C_STRUCT void aiReleaseExportBlob( const aiExportDataBlob* pData ) { delete pData; } diff --git a/code/BlenderModifier.cpp b/code/BlenderModifier.cpp index ba75d06b7..bd7e5ed7c 100644 --- a/code/BlenderModifier.cpp +++ b/code/BlenderModifier.cpp @@ -185,6 +185,8 @@ void BlenderModifier_Mirror :: DoIt(aiNode& out, ConversionData& conv_data, co const MirrorModifierData& mir = static_cast(orig_modifier); ai_assert(mir.modifier.type == ModifierData::eModifierType_Mirror); + conv_data.meshes->reserve(conv_data.meshes->size() + out.mNumMeshes); + // XXX not entirely correct, mirroring on two axes results in 4 distinct objects in blender ... // take all input meshes and clone them diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 809d1a7aa..1ae4da17b 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -145,6 +145,9 @@ public: /** Post processing steps we can apply at the imported data. */ std::vector< BaseProcess* > mPostProcessingSteps; + + /** Last fatal export error */ + std::string mError; }; #define ASSIMP_NUM_EXPORTERS (sizeof(gExporters)/sizeof(gExporters[0])) @@ -168,7 +171,7 @@ Exporter :: Exporter() // ------------------------------------------------------------------------------------------------ Exporter :: ~Exporter() { - delete pimpl; + FreeBlob(); } @@ -224,6 +227,8 @@ const aiExportDataBlob* Exporter :: ExportToBlob( const aiScene* pScene, const aiReturn Exporter :: Export( const aiScene* pScene, const char* pFormatId, const char* pPath, unsigned int pPreprocessing ) { ASSIMP_BEGIN_EXCEPTION_REGION(); + + pimpl->mError = ""; for (size_t i = 0; i < ASSIMP_NUM_EXPORTERS; ++i) { if (!strcmp(gExporters[i].mDescription.id,pFormatId)) { @@ -235,8 +240,24 @@ aiReturn Exporter :: Export( const aiScene* pScene, const char* pFormatId, const SceneCombiner::CopyScene(&scenecopy_tmp,pScene); std::auto_ptr scenecopy(scenecopy_tmp); + const ScenePrivateData* const priv = ScenePriv(pScene); + + // steps that are not idempotent, i.e. we might need to run them again, usually to get back to the + // original state before the step was applied first. When checking which steps we don't need + // to run, those are excluded. + const unsigned int nonIdempotentSteps = aiProcess_FlipWindingOrder | aiProcess_FlipUVs | aiProcess_MakeLeftHanded; + + // Erase all pp steps that were already applied to this scene + unsigned int pp = (gExporters[i].mEnforcePP | pPreprocessing) & ~(priv + ? (priv->mPPStepsApplied & ~nonIdempotentSteps) + : 0u); + + // If no extra postprocessing was specified, and we obtained this scene from an + // Assimp importer, apply the reverse steps automatically. + if (!pPreprocessing && priv) { + pp |= (nonIdempotentSteps & priv->mPPStepsApplied); + } - const unsigned int pp = (gExporters[i].mEnforcePP | pPreprocessing); if (pp) { for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { BaseProcess* const p = pimpl->mPostProcessingSteps[a]; @@ -245,23 +266,45 @@ aiReturn Exporter :: Export( const aiScene* pScene, const char* pFormatId, const p->Execute(scenecopy.get()); } } + ScenePrivateData* const privOut = ScenePriv(scenecopy.get()); + ai_assert(privOut); + + privOut->mPPStepsApplied |= pp; } gExporters[i].mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get()); } 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()); + pimpl->mError = err.what(); return AI_FAILURE; } return AI_SUCCESS; } } + + pimpl->mError = std::string("Found no exporter to handle this file format: ") + pFormatId; ASSIMP_END_EXCEPTION_REGION(aiReturn); return AI_FAILURE; } +// ------------------------------------------------------------------------------------------------ +const char* Exporter :: GetErrorString() const +{ + return pimpl->mError.c_str(); +} + + +// ------------------------------------------------------------------------------------------------ +void Exporter :: FreeBlob( ) +{ + delete pimpl->blob; + pimpl->blob = NULL; + + pimpl->mError = ""; +} + + // ------------------------------------------------------------------------------------------------ const aiExportDataBlob* Exporter :: GetBlob() const { diff --git a/code/SceneCombiner.cpp b/code/SceneCombiner.cpp index f402be1bf..d553c0993 100644 --- a/code/SceneCombiner.cpp +++ b/code/SceneCombiner.cpp @@ -917,11 +917,15 @@ void SceneCombiner::CopySceneFlat(aiScene** _dest,const aiScene* src) } // ------------------------------------------------------------------------------------------------ -void SceneCombiner::CopyScene(aiScene** _dest,const aiScene* src) +void SceneCombiner::CopyScene(aiScene** _dest,const aiScene* src,bool allocate) { ai_assert(NULL != _dest && NULL != src); - aiScene* dest = *_dest = new aiScene(); + if (allocate) { + *_dest = new aiScene(); + } + aiScene* dest = *_dest; + ai_assert(dest); // copy animations dest->mNumAnimations = src->mNumAnimations; @@ -958,6 +962,9 @@ void SceneCombiner::CopyScene(aiScene** _dest,const aiScene* src) // and keep the flags ... dest->mFlags = src->mFlags; + + // source private data might be NULL if the scene is user-allocated (i.e. for use with the export API) + ScenePriv(dest)->mPPStepsApplied = ScenePriv(src) ? ScenePriv(src)->mPPStepsApplied : NULL; } // ------------------------------------------------------------------------------------------------ diff --git a/code/SceneCombiner.h b/code/SceneCombiner.h index 1fb5f9eef..77579d0c0 100644 --- a/code/SceneCombiner.h +++ b/code/SceneCombiner.h @@ -303,7 +303,7 @@ public: * @param dest Receives a pointer to the destination scene * @param src Source scene - remains unmodified. */ - static void CopyScene(aiScene** dest,const aiScene* source); + static void CopyScene(aiScene** dest,const aiScene* source,bool allocate = true); // ------------------------------------------------------------------- diff --git a/include/aiScene.h b/include/aiScene.h index 567bc02e6..d88d70f79 100644 --- a/include/aiScene.h +++ b/include/aiScene.h @@ -362,7 +362,7 @@ struct aiScene #endif // __cplusplus - // internal scene data, do not touch + /** Internal data, do not touch */ #ifdef __cplusplus void* mPrivate; #else diff --git a/include/export.h b/include/export.h index 9df385d62..2a7adb21b 100644 --- a/include/export.h +++ b/include/export.h @@ -56,14 +56,16 @@ extern "C" { struct aiScene; // aiScene.h -/** Describes an file format which Assimp can export to. Use aiGetExportFormatCount() to -* learn how many export formats the current Assimp build supports and aiGetExportFormatDescription() + +// -------------------------------------------------------------------------------- +/** Describes an file format which Assimp can export to. Use #aiGetExportFormatCount() to +* learn how many export formats the current Assimp build supports and #aiGetExportFormatDescription() * to retrieve a description of an export format option. */ struct aiExportFormatDesc { /// a short string ID to uniquely identify the export format. Use this ID string to - /// specify which file format you want to export to when calling aiExportScene(). + /// specify which file format you want to export to when calling #aiExportScene(). /// Example: "dae" or "obj" const char* id; @@ -75,20 +77,89 @@ struct aiExportFormatDesc const char* fileExtension; }; + +// -------------------------------------------------------------------------------- /** Returns the number of export file formats available in the current Assimp build. -* Use aiGetExportFormatDescription() to retrieve infos of a specific export format. -*/ + * Use aiGetExportFormatDescription() to retrieve infos of a specific export format. + */ ASSIMP_API size_t aiGetExportFormatCount(void); -/** Returns a description of the nth export file format. Use aiGetExportFormatCount() -* to learn how many export formats are supported. -* @param pIndex Index of the export format to retrieve information for. Valid range is 0 to aiGetExportFormatCount() -* @return A description of that specific export format. NULL if pIndex is out of range. -*/ + +// -------------------------------------------------------------------------------- +/** Returns a description of the nth export file format. Use #aiGetExportFormatCount() + * to learn how many export formats are supported. + * @param pIndex Index of the export format to retrieve information for. Valid range is + * 0 to #aiGetExportFormatCount() + * @return A description of that specific export format. NULL if pIndex is out of range. + */ ASSIMP_API const C_STRUCT aiExportFormatDesc* aiGetExportFormatDescription( size_t pIndex); -/** Describes a blob of exported scene data. Use aiExportScene() to create a blob containing an -* exported scene. The memory referred by this structure is owned by Assimp. Use aiReleaseExportedFile() + +// -------------------------------------------------------------------------------- +/** Create a modifyable copy of a scene. + * This is useful to import files via Assimp, change their topology and + * export them again. Since the scene returned by the various importer functions + * is const, a modifyable copy is needed. + * @param pIn Valid scene to be copied + * @param pOut User-allocated scene to be filled. + */ +ASSIMP_API void aiCopyScene(const C_STRUCT aiScene* pIn, C_STRUCT aiScene** pOut); + +// -------------------------------------------------------------------------------- +/** Exports the given scene to a chosen file format and writes the result file(s) to disk. +* @param pScene The scene to export. Stays in possession of the caller, is not changed by the function. +* The scene is expected to conform to Assimp's Importer output format as specified +* in the @link data Data Structures Page @endlink. In short, this means the model data +* should use a right-handed coordinate systems, face winding should be counter-clockwise +* and the UV coordinate origin is assumed to be in the upper left. If your input data +* uses different conventions, have a look at the last parameter. +* @param pFormatId ID string to specify to which format you want to export to. Use +* aiGetExportFormatCount() / aiGetExportFormatDescription() to learn which export formats are available. +* @param pFileName Output file to write +* @param pIO custom IO implementation to be used. Use this if you use your own storage methods. +* If none is supplied, a default implementation using standard file IO is used. Note that +* #aiExportSceneToBlob is provided as convenience function to export to memory buffers. +* @param pPreprocessing Accepts any choice of the #aiPostProcessing enumerated +* flags, but in reality only a subset of them makes sense here. Specifying +* 'preprocessing' flags is useful if the input scene does not conform to +* Assimp's default conventions as specified in the @link data Data Structures Page @endlink. +* In short, this means the geometry data should use a right-handed coordinate systems, face +* winding should be counter-clockwise and the UV coordinate origin is assumed to be in +* the upper left. The #aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and +* #aiProcess_FlipWindingOrder flags are used in the import side to allow users +* to have those defaults automatically adapted to their conventions. Specifying those flags +* for exporting has the opposite effect, respectively. Some other of the +* #aiPostProcessSteps enumerated values may be useful as well, but you'll need +* to try out what their effect on the exported file is. Many formats impose +* their own restrictions on the structure of the geometry stored therein, +* so some preprocessing may have little or no effect at all, or may be +* redundant as exporters would apply them anyhow. A good example +* is triangulation - whilst you can enforce it by specifying +* the #aiProcess_Triangulate flag, most export formats support only +* triangulate data so they would run the step anyway. +* @return a status code indicating the result of the export +*/ +ASSIMP_API aiReturn aiExportScene( const C_STRUCT aiScene* pScene, const char* pFormatId, const char* pFileName, unsigned int pPreprocessing); + + +// -------------------------------------------------------------------------------- +/** Exports the given scene to a chosen file format using custom IO logic supplied by you. +* @param pScene The scene to export. Stays in possession of the caller, is not changed by the function. +* @param pFormatId ID string to specify to which format you want to export to. Use +* aiGetExportFormatCount() / aiGetExportFormatDescription() to learn which export formats are available. +* @param pFileName Output file to write +* @param pIO custom IO implementation to be used. Use this if you use your own storage methods. +* If none is supplied, a default implementation using standard file IO is used. Note that +* #aiExportSceneToBlob is provided as convienience function to export to memory buffers. +* @param pPreprocessing Please see the documentation for #aiExportScene +* @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, C_STRUCT aiFileIO* pIO, unsigned int pPreprocessing ); + + +// -------------------------------------------------------------------------------- +/** Describes a blob of exported scene data. Use #aiExportSceneToBlob() to create a blob containing an +* 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. * @@ -132,78 +203,25 @@ struct aiExportDataBlob #endif // __cplusplus }; - -// -------------------------------------------------------------------------------- -/** Exports the given scene to a chosen file format and writes the result file(s) to disk. -* @param pScene The scene to export. Stays in possession of the caller, is not changed by the function. -* The scene is expected to conform to Assimp's Importer output format as specified -* in the @link data Data Structures Page @endlink. In short, this means the model data -* should use a right-handed coordinate systems, face winding should be counter-clockwise -* and the UV coordinate origin is assumed to be in the upper left. If your input data -* uses different conventions, have a look at the last parameter. -* @param pFormatId ID string to specify to which format you want to export to. Use -* aiGetExportFormatCount() / aiGetExportFormatDescription() to learn which export formats are available. -* @param pFileName Output file to write -* @param pIO custom IO implementation to be used. Use this if you use your own storage methods. -* If none is supplied, a default implementation using standard file IO is used. Note that -* #aiExportSceneToBlob is provided as convenience function to export to memory buffers. -* @param pPreprocessing Accepts any choice of the #aiPostProcessing enumerated -* flags, but in reality only a subset of them makes sense here. Specifying -* 'preprocessing' flags is useful if the input scene does not conform to -* Assimp's default conventions as specified in the @link data Data Structures Page @endlink. -* In short, this means the geometry data should use a right-handed coordinate systems, face -* winding should be counter-clockwise and the UV coordinate origin is assumed to be in -* the upper left. The #aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and -* #aiProcess_FlipWindingOrder flags are used in the import side to allow users -* to have those defaults automatically adapted to their conventions. Specifying those flags -* for exporting has the opposite effect, respectively. Some other of the -* #aiPostProcessSteps enumerated values may be useful as well, but you'll need -* to try out what their effect on the exported file is. Many formats impose -* their own restrictions on the structure of the geometry stored therein, -* so some preprocessing may have little or no effect at all, or may be -* redundant as exporters would apply them anyhow. A good example -* is triangulation - whilst you can enforce it by specifying -* the #aiProcess_Triangulate flag, most export formats support only -* triangulate data so they would run the step even if it wasn't requested. -* @return a status code indicating the result of the export -*/ -ASSIMP_API aiReturn aiExportScene( const C_STRUCT aiScene* pScene, const char* pFormatId, const char* pFileName, unsigned int pPreprocessing); - - -// -------------------------------------------------------------------------------- -/** Exports the given scene to a chosen file format using custom IO logic supplied by you. -* @param pScene The scene to export. Stays in possession of the caller, is not changed by the function. -* @param pFormatId ID string to specify to which format you want to export to. Use -* aiGetExportFormatCount() / aiGetExportFormatDescription() to learn which export formats are available. -* @param pFileName Output file to write -* @param pIO custom IO implementation to be used. Use this if you use your own storage methods. -* If none is supplied, a default implementation using standard file IO is used. Note that -* #aiExportSceneToBlob is provided as convienience function to export to memory buffers. -* @param pPreprocessing Please see the documentation for #aiExportScene -* @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, C_STRUCT aiFileIO* pIO, unsigned int pPreprocessing ); - // -------------------------------------------------------------------------------- /** Exports the given scene to a chosen file format. Returns the exported data as a binary blob which -* you can write into a file or something. When you're done with the data, use aiReleaseExportedBlob() +* you can write into a file or something. When you're done with the data, use #aiReleaseExportBlob() * to free the resources associated with the export. * @param pScene The scene to export. Stays in possession of the caller, is not changed by the function. * @param pFormatId ID string to specify to which format you want to export to. Use -* aiGetExportFormatCount() / aiGetExportFormatDescription() to learn which export formats are available. +* #aiGetExportFormatCount() / #aiGetExportFormatDescription() to learn which export formats are available. * @param pPreprocessing Please see the documentation for #aiExportScene * @return the exported data or NULL in case of error */ ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const C_STRUCT aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing ); - // -------------------------------------------------------------------------------- /** Releases the memory associated with the given exported data. Use this function to free a data blob * returned by aiExportScene(). -* @param pData the data blob returned by aiExportScenetoBlob +* @param pData the data blob returned by #aiExportSceneToBlob */ -ASSIMP_API C_STRUCT void aiReleaseExportData( const C_STRUCT aiExportDataBlob* pData ); +ASSIMP_API C_STRUCT void aiReleaseExportBlob( const C_STRUCT aiExportDataBlob* pData ); #ifdef __cplusplus } diff --git a/include/export.hpp b/include/export.hpp index 22592e9a5..43013e589 100644 --- a/include/export.hpp +++ b/include/export.hpp @@ -99,8 +99,7 @@ public: * to use its default implementation, which uses plain file IO. * * @param pIOHandler The IO handler to be used in all file accesses - * of the Importer. - */ + * of the Importer. */ void SetIOHandler( IOSystem* pIOHandler); // ------------------------------------------------------------------- @@ -109,16 +108,14 @@ public: * interface is the default IO handler provided by ASSIMP. The default * handler is active as long the application doesn't supply its own * custom IO handler via #SetIOHandler(). - * @return A valid IOSystem interface, never NULL. - */ + * @return A valid IOSystem interface, never NULL. */ IOSystem* GetIOHandler() const; // ------------------------------------------------------------------- /** Checks whether a default IO handler is active * A default handler is active as long the application doesn't * supply its own custom IO handler via #SetIOHandler(). - * @return true by default - */ + * @return true by default */ bool IsDefaultIOHandler() const; @@ -172,6 +169,18 @@ public: inline aiReturn Export( const aiScene* pScene, const std::string& pFormatId, const std::string& pPath, unsigned int pPreprocessing = 0u); + // ------------------------------------------------------------------- + /** Returns an error description of an error that occurred in #Export + * or #ExportToBlob + * + * Returns an empty string if no error occurred. + * @return A description of the last error, an empty string if no + * error occurred. The string is never NULL. + * + * @note The returned function remains valid until one of the + * following methods is called: #Export, #ExportToBlob, #FreeBlob */ + const char* GetErrorString() const; + // ------------------------------------------------------------------- /** Return the blob obtained from the last call to #ExportToBlob */ @@ -181,10 +190,21 @@ public: // ------------------------------------------------------------------- /** Orphan the blob from the last call to #ExportToBlob. This means * the caller takes ownership and is thus responsible for calling - * #aiReleaseExportData to free the data again. */ + * the C API function #aiReleaseExportBlob to release it. */ const aiExportDataBlob* GetOrphanedBlob() const; + // ------------------------------------------------------------------- + /** Frees the current blob. + * + * The function does nothing if no blob has previously been + * previously produced via #ExportToBlob. #FreeBlob is called + * automatically by the destructor. The only reason to call + * it manually would be to reclain as much storage as possible + * without giving up the #Exporter instance yet. */ + void FreeBlob( ); + + // ------------------------------------------------------------------- /** Returns the number of export file formats available in the current * Assimp build. Use #Exporter::GetExportFormatDescription to