diff --git a/code/DefaultProgressHandler.h b/code/DefaultProgressHandler.h index a40501fe5..851c17be6 100644 --- a/code/DefaultProgressHandler.h +++ b/code/DefaultProgressHandler.h @@ -52,9 +52,7 @@ namespace Assimp { // ------------------------------------------------------------------------------------ /** @brief Internal default implementation of the #ProgressHandler interface. */ -class DefaultProgressHandler - : public ProgressHandler { - +class DefaultProgressHandler : public ProgressHandler { virtual bool Update(float /*percentage*/) { return false; diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 725d7bf5a..63a934091 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -56,22 +56,22 @@ Here we implement only the C++ interface (Assimp::Exporter). #include #include -#include "BaseProcess.h" -#include "Importer.h" // need this for GetPostProcessingStepInstanceList() +#include +#include +#include +#include +#include +#include "DefaultProgressHandler.h" +#include "BaseProcess.h" #include "JoinVerticesProcess.h" #include "MakeVerboseFormat.h" #include "ConvertToLHProcess.h" #include "PretransformVertices.h" #include #include "ScenePrivate.h" -#include -#include -#include -#include -#include -#include +#include namespace Assimp { @@ -188,10 +188,14 @@ Exporter::ExportFormatEntry gExporters[] = class ExporterPimpl { public: ExporterPimpl() - : blob() - , mIOSystem(new Assimp::DefaultIOSystem()) - , mIsDefaultIOHandler(true) - { + : blob() + , mIOSystem(new Assimp::DefaultIOSystem()) + , mIsDefaultIOHandler(true) + , mProgressHandler( nullptr ) + , mIsDefaultProgressHandler( true ) + , mPostProcessingSteps() + , mError() + , mExporters() { GetPostProcessingStepInstanceList(mPostProcessingSteps); // grab all built-in exporters @@ -201,8 +205,7 @@ public: } } - ~ExporterPimpl() - { + ~ExporterPimpl() { delete blob; // Delete all post-processing plug-ins @@ -216,6 +219,10 @@ public: std::shared_ptr< Assimp::IOSystem > mIOSystem; bool mIsDefaultIOHandler; + /** The progress handler */ + ProgressHandler *mProgressHandler; + bool mIsDefaultProgressHandler; + /** Post processing steps we can apply at the imported data. */ std::vector< BaseProcess* > mPostProcessingSteps; @@ -233,13 +240,16 @@ using namespace Assimp; // ------------------------------------------------------------------------------------------------ Exporter :: Exporter() : pimpl(new ExporterPimpl()) { - // empty + pimpl->mProgressHandler = new DefaultProgressHandler(); } // ------------------------------------------------------------------------------------------------ Exporter::~Exporter() { FreeBlob(); - + if (pimpl->mIsDefaultProgressHandler) { + delete pimpl->mProgressHandler; + pimpl->mProgressHandler = nullptr; + } delete pimpl; } @@ -259,12 +269,32 @@ bool Exporter::IsDefaultIOHandler() const { return pimpl->mIsDefaultIOHandler; } +// ------------------------------------------------------------------------------------------------ +void Exporter::SetProgressHandler(ProgressHandler* pHandler) { + ai_assert(nullptr != pimpl); + + if ( nullptr == pHandler) { + // Release pointer in the possession of the caller + pimpl->mProgressHandler = new DefaultProgressHandler(); + pimpl->mIsDefaultProgressHandler = true; + return; + } + + if (pimpl->mProgressHandler == pHandler) { + return; + } + + delete pimpl->mProgressHandler; + pimpl->mProgressHandler = pHandler; + pimpl->mIsDefaultProgressHandler = false; +} + // ------------------------------------------------------------------------------------------------ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId, unsigned int, const ExportProperties* /*pProperties*/ ) { if (pimpl->blob) { delete pimpl->blob; - pimpl->blob = NULL; + pimpl->blob = nullptr; } std::shared_ptr old = pimpl->mIOSystem; @@ -273,7 +303,7 @@ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const cha if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName())) { pimpl->mIOSystem = old; - return NULL; + return nullptr; } pimpl->blob = blobio->GetBlobChain(); @@ -295,6 +325,7 @@ bool IsVerboseFormat(const aiMesh* mesh) { } } } + return true; } @@ -305,6 +336,7 @@ bool IsVerboseFormat(const aiScene* pScene) { return false; } } + return true; } @@ -319,6 +351,8 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c // meshes upfront. const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || IsVerboseFormat(pScene); + pimpl->mProgressHandler->UpdateFileWrite(0, 4); + pimpl->mError = ""; for (size_t i = 0; i < pimpl->mExporters.size(); ++i) { const Exporter::ExportFormatEntry& exp = pimpl->mExporters[i]; @@ -326,9 +360,11 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c try { // Always create a full copy of the scene. We might optimize this one day, // but for now it is the most pragmatic way. - aiScene* scenecopy_tmp = NULL; + aiScene* scenecopy_tmp = nullptr; SceneCombiner::CopyScene(&scenecopy_tmp,pScene); + pimpl->mProgressHandler->UpdateFileWrite(1, 4); + std::unique_ptr scenecopy(scenecopy_tmp); const ScenePrivateData* const priv = ScenePriv(pScene); @@ -375,6 +411,8 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c } } + pimpl->mProgressHandler->UpdateFileWrite(2, 4); + if (pp) { // the three 'conversion' steps need to be executed first because all other steps rely on the standard data layout { @@ -418,11 +456,13 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c } } ScenePrivateData* const privOut = ScenePriv(scenecopy.get()); - ai_assert(privOut); + ai_assert(nullptr != privOut); privOut->mPPStepsApplied |= pp; } + pimpl->mProgressHandler->UpdateFileWrite(3, 4); + if(must_join_again) { JoinVerticesProcess proc; proc.Execute(scenecopy.get()); @@ -430,6 +470,8 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c ExportProperties emptyProperties; // Never pass NULL ExportProperties so Exporters don't have to worry. exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProperties ? pProperties : &emptyProperties); + + pimpl->mProgressHandler->UpdateFileWrite(4, 4); } catch (DeadlyExportError& err) { pimpl->mError = err.what(); return AI_FAILURE; @@ -452,7 +494,7 @@ const char* Exporter::GetErrorString() const { // ------------------------------------------------------------------------------------------------ void Exporter::FreeBlob() { delete pimpl->blob; - pimpl->blob = NULL; + pimpl->blob = nullptr; pimpl->mError = ""; } @@ -465,7 +507,7 @@ const aiExportDataBlob* Exporter::GetBlob() const { // ------------------------------------------------------------------------------------------------ const aiExportDataBlob* Exporter::GetOrphanedBlob() const { const aiExportDataBlob* tmp = pimpl->blob; - pimpl->blob = NULL; + pimpl->blob = nullptr; return tmp; } @@ -545,75 +587,63 @@ bool ExportProperties::SetPropertyString(const char* szName, const std::string& // ------------------------------------------------------------------------------------------------ // Set a configuration property -bool ExportProperties :: SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) -{ +bool ExportProperties::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) { return SetGenericProperty(mMatrixProperties, szName,value); } // ------------------------------------------------------------------------------------------------ // Get a configuration property -int ExportProperties :: GetPropertyInteger(const char* szName, - int iErrorReturn /*= 0xffffffff*/) const -{ +int ExportProperties::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const { return GetGenericProperty(mIntProperties,szName,iErrorReturn); } // ------------------------------------------------------------------------------------------------ // Get a configuration property -ai_real ExportProperties :: GetPropertyFloat(const char* szName, - ai_real iErrorReturn /*= 10e10*/) const -{ +ai_real ExportProperties::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const { return GetGenericProperty(mFloatProperties,szName,iErrorReturn); } // ------------------------------------------------------------------------------------------------ // Get a configuration property -const std::string ExportProperties :: GetPropertyString(const char* szName, - const std::string& iErrorReturn /*= ""*/) const -{ +const std::string ExportProperties::GetPropertyString(const char* szName, + const std::string& iErrorReturn /*= ""*/) const { return GetGenericProperty(mStringProperties,szName,iErrorReturn); } // ------------------------------------------------------------------------------------------------ // Has a configuration property -const aiMatrix4x4 ExportProperties :: GetPropertyMatrix(const char* szName, - const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const -{ +const aiMatrix4x4 ExportProperties::GetPropertyMatrix(const char* szName, + const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const { return GetGenericProperty(mMatrixProperties,szName,iErrorReturn); } // ------------------------------------------------------------------------------------------------ // Has a configuration property -bool ExportProperties :: HasPropertyInteger(const char* szName) const -{ +bool ExportProperties::HasPropertyInteger(const char* szName) const { return HasGenericProperty(mIntProperties, szName); } // ------------------------------------------------------------------------------------------------ // Has a configuration property -bool ExportProperties :: HasPropertyBool(const char* szName) const -{ +bool ExportProperties::HasPropertyBool(const char* szName) const { return HasGenericProperty(mIntProperties, szName); } // ------------------------------------------------------------------------------------------------ // Has a configuration property -bool ExportProperties :: HasPropertyFloat(const char* szName) const -{ +bool ExportProperties::HasPropertyFloat(const char* szName) const { return HasGenericProperty(mFloatProperties, szName); } // ------------------------------------------------------------------------------------------------ // Has a configuration property -bool ExportProperties :: HasPropertyString(const char* szName) const -{ +bool ExportProperties::HasPropertyString(const char* szName) const { return HasGenericProperty(mStringProperties, szName); } // ------------------------------------------------------------------------------------------------ // Has a configuration property -bool ExportProperties :: HasPropertyMatrix(const char* szName) const -{ +bool ExportProperties::HasPropertyMatrix(const char* szName) const { return HasGenericProperty(mMatrixProperties, szName); } diff --git a/code/Importer.cpp b/code/Importer.cpp index 32bec9414..fe6df187c 100644 --- a/code/Importer.cpp +++ b/code/Importer.cpp @@ -315,22 +315,19 @@ void Importer::SetIOHandler( IOSystem* pIOHandler) // ------------------------------------------------------------------------------------------------ // Get the currently set IO handler -IOSystem* Importer::GetIOHandler() const -{ +IOSystem* Importer::GetIOHandler() const { return pimpl->mIOHandler; } // ------------------------------------------------------------------------------------------------ // Check whether a custom IO handler is currently set -bool Importer::IsDefaultIOHandler() const -{ +bool Importer::IsDefaultIOHandler() const { return pimpl->mIsDefaultHandler; } // ------------------------------------------------------------------------------------------------ // Supplies a custom progress handler to get regular callbacks during importing -void Importer::SetProgressHandler ( ProgressHandler* pHandler ) -{ +void Importer::SetProgressHandler ( ProgressHandler* pHandler ) { ASSIMP_BEGIN_EXCEPTION_REGION(); // If the new handler is zero, allocate a default implementation. if (!pHandler) @@ -351,15 +348,13 @@ void Importer::SetProgressHandler ( ProgressHandler* pHandler ) // ------------------------------------------------------------------------------------------------ // Get the currently set progress handler -ProgressHandler* Importer::GetProgressHandler() const -{ +ProgressHandler* Importer::GetProgressHandler() const { return pimpl->mProgressHandler; } // ------------------------------------------------------------------------------------------------ // Check whether a custom progress handler is currently set -bool Importer::IsDefaultProgressHandler() const -{ +bool Importer::IsDefaultProgressHandler() const { return pimpl->mIsDefaultProgressHandler; } diff --git a/include/assimp/Exporter.hpp b/include/assimp/Exporter.hpp index 3d1a9ea85..4e0843b60 100644 --- a/include/assimp/Exporter.hpp +++ b/include/assimp/Exporter.hpp @@ -57,6 +57,7 @@ namespace Assimp { class ExporterPimpl; class IOSystem; +class ProgressHandler; // ---------------------------------------------------------------------------------- /** CPP-API: The Exporter class forms an C++ interface to the export functionality @@ -84,8 +85,7 @@ public: typedef void (*fpExportFunc)(const char*, IOSystem*, const aiScene*, const ExportProperties*); /** Internal description of an Assimp export format option */ - struct ExportFormatEntry - { + struct ExportFormatEntry { /// Public description structure to be returned by aiGetExportFormatDescription() aiExportFormatDesc mDescription; @@ -158,6 +158,19 @@ public: * @return true by default */ bool IsDefaultIOHandler() const; + // ------------------------------------------------------------------- + /** Supplies a custom progress handler to the exporter. This + * interface exposes an #Update() callback, which is called + * more or less periodically (please don't sue us if it + * isn't as periodically as you'd like it to have ...). + * This can be used to implement progress bars and loading + * timeouts. + * @param pHandler Progress callback interface. Pass nullptr to + * disable progress reporting. + * @note Progress handlers can be used to abort the loading + * at almost any time.*/ + void SetProgressHandler(ProgressHandler* pHandler); + // ------------------------------------------------------------------- /** 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. diff --git a/include/assimp/Importer.hpp b/include/assimp/Importer.hpp index 7445c9797..1d1dac19f 100644 --- a/include/assimp/Importer.hpp +++ b/include/assimp/Importer.hpp @@ -330,7 +330,7 @@ public: // ------------------------------------------------------------------- /** Supplies a custom progress handler to the importer. This - * interface exposes a #Update() callback, which is called + * interface exposes an #Update() callback, which is called * more or less periodically (please don't sue us if it * isn't as periodically as you'd like it to have ...). * This can be used to implement progress bars and loading diff --git a/include/assimp/ProgressHandler.hpp b/include/assimp/ProgressHandler.hpp index 0fa1501d4..f295eac39 100644 --- a/include/assimp/ProgressHandler.hpp +++ b/include/assimp/ProgressHandler.hpp @@ -62,11 +62,13 @@ class ASSIMP_API ProgressHandler #endif { protected: - /** @brief Default constructor */ - ProgressHandler () AI_NO_EXCEPT { + /// @brief Default constructor + ProgressHandler () AI_NO_EXCEPT { + // empty } + public: - /** @brief Virtual destructor */ + /// @brief Virtual destructor. virtual ~ProgressHandler () { } @@ -120,8 +122,24 @@ public: Update( f * 0.5f + 0.5f ); } + + // ------------------------------------------------------------------- + /** @brief Progress callback for export steps. + * @param numberOfSteps The number of total processing + * steps + * @param currentStep The index of the current post-processing + * step that will run, or equal to numberOfSteps if all of + * them has finished. This number is always strictly monotone + * increasing, although not necessarily linearly. + * */ + virtual void UpdateFileWrite(int currentStep /*= 0*/, int numberOfSteps /*= 0*/) { + float f = numberOfSteps ? currentStep / (float)numberOfSteps : 1.0f; + Update(f * 0.5f); + } }; // !class ProgressHandler + // ------------------------------------------------------------------------------------ + } // Namespace Assimp #endif // AI_PROGRESSHANDLER_H_INC diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 687432085..01c8daa09 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -87,6 +87,7 @@ SET( IMPORTERS unit/utIFCImportExport.cpp unit/utFBXImporterExporter.cpp unit/utImporter.cpp + unit/ImportExport/utExporter.cpp unit/ut3DImportExport.cpp unit/ut3DSImportExport.cpp unit/utACImportExport.cpp diff --git a/test/unit/ImportExport/utExporter.cpp b/test/unit/ImportExport/utExporter.cpp new file mode 100644 index 000000000..1efe9e132 --- /dev/null +++ b/test/unit/ImportExport/utExporter.cpp @@ -0,0 +1,74 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2018, assimp 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 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. +--------------------------------------------------------------------------- +*/ +#include "UnitTestPCH.h" + +#include +#include + +using namespace Assimp; + +class TestProgressHandler : public ProgressHandler { +public: + TestProgressHandler() : ProgressHandler() { + // empty + } + + virtual ~TestProgressHandler() { + // empty + } + + bool Update(float percentage = -1.f) override { + return true; + } +}; + +class ExporterTest : public ::testing::Test { + // empty +}; + +TEST_F(ExporterTest, ProgressHandlerTest) { + Exporter exporter; + TestProgressHandler *ph(new TestProgressHandler); + exporter.SetProgressHandler(ph); + delete ph; +}