summaryrefslogtreecommitdiff
path: root/libs/assimp/code/Common/BaseImporter.cpp
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-04-16 11:55:09 -0500
committersanine <sanine.not@pm.me>2022-04-16 11:55:09 -0500
commitdb81b925d776103326128bf629cbdda576a223e7 (patch)
tree58bea8155c686733310009f6bed7363f91fbeb9d /libs/assimp/code/Common/BaseImporter.cpp
parent55860037b14fb3893ba21cf2654c83d349cc1082 (diff)
move 3rd-party librarys into libs/ and add built-in honeysuckle
Diffstat (limited to 'libs/assimp/code/Common/BaseImporter.cpp')
-rw-r--r--libs/assimp/code/Common/BaseImporter.cpp620
1 files changed, 620 insertions, 0 deletions
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 %%%");
+ }
+}