diff options
author | sanine <sanine.not@pm.me> | 2022-04-16 11:55:09 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-04-16 11:55:09 -0500 |
commit | db81b925d776103326128bf629cbdda576a223e7 (patch) | |
tree | 58bea8155c686733310009f6bed7363f91fbeb9d /libs/assimp/code/Common | |
parent | 55860037b14fb3893ba21cf2654c83d349cc1082 (diff) |
move 3rd-party librarys into libs/ and add built-in honeysuckle
Diffstat (limited to 'libs/assimp/code/Common')
49 files changed, 13826 insertions, 0 deletions
diff --git a/libs/assimp/code/Common/AssertHandler.cpp b/libs/assimp/code/Common/AssertHandler.cpp new file mode 100644 index 0000000..d0bd6cc --- /dev/null +++ b/libs/assimp/code/Common/AssertHandler.cpp @@ -0,0 +1,72 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ + +/** @file AssertHandler.cpp + * @brief Implementation of assert handling logic. + */ + +#include "AssertHandler.h" + +#include <iostream> +#include <cstdlib> + +void Assimp::defaultAiAssertHandler(const char* failedExpression, const char* file, int line) +{ + std::cerr << "ai_assert failure in " << file << "(" << line << "): " << failedExpression << std::endl; + std::abort(); +} + +namespace +{ + Assimp::AiAssertHandler s_handler = Assimp::defaultAiAssertHandler; +} + +void Assimp::setAiAssertHandler(AiAssertHandler handler) +{ + s_handler = handler; +} + +void Assimp::aiAssertViolation(const char* failedExpression, const char* file, int line) +{ + s_handler(failedExpression, file, line); +} diff --git a/libs/assimp/code/Common/AssertHandler.h b/libs/assimp/code/Common/AssertHandler.h new file mode 100644 index 0000000..2515f0b --- /dev/null +++ b/libs/assimp/code/Common/AssertHandler.h @@ -0,0 +1,75 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2020, 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. + +---------------------------------------------------------------------- +*/ + +/** @file Provides facilities to replace the default assert handler. */ + +#ifndef INCLUDED_AI_ASSERTHANDLER_H +#define INCLUDED_AI_ASSERTHANDLER_H + +#include <assimp/ai_assert.h> +#include <assimp/defs.h> + +namespace Assimp +{ + // --------------------------------------------------------------------------- + /** Signature of functions which handle assert violations. + */ + using AiAssertHandler = void (*)(const char* failedExpression, const char* file, int line); + + // --------------------------------------------------------------------------- + /** Set the assert handler. + */ + ASSIMP_API void setAiAssertHandler(AiAssertHandler handler); + + // --------------------------------------------------------------------------- + /** The assert handler which is set by default. + * + * This issues a message to stderr and calls abort. + */ + ASSIMP_API void defaultAiAssertHandler(const char* failedExpression, const char* file, int line); + + // --------------------------------------------------------------------------- + /** Dispatches an assert violation to the assert handler. + */ + ASSIMP_API void aiAssertViolation(const char* failedExpression, const char* file, int line); +} // end of namespace Assimp + +#endif // INCLUDED_AI_ASSERTHANDLER_H
\ No newline at end of file diff --git a/libs/assimp/code/Common/Assimp.cpp b/libs/assimp/code/Common/Assimp.cpp new file mode 100644 index 0000000..71e312c --- /dev/null +++ b/libs/assimp/code/Common/Assimp.cpp @@ -0,0 +1,1299 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ +/** @file Assimp.cpp + * @brief Implementation of the Plain-C API + */ + +#include <assimp/BaseImporter.h> +#include <assimp/Exceptional.h> +#include <assimp/GenericProperty.h> +#include <assimp/cimport.h> +#include <assimp/importerdesc.h> +#include <assimp/scene.h> +#include <assimp/DefaultLogger.hpp> +#include <assimp/Importer.hpp> +#include <assimp/LogStream.hpp> + +#include "CApi/CInterfaceIOWrapper.h" +#include "Importer.h" +#include "ScenePrivate.h" + +#include <list> + +// ------------------------------------------------------------------------------------------------ +#ifndef ASSIMP_BUILD_SINGLETHREADED +#include <mutex> +#include <thread> +#endif +// ------------------------------------------------------------------------------------------------ +using namespace Assimp; + +namespace Assimp { +// underlying structure for aiPropertyStore +typedef BatchLoader::PropertyMap PropertyMap; + +#if defined(__has_warning) +#if __has_warning("-Wordered-compare-function-pointers") +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wordered-compare-function-pointers" +#endif +#endif + +/** Stores the LogStream objects for all active C log streams */ +struct mpred { + bool operator()(const aiLogStream &s0, const aiLogStream &s1) const { + return s0.callback < s1.callback && s0.user < s1.user; + } +}; + +#if defined(__has_warning) +#if __has_warning("-Wordered-compare-function-pointers") +#pragma GCC diagnostic pop +#endif +#endif +typedef std::map<aiLogStream, Assimp::LogStream *, mpred> LogStreamMap; + +/** Stores the LogStream objects allocated by #aiGetPredefinedLogStream */ +typedef std::list<Assimp::LogStream *> PredefLogStreamMap; + +/** Local storage of all active log streams */ +static LogStreamMap gActiveLogStreams; + +/** Local storage of LogStreams allocated by #aiGetPredefinedLogStream */ +static PredefLogStreamMap gPredefinedStreams; + +/** Error message of the last failed import process */ +static std::string gLastErrorString; + +/** Verbose logging active or not? */ +static aiBool gVerboseLogging = false; + +/** will return all registered importers. */ +void GetImporterInstanceList(std::vector<BaseImporter *> &out); + +/** will delete all registered importers. */ +void DeleteImporterInstanceList(std::vector<BaseImporter *> &out); +} // namespace Assimp + +#ifndef ASSIMP_BUILD_SINGLETHREADED +/** Global mutex to manage the access to the log-stream map */ +static std::mutex gLogStreamMutex; +#endif + +// ------------------------------------------------------------------------------------------------ +// Custom LogStream implementation for the C-API +class LogToCallbackRedirector : public LogStream { +public: + explicit LogToCallbackRedirector(const aiLogStream &s) : + stream(s) { + ai_assert(nullptr != s.callback); + } + + ~LogToCallbackRedirector() { +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard<std::mutex> lock(gLogStreamMutex); +#endif + // (HACK) Check whether the 'stream.user' pointer points to a + // custom LogStream allocated by #aiGetPredefinedLogStream. + // In this case, we need to delete it, too. Of course, this + // might cause strange problems, but the chance is quite low. + + PredefLogStreamMap::iterator it = std::find(gPredefinedStreams.begin(), + gPredefinedStreams.end(), (Assimp::LogStream *)stream.user); + + if (it != gPredefinedStreams.end()) { + delete *it; + gPredefinedStreams.erase(it); + } + } + + /** @copydoc LogStream::write */ + void write(const char *message) { + stream.callback(message, stream.user); + } + +private: + aiLogStream stream; +}; + +// ------------------------------------------------------------------------------------------------ +void ReportSceneNotFoundError() { + ASSIMP_LOG_ERROR("Unable to find the Assimp::Importer for this aiScene. " + "The C-API does not accept scenes produced by the C++ API and vice versa"); + + ai_assert(false); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the given file and returns its content. +const aiScene *aiImportFile(const char *pFile, unsigned int pFlags) { + return aiImportFileEx(pFile, pFlags, nullptr); +} + +// ------------------------------------------------------------------------------------------------ +const aiScene *aiImportFileEx(const char *pFile, unsigned int pFlags, aiFileIO *pFS) { + return aiImportFileExWithProperties(pFile, pFlags, pFS, nullptr); +} + +// ------------------------------------------------------------------------------------------------ +const aiScene *aiImportFileExWithProperties(const char *pFile, unsigned int pFlags, + aiFileIO *pFS, const aiPropertyStore *props) { + ai_assert(nullptr != pFile); + + const aiScene *scene = nullptr; + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // create an Importer for this file + Assimp::Importer *imp = new Assimp::Importer(); + + // copy properties + if (props) { + const PropertyMap *pp = reinterpret_cast<const PropertyMap *>(props); + ImporterPimpl *pimpl = imp->Pimpl(); + pimpl->mIntProperties = pp->ints; + pimpl->mFloatProperties = pp->floats; + pimpl->mStringProperties = pp->strings; + pimpl->mMatrixProperties = pp->matrices; + } + // setup a custom IO system if necessary + if (pFS) { + imp->SetIOHandler(new CIOSystemWrapper(pFS)); + } + + // and have it read the file + scene = imp->ReadFile(pFile, pFlags); + + // if succeeded, store the importer in the scene and keep it alive + if (scene) { + ScenePrivateData *priv = const_cast<ScenePrivateData *>(ScenePriv(scene)); + priv->mOrigImporter = imp; + } else { + // if failed, extract error code and destroy the import + gLastErrorString = imp->GetErrorString(); + delete imp; + } + + // return imported data. If the import failed the pointer is nullptr anyways + ASSIMP_END_EXCEPTION_REGION(const aiScene *); + + return scene; +} + +// ------------------------------------------------------------------------------------------------ +const aiScene *aiImportFileFromMemory( + const char *pBuffer, + unsigned int pLength, + unsigned int pFlags, + const char *pHint) { + return aiImportFileFromMemoryWithProperties(pBuffer, pLength, pFlags, pHint, nullptr); +} + +// ------------------------------------------------------------------------------------------------ +const aiScene *aiImportFileFromMemoryWithProperties( + const char *pBuffer, + unsigned int pLength, + unsigned int pFlags, + const char *pHint, + const aiPropertyStore *props) { + ai_assert(nullptr != pBuffer); + ai_assert(0 != pLength); + + const aiScene *scene = nullptr; + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // create an Importer for this file + Assimp::Importer *imp = new Assimp::Importer(); + + // copy properties + if (props) { + const PropertyMap *pp = reinterpret_cast<const PropertyMap *>(props); + ImporterPimpl *pimpl = imp->Pimpl(); + pimpl->mIntProperties = pp->ints; + pimpl->mFloatProperties = pp->floats; + pimpl->mStringProperties = pp->strings; + pimpl->mMatrixProperties = pp->matrices; + } + + // and have it read the file from the memory buffer + scene = imp->ReadFileFromMemory(pBuffer, pLength, pFlags, pHint); + + // if succeeded, store the importer in the scene and keep it alive + if (scene) { + ScenePrivateData *priv = const_cast<ScenePrivateData *>(ScenePriv(scene)); + priv->mOrigImporter = imp; + } else { + // if failed, extract error code and destroy the import + gLastErrorString = imp->GetErrorString(); + delete imp; + } + // return imported data. If the import failed the pointer is nullptr anyways + ASSIMP_END_EXCEPTION_REGION(const aiScene *); + return scene; +} + +// ------------------------------------------------------------------------------------------------ +// Releases all resources associated with the given import process. +void aiReleaseImport(const aiScene *pScene) { + if (!pScene) { + return; + } + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // find the importer associated with this data + const ScenePrivateData *priv = ScenePriv(pScene); + if (!priv || !priv->mOrigImporter) { + delete pScene; + } else { + // deleting the Importer also deletes the scene + // Note: the reason that this is not written as 'delete priv->mOrigImporter' + // is a suspected bug in gcc 4.4+ (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52339) + Importer *importer = priv->mOrigImporter; + delete importer; + } + + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API const aiScene *aiApplyPostProcessing(const aiScene *pScene, + unsigned int pFlags) { + const aiScene *sc = nullptr; + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // find the importer associated with this data + const ScenePrivateData *priv = ScenePriv(pScene); + if (!priv || !priv->mOrigImporter) { + ReportSceneNotFoundError(); + return nullptr; + } + + sc = priv->mOrigImporter->ApplyPostProcessing(pFlags); + + if (!sc) { + aiReleaseImport(pScene); + return nullptr; + } + + ASSIMP_END_EXCEPTION_REGION(const aiScene *); + return sc; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API const aiScene *aiApplyCustomizedPostProcessing(const aiScene *scene, + BaseProcess *process, + bool requestValidation) { + const aiScene *sc(nullptr); + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // find the importer associated with this data + const ScenePrivateData *priv = ScenePriv(scene); + if (nullptr == priv || nullptr == priv->mOrigImporter) { + ReportSceneNotFoundError(); + return nullptr; + } + + sc = priv->mOrigImporter->ApplyCustomizedPostProcessing(process, requestValidation); + + if (!sc) { + aiReleaseImport(scene); + return nullptr; + } + + ASSIMP_END_EXCEPTION_REGION(const aiScene *); + + return sc; +} + +// ------------------------------------------------------------------------------------------------ +void CallbackToLogRedirector(const char *msg, char *dt) { + ai_assert(nullptr != msg); + ai_assert(nullptr != dt); + LogStream *s = (LogStream *)dt; + + s->write(msg); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiLogStream aiGetPredefinedLogStream(aiDefaultLogStream pStream, const char *file) { + aiLogStream sout; + + ASSIMP_BEGIN_EXCEPTION_REGION(); + LogStream *stream = LogStream::createDefaultStream(pStream, file); + if (!stream) { + sout.callback = nullptr; + sout.user = nullptr; + } else { + sout.callback = &CallbackToLogRedirector; + sout.user = (char *)stream; + } + gPredefinedStreams.push_back(stream); + ASSIMP_END_EXCEPTION_REGION(aiLogStream); + return sout; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiAttachLogStream(const aiLogStream *stream) { + ASSIMP_BEGIN_EXCEPTION_REGION(); + +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard<std::mutex> lock(gLogStreamMutex); +#endif + + LogStream *lg = new LogToCallbackRedirector(*stream); + gActiveLogStreams[*stream] = lg; + + if (DefaultLogger::isNullLogger()) { + DefaultLogger::create(nullptr, (gVerboseLogging == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL)); + } + DefaultLogger::get()->attachStream(lg); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiReturn aiDetachLogStream(const aiLogStream *stream) { + ASSIMP_BEGIN_EXCEPTION_REGION(); + +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard<std::mutex> lock(gLogStreamMutex); +#endif + // find the log-stream associated with this data + LogStreamMap::iterator it = gActiveLogStreams.find(*stream); + // it should be there... else the user is playing fools with us + if (it == gActiveLogStreams.end()) { + return AI_FAILURE; + } + DefaultLogger::get()->detachStream(it->second); + delete it->second; + + gActiveLogStreams.erase(it); + + if (gActiveLogStreams.empty()) { + DefaultLogger::kill(); + } + ASSIMP_END_EXCEPTION_REGION(aiReturn); + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiDetachAllLogStreams(void) { + ASSIMP_BEGIN_EXCEPTION_REGION(); +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard<std::mutex> lock(gLogStreamMutex); +#endif + Logger *logger(DefaultLogger::get()); + if (nullptr == logger) { + return; + } + + for (LogStreamMap::iterator it = gActiveLogStreams.begin(); it != gActiveLogStreams.end(); ++it) { + logger->detachStream(it->second); + delete it->second; + } + gActiveLogStreams.clear(); + DefaultLogger::kill(); + + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiEnableVerboseLogging(aiBool d) { + if (!DefaultLogger::isNullLogger()) { + DefaultLogger::get()->setLogSeverity((d == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL)); + } + gVerboseLogging = d; +} + +// ------------------------------------------------------------------------------------------------ +// Returns the error text of the last failed import process. +const char *aiGetErrorString() { + return gLastErrorString.c_str(); +} + +// ----------------------------------------------------------------------------------------------- +// Return the description of a importer given its index +const aiImporterDesc *aiGetImportFormatDescription(size_t pIndex) { + return Importer().GetImporterInfo(pIndex); +} + +// ----------------------------------------------------------------------------------------------- +// Return the number of importers +size_t aiGetImportFormatCount(void) { + return Importer().GetImporterCount(); +} + +// ------------------------------------------------------------------------------------------------ +// Returns the error text of the last failed import process. +aiBool aiIsExtensionSupported(const char *szExtension) { + ai_assert(nullptr != szExtension); + aiBool candoit = AI_FALSE; + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // FIXME: no need to create a temporary Importer instance just for that .. + Assimp::Importer tmp; + candoit = tmp.IsExtensionSupported(std::string(szExtension)) ? AI_TRUE : AI_FALSE; + + ASSIMP_END_EXCEPTION_REGION(aiBool); + return candoit; +} + +// ------------------------------------------------------------------------------------------------ +// Get a list of all file extensions supported by ASSIMP +void aiGetExtensionList(aiString *szOut) { + ai_assert(nullptr != szOut); + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // FIXME: no need to create a temporary Importer instance just for that .. + Assimp::Importer tmp; + tmp.GetExtensionList(*szOut); + + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Get the memory requirements for a particular import. +void aiGetMemoryRequirements(const C_STRUCT aiScene *pIn, + C_STRUCT aiMemoryInfo *in) { + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // find the importer associated with this data + const ScenePrivateData *priv = ScenePriv(pIn); + if (!priv || !priv->mOrigImporter) { + ReportSceneNotFoundError(); + return; + } + + return priv->mOrigImporter->GetMemoryRequirements(*in); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiPropertyStore *aiCreatePropertyStore(void) { + return reinterpret_cast<aiPropertyStore *>(new PropertyMap()); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiReleasePropertyStore(aiPropertyStore *p) { + delete reinterpret_cast<PropertyMap *>(p); +} + +// ------------------------------------------------------------------------------------------------ +// Importer::SetPropertyInteger +ASSIMP_API void aiSetImportPropertyInteger(aiPropertyStore *p, const char *szName, int value) { + ASSIMP_BEGIN_EXCEPTION_REGION(); + PropertyMap *pp = reinterpret_cast<PropertyMap *>(p); + SetGenericProperty<int>(pp->ints, szName, value); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Importer::SetPropertyFloat +ASSIMP_API void aiSetImportPropertyFloat(aiPropertyStore *p, const char *szName, ai_real value) { + ASSIMP_BEGIN_EXCEPTION_REGION(); + PropertyMap *pp = reinterpret_cast<PropertyMap *>(p); + SetGenericProperty<ai_real>(pp->floats, szName, value); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Importer::SetPropertyString +ASSIMP_API void aiSetImportPropertyString(aiPropertyStore *p, const char *szName, + const C_STRUCT aiString *st) { + if (!st) { + return; + } + ASSIMP_BEGIN_EXCEPTION_REGION(); + PropertyMap *pp = reinterpret_cast<PropertyMap *>(p); + SetGenericProperty<std::string>(pp->strings, szName, std::string(st->C_Str())); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Importer::SetPropertyMatrix +ASSIMP_API void aiSetImportPropertyMatrix(aiPropertyStore *p, const char *szName, + const C_STRUCT aiMatrix4x4 *mat) { + if (!mat) { + return; + } + ASSIMP_BEGIN_EXCEPTION_REGION(); + PropertyMap *pp = reinterpret_cast<PropertyMap *>(p); + SetGenericProperty<aiMatrix4x4>(pp->matrices, szName, *mat); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Rotation matrix to quaternion +ASSIMP_API void aiCreateQuaternionFromMatrix(aiQuaternion *quat, const aiMatrix3x3 *mat) { + ai_assert(nullptr != quat); + ai_assert(nullptr != mat); + *quat = aiQuaternion(*mat); +} + +// ------------------------------------------------------------------------------------------------ +// Matrix decomposition +ASSIMP_API void aiDecomposeMatrix(const aiMatrix4x4 *mat, aiVector3D *scaling, + aiQuaternion *rotation, + aiVector3D *position) { + ai_assert(nullptr != rotation); + ai_assert(nullptr != position); + ai_assert(nullptr != scaling); + ai_assert(nullptr != mat); + mat->Decompose(*scaling, *rotation, *position); +} + +// ------------------------------------------------------------------------------------------------ +// Matrix transpose +ASSIMP_API void aiTransposeMatrix3(aiMatrix3x3 *mat) { + ai_assert(nullptr != mat); + mat->Transpose(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiTransposeMatrix4(aiMatrix4x4 *mat) { + ai_assert(nullptr != mat); + mat->Transpose(); +} + +// ------------------------------------------------------------------------------------------------ +// Vector transformation +ASSIMP_API void aiTransformVecByMatrix3(aiVector3D *vec, + const aiMatrix3x3 *mat) { + ai_assert(nullptr != mat); + ai_assert(nullptr != vec); + *vec *= (*mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiTransformVecByMatrix4(aiVector3D *vec, + const aiMatrix4x4 *mat) { + ai_assert(nullptr != mat); + ai_assert(nullptr != vec); + + *vec *= (*mat); +} + +// ------------------------------------------------------------------------------------------------ +// Matrix multiplication +ASSIMP_API void aiMultiplyMatrix4( + aiMatrix4x4 *dst, + const aiMatrix4x4 *src) { + ai_assert(nullptr != dst); + ai_assert(nullptr != src); + *dst = (*dst) * (*src); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMultiplyMatrix3( + aiMatrix3x3 *dst, + const aiMatrix3x3 *src) { + ai_assert(nullptr != dst); + ai_assert(nullptr != src); + *dst = (*dst) * (*src); +} + +// ------------------------------------------------------------------------------------------------ +// Matrix identity +ASSIMP_API void aiIdentityMatrix3( + aiMatrix3x3 *mat) { + ai_assert(nullptr != mat); + *mat = aiMatrix3x3(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiIdentityMatrix4( + aiMatrix4x4 *mat) { + ai_assert(nullptr != mat); + *mat = aiMatrix4x4(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API C_STRUCT const aiImporterDesc *aiGetImporterDesc(const char *extension) { + if (nullptr == extension) { + return nullptr; + } + const aiImporterDesc *desc(nullptr); + std::vector<BaseImporter *> out; + GetImporterInstanceList(out); + for (size_t i = 0; i < out.size(); ++i) { + if (0 == strncmp(out[i]->GetInfo()->mFileExtensions, extension, strlen(extension))) { + desc = out[i]->GetInfo(); + break; + } + } + + DeleteImporterInstanceList(out); + + return desc; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API int aiVector2AreEqual( + const C_STRUCT aiVector2D *a, + const C_STRUCT aiVector2D *b) { + ai_assert(nullptr != a); + ai_assert(nullptr != b); + return *a == *b; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API int aiVector2AreEqualEpsilon( + const C_STRUCT aiVector2D *a, + const C_STRUCT aiVector2D *b, + const float epsilon) { + ai_assert(nullptr != a); + ai_assert(nullptr != b); + return a->Equal(*b, epsilon); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector2Add( + C_STRUCT aiVector2D *dst, + const C_STRUCT aiVector2D *src) { + ai_assert(nullptr != dst); + ai_assert(nullptr != src); + *dst = *dst + *src; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector2Subtract( + C_STRUCT aiVector2D *dst, + const C_STRUCT aiVector2D *src) { + ai_assert(nullptr != dst); + ai_assert(nullptr != src); + *dst = *dst - *src; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector2Scale( + C_STRUCT aiVector2D *dst, + const float s) { + ai_assert(nullptr != dst); + *dst *= s; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector2SymMul( + C_STRUCT aiVector2D *dst, + const C_STRUCT aiVector2D *other) { + ai_assert(nullptr != dst); + ai_assert(nullptr != other); + *dst = dst->SymMul(*other); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector2DivideByScalar( + C_STRUCT aiVector2D *dst, + const float s) { + ai_assert(nullptr != dst); + *dst /= s; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector2DivideByVector( + C_STRUCT aiVector2D *dst, + C_STRUCT aiVector2D *v) { + ai_assert(nullptr != dst); + ai_assert(nullptr != v); + *dst = *dst / *v; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API float aiVector2Length( + const C_STRUCT aiVector2D *v) { + ai_assert(nullptr != v); + return v->Length(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API float aiVector2SquareLength( + const C_STRUCT aiVector2D *v) { + ai_assert(nullptr != v); + return v->SquareLength(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector2Negate( + C_STRUCT aiVector2D *dst) { + ai_assert(nullptr != dst); + *dst = -(*dst); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API float aiVector2DotProduct( + const C_STRUCT aiVector2D *a, + const C_STRUCT aiVector2D *b) { + ai_assert(nullptr != a); + ai_assert(nullptr != b); + return (*a) * (*b); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector2Normalize( + C_STRUCT aiVector2D *v) { + ai_assert(nullptr != v); + v->Normalize(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API int aiVector3AreEqual( + const C_STRUCT aiVector3D *a, + const C_STRUCT aiVector3D *b) { + ai_assert(nullptr != a); + ai_assert(nullptr != b); + return *a == *b; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API int aiVector3AreEqualEpsilon( + const C_STRUCT aiVector3D *a, + const C_STRUCT aiVector3D *b, + const float epsilon) { + ai_assert(nullptr != a); + ai_assert(nullptr != b); + return a->Equal(*b, epsilon); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API int aiVector3LessThan( + const C_STRUCT aiVector3D *a, + const C_STRUCT aiVector3D *b) { + ai_assert(nullptr != a); + ai_assert(nullptr != b); + return *a < *b; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector3Add( + C_STRUCT aiVector3D *dst, + const C_STRUCT aiVector3D *src) { + ai_assert(nullptr != dst); + ai_assert(nullptr != src); + *dst = *dst + *src; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector3Subtract( + C_STRUCT aiVector3D *dst, + const C_STRUCT aiVector3D *src) { + ai_assert(nullptr != dst); + ai_assert(nullptr != src); + *dst = *dst - *src; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector3Scale( + C_STRUCT aiVector3D *dst, + const float s) { + ai_assert(nullptr != dst); + *dst *= s; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector3SymMul( + C_STRUCT aiVector3D *dst, + const C_STRUCT aiVector3D *other) { + ai_assert(nullptr != dst); + ai_assert(nullptr != other); + *dst = dst->SymMul(*other); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector3DivideByScalar( + C_STRUCT aiVector3D *dst, const float s) { + ai_assert(nullptr != dst); + *dst /= s; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector3DivideByVector( + C_STRUCT aiVector3D *dst, + C_STRUCT aiVector3D *v) { + ai_assert(nullptr != dst); + ai_assert(nullptr != v); + *dst = *dst / *v; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API float aiVector3Length( + const C_STRUCT aiVector3D *v) { + ai_assert(nullptr != v); + return v->Length(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API float aiVector3SquareLength( + const C_STRUCT aiVector3D *v) { + ai_assert(nullptr != v); + return v->SquareLength(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector3Negate( + C_STRUCT aiVector3D *dst) { + ai_assert(nullptr != dst); + *dst = -(*dst); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API float aiVector3DotProduct( + const C_STRUCT aiVector3D *a, + const C_STRUCT aiVector3D *b) { + ai_assert(nullptr != a); + ai_assert(nullptr != b); + return (*a) * (*b); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector3CrossProduct( + C_STRUCT aiVector3D *dst, + const C_STRUCT aiVector3D *a, + const C_STRUCT aiVector3D *b) { + ai_assert(nullptr != dst); + ai_assert(nullptr != a); + ai_assert(nullptr != b); + *dst = *a ^ *b; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector3Normalize( + C_STRUCT aiVector3D *v) { + ai_assert(nullptr != v); + v->Normalize(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector3NormalizeSafe( + C_STRUCT aiVector3D *v) { + ai_assert(nullptr != v); + v->NormalizeSafe(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiVector3RotateByQuaternion( + C_STRUCT aiVector3D *v, + const C_STRUCT aiQuaternion *q) { + ai_assert(nullptr != v); + ai_assert(nullptr != q); + *v = q->Rotate(*v); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix3FromMatrix4( + C_STRUCT aiMatrix3x3 *dst, + const C_STRUCT aiMatrix4x4 *mat) { + ai_assert(nullptr != dst); + ai_assert(nullptr != mat); + *dst = aiMatrix3x3(*mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix3FromQuaternion( + C_STRUCT aiMatrix3x3 *mat, + const C_STRUCT aiQuaternion *q) { + ai_assert(nullptr != mat); + ai_assert(nullptr != q); + *mat = q->GetMatrix(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API int aiMatrix3AreEqual( + const C_STRUCT aiMatrix3x3 *a, + const C_STRUCT aiMatrix3x3 *b) { + ai_assert(nullptr != a); + ai_assert(nullptr != b); + return *a == *b; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API int aiMatrix3AreEqualEpsilon( + const C_STRUCT aiMatrix3x3 *a, + const C_STRUCT aiMatrix3x3 *b, + const float epsilon) { + ai_assert(nullptr != a); + ai_assert(nullptr != b); + return a->Equal(*b, epsilon); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix3Inverse(C_STRUCT aiMatrix3x3 *mat) { + ai_assert(nullptr != mat); + mat->Inverse(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API float aiMatrix3Determinant(const C_STRUCT aiMatrix3x3 *mat) { + ai_assert(nullptr != mat); + return mat->Determinant(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix3RotationZ( + C_STRUCT aiMatrix3x3 *mat, + const float angle) { + ai_assert(nullptr != mat); + aiMatrix3x3::RotationZ(angle, *mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix3FromRotationAroundAxis( + C_STRUCT aiMatrix3x3 *mat, + const C_STRUCT aiVector3D *axis, + const float angle) { + ai_assert(nullptr != mat); + ai_assert(nullptr != axis); + aiMatrix3x3::Rotation(angle, *axis, *mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix3Translation( + C_STRUCT aiMatrix3x3 *mat, + const C_STRUCT aiVector2D *translation) { + ai_assert(nullptr != mat); + ai_assert(nullptr != translation); + aiMatrix3x3::Translation(*translation, *mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix3FromTo( + C_STRUCT aiMatrix3x3 *mat, + const C_STRUCT aiVector3D *from, + const C_STRUCT aiVector3D *to) { + ai_assert(nullptr != mat); + ai_assert(nullptr != from); + ai_assert(nullptr != to); + aiMatrix3x3::FromToMatrix(*from, *to, *mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix4FromMatrix3( + C_STRUCT aiMatrix4x4 *dst, + const C_STRUCT aiMatrix3x3 *mat) { + ai_assert(nullptr != dst); + ai_assert(nullptr != mat); + *dst = aiMatrix4x4(*mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix4FromScalingQuaternionPosition( + C_STRUCT aiMatrix4x4 *mat, + const C_STRUCT aiVector3D *scaling, + const C_STRUCT aiQuaternion *rotation, + const C_STRUCT aiVector3D *position) { + ai_assert(nullptr != mat); + ai_assert(nullptr != scaling); + ai_assert(nullptr != rotation); + ai_assert(nullptr != position); + *mat = aiMatrix4x4(*scaling, *rotation, *position); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix4Add( + C_STRUCT aiMatrix4x4 *dst, + const C_STRUCT aiMatrix4x4 *src) { + ai_assert(nullptr != dst); + ai_assert(nullptr != src); + *dst = *dst + *src; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API int aiMatrix4AreEqual( + const C_STRUCT aiMatrix4x4 *a, + const C_STRUCT aiMatrix4x4 *b) { + ai_assert(nullptr != a); + ai_assert(nullptr != b); + return *a == *b; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API int aiMatrix4AreEqualEpsilon( + const C_STRUCT aiMatrix4x4 *a, + const C_STRUCT aiMatrix4x4 *b, + const float epsilon) { + ai_assert(nullptr != a); + ai_assert(nullptr != b); + return a->Equal(*b, epsilon); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix4Inverse(C_STRUCT aiMatrix4x4 *mat) { + ai_assert(nullptr != mat); + mat->Inverse(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API float aiMatrix4Determinant(const C_STRUCT aiMatrix4x4 *mat) { + ai_assert(nullptr != mat); + return mat->Determinant(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API int aiMatrix4IsIdentity(const C_STRUCT aiMatrix4x4 *mat) { + ai_assert(nullptr != mat); + return mat->IsIdentity(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix4DecomposeIntoScalingEulerAnglesPosition( + const C_STRUCT aiMatrix4x4 *mat, + C_STRUCT aiVector3D *scaling, + C_STRUCT aiVector3D *rotation, + C_STRUCT aiVector3D *position) { + ai_assert(nullptr != mat); + ai_assert(nullptr != scaling); + ai_assert(nullptr != rotation); + ai_assert(nullptr != position); + mat->Decompose(*scaling, *rotation, *position); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix4DecomposeIntoScalingAxisAnglePosition( + const C_STRUCT aiMatrix4x4 *mat, + C_STRUCT aiVector3D *scaling, + C_STRUCT aiVector3D *axis, + ai_real *angle, + C_STRUCT aiVector3D *position) { + ai_assert(nullptr != mat); + ai_assert(nullptr != scaling); + ai_assert(nullptr != axis); + ai_assert(nullptr != angle); + ai_assert(nullptr != position); + mat->Decompose(*scaling, *axis, *angle, *position); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix4DecomposeNoScaling( + const C_STRUCT aiMatrix4x4 *mat, + C_STRUCT aiQuaternion *rotation, + C_STRUCT aiVector3D *position) { + ai_assert(nullptr != mat); + ai_assert(nullptr != rotation); + ai_assert(nullptr != position); + mat->DecomposeNoScaling(*rotation, *position); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix4FromEulerAngles( + C_STRUCT aiMatrix4x4 *mat, + float x, float y, float z) { + ai_assert(nullptr != mat); + mat->FromEulerAnglesXYZ(x, y, z); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix4RotationX( + C_STRUCT aiMatrix4x4 *mat, + const float angle) { + ai_assert(nullptr != mat); + aiMatrix4x4::RotationX(angle, *mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix4RotationY( + C_STRUCT aiMatrix4x4 *mat, + const float angle) { + ai_assert(NULL != mat); + aiMatrix4x4::RotationY(angle, *mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix4RotationZ( + C_STRUCT aiMatrix4x4 *mat, + const float angle) { + ai_assert(nullptr != mat); + aiMatrix4x4::RotationZ(angle, *mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix4FromRotationAroundAxis( + C_STRUCT aiMatrix4x4 *mat, + const C_STRUCT aiVector3D *axis, + const float angle) { + ai_assert(nullptr != mat); + ai_assert(nullptr != axis); + aiMatrix4x4::Rotation(angle, *axis, *mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix4Translation( + C_STRUCT aiMatrix4x4 *mat, + const C_STRUCT aiVector3D *translation) { + ai_assert(nullptr != mat); + ai_assert(nullptr != translation); + aiMatrix4x4::Translation(*translation, *mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix4Scaling( + C_STRUCT aiMatrix4x4 *mat, + const C_STRUCT aiVector3D *scaling) { + ai_assert(nullptr != mat); + ai_assert(nullptr != scaling); + aiMatrix4x4::Scaling(*scaling, *mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMatrix4FromTo( + C_STRUCT aiMatrix4x4 *mat, + const C_STRUCT aiVector3D *from, + const C_STRUCT aiVector3D *to) { + ai_assert(nullptr != mat); + ai_assert(nullptr != from); + ai_assert(nullptr != to); + aiMatrix4x4::FromToMatrix(*from, *to, *mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiQuaternionFromEulerAngles( + C_STRUCT aiQuaternion *q, + float x, float y, float z) { + ai_assert(nullptr != q); + *q = aiQuaternion(x, y, z); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiQuaternionFromAxisAngle( + C_STRUCT aiQuaternion *q, + const C_STRUCT aiVector3D *axis, + const float angle) { + ai_assert(nullptr != q); + ai_assert(nullptr != axis); + *q = aiQuaternion(*axis, angle); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiQuaternionFromNormalizedQuaternion( + C_STRUCT aiQuaternion *q, + const C_STRUCT aiVector3D *normalized) { + ai_assert(nullptr != q); + ai_assert(nullptr != normalized); + *q = aiQuaternion(*normalized); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API int aiQuaternionAreEqual( + const C_STRUCT aiQuaternion *a, + const C_STRUCT aiQuaternion *b) { + ai_assert(nullptr != a); + ai_assert(nullptr != b); + return *a == *b; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API int aiQuaternionAreEqualEpsilon( + const C_STRUCT aiQuaternion *a, + const C_STRUCT aiQuaternion *b, + const float epsilon) { + ai_assert(nullptr != a); + ai_assert(nullptr != b); + return a->Equal(*b, epsilon); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiQuaternionNormalize( + C_STRUCT aiQuaternion *q) { + ai_assert(nullptr != q); + q->Normalize(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiQuaternionConjugate( + C_STRUCT aiQuaternion *q) { + ai_assert(nullptr != q); + q->Conjugate(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiQuaternionMultiply( + C_STRUCT aiQuaternion *dst, + const C_STRUCT aiQuaternion *q) { + ai_assert(nullptr != dst); + ai_assert(nullptr != q); + *dst = (*dst) * (*q); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiQuaternionInterpolate( + C_STRUCT aiQuaternion *dst, + const C_STRUCT aiQuaternion *start, + const C_STRUCT aiQuaternion *end, + const float factor) { + ai_assert(nullptr != dst); + ai_assert(nullptr != start); + ai_assert(nullptr != end); + aiQuaternion::Interpolate(*dst, *start, *end, factor); +} + + +// stb_image is a lightweight image loader. It is shared by: +// - M3D import +// - PBRT export +// Since it's a header-only library, its implementation must be instantiated in some cpp file. +// Don't scatter this task over multiple importers/exporters. Maintain it in a central place (here!). + +#define ASSIMP_HAS_PBRT_EXPORT (!ASSIMP_BUILD_NO_EXPORT && !ASSIMP_BUILD_NO_PBRT_EXPORTER) +#define ASSIMP_HAS_M3D ((!ASSIMP_BUILD_NO_EXPORT && !ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER) + +#if ASSIMP_HAS_PBRT_EXPORT +# define ASSIMP_NEEDS_STB_IMAGE 1 +#elif ASSIMP_HAS_M3D +# define ASSIMP_NEEDS_STB_IMAGE 1 +# define STBI_ONLY_PNG +#endif + +#if ASSIMP_NEEDS_STB_IMAGE + +# if _MSC_VER // "unreferenced function has been removed" (SSE2 detection routine in x64 builds) +# pragma warning(push) +# pragma warning(disable: 4505) +# endif + +# define STB_IMAGE_IMPLEMENTATION +# include "stb/stb_image.h" + +# if _MSC_VER +# pragma warning(pop) +# endif + +#endif diff --git a/libs/assimp/code/Common/Base64.cpp b/libs/assimp/code/Common/Base64.cpp new file mode 100644 index 0000000..3e9c474 --- /dev/null +++ b/libs/assimp/code/Common/Base64.cpp @@ -0,0 +1,179 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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 <assimp/Base64.hpp> +#include <assimp/Exceptional.h> + +namespace Assimp { + +namespace Base64 { + +static const uint8_t tableDecodeBase64[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 64, 0, 0, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, + 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0 +}; + +static const char *tableEncodeBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +static inline char EncodeChar(uint8_t b) { + return tableEncodeBase64[size_t(b)]; +} + +inline uint8_t DecodeChar(char c) { + if (c & 0x80) { + throw DeadlyImportError("Invalid base64 char value: ", size_t(c)); + } + return tableDecodeBase64[size_t(c & 0x7F)]; // TODO faster with lookup table or ifs? +} + +void Encode(const uint8_t *in, size_t inLength, std::string &out) { + size_t outLength = ((inLength + 2) / 3) * 4; + + size_t j = out.size(); + out.resize(j + outLength); + + for (size_t i = 0; i < inLength; i += 3) { + uint8_t b = (in[i] & 0xFC) >> 2; + out[j++] = EncodeChar(b); + + b = (in[i] & 0x03) << 4; + if (i + 1 < inLength) { + b |= (in[i + 1] & 0xF0) >> 4; + out[j++] = EncodeChar(b); + + b = (in[i + 1] & 0x0F) << 2; + if (i + 2 < inLength) { + b |= (in[i + 2] & 0xC0) >> 6; + out[j++] = EncodeChar(b); + + b = in[i + 2] & 0x3F; + out[j++] = EncodeChar(b); + } else { + out[j++] = EncodeChar(b); + out[j++] = '='; + } + } else { + out[j++] = EncodeChar(b); + out[j++] = '='; + out[j++] = '='; + } + } +} + +void Encode(const std::vector<uint8_t> &in, std::string &out) { + Encode(in.data(), in.size(), out); +} + +std::string Encode(const std::vector<uint8_t> &in) { + std::string encoded; + Encode(in, encoded); + return encoded; +} + +size_t Decode(const char *in, size_t inLength, uint8_t *&out) { + if (inLength % 4 != 0) { + throw DeadlyImportError("Invalid base64 encoded data: \"", std::string(in, std::min(size_t(32), inLength)), "\", length:", inLength); + } + + if (inLength < 4) { + out = nullptr; + return 0; + } + + int nEquals = int(in[inLength - 1] == '=') + + int(in[inLength - 2] == '='); + + size_t outLength = (inLength * 3) / 4 - nEquals; + out = new uint8_t[outLength]; + memset(out, 0, outLength); + + size_t i, j = 0; + + for (i = 0; i + 4 < inLength; i += 4) { + uint8_t b0 = DecodeChar(in[i]); + uint8_t b1 = DecodeChar(in[i + 1]); + uint8_t b2 = DecodeChar(in[i + 2]); + uint8_t b3 = DecodeChar(in[i + 3]); + + out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4)); + out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2)); + out[j++] = (uint8_t)((b2 << 6) | b3); + } + + { + uint8_t b0 = DecodeChar(in[i]); + uint8_t b1 = DecodeChar(in[i + 1]); + uint8_t b2 = DecodeChar(in[i + 2]); + uint8_t b3 = DecodeChar(in[i + 3]); + + out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4)); + if (b2 < 64) out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2)); + if (b3 < 64) out[j++] = (uint8_t)((b2 << 6) | b3); + } + + return outLength; +} + +size_t Decode(const std::string &in, std::vector<uint8_t> &out) { + uint8_t *outPtr = nullptr; + size_t decodedSize = Decode(in.data(), in.size(), outPtr); + if (outPtr == nullptr) { + return 0; + } + out.assign(outPtr, outPtr + decodedSize); + delete[] outPtr; + return decodedSize; +} + +std::vector<uint8_t> Decode(const std::string &in) { + std::vector<uint8_t> result; + Decode(in, result); + return result; +} + +} // namespace Base64 +} // namespace Assimp diff --git a/libs/assimp/code/Common/BaseImporter.cpp b/libs/assimp/code/Common/BaseImporter.cpp new file mode 100644 index 0000000..383300e --- /dev/null +++ b/libs/assimp/code/Common/BaseImporter.cpp @@ -0,0 +1,620 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ + +/** @file BaseImporter.cpp + * @brief Implementation of BaseImporter + */ + +#include "FileSystemFilter.h" +#include "Importer.h" +#include <assimp/BaseImporter.h> +#include <assimp/ByteSwapper.h> +#include <assimp/ParsingUtils.h> +#include <assimp/importerdesc.h> +#include <assimp/postprocess.h> +#include <assimp/scene.h> +#include <assimp/Importer.hpp> + +#include <cctype> +#include <ios> +#include <list> +#include <memory> +#include <sstream> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +BaseImporter::BaseImporter() AI_NO_EXCEPT + : m_progress() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +BaseImporter::~BaseImporter() { + // nothing to do here +} + +void BaseImporter::UpdateImporterScale(Importer *pImp) { + ai_assert(pImp != nullptr); + ai_assert(importerScale != 0.0); + ai_assert(fileScale != 0.0); + + double activeScale = importerScale * fileScale; + + // Set active scaling + pImp->SetPropertyFloat(AI_CONFIG_APP_SCALE_KEY, static_cast<float>(activeScale)); + + ASSIMP_LOG_DEBUG("UpdateImporterScale scale set: ", activeScale); +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file and returns the imported data. +aiScene *BaseImporter::ReadFile(Importer *pImp, const std::string &pFile, IOSystem *pIOHandler) { + + m_progress = pImp->GetProgressHandler(); + if (nullptr == m_progress) { + return nullptr; + } + + ai_assert(m_progress); + + // Gather configuration properties for this run + SetupProperties(pImp); + + // Construct a file system filter to improve our success ratio at reading external files + FileSystemFilter filter(pFile, pIOHandler); + + // create a scene object to hold the data + std::unique_ptr<aiScene> sc(new aiScene()); + + // dispatch importing + try { + InternReadFile(pFile, sc.get(), &filter); + + // Calculate import scale hook - required because pImp not available anywhere else + // passes scale into ScaleProcess + UpdateImporterScale(pImp); + + } catch( const std::exception &err ) { + // extract error description + m_ErrorText = err.what(); + ASSIMP_LOG_ERROR(err.what()); + m_Exception = std::current_exception(); + return nullptr; + } + + // return what we gathered from the import. + return sc.release(); +} + +// ------------------------------------------------------------------------------------------------ +void BaseImporter::SetupProperties(const Importer *) { + // the default implementation does nothing +} + +// ------------------------------------------------------------------------------------------------ +void BaseImporter::GetExtensionList(std::set<std::string> &extensions) { + const aiImporterDesc *desc = GetInfo(); + ai_assert(desc != nullptr); + + const char *ext = desc->mFileExtensions; + ai_assert(ext != nullptr); + + const char *last = ext; + do { + if (!*ext || *ext == ' ') { + extensions.insert(std::string(last, ext - last)); + ai_assert(ext - last > 0); + last = ext; + while (*last == ' ') { + ++last; + } + } + } while (*ext++); +} + +// ------------------------------------------------------------------------------------------------ +/*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem *pIOHandler, + const std::string &pFile, + const char **tokens, + std::size_t numTokens, + unsigned int searchBytes /* = 200 */, + bool tokensSol /* false */, + bool noAlphaBeforeTokens /* false */) { + ai_assert(nullptr != tokens); + ai_assert(0 != numTokens); + ai_assert(0 != searchBytes); + + if (nullptr == pIOHandler) { + return false; + } + + std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile)); + if (pStream) { + // read 200 characters from the file + std::unique_ptr<char[]> _buffer(new char[searchBytes + 1 /* for the '\0' */]); + char *buffer(_buffer.get()); + const size_t read(pStream->Read(buffer, 1, searchBytes)); + if (0 == read) { + return false; + } + + for (size_t i = 0; i < read; ++i) { + buffer[i] = static_cast<char>(::tolower((unsigned char)buffer[i])); + } + + // It is not a proper handling of unicode files here ... + // ehm ... but it works in most cases. + char *cur = buffer, *cur2 = buffer, *end = &buffer[read]; + while (cur != end) { + if (*cur) { + *cur2++ = *cur; + } + ++cur; + } + *cur2 = '\0'; + + std::string token; + for (unsigned int i = 0; i < numTokens; ++i) { + ai_assert(nullptr != tokens[i]); + const size_t len(strlen(tokens[i])); + token.clear(); + const char *ptr(tokens[i]); + for (size_t tokIdx = 0; tokIdx < len; ++tokIdx) { + token.push_back(static_cast<char>(tolower(static_cast<unsigned char>(*ptr)))); + ++ptr; + } + const char *r = strstr(buffer, token.c_str()); + if (!r) { + continue; + } + // We need to make sure that we didn't accidentally identify the end of another token as our token, + // e.g. in a previous version the "gltf " present in some gltf files was detected as "f " + if (noAlphaBeforeTokens && (r != buffer && isalpha(static_cast<unsigned char>(r[-1])))) { + continue; + } + // We got a match, either we don't care where it is, or it happens to + // be in the beginning of the file / line + if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') { + ASSIMP_LOG_DEBUG("Found positive match for header keyword: ", tokens[i]); + return true; + } + } + } + + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Simple check for file extension +/*static*/ bool BaseImporter::SimpleExtensionCheck(const std::string &pFile, + const char *ext0, + const char *ext1, + const char *ext2) { + std::string::size_type pos = pFile.find_last_of('.'); + + // no file extension - can't read + if (pos == std::string::npos) + return false; + + const char *ext_real = &pFile[pos + 1]; + if (!ASSIMP_stricmp(ext_real, ext0)) + return true; + + // check for other, optional, file extensions + if (ext1 && !ASSIMP_stricmp(ext_real, ext1)) + return true; + + if (ext2 && !ASSIMP_stricmp(ext_real, ext2)) + return true; + + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Get file extension from path +std::string BaseImporter::GetExtension(const std::string &file) { + std::string::size_type pos = file.find_last_of('.'); + + // no file extension at all + if (pos == std::string::npos) { + return std::string(); + } + + // thanks to Andy Maloney for the hint + std::string ret = file.substr(pos + 1); + ret = ai_tolower(ret); + + return ret; +} + + +// ------------------------------------------------------------------------------------------------ +// Check for magic bytes at the beginning of the file. +/* static */ bool BaseImporter::CheckMagicToken(IOSystem *pIOHandler, const std::string &pFile, + const void *_magic, std::size_t num, unsigned int offset, unsigned int size) { + ai_assert(size <= 16); + ai_assert(_magic); + + if (!pIOHandler) { + return false; + } + union { + const char *magic; + const uint16_t *magic_u16; + const uint32_t *magic_u32; + }; + magic = reinterpret_cast<const char *>(_magic); + std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile)); + if (pStream) { + + // skip to offset + pStream->Seek(offset, aiOrigin_SET); + + // read 'size' characters from the file + union { + char data[16]; + uint16_t data_u16[8]; + uint32_t data_u32[4]; + }; + if (size != pStream->Read(data, 1, size)) { + return false; + } + + for (unsigned int i = 0; i < num; ++i) { + // also check against big endian versions of tokens with size 2,4 + // that's just for convenience, the chance that we cause conflicts + // is quite low and it can save some lines and prevent nasty bugs + if (2 == size) { + uint16_t rev = *magic_u16; + ByteSwap::Swap(&rev); + if (data_u16[0] == *magic_u16 || data_u16[0] == rev) { + return true; + } + } else if (4 == size) { + uint32_t rev = *magic_u32; + ByteSwap::Swap(&rev); + if (data_u32[0] == *magic_u32 || data_u32[0] == rev) { + return true; + } + } else { + // any length ... just compare + if (!memcmp(magic, data, size)) { + return true; + } + } + magic += size; + } + } + return false; +} + +#ifdef ASSIMP_USE_HUNTER +#include <utf8.h> +#else +#include "../contrib/utf8cpp/source/utf8.h" +#endif + +// ------------------------------------------------------------------------------------------------ +// Convert to UTF8 data +void BaseImporter::ConvertToUTF8(std::vector<char> &data) { + //ConversionResult result; + if (data.size() < 8) { + throw DeadlyImportError("File is too small"); + } + + // UTF 8 with BOM + if ((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) { + ASSIMP_LOG_DEBUG("Found UTF-8 BOM ..."); + + std::copy(data.begin() + 3, data.end(), data.begin()); + data.resize(data.size() - 3); + return; + } + + // UTF 32 BE with BOM + if (*((uint32_t *)&data.front()) == 0xFFFE0000) { + + // swap the endianness .. + for (uint32_t *p = (uint32_t *)&data.front(), *end = (uint32_t *)&data.back(); p <= end; ++p) { + AI_SWAP4P(p); + } + } + + // UTF 32 LE with BOM + if (*((uint32_t *)&data.front()) == 0x0000FFFE) { + ASSIMP_LOG_DEBUG("Found UTF-32 BOM ..."); + + std::vector<char> output; + int *ptr = (int *)&data[0]; + int *end = ptr + (data.size() / sizeof(int)) + 1; + utf8::utf32to8(ptr, end, back_inserter(output)); + return; + } + + // UTF 16 BE with BOM + if (*((uint16_t *)&data.front()) == 0xFFFE) { + // Check to ensure no overflow can happen + if(data.size() % 2 != 0) { + return; + } + // swap the endianness .. + for (uint16_t *p = (uint16_t *)&data.front(), *end = (uint16_t *)&data.back(); p <= end; ++p) { + ByteSwap::Swap2(p); + } + } + + // UTF 16 LE with BOM + if (*((uint16_t *)&data.front()) == 0xFEFF) { + ASSIMP_LOG_DEBUG("Found UTF-16 BOM ..."); + + std::vector<unsigned char> output; + utf8::utf16to8(data.begin(), data.end(), back_inserter(output)); + return; + } +} + +// ------------------------------------------------------------------------------------------------ +// Convert to UTF8 data to ISO-8859-1 +void BaseImporter::ConvertUTF8toISO8859_1(std::string &data) { + size_t size = data.size(); + size_t i = 0, j = 0; + + while (i < size) { + if ((unsigned char)data[i] < (size_t)0x80) { + data[j] = data[i]; + } else if (i < size - 1) { + if ((unsigned char)data[i] == 0xC2) { + data[j] = data[++i]; + } else if ((unsigned char)data[i] == 0xC3) { + data[j] = ((unsigned char)data[++i] + 0x40); + } else { + std::stringstream stream; + stream << "UTF8 code " << std::hex << data[i] << data[i + 1] << " can not be converted into ISA-8859-1."; + ASSIMP_LOG_ERROR(stream.str()); + + data[j++] = data[i++]; + data[j] = data[i]; + } + } else { + ASSIMP_LOG_ERROR("UTF8 code but only one character remaining"); + + data[j] = data[i]; + } + + i++; + j++; + } + + data.resize(j); +} + +// ------------------------------------------------------------------------------------------------ +void BaseImporter::TextFileToBuffer(IOStream *stream, + std::vector<char> &data, + TextFileMode mode) { + ai_assert(nullptr != stream); + + const size_t fileSize = stream->FileSize(); + if (mode == FORBID_EMPTY) { + if (!fileSize) { + throw DeadlyImportError("File is empty"); + } + } + + data.reserve(fileSize + 1); + data.resize(fileSize); + if (fileSize > 0) { + if (fileSize != stream->Read(&data[0], 1, fileSize)) { + throw DeadlyImportError("File read error"); + } + + ConvertToUTF8(data); + } + + // append a binary zero to simplify string parsing + data.push_back(0); +} + +// ------------------------------------------------------------------------------------------------ +namespace Assimp { +// Represents an import request +struct LoadRequest { + LoadRequest(const std::string &_file, unsigned int _flags, const BatchLoader::PropertyMap *_map, unsigned int _id) : + file(_file), + flags(_flags), + refCnt(1), + scene(nullptr), + loaded(false), + id(_id) { + if (_map) { + map = *_map; + } + } + + bool operator==(const std::string &f) const { + return file == f; + } + + const std::string file; + unsigned int flags; + unsigned int refCnt; + aiScene *scene; + bool loaded; + BatchLoader::PropertyMap map; + unsigned int id; +}; +} // namespace Assimp + +// ------------------------------------------------------------------------------------------------ +// BatchLoader::pimpl data structure +struct Assimp::BatchData { + BatchData(IOSystem *pIO, bool validate) : + pIOSystem(pIO), pImporter(nullptr), next_id(0xffff), validate(validate) { + ai_assert(nullptr != pIO); + + pImporter = new Importer(); + pImporter->SetIOHandler(pIO); + } + + ~BatchData() { + pImporter->SetIOHandler(nullptr); /* get pointer back into our possession */ + delete pImporter; + } + + // IO system to be used for all imports + IOSystem *pIOSystem; + + // Importer used to load all meshes + Importer *pImporter; + + // List of all imports + std::list<LoadRequest> requests; + + // Base path + std::string pathBase; + + // Id for next item + unsigned int next_id; + + // Validation enabled state + bool validate; +}; + +typedef std::list<LoadRequest>::iterator LoadReqIt; + +// ------------------------------------------------------------------------------------------------ +BatchLoader::BatchLoader(IOSystem *pIO, bool validate) { + ai_assert(nullptr != pIO); + + m_data = new BatchData(pIO, validate); +} + +// ------------------------------------------------------------------------------------------------ +BatchLoader::~BatchLoader() { + // delete all scenes what have not been polled by the user + for (LoadReqIt it = m_data->requests.begin(); it != m_data->requests.end(); ++it) { + delete (*it).scene; + } + delete m_data; +} + +// ------------------------------------------------------------------------------------------------ +void BatchLoader::setValidation(bool enabled) { + m_data->validate = enabled; +} + +// ------------------------------------------------------------------------------------------------ +bool BatchLoader::getValidation() const { + return m_data->validate; +} + +// ------------------------------------------------------------------------------------------------ +unsigned int BatchLoader::AddLoadRequest(const std::string &file, + unsigned int steps /*= 0*/, const PropertyMap *map /*= nullptr*/) { + ai_assert(!file.empty()); + + // check whether we have this loading request already + for (LoadReqIt it = m_data->requests.begin(); it != m_data->requests.end(); ++it) { + // Call IOSystem's path comparison function here + if (m_data->pIOSystem->ComparePaths((*it).file, file)) { + if (map) { + if (!((*it).map == *map)) { + continue; + } + } else if (!(*it).map.empty()) { + continue; + } + + (*it).refCnt++; + return (*it).id; + } + } + + // no, we don't have it. So add it to the queue ... + m_data->requests.emplace_back(file, steps, map, m_data->next_id); + return m_data->next_id++; +} + +// ------------------------------------------------------------------------------------------------ +aiScene *BatchLoader::GetImport(unsigned int which) { + for (LoadReqIt it = m_data->requests.begin(); it != m_data->requests.end(); ++it) { + if ((*it).id == which && (*it).loaded) { + aiScene *sc = (*it).scene; + if (!(--(*it).refCnt)) { + m_data->requests.erase(it); + } + return sc; + } + } + return nullptr; +} + +// ------------------------------------------------------------------------------------------------ +void BatchLoader::LoadAll() { + // no threaded implementation for the moment + for (LoadReqIt it = m_data->requests.begin(); it != m_data->requests.end(); ++it) { + // force validation in debug builds + unsigned int pp = (*it).flags; + if (m_data->validate) { + pp |= aiProcess_ValidateDataStructure; + } + + // setup config properties if necessary + ImporterPimpl *pimpl = m_data->pImporter->Pimpl(); + pimpl->mFloatProperties = (*it).map.floats; + pimpl->mIntProperties = (*it).map.ints; + pimpl->mStringProperties = (*it).map.strings; + pimpl->mMatrixProperties = (*it).map.matrices; + + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_INFO("%%% BEGIN EXTERNAL FILE %%%"); + ASSIMP_LOG_INFO("File: ", (*it).file); + } + m_data->pImporter->ReadFile((*it).file, pp); + (*it).scene = m_data->pImporter->GetOrphanedScene(); + (*it).loaded = true; + + ASSIMP_LOG_INFO("%%% END EXTERNAL FILE %%%"); + } +} diff --git a/libs/assimp/code/Common/BaseProcess.cpp b/libs/assimp/code/Common/BaseProcess.cpp new file mode 100644 index 0000000..48895b9 --- /dev/null +++ b/libs/assimp/code/Common/BaseProcess.cpp @@ -0,0 +1,100 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of BaseProcess */ + +#include "BaseProcess.h" +#include "Importer.h" +#include <assimp/BaseImporter.h> +#include <assimp/scene.h> +#include <assimp/DefaultLogger.hpp> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +BaseProcess::BaseProcess() AI_NO_EXCEPT + : shared(), + progress() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +BaseProcess::~BaseProcess() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +void BaseProcess::ExecuteOnScene(Importer *pImp) { + ai_assert( nullptr != pImp ); + ai_assert( nullptr != pImp->Pimpl()->mScene); + + progress = pImp->GetProgressHandler(); + ai_assert(nullptr != progress); + + SetupProperties(pImp); + + // catch exceptions thrown inside the PostProcess-Step + try { + Execute(pImp->Pimpl()->mScene); + + } catch (const std::exception &err) { + + // extract error description + pImp->Pimpl()->mErrorString = err.what(); + ASSIMP_LOG_ERROR(pImp->Pimpl()->mErrorString); + + // and kill the partially imported data + delete pImp->Pimpl()->mScene; + pImp->Pimpl()->mScene = nullptr; + } +} + +// ------------------------------------------------------------------------------------------------ +void BaseProcess::SetupProperties(const Importer * /*pImp*/) { + // the default implementation does nothing +} + +// ------------------------------------------------------------------------------------------------ +bool BaseProcess::RequireVerboseFormat() const { + return true; +} diff --git a/libs/assimp/code/Common/BaseProcess.h b/libs/assimp/code/Common/BaseProcess.h new file mode 100644 index 0000000..6440356 --- /dev/null +++ b/libs/assimp/code/Common/BaseProcess.h @@ -0,0 +1,251 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +/** @file Base class of all import post processing steps */ +#ifndef INCLUDED_AI_BASEPROCESS_H +#define INCLUDED_AI_BASEPROCESS_H + +#include <assimp/GenericProperty.h> + +#include <map> + +struct aiScene; + +namespace Assimp { + +class Importer; + +// --------------------------------------------------------------------------- +/** Helper class to allow post-processing steps to interact with each other. + * + * The class maintains a simple property list that can be used by pp-steps + * to provide additional information to other steps. This is primarily + * intended for cross-step optimizations. + */ +class SharedPostProcessInfo { +public: + struct Base { + virtual ~Base() {} + }; + + //! Represents data that is allocated on the heap, thus needs to be deleted + template <typename T> + struct THeapData : public Base { + explicit THeapData(T *in) : + data(in) {} + + ~THeapData() { + delete data; + } + T *data; + }; + + //! Represents static, by-value data not allocated on the heap + template <typename T> + struct TStaticData : public Base { + explicit TStaticData(T in) : + data(in) {} + + ~TStaticData() {} + + T data; + }; + + // some typedefs for cleaner code + typedef unsigned int KeyType; + typedef std::map<KeyType, Base *> PropertyMap; + +public: + //! Destructor + ~SharedPostProcessInfo() { + Clean(); + } + + //! Remove all stored properties from the table + void Clean() { + // invoke the virtual destructor for all stored properties + for (PropertyMap::iterator it = pmap.begin(), end = pmap.end(); + it != end; ++it) { + delete (*it).second; + } + pmap.clear(); + } + + //! Add a heap property to the list + template <typename T> + void AddProperty(const char *name, T *in) { + AddProperty(name, (Base *)new THeapData<T>(in)); + } + + //! Add a static by-value property to the list + template <typename T> + void AddProperty(const char *name, T in) { + AddProperty(name, (Base *)new TStaticData<T>(in)); + } + + //! Get a heap property + template <typename T> + bool GetProperty(const char *name, T *&out) const { + THeapData<T> *t = (THeapData<T> *)GetPropertyInternal(name); + if (!t) { + out = nullptr; + return false; + } + out = t->data; + return true; + } + + //! Get a static, by-value property + template <typename T> + bool GetProperty(const char *name, T &out) const { + TStaticData<T> *t = (TStaticData<T> *)GetPropertyInternal(name); + if ( nullptr == t) { + return false; + } + out = t->data; + return true; + } + + //! Remove a property of a specific type + void RemoveProperty(const char *name) { + SetGenericPropertyPtr<Base>(pmap, name, nullptr ); + } + +private: + void AddProperty(const char *name, Base *data) { + SetGenericPropertyPtr<Base>(pmap, name, data); + } + + Base *GetPropertyInternal(const char *name) const { + return GetGenericProperty<Base *>(pmap, name, nullptr ); + } + +private: + //! Map of all stored properties + PropertyMap pmap; +}; + +#define AI_SPP_SPATIAL_SORT "$Spat" + +// --------------------------------------------------------------------------- +/** The BaseProcess defines a common interface for all post processing steps. + * A post processing step is run after a successful import if the caller + * specified the corresponding flag when calling ReadFile(). + * Enum #aiPostProcessSteps defines which flags are available. + * After a successful import the Importer iterates over its internal array + * of processes and calls IsActive() on each process to evaluate if the step + * should be executed. If the function returns true, the class' Execute() + * function is called subsequently. + */ +class ASSIMP_API_WINONLY BaseProcess { + friend class Importer; + +public: + /** Constructor to be privately used by Importer */ + BaseProcess() AI_NO_EXCEPT; + + /** Destructor, private as well */ + virtual ~BaseProcess(); + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. A + * bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + virtual bool IsActive(unsigned int pFlags) const = 0; + + // ------------------------------------------------------------------- + /** Check whether this step expects its input vertex data to be + * in verbose format. */ + virtual bool RequireVerboseFormat() const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * The function deletes the scene if the postprocess step fails ( + * the object pointer will be set to nullptr). + * @param pImp Importer instance (pImp->mScene must be valid) + */ + void ExecuteOnScene(Importer *pImp); + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + virtual void SetupProperties(const Importer *pImp); + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * A process should throw an ImportErrorException* if it fails. + * This method must be implemented by deriving classes. + * @param pScene The imported data to work at. + */ + virtual void Execute(aiScene *pScene) = 0; + + // ------------------------------------------------------------------- + /** Assign a new SharedPostProcessInfo to the step. This object + * allows multiple postprocess steps to share data. + * @param sh May be nullptr + */ + inline void SetSharedData(SharedPostProcessInfo *sh) { + shared = sh; + } + + // ------------------------------------------------------------------- + /** Get the shared data that is assigned to the step. + */ + inline SharedPostProcessInfo *GetSharedData() { + return shared; + } + +protected: + /** See the doc of #SharedPostProcessInfo for more details */ + SharedPostProcessInfo *shared; + + /** Currently active progress handler */ + ProgressHandler *progress; +}; + +} // end of namespace Assimp + +#endif // AI_BASEPROCESS_H_INC diff --git a/libs/assimp/code/Common/Bitmap.cpp b/libs/assimp/code/Common/Bitmap.cpp new file mode 100644 index 0000000..65dfd17 --- /dev/null +++ b/libs/assimp/code/Common/Bitmap.cpp @@ -0,0 +1,156 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ + +/** @file Bitmap.cpp + * @brief Defines bitmap format helper for textures + * + * Used for file formats which embed their textures into the model file. + */ + +#include <assimp/Bitmap.h> +#include <assimp/ByteSwapper.h> +#include <assimp/texture.h> +#include <assimp/IOStream.hpp> + +namespace Assimp { + +bool Bitmap::Save(aiTexture *texture, IOStream *file) { + if (file == nullptr) { + return false; + } + + Header header; + DIB dib; + dib.size = DIB::dib_size; + dib.width = texture->mWidth; + dib.height = texture->mHeight; + dib.planes = 1; + dib.bits_per_pixel = 8 * mBytesPerPixel; + dib.compression = 0; + dib.image_size = (((dib.width * mBytesPerPixel) + 3) & 0x0000FFFC) * dib.height; + dib.x_resolution = 0; + dib.y_resolution = 0; + dib.nb_colors = 0; + dib.nb_important_colors = 0; + + header.type = 0x4D42; // 'BM' + header.offset = Header::header_size + DIB::dib_size; + header.size = header.offset + dib.image_size; + header.reserved1 = 0; + header.reserved2 = 0; + + WriteHeader(header, file); + WriteDIB(dib, file); + WriteData(texture, file); + + return true; +} + +template <typename T> +inline std::size_t Copy(uint8_t *data, const T &field) { +#ifdef AI_BUILD_BIG_ENDIAN + T field_swapped = AI_BE(field); + std::memcpy(data, &field_swapped, sizeof(field)); + return sizeof(field); +#else + std::memcpy(data, &AI_BE(field), sizeof(field)); + return sizeof(field); +#endif +} + +void Bitmap::WriteHeader(Header &header, IOStream *file) { + uint8_t data[Header::header_size]; + + std::size_t offset = 0; + + offset += Copy(&data[offset], header.type); + offset += Copy(&data[offset], header.size); + offset += Copy(&data[offset], header.reserved1); + offset += Copy(&data[offset], header.reserved2); + Copy(&data[offset], header.offset); + + file->Write(data, Header::header_size, 1); +} + +void Bitmap::WriteDIB(DIB &dib, IOStream *file) { + uint8_t data[DIB::dib_size]; + + std::size_t offset = 0; + + offset += Copy(&data[offset], dib.size); + offset += Copy(&data[offset], dib.width); + offset += Copy(&data[offset], dib.height); + offset += Copy(&data[offset], dib.planes); + offset += Copy(&data[offset], dib.bits_per_pixel); + offset += Copy(&data[offset], dib.compression); + offset += Copy(&data[offset], dib.image_size); + offset += Copy(&data[offset], dib.x_resolution); + offset += Copy(&data[offset], dib.y_resolution); + offset += Copy(&data[offset], dib.nb_colors); + Copy(&data[offset], dib.nb_important_colors); + + file->Write(data, DIB::dib_size, 1); +} + +void Bitmap::WriteData(aiTexture *texture, IOStream *file) { + static const std::size_t padding_offset = 4; + static const uint8_t padding_data[padding_offset] = { 0x0, 0x0, 0x0, 0x0 }; + + unsigned int padding = (padding_offset - ((mBytesPerPixel * texture->mWidth) % padding_offset)) % padding_offset; + uint8_t pixel[mBytesPerPixel]; + + for (std::size_t i = 0; i < texture->mHeight; ++i) { + for (std::size_t j = 0; j < texture->mWidth; ++j) { + const aiTexel &texel = texture->pcData[(texture->mHeight - i - 1) * texture->mWidth + j]; // Bitmap files are stored in bottom-up format + + pixel[0] = texel.r; + pixel[1] = texel.g; + pixel[2] = texel.b; + pixel[3] = texel.a; + + file->Write(pixel, mBytesPerPixel, 1); + } + + file->Write(padding_data, padding, 1); + } +} + +} // namespace Assimp diff --git a/libs/assimp/code/Common/Compression.cpp b/libs/assimp/code/Common/Compression.cpp new file mode 100644 index 0000000..3bf306d --- /dev/null +++ b/libs/assimp/code/Common/Compression.cpp @@ -0,0 +1,214 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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 "Compression.h" +#include <assimp/ai_assert.h> +#include <assimp/Exceptional.h> + +namespace Assimp { + +struct Compression::impl { + bool mOpen; + z_stream mZSstream; + FlushMode mFlushMode; + + impl() : + mOpen(false), + mZSstream(), + mFlushMode(Compression::FlushMode::NoFlush) { + // empty + } +}; + +Compression::Compression() : + mImpl(new impl) { + // empty +} + +Compression::~Compression() { + ai_assert(mImpl != nullptr); + + delete mImpl; +} + +bool Compression::open(Format format, FlushMode flush, int windowBits) { + ai_assert(mImpl != nullptr); + + if (mImpl->mOpen) { + return false; + } + + // build a zlib stream + mImpl->mZSstream.opaque = Z_NULL; + mImpl->mZSstream.zalloc = Z_NULL; + mImpl->mZSstream.zfree = Z_NULL; + mImpl->mFlushMode = flush; + if (format == Format::Binary) { + mImpl->mZSstream.data_type = Z_BINARY; + } else { + mImpl->mZSstream.data_type = Z_ASCII; + } + + // raw decompression without a zlib or gzip header + if (windowBits == 0) { + inflateInit(&mImpl->mZSstream); + } else { + inflateInit2(&mImpl->mZSstream, windowBits); + } + mImpl->mOpen = true; + + return mImpl->mOpen; +} + +static int getFlushMode(Compression::FlushMode flush) { + int z_flush = 0; + switch (flush) { + case Compression::FlushMode::NoFlush: + z_flush = Z_NO_FLUSH; + break; + case Compression::FlushMode::Block: + z_flush = Z_BLOCK; + break; + case Compression::FlushMode::Tree: + z_flush = Z_TREES; + break; + case Compression::FlushMode::SyncFlush: + z_flush = Z_SYNC_FLUSH; + break; + case Compression::FlushMode::Finish: + z_flush = Z_FINISH; + break; + default: + ai_assert(false); + break; + } + + return z_flush; +} + +constexpr size_t MYBLOCK = 32786; + +size_t Compression::decompress(const void *data, size_t in, std::vector<char> &uncompressed) { + ai_assert(mImpl != nullptr); + if (data == nullptr || in == 0) { + return 0l; + } + + mImpl->mZSstream.next_in = (Bytef*)(data); + mImpl->mZSstream.avail_in = (uInt)in; + + int ret = 0; + size_t total = 0l; + const int flushMode = getFlushMode(mImpl->mFlushMode); + if (flushMode == Z_FINISH) { + mImpl->mZSstream.avail_out = static_cast<uInt>(uncompressed.size()); + mImpl->mZSstream.next_out = reinterpret_cast<Bytef *>(&*uncompressed.begin()); + ret = inflate(&mImpl->mZSstream, Z_FINISH); + + if (ret != Z_STREAM_END && ret != Z_OK) { + throw DeadlyImportError("Compression", "Failure decompressing this file using gzip."); + } + total = mImpl->mZSstream.avail_out; + } else { + do { + Bytef block[MYBLOCK] = {}; + mImpl->mZSstream.avail_out = MYBLOCK; + mImpl->mZSstream.next_out = block; + + ret = inflate(&mImpl->mZSstream, flushMode); + + if (ret != Z_STREAM_END && ret != Z_OK) { + throw DeadlyImportError("Compression", "Failure decompressing this file using gzip."); + } + const size_t have = MYBLOCK - mImpl->mZSstream.avail_out; + total += have; + uncompressed.resize(total); + ::memcpy(uncompressed.data() + total - have, block, have); + } while (ret != Z_STREAM_END); + } + + return total; +} + +size_t Compression::decompressBlock(const void *data, size_t in, char *out, size_t availableOut) { + ai_assert(mImpl != nullptr); + if (data == nullptr || in == 0 || out == nullptr || availableOut == 0) { + return 0l; + } + + // push data to the stream + mImpl->mZSstream.next_in = (Bytef *)data; + mImpl->mZSstream.avail_in = (uInt)in; + mImpl->mZSstream.next_out = (Bytef *)out; + mImpl->mZSstream.avail_out = (uInt)availableOut; + + // and decompress the data .... + int ret = ::inflate(&mImpl->mZSstream, Z_SYNC_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) { + throw DeadlyImportError("X: Failed to decompress MSZIP-compressed data"); + } + + ::inflateReset(&mImpl->mZSstream); + ::inflateSetDictionary(&mImpl->mZSstream, (const Bytef *)out, (uInt)availableOut - mImpl->mZSstream.avail_out); + + return availableOut - (size_t)mImpl->mZSstream.avail_out; +} + +bool Compression::isOpen() const { + ai_assert(mImpl != nullptr); + + return mImpl->mOpen; +} + +bool Compression::close() { + ai_assert(mImpl != nullptr); + + if (!mImpl->mOpen) { + return false; + } + + inflateEnd(&mImpl->mZSstream); + mImpl->mOpen = false; + + return true; +} + +} // namespace Assimp diff --git a/libs/assimp/code/Common/Compression.h b/libs/assimp/code/Common/Compression.h new file mode 100644 index 0000000..edf1d23 --- /dev/null +++ b/libs/assimp/code/Common/Compression.h @@ -0,0 +1,121 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +#pragma once + +#ifdef ASSIMP_BUILD_NO_OWN_ZLIB +#include <zlib.h> +#else +#include "../contrib/zlib/zlib.h" +#endif + +#include <vector> +#include <cstddef> // size_t + +namespace Assimp { + +/// @brief This class provides the decompression of zlib-compressed data. +class Compression { +public: + static const int MaxWBits = MAX_WBITS; + + /// @brief Describes the format data type + enum class Format { + InvalidFormat = -1, ///< Invalid enum type. + Binary = 0, ///< Binary format. + ASCII, ///< ASCII format. + + NumFormats ///< The number of supported formats. + }; + + /// @brief The supported flush mode, used for blocked access. + enum class FlushMode { + InvalidFormat = -1, ///< Invalid enum type. + NoFlush = 0, ///< No flush, will be done on inflate end. + Block, ///< Assists in combination of compress. + Tree, ///< Assists in combination of compress and returns if stream is finish. + SyncFlush, ///< Synced flush mode. + Finish, ///< Finish mode, all in once, no block access. + + NumModes ///< The number of supported modes. + }; + + /// @brief The class constructor. + Compression(); + + /// @brief The class destructor. + ~Compression(); + + /// @brief Will open the access to the compression. + /// @param[in] format The format type + /// @param[in] flush The flush mode. + /// @param[in] windowBits The windows history working size, shall be between 8 and 15. + /// @return true if close was successful, false if not. + bool open(Format format, FlushMode flush, int windowBits); + + /// @brief Will return the open state. + /// @return true if the access is opened, false if not. + bool isOpen() const; + + /// @brief Will close the decompress access. + /// @return true if close was successful, false if not. + bool close(); + + /// @brief Will decompress the data buffer in one step. + /// @param[in] data The data to decompress + /// @param[in] in The size of the data. + /// @param[out uncompressed A std::vector containing the decompressed data. + size_t decompress(const void *data, size_t in, std::vector<char> &uncompressed); + + /// @brief Will decompress the data buffer block-wise. + /// @param[in] data The compressed data + /// @param[in] in The size of the data buffer + /// @param[out] out The output buffer + /// @param[out] availableOut The upper limit of the output buffer. + /// @return The size of the decompressed data buffer. + size_t decompressBlock(const void *data, size_t in, char *out, size_t availableOut); + +private: + struct impl; + impl *mImpl; +}; + +} // namespace Assimp diff --git a/libs/assimp/code/Common/CreateAnimMesh.cpp b/libs/assimp/code/Common/CreateAnimMesh.cpp new file mode 100644 index 0000000..58f3d90 --- /dev/null +++ b/libs/assimp/code/Common/CreateAnimMesh.cpp @@ -0,0 +1,92 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (C) 2016 The Qt Company Ltd. +Copyright (c) 2006-2022, 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 <assimp/CreateAnimMesh.h> + +namespace Assimp { + +aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh, bool needPositions, bool needNormals, bool needTangents, bool needColors, bool needTexCoords) +{ + aiAnimMesh *animesh = new aiAnimMesh; + animesh->mNumVertices = mesh->mNumVertices; + if (needPositions && mesh->mVertices) { + animesh->mVertices = new aiVector3D[animesh->mNumVertices]; + std::memcpy(animesh->mVertices, mesh->mVertices, mesh->mNumVertices * sizeof(aiVector3D)); + } + if (needNormals && mesh->mNormals) { + animesh->mNormals = new aiVector3D[animesh->mNumVertices]; + std::memcpy(animesh->mNormals, mesh->mNormals, mesh->mNumVertices * sizeof(aiVector3D)); + } + if (needTangents && mesh->mTangents) { + animesh->mTangents = new aiVector3D[animesh->mNumVertices]; + std::memcpy(animesh->mTangents, mesh->mTangents, mesh->mNumVertices * sizeof(aiVector3D)); + } + if (needTangents && mesh->mBitangents) { + animesh->mBitangents = new aiVector3D[animesh->mNumVertices]; + std::memcpy(animesh->mBitangents, mesh->mBitangents, mesh->mNumVertices * sizeof(aiVector3D)); + } + + if (needColors) { + for (int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) { + if (mesh->mColors[i]) { + animesh->mColors[i] = new aiColor4D[animesh->mNumVertices]; + std::memcpy(animesh->mColors[i], mesh->mColors[i], mesh->mNumVertices * sizeof(aiColor4D)); + } else { + animesh->mColors[i] = nullptr; + } + } + } + + if (needTexCoords) { + for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (mesh->mTextureCoords[i]) { + animesh->mTextureCoords[i] = new aiVector3D[animesh->mNumVertices]; + std::memcpy(animesh->mTextureCoords[i], mesh->mTextureCoords[i], mesh->mNumVertices * sizeof(aiVector3D)); + } else { + animesh->mTextureCoords[i] = nullptr; + } + } + } + return animesh; +} + +} // end of namespace Assimp diff --git a/libs/assimp/code/Common/DefaultIOStream.cpp b/libs/assimp/code/Common/DefaultIOStream.cpp new file mode 100644 index 0000000..e30f26a --- /dev/null +++ b/libs/assimp/code/Common/DefaultIOStream.cpp @@ -0,0 +1,180 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ +/** @file DefaultIOStream.cpp + * @brief Default File I/O implementation for #Importer + */ + +#include <assimp/DefaultIOStream.h> +#include <assimp/ai_assert.h> +#include <sys/stat.h> +#include <sys/types.h> + +using namespace Assimp; + +namespace { + +template <size_t sizeOfPointer> +inline size_t select_ftell(FILE *file) { + return ::ftell(file); +} + +template <size_t sizeOfPointer> +inline int select_fseek(FILE *file, int64_t offset, int origin) { + return ::fseek(file, static_cast<long>(offset), origin); +} + + + +#if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) +template <> +inline size_t select_ftell<8>(FILE *file) { + return (size_t)::_ftelli64(file); +} + +template <> +inline int select_fseek<8>(FILE *file, int64_t offset, int origin) { + return ::_fseeki64(file, offset, origin); +} + +#endif // #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) + +} // namespace + +// ---------------------------------------------------------------------------------- +DefaultIOStream::~DefaultIOStream() { + if (mFile) { + ::fclose(mFile); + } +} + +// ---------------------------------------------------------------------------------- +size_t DefaultIOStream::Read(void *pvBuffer, + size_t pSize, + size_t pCount) { + if (0 == pCount) { + return 0; + } + ai_assert(nullptr != pvBuffer); + ai_assert(0 != pSize); + + return (mFile ? ::fread(pvBuffer, pSize, pCount, mFile) : 0); +} + +// ---------------------------------------------------------------------------------- +size_t DefaultIOStream::Write(const void *pvBuffer, + size_t pSize, + size_t pCount) { + ai_assert(nullptr != pvBuffer); + ai_assert(0 != pSize); + + return (mFile ? ::fwrite(pvBuffer, pSize, pCount, mFile) : 0); +} + +// ---------------------------------------------------------------------------------- +aiReturn DefaultIOStream::Seek(size_t pOffset, + aiOrigin pOrigin) { + if (!mFile) { + return AI_FAILURE; + } + + // Just to check whether our enum maps one to one with the CRT constants + static_assert(aiOrigin_CUR == SEEK_CUR && + aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET, + "aiOrigin_CUR == SEEK_CUR && \ + aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET"); + + // do the seek + return (0 == select_fseek<sizeof(void *)>(mFile, (int64_t)pOffset, (int)pOrigin) ? AI_SUCCESS : AI_FAILURE); +} + +// ---------------------------------------------------------------------------------- +size_t DefaultIOStream::Tell() const { + if (!mFile) { + return 0; + } + return select_ftell<sizeof(void *)>(mFile); +} + +// ---------------------------------------------------------------------------------- +size_t DefaultIOStream::FileSize() const { + if (!mFile || mFilename.empty()) { + return 0; + } + + if (SIZE_MAX == mCachedSize) { + + // Although fseek/ftell would allow us to reuse the existing file handle here, + // it is generally unsafe because: + // - For binary streams, it is not technically well-defined + // - For text files the results are meaningless + // That's why we use the safer variant fstat here. + // + // See here for details: + // https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file +#if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) + struct __stat64 fileStat; + //using fileno + fstat avoids having to handle the filename + int err = _fstat64(_fileno(mFile), &fileStat); + if (0 != err) + return 0; + mCachedSize = (size_t)(fileStat.st_size); +#elif defined __GNUC__ || defined __APPLE__ || defined __MACH__ || defined __FreeBSD__ + struct stat fileStat; + int err = stat(mFilename.c_str(), &fileStat); + if (0 != err) + return 0; + const unsigned long long cachedSize = fileStat.st_size; + mCachedSize = static_cast<size_t>(cachedSize); +#else +#error "Unknown platform" +#endif + } + return mCachedSize; +} + +// ---------------------------------------------------------------------------------- +void DefaultIOStream::Flush() { + if (mFile) { + ::fflush(mFile); + } +} + +// ---------------------------------------------------------------------------------- diff --git a/libs/assimp/code/Common/DefaultIOSystem.cpp b/libs/assimp/code/Common/DefaultIOSystem.cpp new file mode 100644 index 0000000..b28910c --- /dev/null +++ b/libs/assimp/code/Common/DefaultIOSystem.cpp @@ -0,0 +1,223 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ +/** @file Default implementation of IOSystem using the standard C file functions */ + +#include <assimp/StringComparison.h> + +#include <assimp/DefaultIOStream.h> +#include <assimp/DefaultIOSystem.h> +#include <assimp/ai_assert.h> +#include <stdlib.h> +#include <assimp/DefaultLogger.hpp> + +#ifdef __unix__ +# include <stdlib.h> +# include <sys/param.h> +#endif + +#ifdef _WIN32 +# include <windows.h> +#endif + +using namespace Assimp; + +#ifdef _WIN32 + +const std::wstring wdummy; + +static std::wstring Utf8ToWide(const char *in) { + if (nullptr == in) { + return wdummy; + } + int size = MultiByteToWideChar(CP_UTF8, 0, in, -1, nullptr, 0); + // size includes terminating null; std::wstring adds null automatically + std::wstring out(static_cast<size_t>(size) - 1, L'\0'); + MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], size); + + return out; +} + +const std::string dummy; + +static std::string WideToUtf8(const wchar_t *in) { + if (nullptr == in) { + return dummy; + } + int size = WideCharToMultiByte(CP_UTF8, 0, in, -1, nullptr, 0, nullptr, nullptr); + // size includes terminating null; std::string adds null automatically + std::string out(static_cast<size_t>(size) - 1, '\0'); + WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], size, nullptr, nullptr); + + return out; +} +#endif + +// ------------------------------------------------------------------------------------------------ +// Tests for the existence of a file at the given path. +bool DefaultIOSystem::Exists(const char *pFile) const { +#ifdef _WIN32 + struct __stat64 filestat; + if (_wstat64(Utf8ToWide(pFile).c_str(), &filestat) != 0) { + return false; + } +#else + FILE *file = ::fopen(pFile, "rb"); + if (!file) { + return false; + } + + ::fclose(file); +#endif + + return true; +} + +// ------------------------------------------------------------------------------------------------ +// Open a new file with a given path. +IOStream *DefaultIOSystem::Open(const char *strFile, const char *strMode) { + ai_assert(strFile != nullptr); + ai_assert(strMode != nullptr); + FILE *file; +#ifdef _WIN32 + std::wstring name = Utf8ToWide(strFile); + if (name.empty()) { + return nullptr; + } + + file = ::_wfopen(name.c_str(), Utf8ToWide(strMode).c_str()); +#else + file = ::fopen(strFile, strMode); +#endif + if (!file) { + return nullptr; + } + + return new DefaultIOStream(file, strFile); +} + +// ------------------------------------------------------------------------------------------------ +// Closes the given file and releases all resources associated with it. +void DefaultIOSystem::Close(IOStream *pFile) { + delete pFile; +} + +// ------------------------------------------------------------------------------------------------ +// Returns the operation specific directory separator +char DefaultIOSystem::getOsSeparator() const { +#ifndef _WIN32 + return '/'; +#else + return '\\'; +#endif +} + +// ------------------------------------------------------------------------------------------------ +// IOSystem default implementation (ComparePaths isn't a pure virtual function) +bool IOSystem::ComparePaths(const char *one, const char *second) const { + return !ASSIMP_stricmp(one, second); +} + +// ------------------------------------------------------------------------------------------------ +// Convert a relative path into an absolute path +inline static std::string MakeAbsolutePath(const char *in) { + ai_assert(in); + std::string out; +#ifdef _WIN32 + wchar_t *ret = ::_wfullpath(nullptr, Utf8ToWide(in).c_str(), 0); + if (ret) { + out = WideToUtf8(ret); + free(ret); + } +#else + char *ret = realpath(in, nullptr); + if (ret) { + out = ret; + free(ret); + } +#endif + else { + // preserve the input path, maybe someone else is able to fix + // the path before it is accessed (e.g. our file system filter) + ASSIMP_LOG_WARN("Invalid path: ", std::string(in)); + out = in; + } + return out; +} + +// ------------------------------------------------------------------------------------------------ +// DefaultIOSystem's more specialized implementation +bool DefaultIOSystem::ComparePaths(const char *one, const char *second) const { + // chances are quite good both paths are formatted identically, + // so we can hopefully return here already + if (!ASSIMP_stricmp(one, second)) + return true; + + std::string temp1 = MakeAbsolutePath(one); + std::string temp2 = MakeAbsolutePath(second); + + return !ASSIMP_stricmp(temp1, temp2); +} + +// ------------------------------------------------------------------------------------------------ +std::string DefaultIOSystem::fileName(const std::string &path) { + std::string ret = path; + std::size_t last = ret.find_last_of("\\/"); + if (last != std::string::npos) ret = ret.substr(last + 1); + return ret; +} + +// ------------------------------------------------------------------------------------------------ +std::string DefaultIOSystem::completeBaseName(const std::string &path) { + std::string ret = fileName(path); + std::size_t pos = ret.find_last_of('.'); + if (pos != std::string::npos) ret = ret.substr(0, pos); + return ret; +} + +// ------------------------------------------------------------------------------------------------ +std::string DefaultIOSystem::absolutePath(const std::string &path) { + std::string ret = path; + std::size_t last = ret.find_last_of("\\/"); + if (last != std::string::npos) ret = ret.substr(0, last); + return ret; +} + +// ------------------------------------------------------------------------------------------------ diff --git a/libs/assimp/code/Common/DefaultLogger.cpp b/libs/assimp/code/Common/DefaultLogger.cpp new file mode 100644 index 0000000..5cb32d3 --- /dev/null +++ b/libs/assimp/code/Common/DefaultLogger.cpp @@ -0,0 +1,430 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ + +/** @file DefaultLogger.cpp + * @brief Implementation of DefaultLogger (and Logger) + */ + +// Default log streams +#include "FileLogStream.h" +#include "StdOStreamLogStream.h" +#include "Win32DebugLogStream.h" +#include <assimp/StringUtils.h> + +#include <assimp/DefaultIOSystem.h> +#include <assimp/ai_assert.h> +#include <stdio.h> +#include <assimp/DefaultLogger.hpp> +#include <assimp/NullLogger.hpp> +#include <iostream> + +#ifndef ASSIMP_BUILD_SINGLETHREADED +#include <mutex> +#include <thread> +std::mutex loggerMutex; +#endif + +namespace Assimp { + +// ---------------------------------------------------------------------------------- +NullLogger DefaultLogger::s_pNullLogger; +Logger *DefaultLogger::m_pLogger = &DefaultLogger::s_pNullLogger; + +static const unsigned int SeverityAll = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging; + +// ---------------------------------------------------------------------------------- +// Represents a log-stream + its error severity +struct LogStreamInfo { + unsigned int m_uiErrorSeverity; + LogStream *m_pStream; + + // Constructor + LogStreamInfo(unsigned int uiErrorSev, LogStream *pStream) : + m_uiErrorSeverity(uiErrorSev), + m_pStream(pStream) { + // empty + } + + // Destructor + ~LogStreamInfo() { + delete m_pStream; + } +}; + +// ---------------------------------------------------------------------------------- +// Construct a default log stream +LogStream *LogStream::createDefaultStream(aiDefaultLogStream streams, + const char *name /*= "AssimpLog.txt"*/, + IOSystem *io /*= nullptr*/) { + switch (streams) { + // This is a platform-specific feature + case aiDefaultLogStream_DEBUGGER: +#ifdef WIN32 + return new Win32DebugLogStream(); +#else + return nullptr; +#endif + + // Platform-independent default streams + case aiDefaultLogStream_STDERR: + return new StdOStreamLogStream(std::cerr); + case aiDefaultLogStream_STDOUT: + return new StdOStreamLogStream(std::cout); + case aiDefaultLogStream_FILE: + return (name && *name ? new FileLogStream(name, io) : nullptr); + default: + // We don't know this default log stream, so raise an assertion + ai_assert(false); + }; + + // For compilers without dead code path detection + return nullptr; +} + +// ---------------------------------------------------------------------------------- +// Creates the only singleton instance +Logger *DefaultLogger::create(const char *name /*= "AssimpLog.txt"*/, + LogSeverity severity /*= NORMAL*/, + unsigned int defStreams /*= aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE*/, + IOSystem *io /*= nullptr*/) { + // enter the mutex here to avoid concurrency problems +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard<std::mutex> lock(loggerMutex); +#endif + + if (m_pLogger && !isNullLogger()) { + delete m_pLogger; + } + + m_pLogger = new DefaultLogger(severity); + + // Attach default log streams + // Stream the log to the MSVC debugger? + if (defStreams & aiDefaultLogStream_DEBUGGER) { + m_pLogger->attachStream(LogStream::createDefaultStream(aiDefaultLogStream_DEBUGGER)); + } + + // Stream the log to COUT? + if (defStreams & aiDefaultLogStream_STDOUT) { + m_pLogger->attachStream(LogStream::createDefaultStream(aiDefaultLogStream_STDOUT)); + } + + // Stream the log to CERR? + if (defStreams & aiDefaultLogStream_STDERR) { + m_pLogger->attachStream(LogStream::createDefaultStream(aiDefaultLogStream_STDERR)); + } + + // Stream the log to a file + if (defStreams & aiDefaultLogStream_FILE && name && *name) { + m_pLogger->attachStream(LogStream::createDefaultStream(aiDefaultLogStream_FILE, name, io)); + } + + return m_pLogger; +} + +// ---------------------------------------------------------------------------------- +void Logger::debug(const char *message) { + + // SECURITY FIX: otherwise it's easy to produce overruns since + // sometimes importers will include data from the input file + // (i.e. node names) in their messages. + if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { + return OnDebug("<fixme: long message discarded>"); + } + return OnDebug(message); +} + +// ---------------------------------------------------------------------------------- +void Logger::verboseDebug(const char *message) { + + // SECURITY FIX: see above + if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { + return OnVerboseDebug("<fixme: long message discarded>"); + } + return OnVerboseDebug(message); +} + +// ---------------------------------------------------------------------------------- +void Logger::info(const char *message) { + + // SECURITY FIX: see above + if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { + return OnInfo("<fixme: long message discarded>"); + } + return OnInfo(message); +} + +// ---------------------------------------------------------------------------------- +void Logger::warn(const char *message) { + + // SECURITY FIX: see above + if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { + return OnWarn("<fixme: long message discarded>"); + } + return OnWarn(message); +} + +// ---------------------------------------------------------------------------------- +void Logger::error(const char *message) { + // SECURITY FIX: see above + if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { + return OnError("<fixme: long message discarded>"); + } + return OnError(message); +} + +// ---------------------------------------------------------------------------------- +void DefaultLogger::set(Logger *logger) { + // enter the mutex here to avoid concurrency problems +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard<std::mutex> lock(loggerMutex); +#endif + + if (nullptr == logger) { + logger = &s_pNullLogger; + } + if (nullptr != m_pLogger && !isNullLogger()) { + delete m_pLogger; + } + + DefaultLogger::m_pLogger = logger; +} + +// ---------------------------------------------------------------------------------- +bool DefaultLogger::isNullLogger() { + return m_pLogger == &s_pNullLogger; +} + +// ---------------------------------------------------------------------------------- +Logger *DefaultLogger::get() { + return m_pLogger; +} + +// ---------------------------------------------------------------------------------- +// Kills the only instance +void DefaultLogger::kill() { + // enter the mutex here to avoid concurrency problems +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard<std::mutex> lock(loggerMutex); +#endif + + if (m_pLogger == &s_pNullLogger) { + return; + } + delete m_pLogger; + m_pLogger = &s_pNullLogger; +} + +// ---------------------------------------------------------------------------------- +// Debug message +void DefaultLogger::OnDebug(const char *message) { + if (m_Severity < Logger::DEBUGGING) { + return; + } + + static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; + char msg[Size]; + ai_snprintf(msg, Size, "Debug, T%u: %s", GetThreadID(), message); + + WriteToStreams(msg, Logger::Debugging); +} + +// Verbose debug message +void DefaultLogger::OnVerboseDebug(const char *message) { + if (m_Severity < Logger::VERBOSE) { + return; + } + + static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; + char msg[Size]; + ai_snprintf(msg, Size, "Debug, T%u: %s", GetThreadID(), message); + + WriteToStreams(msg, Logger::Debugging); +} + +// ---------------------------------------------------------------------------------- +// Logs an info +void DefaultLogger::OnInfo(const char *message) { + static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; + char msg[Size]; + ai_snprintf(msg, Size, "Info, T%u: %s", GetThreadID(), message); + + WriteToStreams(msg, Logger::Info); +} + +// ---------------------------------------------------------------------------------- +// Logs a warning +void DefaultLogger::OnWarn(const char *message) { + static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; + char msg[Size]; + ai_snprintf(msg, Size, "Warn, T%u: %s", GetThreadID(), message); + + WriteToStreams(msg, Logger::Warn); +} + +// ---------------------------------------------------------------------------------- +// Logs an error +void DefaultLogger::OnError(const char *message) { + static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; + char msg[Size]; + ai_snprintf(msg, Size, "Error, T%u: %s", GetThreadID(), message); + + WriteToStreams(msg, Logger::Err); +} + +// ---------------------------------------------------------------------------------- +// Will attach a new stream +bool DefaultLogger::attachStream(LogStream *pStream, unsigned int severity) { + if (nullptr == pStream) { + return false; + } + + if (0 == severity) { + severity = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging; + } + + for (StreamIt it = m_StreamArray.begin(); + it != m_StreamArray.end(); + ++it) { + if ((*it)->m_pStream == pStream) { + (*it)->m_uiErrorSeverity |= severity; + return true; + } + } + + LogStreamInfo *pInfo = new LogStreamInfo(severity, pStream); + m_StreamArray.push_back(pInfo); + return true; +} + +// ---------------------------------------------------------------------------------- +// Detach a stream +bool DefaultLogger::detachStream(LogStream *pStream, unsigned int severity) { + if (nullptr == pStream) { + return false; + } + + if (0 == severity) { + severity = SeverityAll; + } + + bool res(false); + for (StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it) { + if ((*it)->m_pStream == pStream) { + (*it)->m_uiErrorSeverity &= ~severity; + if ((*it)->m_uiErrorSeverity == 0) { + // don't delete the underlying stream 'cause the caller gains ownership again + (**it).m_pStream = nullptr; + delete *it; + m_StreamArray.erase(it); + res = true; + break; + } + return true; + } + } + return res; +} + +// ---------------------------------------------------------------------------------- +// Constructor +DefaultLogger::DefaultLogger(LogSeverity severity) : + Logger(severity), noRepeatMsg(false), lastLen(0) { + lastMsg[0] = '\0'; +} + +// ---------------------------------------------------------------------------------- +// Destructor +DefaultLogger::~DefaultLogger() { + for (StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it) { + // also frees the underlying stream, we are its owner. + delete *it; + } +} + +// ---------------------------------------------------------------------------------- +// Writes message to stream +void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev) { + ai_assert(nullptr != message); + + // Check whether this is a repeated message + auto thisLen = ::strlen(message); + if (thisLen == lastLen - 1 && !::strncmp(message, lastMsg, lastLen - 1)) { + if (!noRepeatMsg) { + noRepeatMsg = true; + message = "Skipping one or more lines with the same contents\n"; + } + return; + } else { + // append a new-line character to the message to be printed + lastLen = thisLen; + ::memcpy(lastMsg, message, lastLen + 1); + ::strcat(lastMsg + lastLen, "\n"); + + message = lastMsg; + noRepeatMsg = false; + ++lastLen; + } + for (ConstStreamIt it = m_StreamArray.begin(); + it != m_StreamArray.end(); + ++it) { + if (ErrorSev & (*it)->m_uiErrorSeverity) + (*it)->m_pStream->write(message); + } +} + +// ---------------------------------------------------------------------------------- +// Returns thread id, if not supported only a zero will be returned. +unsigned int DefaultLogger::GetThreadID() { + // fixme: we can get this value via std::threads + // std::this_thread::get_id().hash() returns a (big) size_t, not sure if this is useful in this case. +#ifdef WIN32 + return (unsigned int)::GetCurrentThreadId(); +#else + return 0; // not supported +#endif +} + +// ---------------------------------------------------------------------------------- + +} // namespace Assimp diff --git a/libs/assimp/code/Common/DefaultProgressHandler.h b/libs/assimp/code/Common/DefaultProgressHandler.h new file mode 100644 index 0000000..ac1bb68 --- /dev/null +++ b/libs/assimp/code/Common/DefaultProgressHandler.h @@ -0,0 +1,66 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +/** @file ProgressHandler.hpp + * @brief Abstract base class 'ProgressHandler'. + */ +#ifndef INCLUDED_AI_DEFAULTPROGRESSHANDLER_H +#define INCLUDED_AI_DEFAULTPROGRESSHANDLER_H + +#include <assimp/ProgressHandler.hpp> + +namespace Assimp { + +// ------------------------------------------------------------------------------------ +/** + * @brief Internal default implementation of the #ProgressHandler interface. + */ +class DefaultProgressHandler : public ProgressHandler { +public: + /// @brief Ignores the update callback. + bool Update(float) override { + return false; + } +}; + +} // Namespace Assimp + +#endif // INCLUDED_AI_DEFAULTPROGRESSHANDLER_H diff --git a/libs/assimp/code/Common/Exceptional.cpp b/libs/assimp/code/Common/Exceptional.cpp new file mode 100644 index 0000000..b25281a --- /dev/null +++ b/libs/assimp/code/Common/Exceptional.cpp @@ -0,0 +1,52 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ + +/** @file Exceptional.cpp + +Implementations of the exception classes. + +*/ + +#include <assimp/Exceptional.h> +#include <assimp/TinyFormatter.h> + +DeadlyErrorBase::DeadlyErrorBase(Assimp::Formatter::format f) : + runtime_error(std::string(f)){} diff --git a/libs/assimp/code/Common/Exporter.cpp b/libs/assimp/code/Common/Exporter.cpp new file mode 100644 index 0000000..9a4e2cf --- /dev/null +++ b/libs/assimp/code/Common/Exporter.cpp @@ -0,0 +1,695 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ + +/** @file Exporter.cpp + +Assimp export interface. While it's public interface bears many similarities +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). +*/ + +#ifndef ASSIMP_BUILD_NO_EXPORT + +#include <assimp/BlobIOSystem.h> +#include <assimp/SceneCombiner.h> +#include <assimp/DefaultIOSystem.h> +#include <assimp/Exporter.hpp> +#include <assimp/mesh.h> +#include <assimp/postprocess.h> +#include <assimp/scene.h> +#include <assimp/Exceptional.h> + +#include "Common/DefaultProgressHandler.h" +#include "Common/BaseProcess.h" +#include "Common/ScenePrivate.h" +#include "PostProcessing/CalcTangentsProcess.h" +#include "PostProcessing/MakeVerboseFormat.h" +#include "PostProcessing/JoinVerticesProcess.h" +#include "PostProcessing/ConvertToLHProcess.h" +#include "PostProcessing/PretransformVertices.h" + +#include <memory> + +namespace Assimp { + +#ifdef _MSC_VER +# pragma warning( disable : 4800 ) +#endif // _MSC_VER + + +// PostStepRegistry.cpp +void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out); + +// ------------------------------------------------------------------------------------------------ +// Exporter worker function prototypes. Do not use const, because some exporter need to convert +// the scene temporary +#ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER +void ExportSceneCollada(const char*,IOSystem*, const aiScene*, const ExportProperties*); +#endif +#ifndef ASSIMP_BUILD_NO_X_EXPORTER +void ExportSceneXFile(const char*,IOSystem*, const aiScene*, const ExportProperties*); +#endif +#ifndef ASSIMP_BUILD_NO_STEP_EXPORTER +void ExportSceneStep(const char*,IOSystem*, const aiScene*, const ExportProperties*); +#endif +#ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER +void ExportSceneObj(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneObjNoMtl(const char*,IOSystem*, const aiScene*, const ExportProperties*); +#endif +#ifndef ASSIMP_BUILD_NO_STL_EXPORTER +void ExportSceneSTL(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneSTLBinary(const char*,IOSystem*, const aiScene*, const ExportProperties*); +#endif +#ifndef ASSIMP_BUILD_NO_PLY_EXPORTER +void ExportScenePly(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportScenePlyBinary(const char*, IOSystem*, const aiScene*, const ExportProperties*); +#endif +#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER +void ExportScene3DS(const char*, IOSystem*, const aiScene*, const ExportProperties*); +#endif +#ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER +void ExportSceneGLTF(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneGLB(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneGLTF2(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneGLB2(const char*, IOSystem*, const aiScene*, const ExportProperties*); +#endif +#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER +void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*); +#endif +#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER +void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*); +#endif +#ifndef ASSIMP_BUILD_NO_X3D_EXPORTER +void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*); +#endif +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER +void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*); +#endif +#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER +void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* ); +#endif +#ifndef ASSIMP_BUILD_NO_M3D_EXPORTER +void ExportSceneM3D(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneM3DA(const char*, IOSystem*, const aiScene*, const ExportProperties*); +#endif +#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER +void ExportAssimp2Json(const char* , IOSystem*, const aiScene* , const Assimp::ExportProperties*); +#endif +#ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER +void ExportScenePbrt(const char*, IOSystem*, const aiScene*, const ExportProperties*); +#endif + +static void setupExporterArray(std::vector<Exporter::ExportFormatEntry> &exporters) { + (void)exporters; + +#ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER + exporters.push_back(Exporter::ExportFormatEntry("collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada)); +#endif + +#ifndef ASSIMP_BUILD_NO_X_EXPORTER + exporters.push_back(Exporter::ExportFormatEntry("x", "X Files", "x", &ExportSceneXFile, + aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs)); +#endif + +#ifndef ASSIMP_BUILD_NO_STEP_EXPORTER + exporters.push_back(Exporter::ExportFormatEntry("stp", "Step Files", "stp", &ExportSceneStep, 0)); +#endif + +#ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER + exporters.push_back(Exporter::ExportFormatEntry("obj", "Wavefront OBJ format", "obj", &ExportSceneObj, + aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */)); + exporters.push_back(Exporter::ExportFormatEntry("objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl, + aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */)); +#endif + +#ifndef ASSIMP_BUILD_NO_STL_EXPORTER + exporters.push_back(Exporter::ExportFormatEntry("stl", "Stereolithography", "stl", &ExportSceneSTL, + aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices)); + exporters.push_back(Exporter::ExportFormatEntry("stlb", "Stereolithography (binary)", "stl", &ExportSceneSTLBinary, + aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices)); +#endif + +#ifndef ASSIMP_BUILD_NO_PLY_EXPORTER + exporters.push_back(Exporter::ExportFormatEntry("ply", "Stanford Polygon Library", "ply", &ExportScenePly, + aiProcess_PreTransformVertices)); + exporters.push_back(Exporter::ExportFormatEntry("plyb", "Stanford Polygon Library (binary)", "ply", &ExportScenePlyBinary, + aiProcess_PreTransformVertices)); +#endif + +#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER + exporters.push_back(Exporter::ExportFormatEntry("3ds", "Autodesk 3DS (legacy)", "3ds", &ExportScene3DS, + aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices)); +#endif + +#if !defined(ASSIMP_BUILD_NO_GLTF_EXPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_EXPORTER) + exporters.push_back(Exporter::ExportFormatEntry("gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2, + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType)); + exporters.push_back(Exporter::ExportFormatEntry("glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2, + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType)); +#endif + +#if !defined(ASSIMP_BUILD_NO_GLTF_EXPORTER) && !defined(ASSIMP_BUILD_NO_GLTF1_EXPORTER) + exporters.push_back(Exporter::ExportFormatEntry("gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF, + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType)); + exporters.push_back(Exporter::ExportFormatEntry("glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB, + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType)); +#endif + +#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER + exporters.push_back(Exporter::ExportFormatEntry("assbin", "Assimp Binary File", "assbin", &ExportSceneAssbin, 0)); +#endif + +#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER + exporters.push_back(Exporter::ExportFormatEntry("assxml", "Assimp XML Document", "assxml", &ExportSceneAssxml, 0)); +#endif + +#ifndef ASSIMP_BUILD_NO_X3D_EXPORTER + exporters.push_back(Exporter::ExportFormatEntry("x3d", "Extensible 3D", "x3d", &ExportSceneX3D, 0)); +#endif + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + exporters.push_back(Exporter::ExportFormatEntry("fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0)); + exporters.push_back(Exporter::ExportFormatEntry("fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0)); +#endif + +#ifndef ASSIMP_BUILD_NO_M3D_EXPORTER + exporters.push_back(Exporter::ExportFormatEntry("m3d", "Model 3D (binary)", "m3d", &ExportSceneM3D, 0)); + exporters.push_back(Exporter::ExportFormatEntry("m3da", "Model 3D (ascii)", "a3d", &ExportSceneM3DA, 0)); +#endif + +#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER + exporters.push_back(Exporter::ExportFormatEntry("3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0)); +#endif + +#ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER + exporters.push_back(Exporter::ExportFormatEntry("pbrt", "pbrt-v4 scene description file", "pbrt", &ExportScenePbrt, aiProcess_Triangulate | aiProcess_SortByPType)); +#endif + +#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER + exporters.push_back(Exporter::ExportFormatEntry("assjson", "Assimp JSON Document", "json", &ExportAssimp2Json, 0)); +#endif +} + +class ExporterPimpl { +public: + ExporterPimpl() + : blob() + , mIOSystem(new Assimp::DefaultIOSystem()) + , mIsDefaultIOHandler(true) + , mProgressHandler( nullptr ) + , mIsDefaultProgressHandler( true ) + , mPostProcessingSteps() + , mError() + , mExporters() { + GetPostProcessingStepInstanceList(mPostProcessingSteps); + + // grab all built-in exporters + setupExporterArray(mExporters); + } + + ~ExporterPimpl() { + delete blob; + + // Delete all post-processing plug-ins + for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++) { + delete mPostProcessingSteps[a]; + } + delete mProgressHandler; + } + +public: + aiExportDataBlob* blob; + 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; + + /** Last fatal export error */ + std::string mError; + + /** Exporters, this includes those registered using #Assimp::Exporter::RegisterExporter */ + std::vector<Exporter::ExportFormatEntry> mExporters; +}; + +} // end of namespace Assimp + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +Exporter :: Exporter() +: pimpl(new ExporterPimpl()) { + pimpl->mProgressHandler = new DefaultProgressHandler(); +} + +// ------------------------------------------------------------------------------------------------ +Exporter::~Exporter() { + ai_assert(nullptr != pimpl); + FreeBlob(); + delete pimpl; +} + +// ------------------------------------------------------------------------------------------------ +void Exporter::SetIOHandler( IOSystem* pIOHandler) { + ai_assert(nullptr != pimpl); + pimpl->mIsDefaultIOHandler = !pIOHandler; + pimpl->mIOSystem.reset(pIOHandler); +} + +// ------------------------------------------------------------------------------------------------ +IOSystem* Exporter::GetIOHandler() const { + ai_assert(nullptr != pimpl); + return pimpl->mIOSystem.get(); +} + +// ------------------------------------------------------------------------------------------------ +bool Exporter::IsDefaultIOHandler() const { + ai_assert(nullptr != pimpl); + 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 pPreprocessing, const ExportProperties* pProperties) { + ai_assert(nullptr != pimpl); + if (pimpl->blob) { + delete pimpl->blob; + pimpl->blob = nullptr; + } + + auto baseName = pProperties ? pProperties->GetPropertyString(AI_CONFIG_EXPORT_BLOB_NAME, AI_BLOBIO_MAGIC) : AI_BLOBIO_MAGIC; + + std::shared_ptr<IOSystem> old = pimpl->mIOSystem; + BlobIOSystem *blobio = new BlobIOSystem(baseName); + pimpl->mIOSystem = std::shared_ptr<IOSystem>( blobio ); + + if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName(), pPreprocessing, pProperties)) { + pimpl->mIOSystem = old; + return nullptr; + } + + pimpl->blob = blobio->GetBlobChain(); + pimpl->mIOSystem = old; + + return pimpl->blob; +} + +// ------------------------------------------------------------------------------------------------ +aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath, + unsigned int pPreprocessing, const ExportProperties* pProperties) { + ASSIMP_BEGIN_EXCEPTION_REGION(); + ai_assert(nullptr != pimpl); + // when they create scenes from scratch, users will likely create them not in verbose + // format. They will likely not be aware that there is a flag in the scene to indicate + // this, however. To avoid surprises and bug reports, we check for duplicates in + // meshes upfront. + const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || MakeVerboseFormatProcess::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]; + if (!strcmp(exp.mDescription.id,pFormatId)) { + 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 = nullptr; + SceneCombiner::CopyScene(&scenecopy_tmp,pScene); + + pimpl->mProgressHandler->UpdateFileWrite(1, 4); + + std::unique_ptr<aiScene> 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 + const unsigned int pp = (exp.mEnforcePP | pPreprocessing) & ~(priv && !priv->mIsCopy + ? (priv->mPPStepsApplied & ~nonIdempotentSteps) + : 0u); + + // If no extra post-processing was specified, and we obtained this scene from an + // Assimp importer, apply the reverse steps automatically. + // TODO: either drop this, or document it. Otherwise it is just a bad surprise. + //if (!pPreprocessing && priv) { + // pp |= (nonIdempotentSteps & priv->mPPStepsApplied); + //} + + // If the input scene is not in verbose format, but there is at least post-processing step that relies on it, + // we need to run the MakeVerboseFormat step first. + bool must_join_again = false; + if (!is_verbose_format) { + bool verbosify = false; + for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { + BaseProcess* const p = pimpl->mPostProcessingSteps[a]; + + if (p->IsActive(pp) && p->RequireVerboseFormat()) { + verbosify = true; + break; + } + } + + if (verbosify || (exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) { + ASSIMP_LOG_DEBUG("export: Scene data not in verbose format, applying MakeVerboseFormat step first"); + + MakeVerboseFormatProcess proc; + proc.Execute(scenecopy.get()); + + if(!(exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) { + must_join_again = true; + } + } + } + + 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 + { + FlipWindingOrderProcess step; + if (step.IsActive(pp)) { + step.Execute(scenecopy.get()); + } + } + + { + FlipUVsProcess step; + if (step.IsActive(pp)) { + step.Execute(scenecopy.get()); + } + } + + { + MakeLeftHandedProcess step; + if (step.IsActive(pp)) { + step.Execute(scenecopy.get()); + } + } + + bool exportPointCloud(false); + if (nullptr != pProperties) { + exportPointCloud = pProperties->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS); + } + + // dispatch other processes + for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { + BaseProcess* const p = pimpl->mPostProcessingSteps[a]; + + if (p->IsActive(pp) + && !dynamic_cast<FlipUVsProcess*>(p) + && !dynamic_cast<FlipWindingOrderProcess*>(p) + && !dynamic_cast<MakeLeftHandedProcess*>(p)) { + if (dynamic_cast<PretransformVertices*>(p) && exportPointCloud) { + continue; + } + p->Execute(scenecopy.get()); + } + } + ScenePrivateData* const privOut = ScenePriv(scenecopy.get()); + ai_assert(nullptr != privOut); + + privOut->mPPStepsApplied |= pp; + } + + pimpl->mProgressHandler->UpdateFileWrite(3, 4); + + if(must_join_again) { + JoinVerticesProcess proc; + proc.Execute(scenecopy.get()); + } + + ExportProperties emptyProperties; // Never pass nullptr ExportProperties so Exporters don't have to worry. + ExportProperties* pProp = pProperties ? (ExportProperties*)pProperties : &emptyProperties; + pProp->SetPropertyBool("bJoinIdenticalVertices", pp & aiProcess_JoinIdenticalVertices); + exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp); + + pimpl->mProgressHandler->UpdateFileWrite(4, 4); + } catch (DeadlyExportError& err) { + 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 { + ai_assert(nullptr != pimpl); + return pimpl->mError.c_str(); +} + +// ------------------------------------------------------------------------------------------------ +void Exporter::FreeBlob() { + ai_assert(nullptr != pimpl); + delete pimpl->blob; + pimpl->blob = nullptr; + + pimpl->mError = ""; +} + +// ------------------------------------------------------------------------------------------------ +const aiExportDataBlob* Exporter::GetBlob() const { + ai_assert(nullptr != pimpl); + return pimpl->blob; +} + +// ------------------------------------------------------------------------------------------------ +const aiExportDataBlob* Exporter::GetOrphanedBlob() const { + ai_assert(nullptr != pimpl); + const aiExportDataBlob *tmp = pimpl->blob; + pimpl->blob = nullptr; + return tmp; +} + +// ------------------------------------------------------------------------------------------------ +size_t Exporter::GetExportFormatCount() const { + ai_assert(nullptr != pimpl); + return pimpl->mExporters.size(); +} + +// ------------------------------------------------------------------------------------------------ +const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const { + ai_assert(nullptr != pimpl); + if (index >= GetExportFormatCount()) { + return nullptr; + } + + // Return from static storage if the requested index is built-in. + if (index < pimpl->mExporters.size()) { + return &pimpl->mExporters[index].mDescription; + } + + return &pimpl->mExporters[index].mDescription; +} + +// ------------------------------------------------------------------------------------------------ +aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) { + ai_assert(nullptr != pimpl); + for (const ExportFormatEntry &e : pimpl->mExporters) { + if (!strcmp(e.mDescription.id,desc.mDescription.id)) { + return aiReturn_FAILURE; + } + } + + pimpl->mExporters.push_back(desc); + return aiReturn_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +void Exporter::UnregisterExporter(const char* id) { + ai_assert(nullptr != pimpl); + for (std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin(); + it != pimpl->mExporters.end(); ++it) { + if (!strcmp((*it).mDescription.id,id)) { + pimpl->mExporters.erase(it); + break; + } + } +} + +// ------------------------------------------------------------------------------------------------ +ExportProperties::ExportProperties() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +ExportProperties::ExportProperties(const ExportProperties &other) +: mIntProperties(other.mIntProperties) +, mFloatProperties(other.mFloatProperties) +, mStringProperties(other.mStringProperties) +, mMatrixProperties(other.mMatrixProperties) +, mCallbackProperties(other.mCallbackProperties){ + // empty +} + +bool ExportProperties::SetPropertyCallback(const char *szName, const std::function<void *(void *)> &f) { + return SetGenericProperty<std::function<void *(void *)>>(mCallbackProperties, szName, f); +} + +std::function<void *(void *)> ExportProperties::GetPropertyCallback(const char *szName) const { + return GetGenericProperty<std::function<void *(void *)>>(mCallbackProperties, szName, 0); +} + +bool ExportProperties::HasPropertyCallback(const char *szName) const { + return HasGenericProperty<std::function<void *(void *)>>(mCallbackProperties, szName); +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool ExportProperties::SetPropertyInteger(const char* szName, int iValue) { + return SetGenericProperty<int>(mIntProperties, szName,iValue); +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool ExportProperties::SetPropertyFloat(const char* szName, ai_real iValue) { + return SetGenericProperty<ai_real>(mFloatProperties, szName,iValue); +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool ExportProperties::SetPropertyString(const char* szName, const std::string& value) { + return SetGenericProperty<std::string>(mStringProperties, szName,value); +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool ExportProperties::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) { + return SetGenericProperty<aiMatrix4x4>(mMatrixProperties, szName,value); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +int ExportProperties::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const { + return GetGenericProperty<int>(mIntProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +ai_real ExportProperties::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const { + return GetGenericProperty<ai_real>(mFloatProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +const std::string ExportProperties::GetPropertyString(const char* szName, + const std::string& iErrorReturn /*= ""*/) const { + return GetGenericProperty<std::string>(mStringProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +const aiMatrix4x4 ExportProperties::GetPropertyMatrix(const char* szName, + const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const { + return GetGenericProperty<aiMatrix4x4>(mMatrixProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +bool ExportProperties::HasPropertyInteger(const char* szName) const { + return HasGenericProperty<int>(mIntProperties, szName); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +bool ExportProperties::HasPropertyBool(const char* szName) const { + return HasGenericProperty<int>(mIntProperties, szName); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +bool ExportProperties::HasPropertyFloat(const char* szName) const { + return HasGenericProperty<ai_real>(mFloatProperties, szName); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +bool ExportProperties::HasPropertyString(const char* szName) const { + return HasGenericProperty<std::string>(mStringProperties, szName); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +bool ExportProperties::HasPropertyMatrix(const char* szName) const { + return HasGenericProperty<aiMatrix4x4>(mMatrixProperties, szName); +} + + +#endif // !ASSIMP_BUILD_NO_EXPORT diff --git a/libs/assimp/code/Common/FileLogStream.h b/libs/assimp/code/Common/FileLogStream.h new file mode 100644 index 0000000..3345414 --- /dev/null +++ b/libs/assimp/code/Common/FileLogStream.h @@ -0,0 +1,100 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ +/** @file FileLofStream.h +*/ +#ifndef ASSIMP_FILELOGSTREAM_H_INC +#define ASSIMP_FILELOGSTREAM_H_INC + +#include <assimp/DefaultIOSystem.h> +#include <assimp/IOStream.hpp> +#include <assimp/LogStream.hpp> + +namespace Assimp { + +// ---------------------------------------------------------------------------------- +/** @class FileLogStream + * @brief Logstream to write into a file. + */ +class FileLogStream : public LogStream { +public: + FileLogStream(const char *file, IOSystem *io = nullptr); + ~FileLogStream(); + void write(const char *message); + +private: + IOStream *m_pStream; +}; + +// ---------------------------------------------------------------------------------- +// Constructor +inline FileLogStream::FileLogStream(const char *file, IOSystem *io) : + m_pStream(nullptr) { + if (!file || 0 == *file) + return; + + // If no IOSystem is specified: take a default one + if (!io) { + DefaultIOSystem FileSystem; + m_pStream = FileSystem.Open(file, "wt"); + } else + m_pStream = io->Open(file, "wt"); +} + +// ---------------------------------------------------------------------------------- +// Destructor +inline FileLogStream::~FileLogStream() { + // The virtual d'tor should destroy the underlying file + delete m_pStream; +} + +// ---------------------------------------------------------------------------------- +// Write method +inline void FileLogStream::write(const char *message) { + if (m_pStream != nullptr) { + m_pStream->Write(message, sizeof(char), ::strlen(message)); + m_pStream->Flush(); + } +} + +// ---------------------------------------------------------------------------------- +} // namespace Assimp + +#endif // !! ASSIMP_FILELOGSTREAM_H_INC diff --git a/libs/assimp/code/Common/FileSystemFilter.h b/libs/assimp/code/Common/FileSystemFilter.h new file mode 100644 index 0000000..81576aa --- /dev/null +++ b/libs/assimp/code/Common/FileSystemFilter.h @@ -0,0 +1,346 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2020, 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. + +---------------------------------------------------------------------- +*/ + +/** @file FileSystemFilter.h + * Implements a filter system to filter calls to Exists() and Open() + * in order to improve the success rate of file opening ... + */ +#pragma once +#ifndef AI_FILESYSTEMFILTER_H_INC +#define AI_FILESYSTEMFILTER_H_INC + +#include <assimp/IOSystem.hpp> +#include <assimp/DefaultLogger.hpp> +#include <assimp/fast_atof.h> +#include <assimp/ParsingUtils.h> + +namespace Assimp { + +inline bool IsHex(char s) { + return (s>='0' && s<='9') || (s>='a' && s<='f') || (s>='A' && s<='F'); +} + +// --------------------------------------------------------------------------- +/** File system filter + */ +class FileSystemFilter : public IOSystem +{ +public: + /** Constructor. */ + FileSystemFilter(const std::string& file, IOSystem* old) + : mWrapped (old) + , mSrc_file(file) + , mSep(mWrapped->getOsSeparator()) { + ai_assert(nullptr != mWrapped); + + // Determine base directory + mBase = mSrc_file; + std::string::size_type ss2; + if (std::string::npos != (ss2 = mBase.find_last_of("\\/"))) { + mBase.erase(ss2,mBase.length()-ss2); + } else { + mBase = std::string(); + } + + // make sure the directory is terminated properly + char s; + + if ( mBase.empty() ) { + mBase = "."; + mBase += getOsSeparator(); + } else if ((s = *(mBase.end()-1)) != '\\' && s != '/') { + mBase += getOsSeparator(); + } + + DefaultLogger::get()->info("Import root directory is \'", mBase, "\'"); + } + + /** Destructor. */ + ~FileSystemFilter() { + // empty + } + + // ------------------------------------------------------------------- + /** Tests for the existence of a file at the given path. */ + bool Exists( const char* pFile) const { + ai_assert( nullptr != mWrapped ); + + std::string tmp = pFile; + + // Currently this IOSystem is also used to open THE ONE FILE. + if (tmp != mSrc_file) { + BuildPath(tmp); + Cleanup(tmp); + } + + return mWrapped->Exists(tmp); + } + + // ------------------------------------------------------------------- + /** Returns the directory separator. */ + char getOsSeparator() const { + return mSep; + } + + // ------------------------------------------------------------------- + /** Open a new file with a given path. */ + IOStream* Open( const char* pFile, const char* pMode = "rb") { + ai_assert( nullptr != mWrapped ); + if ( nullptr == pFile || nullptr == pMode ) { + return nullptr; + } + + ai_assert( nullptr != pFile ); + ai_assert( nullptr != pMode ); + + // First try the unchanged path + IOStream* s = mWrapped->Open(pFile,pMode); + + if (nullptr == s) { + std::string tmp = pFile; + + // Try to convert between absolute and relative paths + BuildPath(tmp); + s = mWrapped->Open(tmp,pMode); + + if (nullptr == s) { + // Finally, look for typical issues with paths + // and try to correct them. This is our last + // resort. + tmp = pFile; + Cleanup(tmp); + BuildPath(tmp); + s = mWrapped->Open(tmp,pMode); + } + } + + return s; + } + + // ------------------------------------------------------------------- + /** Closes the given file and releases all resources associated with it. */ + void Close( IOStream* pFile) { + ai_assert( nullptr != mWrapped ); + return mWrapped->Close(pFile); + } + + // ------------------------------------------------------------------- + /** Compare two paths */ + bool ComparePaths (const char* one, const char* second) const { + ai_assert( nullptr != mWrapped ); + return mWrapped->ComparePaths (one,second); + } + + // ------------------------------------------------------------------- + /** Pushes a new directory onto the directory stack. */ + bool PushDirectory(const std::string &path ) { + ai_assert( nullptr != mWrapped ); + return mWrapped->PushDirectory(path); + } + + // ------------------------------------------------------------------- + /** Returns the top directory from the stack. */ + const std::string &CurrentDirectory() const { + ai_assert( nullptr != mWrapped ); + return mWrapped->CurrentDirectory(); + } + + // ------------------------------------------------------------------- + /** Returns the number of directories stored on the stack. */ + size_t StackSize() const { + ai_assert( nullptr != mWrapped ); + return mWrapped->StackSize(); + } + + // ------------------------------------------------------------------- + /** Pops the top directory from the stack. */ + bool PopDirectory() { + ai_assert( nullptr != mWrapped ); + return mWrapped->PopDirectory(); + } + + // ------------------------------------------------------------------- + /** Creates an new directory at the given path. */ + bool CreateDirectory(const std::string &path) { + ai_assert( nullptr != mWrapped ); + return mWrapped->CreateDirectory(path); + } + + // ------------------------------------------------------------------- + /** Will change the current directory to the given path. */ + bool ChangeDirectory(const std::string &path) { + ai_assert( nullptr != mWrapped ); + return mWrapped->ChangeDirectory(path); + } + + // ------------------------------------------------------------------- + /** Delete file. */ + bool DeleteFile(const std::string &file) { + ai_assert( nullptr != mWrapped ); + return mWrapped->DeleteFile(file); + } + +private: + // ------------------------------------------------------------------- + /** Build a valid path from a given relative or absolute path. + */ + void BuildPath (std::string& in) const { + ai_assert( nullptr != mWrapped ); + // if we can already access the file, great. + if (in.length() < 3 || mWrapped->Exists(in)) { + return; + } + + // Determine whether this is a relative path (Windows-specific - most assets are packaged on Windows). + if (in[1] != ':') { + + // append base path and try + const std::string tmp = mBase + in; + if (mWrapped->Exists(tmp)) { + in = tmp; + return; + } + } + + // Chop of the file name and look in the model directory, if + // this fails try all sub paths of the given path, i.e. + // if the given path is foo/bar/something.lwo, try + // <base>/something.lwo + // <base>/bar/something.lwo + // <base>/foo/bar/something.lwo + std::string::size_type pos = in.rfind('/'); + if (std::string::npos == pos) { + pos = in.rfind('\\'); + } + + if (std::string::npos != pos) { + std::string tmp; + std::string::size_type last_dirsep = std::string::npos; + + while(true) { + tmp = mBase; + tmp += mSep; + + std::string::size_type dirsep = in.rfind('/', last_dirsep); + if (std::string::npos == dirsep) { + dirsep = in.rfind('\\', last_dirsep); + } + + if (std::string::npos == dirsep || dirsep == 0) { + // we did try this already. + break; + } + + last_dirsep = dirsep-1; + + tmp += in.substr(dirsep+1, in.length()-pos); + if (mWrapped->Exists(tmp)) { + in = tmp; + return; + } + } + } + + // hopefully the underlying file system has another few tricks to access this file ... + } + + // ------------------------------------------------------------------- + /** Cleanup the given path + */ + void Cleanup (std::string& in) const { + if(in.empty()) { + return; + } + + // Remove a very common issue when we're parsing file names: spaces at the + // beginning of the path. + char last = 0; + std::string::iterator it = in.begin(); + while (IsSpaceOrNewLine( *it ))++it; + if (it != in.begin()) { + in.erase(in.begin(),it+1); + } + + const char separator = getOsSeparator(); + for (it = in.begin(); it != in.end(); ++it) { + int remaining = std::distance(in.end(), it); + // Exclude :// and \\, which remain untouched. + // https://sourceforge.net/tracker/?func=detail&aid=3031725&group_id=226462&atid=1067632 + if (remaining >= 3 && !strncmp(&*it, "://", 3 )) { + it += 3; + continue; + } + if (it == in.begin() && remaining >= 2 && !strncmp(&*it, "\\\\", 2)) { + it += 2; + continue; + } + + // Cleanup path delimiters + if (*it == '/' || (*it) == '\\') { + *it = separator; + + // And we're removing double delimiters, frequent issue with + // incorrectly composited paths ... + if (last == *it) { + it = in.erase(it); + --it; + } + } else if (*it == '%' && in.end() - it > 2) { + // Hex sequence in URIs + if( IsHex((&*it)[0]) && IsHex((&*it)[1]) ) { + *it = HexOctetToDecimal(&*it); + it = in.erase(it+1,it+2); + --it; + } + } + + last = *it; + } + } + +private: + IOSystem *mWrapped; + std::string mSrc_file, mBase; + char mSep; +}; + +} //!ns Assimp + +#endif //AI_DEFAULTIOSYSTEM_H_INC diff --git a/libs/assimp/code/Common/IFF.h b/libs/assimp/code/Common/IFF.h new file mode 100644 index 0000000..91d7d48 --- /dev/null +++ b/libs/assimp/code/Common/IFF.h @@ -0,0 +1,102 @@ +// Definitions for the Interchange File Format (IFF) +// Alexander Gessler, 2006 +// Adapted to Assimp August 2008 + +#ifndef AI_IFF_H_INCLUDED +#define AI_IFF_H_INCLUDED + +#include <assimp/ByteSwapper.h> + +namespace Assimp { +namespace IFF { + +///////////////////////////////////////////////////////////////////////////////// +//! Describes an IFF chunk header +///////////////////////////////////////////////////////////////////////////////// +struct ChunkHeader +{ + //! Type of the chunk header - FourCC + uint32_t type; + + //! Length of the chunk data, in bytes + uint32_t length; +}; + + +///////////////////////////////////////////////////////////////////////////////// +//! Describes an IFF sub chunk header +///////////////////////////////////////////////////////////////////////////////// +struct SubChunkHeader +{ + //! Type of the chunk header - FourCC + uint32_t type; + + //! Length of the chunk data, in bytes + uint16_t length; +}; + + +#define AI_IFF_FOURCC(a,b,c,d) ((uint32_t) (((uint8_t)a << 24u) | \ + ((uint8_t)b << 16u) | ((uint8_t)c << 8u) | ((uint8_t)d))) + + +#define AI_IFF_FOURCC_FORM AI_IFF_FOURCC('F','O','R','M') + + +///////////////////////////////////////////////////////////////////////////////// +//! Load a chunk header +//! @param outFile Pointer to the file data - points to the chunk data afterwards +//! @return Copy of the chunk header +///////////////////////////////////////////////////////////////////////////////// +inline ChunkHeader LoadChunk(uint8_t*& outFile) +{ + ChunkHeader head; + ::memcpy(&head.type, outFile, 4); + outFile += 4; + ::memcpy(&head.length, outFile, 4); + outFile += 4; + AI_LSWAP4(head.length); + AI_LSWAP4(head.type); + return head; +} + +///////////////////////////////////////////////////////////////////////////////// +//! Load a sub chunk header +//! @param outFile Pointer to the file data - points to the chunk data afterwards +//! @return Copy of the sub chunk header +///////////////////////////////////////////////////////////////////////////////// +inline SubChunkHeader LoadSubChunk(uint8_t*& outFile) +{ + SubChunkHeader head; + ::memcpy(&head.type, outFile, 4); + outFile += 4; + ::memcpy(&head.length, outFile, 2); + outFile += 2; + AI_LSWAP2(head.length); + AI_LSWAP4(head.type); + return head; +} + +///////////////////////////////////////////////////////////////////////////////// +//! Read the file header and return the type of the file and its size +//! @param outFile Pointer to the file data. The buffer must at +//! least be 12 bytes large. +//! @param fileType Receives the type of the file +//! @return 0 if everything was OK, otherwise an error message +///////////////////////////////////////////////////////////////////////////////// +inline const char* ReadHeader(uint8_t* outFile, uint32_t& fileType) +{ + ChunkHeader head = LoadChunk(outFile); + if(AI_IFF_FOURCC_FORM != head.type) + { + return "The file is not an IFF file: FORM chunk is missing"; + } + ::memcpy(&fileType, outFile, 4); + AI_LSWAP4(fileType); + return 0; +} + + +}} + +#endif // !! AI_IFF_H_INCLUDED diff --git a/libs/assimp/code/Common/IOSystem.cpp b/libs/assimp/code/Common/IOSystem.cpp new file mode 100644 index 0000000..1e63827 --- /dev/null +++ b/libs/assimp/code/Common/IOSystem.cpp @@ -0,0 +1,53 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ +/** @file Default implementation of IOSystem using the standard C file functions */ + +#include <assimp/IOSystem.hpp> + +using namespace Assimp; + +const std::string &IOSystem::CurrentDirectory() const { + if ( m_pathStack.empty() ) { + static const std::string Dummy = std::string(); + return Dummy; + } + return m_pathStack[ m_pathStack.size()-1 ]; +} diff --git a/libs/assimp/code/Common/Importer.cpp b/libs/assimp/code/Common/Importer.cpp new file mode 100644 index 0000000..52b6097 --- /dev/null +++ b/libs/assimp/code/Common/Importer.cpp @@ -0,0 +1,1287 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ + +/** @file Importer.cpp + * @brief Implementation of the CPP-API class #Importer + */ + +#include <assimp/version.h> +#include <assimp/config.h> +#include <assimp/importerdesc.h> + +// ------------------------------------------------------------------------------------------------ +/* Uncomment this line to prevent Assimp from catching unknown exceptions. + * + * Note that any Exception except DeadlyImportError may lead to + * undefined behaviour -> loaders could remain in an unusable state and + * further imports with the same Importer instance could fail/crash/burn ... + */ +// ------------------------------------------------------------------------------------------------ +#ifndef ASSIMP_BUILD_DEBUG +# define ASSIMP_CATCH_GLOBAL_EXCEPTIONS +#endif + +// ------------------------------------------------------------------------------------------------ +// Internal headers +// ------------------------------------------------------------------------------------------------ +#include "Common/Importer.h" +#include "Common/BaseProcess.h" +#include "Common/DefaultProgressHandler.h" +#include "PostProcessing/ProcessHelper.h" +#include "Common/ScenePreprocessor.h" +#include "Common/ScenePrivate.h" + +#include <assimp/BaseImporter.h> +#include <assimp/GenericProperty.h> +#include <assimp/MemoryIOWrapper.h> +#include <assimp/Profiler.h> +#include <assimp/TinyFormatter.h> +#include <assimp/Exceptional.h> +#include <assimp/Profiler.h> +#include <assimp/commonMetaData.h> + +#include <exception> +#include <set> +#include <memory> +#include <cctype> + +#include <assimp/DefaultIOStream.h> +#include <assimp/DefaultIOSystem.h> + +#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS +# include "PostProcessing/ValidateDataStructure.h" +#endif + +using namespace Assimp::Profiling; +using namespace Assimp::Formatter; + +namespace Assimp { + // ImporterRegistry.cpp + void GetImporterInstanceList(std::vector< BaseImporter* >& out); + void DeleteImporterInstanceList(std::vector< BaseImporter* >& out); + + // PostStepRegistry.cpp + void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out); +} + +using namespace Assimp; +using namespace Assimp::Intern; + +// ------------------------------------------------------------------------------------------------ +// Intern::AllocateFromAssimpHeap serves as abstract base class. It overrides +// new and delete (and their array counterparts) of public API classes (e.g. Logger) to +// utilize our DLL heap. +// See http://www.gotw.ca/publications/mill15.htm +// ------------------------------------------------------------------------------------------------ +void* AllocateFromAssimpHeap::operator new ( size_t num_bytes) { + return ::operator new(num_bytes); +} + +void* AllocateFromAssimpHeap::operator new ( size_t num_bytes, const std::nothrow_t& ) throw() { + try { + return AllocateFromAssimpHeap::operator new( num_bytes ); + } + catch( ... ) { + return nullptr; + } +} + +void AllocateFromAssimpHeap::operator delete ( void* data) { + return ::operator delete(data); +} + +void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes) { + return ::operator new[](num_bytes); +} + +void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes, const std::nothrow_t& ) throw() { + try { + return AllocateFromAssimpHeap::operator new[]( num_bytes ); + } catch( ... ) { + return nullptr; + } +} + +void AllocateFromAssimpHeap::operator delete[] ( void* data) { + return ::operator delete[](data); +} + +// ------------------------------------------------------------------------------------------------ +// Importer constructor. +Importer::Importer() + : pimpl( new ImporterPimpl ) { + pimpl->mScene = nullptr; + pimpl->mErrorString = std::string(); + + // Allocate a default IO handler + pimpl->mIOHandler = new DefaultIOSystem; + pimpl->mIsDefaultHandler = true; + pimpl->bExtraVerbose = false; // disable extra verbose mode by default + + pimpl->mProgressHandler = new DefaultProgressHandler(); + pimpl->mIsDefaultProgressHandler = true; + + GetImporterInstanceList(pimpl->mImporter); + GetPostProcessingStepInstanceList(pimpl->mPostProcessingSteps); + + // Allocate a SharedPostProcessInfo object and store pointers to it in all post-process steps in the list. + pimpl->mPPShared = new SharedPostProcessInfo(); + for (std::vector<BaseProcess*>::iterator it = pimpl->mPostProcessingSteps.begin(); + it != pimpl->mPostProcessingSteps.end(); + ++it) { + + (*it)->SetSharedData(pimpl->mPPShared); + } +} + +// ------------------------------------------------------------------------------------------------ +// Destructor of Importer +Importer::~Importer() { + // Delete all import plugins + DeleteImporterInstanceList(pimpl->mImporter); + + // Delete all post-processing plug-ins + for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); ++a ) { + delete pimpl->mPostProcessingSteps[a]; + } + + // Delete the assigned IO and progress handler + delete pimpl->mIOHandler; + delete pimpl->mProgressHandler; + + // Kill imported scene. Destructor's should do that recursively + delete pimpl->mScene; + + // Delete shared post-processing data + delete pimpl->mPPShared; + + // and finally the pimpl itself + delete pimpl; +} + +// ------------------------------------------------------------------------------------------------ +// Register a custom post-processing step +aiReturn Importer::RegisterPPStep(BaseProcess* pImp) { + ai_assert( nullptr != pImp ); + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + pimpl->mPostProcessingSteps.push_back(pImp); + ASSIMP_LOG_INFO("Registering custom post-processing step"); + + ASSIMP_END_EXCEPTION_REGION(aiReturn); + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +// Register a custom loader plugin +aiReturn Importer::RegisterLoader(BaseImporter* pImp) { + ai_assert(nullptr != pImp); + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // -------------------------------------------------------------------- + // Check whether we would have two loaders for the same file extension + // This is absolutely OK, but we should warn the developer of the new + // loader that his code will probably never be called if the first + // loader is a bit too lazy in his file checking. + // -------------------------------------------------------------------- + std::set<std::string> st; + std::string baked; + pImp->GetExtensionList(st); + + for(std::set<std::string>::const_iterator it = st.begin(); it != st.end(); ++it) { + +#ifdef ASSIMP_BUILD_DEBUG + if (IsExtensionSupported(*it)) { + ASSIMP_LOG_WARN("The file extension ", *it, " is already in use"); + } +#endif + baked += *it; + } + + // add the loader + pimpl->mImporter.push_back(pImp); + ASSIMP_LOG_INFO("Registering custom importer for these file extensions: ", baked); + ASSIMP_END_EXCEPTION_REGION(aiReturn); + + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +// Unregister a custom loader plugin +aiReturn Importer::UnregisterLoader(BaseImporter* pImp) { + if(!pImp) { + // unregistering a nullptr importer is no problem for us ... really! + return AI_SUCCESS; + } + + ASSIMP_BEGIN_EXCEPTION_REGION(); + std::vector<BaseImporter*>::iterator it = std::find(pimpl->mImporter.begin(), + pimpl->mImporter.end(),pImp); + + if (it != pimpl->mImporter.end()) { + pimpl->mImporter.erase(it); + ASSIMP_LOG_INFO("Unregistering custom importer: "); + return AI_SUCCESS; + } + ASSIMP_LOG_WARN("Unable to remove custom importer: I can't find you ..."); + ASSIMP_END_EXCEPTION_REGION(aiReturn); + + return AI_FAILURE; +} + +// ------------------------------------------------------------------------------------------------ +// Unregister a custom loader plugin +aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) { + if(!pImp) { + // unregistering a nullptr ppstep is no problem for us ... really! + return AI_SUCCESS; + } + + ASSIMP_BEGIN_EXCEPTION_REGION(); + std::vector<BaseProcess*>::iterator it = std::find(pimpl->mPostProcessingSteps.begin(), + pimpl->mPostProcessingSteps.end(),pImp); + + if (it != pimpl->mPostProcessingSteps.end()) { + pimpl->mPostProcessingSteps.erase(it); + ASSIMP_LOG_INFO("Unregistering custom post-processing step"); + return AI_SUCCESS; + } + ASSIMP_LOG_WARN("Unable to remove custom post-processing step: I can't find you .."); + ASSIMP_END_EXCEPTION_REGION(aiReturn); + + return AI_FAILURE; +} + +// ------------------------------------------------------------------------------------------------ +// Supplies a custom IO handler to the importer to open and access files. +void Importer::SetIOHandler( IOSystem* pIOHandler) { + ai_assert(nullptr != pimpl); + + ASSIMP_BEGIN_EXCEPTION_REGION(); + // If the new handler is zero, allocate a default IO implementation. + if (!pIOHandler) { + // Release pointer in the possession of the caller + pimpl->mIOHandler = new DefaultIOSystem(); + pimpl->mIsDefaultHandler = true; + } else if (pimpl->mIOHandler != pIOHandler) { // Otherwise register the custom handler + delete pimpl->mIOHandler; + pimpl->mIOHandler = pIOHandler; + pimpl->mIsDefaultHandler = false; + } + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Get the currently set IO handler +IOSystem* Importer::GetIOHandler() const { + ai_assert(nullptr != pimpl); + + return pimpl->mIOHandler; +} + +// ------------------------------------------------------------------------------------------------ +// Check whether a custom IO handler is currently set +bool Importer::IsDefaultIOHandler() const { + ai_assert(nullptr != pimpl); + + return pimpl->mIsDefaultHandler; +} + +// ------------------------------------------------------------------------------------------------ +// Supplies a custom progress handler to get regular callbacks during importing +void Importer::SetProgressHandler(ProgressHandler* pHandler) { + ai_assert(nullptr != pimpl); + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // If the new handler is zero, allocate a default implementation. + if (!pHandler) { + // Release pointer in the possession of the caller + pimpl->mProgressHandler = new DefaultProgressHandler(); + pimpl->mIsDefaultProgressHandler = true; + } else if (pimpl->mProgressHandler != pHandler) { // Otherwise register the custom handler + delete pimpl->mProgressHandler; + pimpl->mProgressHandler = pHandler; + pimpl->mIsDefaultProgressHandler = false; + } + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Get the currently set progress handler +ProgressHandler* Importer::GetProgressHandler() const { + ai_assert(nullptr != pimpl); + + return pimpl->mProgressHandler; +} + +// ------------------------------------------------------------------------------------------------ +// Check whether a custom progress handler is currently set +bool Importer::IsDefaultProgressHandler() const { + ai_assert(nullptr != pimpl); + + return pimpl->mIsDefaultProgressHandler; +} + +// ------------------------------------------------------------------------------------------------ +// Validate post process step flags +bool _ValidateFlags(unsigned int pFlags) { + if (pFlags & aiProcess_GenSmoothNormals && pFlags & aiProcess_GenNormals) { + ASSIMP_LOG_ERROR("#aiProcess_GenSmoothNormals and #aiProcess_GenNormals are incompatible"); + return false; + } + if (pFlags & aiProcess_OptimizeGraph && pFlags & aiProcess_PreTransformVertices) { + ASSIMP_LOG_ERROR("#aiProcess_OptimizeGraph and #aiProcess_PreTransformVertices are incompatible"); + return false; + } + return true; +} + +// ------------------------------------------------------------------------------------------------ +// Free the current scene +void Importer::FreeScene( ) { + ai_assert(nullptr != pimpl); + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + delete pimpl->mScene; + pimpl->mScene = nullptr; + + pimpl->mErrorString = std::string(); + pimpl->mException = std::exception_ptr(); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Get the current error string, if any +const char* Importer::GetErrorString() const { + ai_assert(nullptr != pimpl); + + // Must remain valid as long as ReadFile() or FreeFile() are not called + return pimpl->mErrorString.c_str(); +} + +const std::exception_ptr& Importer::GetException() const { + ai_assert(nullptr != pimpl); + + // Must remain valid as long as ReadFile() or FreeFile() are not called + return pimpl->mException; +} + +// ------------------------------------------------------------------------------------------------ +// Enable extra-verbose mode +void Importer::SetExtraVerbose(bool bDo) { + ai_assert(nullptr != pimpl); + + pimpl->bExtraVerbose = bDo; +} + +// ------------------------------------------------------------------------------------------------ +// Get the current scene +const aiScene* Importer::GetScene() const { + ai_assert(nullptr != pimpl); + + return pimpl->mScene; +} + +// ------------------------------------------------------------------------------------------------ +// Orphan the current scene and return it. +aiScene* Importer::GetOrphanedScene() { + ai_assert(nullptr != pimpl); + + aiScene* s = pimpl->mScene; + + ASSIMP_BEGIN_EXCEPTION_REGION(); + pimpl->mScene = nullptr; + + pimpl->mErrorString = std::string(); + pimpl->mException = std::exception_ptr(); + ASSIMP_END_EXCEPTION_REGION(aiScene*); + + return s; +} + +// ------------------------------------------------------------------------------------------------ +// Validate post-processing flags +bool Importer::ValidateFlags(unsigned int pFlags) const { + ASSIMP_BEGIN_EXCEPTION_REGION(); + // run basic checks for mutually exclusive flags + if(!_ValidateFlags(pFlags)) { + return false; + } + + // ValidateDS does not anymore occur in the pp list, it plays an awesome extra role ... +#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + if (pFlags & aiProcess_ValidateDataStructure) { + return false; + } +#endif + pFlags &= ~aiProcess_ValidateDataStructure; + + // Now iterate through all bits which are set in the flags and check whether we find at least + // one pp plugin which handles it. + for (unsigned int mask = 1; mask < (1u << (sizeof(unsigned int)*8-1));mask <<= 1) { + + if (pFlags & mask) { + + bool have = false; + for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { + if (pimpl->mPostProcessingSteps[a]-> IsActive(mask) ) { + + have = true; + break; + } + } + if (!have) { + return false; + } + } + } + ASSIMP_END_EXCEPTION_REGION(bool); + return true; +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* Importer::ReadFileFromMemory( const void* pBuffer, + size_t pLength, + unsigned int pFlags, + const char* pHint /*= ""*/) { + ai_assert(nullptr != pimpl); + + ASSIMP_BEGIN_EXCEPTION_REGION(); + if (!pHint) { + pHint = ""; + } + + if (!pBuffer || !pLength || strlen(pHint) > MaxLenHint ) { + pimpl->mErrorString = "Invalid parameters passed to ReadFileFromMemory()"; + return nullptr; + } + + // prevent deletion of the previous IOHandler + IOSystem* io = pimpl->mIOHandler; + pimpl->mIOHandler = nullptr; + + SetIOHandler(new MemoryIOSystem((const uint8_t*)pBuffer,pLength,io)); + + // read the file and recover the previous IOSystem + static const size_t BufSize(Importer::MaxLenHint + 28); + char fbuff[BufSize]; + ai_snprintf(fbuff, BufSize, "%s.%s",AI_MEMORYIO_MAGIC_FILENAME,pHint); + + ReadFile(fbuff,pFlags); + SetIOHandler(io); + + ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString, pimpl->mException); + return pimpl->mScene; +} + +// ------------------------------------------------------------------------------------------------ +void WriteLogOpening(const std::string& file) { + + ASSIMP_LOG_INFO("Load ", file); + + // print a full version dump. This is nice because we don't + // need to ask the authors of incoming bug reports for + // the library version they're using - a log dump is + // sufficient. + const unsigned int flags = aiGetCompileFlags(); + std::stringstream stream; + stream << "Assimp " << aiGetVersionMajor() << "." << aiGetVersionMinor() << "." << aiGetVersionRevision() << " " +#if defined(ASSIMP_BUILD_ARCHITECTURE) + << ASSIMP_BUILD_ARCHITECTURE +#elif defined(_M_IX86) || defined(__x86_32__) || defined(__i386__) + << "x86" +#elif defined(_M_X64) || defined(__x86_64__) + << "amd64" +#elif defined(_M_IA64) || defined(__ia64__) + << "itanium" +#elif defined(__ppc__) || defined(__powerpc__) + << "ppc32" +#elif defined(__powerpc64__) + << "ppc64" +#elif defined(__arm__) + << "arm" +#else + << "<unknown architecture>" +#endif + << " " +#if defined(ASSIMP_BUILD_COMPILER) + << (ASSIMP_BUILD_COMPILER) +#elif defined(_MSC_VER) + << "msvc" +#elif defined(__GNUC__) + << "gcc" +#elif defined(__clang__) + << "clang" +#elif defined(__EMSCRIPTEN__) + << "emscripten" +#elif defined(__MINGW32__) + << "MinGW-w64 32bit" +#elif defined(__MINGW64__) + << "MinGW-w64 64bit" +#else + << "<unknown compiler>" +#endif + +#ifdef ASSIMP_BUILD_DEBUG + << " debug" +#endif + + << (flags & ASSIMP_CFLAGS_NOBOOST ? " noboost" : "") + << (flags & ASSIMP_CFLAGS_SHARED ? " shared" : "") + << (flags & ASSIMP_CFLAGS_SINGLETHREADED ? " singlethreaded" : "") + << (flags & ASSIMP_CFLAGS_DOUBLE_SUPPORT ? " double : " : "single : "); + + ASSIMP_LOG_DEBUG(stream.str()); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the given file and returns its contents if successful. +const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) { + ai_assert(nullptr != pimpl); + + ASSIMP_BEGIN_EXCEPTION_REGION(); + const std::string pFile(_pFile); + + // ---------------------------------------------------------------------- + // Put a large try block around everything to catch all std::exception's + // that might be thrown by STL containers or by new(). + // ImportErrorException's are throw by ourselves and caught elsewhere. + //----------------------------------------------------------------------- + + WriteLogOpening(pFile); + +#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS + try +#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS + { + // Check whether this Importer instance has already loaded + // a scene. In this case we need to delete the old one + if (pimpl->mScene) { + + ASSIMP_LOG_DEBUG("(Deleting previous scene)"); + FreeScene(); + } + + // First check if the file is accessible at all + if( !pimpl->mIOHandler->Exists( pFile)) { + + pimpl->mErrorString = "Unable to open file \"" + pFile + "\"."; + ASSIMP_LOG_ERROR(pimpl->mErrorString); + return nullptr; + } + + std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME, 0) ? new Profiler() : nullptr); + if (profiler) { + profiler->BeginRegion("total"); + } + + // Find an worker class which can handle the file extension. + // Multiple importers may be able to handle the same extension (.xml!); gather them all. + SetPropertyInteger("importerIndex", -1); + struct ImporterAndIndex { + BaseImporter * importer; + unsigned int index; + }; + std::vector<ImporterAndIndex> possibleImporters; + for (unsigned int a = 0; a < pimpl->mImporter.size(); a++) { + + // Every importer has a list of supported extensions. + std::set<std::string> extensions; + pimpl->mImporter[a]->GetExtensionList(extensions); + + // CAUTION: Do not just search for the extension! + // GetExtension() returns the part after the *last* dot, but some extensions have dots + // inside them, e.g. ogre.mesh.xml. Compare the entire end of the string. + for (std::set<std::string>::const_iterator it = extensions.cbegin(); it != extensions.cend(); ++it) { + + // Yay for C++<20 not having std::string::ends_with() + std::string extension = "." + *it; + if (extension.length() <= pFile.length()) { + // Possible optimization: Fetch the lowercase filename! + if (0 == ASSIMP_stricmp(pFile.c_str() + pFile.length() - extension.length(), extension.c_str())) { + ImporterAndIndex candidate = { pimpl->mImporter[a], a }; + possibleImporters.push_back(candidate); + break; + } + } + + } + + } + + // If just one importer supports this extension, pick it and close the case. + BaseImporter* imp = nullptr; + if (1 == possibleImporters.size()) { + imp = possibleImporters[0].importer; + SetPropertyInteger("importerIndex", possibleImporters[0].index); + } + // If multiple importers claim this file extension, ask them to look at the actual file data to decide. + // This can happen e.g. with XML (COLLADA vs. Irrlicht). + else { + for (std::vector<ImporterAndIndex>::const_iterator it = possibleImporters.begin(); it < possibleImporters.end(); ++it) { + BaseImporter & importer = *it->importer; + + ASSIMP_LOG_INFO("Found a possible importer: " + std::string(importer.GetInfo()->mName) + "; trying signature-based detection"); + if (importer.CanRead( pFile, pimpl->mIOHandler, true)) { + imp = &importer; + SetPropertyInteger("importerIndex", it->index); + break; + } + + } + + } + + if (!imp) { + // not so bad yet ... try format auto detection. + ASSIMP_LOG_INFO("File extension not known, trying signature-based detection"); + for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { + if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) { + imp = pimpl->mImporter[a]; + SetPropertyInteger("importerIndex", a); + break; + } + } + // Put a proper error message if no suitable importer was found + if( !imp) { + pimpl->mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\"."; + ASSIMP_LOG_ERROR(pimpl->mErrorString); + return nullptr; + } + } + + // Get file size for progress handler + IOStream * fileIO = pimpl->mIOHandler->Open( pFile ); + uint32_t fileSize = 0; + if (fileIO) + { + fileSize = static_cast<uint32_t>(fileIO->FileSize()); + pimpl->mIOHandler->Close( fileIO ); + } + + // Dispatch the reading to the worker class for this format + const aiImporterDesc *desc( imp->GetInfo() ); + std::string ext( "unknown" ); + if ( nullptr != desc ) { + ext = desc->mName; + } + ASSIMP_LOG_INFO("Found a matching importer for this file format: ", ext, "." ); + pimpl->mProgressHandler->UpdateFileRead( 0, fileSize ); + + if (profiler) { + profiler->BeginRegion("import"); + } + + pimpl->mScene = imp->ReadFile( this, pFile, pimpl->mIOHandler); + pimpl->mProgressHandler->UpdateFileRead( fileSize, fileSize ); + + if (profiler) { + profiler->EndRegion("import"); + } + + SetPropertyString("sourceFilePath", pFile); + + // If successful, apply all active post processing steps to the imported data + if( pimpl->mScene) { + if (!pimpl->mScene->mMetaData || !pimpl->mScene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT)) { + if (!pimpl->mScene->mMetaData) { + pimpl->mScene->mMetaData = new aiMetadata; + } + pimpl->mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT, aiString(ext)); + } + +#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + // The ValidateDS process is an exception. It is executed first, even before ScenePreprocessor is called. + if (pFlags & aiProcess_ValidateDataStructure) { + ValidateDSProcess ds; + ds.ExecuteOnScene (this); + if (!pimpl->mScene) { + return nullptr; + } + } +#endif // no validation + + // Preprocess the scene and prepare it for post-processing + if (profiler) { + profiler->BeginRegion("preprocess"); + } + + ScenePreprocessor pre(pimpl->mScene); + pre.ProcessScene(); + + if (profiler) { + profiler->EndRegion("preprocess"); + } + + // Ensure that the validation process won't be called twice + ApplyPostProcessing(pFlags & (~aiProcess_ValidateDataStructure)); + } + // if failed, extract the error string + else if( !pimpl->mScene) { + pimpl->mErrorString = imp->GetErrorText(); + pimpl->mException = imp->GetException(); + } + + // clear any data allocated by post-process steps + pimpl->mPPShared->Clean(); + + if (profiler) { + profiler->EndRegion("total"); + } + } +#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS + catch (std::exception &e) { +#if (defined _MSC_VER) && (defined _CPPRTTI) + // if we have RTTI get the full name of the exception that occurred + pimpl->mErrorString = std::string(typeid( e ).name()) + ": " + e.what(); +#else + pimpl->mErrorString = std::string("std::exception: ") + e.what(); +#endif + + ASSIMP_LOG_ERROR(pimpl->mErrorString); + delete pimpl->mScene; pimpl->mScene = nullptr; + } +#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS + + // either successful or failure - the pointer expresses it anyways + ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString, pimpl->mException); + + return pimpl->mScene; +} + + +// ------------------------------------------------------------------------------------------------ +// Apply post-processing to the currently bound scene +const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) { + ai_assert(nullptr != pimpl); + + ASSIMP_BEGIN_EXCEPTION_REGION(); + // Return immediately if no scene is active + if (!pimpl->mScene) { + return nullptr; + } + + // If no flags are given, return the current scene with no further action + if (!pFlags) { + return pimpl->mScene; + } + + // In debug builds: run basic flag validation + ai_assert(_ValidateFlags(pFlags)); + ASSIMP_LOG_INFO("Entering post processing pipeline"); + +#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + // The ValidateDS process plays an exceptional role. It isn't contained in the global + // list of post-processing steps, so we need to call it manually. + if (pFlags & aiProcess_ValidateDataStructure) { + ValidateDSProcess ds; + ds.ExecuteOnScene (this); + if (!pimpl->mScene) { + return nullptr; + } + } +#endif // no validation +#ifdef ASSIMP_BUILD_DEBUG + if (pimpl->bExtraVerbose) + { +#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + ASSIMP_LOG_ERROR("Verbose Import is not available due to build settings"); +#endif // no validation + pFlags |= aiProcess_ValidateDataStructure; + } +#else + if (pimpl->bExtraVerbose) { + ASSIMP_LOG_WARN("Not a debug build, ignoring extra verbose setting"); + } +#endif // ! DEBUG + + std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME, 0) ? new Profiler() : nullptr); + for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { + BaseProcess* process = pimpl->mPostProcessingSteps[a]; + pimpl->mProgressHandler->UpdatePostProcess(static_cast<int>(a), static_cast<int>(pimpl->mPostProcessingSteps.size()) ); + if( process->IsActive( pFlags)) { + if (profiler) { + profiler->BeginRegion("postprocess"); + } + + process->ExecuteOnScene ( this ); + + if (profiler) { + profiler->EndRegion("postprocess"); + } + } + if( !pimpl->mScene) { + break; + } +#ifdef ASSIMP_BUILD_DEBUG + +#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + continue; +#endif // no validation + + // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step + if (pimpl->bExtraVerbose) { + ASSIMP_LOG_DEBUG("Verbose Import: re-validating data structures"); + + ValidateDSProcess ds; + ds.ExecuteOnScene (this); + if( !pimpl->mScene) { + ASSIMP_LOG_ERROR("Verbose Import: failed to re-validate data structures"); + break; + } + } +#endif // ! DEBUG + } + pimpl->mProgressHandler->UpdatePostProcess( static_cast<int>(pimpl->mPostProcessingSteps.size()), + static_cast<int>(pimpl->mPostProcessingSteps.size()) ); + + // update private scene flags + if( pimpl->mScene ) { + ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags; + } + + // clear any data allocated by post-process steps + pimpl->mPPShared->Clean(); + ASSIMP_LOG_INFO("Leaving post processing pipeline"); + + ASSIMP_END_EXCEPTION_REGION(const aiScene*); + + return pimpl->mScene; +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) { + ai_assert(nullptr != pimpl); + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // Return immediately if no scene is active + if ( nullptr == pimpl->mScene ) { + return nullptr; + } + + // If no flags are given, return the current scene with no further action + if (nullptr == rootProcess) { + return pimpl->mScene; + } + + // In debug builds: run basic flag validation + ASSIMP_LOG_INFO( "Entering customized post processing pipeline" ); + +#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + // The ValidateDS process plays an exceptional role. It isn't contained in the global + // list of post-processing steps, so we need to call it manually. + if ( requestValidation ) + { + ValidateDSProcess ds; + ds.ExecuteOnScene( this ); + if ( !pimpl->mScene ) { + return nullptr; + } + } +#endif // no validation +#ifdef ASSIMP_BUILD_DEBUG + if ( pimpl->bExtraVerbose ) + { +#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + ASSIMP_LOG_ERROR( "Verbose Import is not available due to build settings" ); +#endif // no validation + } +#else + if ( pimpl->bExtraVerbose ) { + ASSIMP_LOG_WARN( "Not a debug build, ignoring extra verbose setting" ); + } +#endif // ! DEBUG + + std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME, 0) ? new Profiler() : nullptr); + + if ( profiler ) { + profiler->BeginRegion( "postprocess" ); + } + + rootProcess->ExecuteOnScene( this ); + + if ( profiler ) { + profiler->EndRegion( "postprocess" ); + } + + // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step + if ( pimpl->bExtraVerbose || requestValidation ) { + ASSIMP_LOG_DEBUG( "Verbose Import: revalidating data structures" ); + + ValidateDSProcess ds; + ds.ExecuteOnScene( this ); + if ( !pimpl->mScene ) { + ASSIMP_LOG_ERROR( "Verbose Import: failed to revalidate data structures" ); + } + } + + // clear any data allocated by post-process steps + pimpl->mPPShared->Clean(); + ASSIMP_LOG_INFO( "Leaving customized post processing pipeline" ); + + ASSIMP_END_EXCEPTION_REGION( const aiScene* ); + + return pimpl->mScene; +} + +// ------------------------------------------------------------------------------------------------ +// Helper function to check whether an extension is supported by ASSIMP +bool Importer::IsExtensionSupported(const char* szExtension) const { + return nullptr != GetImporter(szExtension); +} + +// ------------------------------------------------------------------------------------------------ +size_t Importer::GetImporterCount() const { + ai_assert(nullptr != pimpl); + + return pimpl->mImporter.size(); +} + +// ------------------------------------------------------------------------------------------------ +const aiImporterDesc* Importer::GetImporterInfo(size_t index) const { + ai_assert(nullptr != pimpl); + + if (index >= pimpl->mImporter.size()) { + return nullptr; + } + return pimpl->mImporter[index]->GetInfo(); +} + + +// ------------------------------------------------------------------------------------------------ +BaseImporter* Importer::GetImporter (size_t index) const { + ai_assert(nullptr != pimpl); + + if (index >= pimpl->mImporter.size()) { + return nullptr; + } + return pimpl->mImporter[index]; +} + +// ------------------------------------------------------------------------------------------------ +// Find a loader plugin for a given file extension +BaseImporter* Importer::GetImporter (const char* szExtension) const { + ai_assert(nullptr != pimpl); + + return GetImporter(GetImporterIndex(szExtension)); +} + +// ------------------------------------------------------------------------------------------------ +// Find a loader plugin for a given file extension +size_t Importer::GetImporterIndex (const char* szExtension) const { + ai_assert(nullptr != pimpl); + ai_assert(nullptr != szExtension); + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // skip over wild-card and dot characters at string head -- + for ( ; *szExtension == '*' || *szExtension == '.'; ++szExtension ); + + std::string ext(szExtension); + if (ext.empty()) { + return static_cast<size_t>(-1); + } + ext = ai_tolower(ext); + std::set<std::string> str; + for (std::vector<BaseImporter*>::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) { + str.clear(); + + (*i)->GetExtensionList(str); + for (std::set<std::string>::const_iterator it = str.begin(); it != str.end(); ++it) { + if (ext == *it) { + return std::distance(static_cast< std::vector<BaseImporter*>::const_iterator >(pimpl->mImporter.begin()), i); + } + } + } + ASSIMP_END_EXCEPTION_REGION(size_t); + return static_cast<size_t>(-1); +} + +// ------------------------------------------------------------------------------------------------ +// Helper function to build a list of all file extensions supported by ASSIMP +void Importer::GetExtensionList(aiString& szOut) const { + ai_assert(nullptr != pimpl); + + ASSIMP_BEGIN_EXCEPTION_REGION(); + std::set<std::string> str; + for (std::vector<BaseImporter*>::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) { + (*i)->GetExtensionList(str); + } + + // List can be empty + if( !str.empty() ) { + for (std::set<std::string>::const_iterator it = str.begin();; ) { + szOut.Append("*."); + szOut.Append((*it).c_str()); + + if (++it == str.end()) { + break; + } + szOut.Append(";"); + } + } + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool Importer::SetPropertyInteger(const char* szName, int iValue) { + ai_assert(nullptr != pimpl); + + bool existing; + ASSIMP_BEGIN_EXCEPTION_REGION(); + existing = SetGenericProperty<int>(pimpl->mIntProperties, szName,iValue); + ASSIMP_END_EXCEPTION_REGION(bool); + return existing; +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) { + ai_assert(nullptr != pimpl); + + bool existing; + ASSIMP_BEGIN_EXCEPTION_REGION(); + existing = SetGenericProperty<ai_real>(pimpl->mFloatProperties, szName,iValue); + ASSIMP_END_EXCEPTION_REGION(bool); + return existing; +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool Importer::SetPropertyString(const char* szName, const std::string& value) { + ai_assert(nullptr != pimpl); + + bool existing; + ASSIMP_BEGIN_EXCEPTION_REGION(); + existing = SetGenericProperty<std::string>(pimpl->mStringProperties, szName,value); + ASSIMP_END_EXCEPTION_REGION(bool); + return existing; +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) { + ai_assert(nullptr != pimpl); + + bool existing; + ASSIMP_BEGIN_EXCEPTION_REGION(); + existing = SetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties, szName,value); + ASSIMP_END_EXCEPTION_REGION(bool); + return existing; +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool Importer::SetPropertyPointer(const char* szName, void* value) { + ai_assert(nullptr != pimpl); + + bool existing; + ASSIMP_BEGIN_EXCEPTION_REGION(); + existing = SetGenericProperty<void*>(pimpl->mPointerProperties, szName,value); + ASSIMP_END_EXCEPTION_REGION(bool); + return existing; +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const { + ai_assert(nullptr != pimpl); + + return GetGenericProperty<int>(pimpl->mIntProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +ai_real Importer::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const { + ai_assert(nullptr != pimpl); + + return GetGenericProperty<ai_real>(pimpl->mFloatProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +std::string Importer::GetPropertyString(const char* szName, const std::string& iErrorReturn /*= ""*/) const { + ai_assert(nullptr != pimpl); + + return GetGenericProperty<std::string>(pimpl->mStringProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const { + ai_assert(nullptr != pimpl); + + return GetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +void* Importer::GetPropertyPointer(const char* szName, void* iErrorReturn /*= nullptr*/) const { + ai_assert(nullptr != pimpl); + + return GetGenericProperty<void*>(pimpl->mPointerProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get the memory requirements of a single node +inline +void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) { + if ( nullptr == pcNode ) { + return; + } + iScene += sizeof(aiNode); + iScene += sizeof(unsigned int) * pcNode->mNumMeshes; + iScene += sizeof(void*) * pcNode->mNumChildren; + + for (unsigned int i = 0; i < pcNode->mNumChildren;++i) { + AddNodeWeight(iScene,pcNode->mChildren[i]); + } +} + +// ------------------------------------------------------------------------------------------------ +// Get the memory requirements of the scene +void Importer::GetMemoryRequirements(aiMemoryInfo& in) const { + ai_assert(nullptr != pimpl); + + in = aiMemoryInfo(); + aiScene* mScene = pimpl->mScene; + + // return if we have no scene loaded + if (!mScene) + return; + + in.total = sizeof(aiScene); + + // add all meshes + for (unsigned int i = 0; i < mScene->mNumMeshes;++i) { + in.meshes += sizeof(aiMesh); + if (mScene->mMeshes[i]->HasPositions()) { + in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; + } + + if (mScene->mMeshes[i]->HasNormals()) { + in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; + } + + if (mScene->mMeshes[i]->HasTangentsAndBitangents()) { + in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices * 2; + } + + for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a) { + if (mScene->mMeshes[i]->HasVertexColors(a)) { + in.meshes += sizeof(aiColor4D) * mScene->mMeshes[i]->mNumVertices; + } else { + break; + } + } + for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) { + if (mScene->mMeshes[i]->HasTextureCoords(a)) { + in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; + } else { + break; + } + } + if (mScene->mMeshes[i]->HasBones()) { + in.meshes += sizeof(void*) * mScene->mMeshes[i]->mNumBones; + for (unsigned int p = 0; p < mScene->mMeshes[i]->mNumBones;++p) { + in.meshes += sizeof(aiBone); + in.meshes += mScene->mMeshes[i]->mBones[p]->mNumWeights * sizeof(aiVertexWeight); + } + } + in.meshes += (sizeof(aiFace) + 3 * sizeof(unsigned int))*mScene->mMeshes[i]->mNumFaces; + } + in.total += in.meshes; + + // add all embedded textures + for (unsigned int i = 0; i < mScene->mNumTextures;++i) { + const aiTexture* pc = mScene->mTextures[i]; + in.textures += sizeof(aiTexture); + if (pc->mHeight) { + in.textures += 4 * pc->mHeight * pc->mWidth; + } else { + in.textures += pc->mWidth; + } + } + in.total += in.textures; + + // add all animations + for (unsigned int i = 0; i < mScene->mNumAnimations;++i) { + const aiAnimation* pc = mScene->mAnimations[i]; + in.animations += sizeof(aiAnimation); + + // add all bone anims + for (unsigned int a = 0; a < pc->mNumChannels; ++a) { + const aiNodeAnim* pc2 = pc->mChannels[a]; + in.animations += sizeof(aiNodeAnim); + in.animations += pc2->mNumPositionKeys * sizeof(aiVectorKey); + in.animations += pc2->mNumScalingKeys * sizeof(aiVectorKey); + in.animations += pc2->mNumRotationKeys * sizeof(aiQuatKey); + } + } + in.total += in.animations; + + // add all cameras and all lights + in.total += in.cameras = sizeof(aiCamera) * mScene->mNumCameras; + in.total += in.lights = sizeof(aiLight) * mScene->mNumLights; + + // add all nodes + AddNodeWeight(in.nodes,mScene->mRootNode); + in.total += in.nodes; + + // add all materials + for (unsigned int i = 0; i < mScene->mNumMaterials;++i) { + const aiMaterial* pc = mScene->mMaterials[i]; + in.materials += sizeof(aiMaterial); + in.materials += pc->mNumAllocated * sizeof(void*); + + for (unsigned int a = 0; a < pc->mNumProperties;++a) { + in.materials += pc->mProperties[a]->mDataLength; + } + } + + in.total += in.materials; +} diff --git a/libs/assimp/code/Common/Importer.h b/libs/assimp/code/Common/Importer.h new file mode 100644 index 0000000..ab7e3a6 --- /dev/null +++ b/libs/assimp/code/Common/Importer.h @@ -0,0 +1,250 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +/** @file Importer.h mostly internal stuff for use by #Assimp::Importer */ +#pragma once +#ifndef INCLUDED_AI_IMPORTER_H +#define INCLUDED_AI_IMPORTER_H + +#include <exception> +#include <map> +#include <vector> +#include <string> +#include <assimp/matrix4x4.h> + +struct aiScene; + +namespace Assimp { + class ProgressHandler; + class IOSystem; + class BaseImporter; + class BaseProcess; + class SharedPostProcessInfo; + + +//! @cond never +// --------------------------------------------------------------------------- +/** @brief Internal PIMPL implementation for Assimp::Importer + * + * Using this idiom here allows us to drop the dependency from + * std::vector and std::map in the public headers. Furthermore we are dropping + * any STL interface problems caused by mismatching STL settings. All + * size calculation are now done by us, not the app heap. */ +class ImporterPimpl { +public: + // Data type to store the key hash + typedef unsigned int KeyType; + + // typedefs for our configuration maps. + typedef std::map<KeyType, int> IntPropertyMap; + typedef std::map<KeyType, ai_real> FloatPropertyMap; + typedef std::map<KeyType, std::string> StringPropertyMap; + typedef std::map<KeyType, aiMatrix4x4> MatrixPropertyMap; + typedef std::map<KeyType, void*> PointerPropertyMap; + + /** IO handler to use for all file accesses. */ + IOSystem* mIOHandler; + bool mIsDefaultHandler; + + /** Progress handler for feedback. */ + ProgressHandler* mProgressHandler; + bool mIsDefaultProgressHandler; + + /** Format-specific importer worker objects - one for each format we can read.*/ + std::vector< BaseImporter* > mImporter; + + /** Post processing steps we can apply at the imported data. */ + std::vector< BaseProcess* > mPostProcessingSteps; + + /** The imported data, if ReadFile() was successful, nullptr otherwise. */ + aiScene* mScene; + + /** The error description, if there was one. In the case of an exception, + * mException will carry the full details. */ + std::string mErrorString; + + /** Any exception which occurred */ + std::exception_ptr mException; + + /** List of integer properties */ + IntPropertyMap mIntProperties; + + /** List of floating-point properties */ + FloatPropertyMap mFloatProperties; + + /** List of string properties */ + StringPropertyMap mStringProperties; + + /** List of Matrix properties */ + MatrixPropertyMap mMatrixProperties; + + /** List of pointer properties */ + PointerPropertyMap mPointerProperties; + + /** Used for testing - extra verbose mode causes the ValidateDataStructure-Step + * to be executed before and after every single post-process step */ + bool bExtraVerbose; + + /** Used by post-process steps to share data */ + SharedPostProcessInfo* mPPShared; + + /// The default class constructor. + ImporterPimpl() AI_NO_EXCEPT; +}; + +inline +ImporterPimpl::ImporterPimpl() AI_NO_EXCEPT : + mIOHandler( nullptr ), + mIsDefaultHandler( false ), + mProgressHandler( nullptr ), + mIsDefaultProgressHandler( false ), + mImporter(), + mPostProcessingSteps(), + mScene( nullptr ), + mErrorString(), + mException(), + mIntProperties(), + mFloatProperties(), + mStringProperties(), + mMatrixProperties(), + mPointerProperties(), + bExtraVerbose( false ), + mPPShared( nullptr ) { + // empty +} +//! @endcond + +struct BatchData; + +// --------------------------------------------------------------------------- +/** FOR IMPORTER PLUGINS ONLY: A helper class to the pleasure of importers + * that need to load many external meshes recursively. + * + * The class uses several threads to load these meshes (or at least it + * could, this has not yet been implemented at the moment). + * + * @note The class may not be used by more than one thread*/ +class ASSIMP_API BatchLoader { +public: + //! @cond never + // ------------------------------------------------------------------- + /** Wraps a full list of configuration properties for an importer. + * Properties can be set using SetGenericProperty */ + struct PropertyMap { + ImporterPimpl::IntPropertyMap ints; + ImporterPimpl::FloatPropertyMap floats; + ImporterPimpl::StringPropertyMap strings; + ImporterPimpl::MatrixPropertyMap matrices; + + bool operator == (const PropertyMap& prop) const { + // fixme: really isocpp? gcc complains + return ints == prop.ints && floats == prop.floats && strings == prop.strings && matrices == prop.matrices; + } + + bool empty () const { + return ints.empty() && floats.empty() && strings.empty() && matrices.empty(); + } + }; + //! @endcond + + // ------------------------------------------------------------------- + /** Construct a batch loader from a given IO system to be used + * to access external files + */ + explicit BatchLoader(IOSystem* pIO, bool validate = false ); + + // ------------------------------------------------------------------- + /** The class destructor. + */ + ~BatchLoader(); + + // ------------------------------------------------------------------- + /** Sets the validation step. True for enable validation during postprocess. + * @param enable True for validation. + */ + void setValidation( bool enabled ); + + // ------------------------------------------------------------------- + /** Returns the current validation step. + * @return The current validation step. + */ + bool getValidation() const; + + // ------------------------------------------------------------------- + /** Add a new file to the list of files to be loaded. + * @param file File to be loaded + * @param steps Post-processing steps to be executed on the file + * @param map Optional configuration properties + * @return 'Load request channel' - an unique ID that can later + * be used to access the imported file data. + * @see GetImport */ + unsigned int AddLoadRequest ( + const std::string& file, + unsigned int steps = 0, + const PropertyMap *map = nullptr + ); + + // ------------------------------------------------------------------- + /** Get an imported scene. + * This polls the import from the internal request list. + * If an import is requested several times, this function + * can be called several times, too. + * + * @param which LRWC returned by AddLoadRequest(). + * @return nullptr if there is no scene with this file name + * in the queue of the scene hasn't been loaded yet. */ + aiScene* GetImport( + unsigned int which + ); + + // ------------------------------------------------------------------- + /** Waits until all scenes have been loaded. This returns + * immediately if no scenes are queued.*/ + void LoadAll(); + +private: + // No need to have that in the public API ... + BatchData *m_data; +}; + +} // Namespace Assimp + +#endif // INCLUDED_AI_IMPORTER_H diff --git a/libs/assimp/code/Common/ImporterRegistry.cpp b/libs/assimp/code/Common/ImporterRegistry.cpp new file mode 100644 index 0000000..78c02d9 --- /dev/null +++ b/libs/assimp/code/Common/ImporterRegistry.cpp @@ -0,0 +1,393 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ + +/** @file ImporterRegistry.cpp + +Central registry for all importers available. Do not edit this file +directly (unless you are adding new loaders), instead use the +corresponding preprocessor flag to selectively disable formats. +*/ + +#include <assimp/anim.h> +#include <assimp/BaseImporter.h> +#include <vector> +#include <cstdlib> + +// ------------------------------------------------------------------------------------------------ +// Importers +// (include_new_importers_here) +// ------------------------------------------------------------------------------------------------ +#ifndef ASSIMP_BUILD_NO_X_IMPORTER +#include "AssetLib/X/XFileImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER +#include "AssetLib/AMF/AMFImporter.hpp" +#endif +#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER +#include "AssetLib/3DS/3DSLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_MD3_IMPORTER +#include "AssetLib/MD3/MD3Loader.h" +#endif +#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER +#include "AssetLib/MDL/MDLLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_MD2_IMPORTER +#include "AssetLib/MD2/MD2Loader.h" +#endif +#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER +#include "AssetLib/Ply/PlyLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER +#include "AssetLib/ASE/ASELoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER +#include "AssetLib/Obj/ObjFileImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_HMP_IMPORTER +#include "AssetLib/HMP/HMPLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER +#include "AssetLib/SMD/SMDLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_MDC_IMPORTER +#include "AssetLib/MDC/MDCLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER +#include "AssetLib/MD5/MD5Loader.h" +#endif +#ifndef ASSIMP_BUILD_NO_STL_IMPORTER +#include "AssetLib/STL/STLLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER +#include "AssetLib/LWO/LWOLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER +#include "AssetLib/DXF/DXFLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_NFF_IMPORTER +#include "AssetLib/NFF/NFFLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER +#include "AssetLib/Raw/RawLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_SIB_IMPORTER +#include "AssetLib/SIB/SIBImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_OFF_IMPORTER +#include "AssetLib/OFF/OFFLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_AC_IMPORTER +#include "AssetLib/AC/ACLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER +#include "AssetLib/BVH/BVHLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER +#include "AssetLib/Irr/IRRMeshLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER +#include "AssetLib/Irr/IRRLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER +#include "AssetLib/Q3D/Q3DLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER +#include "AssetLib/B3D/B3DImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER +#include "AssetLib/Collada/ColladaLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_TERRAGEN_IMPORTER +#include "AssetLib/Terragen/TerragenLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_CSM_IMPORTER +#include "AssetLib/CSM/CSMLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_3D_IMPORTER +#include "AssetLib/Unreal/UnrealLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_LWS_IMPORTER +#include "AssetLib/LWS/LWSLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER +#include "AssetLib/Ogre/OgreImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_OPENGEX_IMPORTER +#include "AssetLib/OpenGEX/OpenGEXImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER +#include "AssetLib/MS3D/MS3DLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_COB_IMPORTER +#include "AssetLib/COB/COBLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER +#include "AssetLib/Blender/BlenderLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER +#include "AssetLib/Q3BSP/Q3BSPFileImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_NDO_IMPORTER +#include "AssetLib/NDO/NDOLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER +#include "AssetLib/IFC/IFCLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_XGL_IMPORTER +#include "AssetLib/XGL/XGLLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER +#include "AssetLib/FBX/FBXImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER +#include "AssetLib/Assbin/AssbinLoader.h" +#endif +#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF1_IMPORTER) +#include "AssetLib/glTF/glTFImporter.h" +#endif +#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER) +#include "AssetLib/glTF2/glTF2Importer.h" +#endif +#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER +#include "AssetLib/C4D/C4DImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER +#include "AssetLib/3MF/D3MFImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER +#include "AssetLib/X3D/X3DImporter.hpp" +#endif +#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER +#include "AssetLib/MMD/MMDImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER +#include "AssetLib/M3D/M3DImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_IQM_IMPORTER +#include "AssetLib/IQM/IQMImporter.h" +#endif + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +void GetImporterInstanceList(std::vector<BaseImporter *> &out) { + + // Some importers may be unimplemented or otherwise unsuitable for general use + // in their current state. Devs can set ASSIMP_ENABLE_DEV_IMPORTERS in their + // local environment to enable them, otherwise they're left out of the registry. + const char *envStr = std::getenv("ASSIMP_ENABLE_DEV_IMPORTERS"); + bool devImportersEnabled = envStr && strcmp(envStr, "0"); + + // Ensure no unused var warnings if all uses are #ifndef'd away below: + (void)devImportersEnabled; + + // ---------------------------------------------------------------------------- + // Add an instance of each worker class here + // (register_new_importers_here) + // ---------------------------------------------------------------------------- + out.reserve(64); +#if (!defined ASSIMP_BUILD_NO_X_IMPORTER) + out.push_back(new XFileImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_OBJ_IMPORTER) + out.push_back(new ObjFileImporter()); +#endif +#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER + out.push_back(new AMFImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER) + out.push_back(new Discreet3DSImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_M3D_IMPORTER) + out.push_back(new M3DImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_MD3_IMPORTER) + out.push_back(new MD3Importer()); +#endif +#if (!defined ASSIMP_BUILD_NO_MD2_IMPORTER) + out.push_back(new MD2Importer()); +#endif +#if (!defined ASSIMP_BUILD_NO_PLY_IMPORTER) + out.push_back(new PLYImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_MDL_IMPORTER) + out.push_back(new MDLImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_ASE_IMPORTER) +#if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER) + out.push_back(new ASEImporter()); +#endif +#endif +#if (!defined ASSIMP_BUILD_NO_HMP_IMPORTER) + out.push_back(new HMPImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_SMD_IMPORTER) + out.push_back(new SMDImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_MDC_IMPORTER) + out.push_back(new MDCImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_MD5_IMPORTER) + out.push_back(new MD5Importer()); +#endif +#if (!defined ASSIMP_BUILD_NO_STL_IMPORTER) + out.push_back(new STLImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_LWO_IMPORTER) + out.push_back(new LWOImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_DXF_IMPORTER) + out.push_back(new DXFImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_NFF_IMPORTER) + out.push_back(new NFFImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_RAW_IMPORTER) + out.push_back(new RAWImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_SIB_IMPORTER) + out.push_back(new SIBImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_OFF_IMPORTER) + out.push_back(new OFFImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_AC_IMPORTER) + out.push_back(new AC3DImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_BVH_IMPORTER) + out.push_back(new BVHLoader()); +#endif +#if (!defined ASSIMP_BUILD_NO_IRRMESH_IMPORTER) + out.push_back(new IRRMeshImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_IRR_IMPORTER) + out.push_back(new IRRImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_Q3D_IMPORTER) + out.push_back(new Q3DImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_B3D_IMPORTER) + out.push_back(new B3DImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_COLLADA_IMPORTER) + out.push_back(new ColladaLoader()); +#endif +#if (!defined ASSIMP_BUILD_NO_TERRAGEN_IMPORTER) + out.push_back(new TerragenImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_CSM_IMPORTER) + out.push_back(new CSMImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_3D_IMPORTER) + out.push_back(new UnrealImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_LWS_IMPORTER) + out.push_back(new LWSImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_OGRE_IMPORTER) + out.push_back(new Ogre::OgreImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_OPENGEX_IMPORTER) + out.push_back(new OpenGEX::OpenGEXImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_MS3D_IMPORTER) + out.push_back(new MS3DImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_COB_IMPORTER) + out.push_back(new COBImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_BLEND_IMPORTER) + out.push_back(new BlenderImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_Q3BSP_IMPORTER) + out.push_back(new Q3BSPFileImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_NDO_IMPORTER) + out.push_back(new NDOImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_IFC_IMPORTER) + out.push_back(new IFCImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_XGL_IMPORTER) + out.push_back(new XGLImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_FBX_IMPORTER) + out.push_back(new FBXImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_ASSBIN_IMPORTER) + out.push_back(new AssbinImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_GLTF_IMPORTER && !defined ASSIMP_BUILD_NO_GLTF1_IMPORTER) + out.push_back(new glTFImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_GLTF_IMPORTER && !defined ASSIMP_BUILD_NO_GLTF2_IMPORTER) + out.push_back(new glTF2Importer()); +#endif +#if (!defined ASSIMP_BUILD_NO_C4D_IMPORTER) + out.push_back(new C4DImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_3MF_IMPORTER) + out.push_back(new D3MFImporter()); +#endif +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + out.push_back(new X3DImporter()); +#endif +#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER + out.push_back(new MMDImporter()); +#endif +#ifndef ASSIMP_BUILD_NO_IQM_IMPORTER + out.push_back(new IQMImporter()); +#endif + //#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER + // out.push_back(new StepFile::StepFileImporter()); + //#endif +} + +/** will delete all registered importers. */ +void DeleteImporterInstanceList(std::vector<BaseImporter *> &deleteList) { + for (size_t i = 0; i < deleteList.size(); ++i) { + delete deleteList[i]; + deleteList[i] = nullptr; + } //for +} + +} // namespace Assimp diff --git a/libs/assimp/code/Common/PolyTools.h b/libs/assimp/code/Common/PolyTools.h new file mode 100644 index 0000000..11f6273 --- /dev/null +++ b/libs/assimp/code/Common/PolyTools.h @@ -0,0 +1,226 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +/** @file PolyTools.h, various utilities for our dealings with arbitrary polygons */ + +#pragma once +#ifndef AI_POLYTOOLS_H_INCLUDED +#define AI_POLYTOOLS_H_INCLUDED + +#include <assimp/material.h> +#include <assimp/ai_assert.h> + +namespace Assimp { + +// ------------------------------------------------------------------------------- +/** Compute the signed area of a triangle. + * The function accepts an unconstrained template parameter for use with + * both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ +template <typename T> +inline double GetArea2D(const T& v1, const T& v2, const T& v3) { + return 0.5 * (v1.x * ((double)v3.y - v2.y) + v2.x * ((double)v1.y - v3.y) + v3.x * ((double)v2.y - v1.y)); +} + +// ------------------------------------------------------------------------------- +/** Test if a given point p2 is on the left side of the line formed by p0-p1. + * The function accepts an unconstrained template parameter for use with + * both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ +template <typename T> +inline bool OnLeftSideOfLine2D(const T& p0, const T& p1,const T& p2) { + return GetArea2D(p0,p2,p1) > 0; +} + +// ------------------------------------------------------------------------------- +/** Test if a given point is inside a given triangle in R2. + * The function accepts an unconstrained template parameter for use with + * both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ +template <typename T> +inline bool PointInTriangle2D(const T& p0, const T& p1,const T& p2, const T& pp) { + // Point in triangle test using baryzentric coordinates + const aiVector2D v0 = p1 - p0; + const aiVector2D v1 = p2 - p0; + const aiVector2D v2 = pp - p0; + + double dot00 = v0 * v0; + double dot11 = v1 * v1; + const double dot01 = v0 * v1; + const double dot02 = v0 * v2; + const double dot12 = v1 * v2; + const double denom = dot00 * dot11 - dot01 * dot01; + if (denom == 0.0) { + return false; + } + + const double invDenom = 1.0 / denom; + dot11 = (dot11 * dot02 - dot01 * dot12) * invDenom; + dot00 = (dot00 * dot12 - dot01 * dot02) * invDenom; + + return (dot11 > 0) && (dot00 > 0) && (dot11 + dot00 < 1); +} + + +// ------------------------------------------------------------------------------- +/** Check whether the winding order of a given polygon is counter-clockwise. + * The function accepts an unconstrained template parameter, but is intended + * to be used only with aiVector2D and aiVector3D (z axis is ignored, only + * x and y are taken into account). + * @note Code taken from http://cgm.cs.mcgill.ca/~godfried/teaching/cg-projects/97/Ian/applet1.html and translated to C++ + */ +template <typename T> +inline bool IsCCW(T* in, size_t npoints) { + double aa, bb, cc, b, c, theta; + double convex_turn; + double convex_sum = 0; + + ai_assert(npoints >= 3); + + for (size_t i = 0; i < npoints - 2; i++) { + aa = ((in[i+2].x - in[i].x) * (in[i+2].x - in[i].x)) + + ((-in[i+2].y + in[i].y) * (-in[i+2].y + in[i].y)); + + bb = ((in[i+1].x - in[i].x) * (in[i+1].x - in[i].x)) + + ((-in[i+1].y + in[i].y) * (-in[i+1].y + in[i].y)); + + cc = ((in[i+2].x - in[i+1].x) * + (in[i+2].x - in[i+1].x)) + + ((-in[i+2].y + in[i+1].y) * + (-in[i+2].y + in[i+1].y)); + + b = std::sqrt(bb); + c = std::sqrt(cc); + theta = std::acos((bb + cc - aa) / (2 * b * c)); + + if (OnLeftSideOfLine2D(in[i],in[i+2],in[i+1])) { + // if (convex(in[i].x, in[i].y, + // in[i+1].x, in[i+1].y, + // in[i+2].x, in[i+2].y)) { + convex_turn = AI_MATH_PI_F - theta; + convex_sum += convex_turn; + } else { + convex_sum -= AI_MATH_PI_F - theta; + } + } + aa = ((in[1].x - in[npoints-2].x) * + (in[1].x - in[npoints-2].x)) + + ((-in[1].y + in[npoints-2].y) * + (-in[1].y + in[npoints-2].y)); + + bb = ((in[0].x - in[npoints-2].x) * + (in[0].x - in[npoints-2].x)) + + ((-in[0].y + in[npoints-2].y) * + (-in[0].y + in[npoints-2].y)); + + cc = ((in[1].x - in[0].x) * (in[1].x - in[0].x)) + + ((-in[1].y + in[0].y) * (-in[1].y + in[0].y)); + + b = std::sqrt(bb); + c = std::sqrt(cc); + theta = std::acos((bb + cc - aa) / (2 * b * c)); + + //if (convex(in[npoints-2].x, in[npoints-2].y, + // in[0].x, in[0].y, + // in[1].x, in[1].y)) { + if (OnLeftSideOfLine2D(in[npoints-2],in[1],in[0])) { + convex_turn = AI_MATH_PI_F - theta; + convex_sum += convex_turn; + } else { + convex_sum -= AI_MATH_PI_F - theta; + } + + return convex_sum >= (2 * AI_MATH_PI_F); +} + +// ------------------------------------------------------------------------------- +/** Compute the normal of an arbitrary polygon in R3. + * + * The code is based on Newell's formula, that is a polygons normal is the ratio + * of its area when projected onto the three coordinate axes. + * + * @param out Receives the output normal + * @param num Number of input vertices + * @param x X data source. x[ofs_x*n] is the n'th element. + * @param y Y data source. y[ofs_y*n] is the y'th element + * @param z Z data source. z[ofs_z*n] is the z'th element + * + * @note The data arrays must have storage for at least num+2 elements. Using + * this method is much faster than the 'other' NewellNormal() + */ +template <int ofs_x, int ofs_y, int ofs_z, typename TReal> +inline void NewellNormal (aiVector3t<TReal>& out, int num, TReal* x, TReal* y, TReal* z) { + // Duplicate the first two vertices at the end + x[(num+0)*ofs_x] = x[0]; + x[(num+1)*ofs_x] = x[ofs_x]; + + y[(num+0)*ofs_y] = y[0]; + y[(num+1)*ofs_y] = y[ofs_y]; + + z[(num+0)*ofs_z] = z[0]; + z[(num+1)*ofs_z] = z[ofs_z]; + + TReal sum_xy = 0.0, sum_yz = 0.0, sum_zx = 0.0; + + TReal *xptr = x +ofs_x, *xlow = x, *xhigh = x + ofs_x*2; + TReal *yptr = y +ofs_y, *ylow = y, *yhigh = y + ofs_y*2; + TReal *zptr = z +ofs_z, *zlow = z, *zhigh = z + ofs_z*2; + + for (int tmp=0; tmp < num; tmp++) { + sum_xy += (*xptr) * ( (*yhigh) - (*ylow) ); + sum_yz += (*yptr) * ( (*zhigh) - (*zlow) ); + sum_zx += (*zptr) * ( (*xhigh) - (*xlow) ); + + xptr += ofs_x; + xlow += ofs_x; + xhigh += ofs_x; + + yptr += ofs_y; + ylow += ofs_y; + yhigh += ofs_y; + + zptr += ofs_z; + zlow += ofs_z; + zhigh += ofs_z; + } + out = aiVector3t<TReal>(sum_yz,sum_zx,sum_xy); +} + +} // ! namespace Assimp + +#endif // AI_POLYTOOLS_H_INCLUDED diff --git a/libs/assimp/code/Common/PostStepRegistry.cpp b/libs/assimp/code/Common/PostStepRegistry.cpp new file mode 100644 index 0000000..de4f390 --- /dev/null +++ b/libs/assimp/code/Common/PostStepRegistry.cpp @@ -0,0 +1,265 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ + +/** @file ImporterRegistry.cpp + +Central registry for all postprocessing steps available. Do not edit this file +directly (unless you are adding new steps), instead use the +corresponding preprocessor flag to selectively disable steps. +*/ + +#include "PostProcessing/ProcessHelper.h" + +#ifndef ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS +# include "PostProcessing/CalcTangentsProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS +# include "PostProcessing/JoinVerticesProcess.h" +#endif +#if !(defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS && defined ASSIMP_BUILD_NO_FLIPUVS_PROCESS && defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS) +# include "PostProcessing/ConvertToLHProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS +# include "PostProcessing/TriangulateProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_DROPFACENORMALS_PROCESS +# include "PostProcessing/DropFaceNormalsProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS +# include "PostProcessing/GenFaceNormalsProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS +# include "PostProcessing/GenVertexNormalsProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_REMOVEVC_PROCESS +# include "PostProcessing/RemoveVCProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS +# include "PostProcessing/SplitLargeMeshes.h" +#endif +#ifndef ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS +# include "PostProcessing/PretransformVertices.h" +#endif +#ifndef ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS +# include "PostProcessing/LimitBoneWeightsProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS +# include "PostProcessing/ValidateDataStructure.h" +#endif +#ifndef ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS +# include "PostProcessing/ImproveCacheLocality.h" +#endif +#ifndef ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS +# include "PostProcessing/FixNormalsStep.h" +#endif +#ifndef ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS +# include "PostProcessing/RemoveRedundantMaterials.h" +#endif +#if (!defined ASSIMP_BUILD_NO_EMBEDTEXTURES_PROCESS) +# include "PostProcessing/EmbedTexturesProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS +# include "PostProcessing/FindInvalidDataProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS +# include "PostProcessing/FindDegenerates.h" +#endif +#ifndef ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS +# include "PostProcessing/SortByPTypeProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS +# include "PostProcessing/ComputeUVMappingProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS +# include "PostProcessing/TextureTransform.h" +#endif +#ifndef ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS +# include "PostProcessing/FindInstancesProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS +# include "PostProcessing/OptimizeMeshes.h" +#endif +#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS +# include "PostProcessing/OptimizeGraph.h" +#endif +#ifndef ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS +# include "PostProcessing/SplitByBoneCountProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_DEBONE_PROCESS +# include "PostProcessing/DeboneProcess.h" +#endif +#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) +# include "PostProcessing/ScaleProcess.h" +#endif +#if (!defined ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS) +# include "PostProcessing/ArmaturePopulate.h" +#endif +#if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS) +# include "PostProcessing/GenBoundingBoxesProcess.h" +#endif + + + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out) +{ + // ---------------------------------------------------------------------------- + // Add an instance of each post processing step here in the order + // of sequence it is executed. Steps that are added here are not + // validated - as RegisterPPStep() does - all dependencies must be given. + // ---------------------------------------------------------------------------- + out.reserve(31); +#if (!defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS) + out.push_back( new MakeLeftHandedProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FLIPUVS_PROCESS) + out.push_back( new FlipUVsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS) + out.push_back( new FlipWindingOrderProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_REMOVEVC_PROCESS) + out.push_back( new RemoveVCProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS) + out.push_back( new RemoveRedundantMatsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_EMBEDTEXTURES_PROCESS) + out.push_back( new EmbedTexturesProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS) + out.push_back( new FindInstancesProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS) + out.push_back( new OptimizeGraphProcess()); +#endif +#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS + out.push_back( new ComputeUVMappingProcess()); +#endif +#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS + out.push_back( new TextureTransformStep()); +#endif +#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) + out.push_back( new ScaleProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS) + out.push_back( new ArmaturePopulate()); +#endif +#if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS) + out.push_back( new PretransformVertices()); +#endif +#if (!defined ASSIMP_BUILD_NO_TRIANGULATE_PROCESS) + out.push_back( new TriangulateProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS) + //find degenerates should run after triangulation (to sort out small + //generated triangles) but before sort by p types (in case there are lines + //and points generated and inserted into a mesh) + out.push_back( new FindDegeneratesProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS) + out.push_back( new SortByPTypeProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS) + out.push_back( new FindInvalidDataProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS) + out.push_back( new OptimizeMeshesProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS) + out.push_back( new FixInfacingNormalsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS) + out.push_back( new SplitByBoneCountProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS) + out.push_back( new SplitLargeMeshesProcess_Triangle()); +#endif +#if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS) + out.push_back( new DropFaceNormalsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS) + out.push_back( new GenFaceNormalsProcess()); +#endif + // ......................................................................... + // DON'T change the order of these five .. + // XXX this is actually a design weakness that dates back to the time + // when Importer would maintain the postprocessing step list exclusively. + // Now that others access it too, we need a better solution. + out.push_back( new ComputeSpatialSortProcess()); + // ......................................................................... + +#if (!defined ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS) + out.push_back( new GenVertexNormalsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS) + out.push_back( new CalcTangentsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_JOINVERTICES_PROCESS) + out.push_back( new JoinVerticesProcess()); +#endif + + // ......................................................................... + out.push_back( new DestroySpatialSortProcess()); + // ......................................................................... + +#if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS) + out.push_back( new SplitLargeMeshesProcess_Vertex()); +#endif +#if (!defined ASSIMP_BUILD_NO_DEBONE_PROCESS) + out.push_back( new DeboneProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS) + out.push_back( new LimitBoneWeightsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS) + out.push_back( new ImproveCacheLocalityProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS) + out.push_back(new GenBoundingBoxesProcess); +#endif +} + +} diff --git a/libs/assimp/code/Common/RemoveComments.cpp b/libs/assimp/code/Common/RemoveComments.cpp new file mode 100644 index 0000000..4fae21c --- /dev/null +++ b/libs/assimp/code/Common/RemoveComments.cpp @@ -0,0 +1,121 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +/** + * @file RemoveComments.cpp + * @brief Defines the CommentRemover utility class + */ + +#include <assimp/RemoveComments.h> +#include <assimp/ParsingUtils.h> + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +// Remove line comments from a file +void CommentRemover::RemoveLineComments(const char* szComment, char* szBuffer, char chReplacement /* = ' ' */) { + // validate parameters + ai_assert(nullptr != szComment); + ai_assert(nullptr != szBuffer); + ai_assert(*szComment); + + size_t len = strlen(szComment); + const size_t lenBuffer = strlen(szBuffer); + if (len > lenBuffer) { + len = lenBuffer; + } + + for(size_t i = 0; i < lenBuffer; i++) { + // skip over quotes + if (szBuffer[i] == '\"' || szBuffer[i] == '\'') + while (++i < lenBuffer && szBuffer[i] != '\"' && szBuffer[i] != '\''); + + if(lenBuffer - i < len) { + break; + } + + if (!strncmp(szBuffer + i,szComment,len)) { + while (i < lenBuffer && !IsLineEnd(szBuffer[i])) { + szBuffer[i++] = chReplacement; + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Remove multi-line comments from a file +void CommentRemover::RemoveMultiLineComments(const char* szCommentStart, + const char* szCommentEnd,char* szBuffer, + char chReplacement) { + // validate parameters + ai_assert(nullptr != szCommentStart); + ai_assert(nullptr != szCommentEnd); + ai_assert(nullptr != szBuffer); + ai_assert(*szCommentStart); + ai_assert(*szCommentEnd); + + const size_t len = strlen(szCommentEnd); + const size_t len2 = strlen(szCommentStart); + + while (*szBuffer) { + // skip over quotes + if (*szBuffer == '\"' || *szBuffer == '\'') { + while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\''); + } + + if (!strncmp(szBuffer,szCommentStart,len2)) { + while (*szBuffer) { + if (!::strncmp(szBuffer,szCommentEnd,len)) { + for (unsigned int i = 0; i < len;++i) { + *szBuffer++ = chReplacement; + } + + break; + } + *szBuffer++ = chReplacement; + } + continue; + } + ++szBuffer; + } +} + +} // !! Assimp diff --git a/libs/assimp/code/Common/SGSpatialSort.cpp b/libs/assimp/code/Common/SGSpatialSort.cpp new file mode 100644 index 0000000..0d16d6f --- /dev/null +++ b/libs/assimp/code/Common/SGSpatialSort.cpp @@ -0,0 +1,168 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the helper class to quickly find +vertices close to a given position. Special implementation for +the 3ds loader handling smooth groups correctly */ + +#include <assimp/SGSpatialSort.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +SGSpatialSort::SGSpatialSort() +{ + // define the reference plane. We choose some arbitrary vector away from all basic axes + // in the hope that no model spreads all its vertices along this plane. + mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f); + mPlaneNormal.Normalize(); +} +// ------------------------------------------------------------------------------------------------ +// Destructor +SGSpatialSort::~SGSpatialSort() +{ + // nothing to do here, everything destructs automatically +} +// ------------------------------------------------------------------------------------------------ +void SGSpatialSort::Add(const aiVector3D& vPosition, unsigned int index, + unsigned int smoothingGroup) +{ + // store position by index and distance + float distance = vPosition * mPlaneNormal; + mPositions.push_back( Entry( index, vPosition, + distance, smoothingGroup)); +} +// ------------------------------------------------------------------------------------------------ +void SGSpatialSort::Prepare() +{ + // now sort the array ascending by distance. + std::sort( this->mPositions.begin(), this->mPositions.end()); +} +// ------------------------------------------------------------------------------------------------ +// Returns an iterator for all positions close to the given position. +void SGSpatialSort::FindPositions( const aiVector3D& pPosition, + uint32_t pSG, + float pRadius, + std::vector<unsigned int>& poResults, + bool exactMatch /*= false*/) const +{ + float dist = pPosition * mPlaneNormal; + float minDist = dist - pRadius, maxDist = dist + pRadius; + + // clear the array + poResults.clear(); + + // quick check for positions outside the range + if( mPositions.empty() ) + return; + if( maxDist < mPositions.front().mDistance) + return; + if( minDist > mPositions.back().mDistance) + return; + + // do a binary search for the minimal distance to start the iteration there + unsigned int index = (unsigned int)mPositions.size() / 2; + unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4; + while( binaryStepSize > 1) + { + if( mPositions[index].mDistance < minDist) + index += binaryStepSize; + else + index -= binaryStepSize; + + binaryStepSize /= 2; + } + + // depending on the direction of the last step we need to single step a bit back or forth + // to find the actual beginning element of the range + while( index > 0 && mPositions[index].mDistance > minDist) + index--; + while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist) + index++; + + // Mow start iterating from there until the first position lays outside of the distance range. + // Add all positions inside the distance range within the given radius to the result array + + float squareEpsilon = pRadius * pRadius; + std::vector<Entry>::const_iterator it = mPositions.begin() + index; + std::vector<Entry>::const_iterator end = mPositions.end(); + + if (exactMatch) + { + while( it->mDistance < maxDist) + { + if((it->mPosition - pPosition).SquareLength() < squareEpsilon && it->mSmoothGroups == pSG) + { + poResults.push_back( it->mIndex); + } + ++it; + if( end == it )break; + } + } + else + { + // if the given smoothing group is 0, we'll return all surrounding vertices + if (!pSG) + { + while( it->mDistance < maxDist) + { + if((it->mPosition - pPosition).SquareLength() < squareEpsilon) + poResults.push_back( it->mIndex); + ++it; + if( end == it)break; + } + } + else while( it->mDistance < maxDist) + { + if((it->mPosition - pPosition).SquareLength() < squareEpsilon && + (it->mSmoothGroups & pSG || !it->mSmoothGroups)) + { + poResults.push_back( it->mIndex); + } + ++it; + if( end == it)break; + } + } +} + + diff --git a/libs/assimp/code/Common/SceneCombiner.cpp b/libs/assimp/code/Common/SceneCombiner.cpp new file mode 100644 index 0000000..2c2539e --- /dev/null +++ b/libs/assimp/code/Common/SceneCombiner.cpp @@ -0,0 +1,1375 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +// TODO: refactor entire file to get rid of the "flat-copy" first approach +// to copying structures. This easily breaks in the most unintuitive way +// possible as new fields are added to assimp structures. + +// ---------------------------------------------------------------------------- +/** + * @file Implements Assimp::SceneCombiner. This is a smart utility + * class that combines multiple scenes, meshes, ... into one. Currently + * these utilities are used by the IRR and LWS loaders and the + * OptimizeGraph step. + */ +// ---------------------------------------------------------------------------- +#include "ScenePrivate.h" +#include "time.h" +#include <assimp/Hash.h> +#include <assimp/SceneCombiner.h> +#include <assimp/StringUtils.h> +#include <assimp/fast_atof.h> +#include <assimp/mesh.h> +#include <assimp/metadata.h> +#include <assimp/scene.h> +#include <stdio.h> +#include <assimp/DefaultLogger.hpp> + +namespace Assimp { + +#if (__GNUC__ >= 8 && __GNUC_MINOR__ >= 0) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif + +// ------------------------------------------------------------------------------------------------ +// Add a prefix to a string +inline void PrefixString(aiString &string, const char *prefix, unsigned int len) { + // If the string is already prefixed, we won't prefix it a second time + if (string.length >= 1 && string.data[0] == '$') + return; + + if (len + string.length >= MAXLEN - 1) { + ASSIMP_LOG_VERBOSE_DEBUG("Can't add an unique prefix because the string is too long"); + ai_assert(false); + return; + } + + // Add the prefix + ::memmove(string.data + len, string.data, string.length + 1); + ::memcpy(string.data, prefix, len); + + // And update the string's length + string.length += len; +} + +// ------------------------------------------------------------------------------------------------ +// Add node identifiers to a hashing set +void SceneCombiner::AddNodeHashes(aiNode *node, std::set<unsigned int> &hashes) { + // Add node name to hashing set if it is non-empty - empty nodes are allowed + // and they can't have any anims assigned so its absolutely safe to duplicate them. + if (node->mName.length) { + hashes.insert(SuperFastHash(node->mName.data, static_cast<uint32_t>(node->mName.length))); + } + + // Process all children recursively + for (unsigned int i = 0; i < node->mNumChildren; ++i) { + AddNodeHashes(node->mChildren[i], hashes); + } +} + +// ------------------------------------------------------------------------------------------------ +// Add a name prefix to all nodes in a hierarchy +void SceneCombiner::AddNodePrefixes(aiNode *node, const char *prefix, unsigned int len) { + ai_assert(nullptr != prefix); + + PrefixString(node->mName, prefix, len); + + // Process all children recursively + for (unsigned int i = 0; i < node->mNumChildren; ++i) { + AddNodePrefixes(node->mChildren[i], prefix, len); + } +} + +// ------------------------------------------------------------------------------------------------ +// Search for matching names +bool SceneCombiner::FindNameMatch(const aiString &name, std::vector<SceneHelper> &input, unsigned int cur) { + const unsigned int hash = SuperFastHash(name.data, static_cast<uint32_t>(name.length)); + + // Check whether we find a positive match in one of the given sets + for (unsigned int i = 0; i < input.size(); ++i) { + if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) { + return true; + } + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Add a name prefix to all nodes in a hierarchy if a hash match is found +void SceneCombiner::AddNodePrefixesChecked(aiNode *node, const char *prefix, unsigned int len, + std::vector<SceneHelper> &input, unsigned int cur) { + ai_assert(nullptr != prefix); + + const unsigned int hash = SuperFastHash(node->mName.data, static_cast<uint32_t>(node->mName.length)); + + // Check whether we find a positive match in one of the given sets + for (unsigned int i = 0; i < input.size(); ++i) { + if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) { + PrefixString(node->mName, prefix, len); + break; + } + } + + // Process all children recursively + for (unsigned int i = 0; i < node->mNumChildren; ++i) { + AddNodePrefixesChecked(node->mChildren[i], prefix, len, input, cur); + } +} + +// ------------------------------------------------------------------------------------------------ +// Add an offset to all mesh indices in a node graph +void SceneCombiner::OffsetNodeMeshIndices(aiNode *node, unsigned int offset) { + for (unsigned int i = 0; i < node->mNumMeshes; ++i) + node->mMeshes[i] += offset; + + for (unsigned int i = 0; i < node->mNumChildren; ++i) { + OffsetNodeMeshIndices(node->mChildren[i], offset); + } +} + +// ------------------------------------------------------------------------------------------------ +// Merges two scenes. Currently only used by the LWS loader. +void SceneCombiner::MergeScenes(aiScene **_dest, std::vector<aiScene *> &src, unsigned int flags) { + if (nullptr == _dest) { + return; + } + + // if _dest points to nullptr allocate a new scene. Otherwise clear the old and reuse it + if (src.empty()) { + if (*_dest) { + (*_dest)->~aiScene(); + SceneCombiner::CopySceneFlat(_dest, src[0]); + } else + *_dest = src[0]; + return; + } + if (*_dest) { + (*_dest)->~aiScene(); + new (*_dest) aiScene(); + } else + *_dest = new aiScene(); + + // Create a dummy scene to serve as master for the others + aiScene *master = new aiScene(); + master->mRootNode = new aiNode(); + master->mRootNode->mName.Set("<MergeRoot>"); + + std::vector<AttachmentInfo> srcList(src.size()); + for (unsigned int i = 0; i < srcList.size(); ++i) { + srcList[i] = AttachmentInfo(src[i], master->mRootNode); + } + + // 'master' will be deleted afterwards + MergeScenes(_dest, master, srcList, flags); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::AttachToGraph(aiNode *attach, std::vector<NodeAttachmentInfo> &srcList) { + unsigned int cnt; + for (cnt = 0; cnt < attach->mNumChildren; ++cnt) { + AttachToGraph(attach->mChildren[cnt], srcList); + } + + cnt = 0; + for (std::vector<NodeAttachmentInfo>::iterator it = srcList.begin(); + it != srcList.end(); ++it) { + if ((*it).attachToNode == attach && !(*it).resolved) + ++cnt; + } + + if (cnt) { + aiNode **n = new aiNode *[cnt + attach->mNumChildren]; + if (attach->mNumChildren) { + ::memcpy(n, attach->mChildren, sizeof(void *) * attach->mNumChildren); + delete[] attach->mChildren; + } + attach->mChildren = n; + + n += attach->mNumChildren; + attach->mNumChildren += cnt; + + for (unsigned int i = 0; i < srcList.size(); ++i) { + NodeAttachmentInfo &att = srcList[i]; + if (att.attachToNode == attach && !att.resolved) { + *n = att.node; + (**n).mParent = attach; + ++n; + + // mark this attachment as resolved + att.resolved = true; + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::AttachToGraph(aiScene *master, std::vector<NodeAttachmentInfo> &src) { + ai_assert(nullptr != master); + + AttachToGraph(master->mRootNode, src); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::MergeScenes(aiScene **_dest, aiScene *master, std::vector<AttachmentInfo> &srcList, unsigned int flags) { + if (nullptr == _dest) { + return; + } + + // if _dest points to nullptr allocate a new scene. Otherwise clear the old and reuse it + if (srcList.empty()) { + if (*_dest) { + SceneCombiner::CopySceneFlat(_dest, master); + } else + *_dest = master; + return; + } + if (*_dest) { + (*_dest)->~aiScene(); + new (*_dest) aiScene(); + } else + *_dest = new aiScene(); + + aiScene *dest = *_dest; + + std::vector<SceneHelper> src(srcList.size() + 1); + src[0].scene = master; + for (unsigned int i = 0; i < srcList.size(); ++i) { + src[i + 1] = SceneHelper(srcList[i].scene); + } + + // this helper array specifies which scenes are duplicates of others + std::vector<unsigned int> duplicates(src.size(), UINT_MAX); + + // this helper array is used as lookup table several times + std::vector<unsigned int> offset(src.size()); + + // Find duplicate scenes + for (unsigned int i = 0; i < src.size(); ++i) { + if (duplicates[i] != i && duplicates[i] != UINT_MAX) { + continue; + } + + duplicates[i] = i; + for (unsigned int a = i + 1; a < src.size(); ++a) { + if (src[i].scene == src[a].scene) { + duplicates[a] = i; + } + } + } + + // Generate unique names for all named stuff? + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { +#if 0 + // Construct a proper random number generator + boost::mt19937 rng( ); + boost::uniform_int<> dist(1u,1 << 24u); + boost::variate_generator<boost::mt19937&, boost::uniform_int<> > rndGen(rng, dist); +#endif + for (unsigned int i = 1; i < src.size(); ++i) { + //if (i != duplicates[i]) + //{ + // // duplicate scenes share the same UID + // ::strcpy( src[i].id, src[duplicates[i]].id ); + // src[i].idlen = src[duplicates[i]].idlen; + + // continue; + //} + + src[i].idlen = ai_snprintf(src[i].id, 32, "$%.6X$_", i); + + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + + // Compute hashes for all identifiers in this scene and store them + // in a sorted table (for convenience I'm using std::set). We hash + // just the node and animation channel names, all identifiers except + // the material names should be caught by doing this. + AddNodeHashes(src[i]->mRootNode, src[i].hashes); + + for (unsigned int a = 0; a < src[i]->mNumAnimations; ++a) { + aiAnimation *anim = src[i]->mAnimations[a]; + src[i].hashes.insert(SuperFastHash(anim->mName.data, static_cast<uint32_t>(anim->mName.length))); + } + } + } + } + + unsigned int cnt; + + // First find out how large the respective output arrays must be + for (unsigned int n = 0; n < src.size(); ++n) { + SceneHelper *cur = &src[n]; + + if (n == duplicates[n] || flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) { + dest->mNumTextures += (*cur)->mNumTextures; + dest->mNumMaterials += (*cur)->mNumMaterials; + dest->mNumMeshes += (*cur)->mNumMeshes; + } + + dest->mNumLights += (*cur)->mNumLights; + dest->mNumCameras += (*cur)->mNumCameras; + dest->mNumAnimations += (*cur)->mNumAnimations; + + // Combine the flags of all scenes + // We need to process them flag-by-flag here to get correct results + // dest->mFlags ; //|= (*cur)->mFlags; + if ((*cur)->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { + dest->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; + } + } + + // generate the output texture list + an offset table for all texture indices + if (dest->mNumTextures) { + aiTexture **pip = dest->mTextures = new aiTexture *[dest->mNumTextures]; + cnt = 0; + for (unsigned int n = 0; n < src.size(); ++n) { + SceneHelper *cur = &src[n]; + for (unsigned int i = 0; i < (*cur)->mNumTextures; ++i) { + if (n != duplicates[n]) { + if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) + Copy(pip, (*cur)->mTextures[i]); + + else + continue; + } else + *pip = (*cur)->mTextures[i]; + ++pip; + } + + offset[n] = cnt; + cnt = (unsigned int)(pip - dest->mTextures); + } + } + + // generate the output material list + an offset table for all material indices + if (dest->mNumMaterials) { + aiMaterial **pip = dest->mMaterials = new aiMaterial *[dest->mNumMaterials]; + cnt = 0; + for (unsigned int n = 0; n < src.size(); ++n) { + SceneHelper *cur = &src[n]; + for (unsigned int i = 0; i < (*cur)->mNumMaterials; ++i) { + if (n != duplicates[n]) { + if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) + Copy(pip, (*cur)->mMaterials[i]); + + else + continue; + } else + *pip = (*cur)->mMaterials[i]; + + if ((*cur)->mNumTextures != dest->mNumTextures) { + // We need to update all texture indices of the mesh. So we need to search for + // a material property called '$tex.file' + + for (unsigned int a = 0; a < (*pip)->mNumProperties; ++a) { + aiMaterialProperty *prop = (*pip)->mProperties[a]; + if (!strncmp(prop->mKey.data, "$tex.file", 9)) { + // Check whether this texture is an embedded texture. + // In this case the property looks like this: *<n>, + // where n is the index of the texture. + // Copy here because we overwrite the string data in-place and the buffer inside of aiString + // will be a lie if we just reinterpret from prop->mData. The size of mData is not guaranteed to be + // MAXLEN in size. + aiString s(*(aiString *)prop->mData); + if ('*' == s.data[0]) { + // Offset the index and write it back .. + const unsigned int idx = strtoul10(&s.data[1]) + offset[n]; + const unsigned int oldLen = s.length; + + s.length = 1 + ASSIMP_itoa10(&s.data[1], sizeof(s.data) - 1, idx); + + // The string changed in size so we need to reallocate the buffer for the property. + if (oldLen < s.length) { + prop->mDataLength += s.length - oldLen; + delete[] prop->mData; + prop->mData = new char[prop->mDataLength]; + } + + memcpy(prop->mData, static_cast<void*>(&s), prop->mDataLength); + } + } + + // Need to generate new, unique material names? + else if (!::strcmp(prop->mKey.data, "$mat.name") && flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) { + aiString *pcSrc = (aiString *)prop->mData; + PrefixString(*pcSrc, (*cur).id, (*cur).idlen); + } + } + } + ++pip; + } + + offset[n] = cnt; + cnt = (unsigned int)(pip - dest->mMaterials); + } + } + + // generate the output mesh list + again an offset table for all mesh indices + if (dest->mNumMeshes) { + aiMesh **pip = dest->mMeshes = new aiMesh *[dest->mNumMeshes]; + cnt = 0; + for (unsigned int n = 0; n < src.size(); ++n) { + SceneHelper *cur = &src[n]; + for (unsigned int i = 0; i < (*cur)->mNumMeshes; ++i) { + if (n != duplicates[n]) { + if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) + Copy(pip, (*cur)->mMeshes[i]); + + else + continue; + } else + *pip = (*cur)->mMeshes[i]; + + // update the material index of the mesh + (*pip)->mMaterialIndex += offset[n]; + ++pip; + } + + // reuse the offset array - store now the mesh offset in it + offset[n] = cnt; + cnt = (unsigned int)(pip - dest->mMeshes); + } + } + + std::vector<NodeAttachmentInfo> nodes; + nodes.reserve(srcList.size()); + + // ---------------------------------------------------------------------------- + // Now generate the output node graph. We need to make those + // names in the graph that are referenced by anims or lights + // or cameras unique. So we add a prefix to them ... $<rand>_ + // We could also use a counter, but using a random value allows us to + // use just one prefix if we are joining multiple scene hierarchies recursively. + // Chances are quite good we don't collide, so we try that ... + // ---------------------------------------------------------------------------- + + // Allocate space for light sources, cameras and animations + aiLight **ppLights = dest->mLights = (dest->mNumLights ? new aiLight *[dest->mNumLights] : nullptr); + + aiCamera **ppCameras = dest->mCameras = (dest->mNumCameras ? new aiCamera *[dest->mNumCameras] : nullptr); + + aiAnimation **ppAnims = dest->mAnimations = (dest->mNumAnimations ? new aiAnimation *[dest->mNumAnimations] : nullptr); + + for (int n = static_cast<int>(src.size() - 1); n >= 0; --n) /* !!! important !!! */ + { + SceneHelper *cur = &src[n]; + aiNode *node; + + // To offset or not to offset, this is the question + if (n != (int)duplicates[n]) { + // Get full scene-graph copy + Copy(&node, (*cur)->mRootNode); + OffsetNodeMeshIndices(node, offset[duplicates[n]]); + + if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) { + // (note:) they are already 'offseted' by offset[duplicates[n]] + OffsetNodeMeshIndices(node, offset[n] - offset[duplicates[n]]); + } + } else // if (n == duplicates[n]) + { + node = (*cur)->mRootNode; + OffsetNodeMeshIndices(node, offset[n]); + } + if (n) // src[0] is the master node + nodes.push_back(NodeAttachmentInfo(node, srcList[n - 1].attachToNode, n)); + + // add name prefixes? + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { + + // or the whole scenegraph + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + AddNodePrefixesChecked(node, (*cur).id, (*cur).idlen, src, n); + } else + AddNodePrefixes(node, (*cur).id, (*cur).idlen); + + // meshes + for (unsigned int i = 0; i < (*cur)->mNumMeshes; ++i) { + aiMesh *mesh = (*cur)->mMeshes[i]; + + // rename all bones + for (unsigned int a = 0; a < mesh->mNumBones; ++a) { + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + if (!FindNameMatch(mesh->mBones[a]->mName, src, n)) + continue; + } + PrefixString(mesh->mBones[a]->mName, (*cur).id, (*cur).idlen); + } + } + } + + // -------------------------------------------------------------------- + // Copy light sources + for (unsigned int i = 0; i < (*cur)->mNumLights; ++i, ++ppLights) { + if (n != (int)duplicates[n]) // duplicate scene? + { + Copy(ppLights, (*cur)->mLights[i]); + } else + *ppLights = (*cur)->mLights[i]; + + // Add name prefixes? + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + if (!FindNameMatch((*ppLights)->mName, src, n)) + continue; + } + + PrefixString((*ppLights)->mName, (*cur).id, (*cur).idlen); + } + } + + // -------------------------------------------------------------------- + // Copy cameras + for (unsigned int i = 0; i < (*cur)->mNumCameras; ++i, ++ppCameras) { + if (n != (int)duplicates[n]) // duplicate scene? + { + Copy(ppCameras, (*cur)->mCameras[i]); + } else + *ppCameras = (*cur)->mCameras[i]; + + // Add name prefixes? + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + if (!FindNameMatch((*ppCameras)->mName, src, n)) + continue; + } + + PrefixString((*ppCameras)->mName, (*cur).id, (*cur).idlen); + } + } + + // -------------------------------------------------------------------- + // Copy animations + for (unsigned int i = 0; i < (*cur)->mNumAnimations; ++i, ++ppAnims) { + if (n != (int)duplicates[n]) // duplicate scene? + { + Copy(ppAnims, (*cur)->mAnimations[i]); + } else + *ppAnims = (*cur)->mAnimations[i]; + + // Add name prefixes? + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + if (!FindNameMatch((*ppAnims)->mName, src, n)) + continue; + } + + PrefixString((*ppAnims)->mName, (*cur).id, (*cur).idlen); + + // don't forget to update all node animation channels + for (unsigned int a = 0; a < (*ppAnims)->mNumChannels; ++a) { + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + if (!FindNameMatch((*ppAnims)->mChannels[a]->mNodeName, src, n)) + continue; + } + + PrefixString((*ppAnims)->mChannels[a]->mNodeName, (*cur).id, (*cur).idlen); + } + } + } + } + + // Now build the output graph + AttachToGraph(master, nodes); + dest->mRootNode = master->mRootNode; + + // Check whether we succeeded at building the output graph + for (std::vector<NodeAttachmentInfo>::iterator it = nodes.begin(); + it != nodes.end(); ++it) { + if (!(*it).resolved) { + if (flags & AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS) { + // search for this attachment point in all other imported scenes, too. + for (unsigned int n = 0; n < src.size(); ++n) { + if (n != (*it).src_idx) { + AttachToGraph(src[n].scene, nodes); + if ((*it).resolved) + break; + } + } + } + if (!(*it).resolved) { + ASSIMP_LOG_ERROR("SceneCombiner: Failed to resolve attachment ", (*it).node->mName.data, + " ", (*it).attachToNode->mName.data); + } + } + } + + // now delete all input scenes. Make sure duplicate scenes aren't + // deleted more than one time + for (unsigned int n = 0; n < src.size(); ++n) { + if (n != duplicates[n]) // duplicate scene? + continue; + + aiScene *deleteMe = src[n].scene; + + // We need to delete the arrays before the destructor is called - + // we are reusing the array members + delete[] deleteMe->mMeshes; + deleteMe->mMeshes = nullptr; + delete[] deleteMe->mCameras; + deleteMe->mCameras = nullptr; + delete[] deleteMe->mLights; + deleteMe->mLights = nullptr; + delete[] deleteMe->mMaterials; + deleteMe->mMaterials = nullptr; + delete[] deleteMe->mAnimations; + deleteMe->mAnimations = nullptr; + delete[] deleteMe->mTextures; + deleteMe->mTextures = nullptr; + + deleteMe->mRootNode = nullptr; + + // Now we can safely delete the scene + delete deleteMe; + } + + // Check flags + if (!dest->mNumMeshes || !dest->mNumMaterials) { + dest->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; + } + + // We're finished +} + +// ------------------------------------------------------------------------------------------------ +// Build a list of unique bones +void SceneCombiner::BuildUniqueBoneList(std::list<BoneWithHash> &asBones, + std::vector<aiMesh *>::const_iterator it, + std::vector<aiMesh *>::const_iterator end) { + unsigned int iOffset = 0; + for (; it != end; ++it) { + for (unsigned int l = 0; l < (*it)->mNumBones; ++l) { + aiBone *p = (*it)->mBones[l]; + uint32_t itml = SuperFastHash(p->mName.data, (unsigned int)p->mName.length); + + std::list<BoneWithHash>::iterator it2 = asBones.begin(); + std::list<BoneWithHash>::iterator end2 = asBones.end(); + + for (; it2 != end2; ++it2) { + if ((*it2).first == itml) { + (*it2).pSrcBones.push_back(BoneSrcIndex(p, iOffset)); + break; + } + } + if (end2 == it2) { + // need to begin a new bone entry + asBones.push_back(BoneWithHash()); + BoneWithHash &btz = asBones.back(); + + // setup members + btz.first = itml; + btz.second = &p->mName; + btz.pSrcBones.push_back(BoneSrcIndex(p, iOffset)); + } + } + iOffset += (*it)->mNumVertices; + } +} + +// ------------------------------------------------------------------------------------------------ +// Merge a list of bones +void SceneCombiner::MergeBones(aiMesh *out, std::vector<aiMesh *>::const_iterator it, + std::vector<aiMesh *>::const_iterator end) { + if (nullptr == out || out->mNumBones == 0) { + return; + } + + // find we need to build an unique list of all bones. + // we work with hashes to make the comparisons MUCH faster, + // at least if we have many bones. + std::list<BoneWithHash> asBones; + BuildUniqueBoneList(asBones, it, end); + + // now create the output bones + out->mNumBones = 0; + out->mBones = new aiBone *[asBones.size()]; + + for (std::list<BoneWithHash>::const_iterator boneIt = asBones.begin(), boneEnd = asBones.end(); boneIt != boneEnd; ++boneIt) { + // Allocate a bone and setup it's name + aiBone *pc = out->mBones[out->mNumBones++] = new aiBone(); + pc->mName = aiString(*(boneIt->second)); + + std::vector<BoneSrcIndex>::const_iterator wend = boneIt->pSrcBones.end(); + + // Loop through all bones to be joined for this bone + for (std::vector<BoneSrcIndex>::const_iterator wmit = boneIt->pSrcBones.begin(); wmit != wend; ++wmit) { + pc->mNumWeights += (*wmit).first->mNumWeights; + + // NOTE: different offset matrices for bones with equal names + // are - at the moment - not handled correctly. + if (wmit != boneIt->pSrcBones.begin() && pc->mOffsetMatrix != wmit->first->mOffsetMatrix) { + ASSIMP_LOG_WARN("Bones with equal names but different offset matrices can't be joined at the moment"); + continue; + } + pc->mOffsetMatrix = wmit->first->mOffsetMatrix; + } + + // Allocate the vertex weight array + aiVertexWeight *avw = pc->mWeights = new aiVertexWeight[pc->mNumWeights]; + + // And copy the final weights - adjust the vertex IDs by the + // face index offset of the corresponding mesh. + for (std::vector<BoneSrcIndex>::const_iterator wmit = (*boneIt).pSrcBones.begin(); wmit != (*boneIt).pSrcBones.end(); ++wmit) { + if (wmit == wend) { + break; + } + + aiBone *pip = (*wmit).first; + for (unsigned int mp = 0; mp < pip->mNumWeights; ++mp, ++avw) { + const aiVertexWeight &vfi = pip->mWeights[mp]; + avw->mWeight = vfi.mWeight; + avw->mVertexId = vfi.mVertexId + (*wmit).second; + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Merge a list of meshes +void SceneCombiner::MergeMeshes(aiMesh **_out, unsigned int /*flags*/, + std::vector<aiMesh *>::const_iterator begin, + std::vector<aiMesh *>::const_iterator end) { + if (nullptr == _out) { + return; + } + + if (begin == end) { + *_out = nullptr; // no meshes ... + return; + } + + // Allocate the output mesh + aiMesh *out = *_out = new aiMesh(); + out->mMaterialIndex = (*begin)->mMaterialIndex; + + std::string name; + // Find out how much output storage we'll need + for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) { + const char *meshName((*it)->mName.C_Str()); + name += std::string(meshName); + if (it != end - 1) { + name += "."; + } + out->mNumVertices += (*it)->mNumVertices; + out->mNumFaces += (*it)->mNumFaces; + out->mNumBones += (*it)->mNumBones; + + // combine primitive type flags + out->mPrimitiveTypes |= (*it)->mPrimitiveTypes; + } + out->mName.Set(name.c_str()); + + if (out->mNumVertices) { + aiVector3D *pv2; + + // copy vertex positions + if ((**begin).HasPositions()) { + + pv2 = out->mVertices = new aiVector3D[out->mNumVertices]; + for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) { + if ((*it)->mVertices) { + ::memcpy(pv2, (*it)->mVertices, (*it)->mNumVertices * sizeof(aiVector3D)); + } else + ASSIMP_LOG_WARN("JoinMeshes: Positions expected but input mesh contains no positions"); + pv2 += (*it)->mNumVertices; + } + } + // copy normals + if ((**begin).HasNormals()) { + + pv2 = out->mNormals = new aiVector3D[out->mNumVertices]; + for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) { + if ((*it)->mNormals) { + ::memcpy(pv2, (*it)->mNormals, (*it)->mNumVertices * sizeof(aiVector3D)); + } else { + ASSIMP_LOG_WARN("JoinMeshes: Normals expected but input mesh contains no normals"); + } + pv2 += (*it)->mNumVertices; + } + } + // copy tangents and bi-tangents + if ((**begin).HasTangentsAndBitangents()) { + + pv2 = out->mTangents = new aiVector3D[out->mNumVertices]; + aiVector3D *pv2b = out->mBitangents = new aiVector3D[out->mNumVertices]; + + for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) { + if ((*it)->mTangents) { + ::memcpy(pv2, (*it)->mTangents, (*it)->mNumVertices * sizeof(aiVector3D)); + ::memcpy(pv2b, (*it)->mBitangents, (*it)->mNumVertices * sizeof(aiVector3D)); + } else { + ASSIMP_LOG_WARN("JoinMeshes: Tangents expected but input mesh contains no tangents"); + } + pv2 += (*it)->mNumVertices; + pv2b += (*it)->mNumVertices; + } + } + // copy texture coordinates + unsigned int n = 0; + while ((**begin).HasTextureCoords(n)) { + out->mNumUVComponents[n] = (*begin)->mNumUVComponents[n]; + + pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices]; + for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) { + if ((*it)->mTextureCoords[n]) { + ::memcpy(pv2, (*it)->mTextureCoords[n], (*it)->mNumVertices * sizeof(aiVector3D)); + } else { + ASSIMP_LOG_WARN("JoinMeshes: UVs expected but input mesh contains no UVs"); + } + pv2 += (*it)->mNumVertices; + } + ++n; + } + // copy vertex colors + n = 0; + while ((**begin).HasVertexColors(n)) { + aiColor4D *pVec2 = out->mColors[n] = new aiColor4D[out->mNumVertices]; + for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) { + if ((*it)->mColors[n]) { + ::memcpy(pVec2, (*it)->mColors[n], (*it)->mNumVertices * sizeof(aiColor4D)); + } else { + ASSIMP_LOG_WARN("JoinMeshes: VCs expected but input mesh contains no VCs"); + } + pVec2 += (*it)->mNumVertices; + } + ++n; + } + } + + if (out->mNumFaces) // just for safety + { + // copy faces + out->mFaces = new aiFace[out->mNumFaces]; + aiFace *pf2 = out->mFaces; + + unsigned int ofs = 0; + for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) { + for (unsigned int m = 0; m < (*it)->mNumFaces; ++m, ++pf2) { + aiFace &face = (*it)->mFaces[m]; + pf2->mNumIndices = face.mNumIndices; + pf2->mIndices = face.mIndices; + + if (ofs) { + // add the offset to the vertex + for (unsigned int q = 0; q < face.mNumIndices; ++q) { + face.mIndices[q] += ofs; + } + } + face.mIndices = nullptr; + } + ofs += (*it)->mNumVertices; + } + } + + // bones - as this is quite lengthy, I moved the code to a separate function + if (out->mNumBones) + MergeBones(out, begin, end); + + // delete all source meshes + for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) + delete *it; +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::MergeMaterials(aiMaterial **dest, + std::vector<aiMaterial *>::const_iterator begin, + std::vector<aiMaterial *>::const_iterator end) { + if (nullptr == dest) { + return; + } + + if (begin == end) { + *dest = nullptr; // no materials ... + return; + } + + // Allocate the output material + aiMaterial *out = *dest = new aiMaterial(); + + // Get the maximal number of properties + unsigned int size = 0; + for (std::vector<aiMaterial *>::const_iterator it = begin; it != end; ++it) { + size += (*it)->mNumProperties; + } + + out->Clear(); + delete[] out->mProperties; + + out->mNumAllocated = size; + out->mNumProperties = 0; + out->mProperties = new aiMaterialProperty *[out->mNumAllocated]; + + for (std::vector<aiMaterial *>::const_iterator it = begin; it != end; ++it) { + for (unsigned int i = 0; i < (*it)->mNumProperties; ++i) { + aiMaterialProperty *sprop = (*it)->mProperties[i]; + + // Test if we already have a matching property + const aiMaterialProperty *prop_exist; + if (aiGetMaterialProperty(out, sprop->mKey.C_Str(), sprop->mSemantic, sprop->mIndex, &prop_exist) != AI_SUCCESS) { + // If not, we add it to the new material + aiMaterialProperty *prop = out->mProperties[out->mNumProperties] = new aiMaterialProperty(); + + prop->mDataLength = sprop->mDataLength; + prop->mData = new char[prop->mDataLength]; + ::memcpy(prop->mData, sprop->mData, prop->mDataLength); + + prop->mIndex = sprop->mIndex; + prop->mSemantic = sprop->mSemantic; + prop->mKey = sprop->mKey; + prop->mType = sprop->mType; + + out->mNumProperties++; + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +template <typename Type> +inline void CopyPtrArray(Type **&dest, const Type *const *src, ai_uint num) { + if (!num) { + dest = nullptr; + return; + } + dest = new Type *[num]; + for (ai_uint i = 0; i < num; ++i) { + SceneCombiner::Copy(&dest[i], src[i]); + } +} + +// ------------------------------------------------------------------------------------------------ +template <typename Type> +inline void GetArrayCopy(Type *&dest, ai_uint num) { + if (!dest) { + return; + } + Type *old = dest; + + dest = new Type[num]; + ::memcpy(dest, old, sizeof(Type) * num); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::CopySceneFlat(aiScene **_dest, const aiScene *src) { + if (nullptr == _dest || nullptr == src) { + return; + } + + // reuse the old scene or allocate a new? + if (*_dest) { + (*_dest)->~aiScene(); + new (*_dest) aiScene(); + } else { + *_dest = new aiScene(); + } + CopyScene(_dest, src, false); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::CopyScene(aiScene **_dest, const aiScene *src, bool allocate) { + if (nullptr == _dest || nullptr == src) { + return; + } + + if (allocate) { + *_dest = new aiScene(); + } + aiScene *dest = *_dest; + ai_assert(nullptr != dest); + + // copy metadata + if (nullptr != src->mMetaData) { + dest->mMetaData = new aiMetadata(*src->mMetaData); + } + + // copy animations + dest->mNumAnimations = src->mNumAnimations; + CopyPtrArray(dest->mAnimations, src->mAnimations, + dest->mNumAnimations); + + // copy textures + dest->mNumTextures = src->mNumTextures; + CopyPtrArray(dest->mTextures, src->mTextures, + dest->mNumTextures); + + // copy materials + dest->mNumMaterials = src->mNumMaterials; + CopyPtrArray(dest->mMaterials, src->mMaterials, + dest->mNumMaterials); + + // copy lights + dest->mNumLights = src->mNumLights; + CopyPtrArray(dest->mLights, src->mLights, + dest->mNumLights); + + // copy cameras + dest->mNumCameras = src->mNumCameras; + CopyPtrArray(dest->mCameras, src->mCameras, + dest->mNumCameras); + + // copy meshes + dest->mNumMeshes = src->mNumMeshes; + CopyPtrArray(dest->mMeshes, src->mMeshes, + dest->mNumMeshes); + + // now - copy the root node of the scene (deep copy, too) + Copy(&dest->mRootNode, src->mRootNode); + + // and keep the flags ... + dest->mFlags = src->mFlags; + + // source private data might be nullptr if the scene is user-allocated (i.e. for use with the export API) + if (dest->mPrivate != nullptr) { + ScenePriv(dest)->mPPStepsApplied = ScenePriv(src) ? ScenePriv(src)->mPPStepsApplied : 0; + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiMesh **_dest, const aiMesh *src) { + if (nullptr == _dest || nullptr == src) { + return; + } + + aiMesh *dest = *_dest = new aiMesh(); + + // get a flat copy + *dest = *src; + + // and reallocate all arrays + GetArrayCopy(dest->mVertices, dest->mNumVertices); + GetArrayCopy(dest->mNormals, dest->mNumVertices); + GetArrayCopy(dest->mTangents, dest->mNumVertices); + GetArrayCopy(dest->mBitangents, dest->mNumVertices); + + unsigned int n = 0; + while (dest->HasTextureCoords(n)) { + GetArrayCopy(dest->mTextureCoords[n++], dest->mNumVertices); + } + + n = 0; + while (dest->HasVertexColors(n)) { + GetArrayCopy(dest->mColors[n++], dest->mNumVertices); + } + + // make a deep copy of all bones + CopyPtrArray(dest->mBones, dest->mBones, dest->mNumBones); + + // make a deep copy of all faces + GetArrayCopy(dest->mFaces, dest->mNumFaces); + for (unsigned int i = 0; i < dest->mNumFaces; ++i) { + aiFace &f = dest->mFaces[i]; + GetArrayCopy(f.mIndices, f.mNumIndices); + } + + // make a deep copy of all blend shapes + CopyPtrArray(dest->mAnimMeshes, dest->mAnimMeshes, dest->mNumAnimMeshes); + + // make a deep copy of all texture coordinate names + if (src->mTextureCoordsNames != nullptr) { + dest->mTextureCoordsNames = new aiString *[AI_MAX_NUMBER_OF_TEXTURECOORDS] {}; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + Copy(&dest->mTextureCoordsNames[i], src->mTextureCoordsNames[i]); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiAnimMesh **_dest, const aiAnimMesh *src) { + if (nullptr == _dest || nullptr == src) { + return; + } + + aiAnimMesh *dest = *_dest = new aiAnimMesh(); + + // get a flat copy + *dest = *src; + + // and reallocate all arrays + GetArrayCopy(dest->mVertices, dest->mNumVertices); + GetArrayCopy(dest->mNormals, dest->mNumVertices); + GetArrayCopy(dest->mTangents, dest->mNumVertices); + GetArrayCopy(dest->mBitangents, dest->mNumVertices); + + unsigned int n = 0; + while (dest->HasTextureCoords(n)) + GetArrayCopy(dest->mTextureCoords[n++], dest->mNumVertices); + + n = 0; + while (dest->HasVertexColors(n)) + GetArrayCopy(dest->mColors[n++], dest->mNumVertices); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiMaterial **_dest, const aiMaterial *src) { + if (nullptr == _dest || nullptr == src) { + return; + } + + aiMaterial *dest = (aiMaterial *)(*_dest = new aiMaterial()); + + dest->Clear(); + delete[] dest->mProperties; + + dest->mNumAllocated = src->mNumAllocated; + dest->mNumProperties = src->mNumProperties; + dest->mProperties = new aiMaterialProperty *[dest->mNumAllocated]; + + for (unsigned int i = 0; i < dest->mNumProperties; ++i) { + aiMaterialProperty *prop = dest->mProperties[i] = new aiMaterialProperty(); + aiMaterialProperty *sprop = src->mProperties[i]; + + prop->mDataLength = sprop->mDataLength; + prop->mData = new char[prop->mDataLength]; + ::memcpy(prop->mData, sprop->mData, prop->mDataLength); + + prop->mIndex = sprop->mIndex; + prop->mSemantic = sprop->mSemantic; + prop->mKey = sprop->mKey; + prop->mType = sprop->mType; + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiTexture **_dest, const aiTexture *src) { + if (nullptr == _dest || nullptr == src) { + return; + } + + aiTexture *dest = *_dest = new aiTexture(); + + // get a flat copy + *dest = *src; + + // and reallocate all arrays. We must do it manually here + const char *old = (const char *)dest->pcData; + if (old) { + unsigned int cpy; + if (!dest->mHeight) + cpy = dest->mWidth; + else + cpy = dest->mHeight * dest->mWidth * sizeof(aiTexel); + + if (!cpy) { + dest->pcData = nullptr; + return; + } + // the cast is legal, the aiTexel c'tor does nothing important + dest->pcData = (aiTexel *)new char[cpy]; + ::memcpy(dest->pcData, old, cpy); + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiAnimation **_dest, const aiAnimation *src) { + if (nullptr == _dest || nullptr == src) { + return; + } + + aiAnimation *dest = *_dest = new aiAnimation(); + + // get a flat copy + *dest = *src; + + // and reallocate all arrays + CopyPtrArray(dest->mChannels, src->mChannels, dest->mNumChannels); + CopyPtrArray(dest->mMorphMeshChannels, src->mMorphMeshChannels, dest->mNumMorphMeshChannels); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiNodeAnim **_dest, const aiNodeAnim *src) { + if (nullptr == _dest || nullptr == src) { + return; + } + + aiNodeAnim *dest = *_dest = new aiNodeAnim(); + + // get a flat copy + *dest = *src; + + // and reallocate all arrays + GetArrayCopy(dest->mPositionKeys, dest->mNumPositionKeys); + GetArrayCopy(dest->mScalingKeys, dest->mNumScalingKeys); + GetArrayCopy(dest->mRotationKeys, dest->mNumRotationKeys); +} + +void SceneCombiner::Copy(aiMeshMorphAnim **_dest, const aiMeshMorphAnim *src) { + if (nullptr == _dest || nullptr == src) { + return; + } + + aiMeshMorphAnim *dest = *_dest = new aiMeshMorphAnim(); + + // get a flat copy + *dest = *src; + + // and reallocate all arrays + GetArrayCopy(dest->mKeys, dest->mNumKeys); + for (ai_uint i = 0; i < dest->mNumKeys; ++i) { + dest->mKeys[i].mValues = new unsigned int[dest->mKeys[i].mNumValuesAndWeights]; + dest->mKeys[i].mWeights = new double[dest->mKeys[i].mNumValuesAndWeights]; + ::memcpy(dest->mKeys[i].mValues, src->mKeys[i].mValues, dest->mKeys[i].mNumValuesAndWeights * sizeof(unsigned int)); + ::memcpy(dest->mKeys[i].mWeights, src->mKeys[i].mWeights, dest->mKeys[i].mNumValuesAndWeights * sizeof(double)); + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiCamera **_dest, const aiCamera *src) { + if (nullptr == _dest || nullptr == src) { + return; + } + + aiCamera *dest = *_dest = new aiCamera(); + + // get a flat copy, that's already OK + *dest = *src; +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiLight **_dest, const aiLight *src) { + if (nullptr == _dest || nullptr == src) { + return; + } + + aiLight *dest = *_dest = new aiLight(); + + // get a flat copy, that's already OK + *dest = *src; +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiBone **_dest, const aiBone *src) { + if (nullptr == _dest || nullptr == src) { + return; + } + + aiBone *dest = *_dest = new aiBone(); + + // get a flat copy + *dest = *src; +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiNode **_dest, const aiNode *src) { + ai_assert(nullptr != _dest); + ai_assert(nullptr != src); + + aiNode *dest = *_dest = new aiNode(); + + // get a flat copy + *dest = *src; + + if (src->mMetaData) { + Copy(&dest->mMetaData, src->mMetaData); + } + + // and reallocate all arrays + GetArrayCopy(dest->mMeshes, dest->mNumMeshes); + CopyPtrArray(dest->mChildren, src->mChildren, dest->mNumChildren); + + // need to set the mParent fields to the created aiNode. + for (unsigned int i = 0; i < dest->mNumChildren; i++) { + dest->mChildren[i]->mParent = dest; + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiMetadata **_dest, const aiMetadata *src) { + if (nullptr == _dest || nullptr == src) { + return; + } + + if (0 == src->mNumProperties) { + return; + } + + aiMetadata *dest = *_dest = aiMetadata::Alloc(src->mNumProperties); + std::copy(src->mKeys, src->mKeys + src->mNumProperties, dest->mKeys); + + for (unsigned int i = 0; i < src->mNumProperties; ++i) { + aiMetadataEntry &in = src->mValues[i]; + aiMetadataEntry &out = dest->mValues[i]; + out.mType = in.mType; + switch (dest->mValues[i].mType) { + case AI_BOOL: + out.mData = new bool(*static_cast<bool *>(in.mData)); + break; + case AI_INT32: + out.mData = new int32_t(*static_cast<int32_t *>(in.mData)); + break; + case AI_UINT64: + out.mData = new uint64_t(*static_cast<uint64_t *>(in.mData)); + break; + case AI_FLOAT: + out.mData = new float(*static_cast<float *>(in.mData)); + break; + case AI_DOUBLE: + out.mData = new double(*static_cast<double *>(in.mData)); + break; + case AI_AISTRING: + out.mData = new aiString(*static_cast<aiString *>(in.mData)); + break; + case AI_AIVECTOR3D: + out.mData = new aiVector3D(*static_cast<aiVector3D *>(in.mData)); + break; + default: + ai_assert(false); + break; + } + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiString **_dest, const aiString *src) { + if (nullptr == _dest || nullptr == src) { + return; + } + + aiString *dest = *_dest = new aiString(); + + // get a flat copy + *dest = *src; +} + +#if (__GNUC__ >= 8 && __GNUC_MINOR__ >= 0) +#pragma GCC diagnostic pop +#endif + +} // Namespace Assimp diff --git a/libs/assimp/code/Common/ScenePreprocessor.cpp b/libs/assimp/code/Common/ScenePreprocessor.cpp new file mode 100644 index 0000000..2ef291e --- /dev/null +++ b/libs/assimp/code/Common/ScenePreprocessor.cpp @@ -0,0 +1,285 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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 "ScenePreprocessor.h" +#include <assimp/ai_assert.h> +#include <assimp/scene.h> +#include <assimp/DefaultLogger.hpp> + +using namespace Assimp; + +// --------------------------------------------------------------------------------------------- +void ScenePreprocessor::ProcessScene() { + ai_assert(scene != nullptr); + + // Process all meshes + for (unsigned int i = 0; i < scene->mNumMeshes; ++i) { + if (nullptr == scene->mMeshes[i]) { + continue; + } + ProcessMesh(scene->mMeshes[i]); + } + + // - nothing to do for nodes for the moment + // - nothing to do for textures for the moment + // - nothing to do for lights for the moment + // - nothing to do for cameras for the moment + + // Process all animations + for (unsigned int i = 0; i < scene->mNumAnimations; ++i) { + if (nullptr == scene->mAnimations[i]) { + continue; + } + ProcessAnimation(scene->mAnimations[i]); + } + + // Generate a default material if none was specified + if (!scene->mNumMaterials && scene->mNumMeshes) { + scene->mMaterials = new aiMaterial *[2]; + aiMaterial *helper; + + aiString name; + + scene->mMaterials[scene->mNumMaterials] = helper = new aiMaterial(); + aiColor3D clr(0.6f, 0.6f, 0.6f); + helper->AddProperty(&clr, 1, AI_MATKEY_COLOR_DIFFUSE); + + // setup the default name to make this material identifiable + name.Set(AI_DEFAULT_MATERIAL_NAME); + helper->AddProperty(&name, AI_MATKEY_NAME); + + ASSIMP_LOG_DEBUG("ScenePreprocessor: Adding default material \'" AI_DEFAULT_MATERIAL_NAME "\'"); + + for (unsigned int i = 0; i < scene->mNumMeshes; ++i) { + if (nullptr == scene->mMeshes[i]) { + continue; + } + scene->mMeshes[i]->mMaterialIndex = scene->mNumMaterials; + } + + scene->mNumMaterials++; + } +} + +// --------------------------------------------------------------------------------------------- +void ScenePreprocessor::ProcessMesh(aiMesh *mesh) { + // If aiMesh::mNumUVComponents is *not* set assign the default value of 2 + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (!mesh->mTextureCoords[i]) { + mesh->mNumUVComponents[i] = 0; + } else { + if (!mesh->mNumUVComponents[i]) { + mesh->mNumUVComponents[i] = 2; + } + + aiVector3D *p = mesh->mTextureCoords[i], *end = p + mesh->mNumVertices; + + // Ensure unused components are zeroed. This will make 1D texture channels work + // as if they were 2D channels .. just in case an application doesn't handle + // this case + if (2 == mesh->mNumUVComponents[i]) { + for (; p != end; ++p) { + p->z = 0.f; + } + } else if (1 == mesh->mNumUVComponents[i]) { + for (; p != end; ++p) { + p->z = p->y = 0.f; + } + } else if (3 == mesh->mNumUVComponents[i]) { + // Really 3D coordinates? Check whether the third coordinate is != 0 for at least one element + for (; p != end; ++p) { + if (p->z != 0) { + break; + } + } + if (p == end) { + ASSIMP_LOG_WARN("ScenePreprocessor: UVs are declared to be 3D but they're obviously not. Reverting to 2D."); + mesh->mNumUVComponents[i] = 2; + } + } + } + } + + // If the information which primitive types are there in the + // mesh is currently not available, compute it. + if (!mesh->mPrimitiveTypes) { + for (unsigned int a = 0; a < mesh->mNumFaces; ++a) { + aiFace &face = mesh->mFaces[a]; + switch (face.mNumIndices) { + case 3u: + mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + + case 2u: + mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + + case 1u: + mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + + default: + mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + break; + } + } + } + + // If tangents and normals are given but no bitangents compute them + if (mesh->mTangents && mesh->mNormals && !mesh->mBitangents) { + mesh->mBitangents = new aiVector3D[mesh->mNumVertices]; + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + mesh->mBitangents[i] = mesh->mNormals[i] ^ mesh->mTangents[i]; + } + } +} + +// --------------------------------------------------------------------------------------------- +void ScenePreprocessor::ProcessAnimation(aiAnimation *anim) { + double first = 10e10, last = -10e10; + for (unsigned int i = 0; i < anim->mNumChannels; ++i) { + aiNodeAnim *channel = anim->mChannels[i]; + + // If the exact duration of the animation is not given + // compute it now. + if (anim->mDuration == -1.) { + // Position keys + for (unsigned int j = 0; j < channel->mNumPositionKeys; ++j) { + aiVectorKey &key = channel->mPositionKeys[j]; + first = std::min(first, key.mTime); + last = std::max(last, key.mTime); + } + + // Scaling keys + for (unsigned int j = 0; j < channel->mNumScalingKeys; ++j) { + aiVectorKey &key = channel->mScalingKeys[j]; + first = std::min(first, key.mTime); + last = std::max(last, key.mTime); + } + + // Rotation keys + for (unsigned int j = 0; j < channel->mNumRotationKeys; ++j) { + aiQuatKey &key = channel->mRotationKeys[j]; + first = std::min(first, key.mTime); + last = std::max(last, key.mTime); + } + } + + // Check whether the animation channel has no rotation + // or position tracks. In this case we generate a dummy + // track from the information we have in the transformation + // matrix of the corresponding node. + if (!channel->mNumRotationKeys || !channel->mNumPositionKeys || !channel->mNumScalingKeys) { + // Find the node that belongs to this animation + aiNode *node = scene->mRootNode->FindNode(channel->mNodeName); + if (node) // ValidateDS will complain later if 'node' is nullptr + { + // Decompose the transformation matrix of the node + aiVector3D scaling, position; + aiQuaternion rotation; + + node->mTransformation.Decompose(scaling, rotation, position); + + // No rotation keys? Generate a dummy track + if (!channel->mNumRotationKeys) { + if (channel->mRotationKeys) { + delete[] channel->mRotationKeys; + channel->mRotationKeys = nullptr; + } + ai_assert(!channel->mRotationKeys); + channel->mNumRotationKeys = 1; + channel->mRotationKeys = new aiQuatKey[1]; + aiQuatKey &q = channel->mRotationKeys[0]; + + q.mTime = 0.; + q.mValue = rotation; + + ASSIMP_LOG_VERBOSE_DEBUG("ScenePreprocessor: Dummy rotation track has been generated"); + } else { + ai_assert(channel->mRotationKeys); + } + + // No scaling keys? Generate a dummy track + if (!channel->mNumScalingKeys) { + if (channel->mScalingKeys) { + delete[] channel->mScalingKeys; + channel->mScalingKeys = nullptr; + } + ai_assert(!channel->mScalingKeys); + channel->mNumScalingKeys = 1; + channel->mScalingKeys = new aiVectorKey[1]; + aiVectorKey &q = channel->mScalingKeys[0]; + + q.mTime = 0.; + q.mValue = scaling; + + ASSIMP_LOG_VERBOSE_DEBUG("ScenePreprocessor: Dummy scaling track has been generated"); + } else { + ai_assert(channel->mScalingKeys); + } + + // No position keys? Generate a dummy track + if (!channel->mNumPositionKeys) { + if (channel->mPositionKeys) { + delete[] channel->mPositionKeys; + channel->mPositionKeys = nullptr; + } + ai_assert(!channel->mPositionKeys); + channel->mNumPositionKeys = 1; + channel->mPositionKeys = new aiVectorKey[1]; + aiVectorKey &q = channel->mPositionKeys[0]; + + q.mTime = 0.; + q.mValue = position; + + ASSIMP_LOG_VERBOSE_DEBUG("ScenePreprocessor: Dummy position track has been generated"); + } else { + ai_assert(channel->mPositionKeys); + } + } + } + } + + if (anim->mDuration == -1.) { + ASSIMP_LOG_VERBOSE_DEBUG("ScenePreprocessor: Setting animation duration"); + anim->mDuration = last - std::min(first, 0.); + } +} diff --git a/libs/assimp/code/Common/ScenePreprocessor.h b/libs/assimp/code/Common/ScenePreprocessor.h new file mode 100644 index 0000000..49e06ed --- /dev/null +++ b/libs/assimp/code/Common/ScenePreprocessor.h @@ -0,0 +1,118 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to search all meshes for + degenerated faces */ +#ifndef AI_SCENE_PREPROCESSOR_H_INC +#define AI_SCENE_PREPROCESSOR_H_INC + +#include <assimp/defs.h> +#include <stddef.h> + +struct aiScene; +struct aiAnimation; +struct aiMesh; + +class ScenePreprocessorTest; +namespace Assimp { + +// ---------------------------------------------------------------------------------- +/** ScenePreprocessor: Preprocess a scene before any post-processing + * steps are executed. + * + * The step computes data that needn't necessarily be provided by the + * importer, such as aiMesh::mPrimitiveTypes. +*/ +// ---------------------------------------------------------------------------------- +class ASSIMP_API ScenePreprocessor { + // Make ourselves a friend of the corresponding test unit. + friend class ::ScenePreprocessorTest; + +public: + // ---------------------------------------------------------------- + /** Default c'tpr. Use SetScene() to assign a scene to the object. + */ + ScenePreprocessor() : + scene(nullptr) {} + + /** Constructs the object and assigns a specific scene to it + */ + ScenePreprocessor(aiScene *_scene) : + scene(_scene) {} + + // ---------------------------------------------------------------- + /** Assign a (new) scene to the object. + * + * One 'SceneProcessor' can be used for multiple scenes. + * Call ProcessScene to have the scene preprocessed. + * @param sc Scene to be processed. + */ + void SetScene(aiScene *sc) { + scene = sc; + } + + // ---------------------------------------------------------------- + /** Preprocess the current scene + */ + void ProcessScene(); + +protected: + // ---------------------------------------------------------------- + /** Preprocess an animation in the scene + * @param anim Anim to be preprocessed. + */ + void ProcessAnimation(aiAnimation *anim); + + // ---------------------------------------------------------------- + /** Preprocess a mesh in the scene + * @param mesh Mesh to be preprocessed. + */ + void ProcessMesh(aiMesh *mesh); + +protected: + //! Scene we're currently working on + aiScene *scene; +}; + +} // namespace Assimp + +#endif // include guard diff --git a/libs/assimp/code/Common/ScenePrivate.h b/libs/assimp/code/Common/ScenePrivate.h new file mode 100644 index 0000000..3910db7 --- /dev/null +++ b/libs/assimp/code/Common/ScenePrivate.h @@ -0,0 +1,105 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +/** @file Stuff to deal with aiScene::mPrivate + */ +#pragma once +#ifndef AI_SCENEPRIVATE_H_INCLUDED +#define AI_SCENEPRIVATE_H_INCLUDED + +#include <assimp/ai_assert.h> +#include <assimp/scene.h> + +namespace Assimp { + +// Forward declarations +class Importer; + +struct ScenePrivateData { + // The struct constructor. + ScenePrivateData() AI_NO_EXCEPT; + + // Importer that originally loaded the scene though the C-API + // If set, this object is owned by this private data instance. + Assimp::Importer* mOrigImporter; + + // List of post-processing steps already applied to the scene. + unsigned int mPPStepsApplied; + + // true if the scene is a copy made with aiCopyScene() + // or the corresponding C++ API. This means that user code + // may have made modifications to it, so mPPStepsApplied + // and mOrigImporter are no longer safe to rely on and only + // serve informative purposes. + bool mIsCopy; +}; + +inline +ScenePrivateData::ScenePrivateData() AI_NO_EXCEPT +: mOrigImporter( nullptr ) +, mPPStepsApplied( 0 ) +, mIsCopy( false ) { + // empty +} + +// Access private data stored in the scene +inline +ScenePrivateData* ScenePriv(aiScene* in) { + ai_assert( nullptr != in ); + if ( nullptr == in ) { + return nullptr; + } + return static_cast<ScenePrivateData*>(in->mPrivate); +} + +inline +const ScenePrivateData* ScenePriv(const aiScene* in) { + ai_assert( nullptr != in ); + if ( nullptr == in ) { + return nullptr; + } + return static_cast<const ScenePrivateData*>(in->mPrivate); +} + +} // Namespace Assimp + +#endif // AI_SCENEPRIVATE_H_INCLUDED diff --git a/libs/assimp/code/Common/SkeletonMeshBuilder.cpp b/libs/assimp/code/Common/SkeletonMeshBuilder.cpp new file mode 100644 index 0000000..5ea30d9 --- /dev/null +++ b/libs/assimp/code/Common/SkeletonMeshBuilder.cpp @@ -0,0 +1,262 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +/** @file SkeletonMeshBuilder.cpp + * @brief Implementation of a little class to construct a dummy mesh for a skeleton + */ + +#include <assimp/SkeletonMeshBuilder.h> +#include <assimp/scene.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// The constructor processes the given scene and adds a mesh there. +SkeletonMeshBuilder::SkeletonMeshBuilder(aiScene *pScene, aiNode *root, bool bKnobsOnly) { + // nothing to do if there's mesh data already present at the scene + if (pScene->mNumMeshes > 0 || pScene->mRootNode == nullptr) { + return; + } + + if (!root) { + root = pScene->mRootNode; + } + + mKnobsOnly = bKnobsOnly; + + // build some faces around each node + CreateGeometry(root); + + // create a mesh to hold all the generated faces + pScene->mNumMeshes = 1; + pScene->mMeshes = new aiMesh *[1]; + pScene->mMeshes[0] = CreateMesh(); + // and install it at the root node + root->mNumMeshes = 1; + root->mMeshes = new unsigned int[1]; + root->mMeshes[0] = 0; + + // create a dummy material for the mesh + if (pScene->mNumMaterials == 0) { + pScene->mNumMaterials = 1; + pScene->mMaterials = new aiMaterial *[1]; + pScene->mMaterials[0] = CreateMaterial(); + } +} + +// ------------------------------------------------------------------------------------------------ +// Recursively builds a simple mesh representation for the given node +void SkeletonMeshBuilder::CreateGeometry(const aiNode *pNode) { + // add a joint entry for the node. + const unsigned int vertexStartIndex = static_cast<unsigned int>(mVertices.size()); + + // now build the geometry. + if (pNode->mNumChildren > 0 && !mKnobsOnly) { + // If the node has children, we build little pointers to each of them + for (unsigned int a = 0; a < pNode->mNumChildren; a++) { + // find a suitable coordinate system + const aiMatrix4x4 &childTransform = pNode->mChildren[a]->mTransformation; + aiVector3D childpos(childTransform.a4, childTransform.b4, childTransform.c4); + ai_real distanceToChild = childpos.Length(); + if (distanceToChild < 0.0001) + continue; + aiVector3D up = aiVector3D(childpos).Normalize(); + + aiVector3D orth(1.0, 0.0, 0.0); + if (std::fabs(orth * up) > 0.99) + orth.Set(0.0, 1.0, 0.0); + + aiVector3D front = (up ^ orth).Normalize(); + aiVector3D side = (front ^ up).Normalize(); + + unsigned int localVertexStart = static_cast<unsigned int>(mVertices.size()); + mVertices.push_back(-front * distanceToChild * (ai_real)0.1); + mVertices.push_back(childpos); + mVertices.push_back(-side * distanceToChild * (ai_real)0.1); + mVertices.push_back(-side * distanceToChild * (ai_real)0.1); + mVertices.push_back(childpos); + mVertices.push_back(front * distanceToChild * (ai_real)0.1); + mVertices.push_back(front * distanceToChild * (ai_real)0.1); + mVertices.push_back(childpos); + mVertices.push_back(side * distanceToChild * (ai_real)0.1); + mVertices.push_back(side * distanceToChild * (ai_real)0.1); + mVertices.push_back(childpos); + mVertices.push_back(-front * distanceToChild * (ai_real)0.1); + + mFaces.push_back(Face(localVertexStart + 0, localVertexStart + 1, localVertexStart + 2)); + mFaces.push_back(Face(localVertexStart + 3, localVertexStart + 4, localVertexStart + 5)); + mFaces.push_back(Face(localVertexStart + 6, localVertexStart + 7, localVertexStart + 8)); + mFaces.push_back(Face(localVertexStart + 9, localVertexStart + 10, localVertexStart + 11)); + } + } else { + // if the node has no children, it's an end node. Put a little knob there instead + aiVector3D ownpos(pNode->mTransformation.a4, pNode->mTransformation.b4, pNode->mTransformation.c4); + ai_real sizeEstimate = ownpos.Length() * ai_real(0.18); + + mVertices.push_back(aiVector3D(-sizeEstimate, 0.0, 0.0)); + mVertices.push_back(aiVector3D(0.0, sizeEstimate, 0.0)); + mVertices.push_back(aiVector3D(0.0, 0.0, -sizeEstimate)); + mVertices.push_back(aiVector3D(0.0, sizeEstimate, 0.0)); + mVertices.push_back(aiVector3D(sizeEstimate, 0.0, 0.0)); + mVertices.push_back(aiVector3D(0.0, 0.0, -sizeEstimate)); + mVertices.push_back(aiVector3D(sizeEstimate, 0.0, 0.0)); + mVertices.push_back(aiVector3D(0.0, -sizeEstimate, 0.0)); + mVertices.push_back(aiVector3D(0.0, 0.0, -sizeEstimate)); + mVertices.push_back(aiVector3D(0.0, -sizeEstimate, 0.0)); + mVertices.push_back(aiVector3D(-sizeEstimate, 0.0, 0.0)); + mVertices.push_back(aiVector3D(0.0, 0.0, -sizeEstimate)); + + mVertices.push_back(aiVector3D(-sizeEstimate, 0.0, 0.0)); + mVertices.push_back(aiVector3D(0.0, 0.0, sizeEstimate)); + mVertices.push_back(aiVector3D(0.0, sizeEstimate, 0.0)); + mVertices.push_back(aiVector3D(0.0, sizeEstimate, 0.0)); + mVertices.push_back(aiVector3D(0.0, 0.0, sizeEstimate)); + mVertices.push_back(aiVector3D(sizeEstimate, 0.0, 0.0)); + mVertices.push_back(aiVector3D(sizeEstimate, 0.0, 0.0)); + mVertices.push_back(aiVector3D(0.0, 0.0, sizeEstimate)); + mVertices.push_back(aiVector3D(0.0, -sizeEstimate, 0.0)); + mVertices.push_back(aiVector3D(0.0, -sizeEstimate, 0.0)); + mVertices.push_back(aiVector3D(0.0, 0.0, sizeEstimate)); + mVertices.push_back(aiVector3D(-sizeEstimate, 0.0, 0.0)); + + mFaces.push_back(Face(vertexStartIndex + 0, vertexStartIndex + 1, vertexStartIndex + 2)); + mFaces.push_back(Face(vertexStartIndex + 3, vertexStartIndex + 4, vertexStartIndex + 5)); + mFaces.push_back(Face(vertexStartIndex + 6, vertexStartIndex + 7, vertexStartIndex + 8)); + mFaces.push_back(Face(vertexStartIndex + 9, vertexStartIndex + 10, vertexStartIndex + 11)); + mFaces.push_back(Face(vertexStartIndex + 12, vertexStartIndex + 13, vertexStartIndex + 14)); + mFaces.push_back(Face(vertexStartIndex + 15, vertexStartIndex + 16, vertexStartIndex + 17)); + mFaces.push_back(Face(vertexStartIndex + 18, vertexStartIndex + 19, vertexStartIndex + 20)); + mFaces.push_back(Face(vertexStartIndex + 21, vertexStartIndex + 22, vertexStartIndex + 23)); + } + + unsigned int numVertices = static_cast<unsigned int>(mVertices.size() - vertexStartIndex); + if (numVertices > 0) { + // create a bone affecting all the newly created vertices + aiBone *bone = new aiBone; + mBones.push_back(bone); + bone->mName = pNode->mName; + + // calculate the bone offset matrix by concatenating the inverse transformations of all parents + bone->mOffsetMatrix = aiMatrix4x4(pNode->mTransformation).Inverse(); + for (aiNode *parent = pNode->mParent; parent != nullptr; parent = parent->mParent) + bone->mOffsetMatrix = aiMatrix4x4(parent->mTransformation).Inverse() * bone->mOffsetMatrix; + + // add all the vertices to the bone's influences + bone->mNumWeights = numVertices; + bone->mWeights = new aiVertexWeight[numVertices]; + for (unsigned int a = 0; a < numVertices; a++) + bone->mWeights[a] = aiVertexWeight(vertexStartIndex + a, 1.0); + + // HACK: (thom) transform all vertices to the bone's local space. Should be done before adding + // them to the array, but I'm tired now and I'm annoyed. + aiMatrix4x4 boneToMeshTransform = aiMatrix4x4(bone->mOffsetMatrix).Inverse(); + for (unsigned int a = vertexStartIndex; a < mVertices.size(); a++) + mVertices[a] = boneToMeshTransform * mVertices[a]; + } + + // and finally recurse into the children list + for (unsigned int a = 0; a < pNode->mNumChildren; a++) + CreateGeometry(pNode->mChildren[a]); +} + +// ------------------------------------------------------------------------------------------------ +// Creates the mesh from the internally accumulated stuff and returns it. +aiMesh *SkeletonMeshBuilder::CreateMesh() { + aiMesh *mesh = new aiMesh(); + + // add points + mesh->mNumVertices = static_cast<unsigned int>(mVertices.size()); + mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + std::copy(mVertices.begin(), mVertices.end(), mesh->mVertices); + + mesh->mNormals = new aiVector3D[mesh->mNumVertices]; + + // add faces + mesh->mNumFaces = static_cast<unsigned int>(mFaces.size()); + mesh->mFaces = new aiFace[mesh->mNumFaces]; + for (unsigned int a = 0; a < mesh->mNumFaces; a++) { + const Face &inface = mFaces[a]; + aiFace &outface = mesh->mFaces[a]; + outface.mNumIndices = 3; + outface.mIndices = new unsigned int[3]; + outface.mIndices[0] = inface.mIndices[0]; + outface.mIndices[1] = inface.mIndices[1]; + outface.mIndices[2] = inface.mIndices[2]; + + // Compute per-face normals ... we don't want the bones to be smoothed ... they're built to visualize + // the skeleton, so it's good if there's a visual difference to the rest of the geometry + aiVector3D nor = ((mVertices[inface.mIndices[2]] - mVertices[inface.mIndices[0]]) ^ + (mVertices[inface.mIndices[1]] - mVertices[inface.mIndices[0]])); + + if (nor.Length() < 1e-5) /* ensure that FindInvalidData won't remove us ...*/ + nor = aiVector3D(1.0, 0.0, 0.0); + + for (unsigned int n = 0; n < 3; ++n) + mesh->mNormals[inface.mIndices[n]] = nor; + } + + // add the bones + mesh->mNumBones = static_cast<unsigned int>(mBones.size()); + mesh->mBones = new aiBone *[mesh->mNumBones]; + std::copy(mBones.begin(), mBones.end(), mesh->mBones); + + // default + mesh->mMaterialIndex = 0; + + return mesh; +} + +// ------------------------------------------------------------------------------------------------ +// Creates a dummy material and returns it. +aiMaterial *SkeletonMeshBuilder::CreateMaterial() { + aiMaterial *matHelper = new aiMaterial; + + // Name + aiString matName(std::string("SkeletonMaterial")); + matHelper->AddProperty(&matName, AI_MATKEY_NAME); + + // Prevent backface culling + const int no_cull = 1; + matHelper->AddProperty(&no_cull, 1, AI_MATKEY_TWOSIDED); + + return matHelper; +} diff --git a/libs/assimp/code/Common/SpatialSort.cpp b/libs/assimp/code/Common/SpatialSort.cpp new file mode 100644 index 0000000..6103002 --- /dev/null +++ b/libs/assimp/code/Common/SpatialSort.cpp @@ -0,0 +1,344 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the helper class to quickly find vertices close to a given position */ + +#include <assimp/SpatialSort.h> +#include <assimp/ai_assert.h> + +using namespace Assimp; + +// CHAR_BIT seems to be defined under MVSC, but not under GCC. Pray that the correct value is 8. +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +const aiVector3D PlaneInit(0.8523f, 0.34321f, 0.5736f); + +// ------------------------------------------------------------------------------------------------ +// Constructs a spatially sorted representation from the given position array. +// define the reference plane. We choose some arbitrary vector away from all basic axes +// in the hope that no model spreads all its vertices along this plane. +SpatialSort::SpatialSort(const aiVector3D *pPositions, unsigned int pNumPositions, unsigned int pElementOffset) : + mPlaneNormal(PlaneInit), + mFinalized(false) { + mPlaneNormal.Normalize(); + Fill(pPositions, pNumPositions, pElementOffset); +} + +// ------------------------------------------------------------------------------------------------ +SpatialSort::SpatialSort() : + mPlaneNormal(PlaneInit), + mFinalized(false) { + mPlaneNormal.Normalize(); +} + +// ------------------------------------------------------------------------------------------------ +// Destructor +SpatialSort::~SpatialSort() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +void SpatialSort::Fill(const aiVector3D *pPositions, unsigned int pNumPositions, + unsigned int pElementOffset, + bool pFinalize /*= true */) { + mPositions.clear(); + mFinalized = false; + Append(pPositions, pNumPositions, pElementOffset, pFinalize); + mFinalized = pFinalize; +} + +// ------------------------------------------------------------------------------------------------ +ai_real SpatialSort::CalculateDistance(const aiVector3D &pPosition) const { + return (pPosition - mCentroid) * mPlaneNormal; +} + +// ------------------------------------------------------------------------------------------------ +void SpatialSort::Finalize() { + const ai_real scale = 1.0f / mPositions.size(); + for (unsigned int i = 0; i < mPositions.size(); i++) { + mCentroid += scale * mPositions[i].mPosition; + } + for (unsigned int i = 0; i < mPositions.size(); i++) { + mPositions[i].mDistance = CalculateDistance(mPositions[i].mPosition); + } + std::sort(mPositions.begin(), mPositions.end()); + mFinalized = true; +} + +// ------------------------------------------------------------------------------------------------ +void SpatialSort::Append(const aiVector3D *pPositions, unsigned int pNumPositions, + unsigned int pElementOffset, + bool pFinalize /*= true */) { + ai_assert(!mFinalized && "You cannot add positions to the SpatialSort object after it has been finalized."); + // store references to all given positions along with their distance to the reference plane + const size_t initial = mPositions.size(); + mPositions.reserve(initial + pNumPositions); + for (unsigned int a = 0; a < pNumPositions; a++) { + const char *tempPointer = reinterpret_cast<const char *>(pPositions); + const aiVector3D *vec = reinterpret_cast<const aiVector3D *>(tempPointer + a * pElementOffset); + mPositions.push_back(Entry(static_cast<unsigned int>(a + initial), *vec)); + } + + if (pFinalize) { + // now sort the array ascending by distance. + Finalize(); + } +} + +// ------------------------------------------------------------------------------------------------ +// Returns an iterator for all positions close to the given position. +void SpatialSort::FindPositions(const aiVector3D &pPosition, + ai_real pRadius, std::vector<unsigned int> &poResults) const { + ai_assert(mFinalized && "The SpatialSort object must be finalized before FindPositions can be called."); + const ai_real dist = CalculateDistance(pPosition); + const ai_real minDist = dist - pRadius, maxDist = dist + pRadius; + + // clear the array + poResults.clear(); + + // quick check for positions outside the range + if (mPositions.size() == 0) + return; + if (maxDist < mPositions.front().mDistance) + return; + if (minDist > mPositions.back().mDistance) + return; + + // do a binary search for the minimal distance to start the iteration there + unsigned int index = (unsigned int)mPositions.size() / 2; + unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4; + while (binaryStepSize > 1) { + if (mPositions[index].mDistance < minDist) + index += binaryStepSize; + else + index -= binaryStepSize; + + binaryStepSize /= 2; + } + + // depending on the direction of the last step we need to single step a bit back or forth + // to find the actual beginning element of the range + while (index > 0 && mPositions[index].mDistance > minDist) + index--; + while (index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist) + index++; + + // Mow start iterating from there until the first position lays outside of the distance range. + // Add all positions inside the distance range within the given radius to the result array + std::vector<Entry>::const_iterator it = mPositions.begin() + index; + const ai_real pSquared = pRadius * pRadius; + while (it->mDistance < maxDist) { + if ((it->mPosition - pPosition).SquareLength() < pSquared) + poResults.push_back(it->mIndex); + ++it; + if (it == mPositions.end()) + break; + } + + // that's it +} + +namespace { + +// Binary, signed-integer representation of a single-precision floating-point value. +// IEEE 754 says: "If two floating-point numbers in the same format are ordered then they are +// ordered the same way when their bits are reinterpreted as sign-magnitude integers." +// This allows us to convert all floating-point numbers to signed integers of arbitrary size +// and then use them to work with ULPs (Units in the Last Place, for high-precision +// computations) or to compare them (integer comparisons are faster than floating-point +// comparisons on many platforms). +typedef ai_int BinFloat; + +// -------------------------------------------------------------------------------------------- +// Converts the bit pattern of a floating-point number to its signed integer representation. +BinFloat ToBinary(const ai_real &pValue) { + + // If this assertion fails, signed int is not big enough to store a float on your platform. + // Please correct the declaration of BinFloat a few lines above - but do it in a portable, + // #ifdef'd manner! + static_assert(sizeof(BinFloat) >= sizeof(ai_real), "sizeof(BinFloat) >= sizeof(ai_real)"); + +#if defined(_MSC_VER) + // If this assertion fails, Visual C++ has finally moved to ILP64. This means that this + // code has just become legacy code! Find out the current value of _MSC_VER and modify + // the #if above so it evaluates false on the current and all upcoming VC versions (or + // on the current platform, if LP64 or LLP64 are still used on other platforms). + static_assert(sizeof(BinFloat) == sizeof(ai_real), "sizeof(BinFloat) == sizeof(ai_real)"); + + // This works best on Visual C++, but other compilers have their problems with it. + const BinFloat binValue = reinterpret_cast<BinFloat const &>(pValue); + //::memcpy(&binValue, &pValue, sizeof(pValue)); + //return binValue; +#else + // On many compilers, reinterpreting a float address as an integer causes aliasing + // problems. This is an ugly but more or less safe way of doing it. + union { + ai_real asFloat; + BinFloat asBin; + } conversion; + conversion.asBin = 0; // zero empty space in case sizeof(BinFloat) > sizeof(float) + conversion.asFloat = pValue; + const BinFloat binValue = conversion.asBin; +#endif + + // floating-point numbers are of sign-magnitude format, so find out what signed number + // representation we must convert negative values to. + // See http://en.wikipedia.org/wiki/Signed_number_representations. + const BinFloat mask = BinFloat(1) << (CHAR_BIT * sizeof(BinFloat) - 1); + + // Two's complement? + const bool DefaultValue = ((-42 == (~42 + 1)) && (binValue & mask)); + const bool OneComplement = ((-42 == ~42) && (binValue & mask)); + + if (DefaultValue) + return mask - binValue; + // One's complement? + else if (OneComplement) + return BinFloat(-0) - binValue; + // Sign-magnitude? -0 = 1000... binary + return binValue; +} + +} // namespace + +// ------------------------------------------------------------------------------------------------ +// Fills an array with indices of all positions identical to the given position. In opposite to +// FindPositions(), not an epsilon is used but a (very low) tolerance of four floating-point units. +void SpatialSort::FindIdenticalPositions(const aiVector3D &pPosition, std::vector<unsigned int> &poResults) const { + ai_assert(mFinalized && "The SpatialSort object must be finalized before FindIdenticalPositions can be called."); + // Epsilons have a huge disadvantage: they are of constant precision, while floating-point + // values are of log2 precision. If you apply e=0.01 to 100, the epsilon is rather small, but + // if you apply it to 0.001, it is enormous. + + // The best way to overcome this is the unit in the last place (ULP). A precision of 2 ULPs + // tells us that a float does not differ more than 2 bits from the "real" value. ULPs are of + // logarithmic precision - around 1, they are 1*(2^24) and around 10000, they are 0.00125. + + // For standard C math, we can assume a precision of 0.5 ULPs according to IEEE 754. The + // incoming vertex positions might have already been transformed, probably using rather + // inaccurate SSE instructions, so we assume a tolerance of 4 ULPs to safely identify + // identical vertex positions. + static const int toleranceInULPs = 4; + // An interesting point is that the inaccuracy grows linear with the number of operations: + // multiplying to numbers, each inaccurate to four ULPs, results in an inaccuracy of four ULPs + // plus 0.5 ULPs for the multiplication. + // To compute the distance to the plane, a dot product is needed - that is a multiplication and + // an addition on each number. + static const int distanceToleranceInULPs = toleranceInULPs + 1; + // The squared distance between two 3D vectors is computed the same way, but with an additional + // subtraction. + static const int distance3DToleranceInULPs = distanceToleranceInULPs + 1; + + // Convert the plane distance to its signed integer representation so the ULPs tolerance can be + // applied. For some reason, VC won't optimize two calls of the bit pattern conversion. + const BinFloat minDistBinary = ToBinary(CalculateDistance(pPosition)) - distanceToleranceInULPs; + const BinFloat maxDistBinary = minDistBinary + 2 * distanceToleranceInULPs; + + // clear the array in this strange fashion because a simple clear() would also deallocate + // the array which we want to avoid + poResults.resize(0); + + // do a binary search for the minimal distance to start the iteration there + unsigned int index = (unsigned int)mPositions.size() / 2; + unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4; + while (binaryStepSize > 1) { + // Ugly, but conditional jumps are faster with integers than with floats + if (minDistBinary > ToBinary(mPositions[index].mDistance)) + index += binaryStepSize; + else + index -= binaryStepSize; + + binaryStepSize /= 2; + } + + // depending on the direction of the last step we need to single step a bit back or forth + // to find the actual beginning element of the range + while (index > 0 && minDistBinary < ToBinary(mPositions[index].mDistance)) + index--; + while (index < (mPositions.size() - 1) && minDistBinary > ToBinary(mPositions[index].mDistance)) + index++; + + // Now start iterating from there until the first position lays outside of the distance range. + // Add all positions inside the distance range within the tolerance to the result array + std::vector<Entry>::const_iterator it = mPositions.begin() + index; + while (ToBinary(it->mDistance) < maxDistBinary) { + if (distance3DToleranceInULPs >= ToBinary((it->mPosition - pPosition).SquareLength())) + poResults.push_back(it->mIndex); + ++it; + if (it == mPositions.end()) + break; + } + + // that's it +} + +// ------------------------------------------------------------------------------------------------ +unsigned int SpatialSort::GenerateMappingTable(std::vector<unsigned int> &fill, ai_real pRadius) const { + ai_assert(mFinalized && "The SpatialSort object must be finalized before GenerateMappingTable can be called."); + fill.resize(mPositions.size(), UINT_MAX); + ai_real dist, maxDist; + + unsigned int t = 0; + const ai_real pSquared = pRadius * pRadius; + for (size_t i = 0; i < mPositions.size();) { + dist = (mPositions[i].mPosition - mCentroid) * mPlaneNormal; + maxDist = dist + pRadius; + + fill[mPositions[i].mIndex] = t; + const aiVector3D &oldpos = mPositions[i].mPosition; + for (++i; i < fill.size() && mPositions[i].mDistance < maxDist && (mPositions[i].mPosition - oldpos).SquareLength() < pSquared; ++i) { + fill[mPositions[i].mIndex] = t; + } + ++t; + } + +#ifdef ASSIMP_BUILD_DEBUG + + // debug invariant: mPositions[i].mIndex values must range from 0 to mPositions.size()-1 + for (size_t i = 0; i < fill.size(); ++i) { + ai_assert(fill[i] < mPositions.size()); + } + +#endif + return t; +} diff --git a/libs/assimp/code/Common/StandardShapes.cpp b/libs/assimp/code/Common/StandardShapes.cpp new file mode 100644 index 0000000..e94b5b9 --- /dev/null +++ b/libs/assimp/code/Common/StandardShapes.cpp @@ -0,0 +1,479 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +/** @file StandardShapes.cpp + * @brief Implementation of the StandardShapes class + * + * The primitive geometry data comes from + * http://geometrictools.com/Documentation/PlatonicSolids.pdf. + */ + +#include <assimp/StandardShapes.h> +#include <assimp/StringComparison.h> +#include <assimp/mesh.h> + +namespace Assimp { + +#define ADD_TRIANGLE(n0, n1, n2) \ + positions.push_back(n0); \ + positions.push_back(n1); \ + positions.push_back(n2); + +#define ADD_PENTAGON(n0, n1, n2, n3, n4) \ + if (polygons) { \ + positions.push_back(n0); \ + positions.push_back(n1); \ + positions.push_back(n2); \ + positions.push_back(n3); \ + positions.push_back(n4); \ + } else { \ + ADD_TRIANGLE(n0, n1, n2) \ + ADD_TRIANGLE(n0, n2, n3) \ + ADD_TRIANGLE(n0, n3, n4) \ + } + +#define ADD_QUAD(n0, n1, n2, n3) \ + if (polygons) { \ + positions.push_back(n0); \ + positions.push_back(n1); \ + positions.push_back(n2); \ + positions.push_back(n3); \ + } else { \ + ADD_TRIANGLE(n0, n1, n2) \ + ADD_TRIANGLE(n0, n2, n3) \ + } + +// ------------------------------------------------------------------------------------------------ +// Fast subdivision for a mesh whose verts have a magnitude of 1 +void Subdivide(std::vector<aiVector3D> &positions) { + // assume this to be constant - (fixme: must be 1.0? I think so) + const ai_real fl1 = positions[0].Length(); + + unsigned int origSize = (unsigned int)positions.size(); + for (unsigned int i = 0; i < origSize; i += 3) { + aiVector3D &tv0 = positions[i]; + aiVector3D &tv1 = positions[i + 1]; + aiVector3D &tv2 = positions[i + 2]; + + aiVector3D a = tv0, b = tv1, c = tv2; + aiVector3D v1 = aiVector3D(a.x + b.x, a.y + b.y, a.z + b.z).Normalize() * fl1; + aiVector3D v2 = aiVector3D(a.x + c.x, a.y + c.y, a.z + c.z).Normalize() * fl1; + aiVector3D v3 = aiVector3D(b.x + c.x, b.y + c.y, b.z + c.z).Normalize() * fl1; + + tv0 = v1; + tv1 = v3; + tv2 = v2; // overwrite the original + ADD_TRIANGLE(v1, v2, a); + ADD_TRIANGLE(v2, v3, c); + ADD_TRIANGLE(v3, v1, b); + } +} + +// ------------------------------------------------------------------------------------------------ +// Construct a mesh from given vertex positions +aiMesh *StandardShapes::MakeMesh(const std::vector<aiVector3D> &positions, + unsigned int numIndices) { + if (positions.empty() || !numIndices) { + return nullptr; + } + + // Determine which kinds of primitives the mesh consists of + aiMesh *out = new aiMesh(); + switch (numIndices) { + case 1: + out->mPrimitiveTypes = aiPrimitiveType_POINT; + break; + case 2: + out->mPrimitiveTypes = aiPrimitiveType_LINE; + break; + case 3: + out->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + break; + default: + out->mPrimitiveTypes = aiPrimitiveType_POLYGON; + break; + }; + + out->mNumFaces = (unsigned int)positions.size() / numIndices; + out->mFaces = new aiFace[out->mNumFaces]; + for (unsigned int i = 0, a = 0; i < out->mNumFaces; ++i) { + aiFace &f = out->mFaces[i]; + f.mNumIndices = numIndices; + f.mIndices = new unsigned int[numIndices]; + for (unsigned int j = 0; j < numIndices; ++j, ++a) { + f.mIndices[j] = a; + } + } + out->mNumVertices = (unsigned int)positions.size(); + out->mVertices = new aiVector3D[out->mNumVertices]; + ::memcpy(out->mVertices, &positions[0], out->mNumVertices * sizeof(aiVector3D)); + + return out; +} + +// ------------------------------------------------------------------------------------------------ +// Construct a mesh with a specific shape (callback) +aiMesh *StandardShapes::MakeMesh(unsigned int (*GenerateFunc)( + std::vector<aiVector3D> &)) { + std::vector<aiVector3D> temp; + unsigned num = (*GenerateFunc)(temp); + return MakeMesh(temp, num); +} + +// ------------------------------------------------------------------------------------------------ +// Construct a mesh with a specific shape (callback) +aiMesh *StandardShapes::MakeMesh(unsigned int (*GenerateFunc)( + std::vector<aiVector3D> &, bool)) { + std::vector<aiVector3D> temp; + unsigned num = (*GenerateFunc)(temp, true); + return MakeMesh(temp, num); +} + +// ------------------------------------------------------------------------------------------------ +// Construct a mesh with a specific shape (callback) +aiMesh *StandardShapes::MakeMesh(unsigned int num, void (*GenerateFunc)( + unsigned int, std::vector<aiVector3D> &)) { + std::vector<aiVector3D> temp; + (*GenerateFunc)(num, temp); + return MakeMesh(temp, 3); +} + +// ------------------------------------------------------------------------------------------------ +// Build an incosahedron with points.magnitude == 1 +unsigned int StandardShapes::MakeIcosahedron(std::vector<aiVector3D> &positions) { + positions.reserve(positions.size() + 60); + + const ai_real t = (ai_real(1.0) + ai_real(2.236067977)) / ai_real(2.0); + const ai_real s = std::sqrt(ai_real(1.0) + t * t); + + const aiVector3D v0 = aiVector3D(t, 1.0, 0.0) / s; + const aiVector3D v1 = aiVector3D(-t, 1.0, 0.0) / s; + const aiVector3D v2 = aiVector3D(t, -1.0, 0.0) / s; + const aiVector3D v3 = aiVector3D(-t, -1.0, 0.0) / s; + const aiVector3D v4 = aiVector3D(1.0, 0.0, t) / s; + const aiVector3D v5 = aiVector3D(1.0, 0.0, -t) / s; + const aiVector3D v6 = aiVector3D(-1.0, 0.0, t) / s; + const aiVector3D v7 = aiVector3D(-1.0, 0.0, -t) / s; + const aiVector3D v8 = aiVector3D(0.0, t, 1.0) / s; + const aiVector3D v9 = aiVector3D(0.0, -t, 1.0) / s; + const aiVector3D v10 = aiVector3D(0.0, t, -1.0) / s; + const aiVector3D v11 = aiVector3D(0.0, -t, -1.0) / s; + + ADD_TRIANGLE(v0, v8, v4); + ADD_TRIANGLE(v0, v5, v10); + ADD_TRIANGLE(v2, v4, v9); + ADD_TRIANGLE(v2, v11, v5); + + ADD_TRIANGLE(v1, v6, v8); + ADD_TRIANGLE(v1, v10, v7); + ADD_TRIANGLE(v3, v9, v6); + ADD_TRIANGLE(v3, v7, v11); + + ADD_TRIANGLE(v0, v10, v8); + ADD_TRIANGLE(v1, v8, v10); + ADD_TRIANGLE(v2, v9, v11); + ADD_TRIANGLE(v3, v11, v9); + + ADD_TRIANGLE(v4, v2, v0); + ADD_TRIANGLE(v5, v0, v2); + ADD_TRIANGLE(v6, v1, v3); + ADD_TRIANGLE(v7, v3, v1); + + ADD_TRIANGLE(v8, v6, v4); + ADD_TRIANGLE(v9, v4, v6); + ADD_TRIANGLE(v10, v5, v7); + ADD_TRIANGLE(v11, v7, v5); + return 3; +} + +// ------------------------------------------------------------------------------------------------ +// Build a dodecahedron with points.magnitude == 1 +unsigned int StandardShapes::MakeDodecahedron(std::vector<aiVector3D> &positions, + bool polygons /*= false*/) { + positions.reserve(positions.size() + 108); + + const ai_real a = ai_real(1.0) / ai_real(1.7320508); + const ai_real b = std::sqrt((ai_real(3.0) - ai_real(2.23606797)) / ai_real(6.0)); + const ai_real c = std::sqrt((ai_real(3.0) + ai_real(2.23606797f)) / ai_real(6.0)); + + const aiVector3D v0 = aiVector3D(a, a, a); + const aiVector3D v1 = aiVector3D(a, a, -a); + const aiVector3D v2 = aiVector3D(a, -a, a); + const aiVector3D v3 = aiVector3D(a, -a, -a); + const aiVector3D v4 = aiVector3D(-a, a, a); + const aiVector3D v5 = aiVector3D(-a, a, -a); + const aiVector3D v6 = aiVector3D(-a, -a, a); + const aiVector3D v7 = aiVector3D(-a, -a, -a); + const aiVector3D v8 = aiVector3D(b, c, 0.0); + const aiVector3D v9 = aiVector3D(-b, c, 0.0); + const aiVector3D v10 = aiVector3D(b, -c, 0.0); + const aiVector3D v11 = aiVector3D(-b, -c, 0.0); + const aiVector3D v12 = aiVector3D(c, 0.0, b); + const aiVector3D v13 = aiVector3D(c, 0.0, -b); + const aiVector3D v14 = aiVector3D(-c, 0.0, b); + const aiVector3D v15 = aiVector3D(-c, 0.0, -b); + const aiVector3D v16 = aiVector3D(0.0, b, c); + const aiVector3D v17 = aiVector3D(0.0, -b, c); + const aiVector3D v18 = aiVector3D(0.0, b, -c); + const aiVector3D v19 = aiVector3D(0.0, -b, -c); + + ADD_PENTAGON(v0, v8, v9, v4, v16); + ADD_PENTAGON(v0, v12, v13, v1, v8); + ADD_PENTAGON(v0, v16, v17, v2, v12); + ADD_PENTAGON(v8, v1, v18, v5, v9); + ADD_PENTAGON(v12, v2, v10, v3, v13); + ADD_PENTAGON(v16, v4, v14, v6, v17); + ADD_PENTAGON(v9, v5, v15, v14, v4); + + ADD_PENTAGON(v6, v11, v10, v2, v17); + ADD_PENTAGON(v3, v19, v18, v1, v13); + ADD_PENTAGON(v7, v15, v5, v18, v19); + ADD_PENTAGON(v7, v11, v6, v14, v15); + ADD_PENTAGON(v7, v19, v3, v10, v11); + return (polygons ? 5 : 3); +} + +// ------------------------------------------------------------------------------------------------ +// Build an octahedron with points.magnitude == 1 +unsigned int StandardShapes::MakeOctahedron(std::vector<aiVector3D> &positions) { + positions.reserve(positions.size() + 24); + + const aiVector3D v0 = aiVector3D(1.0, 0.0, 0.0); + const aiVector3D v1 = aiVector3D(-1.0, 0.0, 0.0); + const aiVector3D v2 = aiVector3D(0.0, 1.0, 0.0); + const aiVector3D v3 = aiVector3D(0.0, -1.0, 0.0); + const aiVector3D v4 = aiVector3D(0.0, 0.0, 1.0); + const aiVector3D v5 = aiVector3D(0.0, 0.0, -1.0); + + ADD_TRIANGLE(v4, v0, v2); + ADD_TRIANGLE(v4, v2, v1); + ADD_TRIANGLE(v4, v1, v3); + ADD_TRIANGLE(v4, v3, v0); + + ADD_TRIANGLE(v5, v2, v0); + ADD_TRIANGLE(v5, v1, v2); + ADD_TRIANGLE(v5, v3, v1); + ADD_TRIANGLE(v5, v0, v3); + return 3; +} + +// ------------------------------------------------------------------------------------------------ +// Build a tetrahedron with points.magnitude == 1 +unsigned int StandardShapes::MakeTetrahedron(std::vector<aiVector3D> &positions) { + positions.reserve(positions.size() + 9); + + const ai_real invThree = ai_real(1.0) / ai_real(3.0); + const ai_real a = ai_real(1.41421) * invThree; + const ai_real b = ai_real(2.4494) * invThree; + + const aiVector3D v0 = aiVector3D(0.0, 0.0, 1.0); + const aiVector3D v1 = aiVector3D(2 * a, 0, -invThree); + const aiVector3D v2 = aiVector3D(-a, b, -invThree); + const aiVector3D v3 = aiVector3D(-a, -b, -invThree); + + ADD_TRIANGLE(v0, v1, v2); + ADD_TRIANGLE(v0, v2, v3); + ADD_TRIANGLE(v0, v3, v1); + ADD_TRIANGLE(v1, v3, v2); + return 3; +} + +// ------------------------------------------------------------------------------------------------ +// Build a hexahedron with points.magnitude == 1 +unsigned int StandardShapes::MakeHexahedron(std::vector<aiVector3D> &positions, + bool polygons /*= false*/) { + positions.reserve(positions.size() + 36); + const ai_real length = ai_real(1.0) / ai_real(1.73205080); + + const aiVector3D v0 = aiVector3D(-1.0, -1.0, -1.0) * length; + const aiVector3D v1 = aiVector3D(1.0, -1.0, -1.0) * length; + const aiVector3D v2 = aiVector3D(1.0, 1.0, -1.0) * length; + const aiVector3D v3 = aiVector3D(-1.0, 1.0, -1.0) * length; + const aiVector3D v4 = aiVector3D(-1.0, -1.0, 1.0) * length; + const aiVector3D v5 = aiVector3D(1.0, -1.0, 1.0) * length; + const aiVector3D v6 = aiVector3D(1.0, 1.0, 1.0) * length; + const aiVector3D v7 = aiVector3D(-1.0, 1.0, 1.0) * length; + + ADD_QUAD(v0, v3, v2, v1); + ADD_QUAD(v0, v1, v5, v4); + ADD_QUAD(v0, v4, v7, v3); + ADD_QUAD(v6, v5, v1, v2); + ADD_QUAD(v6, v2, v3, v7); + ADD_QUAD(v6, v7, v4, v5); + return (polygons ? 4 : 3); +} + +// Cleanup ... +#undef ADD_TRIANGLE +#undef ADD_QUAD +#undef ADD_PENTAGON + +// ------------------------------------------------------------------------------------------------ +// Create a subdivision sphere +void StandardShapes::MakeSphere(unsigned int tess, + std::vector<aiVector3D> &positions) { + // Reserve enough storage. Every subdivision + // splits each triangle in 4, the icosahedron consists of 60 verts + positions.reserve(positions.size() + 60 * integer_pow(4, tess)); + + // Construct an icosahedron to start with + MakeIcosahedron(positions); + + // ... and subdivide it until the requested output + // tessellation is reached + for (unsigned int i = 0; i < tess; ++i) + Subdivide(positions); +} + +// ------------------------------------------------------------------------------------------------ +// Build a cone +void StandardShapes::MakeCone(ai_real height, ai_real radius1, + ai_real radius2, unsigned int tess, + std::vector<aiVector3D> &positions, bool bOpen /*= false */) { + // Sorry, a cone with less than 3 segments makes ABSOLUTELY NO SENSE + if (tess < 3 || !height) + return; + + size_t old = positions.size(); + + // No negative radii + radius1 = std::fabs(radius1); + radius2 = std::fabs(radius2); + + ai_real halfHeight = height / ai_real(2.0); + + // radius1 is always the smaller one + if (radius2 > radius1) { + std::swap(radius2, radius1); + halfHeight = -halfHeight; + } else + old = SIZE_MAX; + + // Use a large epsilon to check whether the cone is pointy + if (radius1 < (radius2 - radius1) * 10e-3) radius1 = 0.0; + + // We will need 3*2 verts per segment + 3*2 verts per segment + // if the cone is closed + const unsigned int mem = tess * 6 + (!bOpen ? tess * 3 * (radius1 ? 2 : 1) : 0); + positions.reserve(positions.size() + mem); + + // Now construct all segments + const ai_real angle_delta = (ai_real)AI_MATH_TWO_PI / tess; + const ai_real angle_max = (ai_real)AI_MATH_TWO_PI; + + ai_real s = 1.0; // std::cos(angle == 0); + ai_real t = 0.0; // std::sin(angle == 0); + + for (ai_real angle = 0.0; angle < angle_max;) { + const aiVector3D v1 = aiVector3D(s * radius1, -halfHeight, t * radius1); + const aiVector3D v2 = aiVector3D(s * radius2, halfHeight, t * radius2); + + const ai_real next = angle + angle_delta; + ai_real s2 = std::cos(next); + ai_real t2 = std::sin(next); + + const aiVector3D v3 = aiVector3D(s2 * radius2, halfHeight, t2 * radius2); + const aiVector3D v4 = aiVector3D(s2 * radius1, -halfHeight, t2 * radius1); + + positions.push_back(v1); + positions.push_back(v2); + positions.push_back(v3); + positions.push_back(v4); + positions.push_back(v1); + positions.push_back(v3); + + if (!bOpen) { + // generate the end 'cap' + positions.push_back(aiVector3D(s * radius2, halfHeight, t * radius2)); + positions.push_back(aiVector3D(s2 * radius2, halfHeight, t2 * radius2)); + positions.push_back(aiVector3D(0.0, halfHeight, 0.0)); + + if (radius1) { + // generate the other end 'cap' + positions.push_back(aiVector3D(s * radius1, -halfHeight, t * radius1)); + positions.push_back(aiVector3D(s2 * radius1, -halfHeight, t2 * radius1)); + positions.push_back(aiVector3D(0.0, -halfHeight, 0.0)); + } + } + s = s2; + t = t2; + angle = next; + } + + // Need to flip face order? + if (SIZE_MAX != old) { + for (size_t p = old; p < positions.size(); p += 3) { + std::swap(positions[p], positions[p + 1]); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Build a circle +void StandardShapes::MakeCircle(ai_real radius, unsigned int tess, + std::vector<aiVector3D> &positions) { + // Sorry, a circle with less than 3 segments makes ABSOLUTELY NO SENSE + if (tess < 3 || !radius) + return; + + radius = std::fabs(radius); + + // We will need 3 vertices per segment + positions.reserve(positions.size() + tess * 3); + + const ai_real angle_delta = (ai_real)AI_MATH_TWO_PI / tess; + const ai_real angle_max = (ai_real)AI_MATH_TWO_PI; + + ai_real s = 1.0; // std::cos(angle == 0); + ai_real t = 0.0; // std::sin(angle == 0); + + for (ai_real angle = 0.0; angle < angle_max;) { + positions.push_back(aiVector3D(s * radius, 0.0, t * radius)); + angle += angle_delta; + s = std::cos(angle); + t = std::sin(angle); + positions.push_back(aiVector3D(s * radius, 0.0, t * radius)); + + positions.push_back(aiVector3D(0.0, 0.0, 0.0)); + } +} + +} // namespace Assimp diff --git a/libs/assimp/code/Common/StdOStreamLogStream.h b/libs/assimp/code/Common/StdOStreamLogStream.h new file mode 100644 index 0000000..cc0e062 --- /dev/null +++ b/libs/assimp/code/Common/StdOStreamLogStream.h @@ -0,0 +1,101 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ + +/** @file StdOStreamLogStream.h +* @brief Implementation of StdOStreamLogStream +*/ + +#ifndef AI_STROSTREAMLOGSTREAM_H_INC +#define AI_STROSTREAMLOGSTREAM_H_INC + +#include <assimp/LogStream.hpp> +#include <ostream> + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** @class StdOStreamLogStream + * @brief Logs into a std::ostream + */ +class StdOStreamLogStream : public LogStream { +public: + /** @brief Construction from an existing std::ostream + * @param _ostream Output stream to be used + */ + explicit StdOStreamLogStream(std::ostream& _ostream); + + /** @brief Destructor */ + ~StdOStreamLogStream(); + + /** @brief Writer */ + void write(const char* message); + +private: + std::ostream& mOstream; +}; + +// --------------------------------------------------------------------------- +// Default constructor +inline StdOStreamLogStream::StdOStreamLogStream(std::ostream& _ostream) +: mOstream (_ostream){ + // empty +} + +// --------------------------------------------------------------------------- +// Default constructor +inline StdOStreamLogStream::~StdOStreamLogStream() { + // empty +} + +// --------------------------------------------------------------------------- +// Write method +inline void StdOStreamLogStream::write(const char* message) { + mOstream << message; + mOstream.flush(); +} + +// --------------------------------------------------------------------------- + +} // Namespace Assimp + +#endif // guard diff --git a/libs/assimp/code/Common/Subdivision.cpp b/libs/assimp/code/Common/Subdivision.cpp new file mode 100644 index 0000000..705ea3f --- /dev/null +++ b/libs/assimp/code/Common/Subdivision.cpp @@ -0,0 +1,581 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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 <assimp/Subdivision.h> +#include <assimp/SceneCombiner.h> +#include <assimp/SpatialSort.h> +#include <assimp/Vertex.h> +#include <assimp/ai_assert.h> + +#include "PostProcessing/ProcessHelper.h" + +#include <stdio.h> + +using namespace Assimp; +void mydummy() {} + +#ifdef _MSC_VER +#pragma warning(disable : 4709) +#endif // _MSC_VER +// ------------------------------------------------------------------------------------------------ +/** Subdivider stub class to implement the Catmull-Clarke subdivision algorithm. The + * implementation is basing on recursive refinement. Directly evaluating the result is also + * possible and much quicker, but it depends on lengthy matrix lookup tables. */ +// ------------------------------------------------------------------------------------------------ +class CatmullClarkSubdivider : public Subdivider { +public: + void Subdivide(aiMesh *mesh, aiMesh *&out, unsigned int num, bool discard_input); + void Subdivide(aiMesh **smesh, size_t nmesh, + aiMesh **out, unsigned int num, bool discard_input); + + // --------------------------------------------------------------------------- + /** Intermediate description of an edge between two corners of a polygon*/ + // --------------------------------------------------------------------------- + struct Edge { + Edge() : + ref(0) {} + Vertex edge_point, midpoint; + unsigned int ref; + }; + + typedef std::vector<unsigned int> UIntVector; + typedef std::map<uint64_t, Edge> EdgeMap; + + // --------------------------------------------------------------------------- + // Hashing function to derive an index into an #EdgeMap from two given + // 'unsigned int' vertex coordinates (!!distinct coordinates - same + // vertex position == same index!!). + // NOTE - this leads to rare hash collisions if a) sizeof(unsigned int)>4 + // and (id[0]>2^32-1 or id[0]>2^32-1). + // MAKE_EDGE_HASH() uses temporaries, so INIT_EDGE_HASH() needs to be put + // at the head of every function which is about to use MAKE_EDGE_HASH(). + // Reason is that the hash is that hash construction needs to hold the + // invariant id0<id1 to identify an edge - else two hashes would refer + // to the same edge. + // --------------------------------------------------------------------------- +#define MAKE_EDGE_HASH(id0, id1) (eh_tmp0__ = id0, eh_tmp1__ = id1, \ + (eh_tmp0__ < eh_tmp1__ ? std::swap(eh_tmp0__, eh_tmp1__) : mydummy()), (uint64_t)eh_tmp0__ ^ ((uint64_t)eh_tmp1__ << 32u)) + +#define INIT_EDGE_HASH_TEMPORARIES() \ + unsigned int eh_tmp0__, eh_tmp1__; + +private: + void InternSubdivide(const aiMesh *const *smesh, + size_t nmesh, aiMesh **out, unsigned int num); +}; + +// ------------------------------------------------------------------------------------------------ +// Construct a subdivider of a specific type +Subdivider *Subdivider::Create(Algorithm algo) { + switch (algo) { + case CATMULL_CLARKE: + return new CatmullClarkSubdivider(); + }; + + ai_assert(false); + + return nullptr; // shouldn't happen +} + +// ------------------------------------------------------------------------------------------------ +// Call the Catmull Clark subdivision algorithm for one mesh +void CatmullClarkSubdivider::Subdivide( + aiMesh *mesh, + aiMesh *&out, + unsigned int num, + bool discard_input) { + ai_assert(mesh != out); + + Subdivide(&mesh, 1, &out, num, discard_input); +} + +// ------------------------------------------------------------------------------------------------ +// Call the Catmull Clark subdivision algorithm for multiple meshes +void CatmullClarkSubdivider::Subdivide( + aiMesh **smesh, + size_t nmesh, + aiMesh **out, + unsigned int num, + bool discard_input) { + ai_assert(nullptr != smesh); + ai_assert(nullptr != out); + + // course, both regions may not overlap + ai_assert(smesh < out || smesh + nmesh > out + nmesh); + if (!num) { + // No subdivision at all. Need to copy all the meshes .. argh. + if (discard_input) { + for (size_t s = 0; s < nmesh; ++s) { + out[s] = smesh[s]; + smesh[s] = nullptr; + } + } else { + for (size_t s = 0; s < nmesh; ++s) { + SceneCombiner::Copy(out + s, smesh[s]); + } + } + return; + } + + std::vector<aiMesh *> inmeshes; + std::vector<aiMesh *> outmeshes; + std::vector<unsigned int> maptbl; + + inmeshes.reserve(nmesh); + outmeshes.reserve(nmesh); + maptbl.reserve(nmesh); + + // Remove pure line and point meshes from the working set to reduce the + // number of edge cases the subdivider is forced to deal with. Line and + // point meshes are simply passed through. + for (size_t s = 0; s < nmesh; ++s) { + aiMesh *i = smesh[s]; + // FIX - mPrimitiveTypes might not yet be initialized + if (i->mPrimitiveTypes && (i->mPrimitiveTypes & (aiPrimitiveType_LINE | aiPrimitiveType_POINT)) == i->mPrimitiveTypes) { + ASSIMP_LOG_VERBOSE_DEBUG("Catmull-Clark Subdivider: Skipping pure line/point mesh"); + + if (discard_input) { + out[s] = i; + smesh[s] = nullptr; + } else { + SceneCombiner::Copy(out + s, i); + } + continue; + } + + outmeshes.push_back(nullptr); + inmeshes.push_back(i); + maptbl.push_back(static_cast<unsigned int>(s)); + } + + // Do the actual subdivision on the preallocated storage. InternSubdivide + // *always* assumes that enough storage is available, it does not bother + // checking any ranges. + ai_assert(inmeshes.size() == outmeshes.size()); + ai_assert(inmeshes.size() == maptbl.size()); + if (inmeshes.empty()) { + ASSIMP_LOG_WARN("Catmull-Clark Subdivider: Pure point/line scene, I can't do anything"); + return; + } + InternSubdivide(&inmeshes.front(), inmeshes.size(), &outmeshes.front(), num); + for (unsigned int i = 0; i < maptbl.size(); ++i) { + ai_assert(nullptr != outmeshes[i]); + out[maptbl[i]] = outmeshes[i]; + } + + if (discard_input) { + for (size_t s = 0; s < nmesh; ++s) { + delete smesh[s]; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Note - this is an implementation of the standard (recursive) Cm-Cl algorithm without further +// optimizations (except we're using some nice LUTs). A description of the algorithm can be found +// here: http://en.wikipedia.org/wiki/Catmull-Clark_subdivision_surface +// +// The code is mostly O(n), however parts are O(nlogn) which is therefore the algorithm's +// expected total runtime complexity. The implementation is able to work in-place on the same +// mesh arrays. Calling #InternSubdivide() directly is not encouraged. The code can operate +// in-place unless 'smesh' and 'out' are equal (no strange overlaps or reorderings). +// Previous data is replaced/deleted then. +// ------------------------------------------------------------------------------------------------ +void CatmullClarkSubdivider::InternSubdivide( + const aiMesh *const *smesh, + size_t nmesh, + aiMesh **out, + unsigned int num) { + ai_assert(nullptr != smesh); + ai_assert(nullptr != out); + + INIT_EDGE_HASH_TEMPORARIES(); + + // no subdivision requested or end of recursive refinement + if (!num) { + return; + } + + UIntVector maptbl; + SpatialSort spatial; + + // --------------------------------------------------------------------- + // 0. Offset table to index all meshes continuously, generate a spatially + // sorted representation of all vertices in all meshes. + // --------------------------------------------------------------------- + typedef std::pair<unsigned int, unsigned int> IntPair; + std::vector<IntPair> moffsets(nmesh); + unsigned int totfaces = 0, totvert = 0; + for (size_t t = 0; t < nmesh; ++t) { + const aiMesh *mesh = smesh[t]; + + spatial.Append(mesh->mVertices, mesh->mNumVertices, sizeof(aiVector3D), false); + moffsets[t] = IntPair(totfaces, totvert); + + totfaces += mesh->mNumFaces; + totvert += mesh->mNumVertices; + } + + spatial.Finalize(); + const unsigned int num_unique = spatial.GenerateMappingTable(maptbl, ComputePositionEpsilon(smesh, nmesh)); + +#define FLATTEN_VERTEX_IDX(mesh_idx, vert_idx) (moffsets[mesh_idx].second + vert_idx) +#define FLATTEN_FACE_IDX(mesh_idx, face_idx) (moffsets[mesh_idx].first + face_idx) + + // --------------------------------------------------------------------- + // 1. Compute the centroid point for all faces + // --------------------------------------------------------------------- + std::vector<Vertex> centroids(totfaces); + unsigned int nfacesout = 0; + for (size_t t = 0, n = 0; t < nmesh; ++t) { + const aiMesh *mesh = smesh[t]; + for (unsigned int i = 0; i < mesh->mNumFaces; ++i, ++n) { + const aiFace &face = mesh->mFaces[i]; + Vertex &c = centroids[n]; + + for (unsigned int a = 0; a < face.mNumIndices; ++a) { + c += Vertex(mesh, face.mIndices[a]); + } + + c /= static_cast<float>(face.mNumIndices); + nfacesout += face.mNumIndices; + } + } + + { + // we want edges to go away before the recursive calls so begin a new scope + EdgeMap edges; + + // --------------------------------------------------------------------- + // 2. Set each edge point to be the average of all neighbouring + // face points and original points. Every edge exists twice + // if there is a neighboring face. + // --------------------------------------------------------------------- + for (size_t t = 0; t < nmesh; ++t) { + const aiMesh *mesh = smesh[t]; + + for (unsigned int i = 0; i < mesh->mNumFaces; ++i) { + const aiFace &face = mesh->mFaces[i]; + + for (unsigned int p = 0; p < face.mNumIndices; ++p) { + const unsigned int id[] = { + face.mIndices[p], + face.mIndices[p == face.mNumIndices - 1 ? 0 : p + 1] + }; + const unsigned int mp[] = { + maptbl[FLATTEN_VERTEX_IDX(t, id[0])], + maptbl[FLATTEN_VERTEX_IDX(t, id[1])] + }; + + Edge &e = edges[MAKE_EDGE_HASH(mp[0], mp[1])]; + e.ref++; + if (e.ref <= 2) { + if (e.ref == 1) { // original points (end points) - add only once + e.edge_point = e.midpoint = Vertex(mesh, id[0]) + Vertex(mesh, id[1]); + e.midpoint *= 0.5f; + } + e.edge_point += centroids[FLATTEN_FACE_IDX(t, i)]; + } + } + } + } + + // --------------------------------------------------------------------- + // 3. Normalize edge points + // --------------------------------------------------------------------- + { + unsigned int bad_cnt = 0; + for (EdgeMap::iterator it = edges.begin(); it != edges.end(); ++it) { + if ((*it).second.ref < 2) { + ai_assert((*it).second.ref); + ++bad_cnt; + } + (*it).second.edge_point *= 1.f / ((*it).second.ref + 2.f); + } + + if (bad_cnt) { + // Report the number of bad edges. bad edges are referenced by less than two + // faces in the mesh. They occur at outer model boundaries in non-closed + // shapes. + ASSIMP_LOG_VERBOSE_DEBUG("Catmull-Clark Subdivider: got ", bad_cnt, " bad edges touching only one face (totally ", + static_cast<unsigned int>(edges.size()), " edges). "); + } + } + + // --------------------------------------------------------------------- + // 4. Compute a vertex-face adjacency table. We can't reuse the code + // from VertexTriangleAdjacency because we need the table for multiple + // meshes and out vertex indices need to be mapped to distinct values + // first. + // --------------------------------------------------------------------- + UIntVector faceadjac(nfacesout), cntadjfac(maptbl.size(), 0), ofsadjvec(maptbl.size() + 1, 0); + { + for (size_t t = 0; t < nmesh; ++t) { + const aiMesh *const minp = smesh[t]; + for (unsigned int i = 0; i < minp->mNumFaces; ++i) { + + const aiFace &f = minp->mFaces[i]; + for (unsigned int n = 0; n < f.mNumIndices; ++n) { + ++cntadjfac[maptbl[FLATTEN_VERTEX_IDX(t, f.mIndices[n])]]; + } + } + } + unsigned int cur = 0; + for (size_t i = 0; i < cntadjfac.size(); ++i) { + ofsadjvec[i + 1] = cur; + cur += cntadjfac[i]; + } + for (size_t t = 0; t < nmesh; ++t) { + const aiMesh *const minp = smesh[t]; + for (unsigned int i = 0; i < minp->mNumFaces; ++i) { + + const aiFace &f = minp->mFaces[i]; + for (unsigned int n = 0; n < f.mNumIndices; ++n) { + faceadjac[ofsadjvec[1 + maptbl[FLATTEN_VERTEX_IDX(t, f.mIndices[n])]]++] = FLATTEN_FACE_IDX(t, i); + } + } + } + + // check the other way round for consistency +#ifdef ASSIMP_BUILD_DEBUG + + for (size_t t = 0; t < ofsadjvec.size() - 1; ++t) { + for (unsigned int m = 0; m < cntadjfac[t]; ++m) { + const unsigned int fidx = faceadjac[ofsadjvec[t] + m]; + ai_assert(fidx < totfaces); + for (size_t n = 1; n < nmesh; ++n) { + + if (moffsets[n].first > fidx) { + const aiMesh *msh = smesh[--n]; + const aiFace &f = msh->mFaces[fidx - moffsets[n].first]; + + bool haveit = false; + for (unsigned int i = 0; i < f.mNumIndices; ++i) { + if (maptbl[FLATTEN_VERTEX_IDX(n, f.mIndices[i])] == (unsigned int)t) { + haveit = true; + break; + } + } + ai_assert(haveit); + if (!haveit) { + ASSIMP_LOG_VERBOSE_DEBUG("Catmull-Clark Subdivider: Index not used"); + } + break; + } + } + } + } + +#endif + } + +#define GET_ADJACENT_FACES_AND_CNT(vidx, fstartout, numout) \ + fstartout = &faceadjac[ofsadjvec[vidx]], numout = cntadjfac[vidx] + + typedef std::pair<bool, Vertex> TouchedOVertex; + std::vector<TouchedOVertex> new_points(num_unique, TouchedOVertex(false, Vertex())); + // --------------------------------------------------------------------- + // 5. Spawn a quad from each face point to the corresponding edge points + // the original points being the fourth quad points. + // --------------------------------------------------------------------- + for (size_t t = 0; t < nmesh; ++t) { + const aiMesh *const minp = smesh[t]; + aiMesh *const mout = out[t] = new aiMesh(); + + for (unsigned int a = 0; a < minp->mNumFaces; ++a) { + mout->mNumFaces += minp->mFaces[a].mNumIndices; + } + + // We need random access to the old face buffer, so reuse is not possible. + mout->mFaces = new aiFace[mout->mNumFaces]; + + mout->mNumVertices = mout->mNumFaces * 4; + mout->mVertices = new aiVector3D[mout->mNumVertices]; + + // quads only, keep material index + mout->mPrimitiveTypes = aiPrimitiveType_POLYGON; + mout->mMaterialIndex = minp->mMaterialIndex; + + if (minp->HasNormals()) { + mout->mNormals = new aiVector3D[mout->mNumVertices]; + } + + if (minp->HasTangentsAndBitangents()) { + mout->mTangents = new aiVector3D[mout->mNumVertices]; + mout->mBitangents = new aiVector3D[mout->mNumVertices]; + } + + for (unsigned int i = 0; minp->HasTextureCoords(i); ++i) { + mout->mTextureCoords[i] = new aiVector3D[mout->mNumVertices]; + mout->mNumUVComponents[i] = minp->mNumUVComponents[i]; + } + + for (unsigned int i = 0; minp->HasVertexColors(i); ++i) { + mout->mColors[i] = new aiColor4D[mout->mNumVertices]; + } + + mout->mNumVertices = mout->mNumFaces << 2u; + for (unsigned int i = 0, v = 0, n = 0; i < minp->mNumFaces; ++i) { + + const aiFace &face = minp->mFaces[i]; + for (unsigned int a = 0; a < face.mNumIndices; ++a) { + + // Get a clean new face. + aiFace &faceOut = mout->mFaces[n++]; + faceOut.mIndices = new unsigned int[faceOut.mNumIndices = 4]; + + // Spawn a new quadrilateral (ccw winding) for this original point between: + // a) face centroid + centroids[FLATTEN_FACE_IDX(t, i)].SortBack(mout, faceOut.mIndices[0] = v++); + + // b) adjacent edge on the left, seen from the centroid + const Edge &e0 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t, face.mIndices[a])], + maptbl[FLATTEN_VERTEX_IDX(t, face.mIndices[a == face.mNumIndices - 1 ? 0 : a + 1])])]; // fixme: replace with mod face.mNumIndices? + + // c) adjacent edge on the right, seen from the centroid + const Edge &e1 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t, face.mIndices[a])], + maptbl[FLATTEN_VERTEX_IDX(t, face.mIndices[!a ? face.mNumIndices - 1 : a - 1])])]; // fixme: replace with mod face.mNumIndices? + + e0.edge_point.SortBack(mout, faceOut.mIndices[3] = v++); + e1.edge_point.SortBack(mout, faceOut.mIndices[1] = v++); + + // d= original point P with distinct index i + // F := 0 + // R := 0 + // n := 0 + // for each face f containing i + // F := F+ centroid of f + // R := R+ midpoint of edge of f from i to i+1 + // n := n+1 + // + // (F+2R+(n-3)P)/n + const unsigned int org = maptbl[FLATTEN_VERTEX_IDX(t, face.mIndices[a])]; + TouchedOVertex &ov = new_points[org]; + + if (!ov.first) { + ov.first = true; + + const unsigned int *adj; + unsigned int cnt; + GET_ADJACENT_FACES_AND_CNT(org, adj, cnt); + + if (cnt < 3) { + ov.second = Vertex(minp, face.mIndices[a]); + } else { + + Vertex F, R; + for (unsigned int o = 0; o < cnt; ++o) { + ai_assert(adj[o] < totfaces); + F += centroids[adj[o]]; + + // adj[0] is a global face index - search the face in the mesh list + const aiMesh *mp = nullptr; + size_t nidx; + + if (adj[o] < moffsets[0].first) { + mp = smesh[nidx = 0]; + } else { + for (nidx = 1; nidx <= nmesh; ++nidx) { + if (nidx == nmesh || moffsets[nidx].first > adj[o]) { + mp = smesh[--nidx]; + break; + } + } + } + + ai_assert(adj[o] - moffsets[nidx].first < mp->mNumFaces); + const aiFace &f = mp->mFaces[adj[o] - moffsets[nidx].first]; + bool haveit = false; + + // find our original point in the face + for (unsigned int m = 0; m < f.mNumIndices; ++m) { + if (maptbl[FLATTEN_VERTEX_IDX(nidx, f.mIndices[m])] == org) { + + // add *both* edges. this way, we can be sure that we add + // *all* adjacent edges to R. In a closed shape, every + // edge is added twice - so we simply leave out the + // factor 2.f in the amove formula and get the right + // result. + + const Edge &c0 = edges[MAKE_EDGE_HASH(org, maptbl[FLATTEN_VERTEX_IDX( + nidx, f.mIndices[!m ? f.mNumIndices - 1 : m - 1])])]; + // fixme: replace with mod face.mNumIndices? + + const Edge &c1 = edges[MAKE_EDGE_HASH(org, maptbl[FLATTEN_VERTEX_IDX( + nidx, f.mIndices[m == f.mNumIndices - 1 ? 0 : m + 1])])]; + // fixme: replace with mod face.mNumIndices? + R += c0.midpoint + c1.midpoint; + + haveit = true; + break; + } + } + + // this invariant *must* hold if the vertex-to-face adjacency table is valid + ai_assert(haveit); + if (!haveit) { + ASSIMP_LOG_WARN("OBJ: no name for material library specified."); + } + } + + const float div = static_cast<float>(cnt), divsq = 1.f / (div * div); + ov.second = Vertex(minp, face.mIndices[a]) * ((div - 3.f) / div) + R * divsq + F * divsq; + } + } + ov.second.SortBack(mout, faceOut.mIndices[2] = v++); + } + } + } + } // end of scope for edges, freeing its memory + + // --------------------------------------------------------------------- + // 7. Apply the next subdivision step. + // --------------------------------------------------------------------- + if (num != 1) { + std::vector<aiMesh *> tmp(nmesh); + InternSubdivide(out, nmesh, &tmp.front(), num - 1); + for (size_t i = 0; i < nmesh; ++i) { + delete out[i]; + out[i] = tmp[i]; + } + } +} diff --git a/libs/assimp/code/Common/TargetAnimation.cpp b/libs/assimp/code/Common/TargetAnimation.cpp new file mode 100644 index 0000000..5f6d9ba --- /dev/null +++ b/libs/assimp/code/Common/TargetAnimation.cpp @@ -0,0 +1,226 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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 "TargetAnimation.h" +#include <assimp/ai_assert.h> +#include <algorithm> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +KeyIterator::KeyIterator(const std::vector<aiVectorKey> *_objPos, + const std::vector<aiVectorKey> *_targetObjPos, + const aiVector3D *defaultObjectPos /*= nullptr*/, + const aiVector3D *defaultTargetPos /*= nullptr*/) : + reachedEnd(false), + curTime(-1.), + objPos(_objPos), + targetObjPos(_targetObjPos), + nextObjPos(0), + nextTargetObjPos(0) { + // Generate default transformation tracks if necessary + if (!objPos || objPos->empty()) { + defaultObjPos.resize(1); + defaultObjPos.front().mTime = 10e10; + + if (defaultObjectPos) + defaultObjPos.front().mValue = *defaultObjectPos; + + objPos = &defaultObjPos; + } + if (!targetObjPos || targetObjPos->empty()) { + defaultTargetObjPos.resize(1); + defaultTargetObjPos.front().mTime = 10e10; + + if (defaultTargetPos) + defaultTargetObjPos.front().mValue = *defaultTargetPos; + + targetObjPos = &defaultTargetObjPos; + } +} + +// ------------------------------------------------------------------------------------------------ +template <class T> +inline T Interpolate(const T &one, const T &two, ai_real val) { + return one + (two - one) * val; +} + +// ------------------------------------------------------------------------------------------------ +void KeyIterator::operator++() { + // If we are already at the end of all keyframes, return + if (reachedEnd) { + return; + } + + // Now search in all arrays for the time value closest + // to our current position on the time line + double d0, d1; + + d0 = objPos->at(std::min(nextObjPos, static_cast<unsigned int>(objPos->size() - 1))).mTime; + d1 = targetObjPos->at(std::min(nextTargetObjPos, static_cast<unsigned int>(targetObjPos->size() - 1))).mTime; + + // Easiest case - all are identical. In this + // case we don't need to interpolate so we can + // return earlier + if (d0 == d1) { + curTime = d0; + curPosition = objPos->at(nextObjPos).mValue; + curTargetPosition = targetObjPos->at(nextTargetObjPos).mValue; + + // increment counters + if (objPos->size() != nextObjPos - 1) + ++nextObjPos; + + if (targetObjPos->size() != nextTargetObjPos - 1) + ++nextTargetObjPos; + } + + // An object position key is closest to us + else if (d0 < d1) { + curTime = d0; + + // interpolate the other + if (1 == targetObjPos->size() || !nextTargetObjPos) { + curTargetPosition = targetObjPos->at(0).mValue; + } else { + const aiVectorKey &last = targetObjPos->at(nextTargetObjPos); + const aiVectorKey &first = targetObjPos->at(nextTargetObjPos - 1); + + curTargetPosition = Interpolate(first.mValue, last.mValue, (ai_real)((curTime - first.mTime) / (last.mTime - first.mTime))); + } + + if (objPos->size() != nextObjPos - 1) + ++nextObjPos; + } + // A target position key is closest to us + else { + curTime = d1; + + // interpolate the other + if (1 == objPos->size() || !nextObjPos) { + curPosition = objPos->at(0).mValue; + } else { + const aiVectorKey &last = objPos->at(nextObjPos); + const aiVectorKey &first = objPos->at(nextObjPos - 1); + + curPosition = Interpolate(first.mValue, last.mValue, (ai_real)((curTime - first.mTime) / (last.mTime - first.mTime))); + } + + if (targetObjPos->size() != nextTargetObjPos - 1) + ++nextTargetObjPos; + } + + if (nextObjPos >= objPos->size() - 1 && + nextTargetObjPos >= targetObjPos->size() - 1) { + // We reached the very last keyframe + reachedEnd = true; + } +} + +// ------------------------------------------------------------------------------------------------ +void TargetAnimationHelper::SetTargetAnimationChannel( + const std::vector<aiVectorKey> *_targetPositions) { + ai_assert(nullptr != _targetPositions); + + targetPositions = _targetPositions; +} + +// ------------------------------------------------------------------------------------------------ +void TargetAnimationHelper::SetMainAnimationChannel( + const std::vector<aiVectorKey> *_objectPositions) { + ai_assert(nullptr != _objectPositions); + + objectPositions = _objectPositions; +} + +// ------------------------------------------------------------------------------------------------ +void TargetAnimationHelper::SetFixedMainAnimationChannel( + const aiVector3D &fixed) { + objectPositions = nullptr; // just to avoid confusion + fixedMain = fixed; +} + +// ------------------------------------------------------------------------------------------------ +void TargetAnimationHelper::Process(std::vector<aiVectorKey> *distanceTrack) { + ai_assert(nullptr != targetPositions); + ai_assert(nullptr != distanceTrack); + + // TODO: in most cases we won't need the extra array + std::vector<aiVectorKey> real; + + std::vector<aiVectorKey> *fill = (distanceTrack == objectPositions ? &real : distanceTrack); + fill->reserve(std::max(objectPositions->size(), targetPositions->size())); + + // Iterate through all object keys and interpolate their values if necessary. + // Then get the corresponding target position, compute the difference + // vector between object and target position. Then compute a rotation matrix + // that rotates the base vector of the object coordinate system at that time + // to match the diff vector. + + KeyIterator iter(objectPositions, targetPositions, &fixedMain); + for (; !iter.Finished(); ++iter) { + const aiVector3D &position = iter.GetCurPosition(); + const aiVector3D &tposition = iter.GetCurTargetPosition(); + + // diff vector + aiVector3D diff = tposition - position; + ai_real f = diff.Length(); + + // output distance vector + if (f) { + fill->push_back(aiVectorKey()); + aiVectorKey &v = fill->back(); + v.mTime = iter.GetCurTime(); + v.mValue = diff; + + diff /= f; + } else { + // FIXME: handle this + } + + // diff is now the vector in which our camera is pointing + } + + if (real.size()) { + *distanceTrack = real; + } +} diff --git a/libs/assimp/code/Common/TargetAnimation.h b/libs/assimp/code/Common/TargetAnimation.h new file mode 100644 index 0000000..863406b --- /dev/null +++ b/libs/assimp/code/Common/TargetAnimation.h @@ -0,0 +1,161 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a helper class for the ASE and 3DS loaders to + help them compute camera and spot light animation channels */ +#ifndef AI_TARGET_ANIMATION_H_INC +#define AI_TARGET_ANIMATION_H_INC + +#include <assimp/anim.h> +#include <vector> + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** Helper class to iterate through all keys in an animation channel. + * + * Missing tracks are interpolated. This is a helper class for + * TargetAnimationHelper, but it can be freely used for other purposes. +*/ +class KeyIterator { +public: + // ------------------------------------------------------------------ + /** Constructs a new key iterator + * + * @param _objPos Object position track. May be nullptr. + * @param _targetObjPos Target object position track. May be nullptr. + * @param defaultObjectPos Default object position to be used if + * no animated track is available. May be nullptr. + * @param defaultTargetPos Default target position to be used if + * no animated track is available. May be nullptr. + */ + KeyIterator(const std::vector<aiVectorKey> *_objPos, + const std::vector<aiVectorKey> *_targetObjPos, + const aiVector3D *defaultObjectPos = nullptr, + const aiVector3D *defaultTargetPos = nullptr); + + // ------------------------------------------------------------------ + /** Returns true if all keys have been processed + */ + bool Finished() const { return reachedEnd; } + + // ------------------------------------------------------------------ + /** Increment the iterator + */ + void operator++(); + inline void operator++(int) { return ++(*this); } + + // ------------------------------------------------------------------ + /** Getters to retrieve the current state of the iterator + */ + inline const aiVector3D &GetCurPosition() const { return curPosition; } + + inline const aiVector3D &GetCurTargetPosition() const { return curTargetPosition; } + + inline double GetCurTime() const { return curTime; } + +private: + //! Did we reach the end? + bool reachedEnd; + + //! Represents the current position of the iterator + aiVector3D curPosition, curTargetPosition; + + double curTime; + + //! Input tracks and the next key to process + const std::vector<aiVectorKey> *objPos, *targetObjPos; + + unsigned int nextObjPos, nextTargetObjPos; + std::vector<aiVectorKey> defaultObjPos, defaultTargetObjPos; +}; + +// --------------------------------------------------------------------------- +/** Helper class for the 3DS and ASE loaders to compute camera and spot light + * animations. + * + * 3DS and ASE store the differently to Assimp - there is an animation + * channel for the camera/spot light itself and a separate position + * animation channels specifying the position of the camera/spot light + * look-at target */ +class TargetAnimationHelper { +public: + TargetAnimationHelper() : + targetPositions(nullptr), + objectPositions(nullptr) { + // empty + } + + // ------------------------------------------------------------------ + /** Sets the target animation channel + * + * This channel specifies the position of the camera/spot light + * target at a specific position. + * + * @param targetPositions Translation channel*/ + void SetTargetAnimationChannel(const std::vector<aiVectorKey> *targetPositions); + + // ------------------------------------------------------------------ + /** Sets the main animation channel + * + * @param objectPositions Translation channel */ + void SetMainAnimationChannel(const std::vector<aiVectorKey> *objectPositions); + + // ------------------------------------------------------------------ + /** Sets the main animation channel to a fixed value + * + * @param fixed Fixed value for the main animation channel*/ + void SetFixedMainAnimationChannel(const aiVector3D &fixed); + + // ------------------------------------------------------------------ + /** Computes final animation channels + * @param distanceTrack Receive camera translation keys ... != nullptr. */ + void Process(std::vector<aiVectorKey> *distanceTrack); + +private: + const std::vector<aiVectorKey> *targetPositions, *objectPositions; + aiVector3D fixedMain; +}; + +} // namespace Assimp + +#endif // include guard diff --git a/libs/assimp/code/Common/Version.cpp b/libs/assimp/code/Common/Version.cpp new file mode 100644 index 0000000..86d3f22 --- /dev/null +++ b/libs/assimp/code/Common/Version.cpp @@ -0,0 +1,186 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ + +// Actually just a dummy, used by the compiler to build the pre-compiled header. + +#include "ScenePrivate.h" +#include <assimp/scene.h> +#include <assimp/version.h> + +#include "revision.h" + +// -------------------------------------------------------------------------------- +// Legal information string - don't remove this. +static const char *LEGAL_INFORMATION = + "Open Asset Import Library (Assimp).\n" + "A free C/C++ library to import various 3D file formats into applications\n\n" + "(c) 2006-2022, Assimp team\n" + "License under the terms and conditions of the 3-clause BSD license\n" + "https://www.assimp.org\n"; + +// ------------------------------------------------------------------------------------------------ +// Get legal string +ASSIMP_API const char *aiGetLegalString() { + return LEGAL_INFORMATION; +} + +// ------------------------------------------------------------------------------------------------ +// Get Assimp patch version +ASSIMP_API unsigned int aiGetVersionPatch() { + return VER_PATCH; +} + +// ------------------------------------------------------------------------------------------------ +// Get Assimp minor version +ASSIMP_API unsigned int aiGetVersionMinor() { + return VER_MINOR; +} + +// ------------------------------------------------------------------------------------------------ +// Get Assimp major version +ASSIMP_API unsigned int aiGetVersionMajor() { + return VER_MAJOR; +} + +// ------------------------------------------------------------------------------------------------ +// Get flags used for compilation +ASSIMP_API unsigned int aiGetCompileFlags() { + + unsigned int flags = 0; + +#ifdef ASSIMP_BUILD_BOOST_WORKAROUND + flags |= ASSIMP_CFLAGS_NOBOOST; +#endif +#ifdef ASSIMP_BUILD_SINGLETHREADED + flags |= ASSIMP_CFLAGS_SINGLETHREADED; +#endif +#ifdef ASSIMP_BUILD_DEBUG + flags |= ASSIMP_CFLAGS_DEBUG; +#endif +#ifdef ASSIMP_BUILD_DLL_EXPORT + flags |= ASSIMP_CFLAGS_SHARED; +#endif +#ifdef _STLPORT_VERSION + flags |= ASSIMP_CFLAGS_STLPORT; +#endif +#ifdef ASSIMP_DOUBLE_PRECISION + flags |= ASSIMP_CFLAGS_DOUBLE_SUPPORT; +#endif + + return flags; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API unsigned int aiGetVersionRevision() { + return GitVersion; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API const char *aiGetBranchName() { + return GitBranch; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiScene::aiScene() : + mFlags(0), + mRootNode(nullptr), + mNumMeshes(0), + mMeshes(nullptr), + mNumMaterials(0), + mMaterials(nullptr), + mNumAnimations(0), + mAnimations(nullptr), + mNumTextures(0), + mTextures(nullptr), + mNumLights(0), + mLights(nullptr), + mNumCameras(0), + mCameras(nullptr), + mMetaData(nullptr), + mPrivate(new Assimp::ScenePrivateData()) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiScene::~aiScene() { + // delete all sub-objects recursively + delete mRootNode; + + // To make sure we won't crash if the data is invalid it's + // much better to check whether both mNumXXX and mXXX are + // valid instead of relying on just one of them. + if (mNumMeshes && mMeshes) + for (unsigned int a = 0; a < mNumMeshes; a++) + delete mMeshes[a]; + delete[] mMeshes; + + if (mNumMaterials && mMaterials) { + for (unsigned int a = 0; a < mNumMaterials; ++a) { + delete mMaterials[a]; + } + } + delete[] mMaterials; + + if (mNumAnimations && mAnimations) + for (unsigned int a = 0; a < mNumAnimations; a++) + delete mAnimations[a]; + delete[] mAnimations; + + if (mNumTextures && mTextures) + for (unsigned int a = 0; a < mNumTextures; a++) + delete mTextures[a]; + delete[] mTextures; + + if (mNumLights && mLights) + for (unsigned int a = 0; a < mNumLights; a++) + delete mLights[a]; + delete[] mLights; + + if (mNumCameras && mCameras) + for (unsigned int a = 0; a < mNumCameras; a++) + delete mCameras[a]; + delete[] mCameras; + + aiMetadata::Dealloc(mMetaData); + mMetaData = nullptr; + + delete static_cast<Assimp::ScenePrivateData *>(mPrivate); +} diff --git a/libs/assimp/code/Common/VertexTriangleAdjacency.cpp b/libs/assimp/code/Common/VertexTriangleAdjacency.cpp new file mode 100644 index 0000000..555e3e3 --- /dev/null +++ b/libs/assimp/code/Common/VertexTriangleAdjacency.cpp @@ -0,0 +1,131 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the VertexTriangleAdjacency helper class + */ + +// internal headers +#include "VertexTriangleAdjacency.h" +#include <assimp/mesh.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces, + unsigned int iNumFaces, + unsigned int iNumVertices /*= 0*/, + bool bComputeNumTriangles /*= false*/) { + // compute the number of referenced vertices if it wasn't specified by the caller + const aiFace *const pcFaceEnd = pcFaces + iNumFaces; + if (0 == iNumVertices) { + for (aiFace *pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) { + ai_assert(nullptr != pcFace); + ai_assert(3 == pcFace->mNumIndices); + iNumVertices = std::max(iNumVertices, pcFace->mIndices[0]); + iNumVertices = std::max(iNumVertices, pcFace->mIndices[1]); + iNumVertices = std::max(iNumVertices, pcFace->mIndices[2]); + } + } + + mNumVertices = iNumVertices + 1; + + unsigned int *pi; + + // allocate storage + if (bComputeNumTriangles) { + pi = mLiveTriangles = new unsigned int[iNumVertices + 1]; + ::memset(mLiveTriangles, 0, sizeof(unsigned int) * (iNumVertices + 1)); + mOffsetTable = new unsigned int[iNumVertices + 2] + 1; + } else { + pi = mOffsetTable = new unsigned int[iNumVertices + 2] + 1; + ::memset(mOffsetTable, 0, sizeof(unsigned int) * (iNumVertices + 1)); + mLiveTriangles = nullptr; // important, otherwise the d'tor would crash + } + + // get a pointer to the end of the buffer + unsigned int *piEnd = pi + iNumVertices; + *piEnd++ = 0u; + + // first pass: compute the number of faces referencing each vertex + for (aiFace *pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) { + unsigned nind = pcFace->mNumIndices; + unsigned *ind = pcFace->mIndices; + if (nind > 0) pi[ind[0]]++; + if (nind > 1) pi[ind[1]]++; + if (nind > 2) pi[ind[2]]++; + } + + // second pass: compute the final offset table + unsigned int iSum = 0; + unsigned int *piCurOut = this->mOffsetTable; + for (unsigned int *piCur = pi; piCur != piEnd; ++piCur, ++piCurOut) { + + unsigned int iLastSum = iSum; + iSum += *piCur; + *piCurOut = iLastSum; + } + pi = this->mOffsetTable; + + // third pass: compute the final table + this->mAdjacencyTable = new unsigned int[iSum]; + iSum = 0; + for (aiFace *pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace, ++iSum) { + unsigned nind = pcFace->mNumIndices; + unsigned *ind = pcFace->mIndices; + + if (nind > 0) mAdjacencyTable[pi[ind[0]]++] = iSum; + if (nind > 1) mAdjacencyTable[pi[ind[1]]++] = iSum; + if (nind > 2) mAdjacencyTable[pi[ind[2]]++] = iSum; + } + // fourth pass: undo the offset computations made during the third pass + // We could do this in a separate buffer, but this would be TIMES slower. + --mOffsetTable; + *mOffsetTable = 0u; +} +// ------------------------------------------------------------------------------------------------ +VertexTriangleAdjacency::~VertexTriangleAdjacency() { + // delete allocated storage + delete[] mOffsetTable; + delete[] mAdjacencyTable; + delete[] mLiveTriangles; +} diff --git a/libs/assimp/code/Common/VertexTriangleAdjacency.h b/libs/assimp/code/Common/VertexTriangleAdjacency.h new file mode 100644 index 0000000..41d8094 --- /dev/null +++ b/libs/assimp/code/Common/VertexTriangleAdjacency.h @@ -0,0 +1,117 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a helper class to compute a vertex-triangle adjacency map */ +#ifndef AI_VTADJACENCY_H_INC +#define AI_VTADJACENCY_H_INC + +#include "BaseProcess.h" +#include <assimp/types.h> +#include <assimp/ai_assert.h> + +struct aiMesh; +struct aiFace; + +namespace Assimp { + +// -------------------------------------------------------------------------------------------- +/** @brief The VertexTriangleAdjacency class computes a vertex-triangle + * adjacency map from a given index buffer. + * + * @note Although it is called #VertexTriangleAdjacency, the current version does also + * support arbitrary polygons. */ +// -------------------------------------------------------------------------------------------- +class ASSIMP_API VertexTriangleAdjacency { +public: + // ---------------------------------------------------------------------------- + /** @brief Construction from an existing index buffer + * @param pcFaces Index buffer + * @param iNumFaces Number of faces in the buffer + * @param iNumVertices Number of referenced vertices. This value + * is computed automatically if 0 is specified. + * @param bComputeNumTriangles If you want the class to compute + * a list containing the number of referenced triangles per vertex + * per vertex - pass true. */ + VertexTriangleAdjacency(aiFace* pcFaces,unsigned int iNumFaces, + unsigned int iNumVertices = 0, + bool bComputeNumTriangles = true); + + // ---------------------------------------------------------------------------- + /** @brief Destructor */ + ~VertexTriangleAdjacency(); + + // ---------------------------------------------------------------------------- + /** @brief Get all triangles adjacent to a vertex + * @param iVertIndex Index of the vertex + * @return A pointer to the adjacency list. */ + unsigned int* GetAdjacentTriangles(unsigned int iVertIndex) const { + ai_assert(iVertIndex < mNumVertices); + return &mAdjacencyTable[ mOffsetTable[iVertIndex]]; + } + + // ---------------------------------------------------------------------------- + /** @brief Get the number of triangles that are referenced by + * a vertex. This function returns a reference that can be modified + * @param iVertIndex Index of the vertex + * @return Number of referenced triangles */ + unsigned int& GetNumTrianglesPtr(unsigned int iVertIndex) { + ai_assert( iVertIndex < mNumVertices ); + ai_assert( nullptr != mLiveTriangles ); + return mLiveTriangles[iVertIndex]; + } + + //! Offset table + unsigned int* mOffsetTable; + + //! Adjacency table + unsigned int* mAdjacencyTable; + + //! Table containing the number of referenced triangles per vertex + unsigned int* mLiveTriangles; + + //! Debug: Number of referenced vertices + unsigned int mNumVertices; +}; + +} //! ns Assimp + +#endif // !! AI_VTADJACENCY_H_INC diff --git a/libs/assimp/code/Common/Win32DebugLogStream.h b/libs/assimp/code/Common/Win32DebugLogStream.h new file mode 100644 index 0000000..34d849e --- /dev/null +++ b/libs/assimp/code/Common/Win32DebugLogStream.h @@ -0,0 +1,95 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ + +/** @file Win32DebugLogStream.h +* @brief Implementation of Win32DebugLogStream +*/ +#ifndef AI_WIN32DEBUGLOGSTREAM_H_INC +#define AI_WIN32DEBUGLOGSTREAM_H_INC + +#ifdef _WIN32 + +#include <assimp/LogStream.hpp> +#include "windows.h" + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** @class Win32DebugLogStream + * @brief Logs into the debug stream from win32. + */ +class Win32DebugLogStream : public LogStream { +public: + /** @brief Default constructor */ + Win32DebugLogStream(); + + /** @brief Destructor */ + ~Win32DebugLogStream(); + + /** @brief Writer */ + void write(const char* messgae); +}; + +// --------------------------------------------------------------------------- +inline +Win32DebugLogStream::Win32DebugLogStream(){ + // empty +} + +// --------------------------------------------------------------------------- +inline +Win32DebugLogStream::~Win32DebugLogStream(){ + // empty +} + +// --------------------------------------------------------------------------- +inline +void Win32DebugLogStream::write(const char* message) { + ::OutputDebugStringA( message); +} + +// --------------------------------------------------------------------------- +} // Namespace Assimp + +#endif // ! _WIN32 +#endif // guard diff --git a/libs/assimp/code/Common/ZipArchiveIOSystem.cpp b/libs/assimp/code/Common/ZipArchiveIOSystem.cpp new file mode 100644 index 0000000..c322b14 --- /dev/null +++ b/libs/assimp/code/Common/ZipArchiveIOSystem.cpp @@ -0,0 +1,563 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +/** @file ZipArchiveIOSystem.cpp + * @brief Zip File I/O implementation for #Importer + */ + +#include <assimp/BaseImporter.h> +#include <assimp/ZipArchiveIOSystem.h> + +#include <assimp/ai_assert.h> + +#include <map> +#include <memory> + +#ifdef ASSIMP_USE_HUNTER +# include <minizip/unzip.h> +#else +# include <unzip.h> +#endif + +namespace Assimp { + +// ---------------------------------------------------------------- +// A read-only file inside a ZIP + +class ZipFile : public IOStream { + friend class ZipFileInfo; + explicit ZipFile(std::string &filename, size_t size); + +public: + std::string m_Filename; + virtual ~ZipFile(); + + // IOStream interface + size_t Read(void *pvBuffer, size_t pSize, size_t pCount) override; + size_t Write(const void * /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) override { return 0; } + size_t FileSize() const override; + aiReturn Seek(size_t pOffset, aiOrigin pOrigin) override; + size_t Tell() const override; + void Flush() override {} + +private: + size_t m_Size = 0; + size_t m_SeekPtr = 0; + std::unique_ptr<uint8_t[]> m_Buffer; +}; + + +// ---------------------------------------------------------------- +// Wraps an existing Assimp::IOSystem for unzip +class IOSystem2Unzip { +public: + static voidpf open(voidpf opaque, const char *filename, int mode); + static voidpf opendisk(voidpf opaque, voidpf stream, uint32_t number_disk, int mode); + static uLong read(voidpf opaque, voidpf stream, void *buf, uLong size); + static uLong write(voidpf opaque, voidpf stream, const void *buf, uLong size); + static long tell(voidpf opaque, voidpf stream); + static long seek(voidpf opaque, voidpf stream, uLong offset, int origin); + static int close(voidpf opaque, voidpf stream); + static int testerror(voidpf opaque, voidpf stream); + static zlib_filefunc_def get(IOSystem *pIOHandler); +}; + +voidpf IOSystem2Unzip::open(voidpf opaque, const char *filename, int mode) { + IOSystem *io_system = reinterpret_cast<IOSystem *>(opaque); + + const char *mode_fopen = nullptr; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) { + mode_fopen = "rb"; + } else { + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) { + mode_fopen = "r+b"; + } else { + if (mode & ZLIB_FILEFUNC_MODE_CREATE) { + mode_fopen = "wb"; + } + } + } + + return (voidpf)io_system->Open(filename, mode_fopen); +} + +voidpf IOSystem2Unzip::opendisk(voidpf opaque, voidpf stream, uint32_t number_disk, int mode) { + ZipFile *io_stream = (ZipFile *)stream; + voidpf ret = NULL; + size_t i; + + char *disk_filename = (char*)malloc(io_stream->m_Filename.length() + 1); + strncpy(disk_filename, io_stream->m_Filename.c_str(), io_stream->m_Filename.length() + 1); + for (i = io_stream->m_Filename.length() - 1; i >= 0; i -= 1) + { + if (disk_filename[i] != '.') + continue; + snprintf(&disk_filename[i], io_stream->m_Filename.length() - i, ".z%02u", number_disk + 1); + break; + } + + if (i >= 0) + ret = open(opaque, disk_filename, mode); + + free(disk_filename); + return ret; +} + +uLong IOSystem2Unzip::read(voidpf /*opaque*/, voidpf stream, void *buf, uLong size) { + IOStream *io_stream = (IOStream *)stream; + + return static_cast<uLong>(io_stream->Read(buf, 1, size)); +} + +uLong IOSystem2Unzip::write(voidpf /*opaque*/, voidpf stream, const void *buf, uLong size) { + IOStream *io_stream = (IOStream *)stream; + + return static_cast<uLong>(io_stream->Write(buf, 1, size)); +} + +long IOSystem2Unzip::tell(voidpf /*opaque*/, voidpf stream) { + IOStream *io_stream = (IOStream *)stream; + + return static_cast<long>(io_stream->Tell()); +} + +long IOSystem2Unzip::seek(voidpf /*opaque*/, voidpf stream, uLong offset, int origin) { + IOStream *io_stream = (IOStream *)stream; + + aiOrigin assimp_origin; + switch (origin) { + default: + case ZLIB_FILEFUNC_SEEK_CUR: + assimp_origin = aiOrigin_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END: + assimp_origin = aiOrigin_END; + break; + case ZLIB_FILEFUNC_SEEK_SET: + assimp_origin = aiOrigin_SET; + break; + } + + return (io_stream->Seek(offset, assimp_origin) == aiReturn_SUCCESS ? 0 : -1); +} + +int IOSystem2Unzip::close(voidpf opaque, voidpf stream) { + IOSystem *io_system = (IOSystem *)opaque; + IOStream *io_stream = (IOStream *)stream; + + io_system->Close(io_stream); + + return 0; +} + +int IOSystem2Unzip::testerror(voidpf /*opaque*/, voidpf /*stream*/) { + return 0; +} + +zlib_filefunc_def IOSystem2Unzip::get(IOSystem *pIOHandler) { + zlib_filefunc_def mapping; + + mapping.zopen_file = (open_file_func)open; + mapping.zopendisk_file = (opendisk_file_func)opendisk; + mapping.zread_file = (read_file_func)read; + mapping.zwrite_file = (write_file_func)write; + mapping.ztell_file = (tell_file_func)tell; + mapping.zseek_file = (seek_file_func)seek; + mapping.zclose_file = (close_file_func)close; + mapping.zerror_file = testerror; + + mapping.opaque = reinterpret_cast<voidpf>(pIOHandler); + + return mapping; +} + +// ---------------------------------------------------------------- +// Info about a read-only file inside a ZIP +class ZipFileInfo { +public: + explicit ZipFileInfo(unzFile zip_handle, size_t size); + + // Allocate and Extract data from the ZIP + ZipFile *Extract(std::string &filename, unzFile zip_handle) const; + +private: + size_t m_Size = 0; + unz_file_pos_s m_ZipFilePos; +}; + +ZipFileInfo::ZipFileInfo(unzFile zip_handle, size_t size) : + m_Size(size) { + ai_assert(m_Size != 0); + // Workaround for MSVC 2013 - C2797 + m_ZipFilePos.num_of_file = 0; + m_ZipFilePos.pos_in_zip_directory = 0; + unzGetFilePos(zip_handle, &(m_ZipFilePos)); +} + +ZipFile *ZipFileInfo::Extract(std::string &filename, unzFile zip_handle) const { + // Find in the ZIP. This cannot fail + unz_file_pos_s *filepos = const_cast<unz_file_pos_s *>(&(m_ZipFilePos)); + if (unzGoToFilePos(zip_handle, filepos) != UNZ_OK) + return nullptr; + + if (unzOpenCurrentFile(zip_handle) != UNZ_OK) + return nullptr; + + ZipFile *zip_file = new ZipFile(filename, m_Size); + + // Unzip has a limit of UINT16_MAX bytes buffer + uint16_t unzipBufferSize = zip_file->m_Size <= UINT16_MAX ? static_cast<uint16_t>(zip_file->m_Size) : UINT16_MAX; + std::unique_ptr<uint8_t[]> unzipBuffer = std::unique_ptr<uint8_t[]>(new uint8_t[unzipBufferSize]); + size_t readCount = 0; + while (readCount < zip_file->m_Size) + { + size_t bufferSize = zip_file->m_Size - readCount; + if (bufferSize > UINT16_MAX) { + bufferSize = UINT16_MAX; + } + + int ret = unzReadCurrentFile(zip_handle, unzipBuffer.get(), static_cast<unsigned int>(bufferSize)); + if (ret != static_cast<int>(bufferSize)) + { + // Failed, release the memory + delete zip_file; + zip_file = nullptr; + break; + } + + std::memcpy(zip_file->m_Buffer.get() + readCount, unzipBuffer.get(), ret); + readCount += ret; + } + + ai_assert(unzCloseCurrentFile(zip_handle) == UNZ_OK); + return zip_file; +} + +ZipFile::ZipFile(std::string &filename, size_t size) : + m_Filename(filename), m_Size(size) { + ai_assert(m_Size != 0); + m_Buffer = std::unique_ptr<uint8_t[]>(new uint8_t[m_Size]); +} + +ZipFile::~ZipFile() { +} + +size_t ZipFile::Read(void *pvBuffer, size_t pSize, size_t pCount) { + // Should be impossible + ai_assert(m_Buffer != nullptr); + ai_assert(nullptr != pvBuffer); + ai_assert(0 != pSize); + ai_assert(0 != pCount); + + // Clip down to file size + size_t byteSize = pSize * pCount; + if ((byteSize + m_SeekPtr) > m_Size) { + pCount = (m_Size - m_SeekPtr) / pSize; + byteSize = pSize * pCount; + if (byteSize == 0) { + return 0; + } + } + + std::memcpy(pvBuffer, m_Buffer.get() + m_SeekPtr, byteSize); + + m_SeekPtr += byteSize; + + return pCount; +} + +size_t ZipFile::FileSize() const { + return m_Size; +} + +aiReturn ZipFile::Seek(size_t pOffset, aiOrigin pOrigin) { + switch (pOrigin) { + case aiOrigin_SET: { + if (pOffset > m_Size) return aiReturn_FAILURE; + m_SeekPtr = pOffset; + return aiReturn_SUCCESS; + } + + case aiOrigin_CUR: { + if ((pOffset + m_SeekPtr) > m_Size) return aiReturn_FAILURE; + m_SeekPtr += pOffset; + return aiReturn_SUCCESS; + } + + case aiOrigin_END: { + if (pOffset > m_Size) return aiReturn_FAILURE; + m_SeekPtr = m_Size - pOffset; + return aiReturn_SUCCESS; + } + default:; + } + + return aiReturn_FAILURE; +} + +size_t ZipFile::Tell() const { + return m_SeekPtr; +} + +// ---------------------------------------------------------------- +// pImpl of the Zip Archive IO +class ZipArchiveIOSystem::Implement { +public: + static const unsigned int FileNameSize = 256; + + Implement(IOSystem *pIOHandler, const char *pFilename, const char *pMode); + ~Implement(); + + bool isOpen() const; + void getFileList(std::vector<std::string> &rFileList); + void getFileListExtension(std::vector<std::string> &rFileList, const std::string &extension); + bool Exists(std::string &filename); + IOStream *OpenFile(std::string &filename); + + static void SimplifyFilename(std::string &filename); + +private: + void MapArchive(); + +private: + typedef std::map<std::string, ZipFileInfo> ZipFileInfoMap; + + unzFile m_ZipFileHandle = nullptr; + ZipFileInfoMap m_ArchiveMap; +}; + +ZipArchiveIOSystem::Implement::Implement(IOSystem *pIOHandler, const char *pFilename, const char *pMode) { + ai_assert(strcmp(pMode, "r") == 0); + ai_assert(pFilename != nullptr); + if (pFilename[0] == 0 || nullptr == pMode) { + return; + } + + zlib_filefunc_def mapping = IOSystem2Unzip::get(pIOHandler); + m_ZipFileHandle = unzOpen2(pFilename, &mapping); +} + +ZipArchiveIOSystem::Implement::~Implement() { + if (m_ZipFileHandle != nullptr) { + unzClose(m_ZipFileHandle); + } +} + +void ZipArchiveIOSystem::Implement::MapArchive() { + if (m_ZipFileHandle == nullptr) + return; + + if (!m_ArchiveMap.empty()) + return; + + // At first ensure file is already open + if (unzGoToFirstFile(m_ZipFileHandle) != UNZ_OK) + return; + + // Loop over all files + do { + char filename[FileNameSize]; + unz_file_info fileInfo; + + if (unzGetCurrentFileInfo(m_ZipFileHandle, &fileInfo, filename, FileNameSize, nullptr, 0, nullptr, 0) == UNZ_OK) { + if (fileInfo.uncompressed_size != 0 && fileInfo.size_filename <= FileNameSize) { + std::string filename_string(filename, fileInfo.size_filename); + SimplifyFilename(filename_string); + m_ArchiveMap.emplace(filename_string, ZipFileInfo(m_ZipFileHandle, fileInfo.uncompressed_size)); + } + } + } while (unzGoToNextFile(m_ZipFileHandle) != UNZ_END_OF_LIST_OF_FILE); +} + +bool ZipArchiveIOSystem::Implement::isOpen() const { + return (m_ZipFileHandle != nullptr); +} + +void ZipArchiveIOSystem::Implement::getFileList(std::vector<std::string> &rFileList) { + MapArchive(); + rFileList.clear(); + + for (const auto &file : m_ArchiveMap) { + rFileList.push_back(file.first); + } +} + +void ZipArchiveIOSystem::Implement::getFileListExtension(std::vector<std::string> &rFileList, const std::string &extension) { + MapArchive(); + rFileList.clear(); + + for (const auto &file : m_ArchiveMap) { + if (extension == BaseImporter::GetExtension(file.first)) + rFileList.push_back(file.first); + } +} + +bool ZipArchiveIOSystem::Implement::Exists(std::string &filename) { + MapArchive(); + + ZipFileInfoMap::const_iterator it = m_ArchiveMap.find(filename); + return (it != m_ArchiveMap.end()); +} + +IOStream *ZipArchiveIOSystem::Implement::OpenFile(std::string &filename) { + MapArchive(); + + SimplifyFilename(filename); + + // Find in the map + ZipFileInfoMap::const_iterator zip_it = m_ArchiveMap.find(filename); + if (zip_it == m_ArchiveMap.cend()) + return nullptr; + + const ZipFileInfo &zip_file = (*zip_it).second; + return zip_file.Extract(filename, m_ZipFileHandle); +} + +inline void ReplaceAll(std::string &data, const std::string &before, const std::string &after) { + size_t pos = data.find(before); + while (pos != std::string::npos) { + data.replace(pos, before.size(), after); + pos = data.find(before, pos + after.size()); + } +} + +inline void ReplaceAllChar(std::string &data, const char before, const char after) { + size_t pos = data.find(before); + while (pos != std::string::npos) { + data[pos] = after; + pos = data.find(before, pos + 1); + } +} + +void ZipArchiveIOSystem::Implement::SimplifyFilename(std::string &filename) { + ReplaceAllChar(filename, '\\', '/'); + + // Remove all . and / from the beginning of the path + size_t pos = filename.find_first_not_of("./"); + if (pos != 0) + filename.erase(0, pos); + + // Simplify "my/folder/../file.png" constructions, if any + static const std::string relative("/../"); + const size_t relsize = relative.size() - 1; + pos = filename.find(relative); + while (pos != std::string::npos) { + // Previous slash + size_t prevpos = filename.rfind('/', pos - 1); + if (prevpos == pos) + filename.erase(0, pos + relative.size()); + else + filename.erase(prevpos, pos + relsize - prevpos); + + pos = filename.find(relative); + } +} + +ZipArchiveIOSystem::ZipArchiveIOSystem(IOSystem *pIOHandler, const char *pFilename, const char *pMode) : + pImpl(new Implement(pIOHandler, pFilename, pMode)) { +} + +// ---------------------------------------------------------------- +// The ZipArchiveIO +ZipArchiveIOSystem::ZipArchiveIOSystem(IOSystem *pIOHandler, const std::string &rFilename, const char *pMode) : + pImpl(new Implement(pIOHandler, rFilename.c_str(), pMode)) { +} + +ZipArchiveIOSystem::~ZipArchiveIOSystem() { + delete pImpl; +} + +bool ZipArchiveIOSystem::Exists(const char *pFilename) const { + ai_assert(pFilename != nullptr); + + if (pFilename == nullptr) { + return false; + } + + std::string filename(pFilename); + return pImpl->Exists(filename); +} + +// This is always '/' in a ZIP +char ZipArchiveIOSystem::getOsSeparator() const { + return '/'; +} + +// Only supports Reading +IOStream *ZipArchiveIOSystem::Open(const char *pFilename, const char *pMode) { + ai_assert(pFilename != nullptr); + + for (size_t i = 0; pMode[i] != 0; ++i) { + ai_assert(pMode[i] != 'w'); + if (pMode[i] == 'w') + return nullptr; + } + + std::string filename(pFilename); + return pImpl->OpenFile(filename); +} + +void ZipArchiveIOSystem::Close(IOStream *pFile) { + delete pFile; +} + +bool ZipArchiveIOSystem::isOpen() const { + return (pImpl->isOpen()); +} + +void ZipArchiveIOSystem::getFileList(std::vector<std::string> &rFileList) const { + return pImpl->getFileList(rFileList); +} + +void ZipArchiveIOSystem::getFileListExtension(std::vector<std::string> &rFileList, const std::string &extension) const { + return pImpl->getFileListExtension(rFileList, extension); +} + +bool ZipArchiveIOSystem::isZipArchive(IOSystem *pIOHandler, const char *pFilename) { + Implement tmp(pIOHandler, pFilename, "r"); + return tmp.isOpen(); +} + +bool ZipArchiveIOSystem::isZipArchive(IOSystem *pIOHandler, const std::string &rFilename) { + return isZipArchive(pIOHandler, rFilename.c_str()); +} + +} // namespace Assimp diff --git a/libs/assimp/code/Common/assbin_chunks.h b/libs/assimp/code/Common/assbin_chunks.h new file mode 100644 index 0000000..822df51 --- /dev/null +++ b/libs/assimp/code/Common/assbin_chunks.h @@ -0,0 +1,196 @@ +#ifndef INCLUDED_ASSBIN_CHUNKS_H +#define INCLUDED_ASSBIN_CHUNKS_H + +#define ASSBIN_VERSION_MAJOR 1 +#define ASSBIN_VERSION_MINOR 0 + +/** +@page assfile .ASS File formats + +@section over Overview +Assimp provides its own interchange format, which is intended to applications which need +to serialize 3D-models and to reload them quickly. Assimp's file formats are designed to +be read by Assimp itself. They encode additional information needed by Assimp to optimize +its postprocessing pipeline. If you once apply specific steps to a scene, then save it +and reread it from an ASS format using the same post processing settings, they won't +be executed again. + +The format comes in two flavours: XML and binary - both of them hold a complete dump of +the 'aiScene' data structure returned by the APIs. The focus for the binary format +(<tt>.assbin</tt>) is fast loading. Optional deflate compression helps reduce file size. The XML +flavour, <tt>.assxml</tt> or simply .xml, is just a plain-to-xml conversion of aiScene. + +ASSBIN is Assimp's binary interchange format. assimp_cmd (<tt><root>/tools/assimp_cmd</tt>) is able to +write it and the core library provides a loader for it. + +@section assxml XML File format + +The format is pretty much self-explanatory due to its similarity to the in-memory aiScene structure. +With few exceptions, C structures are wrapped in XML elements. + +The DTD for ASSXML can be found in <tt><root>/doc/AssXML_Scheme.xml</tt>. Or have look +at the output files generated by assimp_cmd. + +@section assbin Binary file format + +The ASSBIN file format is composed of chunks to represent the hierarchical aiScene data structure. +This makes the format extensible and allows backward-compatibility with future data structure +versions. The <tt><root>/code/assbin_chunks.h</tt> header contains some magic constants +for use by stand-alone ASSBIN loaders. Also, Assimp's own file writer can be found +in <tt><root>/tools/assimp_cmd/WriteDump.cpp</tt> (yes, the 'b' is no typo ...). + +@verbatim + +------------------------------------------------------------------------------- +1. File structure: +------------------------------------------------------------------------------- + +---------------------- +| Header (512 bytes) | +---------------------- +| Variable chunks | +---------------------- + +------------------------------------------------------------------------------- +2. Definitions: +------------------------------------------------------------------------------- + +integer is four bytes wide, stored in little-endian byte order. +short is two bytes wide, stored in little-endian byte order. +byte is a single byte. +string is an integer n followed by n UTF-8 characters, not terminated by zero +float is an IEEE 754 single-precision floating-point value +double is an IEEE 754 double-precision floating-point value +t[n] is an array of n elements of type t + +------------------------------------------------------------------------------- +2. Header: +------------------------------------------------------------------------------- + +byte[44] Magic identification string for ASSBIN files. + 'ASSIMP.binary' + +integer Major version of the Assimp library which wrote the file +integer Minor version of the Assimp library which wrote the file + match these against ASSBIN_VERSION_MAJOR and ASSBIN_VERSION_MINOR + +integer SVN revision of the Assimp library (intended for our internal + debugging - if you write Ass files from your own APPs, set this value to 0. +integer Assimp compile flags + +short 0 for normal files, 1 for shortened dumps for regression tests + these should have the file extension assbin.regress + +short 1 if the data after the header is compressed with the DEFLATE algorithm, + 0 for uncompressed files. + For compressed files, the first integer after the header is + always the uncompressed data size + +byte[256] Zero-terminated source file name, UTF-8 +byte[128] Zero-terminated command line parameters passed to assimp_cmd, UTF-8 + +byte[64] Reserved for future use +---> Total length: 512 bytes + +------------------------------------------------------------------------------- +3. Chunks: +------------------------------------------------------------------------------- + +integer Magic chunk ID (ASSBIN_CHUNK_XXX) +integer Chunk data length, in bytes + (unknown chunks are possible, a good reader skips over them) + (chunk-data-length does not include the first two integers) + +byte[n] chunk-data-length bytes of data, depending on the chunk type + +Chunks can contain nested chunks. Nested chunks are ALWAYS at the end of the chunk, +their size is included in chunk-data-length. + +The chunk layout for all ASSIMP data structures is derived from their C declarations. +The general 'rule' to get from Assimp headers to the serialized layout is: + + 1. POD members (i.e. aiMesh::mPrimitiveTypes, aiMesh::mNumVertices), + in order of declaration. + + 2. Array-members (aiMesh::mFaces, aiMesh::mVertices, aiBone::mWeights), + in order of declaration. + + 2. Object array members (i.e aiMesh::mBones, aiScene::mMeshes) are stored in + subchunks directly following the data written in 1.) and 2.) + + + Of course, there are some exceptions to this general order: + +[[aiScene]] + + - The root node holding the scene structure is naturally stored in + a ASSBIN_CHUNK_AINODE subchunk following 1.) and 2.) (which is + empty for aiScene). + +[[aiMesh]] + + - mTextureCoords and mNumUVComponents are serialized as follows: + + [number of used uv channels times] + integer mNumUVComponents[n] + float mTextureCoords[n][3] + + -> more than AI_MAX_TEXCOORD_CHANNELS can be stored. This allows Assimp + builds with different settings for AI_MAX_TEXCOORD_CHANNELS to exchange + data. + -> the on-disk format always uses 3 floats to write UV coordinates. + If mNumUVComponents[0] is 1, the corresponding mTextureCoords array + consists of 3 floats. + + - The array member block of aiMesh is prefixed with an integer that specifies + the kinds of vertex components actually present in the mesh. This is a + bitwise combination of the ASSBIN_MESH_HAS_xxx constants. + +[[aiFace]] + + - mNumIndices is stored as short + - mIndices are written as short, if aiMesh::mNumVertices<65536 + +[[aiNode]] + + - mParent is omitted + +[[aiLight]] + + - mAttenuationXXX not written if aiLight::mType == aiLightSource_DIRECTIONAL + - mAngleXXX not written if aiLight::mType != aiLightSource_SPOT + +[[aiMaterial]] + + - mNumAllocated is omitted, for obvious reasons :-) + + + @endverbatim*/ + + +#define ASSBIN_HEADER_LENGTH 512 + +// these are the magic chunk identifiers for the binary ASS file format +#define ASSBIN_CHUNK_AICAMERA 0x1234 +#define ASSBIN_CHUNK_AILIGHT 0x1235 +#define ASSBIN_CHUNK_AITEXTURE 0x1236 +#define ASSBIN_CHUNK_AIMESH 0x1237 +#define ASSBIN_CHUNK_AINODEANIM 0x1238 +#define ASSBIN_CHUNK_AISCENE 0x1239 +#define ASSBIN_CHUNK_AIBONE 0x123a +#define ASSBIN_CHUNK_AIANIMATION 0x123b +#define ASSBIN_CHUNK_AINODE 0x123c +#define ASSBIN_CHUNK_AIMATERIAL 0x123d +#define ASSBIN_CHUNK_AIMATERIALPROPERTY 0x123e + +#define ASSBIN_MESH_HAS_POSITIONS 0x1 +#define ASSBIN_MESH_HAS_NORMALS 0x2 +#define ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS 0x4 +#define ASSBIN_MESH_HAS_TEXCOORD_BASE 0x100 +#define ASSBIN_MESH_HAS_COLOR_BASE 0x10000 + +#define ASSBIN_MESH_HAS_TEXCOORD(n) (ASSBIN_MESH_HAS_TEXCOORD_BASE << n) +#define ASSBIN_MESH_HAS_COLOR(n) (ASSBIN_MESH_HAS_COLOR_BASE << n) + + +#endif // INCLUDED_ASSBIN_CHUNKS_H diff --git a/libs/assimp/code/Common/material.cpp b/libs/assimp/code/Common/material.cpp new file mode 100644 index 0000000..a7541d4 --- /dev/null +++ b/libs/assimp/code/Common/material.cpp @@ -0,0 +1,100 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +/// @file material.cpp +/** Implement common material related functions. */ + +#include <assimp/ai_assert.h> +#include <assimp/material.h> + +// ------------------------------------------------------------------------------- +const char *TextureTypeToString(aiTextureType in) { + switch (in) { + case aiTextureType_NONE: + return "n/a"; + case aiTextureType_DIFFUSE: + return "Diffuse"; + case aiTextureType_SPECULAR: + return "Specular"; + case aiTextureType_AMBIENT: + return "Ambient"; + case aiTextureType_EMISSIVE: + return "Emissive"; + case aiTextureType_OPACITY: + return "Opacity"; + case aiTextureType_NORMALS: + return "Normals"; + case aiTextureType_HEIGHT: + return "Height"; + case aiTextureType_SHININESS: + return "Shininess"; + case aiTextureType_DISPLACEMENT: + return "Displacement"; + case aiTextureType_LIGHTMAP: + return "Lightmap"; + case aiTextureType_REFLECTION: + return "Reflection"; + case aiTextureType_BASE_COLOR: + return "BaseColor"; + case aiTextureType_NORMAL_CAMERA: + return "NormalCamera"; + case aiTextureType_EMISSION_COLOR: + return "EmissionColor"; + case aiTextureType_METALNESS: + return "Metalness"; + case aiTextureType_DIFFUSE_ROUGHNESS: + return "DiffuseRoughness"; + case aiTextureType_AMBIENT_OCCLUSION: + return "AmbientOcclusion"; + case aiTextureType_SHEEN: + return "Sheen"; + case aiTextureType_CLEARCOAT: + return "Clearcoat"; + case aiTextureType_TRANSMISSION: + return "Transmission"; + case aiTextureType_UNKNOWN: + return "Unknown"; + default: + break; + } + ai_assert(false); + return "BUG"; +} diff --git a/libs/assimp/code/Common/scene.cpp b/libs/assimp/code/Common/scene.cpp new file mode 100644 index 0000000..b0e8821 --- /dev/null +++ b/libs/assimp/code/Common/scene.cpp @@ -0,0 +1,135 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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 <assimp/scene.h> + +aiNode::aiNode() : + mName(""), + mParent(nullptr), + mNumChildren(0), + mChildren(nullptr), + mNumMeshes(0), + mMeshes(nullptr), + mMetaData(nullptr) { + // empty +} + +aiNode::aiNode(const std::string &name) : + mName(name), + mParent(nullptr), + mNumChildren(0), + mChildren(nullptr), + mNumMeshes(0), + mMeshes(nullptr), + mMetaData(nullptr) { + // empty +} + +/** Destructor */ +aiNode::~aiNode() { + // delete all children recursively + // to make sure we won't crash if the data is invalid ... + if (mNumChildren && mChildren) { + for (unsigned int a = 0; a < mNumChildren; a++) + delete mChildren[a]; + } + delete[] mChildren; + delete[] mMeshes; + delete mMetaData; +} + +const aiNode *aiNode::FindNode(const char *name) const { + if (nullptr == name) { + return nullptr; + } + if (!::strcmp(mName.data, name)) { + return this; + } + for (unsigned int i = 0; i < mNumChildren; ++i) { + const aiNode *const p = mChildren[i]->FindNode(name); + if (p) { + return p; + } + } + // there is definitely no sub-node with this name + return nullptr; +} + +aiNode *aiNode::FindNode(const char *name) { + if (!::strcmp(mName.data, name)) return this; + for (unsigned int i = 0; i < mNumChildren; ++i) { + aiNode *const p = mChildren[i]->FindNode(name); + if (p) { + return p; + } + } + // there is definitely no sub-node with this name + return nullptr; +} + +void aiNode::addChildren(unsigned int numChildren, aiNode **children) { + if (nullptr == children || 0 == numChildren) { + return; + } + + for (unsigned int i = 0; i < numChildren; i++) { + aiNode *child = children[i]; + if (nullptr != child) { + child->mParent = this; + } + } + + if (mNumChildren > 0) { + aiNode **tmp = new aiNode *[mNumChildren]; + ::memcpy(tmp, mChildren, sizeof(aiNode *) * mNumChildren); + delete[] mChildren; + mChildren = new aiNode *[mNumChildren + numChildren]; + ::memcpy(mChildren, tmp, sizeof(aiNode *) * mNumChildren); + ::memcpy(&mChildren[mNumChildren], children, sizeof(aiNode *) * numChildren); + mNumChildren += numChildren; + delete[] tmp; + } else { + mChildren = new aiNode *[numChildren]; + for (unsigned int i = 0; i < numChildren; i++) { + mChildren[i] = children[i]; + } + mNumChildren = numChildren; + } +} diff --git a/libs/assimp/code/Common/simd.cpp b/libs/assimp/code/Common/simd.cpp new file mode 100644 index 0000000..0dd437d --- /dev/null +++ b/libs/assimp/code/Common/simd.cpp @@ -0,0 +1,77 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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 "simd.h" + +namespace Assimp { + +bool CPUSupportsSSE2() { +#if defined(__x86_64__) || defined(_M_X64) + //* x86_64 always has SSE2 instructions */ + return true; +#elif defined(__GNUC__) && defined(i386) + // for GCC x86 we check cpuid + unsigned int d; + __asm__( + "pushl %%ebx\n\t" + "cpuid\n\t" + "popl %%ebx\n\t" + : "=d" ( d ) + :"a" ( 1 ) ); + return ( d & 0x04000000 ) != 0; +#elif (defined(_MSC_VER) && defined(_M_IX86)) + // also check cpuid for MSVC x86 + unsigned int d; + __asm { + xor eax, eax + inc eax + push ebx + cpuid + pop ebx + mov d, edx + } + return ( d & 0x04000000 ) != 0; +#else + return false; +#endif +} + + +} // Namespace Assimp diff --git a/libs/assimp/code/Common/simd.h b/libs/assimp/code/Common/simd.h new file mode 100644 index 0000000..0a062a2 --- /dev/null +++ b/libs/assimp/code/Common/simd.h @@ -0,0 +1,53 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. +--------------------------------------------------------------------------- +*/ +#pragma once + +#include <assimp/defs.h> + +namespace Assimp { + +/// @brief Checks if the platform supports SSE2 optimization +/// @return true, if SSE2 is supported. false if SSE2 is not supported. +bool ASSIMP_API CPUSupportsSSE2(); + +} // Namespace Assimp |