summaryrefslogtreecommitdiff
path: root/libs/assimp/code/Common
diff options
context:
space:
mode:
Diffstat (limited to 'libs/assimp/code/Common')
-rw-r--r--libs/assimp/code/Common/AssertHandler.cpp72
-rw-r--r--libs/assimp/code/Common/AssertHandler.h75
-rw-r--r--libs/assimp/code/Common/Assimp.cpp1299
-rw-r--r--libs/assimp/code/Common/Base64.cpp179
-rw-r--r--libs/assimp/code/Common/BaseImporter.cpp620
-rw-r--r--libs/assimp/code/Common/BaseProcess.cpp100
-rw-r--r--libs/assimp/code/Common/BaseProcess.h251
-rw-r--r--libs/assimp/code/Common/Bitmap.cpp156
-rw-r--r--libs/assimp/code/Common/Compression.cpp214
-rw-r--r--libs/assimp/code/Common/Compression.h121
-rw-r--r--libs/assimp/code/Common/CreateAnimMesh.cpp92
-rw-r--r--libs/assimp/code/Common/DefaultIOStream.cpp180
-rw-r--r--libs/assimp/code/Common/DefaultIOSystem.cpp223
-rw-r--r--libs/assimp/code/Common/DefaultLogger.cpp430
-rw-r--r--libs/assimp/code/Common/DefaultProgressHandler.h66
-rw-r--r--libs/assimp/code/Common/Exceptional.cpp52
-rw-r--r--libs/assimp/code/Common/Exporter.cpp695
-rw-r--r--libs/assimp/code/Common/FileLogStream.h100
-rw-r--r--libs/assimp/code/Common/FileSystemFilter.h346
-rw-r--r--libs/assimp/code/Common/IFF.h102
-rw-r--r--libs/assimp/code/Common/IOSystem.cpp53
-rw-r--r--libs/assimp/code/Common/Importer.cpp1287
-rw-r--r--libs/assimp/code/Common/Importer.h250
-rw-r--r--libs/assimp/code/Common/ImporterRegistry.cpp393
-rw-r--r--libs/assimp/code/Common/PolyTools.h226
-rw-r--r--libs/assimp/code/Common/PostStepRegistry.cpp265
-rw-r--r--libs/assimp/code/Common/RemoveComments.cpp121
-rw-r--r--libs/assimp/code/Common/SGSpatialSort.cpp168
-rw-r--r--libs/assimp/code/Common/SceneCombiner.cpp1375
-rw-r--r--libs/assimp/code/Common/ScenePreprocessor.cpp285
-rw-r--r--libs/assimp/code/Common/ScenePreprocessor.h118
-rw-r--r--libs/assimp/code/Common/ScenePrivate.h105
-rw-r--r--libs/assimp/code/Common/SkeletonMeshBuilder.cpp262
-rw-r--r--libs/assimp/code/Common/SpatialSort.cpp344
-rw-r--r--libs/assimp/code/Common/StandardShapes.cpp479
-rw-r--r--libs/assimp/code/Common/StdOStreamLogStream.h101
-rw-r--r--libs/assimp/code/Common/Subdivision.cpp581
-rw-r--r--libs/assimp/code/Common/TargetAnimation.cpp226
-rw-r--r--libs/assimp/code/Common/TargetAnimation.h161
-rw-r--r--libs/assimp/code/Common/Version.cpp186
-rw-r--r--libs/assimp/code/Common/VertexTriangleAdjacency.cpp131
-rw-r--r--libs/assimp/code/Common/VertexTriangleAdjacency.h117
-rw-r--r--libs/assimp/code/Common/Win32DebugLogStream.h95
-rw-r--r--libs/assimp/code/Common/ZipArchiveIOSystem.cpp563
-rw-r--r--libs/assimp/code/Common/assbin_chunks.h196
-rw-r--r--libs/assimp/code/Common/material.cpp100
-rw-r--r--libs/assimp/code/Common/scene.cpp135
-rw-r--r--libs/assimp/code/Common/simd.cpp77
-rw-r--r--libs/assimp/code/Common/simd.h53
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>&lt;root&gt;/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>&lt;root&gt;/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>&lt;root&gt;/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>&lt;root&gt;/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