summaryrefslogtreecommitdiff
path: root/libs/assimp/code/AssetLib/Blender
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/AssetLib/Blender
parent55860037b14fb3893ba21cf2654c83d349cc1082 (diff)
move 3rd-party librarys into libs/ and add built-in honeysuckle
Diffstat (limited to 'libs/assimp/code/AssetLib/Blender')
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderBMesh.cpp184
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderBMesh.h94
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderCustomData.cpp181
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderCustomData.h89
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderDNA.cpp351
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderDNA.h808
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderDNA.inl845
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderIntermediate.h206
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderLoader.cpp1340
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderLoader.h198
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderModifier.cpp299
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderModifier.h155
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderScene.cpp891
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderScene.h983
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderSceneGen.h272
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderTessellator.cpp532
-rw-r--r--libs/assimp/code/AssetLib/Blender/BlenderTessellator.h214
17 files changed, 7642 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderBMesh.cpp b/libs/assimp/code/AssetLib/Blender/BlenderBMesh.cpp
new file mode 100644
index 0000000..be536eb
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderBMesh.cpp
@@ -0,0 +1,184 @@
+/*
+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 BlenderBMesh.cpp
+ * @brief Conversion of Blender's new BMesh stuff
+ */
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+
+#include "BlenderBMesh.h"
+#include "BlenderDNA.h"
+#include "BlenderScene.h"
+#include "BlenderTessellator.h"
+
+namespace Assimp {
+template <>
+const char *LogFunctions<BlenderBMeshConverter>::Prefix() {
+ static auto prefix = "BLEND_BMESH: ";
+ return prefix;
+}
+} // namespace Assimp
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+using namespace Assimp::Formatter;
+
+// ------------------------------------------------------------------------------------------------
+BlenderBMeshConverter::BlenderBMeshConverter(const Mesh *mesh) :
+ BMesh(mesh),
+ triMesh(nullptr) {
+ ai_assert(nullptr != mesh);
+}
+
+// ------------------------------------------------------------------------------------------------
+BlenderBMeshConverter::~BlenderBMeshConverter() {
+ DestroyTriMesh();
+}
+
+// ------------------------------------------------------------------------------------------------
+bool BlenderBMeshConverter::ContainsBMesh() const {
+ // TODO - Should probably do some additional verification here
+ return BMesh->totpoly && BMesh->totloop && BMesh->totvert;
+}
+
+// ------------------------------------------------------------------------------------------------
+const Mesh *BlenderBMeshConverter::TriangulateBMesh() {
+ AssertValidMesh();
+ AssertValidSizes();
+ PrepareTriMesh();
+
+ for (int i = 0; i < BMesh->totpoly; ++i) {
+ const MPoly &poly = BMesh->mpoly[i];
+ ConvertPolyToFaces(poly);
+ }
+
+ return triMesh;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::AssertValidMesh() {
+ if (!ContainsBMesh()) {
+ ThrowException("BlenderBMeshConverter requires a BMesh with \"polygons\" - please call BlenderBMeshConverter::ContainsBMesh to check this first");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::AssertValidSizes() {
+ if (BMesh->totpoly != static_cast<int>(BMesh->mpoly.size())) {
+ ThrowException("BMesh poly array has incorrect size");
+ }
+ if (BMesh->totloop != static_cast<int>(BMesh->mloop.size())) {
+ ThrowException("BMesh loop array has incorrect size");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::PrepareTriMesh() {
+ if (triMesh) {
+ DestroyTriMesh();
+ }
+
+ triMesh = new Mesh(*BMesh);
+ triMesh->totface = 0;
+ triMesh->mface.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::DestroyTriMesh() {
+ delete triMesh;
+ triMesh = nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::ConvertPolyToFaces(const MPoly &poly) {
+ const MLoop *polyLoop = &BMesh->mloop[poly.loopstart];
+
+ if (poly.totloop == 3 || poly.totloop == 4) {
+ AddFace(polyLoop[0].v, polyLoop[1].v, polyLoop[2].v, poly.totloop == 4 ? polyLoop[3].v : 0);
+
+ // UVs are optional, so only convert when present.
+ if (BMesh->mloopuv.size()) {
+ if ((poly.loopstart + poly.totloop) > static_cast<int>(BMesh->mloopuv.size())) {
+ ThrowException("BMesh uv loop array has incorrect size");
+ }
+ const MLoopUV *loopUV = &BMesh->mloopuv[poly.loopstart];
+ AddTFace(loopUV[0].uv, loopUV[1].uv, loopUV[2].uv, poly.totloop == 4 ? loopUV[3].uv : 0);
+ }
+ } else if (poly.totloop > 4) {
+#if ASSIMP_BLEND_WITH_GLU_TESSELLATE
+ BlenderTessellatorGL tessGL(*this);
+ tessGL.Tessellate(polyLoop, poly.totloop, triMesh->mvert);
+#elif ASSIMP_BLEND_WITH_POLY_2_TRI
+ BlenderTessellatorP2T tessP2T(*this);
+ tessP2T.Tessellate(polyLoop, poly.totloop, triMesh->mvert);
+#endif
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::AddFace(int v1, int v2, int v3, int v4) {
+ MFace face;
+ face.v1 = v1;
+ face.v2 = v2;
+ face.v3 = v3;
+ face.v4 = v4;
+ face.flag = 0;
+ // TODO - Work out how materials work
+ face.mat_nr = 0;
+ triMesh->mface.push_back(face);
+ triMesh->totface = static_cast<int>(triMesh->mface.size());
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::AddTFace(const float *uv1, const float *uv2, const float *uv3, const float *uv4) {
+ MTFace mtface;
+ memcpy(&mtface.uv[0], uv1, sizeof(float) * 2);
+ memcpy(&mtface.uv[1], uv2, sizeof(float) * 2);
+ memcpy(&mtface.uv[2], uv3, sizeof(float) * 2);
+
+ if (uv4) {
+ memcpy(&mtface.uv[3], uv4, sizeof(float) * 2);
+ }
+
+ triMesh->mtface.push_back(mtface);
+}
+
+#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderBMesh.h b/libs/assimp/code/AssetLib/Blender/BlenderBMesh.h
new file mode 100644
index 0000000..45ca2c8
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderBMesh.h
@@ -0,0 +1,94 @@
+/*
+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 BlenderBMesh.h
+ * @brief Conversion of Blender's new BMesh stuff
+ */
+#ifndef INCLUDED_AI_BLEND_BMESH_H
+#define INCLUDED_AI_BLEND_BMESH_H
+
+#include <assimp/LogAux.h>
+
+namespace Assimp
+{
+ // TinyFormatter.h
+ namespace Formatter
+ {
+ template < typename T,typename TR, typename A > class basic_formatter;
+ typedef class basic_formatter< char, std::char_traits< char >, std::allocator< char > > format;
+ }
+
+ // BlenderScene.h
+ namespace Blender
+ {
+ struct Mesh;
+ struct MPoly;
+ struct MLoop;
+ }
+
+ class BlenderBMeshConverter: public LogFunctions< BlenderBMeshConverter >
+ {
+ public:
+ BlenderBMeshConverter( const Blender::Mesh* mesh );
+ ~BlenderBMeshConverter( );
+
+ bool ContainsBMesh( ) const;
+
+ const Blender::Mesh* TriangulateBMesh( );
+
+ private:
+ void AssertValidMesh( );
+ void AssertValidSizes( );
+ void PrepareTriMesh( );
+ void DestroyTriMesh( );
+ void ConvertPolyToFaces( const Blender::MPoly& poly );
+ void AddFace( int v1, int v2, int v3, int v4 = 0 );
+ void AddTFace( const float* uv1, const float* uv2, const float *uv3, const float* uv4 = 0 );
+
+ const Blender::Mesh* BMesh;
+ Blender::Mesh* triMesh;
+
+ friend class BlenderTessellatorGL;
+ friend class BlenderTessellatorP2T;
+ };
+
+} // end of namespace Assimp
+
+#endif // INCLUDED_AI_BLEND_BMESH_H
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderCustomData.cpp b/libs/assimp/code/AssetLib/Blender/BlenderCustomData.cpp
new file mode 100644
index 0000000..c74a6bb
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderCustomData.cpp
@@ -0,0 +1,181 @@
+#include "BlenderCustomData.h"
+#include "BlenderDNA.h"
+#include <array>
+#include <functional>
+
+namespace Assimp {
+namespace Blender {
+/**
+ * @brief read/convert of Structure array to memory
+ */
+template <typename T>
+bool read(const Structure &s, T *p, const size_t cnt, const FileDatabase &db) {
+ for (size_t i = 0; i < cnt; ++i) {
+ T read;
+ s.Convert(read, db);
+ *p = read;
+ p++;
+ }
+ return true;
+}
+
+/**
+ * @brief pointer to function read memory for n CustomData types
+ */
+typedef bool (*PRead)(ElemBase *pOut, const size_t cnt, const FileDatabase &db);
+typedef ElemBase *(*PCreate)(const size_t cnt);
+typedef void (*PDestroy)(ElemBase *);
+
+#define IMPL_STRUCT_READ(ty) \
+ bool read##ty(ElemBase *v, const size_t cnt, const FileDatabase &db) { \
+ ty *ptr = dynamic_cast<ty *>(v); \
+ if (nullptr == ptr) { \
+ return false; \
+ } \
+ return read<ty>(db.dna[#ty], ptr, cnt, db); \
+ }
+
+#define IMPL_STRUCT_CREATE(ty) \
+ ElemBase *create##ty(const size_t cnt) { \
+ return new ty[cnt]; \
+ }
+
+#define IMPL_STRUCT_DESTROY(ty) \
+ void destroy##ty(ElemBase *pE) { \
+ ty *p = dynamic_cast<ty *>(pE); \
+ delete[] p; \
+ }
+
+/**
+ * @brief helper macro to define Structure functions
+ */
+#define IMPL_STRUCT(ty) \
+ IMPL_STRUCT_READ(ty) \
+ IMPL_STRUCT_CREATE(ty) \
+ IMPL_STRUCT_DESTROY(ty)
+
+// supported structures for CustomData
+IMPL_STRUCT(MVert)
+IMPL_STRUCT(MEdge)
+IMPL_STRUCT(MFace)
+IMPL_STRUCT(MTFace)
+IMPL_STRUCT(MTexPoly)
+IMPL_STRUCT(MLoopUV)
+IMPL_STRUCT(MLoopCol)
+IMPL_STRUCT(MPoly)
+IMPL_STRUCT(MLoop)
+
+/**
+ * @brief describes the size of data and the read function to be used for single CustomerData.type
+ */
+struct CustomDataTypeDescription {
+ PRead Read; ///< function to read one CustomData type element
+ PCreate Create; ///< function to allocate n type elements
+ PDestroy Destroy;
+
+ CustomDataTypeDescription(PRead read, PCreate create, PDestroy destroy) :
+ Read(read), Create(create), Destroy(destroy) {}
+};
+
+/**
+ * @brief helper macro to define Structure type specific CustomDataTypeDescription
+ * @note IMPL_STRUCT_READ for same ty must be used earlier to implement the typespecific read function
+ */
+#define DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(ty) \
+ CustomDataTypeDescription { &read##ty, &create##ty, &destroy##ty }
+
+/**
+ * @brief helper macro to define CustomDataTypeDescription for UNSUPPORTED type
+ */
+#define DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION \
+ CustomDataTypeDescription { nullptr, nullptr, nullptr }
+
+/**
+ * @brief descriptors for data pointed to from CustomDataLayer.data
+ * @note some of the CustomData uses already well defined Structures
+ * other (like CD_ORCO, ...) uses arrays of rawtypes or even arrays of Structures
+ * use a special readfunction for that cases
+ */
+std::array<CustomDataTypeDescription, CD_NUMTYPES> customDataTypeDescriptions = { { DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MVert),
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MEdge),
+ DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MFace),
+ DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MTFace),
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MTexPoly),
+ DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MLoopUV),
+ DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MLoopCol),
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MPoly),
+ DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MLoop),
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+ DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION } };
+
+bool isValidCustomDataType(const int cdtype) {
+ return cdtype >= 0 && cdtype < CD_NUMTYPES;
+}
+
+bool readCustomData(std::shared_ptr<ElemBase> &out, const int cdtype, const size_t cnt, const FileDatabase &db) {
+ if (!isValidCustomDataType(cdtype)) {
+ throw Error("CustomData.type ", cdtype, " out of index");
+ }
+
+ const CustomDataTypeDescription cdtd = customDataTypeDescriptions[cdtype];
+ if (cdtd.Read && cdtd.Create && cdtd.Destroy && cnt > 0) {
+ // allocate cnt elements and parse them from file
+ out.reset(cdtd.Create(cnt), cdtd.Destroy);
+ return cdtd.Read(out.get(), cnt, db);
+ }
+ return false;
+}
+
+std::shared_ptr<CustomDataLayer> getCustomDataLayer(const CustomData &customdata, const CustomDataType cdtype, const std::string &name) {
+ for (auto it = customdata.layers.begin(); it != customdata.layers.end(); ++it) {
+ if (it->get()->type == cdtype && name == it->get()->name) {
+ return *it;
+ }
+ }
+ return nullptr;
+}
+
+const ElemBase *getCustomDataLayerData(const CustomData &customdata, const CustomDataType cdtype, const std::string &name) {
+ const std::shared_ptr<CustomDataLayer> pLayer = getCustomDataLayer(customdata, cdtype, name);
+ if (pLayer && pLayer->data) {
+ return pLayer->data.get();
+ }
+ return nullptr;
+}
+} // namespace Blender
+} // namespace Assimp
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderCustomData.h b/libs/assimp/code/AssetLib/Blender/BlenderCustomData.h
new file mode 100644
index 0000000..f61d79a
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderCustomData.h
@@ -0,0 +1,89 @@
+#pragma once
+
+#include "BlenderDNA.h"
+#include "BlenderScene.h"
+#include <memory>
+
+namespace Assimp {
+ namespace Blender {
+ /* CustomData.type from Blender (2.79b) */
+ enum CustomDataType {
+ CD_AUTO_FROM_NAME = -1,
+ CD_MVERT = 0,
+#ifdef DNA_DEPRECATED
+ CD_MSTICKY = 1, /* DEPRECATED */
+#endif
+ CD_MDEFORMVERT = 2,
+ CD_MEDGE = 3,
+ CD_MFACE = 4,
+ CD_MTFACE = 5,
+ CD_MCOL = 6,
+ CD_ORIGINDEX = 7,
+ CD_NORMAL = 8,
+ /* CD_POLYINDEX = 9, */
+ CD_PROP_FLT = 10,
+ CD_PROP_INT = 11,
+ CD_PROP_STR = 12,
+ CD_ORIGSPACE = 13, /* for modifier stack face location mapping */
+ CD_ORCO = 14,
+ CD_MTEXPOLY = 15,
+ CD_MLOOPUV = 16,
+ CD_MLOOPCOL = 17,
+ CD_TANGENT = 18,
+ CD_MDISPS = 19,
+ CD_PREVIEW_MCOL = 20, /* for displaying weightpaint colors */
+ /* CD_ID_MCOL = 21, */
+ CD_TEXTURE_MLOOPCOL = 22,
+ CD_CLOTH_ORCO = 23,
+ CD_RECAST = 24,
+
+ /* BMESH ONLY START */
+ CD_MPOLY = 25,
+ CD_MLOOP = 26,
+ CD_SHAPE_KEYINDEX = 27,
+ CD_SHAPEKEY = 28,
+ CD_BWEIGHT = 29,
+ CD_CREASE = 30,
+ CD_ORIGSPACE_MLOOP = 31,
+ CD_PREVIEW_MLOOPCOL = 32,
+ CD_BM_ELEM_PYPTR = 33,
+ /* BMESH ONLY END */
+
+ CD_PAINT_MASK = 34,
+ CD_GRID_PAINT_MASK = 35,
+ CD_MVERT_SKIN = 36,
+ CD_FREESTYLE_EDGE = 37,
+ CD_FREESTYLE_FACE = 38,
+ CD_MLOOPTANGENT = 39,
+ CD_TESSLOOPNORMAL = 40,
+ CD_CUSTOMLOOPNORMAL = 41,
+
+ CD_NUMTYPES = 42
+ };
+
+ /**
+ * @brief check if given cdtype is valid (ie >= 0 and < CD_NUMTYPES)
+ * @param[in] cdtype to check
+ * @return true when valid
+ */
+ bool isValidCustomDataType(const int cdtype);
+
+ /**
+ * @brief returns CustomDataLayer ptr for given cdtype and name
+ * @param[in] customdata CustomData to search for wanted layer
+ * @param[in] cdtype to search for
+ * @param[in] name to search for
+ * @return CustomDataLayer * or nullptr if not found
+ */
+ std::shared_ptr<CustomDataLayer> getCustomDataLayer(const CustomData &customdata, CustomDataType cdtype, const std::string &name);
+
+ /**
+ * @brief returns CustomDataLayer data ptr for given cdtype and name
+ * @param[in] customdata CustomData to search for wanted layer
+ * @param[in] cdtype to search for
+ * @param[in] name to search for
+ * @return * to struct data or nullptr if not found
+ */
+ const ElemBase * getCustomDataLayerData(const CustomData &customdata, CustomDataType cdtype, const std::string &name);
+ }
+}
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderDNA.cpp b/libs/assimp/code/AssetLib/Blender/BlenderDNA.cpp
new file mode 100644
index 0000000..2910904
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderDNA.cpp
@@ -0,0 +1,351 @@
+/*
+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 BlenderDNA.cpp
+ * @brief Implementation of the Blender `DNA`, that is its own
+ * serialized set of data structures.
+ */
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+#include "BlenderDNA.h"
+#include <assimp/StreamReader.h>
+#include <assimp/TinyFormatter.h>
+#include <assimp/fast_atof.h>
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+using namespace Assimp::Formatter;
+
+static bool match4(StreamReaderAny &stream, const char *string) {
+ ai_assert(nullptr != string);
+ char tmp[4];
+ tmp[0] = (stream).GetI1();
+ tmp[1] = (stream).GetI1();
+ tmp[2] = (stream).GetI1();
+ tmp[3] = (stream).GetI1();
+ return (tmp[0] == string[0] && tmp[1] == string[1] && tmp[2] == string[2] && tmp[3] == string[3]);
+}
+
+struct Type {
+ size_t size;
+ std::string name;
+};
+
+// ------------------------------------------------------------------------------------------------
+void DNAParser::Parse() {
+ StreamReaderAny &stream = *db.reader.get();
+ DNA &dna = db.dna;
+
+ if (!match4(stream, "SDNA")) {
+ throw DeadlyImportError("BlenderDNA: Expected SDNA chunk");
+ }
+
+ // name dictionary
+ if (!match4(stream, "NAME")) {
+ throw DeadlyImportError("BlenderDNA: Expected NAME field");
+ }
+
+ std::vector<std::string> names(stream.GetI4());
+ for (std::string &s : names) {
+ while (char c = stream.GetI1()) {
+ s += c;
+ }
+ }
+
+ // type dictionary
+ for (; stream.GetCurrentPos() & 0x3; stream.GetI1())
+ ;
+ if (!match4(stream, "TYPE")) {
+ throw DeadlyImportError("BlenderDNA: Expected TYPE field");
+ }
+
+ std::vector<Type> types(stream.GetI4());
+ for (Type &s : types) {
+ while (char c = stream.GetI1()) {
+ s.name += c;
+ }
+ }
+
+ // type length dictionary
+ for (; stream.GetCurrentPos() & 0x3; stream.GetI1())
+ ;
+ if (!match4(stream, "TLEN")) {
+ throw DeadlyImportError("BlenderDNA: Expected TLEN field");
+ }
+
+ for (Type &s : types) {
+ s.size = stream.GetI2();
+ }
+
+ // structures dictionary
+ for (; stream.GetCurrentPos() & 0x3; stream.GetI1())
+ ;
+ if (!match4(stream, "STRC")) {
+ throw DeadlyImportError("BlenderDNA: Expected STRC field");
+ }
+
+ size_t end = stream.GetI4(), fields = 0;
+
+ dna.structures.reserve(end);
+ for (size_t i = 0; i != end; ++i) {
+
+ uint16_t n = stream.GetI2();
+ if (n >= types.size()) {
+ throw DeadlyImportError("BlenderDNA: Invalid type index in structure name", n, " (there are only ", types.size(), " entries)");
+ }
+
+ // maintain separate indexes
+ dna.indices[types[n].name] = dna.structures.size();
+
+ dna.structures.push_back(Structure());
+ Structure &s = dna.structures.back();
+ s.name = types[n].name;
+
+ n = stream.GetI2();
+ s.fields.reserve(n);
+
+ size_t offset = 0;
+ for (size_t m = 0; m < n; ++m, ++fields) {
+
+ uint16_t j = stream.GetI2();
+ if (j >= types.size()) {
+ throw DeadlyImportError("BlenderDNA: Invalid type index in structure field ", j, " (there are only ", types.size(), " entries)");
+ }
+ s.fields.push_back(Field());
+ Field &f = s.fields.back();
+ f.offset = offset;
+
+ f.type = types[j].name;
+ f.size = types[j].size;
+
+ j = stream.GetI2();
+ if (j >= names.size()) {
+ throw DeadlyImportError("BlenderDNA: Invalid name index in structure field ", j, " (there are only ", names.size(), " entries)");
+ }
+
+ f.name = names[j];
+ f.flags = 0u;
+
+ // pointers always specify the size of the pointee instead of their own.
+ // The pointer asterisk remains a property of the lookup name.
+ if (f.name[0] == '*') {
+ f.size = db.i64bit ? 8 : 4;
+ f.flags |= FieldFlag_Pointer;
+ }
+
+ // arrays, however, specify the size of a single element so we
+ // need to parse the (possibly multi-dimensional) array declaration
+ // in order to obtain the actual size of the array in the file.
+ // Also we need to alter the lookup name to include no array
+ // brackets anymore or size fixup won't work (if our size does
+ // not match the size read from the DNA).
+ if (*f.name.rbegin() == ']') {
+ const std::string::size_type rb = f.name.find('[');
+ if (rb == std::string::npos) {
+ throw DeadlyImportError("BlenderDNA: Encountered invalid array declaration ", f.name);
+ }
+
+ f.flags |= FieldFlag_Array;
+ DNA::ExtractArraySize(f.name, f.array_sizes);
+ f.name = f.name.substr(0, rb);
+
+ f.size *= f.array_sizes[0] * f.array_sizes[1];
+ }
+
+ // maintain separate indexes
+ s.indices[f.name] = s.fields.size() - 1;
+ offset += f.size;
+ }
+ s.size = offset;
+ }
+
+ ASSIMP_LOG_DEBUG("BlenderDNA: Got ", dna.structures.size(), " structures with totally ", fields, " fields");
+
+#if ASSIMP_BUILD_BLENDER_DEBUG_DNA
+ dna.DumpToFile();
+#endif
+
+ dna.AddPrimitiveStructures();
+ dna.RegisterConverters();
+}
+
+#if ASSIMP_BUILD_BLENDER_DEBUG_DNA
+
+#include <fstream>
+// ------------------------------------------------------------------------------------------------
+void DNA ::DumpToFile() {
+ // we don't bother using the VFS here for this is only for debugging.
+ // (and all your bases are belong to us).
+
+ std::ofstream f("dna.txt");
+ if (f.fail()) {
+ ASSIMP_LOG_ERROR("Could not dump dna to dna.txt");
+ return;
+ }
+ f << "Field format: type name offset size"
+ << "\n";
+ f << "Structure format: name size"
+ << "\n";
+
+ for (const Structure &s : structures) {
+ f << s.name << " " << s.size << "\n\n";
+ for (const Field &ff : s.fields) {
+ f << "\t" << ff.type << " " << ff.name << " " << ff.offset << " " << ff.size << "\n";
+ }
+ f << "\n";
+ }
+ f << std::flush;
+
+ ASSIMP_LOG_INFO("BlenderDNA: Dumped dna to dna.txt");
+}
+#endif // ASSIMP_BUILD_BLENDER_DEBUG_DNA
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ void DNA ::ExtractArraySize(
+ const std::string &out,
+ size_t array_sizes[2]) {
+ array_sizes[0] = array_sizes[1] = 1;
+ std::string::size_type pos = out.find('[');
+ if (pos++ == std::string::npos) {
+ return;
+ }
+ array_sizes[0] = strtoul10(&out[pos]);
+
+ pos = out.find('[', pos);
+ if (pos++ == std::string::npos) {
+ return;
+ }
+ array_sizes[1] = strtoul10(&out[pos]);
+}
+
+// ------------------------------------------------------------------------------------------------
+std::shared_ptr<ElemBase> DNA ::ConvertBlobToStructure(
+ const Structure &structure,
+ const FileDatabase &db) const {
+ std::map<std::string, FactoryPair>::const_iterator it = converters.find(structure.name);
+ if (it == converters.end()) {
+ return std::shared_ptr<ElemBase>();
+ }
+
+ std::shared_ptr<ElemBase> ret = (structure.*((*it).second.first))();
+ (structure.*((*it).second.second))(ret, db);
+
+ return ret;
+}
+
+// ------------------------------------------------------------------------------------------------
+DNA::FactoryPair DNA ::GetBlobToStructureConverter(
+ const Structure &structure,
+ const FileDatabase & /*db*/
+) const {
+ std::map<std::string, FactoryPair>::const_iterator it = converters.find(structure.name);
+ return it == converters.end() ? FactoryPair() : (*it).second;
+}
+
+// basing on http://www.blender.org/development/architecture/notes-on-sdna/
+// ------------------------------------------------------------------------------------------------
+void DNA ::AddPrimitiveStructures() {
+ // NOTE: these are just dummies. Their presence enforces
+ // Structure::Convert<target_type> to be called on these
+ // empty structures. These converters are special
+ // overloads which scan the name of the structure and
+ // perform the required data type conversion if one
+ // of these special names is found in the structure
+ // in question.
+
+ indices["int"] = structures.size();
+ structures.push_back(Structure());
+ structures.back().name = "int";
+ structures.back().size = 4;
+
+ indices["short"] = structures.size();
+ structures.push_back(Structure());
+ structures.back().name = "short";
+ structures.back().size = 2;
+
+ indices["char"] = structures.size();
+ structures.push_back(Structure());
+ structures.back().name = "char";
+ structures.back().size = 1;
+
+ indices["float"] = structures.size();
+ structures.push_back(Structure());
+ structures.back().name = "float";
+ structures.back().size = 4;
+
+ indices["double"] = structures.size();
+ structures.push_back(Structure());
+ structures.back().name = "double";
+ structures.back().size = 8;
+
+ // no long, seemingly.
+}
+
+// ------------------------------------------------------------------------------------------------
+void SectionParser ::Next() {
+ stream.SetCurrentPos(current.start + current.size);
+
+ const char tmp[] = {
+ (const char)stream.GetI1(),
+ (const char)stream.GetI1(),
+ (const char)stream.GetI1(),
+ (const char)stream.GetI1()
+ };
+ current.id = std::string(tmp, tmp[3] ? 4 : tmp[2] ? 3 : tmp[1] ? 2 : 1);
+
+ current.size = stream.GetI4();
+ current.address.val = ptr64 ? stream.GetU8() : stream.GetU4();
+
+ current.dna_index = stream.GetI4();
+ current.num = stream.GetI4();
+
+ current.start = stream.GetCurrentPos();
+ if (stream.GetRemainingSizeToLimit() < current.size) {
+ throw DeadlyImportError("BLEND: invalid size of file block");
+ }
+
+#ifdef ASSIMP_BUILD_BLENDER_DEBUG
+ ASSIMP_LOG_VERBOSE_DEBUG(current.id);
+#endif
+}
+
+#endif
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderDNA.h b/libs/assimp/code/AssetLib/Blender/BlenderDNA.h
new file mode 100644
index 0000000..b2158f2
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderDNA.h
@@ -0,0 +1,808 @@
+/*
+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 BlenderDNA.h
+ * @brief Blender `DNA` (file format specification embedded in
+ * blend file itself) loader.
+ */
+#ifndef INCLUDED_AI_BLEND_DNA_H
+#define INCLUDED_AI_BLEND_DNA_H
+
+#include <assimp/BaseImporter.h>
+#include <assimp/StreamReader.h>
+#include <stdint.h>
+#include <assimp/DefaultLogger.hpp>
+#include <map>
+#include <memory>
+
+// enable verbose log output. really verbose, so be careful.
+#ifdef ASSIMP_BUILD_DEBUG
+#define ASSIMP_BUILD_BLENDER_DEBUG
+#endif
+
+// set this to non-zero to dump BlenderDNA stuff to dna.txt.
+// you could set it on the assimp build command line too without touching it here.
+// !!! please make sure this is set to 0 in the repo !!!
+#ifndef ASSIMP_BUILD_BLENDER_DEBUG_DNA
+#define ASSIMP_BUILD_BLENDER_DEBUG_DNA 0
+#endif
+
+// #define ASSIMP_BUILD_BLENDER_NO_STATS
+
+namespace Assimp {
+
+template <bool, bool>
+class StreamReader;
+typedef StreamReader<true, true> StreamReaderAny;
+
+namespace Blender {
+
+class FileDatabase;
+struct FileBlockHead;
+
+template <template <typename> class TOUT>
+class ObjectCache;
+
+// -------------------------------------------------------------------------------
+/** Exception class used by the blender loader to selectively catch exceptions
+ * thrown in its own code (DeadlyImportErrors thrown in general utility
+ * functions are untouched then). If such an exception is not caught by
+ * the loader itself, it will still be caught by Assimp due to its
+ * ancestry. */
+// -------------------------------------------------------------------------------
+struct Error : DeadlyImportError {
+ template <typename... T>
+ explicit Error(T &&...args) :
+ DeadlyImportError(args...) {
+ }
+};
+
+// -------------------------------------------------------------------------------
+/** The only purpose of this structure is to feed a virtual dtor into its
+ * descendents. It serves as base class for all data structure fields. */
+// -------------------------------------------------------------------------------
+struct ElemBase {
+ ElemBase() :
+ dna_type(nullptr) {
+ // empty
+ }
+
+ virtual ~ElemBase() {
+ // empty
+ }
+
+ /** Type name of the element. The type
+ * string points is the `c_str` of the `name` attribute of the
+ * corresponding `Structure`, that is, it is only valid as long
+ * as the DNA is not modified. The dna_type is only set if the
+ * data type is not static, i.e. a std::shared_ptr<ElemBase>
+ * in the scene description would have its type resolved
+ * at runtime, so this member is always set. */
+ const char *dna_type;
+};
+
+// -------------------------------------------------------------------------------
+/** Represents a generic pointer to a memory location, which can be either 32
+ * or 64 bits. These pointers are loaded from the BLEND file and finally
+ * fixed to point to the real, converted representation of the objects
+ * they used to point to.*/
+// -------------------------------------------------------------------------------
+struct Pointer {
+ Pointer() :
+ val() {
+ // empty
+ }
+ uint64_t val;
+};
+
+// -------------------------------------------------------------------------------
+/** Represents a generic offset within a BLEND file */
+// -------------------------------------------------------------------------------
+struct FileOffset {
+ FileOffset() :
+ val() {
+ // empty
+ }
+ uint64_t val;
+};
+
+// -------------------------------------------------------------------------------
+/** Dummy derivate of std::vector to be able to use it in templates simultaenously
+ * with std::shared_ptr, which takes only one template argument
+ * while std::vector takes three. Also we need to provide some special member
+ * functions of shared_ptr */
+// -------------------------------------------------------------------------------
+template <typename T>
+class vector : public std::vector<T> {
+public:
+ using std::vector<T>::resize;
+ using std::vector<T>::empty;
+
+ void reset() {
+ resize(0);
+ }
+
+ operator bool() const {
+ return !empty();
+ }
+};
+
+// -------------------------------------------------------------------------------
+/** Mixed flags for use in #Field */
+// -------------------------------------------------------------------------------
+enum FieldFlags {
+ FieldFlag_Pointer = 0x1,
+ FieldFlag_Array = 0x2
+};
+
+// -------------------------------------------------------------------------------
+/** Represents a single member of a data structure in a BLEND file */
+// -------------------------------------------------------------------------------
+struct Field {
+ std::string name;
+ std::string type;
+
+ size_t size;
+ size_t offset;
+
+ /** Size of each array dimension. For flat arrays,
+ * the second dimension is set to 1. */
+ size_t array_sizes[2];
+
+ /** Any of the #FieldFlags enumerated values */
+ unsigned int flags;
+};
+
+// -------------------------------------------------------------------------------
+/** Range of possible behaviors for fields absence in the input file. Some are
+ * mission critical so we need them, while others can silently be default
+ * initialized and no animations are harmed. */
+// -------------------------------------------------------------------------------
+enum ErrorPolicy {
+ /** Substitute default value and ignore */
+ ErrorPolicy_Igno,
+ /** Substitute default value and write to log */
+ ErrorPolicy_Warn,
+ /** Substitute a massive error message and crash the whole matrix. Its time for another zion */
+ ErrorPolicy_Fail
+};
+
+#ifdef ASSIMP_BUILD_BLENDER_DEBUG
+#define ErrorPolicy_Igno ErrorPolicy_Warn
+#endif
+
+// -------------------------------------------------------------------------------
+/** Represents a data structure in a BLEND file. A Structure defines n fields
+ * and their locations and encodings the input stream. Usually, every
+ * Structure instance pertains to one equally-named data structure in the
+ * BlenderScene.h header. This class defines various utilities to map a
+ * binary `blob` read from the file to such a structure instance with
+ * meaningful contents. */
+// -------------------------------------------------------------------------------
+class Structure {
+ template <template <typename> class>
+ friend class ObjectCache;
+
+public:
+ Structure() :
+ cache_idx(static_cast<size_t>(-1)) {
+ // empty
+ }
+
+ // publicly accessible members
+ std::string name;
+ vector<Field> fields;
+ std::map<std::string, size_t> indices;
+
+ size_t size;
+
+ // --------------------------------------------------------
+ /** Access a field of the structure by its canonical name. The pointer version
+ * returns nullptr on failure while the reference version raises an import error. */
+ inline const Field &operator[](const std::string &ss) const;
+ inline const Field *Get(const std::string &ss) const;
+
+ // --------------------------------------------------------
+ /** Access a field of the structure by its index */
+ inline const Field &operator[](const size_t i) const;
+
+ // --------------------------------------------------------
+ inline bool operator==(const Structure &other) const {
+ return name == other.name; // name is meant to be an unique identifier
+ }
+
+ // --------------------------------------------------------
+ inline bool operator!=(const Structure &other) const {
+ return name != other.name;
+ }
+
+ // --------------------------------------------------------
+ /** Try to read an instance of the structure from the stream
+ * and attempt to convert to `T`. This is done by
+ * an appropriate specialization. If none is available,
+ * a compiler complain is the result.
+ * @param dest Destination value to be written
+ * @param db File database, including input stream. */
+ template <typename T>
+ void Convert(T &dest, const FileDatabase &db) const;
+
+ // --------------------------------------------------------
+ // generic converter
+ template <typename T>
+ void Convert(std::shared_ptr<ElemBase> in, const FileDatabase &db) const;
+
+ // --------------------------------------------------------
+ // generic allocator
+ template <typename T>
+ std::shared_ptr<ElemBase> Allocate() const;
+
+ // --------------------------------------------------------
+ // field parsing for 1d arrays
+ template <int error_policy, typename T, size_t M>
+ void ReadFieldArray(T (&out)[M], const char *name,
+ const FileDatabase &db) const;
+
+ // --------------------------------------------------------
+ // field parsing for 2d arrays
+ template <int error_policy, typename T, size_t M, size_t N>
+ void ReadFieldArray2(T (&out)[M][N], const char *name,
+ const FileDatabase &db) const;
+
+ // --------------------------------------------------------
+ // field parsing for pointer or dynamic array types
+ // (std::shared_ptr)
+ // The return value indicates whether the data was already cached.
+ template <int error_policy, template <typename> class TOUT, typename T>
+ bool ReadFieldPtr(TOUT<T> &out, const char *name,
+ const FileDatabase &db,
+ bool non_recursive = false) const;
+
+ // --------------------------------------------------------
+ // field parsing for static arrays of pointer or dynamic
+ // array types (std::shared_ptr[])
+ // The return value indicates whether the data was already cached.
+ template <int error_policy, template <typename> class TOUT, typename T, size_t N>
+ bool ReadFieldPtr(TOUT<T> (&out)[N], const char *name,
+ const FileDatabase &db) const;
+
+ // --------------------------------------------------------
+ // field parsing for `normal` values
+ // The return value indicates whether the data was already cached.
+ template <int error_policy, typename T>
+ void ReadField(T &out, const char *name,
+ const FileDatabase &db) const;
+
+ // --------------------------------------------------------
+ /**
+ * @brief field parsing for dynamic vectors
+ * @param[in] out vector of struct to be filled
+ * @param[in] name of field
+ * @param[in] db to access the file, dna, ...
+ * @return true when read was successful
+ */
+ template <int error_policy, template <typename> class TOUT, typename T>
+ bool ReadFieldPtrVector(vector<TOUT<T>> &out, const char *name, const FileDatabase &db) const;
+
+ /**
+ * @brief parses raw customdata
+ * @param[in] out shared_ptr to be filled
+ * @param[in] cdtype customdata type to read
+ * @param[in] name of field ptr
+ * @param[in] db to access the file, dna, ...
+ * @return true when read was successful
+ */
+ template <int error_policy>
+ bool ReadCustomDataPtr(std::shared_ptr<ElemBase> &out, int cdtype, const char *name, const FileDatabase &db) const;
+
+private:
+ // --------------------------------------------------------
+ template <template <typename> class TOUT, typename T>
+ bool ResolvePointer(TOUT<T> &out, const Pointer &ptrval,
+ const FileDatabase &db, const Field &f,
+ bool non_recursive = false) const;
+
+ // --------------------------------------------------------
+ template <template <typename> class TOUT, typename T>
+ bool ResolvePointer(vector<TOUT<T>> &out, const Pointer &ptrval,
+ const FileDatabase &db, const Field &f, bool) const;
+
+ // --------------------------------------------------------
+ bool ResolvePointer(std::shared_ptr<FileOffset> &out, const Pointer &ptrval,
+ const FileDatabase &db, const Field &f, bool) const;
+
+ // --------------------------------------------------------
+ inline const FileBlockHead *LocateFileBlockForAddress(
+ const Pointer &ptrval,
+ const FileDatabase &db) const;
+
+private:
+ // ------------------------------------------------------------------------------
+ template <typename T>
+ T *_allocate(std::shared_ptr<T> &out, size_t &s) const {
+ out = std::shared_ptr<T>(new T());
+ s = 1;
+ return out.get();
+ }
+
+ template <typename T>
+ T *_allocate(vector<T> &out, size_t &s) const {
+ out.resize(s);
+ return s ? &out.front() : nullptr;
+ }
+
+ // --------------------------------------------------------
+ template <int error_policy>
+ struct _defaultInitializer {
+
+ template <typename T, unsigned int N>
+ void operator()(T (&out)[N], const char * = nullptr) {
+ for (unsigned int i = 0; i < N; ++i) {
+ out[i] = T();
+ }
+ }
+
+ template <typename T, unsigned int N, unsigned int M>
+ void operator()(T (&out)[N][M], const char * = nullptr) {
+ for (unsigned int i = 0; i < N; ++i) {
+ for (unsigned int j = 0; j < M; ++j) {
+ out[i][j] = T();
+ }
+ }
+ }
+
+ template <typename T>
+ void operator()(T &out, const char * = nullptr) {
+ out = T();
+ }
+ };
+
+private:
+ mutable size_t cache_idx;
+};
+
+// --------------------------------------------------------
+template <>
+struct Structure::_defaultInitializer<ErrorPolicy_Warn> {
+
+ template <typename T>
+ void operator()(T &out, const char *reason = "<add reason>") {
+ ASSIMP_LOG_WARN(reason);
+
+ // ... and let the show go on
+ _defaultInitializer<0 /*ErrorPolicy_Igno*/>()(out);
+ }
+};
+
+template <>
+struct Structure::_defaultInitializer<ErrorPolicy_Fail> {
+
+ template <typename T>
+ void operator()(T & /*out*/, const char * = "") {
+ // obviously, it is crucial that _DefaultInitializer is used
+ // only from within a catch clause.
+ throw DeadlyImportError("Constructing BlenderDNA Structure encountered an error");
+ }
+};
+
+// -------------------------------------------------------------------------------------------------------
+template <>
+inline bool Structure ::ResolvePointer<std::shared_ptr, ElemBase>(std::shared_ptr<ElemBase> &out,
+ const Pointer &ptrval,
+ const FileDatabase &db,
+ const Field &f,
+ bool) const;
+
+// -------------------------------------------------------------------------------
+/** Represents the full data structure information for a single BLEND file.
+ * This data is extracted from the DNA1 chunk in the file.
+ * #DNAParser does the reading and represents currently the only place where
+ * DNA is altered.*/
+// -------------------------------------------------------------------------------
+class DNA {
+public:
+ typedef void (Structure::*ConvertProcPtr)(
+ std::shared_ptr<ElemBase> in,
+ const FileDatabase &) const;
+
+ typedef std::shared_ptr<ElemBase> (
+ Structure::*AllocProcPtr)() const;
+
+ typedef std::pair<AllocProcPtr, ConvertProcPtr> FactoryPair;
+
+public:
+ std::map<std::string, FactoryPair> converters;
+ vector<Structure> structures;
+ std::map<std::string, size_t> indices;
+
+public:
+ // --------------------------------------------------------
+ /** Access a structure by its canonical name, the pointer version returns nullptr on failure
+ * while the reference version raises an error. */
+ inline const Structure &operator[](const std::string &ss) const;
+ inline const Structure *Get(const std::string &ss) const;
+
+ // --------------------------------------------------------
+ /** Access a structure by its index */
+ inline const Structure &operator[](const size_t i) const;
+
+public:
+ // --------------------------------------------------------
+ /** Add structure definitions for all the primitive types,
+ * i.e. integer, short, char, float */
+ void AddPrimitiveStructures();
+
+ // --------------------------------------------------------
+ /** Fill the @c converters member with converters for all
+ * known data types. The implementation of this method is
+ * in BlenderScene.cpp and is machine-generated.
+ * Converters are used to quickly handle objects whose
+ * exact data type is a runtime-property and not yet
+ * known at compile time (consider Object::data).*/
+ void RegisterConverters();
+
+ // --------------------------------------------------------
+ /** Take an input blob from the stream, interpret it according to
+ * a its structure name and convert it to the intermediate
+ * representation.
+ * @param structure Destination structure definition
+ * @param db File database.
+ * @return A null pointer if no appropriate converter is available.*/
+ std::shared_ptr<ElemBase> ConvertBlobToStructure(
+ const Structure &structure,
+ const FileDatabase &db) const;
+
+ // --------------------------------------------------------
+ /** Find a suitable conversion function for a given Structure.
+ * Such a converter function takes a blob from the input
+ * stream, reads as much as it needs, and builds up a
+ * complete object in intermediate representation.
+ * @param structure Destination structure definition
+ * @param db File database.
+ * @return A null pointer in .first if no appropriate converter is available.*/
+ FactoryPair GetBlobToStructureConverter(
+ const Structure &structure,
+ const FileDatabase &db) const;
+
+#if ASSIMP_BUILD_BLENDER_DEBUG_DNA
+ // --------------------------------------------------------
+ /** Dump the DNA to a text file. This is for debugging purposes.
+ * The output file is `dna.txt` in the current working folder*/
+ void DumpToFile();
+#endif
+
+ // --------------------------------------------------------
+ /** Extract array dimensions from a C array declaration, such
+ * as `...[4][6]`. Returned string would be `...[][]`.
+ * @param out
+ * @param array_sizes Receive maximally two array dimensions,
+ * the second element is set to 1 if the array is flat.
+ * Both are set to 1 if the input is not an array.
+ * @throw DeadlyImportError if more than 2 dimensions are
+ * encountered. */
+ static void ExtractArraySize(
+ const std::string &out,
+ size_t array_sizes[2]);
+};
+
+// special converters for primitive types
+template <>
+inline void Structure ::Convert<int>(int &dest, const FileDatabase &db) const;
+template <>
+inline void Structure ::Convert<short>(short &dest, const FileDatabase &db) const;
+template <>
+inline void Structure ::Convert<char>(char &dest, const FileDatabase &db) const;
+template <>
+inline void Structure ::Convert<float>(float &dest, const FileDatabase &db) const;
+template <>
+inline void Structure ::Convert<double>(double &dest, const FileDatabase &db) const;
+template <>
+inline void Structure ::Convert<Pointer>(Pointer &dest, const FileDatabase &db) const;
+
+// -------------------------------------------------------------------------------
+/** Describes a master file block header. Each master file sections holds n
+ * elements of a certain SDNA structure (or otherwise unspecified data). */
+// -------------------------------------------------------------------------------
+struct FileBlockHead {
+ // points right after the header of the file block
+ StreamReaderAny::pos start;
+
+ std::string id;
+ size_t size;
+
+ // original memory address of the data
+ Pointer address;
+
+ // index into DNA
+ unsigned int dna_index;
+
+ // number of structure instances to follow
+ size_t num;
+
+ // file blocks are sorted by address to quickly locate specific memory addresses
+ bool operator<(const FileBlockHead &o) const {
+ return address.val < o.address.val;
+ }
+
+ // for std::upper_bound
+ operator const Pointer &() const {
+ return address;
+ }
+};
+
+// for std::upper_bound
+inline bool operator<(const Pointer &a, const Pointer &b) {
+ return a.val < b.val;
+}
+
+// -------------------------------------------------------------------------------
+/** Utility to read all master file blocks in turn. */
+// -------------------------------------------------------------------------------
+class SectionParser {
+public:
+ // --------------------------------------------------------
+ /** @param stream Inout stream, must point to the
+ * first section in the file. Call Next() once
+ * to have it read.
+ * @param ptr64 Pointer size in file is 64 bits? */
+ SectionParser(StreamReaderAny &stream, bool ptr64) :
+ stream(stream), ptr64(ptr64) {
+ current.size = current.start = 0;
+ }
+
+public:
+ // --------------------------------------------------------
+ const FileBlockHead &GetCurrent() const {
+ return current;
+ }
+
+public:
+ // --------------------------------------------------------
+ /** Advance to the next section.
+ * @throw DeadlyImportError if the last chunk was passed. */
+ void Next();
+
+public:
+ FileBlockHead current;
+ StreamReaderAny &stream;
+ bool ptr64;
+};
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+// -------------------------------------------------------------------------------
+/** Import statistics, i.e. number of file blocks read*/
+// -------------------------------------------------------------------------------
+class Statistics {
+
+public:
+ Statistics() :
+ fields_read(), pointers_resolved(), cache_hits()
+ // , blocks_read ()
+ ,
+ cached_objects() {}
+
+public:
+ /** total number of fields we read */
+ unsigned int fields_read;
+
+ /** total number of resolved pointers */
+ unsigned int pointers_resolved;
+
+ /** number of pointers resolved from the cache */
+ unsigned int cache_hits;
+
+ /** number of blocks (from FileDatabase::entries)
+ we did actually read from. */
+ // unsigned int blocks_read;
+
+ /** objects in FileData::cache */
+ unsigned int cached_objects;
+};
+#endif
+
+// -------------------------------------------------------------------------------
+/** The object cache - all objects addressed by pointers are added here. This
+ * avoids circular references and avoids object duplication. */
+// -------------------------------------------------------------------------------
+template <template <typename> class TOUT>
+class ObjectCache {
+public:
+ typedef std::map<Pointer, TOUT<ElemBase>> StructureCache;
+
+public:
+ ObjectCache(const FileDatabase &db) :
+ db(db) {
+ // currently there are only ~400 structure records per blend file.
+ // we read only a small part of them and don't cache objects
+ // which we don't need, so this should suffice.
+ caches.reserve(64);
+ }
+
+public:
+ // --------------------------------------------------------
+ /** Check whether a specific item is in the cache.
+ * @param s Data type of the item
+ * @param out Output pointer. Unchanged if the
+ * cache doesn't know the item yet.
+ * @param ptr Item address to look for. */
+ template <typename T>
+ void get(
+ const Structure &s,
+ TOUT<T> &out,
+ const Pointer &ptr) const;
+
+ // --------------------------------------------------------
+ /** Add an item to the cache after the item has
+ * been fully read. Do not insert anything that
+ * may be faulty or might cause the loading
+ * to abort.
+ * @param s Data type of the item
+ * @param out Item to insert into the cache
+ * @param ptr address (cache key) of the item. */
+ template <typename T>
+ void set(const Structure &s,
+ const TOUT<T> &out,
+ const Pointer &ptr);
+
+private:
+ mutable vector<StructureCache> caches;
+ const FileDatabase &db;
+};
+
+// -------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------
+template <>
+class ObjectCache<Blender::vector> {
+public:
+ ObjectCache(const FileDatabase &) {}
+
+ template <typename T>
+ void get(const Structure &, vector<T> &, const Pointer &) {}
+ template <typename T>
+ void set(const Structure &, const vector<T> &, const Pointer &) {}
+};
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4355)
+#endif
+
+// -------------------------------------------------------------------------------
+/** Memory representation of a full BLEND file and all its dependencies. The
+ * output aiScene is constructed from an instance of this data structure. */
+// -------------------------------------------------------------------------------
+class FileDatabase {
+ template <template <typename> class TOUT>
+ friend class ObjectCache;
+
+public:
+ FileDatabase() :
+ _cacheArrays(*this), _cache(*this), next_cache_idx() {}
+
+public:
+ // publicly accessible fields
+ bool i64bit;
+ bool little;
+
+ DNA dna;
+ std::shared_ptr<StreamReaderAny> reader;
+ vector<FileBlockHead> entries;
+
+public:
+ Statistics &stats() const {
+ return _stats;
+ }
+
+ // For all our templates to work on both shared_ptr's and vector's
+ // using the same code, a dummy cache for arrays is provided. Actually,
+ // arrays of objects are never cached because we can't easily
+ // ensure their proper destruction.
+ template <typename T>
+ ObjectCache<std::shared_ptr> &cache(std::shared_ptr<T> & /*in*/) const {
+ return _cache;
+ }
+
+ template <typename T>
+ ObjectCache<vector> &cache(vector<T> & /*in*/) const {
+ return _cacheArrays;
+ }
+
+private:
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ mutable Statistics _stats;
+#endif
+
+ mutable ObjectCache<vector> _cacheArrays;
+ mutable ObjectCache<std::shared_ptr> _cache;
+
+ mutable size_t next_cache_idx;
+};
+
+#ifdef _MSC_VER
+#pragma warning(default : 4355)
+#endif
+
+// -------------------------------------------------------------------------------
+/** Factory to extract a #DNA from the DNA1 file block in a BLEND file. */
+// -------------------------------------------------------------------------------
+class DNAParser {
+
+public:
+ /** Bind the parser to a empty DNA and an input stream */
+ DNAParser(FileDatabase &db) :
+ db(db) {}
+
+public:
+ // --------------------------------------------------------
+ /** Locate the DNA in the file and parse it. The input
+ * stream is expected to point to the beginning of the DN1
+ * chunk at the time this method is called and is
+ * undefined afterwards.
+ * @throw DeadlyImportError if the DNA cannot be read.
+ * @note The position of the stream pointer is undefined
+ * afterwards.*/
+ void Parse();
+
+public:
+ /** Obtain a reference to the extracted DNA information */
+ const Blender::DNA &GetDNA() const {
+ return db.dna;
+ }
+
+private:
+ FileDatabase &db;
+};
+
+/**
+* @brief read CustomData's data to ptr to mem
+* @param[out] out memory ptr to set
+* @param[in] cdtype to read
+* @param[in] cnt cnt of elements to read
+* @param[in] db to read elements from
+* @return true when ok
+*/
+bool readCustomData(std::shared_ptr<ElemBase> &out, int cdtype, size_t cnt, const FileDatabase &db);
+
+} // namespace Blender
+} // namespace Assimp
+
+#include "BlenderDNA.inl"
+
+#endif
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderDNA.inl b/libs/assimp/code/AssetLib/Blender/BlenderDNA.inl
new file mode 100644
index 0000000..4f64987
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderDNA.inl
@@ -0,0 +1,845 @@
+/*
+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 BlenderDNA.inl
+ * @brief Blender `DNA` (file format specification embedded in
+ * blend file itself) loader.
+ */
+#ifndef INCLUDED_AI_BLEND_DNA_INL
+#define INCLUDED_AI_BLEND_DNA_INL
+
+#include <memory>
+#include <assimp/TinyFormatter.h>
+
+namespace Assimp {
+namespace Blender {
+
+//--------------------------------------------------------------------------------
+const Field& Structure :: operator [] (const std::string& ss) const
+{
+ std::map<std::string, size_t>::const_iterator it = indices.find(ss);
+ if (it == indices.end()) {
+ throw Error("BlendDNA: Did not find a field named `",ss,"` in structure `",name,"`");
+ }
+
+ return fields[(*it).second];
+}
+
+//--------------------------------------------------------------------------------
+const Field* Structure :: Get (const std::string& ss) const
+{
+ std::map<std::string, size_t>::const_iterator it = indices.find(ss);
+ return it == indices.end() ? nullptr : &fields[(*it).second];
+}
+
+//--------------------------------------------------------------------------------
+const Field& Structure :: operator [] (const size_t i) const
+{
+ if (i >= fields.size()) {
+ throw Error("BlendDNA: There is no field with index `",i,"` in structure `",name,"`");
+ }
+
+ return fields[i];
+}
+
+//--------------------------------------------------------------------------------
+template <typename T> std::shared_ptr<ElemBase> Structure :: Allocate() const
+{
+ return std::shared_ptr<T>(new T());
+}
+
+//--------------------------------------------------------------------------------
+template <typename T> void Structure :: Convert(
+ std::shared_ptr<ElemBase> in,
+ const FileDatabase& db) const
+{
+ Convert<T> (*static_cast<T*> ( in.get() ),db);
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, typename T, size_t M>
+void Structure :: ReadFieldArray(T (& out)[M], const char* name, const FileDatabase& db) const
+{
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+ try {
+ const Field& f = (*this)[name];
+ const Structure& s = db.dna[f.type];
+
+ // is the input actually an array?
+ if (!(f.flags & FieldFlag_Array)) {
+ throw Error("Field `",name,"` of structure `",this->name,"` ought to be an array of size ",M);
+ }
+
+ db.reader->IncPtr(f.offset);
+
+ // size conversions are always allowed, regardless of error_policy
+ unsigned int i = 0;
+ for(; i < std::min(f.array_sizes[0],M); ++i) {
+ s.Convert(out[i],db);
+ }
+ for(; i < M; ++i) {
+ _defaultInitializer<ErrorPolicy_Igno>()(out[i]);
+ }
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out,e.what());
+ }
+
+ // and recover the previous stream position
+ db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, typename T, size_t M, size_t N>
+void Structure :: ReadFieldArray2(T (& out)[M][N], const char* name, const FileDatabase& db) const
+{
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+ try {
+ const Field& f = (*this)[name];
+ const Structure& s = db.dna[f.type];
+
+ // is the input actually an array?
+ if (!(f.flags & FieldFlag_Array)) {
+ throw Error("Field `",name,"` of structure `",
+ this->name,"` ought to be an array of size ",M,"*",N
+ );
+ }
+
+ db.reader->IncPtr(f.offset);
+
+ // size conversions are always allowed, regardless of error_policy
+ unsigned int i = 0;
+ for(; i < std::min(f.array_sizes[0],M); ++i) {
+ unsigned int j = 0;
+ for(; j < std::min(f.array_sizes[1],N); ++j) {
+ s.Convert(out[i][j],db);
+ }
+ for(; j < N; ++j) {
+ _defaultInitializer<ErrorPolicy_Igno>()(out[i][j]);
+ }
+ }
+ for(; i < M; ++i) {
+ _defaultInitializer<ErrorPolicy_Igno>()(out[i]);
+ }
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out,e.what());
+ }
+
+ // and recover the previous stream position
+ db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, template <typename> class TOUT, typename T>
+bool Structure :: ReadFieldPtr(TOUT<T>& out, const char* name, const FileDatabase& db,
+ bool non_recursive /*= false*/) const
+{
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+ Pointer ptrval;
+ const Field* f;
+ try {
+ f = &(*this)[name];
+
+ // sanity check, should never happen if the genblenddna script is right
+ if (!(f->flags & FieldFlag_Pointer)) {
+ throw Error("Field `",name,"` of structure `",
+ this->name,"` ought to be a pointer");
+ }
+
+ db.reader->IncPtr(f->offset);
+ Convert(ptrval,db);
+ // actually it is meaningless on which Structure the Convert is called
+ // because the `Pointer` argument triggers a special implementation.
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out,e.what());
+
+ out.reset();
+ return false;
+ }
+
+ // resolve the pointer and load the corresponding structure
+ const bool res = ResolvePointer(out,ptrval,db,*f, non_recursive);
+
+ if(!non_recursive) {
+ // and recover the previous stream position
+ db.reader->SetCurrentPos(old);
+ }
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+
+ return res;
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, template <typename> class TOUT, typename T, size_t N>
+bool Structure :: ReadFieldPtr(TOUT<T> (&out)[N], const char* name,
+ const FileDatabase& db) const
+{
+ // XXX see if we can reduce this to call to the 'normal' ReadFieldPtr
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+ Pointer ptrval[N];
+ const Field* f;
+ try {
+ f = &(*this)[name];
+
+#ifdef _DEBUG
+ // sanity check, should never happen if the genblenddna script is right
+ if ((FieldFlag_Pointer|FieldFlag_Pointer) != (f->flags & (FieldFlag_Pointer|FieldFlag_Pointer))) {
+ throw Error("Field `",name,"` of structure `",
+ this->name,"` ought to be a pointer AND an array");
+ }
+#endif // _DEBUG
+
+ db.reader->IncPtr(f->offset);
+
+ size_t i = 0;
+ for(; i < std::min(f->array_sizes[0],N); ++i) {
+ Convert(ptrval[i],db);
+ }
+ for(; i < N; ++i) {
+ _defaultInitializer<ErrorPolicy_Igno>()(ptrval[i]);
+ }
+
+ // actually it is meaningless on which Structure the Convert is called
+ // because the `Pointer` argument triggers a special implementation.
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out,e.what());
+ for(size_t i = 0; i < N; ++i) {
+ out[i].reset();
+ }
+ return false;
+ }
+
+ bool res = true;
+ for(size_t i = 0; i < N; ++i) {
+ // resolve the pointer and load the corresponding structure
+ res = ResolvePointer(out[i],ptrval[i],db,*f) && res;
+ }
+
+ // and recover the previous stream position
+ db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+ return res;
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, typename T>
+void Structure :: ReadField(T& out, const char* name, const FileDatabase& db) const
+{
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+ try {
+ const Field& f = (*this)[name];
+ // find the structure definition pertaining to this field
+ const Structure& s = db.dna[f.type];
+
+ db.reader->IncPtr(f.offset);
+ s.Convert(out,db);
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out,e.what());
+ }
+
+ // and recover the previous stream position
+ db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+}
+
+
+//--------------------------------------------------------------------------------
+// field parsing for raw untyped data (like CustomDataLayer.data)
+template <int error_policy>
+bool Structure::ReadCustomDataPtr(std::shared_ptr<ElemBase>&out, int cdtype, const char* name, const FileDatabase& db) const {
+
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+
+ Pointer ptrval;
+ const Field* f;
+ try {
+ f = &(*this)[name];
+
+ // sanity check, should never happen if the genblenddna script is right
+ if (!(f->flags & FieldFlag_Pointer)) {
+ throw Error("Field `", name, "` of structure `",
+ this->name, "` ought to be a pointer");
+ }
+
+ db.reader->IncPtr(f->offset);
+ Convert(ptrval, db);
+ // actually it is meaningless on which Structure the Convert is called
+ // because the `Pointer` argument triggers a special implementation.
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out, e.what());
+ out.reset();
+ }
+
+ bool readOk = true;
+ if (ptrval.val) {
+ // get block for ptr
+ const FileBlockHead* block = LocateFileBlockForAddress(ptrval, db);
+ db.reader->SetCurrentPos(block->start + static_cast<size_t>((ptrval.val - block->address.val)));
+ // read block->num instances of given type to out
+ readOk = readCustomData(out, cdtype, block->num, db);
+ }
+
+ // and recover the previous stream position
+ db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+
+ return readOk;
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, template <typename> class TOUT, typename T>
+bool Structure::ReadFieldPtrVector(vector<TOUT<T>>&out, const char* name, const FileDatabase& db) const {
+ out.clear();
+
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+
+ Pointer ptrval;
+ const Field* f;
+ try {
+ f = &(*this)[name];
+
+ // sanity check, should never happen if the genblenddna script is right
+ if (!(f->flags & FieldFlag_Pointer)) {
+ throw Error("Field `", name, "` of structure `",
+ this->name, "` ought to be a pointer");
+ }
+
+ db.reader->IncPtr(f->offset);
+ Convert(ptrval, db);
+ // actually it is meaningless on which Structure the Convert is called
+ // because the `Pointer` argument triggers a special implementation.
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out, e.what());
+ out.clear();
+ return false;
+ }
+
+
+ if (ptrval.val) {
+ // find the file block the pointer is pointing to
+ const FileBlockHead* block = LocateFileBlockForAddress(ptrval, db);
+ db.reader->SetCurrentPos(block->start + static_cast<size_t>((ptrval.val - block->address.val)));
+ // FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems.
+ // I really ought to improve StreamReader to work with 64 bit indices exclusively.
+
+ const Structure& s = db.dna[f->type];
+ for (size_t i = 0; i < block->num; ++i) {
+ TOUT<T> p(new T);
+ s.Convert(*p, db);
+ out.push_back(p);
+ }
+ }
+
+ db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+
+ return true;
+}
+
+
+//--------------------------------------------------------------------------------
+template <template <typename> class TOUT, typename T>
+bool Structure :: ResolvePointer(TOUT<T>& out, const Pointer & ptrval, const FileDatabase& db,
+ const Field& f,
+ bool non_recursive /*= false*/) const
+{
+ out.reset(); // ensure null pointers work
+ if (!ptrval.val) {
+ return false;
+ }
+ const Structure& s = db.dna[f.type];
+ // find the file block the pointer is pointing to
+ const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
+
+ // also determine the target type from the block header
+ // and check if it matches the type which we expect.
+ const Structure& ss = db.dna[block->dna_index];
+ if (ss != s) {
+ throw Error("Expected target to be of type `",s.name,
+ "` but seemingly it is a `",ss.name,"` instead"
+ );
+ }
+
+ // try to retrieve the object from the cache
+ db.cache(out).get(s,out,ptrval);
+ if (out) {
+ return true;
+ }
+
+ // seek to this location, but save the previous stream pointer.
+ const StreamReaderAny::pos pold = db.reader->GetCurrentPos();
+ db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) ));
+ // FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems.
+ // I really ought to improve StreamReader to work with 64 bit indices exclusively.
+
+ // continue conversion after allocating the required storage
+ size_t num = block->size / ss.size;
+ T* o = _allocate(out,num);
+
+ // cache the object before we convert it to avoid cyclic recursion.
+ db.cache(out).set(s,out,ptrval);
+
+ // if the non_recursive flag is set, we don't do anything but leave
+ // the cursor at the correct position to resolve the object.
+ if (!non_recursive) {
+ for (size_t i = 0; i < num; ++i,++o) {
+ s.Convert(*o,db);
+ }
+
+ db.reader->SetCurrentPos(pold);
+ }
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ if(out) {
+ ++db.stats().pointers_resolved;
+ }
+#endif
+ return false;
+}
+
+
+//--------------------------------------------------------------------------------
+inline bool Structure :: ResolvePointer( std::shared_ptr< FileOffset >& out, const Pointer & ptrval,
+ const FileDatabase& db,
+ const Field&,
+ bool) const
+{
+ // Currently used exclusively by PackedFile::data to represent
+ // a simple offset into the mapped BLEND file.
+ out.reset();
+ if (!ptrval.val) {
+ return false;
+ }
+
+ // find the file block the pointer is pointing to
+ const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
+
+ out = std::shared_ptr< FileOffset > (new FileOffset());
+ out->val = block->start+ static_cast<size_t>((ptrval.val - block->address.val) );
+ return false;
+}
+
+//--------------------------------------------------------------------------------
+template <template <typename> class TOUT, typename T>
+bool Structure :: ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval,
+ const FileDatabase& db,
+ const Field& f,
+ bool) const
+{
+ // This is a function overload, not a template specialization. According to
+ // the partial ordering rules, it should be selected by the compiler
+ // for array-of-pointer inputs, i.e. Object::mats.
+
+ out.reset();
+ if (!ptrval.val) {
+ return false;
+ }
+
+ // find the file block the pointer is pointing to
+ const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
+ const size_t num = block->size / (db.i64bit?8:4);
+
+ // keep the old stream position
+ const StreamReaderAny::pos pold = db.reader->GetCurrentPos();
+ db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) ));
+
+ bool res = false;
+ // allocate raw storage for the array
+ out.resize(num);
+ for (size_t i = 0; i< num; ++i) {
+ Pointer val;
+ Convert(val,db);
+
+ // and resolve the pointees
+ res = ResolvePointer(out[i],val,db,f) && res;
+ }
+
+ db.reader->SetCurrentPos(pold);
+ return res;
+}
+
+//--------------------------------------------------------------------------------
+template <> bool Structure :: ResolvePointer<std::shared_ptr,ElemBase>(std::shared_ptr<ElemBase>& out,
+ const Pointer & ptrval,
+ const FileDatabase& db,
+ const Field&,
+ bool
+) const
+{
+ // Special case when the data type needs to be determined at runtime.
+ // Less secure than in the `strongly-typed` case.
+
+ out.reset();
+ if (!ptrval.val) {
+ return false;
+ }
+
+ // find the file block the pointer is pointing to
+ const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
+
+ // determine the target type from the block header
+ const Structure& s = db.dna[block->dna_index];
+
+ // try to retrieve the object from the cache
+ db.cache(out).get(s,out,ptrval);
+ if (out) {
+ return true;
+ }
+
+ // seek to this location, but save the previous stream pointer.
+ const StreamReaderAny::pos pold = db.reader->GetCurrentPos();
+ db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) ));
+ // FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems.
+ // I really ought to improve StreamReader to work with 64 bit indices exclusively.
+
+ // continue conversion after allocating the required storage
+ DNA::FactoryPair builders = db.dna.GetBlobToStructureConverter(s,db);
+ if (!builders.first) {
+ // this might happen if DNA::RegisterConverters hasn't been called so far
+ // or if the target type is not contained in `our` DNA.
+ out.reset();
+ ASSIMP_LOG_WARN( "Failed to find a converter for the `",s.name,"` structure" );
+ return false;
+ }
+
+ // allocate the object hull
+ out = (s.*builders.first)();
+
+ // cache the object immediately to prevent infinite recursion in a
+ // circular list with a single element (i.e. a self-referencing element).
+ db.cache(out).set(s,out,ptrval);
+
+ // and do the actual conversion
+ (s.*builders.second)(out,db);
+ db.reader->SetCurrentPos(pold);
+
+ // store a pointer to the name string of the actual type
+ // in the object itself. This allows the conversion code
+ // to perform additional type checking.
+ out->dna_type = s.name.c_str();
+
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().pointers_resolved;
+#endif
+ return false;
+}
+
+//--------------------------------------------------------------------------------
+const FileBlockHead* Structure :: LocateFileBlockForAddress(const Pointer & ptrval, const FileDatabase& db) const
+{
+ // the file blocks appear in list sorted by
+ // with ascending base addresses so we can run a
+ // binary search to locate the pointer quickly.
+
+ // NOTE: Blender seems to distinguish between side-by-side
+ // data (stored in the same data block) and far pointers,
+ // which are only used for structures starting with an ID.
+ // We don't need to make this distinction, our algorithm
+ // works regardless where the data is stored.
+ vector<FileBlockHead>::const_iterator it = std::lower_bound(db.entries.begin(),db.entries.end(),ptrval);
+ if (it == db.entries.end()) {
+ // this is crucial, pointers may not be invalid.
+ // this is either a corrupted file or an attempted attack.
+ throw DeadlyImportError("Failure resolving pointer 0x",
+ std::hex,ptrval.val,", no file block falls into this address range");
+ }
+ if (ptrval.val >= (*it).address.val + (*it).size) {
+ throw DeadlyImportError("Failure resolving pointer 0x",
+ std::hex,ptrval.val,", nearest file block starting at 0x",
+ (*it).address.val," ends at 0x",
+ (*it).address.val + (*it).size);
+ }
+ return &*it;
+}
+
+// ------------------------------------------------------------------------------------------------
+// NOTE: The MSVC debugger keeps showing up this annoying `a cast to a smaller data type has
+// caused a loss of data`-warning. Avoid this warning by a masking with an appropriate bitmask.
+
+template <typename T> struct signless;
+template <> struct signless<char> {typedef unsigned char type;};
+template <> struct signless<short> {typedef unsigned short type;};
+template <> struct signless<int> {typedef unsigned int type;};
+template <> struct signless<unsigned char> { typedef unsigned char type; };
+template <typename T>
+struct static_cast_silent {
+ template <typename V>
+ T operator()(V in) {
+ return static_cast<T>(in & static_cast<typename signless<T>::type>(-1));
+ }
+};
+
+template <> struct static_cast_silent<float> {
+ template <typename V> float operator()(V in) {
+ return static_cast<float> (in);
+ }
+};
+
+template <> struct static_cast_silent<double> {
+ template <typename V> double operator()(V in) {
+ return static_cast<double>(in);
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+template <typename T> inline void ConvertDispatcher(T& out, const Structure& in,const FileDatabase& db)
+{
+ if (in.name == "int") {
+ out = static_cast_silent<T>()(db.reader->GetU4());
+ }
+ else if (in.name == "short") {
+ out = static_cast_silent<T>()(db.reader->GetU2());
+ }
+ else if (in.name == "char") {
+ out = static_cast_silent<T>()(db.reader->GetU1());
+ }
+ else if (in.name == "float") {
+ out = static_cast<T>(db.reader->GetF4());
+ }
+ else if (in.name == "double") {
+ out = static_cast<T>(db.reader->GetF8());
+ }
+ else {
+ throw DeadlyImportError("Unknown source for conversion to primitive data type: ", in.name);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<int> (int& dest,const FileDatabase& db) const
+{
+ ConvertDispatcher(dest,*this,db);
+}
+
+// ------------------------------------------------------------------------------------------------
+template<> inline void Structure :: Convert<short> (short& dest,const FileDatabase& db) const
+{
+ // automatic rescaling from short to float and vice versa (seems to be used by normals)
+ if (name == "float") {
+ float f = db.reader->GetF4();
+ if ( f > 1.0f )
+ f = 1.0f;
+ dest = static_cast<short>( f * 32767.f);
+ //db.reader->IncPtr(-4);
+ return;
+ }
+ else if (name == "double") {
+ dest = static_cast<short>(db.reader->GetF8() * 32767.);
+ //db.reader->IncPtr(-8);
+ return;
+ }
+ ConvertDispatcher(dest,*this,db);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<char> (char& dest,const FileDatabase& db) const
+{
+ // automatic rescaling from char to float and vice versa (seems useful for RGB colors)
+ if (name == "float") {
+ dest = static_cast<char>(db.reader->GetF4() * 255.f);
+ return;
+ }
+ else if (name == "double") {
+ dest = static_cast<char>(db.reader->GetF8() * 255.f);
+ return;
+ }
+ ConvertDispatcher(dest,*this,db);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure::Convert<unsigned char>(unsigned char& dest, const FileDatabase& db) const
+{
+ // automatic rescaling from char to float and vice versa (seems useful for RGB colors)
+ if (name == "float") {
+ dest = static_cast<unsigned char>(db.reader->GetF4() * 255.f);
+ return;
+ }
+ else if (name == "double") {
+ dest = static_cast<unsigned char>(db.reader->GetF8() * 255.f);
+ return;
+ }
+ ConvertDispatcher(dest, *this, db);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<float> (float& dest,const FileDatabase& db) const
+{
+ // automatic rescaling from char to float and vice versa (seems useful for RGB colors)
+ if (name == "char") {
+ dest = db.reader->GetI1() / 255.f;
+ return;
+ }
+ // automatic rescaling from short to float and vice versa (used by normals)
+ else if (name == "short") {
+ dest = db.reader->GetI2() / 32767.f;
+ return;
+ }
+ ConvertDispatcher(dest,*this,db);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<double> (double& dest,const FileDatabase& db) const
+{
+ if (name == "char") {
+ dest = db.reader->GetI1() / 255.;
+ return;
+ }
+ else if (name == "short") {
+ dest = db.reader->GetI2() / 32767.;
+ return;
+ }
+ ConvertDispatcher(dest,*this,db);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<Pointer> (Pointer& dest,const FileDatabase& db) const
+{
+ if (db.i64bit) {
+ dest.val = db.reader->GetU8();
+ //db.reader->IncPtr(-8);
+ return;
+ }
+ dest.val = db.reader->GetU4();
+ //db.reader->IncPtr(-4);
+}
+
+//--------------------------------------------------------------------------------
+const Structure& DNA :: operator [] (const std::string& ss) const
+{
+ std::map<std::string, size_t>::const_iterator it = indices.find(ss);
+ if (it == indices.end()) {
+ throw Error("BlendDNA: Did not find a structure named `",ss,"`");
+ }
+
+ return structures[(*it).second];
+}
+
+//--------------------------------------------------------------------------------
+const Structure* DNA :: Get (const std::string& ss) const
+{
+ std::map<std::string, size_t>::const_iterator it = indices.find(ss);
+ return it == indices.end() ? nullptr : &structures[(*it).second];
+}
+
+//--------------------------------------------------------------------------------
+const Structure& DNA :: operator [] (const size_t i) const
+{
+ if (i >= structures.size()) {
+ throw Error("BlendDNA: There is no structure with index `",i,"`");
+ }
+
+ return structures[i];
+}
+
+//--------------------------------------------------------------------------------
+template <template <typename> class TOUT> template <typename T> void ObjectCache<TOUT> :: get (
+ const Structure& s,
+ TOUT<T>& out,
+ const Pointer& ptr
+) const {
+
+ if(s.cache_idx == static_cast<size_t>(-1)) {
+ s.cache_idx = db.next_cache_idx++;
+ caches.resize(db.next_cache_idx);
+ return;
+ }
+
+ typename StructureCache::const_iterator it = caches[s.cache_idx].find(ptr);
+ if (it != caches[s.cache_idx].end()) {
+ out = std::static_pointer_cast<T>( (*it).second );
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().cache_hits;
+#endif
+ }
+ // otherwise, out remains untouched
+}
+
+
+//--------------------------------------------------------------------------------
+template <template <typename> class TOUT> template <typename T> void ObjectCache<TOUT> :: set (
+ const Structure& s,
+ const TOUT<T>& out,
+ const Pointer& ptr
+) {
+ if(s.cache_idx == static_cast<size_t>(-1)) {
+ s.cache_idx = db.next_cache_idx++;
+ caches.resize(db.next_cache_idx);
+ }
+ caches[s.cache_idx][ptr] = std::static_pointer_cast<ElemBase>( out );
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().cached_objects;
+#endif
+}
+
+}}
+#endif
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderIntermediate.h b/libs/assimp/code/AssetLib/Blender/BlenderIntermediate.h
new file mode 100644
index 0000000..0651f71
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderIntermediate.h
@@ -0,0 +1,206 @@
+/*
+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 BlenderIntermediate.h
+ * @brief Internal utility structures for the BlenderLoader. It also serves
+ * as master include file for the whole (internal) Blender subsystem.
+ */
+#ifndef INCLUDED_AI_BLEND_INTERMEDIATE_H
+#define INCLUDED_AI_BLEND_INTERMEDIATE_H
+
+#include "BlenderLoader.h"
+#include "BlenderDNA.h"
+#include "BlenderScene.h"
+#include <deque>
+#include <assimp/material.h>
+
+struct aiTexture;
+
+namespace Assimp {
+namespace Blender {
+
+ // --------------------------------------------------------------------
+ /** Mini smart-array to avoid pulling in even more boost stuff. usable with vector and deque */
+ // --------------------------------------------------------------------
+ template <template <typename,typename> class TCLASS, typename T>
+ struct TempArray {
+ typedef TCLASS< T*,std::allocator<T*> > mywrap;
+
+ TempArray() {
+ }
+
+ ~TempArray () {
+ for(T* elem : arr) {
+ delete elem;
+ }
+ }
+
+ void dismiss() {
+ arr.clear();
+ }
+
+ mywrap* operator -> () {
+ return &arr;
+ }
+
+ operator mywrap& () {
+ return arr;
+ }
+
+ operator const mywrap& () const {
+ return arr;
+ }
+
+ mywrap& get () {
+ return arr;
+ }
+
+ const mywrap& get () const {
+ return arr;
+ }
+
+ T* operator[] (size_t idx) const {
+ return arr[idx];
+ }
+
+ T*& operator[] (size_t idx) {
+ return arr[idx];
+ }
+
+ private:
+ // no copy semantics
+ void operator= (const TempArray&) {
+ }
+
+ TempArray(const TempArray& /*arr*/) {
+ }
+
+ private:
+ mywrap arr;
+ };
+
+#ifdef _MSC_VER
+# pragma warning(disable:4351)
+#endif
+
+ // As counter-intuitive as it may seem, a comparator must return false for equal values.
+ // The C++ standard defines and expects this behavior: true if lhs < rhs, false otherwise.
+ struct ObjectCompare {
+ bool operator() (const Object* left, const Object* right) const {
+ return ::strncmp(left->id.name, right->id.name, strlen( left->id.name ) ) < 0;
+ }
+ };
+
+ // When keeping objects in sets, sort them by their name.
+ typedef std::set<const Object*, ObjectCompare> ObjectSet;
+
+ // --------------------------------------------------------------------
+ /** ConversionData acts as intermediate storage location for
+ * the various ConvertXXX routines in BlenderImporter.*/
+ // --------------------------------------------------------------------
+ struct ConversionData
+ {
+ ConversionData(const FileDatabase& db)
+ : sentinel_cnt()
+ , next_texture()
+ , db(db)
+ {}
+
+ // As counter-intuitive as it may seem, a comparator must return false for equal values.
+ // The C++ standard defines and expects this behavior: true if lhs < rhs, false otherwise.
+ struct ObjectCompare {
+ bool operator() (const Object* left, const Object* right) const {
+ return ::strncmp( left->id.name, right->id.name, strlen( left->id.name ) ) < 0;
+ }
+ };
+
+ ObjectSet objects;
+
+ TempArray <std::vector, aiMesh> meshes;
+ TempArray <std::vector, aiCamera> cameras;
+ TempArray <std::vector, aiLight> lights;
+ TempArray <std::vector, aiMaterial> materials;
+ TempArray <std::vector, aiTexture> textures;
+
+ // set of all materials referenced by at least one mesh in the scene
+ std::deque< std::shared_ptr< Material > > materials_raw;
+
+ // counter to name sentinel textures inserted as substitutes for procedural textures.
+ unsigned int sentinel_cnt;
+
+ // next texture ID for each texture type, respectively
+ unsigned int next_texture[aiTextureType_UNKNOWN+1];
+
+ // original file data
+ const FileDatabase& db;
+ };
+#ifdef _MSC_VER
+# pragma warning(default:4351)
+#endif
+
+// ------------------------------------------------------------------------------------------------
+inline const char* GetTextureTypeDisplayString(Tex::Type t)
+{
+ switch (t) {
+ case Tex::Type_CLOUDS : return "Clouds";
+ case Tex::Type_WOOD : return "Wood";
+ case Tex::Type_MARBLE : return "Marble";
+ case Tex::Type_MAGIC : return "Magic";
+ case Tex::Type_BLEND : return "Blend";
+ case Tex::Type_STUCCI : return "Stucci";
+ case Tex::Type_NOISE : return "Noise";
+ case Tex::Type_PLUGIN : return "Plugin";
+ case Tex::Type_MUSGRAVE : return "Musgrave";
+ case Tex::Type_VORONOI : return "Voronoi";
+ case Tex::Type_DISTNOISE : return "DistortedNoise";
+ case Tex::Type_ENVMAP : return "EnvMap";
+ case Tex::Type_IMAGE : return "Image";
+ default:
+ break;
+ }
+ return "<Unknown>";
+}
+
+} // ! Blender
+} // ! Assimp
+
+#endif // ! INCLUDED_AI_BLEND_INTERMEDIATE_H
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderLoader.cpp b/libs/assimp/code/AssetLib/Blender/BlenderLoader.cpp
new file mode 100644
index 0000000..b3591b4
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderLoader.cpp
@@ -0,0 +1,1340 @@
+
+/*
+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 BlenderLoader.cpp
+ * @brief Implementation of the Blender3D importer class.
+ */
+
+//#define ASSIMP_BUILD_NO_COMPRESSED_BLEND
+// Uncomment this to disable support for (gzip)compressed .BLEND files
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+
+#include "BlenderBMesh.h"
+#include "BlenderCustomData.h"
+#include "BlenderIntermediate.h"
+#include "BlenderModifier.h"
+#include <assimp/StringUtils.h>
+#include <assimp/importerdesc.h>
+#include <assimp/scene.h>
+
+#include <assimp/MemoryIOWrapper.h>
+#include <assimp/StreamReader.h>
+#include <assimp/StringComparison.h>
+
+#include <cctype>
+
+// zlib is needed for compressed blend files
+#ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
+#include "Common/Compression.h"
+/* #ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+# include <zlib.h>
+# else
+# include "../contrib/zlib/zlib.h"
+# endif*/
+#endif
+
+namespace Assimp {
+
+template <>
+const char *LogFunctions<BlenderImporter>::Prefix() {
+ static auto prefix = "BLEND: ";
+ return prefix;
+}
+
+} // namespace Assimp
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+using namespace Assimp::Formatter;
+
+static const aiImporterDesc blenderDesc = {
+ "Blender 3D Importer (http://www.blender3d.org)",
+ "",
+ "",
+ "No animation support yet",
+ aiImporterFlags_SupportBinaryFlavour,
+ 0,
+ 0,
+ 2,
+ 50,
+ "blend"
+};
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+BlenderImporter::BlenderImporter() :
+ modifier_cache(new BlenderModifierShowcase()) {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+BlenderImporter::~BlenderImporter() {
+ delete modifier_cache;
+}
+
+static const char * const Tokens[] = { "BLENDER" };
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool BlenderImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+ // note: this won't catch compressed files
+ static const char *tokens[] = { "<BLENDER", "blender" };
+
+ return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Loader registry entry
+const aiImporterDesc *BlenderImporter::GetInfo() const {
+ return &blenderDesc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties for the loader
+void BlenderImporter::SetupProperties(const Importer * /*pImp*/) {
+ // nothing to be done for the moment
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void BlenderImporter::InternReadFile(const std::string &pFile,
+ aiScene *pScene, IOSystem *pIOHandler) {
+#ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
+ std::vector<char> uncompressed;
+#endif
+
+ FileDatabase file;
+ std::shared_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
+ if (!stream) {
+ ThrowException("Could not open file for reading");
+ }
+
+ char magic[8] = { 0 };
+ stream->Read(magic, 7, 1);
+ if (strcmp(magic, Tokens[0])) {
+ // Check for presence of the gzip header. If yes, assume it is a
+ // compressed blend file and try uncompressing it, else fail. This is to
+ // avoid uncompressing random files which our loader might end up with.
+#ifdef ASSIMP_BUILD_NO_COMPRESSED_BLEND
+ ThrowException("BLENDER magic bytes are missing, is this file compressed (Assimp was built without decompression support)?");
+#else
+ if (magic[0] != 0x1f || static_cast<uint8_t>(magic[1]) != 0x8b) {
+ ThrowException("BLENDER magic bytes are missing, couldn't find GZIP header either");
+ }
+
+ LogDebug("Found no BLENDER magic word but a GZIP header, might be a compressed file");
+ if (magic[2] != 8) {
+ ThrowException("Unsupported GZIP compression method");
+ }
+
+ // http://www.gzip.org/zlib/rfc-gzip.html#header-trailer
+ stream->Seek(0L, aiOrigin_SET);
+ std::shared_ptr<StreamReaderLE> reader = std::shared_ptr<StreamReaderLE>(new StreamReaderLE(stream));
+
+ size_t total = 0;
+ Compression compression;
+ if (compression.open(Compression::Format::Binary, Compression::FlushMode::NoFlush, 16 + Compression::MaxWBits)) {
+ total = compression.decompress((unsigned char *)reader->GetPtr(), reader->GetRemainingSize(), uncompressed);
+ compression.close();
+ }
+
+ // replace the input stream with a memory stream
+ stream.reset(new MemoryIOStream(reinterpret_cast<uint8_t *>(uncompressed.data()), total));
+
+ // .. and retry
+ stream->Read(magic, 7, 1);
+ if (strcmp(magic, "BLENDER")) {
+ ThrowException("Found no BLENDER magic word in decompressed GZIP file");
+ }
+#endif
+ }
+
+ file.i64bit = (stream->Read(magic, 1, 1), magic[0] == '-');
+ file.little = (stream->Read(magic, 1, 1), magic[0] == 'v');
+
+ stream->Read(magic, 3, 1);
+ magic[3] = '\0';
+
+ LogInfo("Blender version is ", magic[0], ".", magic + 1,
+ " (64bit: ", file.i64bit ? "true" : "false",
+ ", little endian: ", file.little ? "true" : "false", ")");
+
+ ParseBlendFile(file, stream);
+
+ Scene scene;
+ ExtractScene(scene, file);
+
+ ConvertBlendFile(pScene, scene, file);
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ParseBlendFile(FileDatabase &out, std::shared_ptr<IOStream> stream) {
+ out.reader = std::make_shared<StreamReaderAny>(stream, out.little);
+
+ DNAParser dna_reader(out);
+ const DNA *dna = nullptr;
+
+ out.entries.reserve(128);
+ { // even small BLEND files tend to consist of many file blocks
+ SectionParser parser(*out.reader.get(), out.i64bit);
+
+ // first parse the file in search for the DNA and insert all other sections into the database
+ while ((parser.Next(), 1)) {
+ const FileBlockHead &head = parser.GetCurrent();
+
+ if (head.id == "ENDB") {
+ break; // only valid end of the file
+ } else if (head.id == "DNA1") {
+ dna_reader.Parse();
+ dna = &dna_reader.GetDNA();
+ continue;
+ }
+
+ out.entries.push_back(head);
+ }
+ }
+ if (!dna) {
+ ThrowException("SDNA not found");
+ }
+
+ std::sort(out.entries.begin(), out.entries.end());
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ExtractScene(Scene &out, const FileDatabase &file) {
+ const FileBlockHead *block = nullptr;
+ std::map<std::string, size_t>::const_iterator it = file.dna.indices.find("Scene");
+ if (it == file.dna.indices.end()) {
+ ThrowException("There is no `Scene` structure record");
+ }
+
+ const Structure &ss = file.dna.structures[(*it).second];
+
+ // we need a scene somewhere to start with.
+ for (const FileBlockHead &bl : file.entries) {
+
+ // Fix: using the DNA index is more reliable to locate scenes
+ //if (bl.id == "SC") {
+
+ if (bl.dna_index == (*it).second) {
+ block = &bl;
+ break;
+ }
+ }
+
+ if (!block) {
+ ThrowException("There is not a single `Scene` record to load");
+ }
+
+ file.reader->SetCurrentPos(block->start);
+ ss.Convert(out, file);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ASSIMP_LOG_INFO(
+ "(Stats) Fields read: ", file.stats().fields_read,
+ ", pointers resolved: ", file.stats().pointers_resolved,
+ ", cache hits: ", file.stats().cache_hits,
+ ", cached objects: ", file.stats().cached_objects);
+#endif
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ParseSubCollection(const Blender::Scene &in, aiNode *root, std::shared_ptr<Collection> collection, ConversionData &conv_data) {
+
+ std::deque<Object *> root_objects;
+ // Count number of objects
+ for (std::shared_ptr<CollectionObject> cur = std::static_pointer_cast<CollectionObject>(collection->gobject.first); cur; cur = cur->next) {
+ if (cur->ob) {
+ root_objects.push_back(cur->ob);
+ }
+ }
+ std::deque<Collection *> root_children;
+ // Count number of child nodes
+ for (std::shared_ptr<CollectionChild> cur = std::static_pointer_cast<CollectionChild>(collection->children.first); cur; cur = cur->next) {
+ if (cur->collection) {
+ root_children.push_back(cur->collection.get());
+ }
+ }
+ root->mNumChildren = static_cast<unsigned int>(root_objects.size() + root_children.size());
+ root->mChildren = new aiNode *[root->mNumChildren]();
+
+ for (unsigned int i = 0; i < static_cast<unsigned int>(root_objects.size()); ++i) {
+ root->mChildren[i] = ConvertNode(in, root_objects[i], conv_data, aiMatrix4x4());
+ root->mChildren[i]->mParent = root;
+ }
+
+ // For each subcollection create a new node to represent it
+ unsigned int iterator = static_cast<unsigned int>(root_objects.size());
+ for (std::shared_ptr<CollectionChild> cur = std::static_pointer_cast<CollectionChild>(collection->children.first); cur; cur = cur->next) {
+ if (cur->collection) {
+ root->mChildren[iterator] = new aiNode(cur->collection->id.name + 2); // skip over the name prefix 'OB'
+ root->mChildren[iterator]->mParent = root;
+ ParseSubCollection(in, root->mChildren[iterator], cur->collection, conv_data);
+ }
+ iterator += 1;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ConvertBlendFile(aiScene *out, const Scene &in, const FileDatabase &file) {
+ ConversionData conv(file);
+
+ aiNode *root = out->mRootNode = new aiNode("<BlenderRoot>");
+ // Iterate over all objects directly under master_collection,
+ // If in.master_collection == null, then we're parsing something older.
+ if (in.master_collection) {
+ ParseSubCollection(in, root, in.master_collection, conv);
+ } else {
+ std::deque<const Object *> no_parents;
+ for (std::shared_ptr<Base> cur = std::static_pointer_cast<Base>(in.base.first); cur; cur = cur->next) {
+ if (cur->object) {
+ if (!cur->object->parent) {
+ no_parents.push_back(cur->object.get());
+ } else {
+ conv.objects.insert(cur->object.get());
+ }
+ }
+ }
+ for (std::shared_ptr<Base> cur = in.basact; cur; cur = cur->next) {
+ if (cur->object) {
+ if (cur->object->parent) {
+ conv.objects.insert(cur->object.get());
+ }
+ }
+ }
+
+ if (no_parents.empty()) {
+ ThrowException("Expected at least one object with no parent");
+ }
+
+ root->mNumChildren = static_cast<unsigned int>(no_parents.size());
+ root->mChildren = new aiNode *[root->mNumChildren]();
+ for (unsigned int i = 0; i < root->mNumChildren; ++i) {
+ root->mChildren[i] = ConvertNode(in, no_parents[i], conv, aiMatrix4x4());
+ root->mChildren[i]->mParent = root;
+ }
+ }
+
+ BuildMaterials(conv);
+
+ if (conv.meshes->size()) {
+ out->mMeshes = new aiMesh *[out->mNumMeshes = static_cast<unsigned int>(conv.meshes->size())];
+ std::copy(conv.meshes->begin(), conv.meshes->end(), out->mMeshes);
+ conv.meshes.dismiss();
+ }
+
+ if (conv.lights->size()) {
+ out->mLights = new aiLight *[out->mNumLights = static_cast<unsigned int>(conv.lights->size())];
+ std::copy(conv.lights->begin(), conv.lights->end(), out->mLights);
+ conv.lights.dismiss();
+ }
+
+ if (conv.cameras->size()) {
+ out->mCameras = new aiCamera *[out->mNumCameras = static_cast<unsigned int>(conv.cameras->size())];
+ std::copy(conv.cameras->begin(), conv.cameras->end(), out->mCameras);
+ conv.cameras.dismiss();
+ }
+
+ if (conv.materials->size()) {
+ out->mMaterials = new aiMaterial *[out->mNumMaterials = static_cast<unsigned int>(conv.materials->size())];
+ std::copy(conv.materials->begin(), conv.materials->end(), out->mMaterials);
+ conv.materials.dismiss();
+ }
+
+ if (conv.textures->size()) {
+ out->mTextures = new aiTexture *[out->mNumTextures = static_cast<unsigned int>(conv.textures->size())];
+ std::copy(conv.textures->begin(), conv.textures->end(), out->mTextures);
+ conv.textures.dismiss();
+ }
+
+ // acknowledge that the scene might come out incomplete
+ // by Assimp's definition of `complete`: blender scenes
+ // can consist of thousands of cameras or lights with
+ // not a single mesh between them.
+ if (!out->mNumMeshes) {
+ out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ResolveImage(aiMaterial *out, const Material *mat, const MTex *tex, const Image *img, ConversionData &conv_data) {
+ (void)mat;
+ (void)tex;
+ (void)conv_data;
+ aiString name;
+
+ // check if the file contents are bundled with the BLEND file
+ if (img->packedfile) {
+ name.data[0] = '*';
+ name.length = 1 + ASSIMP_itoa10(name.data + 1, static_cast<unsigned int>(MAXLEN - 1), static_cast<int32_t>(conv_data.textures->size()));
+
+ conv_data.textures->push_back(new aiTexture());
+ aiTexture *curTex = conv_data.textures->back();
+
+ // usually 'img->name' will be the original file name of the embedded textures,
+ // so we can extract the file extension from it.
+ const size_t nlen = strlen(img->name);
+ const char *s = img->name + nlen, *e = s;
+ while (s >= img->name && *s != '.') {
+ --s;
+ }
+
+ curTex->achFormatHint[0] = s + 1 > e ? '\0' : (char)::tolower((unsigned char)s[1]);
+ curTex->achFormatHint[1] = s + 2 > e ? '\0' : (char)::tolower((unsigned char)s[2]);
+ curTex->achFormatHint[2] = s + 3 > e ? '\0' : (char)::tolower((unsigned char)s[3]);
+ curTex->achFormatHint[3] = '\0';
+
+ // tex->mHeight = 0;
+ curTex->mWidth = img->packedfile->size;
+ uint8_t *ch = new uint8_t[curTex->mWidth];
+
+ conv_data.db.reader->SetCurrentPos(static_cast<size_t>(img->packedfile->data->val));
+ conv_data.db.reader->CopyAndAdvance(ch, curTex->mWidth);
+
+ curTex->pcData = reinterpret_cast<aiTexel *>(ch);
+
+ LogInfo("Reading embedded texture, original file was ", img->name);
+ } else {
+ name = aiString(img->name);
+ }
+
+ aiTextureType texture_type = aiTextureType_UNKNOWN;
+ MTex::MapType map_type = tex->mapto;
+
+ if (map_type & MTex::MapType_COL)
+ texture_type = aiTextureType_DIFFUSE;
+ else if (map_type & MTex::MapType_NORM) {
+ if (tex->tex->imaflag & Tex::ImageFlags_NORMALMAP) {
+ texture_type = aiTextureType_NORMALS;
+ } else {
+ texture_type = aiTextureType_HEIGHT;
+ }
+ out->AddProperty(&tex->norfac, 1, AI_MATKEY_BUMPSCALING);
+ } else if (map_type & MTex::MapType_COLSPEC)
+ texture_type = aiTextureType_SPECULAR;
+ else if (map_type & MTex::MapType_COLMIR)
+ texture_type = aiTextureType_REFLECTION;
+ //else if (map_type & MTex::MapType_REF)
+ else if (map_type & MTex::MapType_SPEC)
+ texture_type = aiTextureType_SHININESS;
+ else if (map_type & MTex::MapType_EMIT)
+ texture_type = aiTextureType_EMISSIVE;
+ //else if (map_type & MTex::MapType_ALPHA)
+ //else if (map_type & MTex::MapType_HAR)
+ //else if (map_type & MTex::MapType_RAYMIRR)
+ //else if (map_type & MTex::MapType_TRANSLU)
+ else if (map_type & MTex::MapType_AMB)
+ texture_type = aiTextureType_AMBIENT;
+ else if (map_type & MTex::MapType_DISPLACE)
+ texture_type = aiTextureType_DISPLACEMENT;
+ //else if (map_type & MTex::MapType_WARP)
+
+ out->AddProperty(&name, AI_MATKEY_TEXTURE(texture_type,
+ conv_data.next_texture[texture_type]++));
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::AddSentinelTexture(aiMaterial *out, const Material *mat, const MTex *tex, ConversionData &conv_data) {
+ (void)mat;
+ (void)tex;
+ (void)conv_data;
+
+ aiString name;
+ name.length = ai_snprintf(name.data, MAXLEN, "Procedural,num=%i,type=%s", conv_data.sentinel_cnt++,
+ GetTextureTypeDisplayString(tex->tex->type));
+ out->AddProperty(&name, AI_MATKEY_TEXTURE_DIFFUSE(
+ conv_data.next_texture[aiTextureType_DIFFUSE]++));
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ResolveTexture(aiMaterial *out, const Material *mat, const MTex *tex, ConversionData &conv_data) {
+ const Tex *rtex = tex->tex.get();
+ if (!rtex || !rtex->type) {
+ return;
+ }
+
+ // We can't support most of the texture types because they're mostly procedural.
+ // These are substituted by a dummy texture.
+ const char *dispnam = "";
+ switch (rtex->type) {
+ // these are listed in blender's UI
+ case Tex::Type_CLOUDS:
+ case Tex::Type_WOOD:
+ case Tex::Type_MARBLE:
+ case Tex::Type_MAGIC:
+ case Tex::Type_BLEND:
+ case Tex::Type_STUCCI:
+ case Tex::Type_NOISE:
+ case Tex::Type_PLUGIN:
+ case Tex::Type_MUSGRAVE:
+ case Tex::Type_VORONOI:
+ case Tex::Type_DISTNOISE:
+ case Tex::Type_ENVMAP:
+
+ // these do no appear in the UI, why?
+ case Tex::Type_POINTDENSITY:
+ case Tex::Type_VOXELDATA:
+
+ LogWarn("Encountered a texture with an unsupported type: ", dispnam);
+ AddSentinelTexture(out, mat, tex, conv_data);
+ break;
+
+ case Tex::Type_IMAGE:
+ if (!rtex->ima) {
+ LogError("A texture claims to be an Image, but no image reference is given");
+ break;
+ }
+ ResolveImage(out, mat, tex, rtex->ima.get(), conv_data);
+ break;
+
+ default:
+ ai_assert(false);
+ };
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::BuildDefaultMaterial(Blender::ConversionData &conv_data) {
+ // add a default material if necessary
+ unsigned int index = static_cast<unsigned int>(-1);
+ for (aiMesh *mesh : conv_data.meshes.get()) {
+ if (mesh->mMaterialIndex == static_cast<unsigned int>(-1)) {
+
+ if (index == static_cast<unsigned int>(-1)) {
+ // Setup a default material.
+ std::shared_ptr<Material> p(new Material());
+ ai_assert(::strlen(AI_DEFAULT_MATERIAL_NAME) < sizeof(p->id.name) - 2);
+ strcpy(p->id.name + 2, AI_DEFAULT_MATERIAL_NAME);
+
+ // Note: MSVC11 does not zero-initialize Material here, although it should.
+ // Thus all relevant fields should be explicitly initialized. We cannot add
+ // a default constructor to Material since the DNA codegen does not support
+ // parsing it.
+ p->r = p->g = p->b = 0.6f;
+ p->specr = p->specg = p->specb = 0.6f;
+ p->ambr = p->ambg = p->ambb = 0.0f;
+ p->mirr = p->mirg = p->mirb = 0.0f;
+ p->emit = 0.f;
+ p->alpha = 0.f;
+ p->har = 0;
+
+ index = static_cast<unsigned int>(conv_data.materials_raw.size());
+ conv_data.materials_raw.push_back(p);
+ LogInfo("Adding default material");
+ }
+ mesh->mMaterialIndex = index;
+ }
+ }
+}
+
+void BlenderImporter::AddBlendParams(aiMaterial *result, const Material *source) {
+ aiColor3D diffuseColor(source->r, source->g, source->b);
+ result->AddProperty(&diffuseColor, 1, "$mat.blend.diffuse.color", 0, 0);
+
+ float diffuseIntensity = source->ref;
+ result->AddProperty(&diffuseIntensity, 1, "$mat.blend.diffuse.intensity", 0, 0);
+
+ int diffuseShader = source->diff_shader;
+ result->AddProperty(&diffuseShader, 1, "$mat.blend.diffuse.shader", 0, 0);
+
+ int diffuseRamp = 0;
+ result->AddProperty(&diffuseRamp, 1, "$mat.blend.diffuse.ramp", 0, 0);
+
+ aiColor3D specularColor(source->specr, source->specg, source->specb);
+ result->AddProperty(&specularColor, 1, "$mat.blend.specular.color", 0, 0);
+
+ float specularIntensity = source->spec;
+ result->AddProperty(&specularIntensity, 1, "$mat.blend.specular.intensity", 0, 0);
+
+ int specularShader = source->spec_shader;
+ result->AddProperty(&specularShader, 1, "$mat.blend.specular.shader", 0, 0);
+
+ int specularRamp = 0;
+ result->AddProperty(&specularRamp, 1, "$mat.blend.specular.ramp", 0, 0);
+
+ int specularHardness = source->har;
+ result->AddProperty(&specularHardness, 1, "$mat.blend.specular.hardness", 0, 0);
+
+ int transparencyUse = source->mode & MA_TRANSPARENCY ? 1 : 0;
+ result->AddProperty(&transparencyUse, 1, "$mat.blend.transparency.use", 0, 0);
+
+ int transparencyMethod = source->mode & MA_RAYTRANSP ? 2 : (source->mode & MA_ZTRANSP ? 1 : 0);
+ result->AddProperty(&transparencyMethod, 1, "$mat.blend.transparency.method", 0, 0);
+
+ float transparencyAlpha = source->alpha;
+ result->AddProperty(&transparencyAlpha, 1, "$mat.blend.transparency.alpha", 0, 0);
+
+ float transparencySpecular = source->spectra;
+ result->AddProperty(&transparencySpecular, 1, "$mat.blend.transparency.specular", 0, 0);
+
+ float transparencyFresnel = source->fresnel_tra;
+ result->AddProperty(&transparencyFresnel, 1, "$mat.blend.transparency.fresnel", 0, 0);
+
+ float transparencyBlend = source->fresnel_tra_i;
+ result->AddProperty(&transparencyBlend, 1, "$mat.blend.transparency.blend", 0, 0);
+
+ float transparencyIor = source->ang;
+ result->AddProperty(&transparencyIor, 1, "$mat.blend.transparency.ior", 0, 0);
+
+ float transparencyFilter = source->filter;
+ result->AddProperty(&transparencyFilter, 1, "$mat.blend.transparency.filter", 0, 0);
+
+ float transparencyFalloff = source->tx_falloff;
+ result->AddProperty(&transparencyFalloff, 1, "$mat.blend.transparency.falloff", 0, 0);
+
+ float transparencyLimit = source->tx_limit;
+ result->AddProperty(&transparencyLimit, 1, "$mat.blend.transparency.limit", 0, 0);
+
+ int transparencyDepth = source->ray_depth_tra;
+ result->AddProperty(&transparencyDepth, 1, "$mat.blend.transparency.depth", 0, 0);
+
+ float transparencyGlossAmount = source->gloss_tra;
+ result->AddProperty(&transparencyGlossAmount, 1, "$mat.blend.transparency.glossAmount", 0, 0);
+
+ float transparencyGlossThreshold = source->adapt_thresh_tra;
+ result->AddProperty(&transparencyGlossThreshold, 1, "$mat.blend.transparency.glossThreshold", 0, 0);
+
+ int transparencyGlossSamples = source->samp_gloss_tra;
+ result->AddProperty(&transparencyGlossSamples, 1, "$mat.blend.transparency.glossSamples", 0, 0);
+
+ int mirrorUse = source->mode & MA_RAYMIRROR ? 1 : 0;
+ result->AddProperty(&mirrorUse, 1, "$mat.blend.mirror.use", 0, 0);
+
+ float mirrorReflectivity = source->ray_mirror;
+ result->AddProperty(&mirrorReflectivity, 1, "$mat.blend.mirror.reflectivity", 0, 0);
+
+ aiColor3D mirrorColor(source->mirr, source->mirg, source->mirb);
+ result->AddProperty(&mirrorColor, 1, "$mat.blend.mirror.color", 0, 0);
+
+ float mirrorFresnel = source->fresnel_mir;
+ result->AddProperty(&mirrorFresnel, 1, "$mat.blend.mirror.fresnel", 0, 0);
+
+ float mirrorBlend = source->fresnel_mir_i;
+ result->AddProperty(&mirrorBlend, 1, "$mat.blend.mirror.blend", 0, 0);
+
+ int mirrorDepth = source->ray_depth;
+ result->AddProperty(&mirrorDepth, 1, "$mat.blend.mirror.depth", 0, 0);
+
+ float mirrorMaxDist = source->dist_mir;
+ result->AddProperty(&mirrorMaxDist, 1, "$mat.blend.mirror.maxDist", 0, 0);
+
+ int mirrorFadeTo = source->fadeto_mir;
+ result->AddProperty(&mirrorFadeTo, 1, "$mat.blend.mirror.fadeTo", 0, 0);
+
+ float mirrorGlossAmount = source->gloss_mir;
+ result->AddProperty(&mirrorGlossAmount, 1, "$mat.blend.mirror.glossAmount", 0, 0);
+
+ float mirrorGlossThreshold = source->adapt_thresh_mir;
+ result->AddProperty(&mirrorGlossThreshold, 1, "$mat.blend.mirror.glossThreshold", 0, 0);
+
+ int mirrorGlossSamples = source->samp_gloss_mir;
+ result->AddProperty(&mirrorGlossSamples, 1, "$mat.blend.mirror.glossSamples", 0, 0);
+
+ float mirrorGlossAnisotropic = source->aniso_gloss_mir;
+ result->AddProperty(&mirrorGlossAnisotropic, 1, "$mat.blend.mirror.glossAnisotropic", 0, 0);
+}
+
+void BlenderImporter::BuildMaterials(ConversionData &conv_data) {
+ conv_data.materials->reserve(conv_data.materials_raw.size());
+
+ BuildDefaultMaterial(conv_data);
+
+ for (const std::shared_ptr<Material> &mat : conv_data.materials_raw) {
+
+ // reset per material global counters
+ for (size_t i = 0; i < sizeof(conv_data.next_texture) / sizeof(conv_data.next_texture[0]); ++i) {
+ conv_data.next_texture[i] = 0;
+ }
+
+ aiMaterial *mout = new aiMaterial();
+ conv_data.materials->push_back(mout);
+ // For any new material field handled here, the default material above must be updated with an appropriate default value.
+
+ // set material name
+ aiString name = aiString(mat->id.name + 2); // skip over the name prefix 'MA'
+ mout->AddProperty(&name, AI_MATKEY_NAME);
+
+ // basic material colors
+ aiColor3D col(mat->r, mat->g, mat->b);
+ if (mat->r || mat->g || mat->b) {
+
+ // Usually, zero diffuse color means no diffuse color at all in the equation.
+ // So we omit this member to express this intent.
+ mout->AddProperty(&col, 1, AI_MATKEY_COLOR_DIFFUSE);
+
+ if (mat->emit) {
+ aiColor3D emit_col(mat->emit * mat->r, mat->emit * mat->g, mat->emit * mat->b);
+ mout->AddProperty(&emit_col, 1, AI_MATKEY_COLOR_EMISSIVE);
+ }
+ }
+
+ col = aiColor3D(mat->specr, mat->specg, mat->specb);
+ mout->AddProperty(&col, 1, AI_MATKEY_COLOR_SPECULAR);
+
+ // is hardness/shininess set?
+ if (mat->har) {
+ const float har = mat->har;
+ mout->AddProperty(&har, 1, AI_MATKEY_SHININESS);
+ }
+
+ col = aiColor3D(mat->ambr, mat->ambg, mat->ambb);
+ mout->AddProperty(&col, 1, AI_MATKEY_COLOR_AMBIENT);
+
+ // is mirror enabled?
+ if (mat->mode & MA_RAYMIRROR) {
+ const float ray_mirror = mat->ray_mirror;
+ mout->AddProperty(&ray_mirror, 1, AI_MATKEY_REFLECTIVITY);
+ }
+
+ col = aiColor3D(mat->mirr, mat->mirg, mat->mirb);
+ mout->AddProperty(&col, 1, AI_MATKEY_COLOR_REFLECTIVE);
+
+ for (size_t i = 0; i < sizeof(mat->mtex) / sizeof(mat->mtex[0]); ++i) {
+ if (!mat->mtex[i]) {
+ continue;
+ }
+
+ ResolveTexture(mout, mat.get(), mat->mtex[i].get(), conv_data);
+ }
+
+ AddBlendParams(mout, mat.get());
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::CheckActualType(const ElemBase *dt, const char *check) {
+ ai_assert(dt);
+ if (strcmp(dt->dna_type, check)) {
+ ThrowException("Expected object at ", std::hex, dt, " to be of type `", check,
+ "`, but it claims to be a `", dt->dna_type, "`instead");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::NotSupportedObjectType(const Object *obj, const char *type) {
+ LogWarn("Object `", obj->id.name, "` - type is unsupported: `", type, "`, skipping");
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ConvertMesh(const Scene & /*in*/, const Object * /*obj*/, const Mesh *mesh,
+ ConversionData &conv_data, TempArray<std::vector, aiMesh> &temp) {
+ // TODO: Resolve various problems with BMesh triangulation before re-enabling.
+ // See issues #400, #373, #318 #315 and #132.
+#if defined(TODO_FIX_BMESH_CONVERSION)
+ BlenderBMeshConverter BMeshConverter(mesh);
+ if (BMeshConverter.ContainsBMesh()) {
+ mesh = BMeshConverter.TriangulateBMesh();
+ }
+#endif
+
+ typedef std::pair<const int, size_t> MyPair;
+ if ((!mesh->totface && !mesh->totloop) || !mesh->totvert) {
+ return;
+ }
+
+ // some sanity checks
+ if (static_cast<size_t>(mesh->totface) > mesh->mface.size()) {
+ ThrowException("Number of faces is larger than the corresponding array");
+ }
+
+ if (static_cast<size_t>(mesh->totvert) > mesh->mvert.size()) {
+ ThrowException("Number of vertices is larger than the corresponding array");
+ }
+
+ if (static_cast<size_t>(mesh->totloop) > mesh->mloop.size()) {
+ ThrowException("Number of vertices is larger than the corresponding array");
+ }
+
+ // collect per-submesh numbers
+ std::map<int, size_t> per_mat;
+ std::map<int, size_t> per_mat_verts;
+ for (int i = 0; i < mesh->totface; ++i) {
+
+ const MFace &mf = mesh->mface[i];
+ per_mat[mf.mat_nr]++;
+ per_mat_verts[mf.mat_nr] += mf.v4 ? 4 : 3;
+ }
+
+ for (int i = 0; i < mesh->totpoly; ++i) {
+ const MPoly &mp = mesh->mpoly[i];
+ per_mat[mp.mat_nr]++;
+ per_mat_verts[mp.mat_nr] += mp.totloop;
+ }
+
+ // ... and allocate the corresponding meshes
+ const size_t old = temp->size();
+ temp->reserve(temp->size() + per_mat.size());
+
+ std::map<size_t, size_t> mat_num_to_mesh_idx;
+ for (MyPair &it : per_mat) {
+
+ mat_num_to_mesh_idx[it.first] = temp->size();
+ temp->push_back(new aiMesh());
+
+ aiMesh *out = temp->back();
+ out->mVertices = new aiVector3D[per_mat_verts[it.first]];
+ out->mNormals = new aiVector3D[per_mat_verts[it.first]];
+
+ //out->mNumFaces = 0
+ //out->mNumVertices = 0
+ out->mFaces = new aiFace[it.second]();
+
+ // all sub-meshes created from this mesh are named equally. this allows
+ // curious users to recover the original adjacency.
+ out->mName = aiString(mesh->id.name + 2);
+ // skip over the name prefix 'ME'
+
+ // resolve the material reference and add this material to the set of
+ // output materials. The (temporary) material index is the index
+ // of the material entry within the list of resolved materials.
+ if (mesh->mat) {
+
+ if (static_cast<size_t>(it.first) >= mesh->mat.size()) {
+ ThrowException("Material index is out of range");
+ }
+
+ std::shared_ptr<Material> mat = mesh->mat[it.first];
+ const std::deque<std::shared_ptr<Material>>::iterator has = std::find(
+ conv_data.materials_raw.begin(),
+ conv_data.materials_raw.end(), mat);
+
+ if (has != conv_data.materials_raw.end()) {
+ out->mMaterialIndex = static_cast<unsigned int>(std::distance(conv_data.materials_raw.begin(), has));
+ } else {
+ out->mMaterialIndex = static_cast<unsigned int>(conv_data.materials_raw.size());
+ conv_data.materials_raw.push_back(mat);
+ }
+ } else
+ out->mMaterialIndex = static_cast<unsigned int>(-1);
+ }
+
+ for (int i = 0; i < mesh->totface; ++i) {
+
+ const MFace &mf = mesh->mface[i];
+
+ aiMesh *const out = temp[mat_num_to_mesh_idx[mf.mat_nr]];
+ aiFace &f = out->mFaces[out->mNumFaces++];
+
+ f.mIndices = new unsigned int[f.mNumIndices = mf.v4 ? 4 : 3];
+ aiVector3D *vo = out->mVertices + out->mNumVertices;
+ aiVector3D *vn = out->mNormals + out->mNumVertices;
+
+ // XXX we can't fold this easily, because we are restricted
+ // to the member names from the BLEND file (v1,v2,v3,v4)
+ // which are assigned by the genblenddna.py script and
+ // cannot be changed without breaking the entire
+ // import process.
+
+ if (mf.v1 >= mesh->totvert) {
+ ThrowException("Vertex index v1 out of range");
+ }
+ const MVert *v = &mesh->mvert[mf.v1];
+ vo->x = v->co[0];
+ vo->y = v->co[1];
+ vo->z = v->co[2];
+ vn->x = v->no[0];
+ vn->y = v->no[1];
+ vn->z = v->no[2];
+ f.mIndices[0] = out->mNumVertices++;
+ ++vo;
+ ++vn;
+
+ // if (f.mNumIndices >= 2) {
+ if (mf.v2 >= mesh->totvert) {
+ ThrowException("Vertex index v2 out of range");
+ }
+ v = &mesh->mvert[mf.v2];
+ vo->x = v->co[0];
+ vo->y = v->co[1];
+ vo->z = v->co[2];
+ vn->x = v->no[0];
+ vn->y = v->no[1];
+ vn->z = v->no[2];
+ f.mIndices[1] = out->mNumVertices++;
+ ++vo;
+ ++vn;
+
+ if (mf.v3 >= mesh->totvert) {
+ ThrowException("Vertex index v3 out of range");
+ }
+ // if (f.mNumIndices >= 3) {
+ v = &mesh->mvert[mf.v3];
+ vo->x = v->co[0];
+ vo->y = v->co[1];
+ vo->z = v->co[2];
+ vn->x = v->no[0];
+ vn->y = v->no[1];
+ vn->z = v->no[2];
+ f.mIndices[2] = out->mNumVertices++;
+ ++vo;
+ ++vn;
+
+ if (mf.v4 >= mesh->totvert) {
+ ThrowException("Vertex index v4 out of range");
+ }
+ // if (f.mNumIndices >= 4) {
+ if (mf.v4) {
+ v = &mesh->mvert[mf.v4];
+ vo->x = v->co[0];
+ vo->y = v->co[1];
+ vo->z = v->co[2];
+ vn->x = v->no[0];
+ vn->y = v->no[1];
+ vn->z = v->no[2];
+ f.mIndices[3] = out->mNumVertices++;
+ ++vo;
+ ++vn;
+
+ out->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+ } else
+ out->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+
+ // }
+ // }
+ // }
+ }
+
+ for (int i = 0; i < mesh->totpoly; ++i) {
+
+ const MPoly &mf = mesh->mpoly[i];
+
+ aiMesh *const out = temp[mat_num_to_mesh_idx[mf.mat_nr]];
+ aiFace &f = out->mFaces[out->mNumFaces++];
+
+ f.mIndices = new unsigned int[f.mNumIndices = mf.totloop];
+ aiVector3D *vo = out->mVertices + out->mNumVertices;
+ aiVector3D *vn = out->mNormals + out->mNumVertices;
+
+ // XXX we can't fold this easily, because we are restricted
+ // to the member names from the BLEND file (v1,v2,v3,v4)
+ // which are assigned by the genblenddna.py script and
+ // cannot be changed without breaking the entire
+ // import process.
+ for (int j = 0; j < mf.totloop; ++j) {
+ const MLoop &loop = mesh->mloop[mf.loopstart + j];
+
+ if (loop.v >= mesh->totvert) {
+ ThrowException("Vertex index out of range");
+ }
+
+ const MVert &v = mesh->mvert[loop.v];
+
+ vo->x = v.co[0];
+ vo->y = v.co[1];
+ vo->z = v.co[2];
+ vn->x = v.no[0];
+ vn->y = v.no[1];
+ vn->z = v.no[2];
+ f.mIndices[j] = out->mNumVertices++;
+
+ ++vo;
+ ++vn;
+ }
+ if (mf.totloop == 3) {
+ out->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+ } else {
+ out->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+ }
+ }
+
+ // TODO should we create the TextureUVMapping map in Convert<Material> to prevent redundant processing?
+
+ // create texture <-> uvname mapping for all materials
+ // key is texture number, value is data *
+ typedef std::map<uint32_t, const MLoopUV *> TextureUVMapping;
+ // key is material number, value is the TextureUVMapping for the material
+ typedef std::map<uint32_t, TextureUVMapping> MaterialTextureUVMappings;
+ MaterialTextureUVMappings matTexUvMappings;
+ const uint32_t maxMat = static_cast<const uint32_t>(mesh->mat.size());
+ for (uint32_t m = 0; m < maxMat; ++m) {
+ // get material by index
+ const std::shared_ptr<Material> pMat = mesh->mat[m];
+ TextureUVMapping texuv;
+ const uint32_t maxTex = sizeof(pMat->mtex) / sizeof(pMat->mtex[0]);
+ for (uint32_t t = 0; t < maxTex; ++t) {
+ if (pMat->mtex[t] && pMat->mtex[t]->uvname[0]) {
+ // get the CustomData layer for given uvname and correct type
+ const ElemBase *pLoop = getCustomDataLayerData(mesh->ldata, CD_MLOOPUV, pMat->mtex[t]->uvname);
+ if (pLoop) {
+ texuv.insert(std::make_pair(t, dynamic_cast<const MLoopUV *>(pLoop)));
+ }
+ }
+ }
+ if (texuv.size()) {
+ matTexUvMappings.insert(std::make_pair(m, texuv));
+ }
+ }
+
+ // collect texture coordinates, they're stored in a separate per-face buffer
+ if (mesh->mtface || mesh->mloopuv) {
+ if (mesh->totface > static_cast<int>(mesh->mtface.size())) {
+ ThrowException("Number of UV faces is larger than the corresponding UV face array (#1)");
+ }
+ for (std::vector<aiMesh *>::iterator it = temp->begin() + old; it != temp->end(); ++it) {
+ ai_assert(0 != (*it)->mNumVertices);
+ ai_assert(0 != (*it)->mNumFaces);
+ const auto itMatTexUvMapping = matTexUvMappings.find((*it)->mMaterialIndex);
+ if (itMatTexUvMapping == matTexUvMappings.end()) {
+ // default behaviour like before
+ (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices];
+ } else {
+ // create texture coords for every mapped tex
+ for (uint32_t i = 0; i < itMatTexUvMapping->second.size(); ++i) {
+ (*it)->mTextureCoords[i] = new aiVector3D[(*it)->mNumVertices];
+ }
+ }
+ (*it)->mNumFaces = (*it)->mNumVertices = 0;
+ }
+
+ for (int i = 0; i < mesh->totface; ++i) {
+ const MTFace *v = &mesh->mtface[i];
+
+ aiMesh *const out = temp[mat_num_to_mesh_idx[mesh->mface[i].mat_nr]];
+ const aiFace &f = out->mFaces[out->mNumFaces++];
+
+ aiVector3D *vo = &out->mTextureCoords[0][out->mNumVertices];
+ for (unsigned int j = 0; j < f.mNumIndices; ++j, ++vo, ++out->mNumVertices) {
+ vo->x = v->uv[j][0];
+ vo->y = v->uv[j][1];
+ }
+ }
+
+ for (int i = 0; i < mesh->totpoly; ++i) {
+ const MPoly &v = mesh->mpoly[i];
+ aiMesh *const out = temp[mat_num_to_mesh_idx[v.mat_nr]];
+ const aiFace &f = out->mFaces[out->mNumFaces++];
+
+ const auto itMatTexUvMapping = matTexUvMappings.find(v.mat_nr);
+ if (itMatTexUvMapping == matTexUvMappings.end()) {
+ // old behavior
+ aiVector3D *vo = &out->mTextureCoords[0][out->mNumVertices];
+ for (unsigned int j = 0; j < f.mNumIndices; ++j, ++vo, ++out->mNumVertices) {
+ const MLoopUV &uv = mesh->mloopuv[v.loopstart + j];
+ vo->x = uv.uv[0];
+ vo->y = uv.uv[1];
+ }
+ } else {
+ // create textureCoords for every mapped tex
+ for (uint32_t m = 0; m < itMatTexUvMapping->second.size(); ++m) {
+ const MLoopUV *tm = itMatTexUvMapping->second[m];
+ aiVector3D *vo = &out->mTextureCoords[m][out->mNumVertices];
+ uint32_t j = 0;
+ for (; j < f.mNumIndices; ++j, ++vo) {
+ const MLoopUV &uv = tm[v.loopstart + j];
+ vo->x = uv.uv[0];
+ vo->y = uv.uv[1];
+ }
+ // only update written mNumVertices in last loop
+ // TODO why must the numVertices be incremented here?
+ if (m == itMatTexUvMapping->second.size() - 1) {
+ out->mNumVertices += j;
+ }
+ }
+ }
+ }
+ }
+
+ // collect texture coordinates, old-style (marked as deprecated in current blender sources)
+ if (mesh->tface) {
+ if (mesh->totface > static_cast<int>(mesh->tface.size())) {
+ ThrowException("Number of faces is larger than the corresponding UV face array (#2)");
+ }
+ for (std::vector<aiMesh *>::iterator it = temp->begin() + old; it != temp->end(); ++it) {
+ ai_assert(0 != (*it)->mNumVertices);
+ ai_assert(0 != (*it)->mNumFaces);
+
+ (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices];
+ (*it)->mNumFaces = (*it)->mNumVertices = 0;
+ }
+
+ for (int i = 0; i < mesh->totface; ++i) {
+ const TFace *v = &mesh->tface[i];
+
+ aiMesh *const out = temp[mat_num_to_mesh_idx[mesh->mface[i].mat_nr]];
+ const aiFace &f = out->mFaces[out->mNumFaces++];
+
+ aiVector3D *vo = &out->mTextureCoords[0][out->mNumVertices];
+ for (unsigned int j = 0; j < f.mNumIndices; ++j, ++vo, ++out->mNumVertices) {
+ vo->x = v->uv[j][0];
+ vo->y = v->uv[j][1];
+ }
+ }
+ }
+
+ // collect vertex colors, stored separately as well
+ if (mesh->mcol || mesh->mloopcol) {
+ if (mesh->totface > static_cast<int>((mesh->mcol.size() / 4))) {
+ ThrowException("Number of faces is larger than the corresponding color face array");
+ }
+ for (std::vector<aiMesh *>::iterator it = temp->begin() + old; it != temp->end(); ++it) {
+ ai_assert(0 != (*it)->mNumVertices);
+ ai_assert(0 != (*it)->mNumFaces);
+
+ (*it)->mColors[0] = new aiColor4D[(*it)->mNumVertices];
+ (*it)->mNumFaces = (*it)->mNumVertices = 0;
+ }
+
+ for (int i = 0; i < mesh->totface; ++i) {
+
+ aiMesh *const out = temp[mat_num_to_mesh_idx[mesh->mface[i].mat_nr]];
+ const aiFace &f = out->mFaces[out->mNumFaces++];
+
+ aiColor4D *vo = &out->mColors[0][out->mNumVertices];
+ for (unsigned int n = 0; n < f.mNumIndices; ++n, ++vo, ++out->mNumVertices) {
+ const MCol *col = &mesh->mcol[(i << 2) + n];
+
+ vo->r = col->r;
+ vo->g = col->g;
+ vo->b = col->b;
+ vo->a = col->a;
+ }
+ for (unsigned int n = f.mNumIndices; n < 4; ++n)
+ ;
+ }
+
+ for (int i = 0; i < mesh->totpoly; ++i) {
+ const MPoly &v = mesh->mpoly[i];
+ aiMesh *const out = temp[mat_num_to_mesh_idx[v.mat_nr]];
+ const aiFace &f = out->mFaces[out->mNumFaces++];
+
+ aiColor4D *vo = &out->mColors[0][out->mNumVertices];
+ const ai_real scaleZeroToOne = 1.f / 255.f;
+ for (unsigned int j = 0; j < f.mNumIndices; ++j, ++vo, ++out->mNumVertices) {
+ const MLoopCol &col = mesh->mloopcol[v.loopstart + j];
+ vo->r = ai_real(col.r) * scaleZeroToOne;
+ vo->g = ai_real(col.g) * scaleZeroToOne;
+ vo->b = ai_real(col.b) * scaleZeroToOne;
+ vo->a = ai_real(col.a) * scaleZeroToOne;
+ }
+ }
+ }
+
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiCamera *BlenderImporter::ConvertCamera(const Scene & /*in*/, const Object *obj, const Camera *cam, ConversionData & /*conv_data*/) {
+ std::unique_ptr<aiCamera> out(new aiCamera());
+ out->mName = obj->id.name + 2;
+ out->mPosition = aiVector3D(0.f, 0.f, 0.f);
+ out->mUp = aiVector3D(0.f, 1.f, 0.f);
+ out->mLookAt = aiVector3D(0.f, 0.f, -1.f);
+ if (cam->sensor_x && cam->lens) {
+ out->mHorizontalFOV = 2.f * std::atan2(cam->sensor_x, 2.f * cam->lens);
+ }
+ out->mClipPlaneNear = cam->clipsta;
+ out->mClipPlaneFar = cam->clipend;
+
+ return out.release();
+}
+
+// ------------------------------------------------------------------------------------------------
+aiLight *BlenderImporter::ConvertLight(const Scene & /*in*/, const Object *obj, const Lamp *lamp, ConversionData & /*conv_data*/) {
+ std::unique_ptr<aiLight> out(new aiLight());
+ out->mName = obj->id.name + 2;
+
+ switch (lamp->type) {
+ case Lamp::Type_Local:
+ out->mType = aiLightSource_POINT;
+ break;
+ case Lamp::Type_Spot:
+ out->mType = aiLightSource_SPOT;
+
+ // blender orients directional lights as facing toward -z
+ out->mDirection = aiVector3D(0.f, 0.f, -1.f);
+ out->mUp = aiVector3D(0.f, 1.f, 0.f);
+
+ out->mAngleInnerCone = lamp->spotsize * (1.0f - lamp->spotblend);
+ out->mAngleOuterCone = lamp->spotsize;
+ break;
+ case Lamp::Type_Sun:
+ out->mType = aiLightSource_DIRECTIONAL;
+
+ // blender orients directional lights as facing toward -z
+ out->mDirection = aiVector3D(0.f, 0.f, -1.f);
+ out->mUp = aiVector3D(0.f, 1.f, 0.f);
+ break;
+
+ case Lamp::Type_Area:
+ out->mType = aiLightSource_AREA;
+
+ if (lamp->area_shape == 0) {
+ out->mSize = aiVector2D(lamp->area_size, lamp->area_size);
+ } else {
+ out->mSize = aiVector2D(lamp->area_size, lamp->area_sizey);
+ }
+
+ // blender orients directional lights as facing toward -z
+ out->mDirection = aiVector3D(0.f, 0.f, -1.f);
+ out->mUp = aiVector3D(0.f, 1.f, 0.f);
+ break;
+
+ default:
+ break;
+ }
+
+ out->mColorAmbient = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
+ out->mColorSpecular = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
+ out->mColorDiffuse = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
+
+ // If default values are supplied, compute the coefficients from light's max distance
+ // Read this: https://imdoingitwrong.wordpress.com/2011/01/31/light-attenuation/
+ //
+ if (lamp->constant_coefficient == 1.0f && lamp->linear_coefficient == 0.0f && lamp->quadratic_coefficient == 0.0f && lamp->dist > 0.0f) {
+ out->mAttenuationConstant = 1.0f;
+ out->mAttenuationLinear = 2.0f / lamp->dist;
+ out->mAttenuationQuadratic = 1.0f / (lamp->dist * lamp->dist);
+ } else {
+ out->mAttenuationConstant = lamp->constant_coefficient;
+ out->mAttenuationLinear = lamp->linear_coefficient;
+ out->mAttenuationQuadratic = lamp->quadratic_coefficient;
+ }
+
+ return out.release();
+}
+
+// ------------------------------------------------------------------------------------------------
+aiNode *BlenderImporter::ConvertNode(const Scene &in, const Object *obj, ConversionData &conv_data, const aiMatrix4x4 &parentTransform) {
+ std::deque<const Object *> children;
+ for (ObjectSet::iterator it = conv_data.objects.begin(); it != conv_data.objects.end();) {
+ const Object *object = *it;
+ if (object->parent == obj) {
+ children.push_back(object);
+
+ conv_data.objects.erase(it++);
+ continue;
+ }
+ ++it;
+ }
+
+ std::unique_ptr<aiNode> node(new aiNode(obj->id.name + 2)); // skip over the name prefix 'OB'
+ if (obj->data) {
+ switch (obj->type) {
+ case Object ::Type_EMPTY:
+ break; // do nothing
+
+ // supported object types
+ case Object ::Type_MESH: {
+ const size_t old = conv_data.meshes->size();
+
+ CheckActualType(obj->data.get(), "Mesh");
+ ConvertMesh(in, obj, static_cast<const Mesh *>(obj->data.get()), conv_data, conv_data.meshes);
+
+ if (conv_data.meshes->size() > old) {
+ node->mMeshes = new unsigned int[node->mNumMeshes = static_cast<unsigned int>(conv_data.meshes->size() - old)];
+ for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
+ node->mMeshes[i] = static_cast<unsigned int>(i + old);
+ }
+ }
+ } break;
+ case Object ::Type_LAMP: {
+ CheckActualType(obj->data.get(), "Lamp");
+ aiLight *mesh = ConvertLight(in, obj, static_cast<const Lamp *>(obj->data.get()), conv_data);
+
+ if (mesh) {
+ conv_data.lights->push_back(mesh);
+ }
+ } break;
+ case Object ::Type_CAMERA: {
+ CheckActualType(obj->data.get(), "Camera");
+ aiCamera *mesh = ConvertCamera(in, obj, static_cast<const Camera *>(obj->data.get()), conv_data);
+
+ if (mesh) {
+ conv_data.cameras->push_back(mesh);
+ }
+ } break;
+
+ // unsupported object types / log, but do not break
+ case Object ::Type_CURVE:
+ NotSupportedObjectType(obj, "Curve");
+ break;
+ case Object ::Type_SURF:
+ NotSupportedObjectType(obj, "Surface");
+ break;
+ case Object ::Type_FONT:
+ NotSupportedObjectType(obj, "Font");
+ break;
+ case Object ::Type_MBALL:
+ NotSupportedObjectType(obj, "MetaBall");
+ break;
+ case Object ::Type_WAVE:
+ NotSupportedObjectType(obj, "Wave");
+ break;
+ case Object ::Type_LATTICE:
+ NotSupportedObjectType(obj, "Lattice");
+ break;
+
+ // invalid or unknown type
+ default:
+ break;
+ }
+ }
+
+ for (unsigned int x = 0; x < 4; ++x) {
+ for (unsigned int y = 0; y < 4; ++y) {
+ node->mTransformation[y][x] = obj->obmat[x][y];
+ }
+ }
+
+ aiMatrix4x4 m = parentTransform;
+ m = m.Inverse();
+
+ node->mTransformation = m * node->mTransformation;
+
+ if (children.size()) {
+ node->mNumChildren = static_cast<unsigned int>(children.size());
+ aiNode **nd = node->mChildren = new aiNode *[node->mNumChildren]();
+ for (const Object *nobj : children) {
+ *nd = ConvertNode(in, nobj, conv_data, node->mTransformation * parentTransform);
+ (*nd++)->mParent = node.get();
+ }
+ }
+
+ // apply modifiers
+ modifier_cache->ApplyModifiers(*node, conv_data, in, *obj);
+
+ return node.release();
+}
+
+#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderLoader.h b/libs/assimp/code/AssetLib/Blender/BlenderLoader.h
new file mode 100644
index 0000000..0299ae3
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderLoader.h
@@ -0,0 +1,198 @@
+/*
+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 BlenderLoader.h
+ * @brief Declaration of the Blender 3D (*.blend) importer class.
+ */
+#pragma once
+#ifndef INCLUDED_AI_BLEND_LOADER_H
+#define INCLUDED_AI_BLEND_LOADER_H
+
+#include <assimp/BaseImporter.h>
+#include <assimp/LogAux.h>
+#include <memory>
+
+struct aiNode;
+struct aiMesh;
+struct aiLight;
+struct aiCamera;
+struct aiMaterial;
+
+namespace Assimp {
+
+// TinyFormatter.h
+namespace Formatter {
+
+template <typename T, typename TR, typename A>
+class basic_formatter;
+
+typedef class basic_formatter<char, std::char_traits<char>, std::allocator<char>> format;
+
+} // namespace Formatter
+
+// BlenderDNA.h
+namespace Blender {
+class FileDatabase;
+struct ElemBase;
+} // namespace Blender
+
+// BlenderScene.h
+namespace Blender {
+struct Scene;
+struct Object;
+struct Collection;
+struct Mesh;
+struct Camera;
+struct Lamp;
+struct MTex;
+struct Image;
+struct Material;
+} // namespace Blender
+
+// BlenderIntermediate.h
+namespace Blender {
+struct ConversionData;
+template <template <typename, typename> class TCLASS, typename T>
+struct TempArray;
+} // namespace Blender
+
+// BlenderModifier.h
+namespace Blender {
+class BlenderModifierShowcase;
+class BlenderModifier;
+} // namespace Blender
+
+// -------------------------------------------------------------------------------------------
+/** Load blenders official binary format. The actual file structure (the `DNA` how they
+ * call it is outsourced to BlenderDNA.cpp/BlenderDNA.h. This class only performs the
+ * conversion from intermediate format to aiScene. */
+// -------------------------------------------------------------------------------------------
+class BlenderImporter : public BaseImporter, public LogFunctions<BlenderImporter> {
+public:
+ BlenderImporter();
+ ~BlenderImporter() override;
+ bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
+
+protected:
+ const aiImporterDesc *GetInfo() const override;
+ void SetupProperties(const Importer *pImp) override;
+ void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
+ void ParseBlendFile(Blender::FileDatabase &out, std::shared_ptr<IOStream> stream);
+ void ExtractScene(Blender::Scene &out, const Blender::FileDatabase &file);
+ void ParseSubCollection(const Blender::Scene &in, aiNode *root, std::shared_ptr<Blender::Collection> collection, Blender::ConversionData &conv_data);
+ void ConvertBlendFile(aiScene *out, const Blender::Scene &in, const Blender::FileDatabase &file);
+
+private:
+ aiNode *ConvertNode(const Blender::Scene &in,
+ const Blender::Object *obj,
+ Blender::ConversionData &conv_info,
+ const aiMatrix4x4 &parentTransform);
+
+ // --------------------
+ void ConvertMesh(const Blender::Scene &in,
+ const Blender::Object *obj,
+ const Blender::Mesh *mesh,
+ Blender::ConversionData &conv_data,
+ Blender::TempArray<std::vector, aiMesh> &temp);
+
+ // --------------------
+ aiLight *ConvertLight(const Blender::Scene &in,
+ const Blender::Object *obj,
+ const Blender::Lamp *mesh,
+ Blender::ConversionData &conv_data);
+
+ // --------------------
+ aiCamera *ConvertCamera(const Blender::Scene &in,
+ const Blender::Object *obj,
+ const Blender::Camera *mesh,
+ Blender::ConversionData &conv_data);
+
+ // --------------------
+ void BuildDefaultMaterial(
+ Blender::ConversionData &conv_data);
+
+ // --------------------
+ void AddBlendParams(
+ aiMaterial *result,
+ const Blender::Material *source);
+
+ // --------------------
+ void BuildMaterials(
+ Blender::ConversionData &conv_data);
+
+ // --------------------
+ void ResolveTexture(
+ aiMaterial *out,
+ const Blender::Material *mat,
+ const Blender::MTex *tex,
+ Blender::ConversionData &conv_data);
+
+ // --------------------
+ void ResolveImage(
+ aiMaterial *out,
+ const Blender::Material *mat,
+ const Blender::MTex *tex,
+ const Blender::Image *img,
+ Blender::ConversionData &conv_data);
+
+ // --------------------
+ void AddSentinelTexture(
+ aiMaterial *out,
+ const Blender::Material *mat,
+ const Blender::MTex *tex,
+ Blender::ConversionData &conv_data);
+
+private: // static stuff, mostly logging and error reporting.
+ // --------------------
+ static void CheckActualType(const Blender::ElemBase *dt,
+ const char *check);
+
+ // --------------------
+ static void NotSupportedObjectType(const Blender::Object *obj,
+ const char *type);
+
+private:
+ Blender::BlenderModifierShowcase *modifier_cache;
+
+}; // !class BlenderImporter
+
+} // end of namespace Assimp
+#endif // AI_UNREALIMPORTER_H_INC
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderModifier.cpp b/libs/assimp/code/AssetLib/Blender/BlenderModifier.cpp
new file mode 100644
index 0000000..d2b393d
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderModifier.cpp
@@ -0,0 +1,299 @@
+/*
+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 BlenderModifier.cpp
+ * @brief Implementation of some blender modifiers (i.e subdivision, mirror).
+ */
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+
+#include "BlenderModifier.h"
+#include <assimp/SceneCombiner.h>
+#include <assimp/Subdivision.h>
+#include <assimp/scene.h>
+#include <memory>
+
+#include <functional>
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+
+template <typename T>
+BlenderModifier *god() {
+ return new T();
+}
+
+// add all available modifiers here
+typedef BlenderModifier *(*fpCreateModifier)();
+static const fpCreateModifier creators[] = {
+ &god<BlenderModifier_Mirror>,
+ &god<BlenderModifier_Subdivision>,
+
+ nullptr // sentinel
+};
+
+// ------------------------------------------------------------------------------------------------
+struct SharedModifierData : ElemBase {
+ ModifierData modifier;
+};
+
+// ------------------------------------------------------------------------------------------------
+void BlenderModifierShowcase::ApplyModifiers(aiNode &out, ConversionData &conv_data, const Scene &in, const Object &orig_object) {
+ size_t cnt = 0u, ful = 0u;
+
+ // NOTE: this cast is potentially unsafe by design, so we need to perform type checks before
+ // we're allowed to dereference the pointers without risking to crash. We might still be
+ // invoking UB btw - we're assuming that the ModifierData member of the respective modifier
+ // structures is at offset sizeof(vftable) with no padding.
+ const SharedModifierData *cur = static_cast<const SharedModifierData *>(orig_object.modifiers.first.get());
+ for (; cur; cur = static_cast<const SharedModifierData *>(cur->modifier.next.get()), ++ful) {
+ ai_assert(cur->dna_type);
+
+ const Structure *s = conv_data.db.dna.Get(cur->dna_type);
+ if (!s) {
+ ASSIMP_LOG_WARN("BlendModifier: could not resolve DNA name: ", cur->dna_type);
+ continue;
+ }
+
+ // this is a common trait of all XXXMirrorData structures in BlenderDNA
+ const Field *f = s->Get("modifier");
+ if (!f || f->offset != 0) {
+ ASSIMP_LOG_WARN("BlendModifier: expected a `modifier` member at offset 0");
+ continue;
+ }
+
+ s = conv_data.db.dna.Get(f->type);
+ if (!s || s->name != "ModifierData") {
+ ASSIMP_LOG_WARN("BlendModifier: expected a ModifierData structure as first member");
+ continue;
+ }
+
+ // now, we can be sure that we should be fine to dereference *cur* as
+ // ModifierData (with the above note).
+ const ModifierData &dat = cur->modifier;
+
+ const fpCreateModifier *curgod = creators;
+ std::vector<BlenderModifier *>::iterator curmod = cached_modifiers->begin(), endmod = cached_modifiers->end();
+
+ for (; *curgod; ++curgod, ++curmod) { // allocate modifiers on the fly
+ if (curmod == endmod) {
+ cached_modifiers->push_back((*curgod)());
+
+ endmod = cached_modifiers->end();
+ curmod = endmod - 1;
+ }
+
+ BlenderModifier *const modifier = *curmod;
+ if (modifier->IsActive(dat)) {
+ modifier->DoIt(out, conv_data, *static_cast<const ElemBase *>(cur), in, orig_object);
+ cnt++;
+
+ curgod = nullptr;
+ break;
+ }
+ }
+ if (curgod) {
+ ASSIMP_LOG_WARN("Couldn't find a handler for modifier: ", dat.name);
+ }
+ }
+
+ // Even though we managed to resolve some or all of the modifiers on this
+ // object, we still can't say whether our modifier implementations were
+ // able to fully do their job.
+ if (ful) {
+ ASSIMP_LOG_DEBUG("BlendModifier: found handlers for ", cnt, " of ", ful, " modifiers on `", orig_object.id.name,
+ "`, check log messages above for errors");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+bool BlenderModifier_Mirror ::IsActive(const ModifierData &modin) {
+ return modin.type == ModifierData::eModifierType_Mirror;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderModifier_Mirror ::DoIt(aiNode &out, ConversionData &conv_data, const ElemBase &orig_modifier,
+ const Scene & /*in*/,
+ const Object &orig_object) {
+ // hijacking the ABI, see the big note in BlenderModifierShowcase::ApplyModifiers()
+ const MirrorModifierData &mir = static_cast<const MirrorModifierData &>(orig_modifier);
+ ai_assert(mir.modifier.type == ModifierData::eModifierType_Mirror);
+
+ conv_data.meshes->reserve(conv_data.meshes->size() + out.mNumMeshes);
+
+ // XXX not entirely correct, mirroring on two axes results in 4 distinct objects in blender ...
+
+ // take all input meshes and clone them
+ for (unsigned int i = 0; i < out.mNumMeshes; ++i) {
+ aiMesh *mesh;
+ SceneCombiner::Copy(&mesh, conv_data.meshes[out.mMeshes[i]]);
+
+ const float xs = mir.flag & MirrorModifierData::Flags_AXIS_X ? -1.f : 1.f;
+ const float ys = mir.flag & MirrorModifierData::Flags_AXIS_Y ? -1.f : 1.f;
+ const float zs = mir.flag & MirrorModifierData::Flags_AXIS_Z ? -1.f : 1.f;
+
+ if (mir.mirror_ob) {
+ const aiVector3D center(mir.mirror_ob->obmat[3][0], mir.mirror_ob->obmat[3][1], mir.mirror_ob->obmat[3][2]);
+ for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+ aiVector3D &v = mesh->mVertices[j];
+
+ v.x = center.x + xs * (center.x - v.x);
+ v.y = center.y + ys * (center.y - v.y);
+ v.z = center.z + zs * (center.z - v.z);
+ }
+ } else {
+ for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+ aiVector3D &v = mesh->mVertices[j];
+ v.x *= xs;
+ v.y *= ys;
+ v.z *= zs;
+ }
+ }
+
+ if (mesh->mNormals) {
+ for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+ aiVector3D &v = mesh->mNormals[j];
+ v.x *= xs;
+ v.y *= ys;
+ v.z *= zs;
+ }
+ }
+
+ if (mesh->mTangents) {
+ for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+ aiVector3D &v = mesh->mTangents[j];
+ v.x *= xs;
+ v.y *= ys;
+ v.z *= zs;
+ }
+ }
+
+ if (mesh->mBitangents) {
+ for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+ aiVector3D &v = mesh->mBitangents[j];
+ v.x *= xs;
+ v.y *= ys;
+ v.z *= zs;
+ }
+ }
+
+ const float us = mir.flag & MirrorModifierData::Flags_MIRROR_U ? -1.f : 1.f;
+ const float vs = mir.flag & MirrorModifierData::Flags_MIRROR_V ? -1.f : 1.f;
+
+ for (unsigned int n = 0; mesh->HasTextureCoords(n); ++n) {
+ for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+ aiVector3D &v = mesh->mTextureCoords[n][j];
+ v.x *= us;
+ v.y *= vs;
+ }
+ }
+
+ // Only reverse the winding order if an odd number of axes were mirrored.
+ if (xs * ys * zs < 0) {
+ for (unsigned int j = 0; j < mesh->mNumFaces; ++j) {
+ aiFace &face = mesh->mFaces[j];
+ for (unsigned int fi = 0; fi < face.mNumIndices / 2; ++fi)
+ std::swap(face.mIndices[fi], face.mIndices[face.mNumIndices - 1 - fi]);
+ }
+ }
+
+ conv_data.meshes->push_back(mesh);
+ }
+ unsigned int *nind = new unsigned int[out.mNumMeshes * 2];
+
+ std::copy(out.mMeshes, out.mMeshes + out.mNumMeshes, nind);
+ std::transform(out.mMeshes, out.mMeshes + out.mNumMeshes, nind + out.mNumMeshes,
+ [&out](unsigned int n) { return out.mNumMeshes + n; });
+
+ delete[] out.mMeshes;
+ out.mMeshes = nind;
+ out.mNumMeshes *= 2;
+
+ ASSIMP_LOG_INFO("BlendModifier: Applied the `Mirror` modifier to `",
+ orig_object.id.name, "`");
+}
+
+// ------------------------------------------------------------------------------------------------
+bool BlenderModifier_Subdivision ::IsActive(const ModifierData &modin) {
+ return modin.type == ModifierData::eModifierType_Subsurf;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderModifier_Subdivision ::DoIt(aiNode &out, ConversionData &conv_data, const ElemBase &orig_modifier,
+ const Scene & /*in*/,
+ const Object &orig_object) {
+ // hijacking the ABI, see the big note in BlenderModifierShowcase::ApplyModifiers()
+ const SubsurfModifierData &mir = static_cast<const SubsurfModifierData &>(orig_modifier);
+ ai_assert(mir.modifier.type == ModifierData::eModifierType_Subsurf);
+
+ Subdivider::Algorithm algo;
+ switch (mir.subdivType) {
+ case SubsurfModifierData::TYPE_CatmullClarke:
+ algo = Subdivider::CATMULL_CLARKE;
+ break;
+
+ case SubsurfModifierData::TYPE_Simple:
+ ASSIMP_LOG_WARN("BlendModifier: The `SIMPLE` subdivision algorithm is not currently implemented, using Catmull-Clarke");
+ algo = Subdivider::CATMULL_CLARKE;
+ break;
+
+ default:
+ ASSIMP_LOG_WARN("BlendModifier: Unrecognized subdivision algorithm: ", mir.subdivType);
+ return;
+ };
+
+ std::unique_ptr<Subdivider> subd(Subdivider::Create(algo));
+ ai_assert(subd);
+ if (conv_data.meshes->empty()) {
+ return;
+ }
+ aiMesh **const meshes = &conv_data.meshes[conv_data.meshes->size() - out.mNumMeshes];
+ std::unique_ptr<aiMesh *[]> tempmeshes(new aiMesh *[out.mNumMeshes]());
+
+ subd->Subdivide(meshes, out.mNumMeshes, tempmeshes.get(), std::max(mir.renderLevels, mir.levels), true);
+ std::copy(tempmeshes.get(), tempmeshes.get() + out.mNumMeshes, meshes);
+
+ ASSIMP_LOG_INFO("BlendModifier: Applied the `Subdivision` modifier to `",
+ orig_object.id.name, "`");
+}
+
+#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderModifier.h b/libs/assimp/code/AssetLib/Blender/BlenderModifier.h
new file mode 100644
index 0000000..5af560c
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderModifier.h
@@ -0,0 +1,155 @@
+/*
+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 BlenderModifier.h
+ * @brief Declare dedicated helper classes to simulate some blender modifiers (i.e. mirror)
+ */
+#ifndef INCLUDED_AI_BLEND_MODIFIER_H
+#define INCLUDED_AI_BLEND_MODIFIER_H
+
+#include "BlenderIntermediate.h"
+
+namespace Assimp {
+namespace Blender {
+
+// -------------------------------------------------------------------------------------------
+/**
+ * Dummy base class for all blender modifiers. Modifiers are reused between imports, so
+ * they should be stateless and not try to cache model data.
+ */
+// -------------------------------------------------------------------------------------------
+class BlenderModifier {
+public:
+ /**
+ * The class destructor, virtual.
+ */
+ virtual ~BlenderModifier() {
+ // empty
+ }
+
+ // --------------------
+ /**
+ * Check if *this* modifier is active, given a ModifierData& block.
+ */
+ virtual bool IsActive( const ModifierData& /*modin*/) {
+ return false;
+ }
+
+ // --------------------
+ /**
+ * Apply the modifier to a given output node. The original data used
+ * to construct the node is given as well. Not called unless IsActive()
+ * was called and gave positive response.
+ */
+ virtual void DoIt(aiNode& /*out*/,
+ ConversionData& /*conv_data*/,
+ const ElemBase& orig_modifier,
+ const Scene& /*in*/,
+ const Object& /*orig_object*/
+ ) {
+ ASSIMP_LOG_INFO("This modifier is not supported, skipping: ",orig_modifier.dna_type );
+ return;
+ }
+};
+
+// -------------------------------------------------------------------------------------------
+/**
+ * Manage all known modifiers and instance and apply them if necessary
+ */
+// -------------------------------------------------------------------------------------------
+class BlenderModifierShowcase {
+public:
+ // --------------------
+ /** Apply all requested modifiers provided we support them. */
+ void ApplyModifiers(aiNode& out,
+ ConversionData& conv_data,
+ const Scene& in,
+ const Object& orig_object
+ );
+
+private:
+ TempArray< std::vector,BlenderModifier > cached_modifiers;
+};
+
+// MODIFIERS /////////////////////////////////////////////////////////////////////////////////
+
+// -------------------------------------------------------------------------------------------
+/**
+ * Mirror modifier. Status: implemented.
+ */
+// -------------------------------------------------------------------------------------------
+class BlenderModifier_Mirror : public BlenderModifier {
+public:
+ // --------------------
+ virtual bool IsActive( const ModifierData& modin);
+
+ // --------------------
+ virtual void DoIt(aiNode& out,
+ ConversionData& conv_data,
+ const ElemBase& orig_modifier,
+ const Scene& in,
+ const Object& orig_object
+ ) ;
+};
+
+// -------------------------------------------------------------------------------------------
+/** Subdivision modifier. Status: dummy. */
+// -------------------------------------------------------------------------------------------
+class BlenderModifier_Subdivision : public BlenderModifier {
+public:
+
+ // --------------------
+ virtual bool IsActive( const ModifierData& modin);
+
+ // --------------------
+ virtual void DoIt(aiNode& out,
+ ConversionData& conv_data,
+ const ElemBase& orig_modifier,
+ const Scene& in,
+ const Object& orig_object
+ ) ;
+};
+
+}
+}
+
+#endif // !INCLUDED_AI_BLEND_MODIFIER_H
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderScene.cpp b/libs/assimp/code/AssetLib/Blender/BlenderScene.cpp
new file mode 100644
index 0000000..9ad086f
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderScene.cpp
@@ -0,0 +1,891 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, ASSIMP Development 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 Development 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 BlenderScene.cpp
+ * @brief MACHINE GENERATED BY ./scripts/BlenderImporter/genblenddna.py
+ */
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+
+#include "BlenderScene.h"
+#include "BlenderCustomData.h"
+#include "BlenderDNA.h"
+#include "BlenderSceneGen.h"
+
+namespace Assimp {
+namespace Blender {
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Object>(
+ Object &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+ int temp = 0;
+ ReadField<ErrorPolicy_Fail>(temp, "type", db);
+ dest.type = static_cast<Assimp::Blender::Object::Type>(temp);
+ ReadFieldArray2<ErrorPolicy_Warn>(dest.obmat, "obmat", db);
+ ReadFieldArray2<ErrorPolicy_Warn>(dest.parentinv, "parentinv", db);
+ ReadFieldArray<ErrorPolicy_Warn>(dest.parsubstr, "parsubstr", db);
+ {
+ std::shared_ptr<Object> parent;
+ ReadFieldPtr<ErrorPolicy_Warn>(parent, "*parent", db);
+ dest.parent = parent.get();
+ }
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.track, "*track", db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy, "*proxy", db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy_from, "*proxy_from", db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy_group, "*proxy_group", db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.dup_group, "*dup_group", db);
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.data, "*data", db);
+ ReadField<ErrorPolicy_Igno>(dest.modifiers, "modifiers", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Group>(
+ Group &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+ ReadField<ErrorPolicy_Igno>(dest.layer, "layer", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.gobject, "*gobject", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure::Convert<CollectionObject>(
+ CollectionObject &dest,
+ const FileDatabase &db) const {
+
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.next, "*next", db);
+ {
+ //std::shared_ptr<CollectionObject> prev;
+ //ReadFieldPtr<ErrorPolicy_Fail>(prev, "*prev", db);
+ //dest.prev = prev.get();
+
+ std::shared_ptr<Object> ob;
+ ReadFieldPtr<ErrorPolicy_Igno>(ob, "*ob", db);
+ dest.ob = ob.get();
+ }
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure::Convert<CollectionChild>(
+ CollectionChild &dest,
+ const FileDatabase &db) const {
+
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.prev, "*prev", db);
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.next, "*next", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.collection, "*collection", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure::Convert<Collection>(
+ Collection &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+ ReadField<ErrorPolicy_Fail>(dest.gobject, "gobject", db);
+ ReadField<ErrorPolicy_Fail>(dest.children, "children", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MTex>(
+ MTex &dest,
+ const FileDatabase &db) const {
+
+ int temp_short = 0;
+ ReadField<ErrorPolicy_Igno>(temp_short, "mapto", db);
+ dest.mapto = static_cast<Assimp::Blender::MTex::MapType>(temp_short);
+ int temp = 0;
+ ReadField<ErrorPolicy_Igno>(temp, "blendtype", db);
+ dest.blendtype = static_cast<Assimp::Blender::MTex::BlendType>(temp);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.object, "*object", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.tex, "*tex", db);
+ ReadFieldArray<ErrorPolicy_Igno>(dest.uvname, "uvname", db);
+ ReadField<ErrorPolicy_Igno>(temp, "projx", db);
+ dest.projx = static_cast<Assimp::Blender::MTex::Projection>(temp);
+ ReadField<ErrorPolicy_Igno>(temp, "projy", db);
+ dest.projy = static_cast<Assimp::Blender::MTex::Projection>(temp);
+ ReadField<ErrorPolicy_Igno>(temp, "projz", db);
+ dest.projz = static_cast<Assimp::Blender::MTex::Projection>(temp);
+ ReadField<ErrorPolicy_Igno>(dest.mapping, "mapping", db);
+ ReadFieldArray<ErrorPolicy_Igno>(dest.ofs, "ofs", db);
+ ReadFieldArray<ErrorPolicy_Igno>(dest.size, "size", db);
+ ReadField<ErrorPolicy_Igno>(dest.rot, "rot", db);
+ ReadField<ErrorPolicy_Igno>(dest.texflag, "texflag", db);
+ ReadField<ErrorPolicy_Igno>(dest.colormodel, "colormodel", db);
+ ReadField<ErrorPolicy_Igno>(dest.pmapto, "pmapto", db);
+ ReadField<ErrorPolicy_Igno>(dest.pmaptoneg, "pmaptoneg", db);
+ ReadField<ErrorPolicy_Warn>(dest.r, "r", db);
+ ReadField<ErrorPolicy_Warn>(dest.g, "g", db);
+ ReadField<ErrorPolicy_Warn>(dest.b, "b", db);
+ ReadField<ErrorPolicy_Warn>(dest.k, "k", db);
+ ReadField<ErrorPolicy_Igno>(dest.colspecfac, "colspecfac", db);
+ ReadField<ErrorPolicy_Igno>(dest.mirrfac, "mirrfac", db);
+ ReadField<ErrorPolicy_Igno>(dest.alphafac, "alphafac", db);
+ ReadField<ErrorPolicy_Igno>(dest.difffac, "difffac", db);
+ ReadField<ErrorPolicy_Igno>(dest.specfac, "specfac", db);
+ ReadField<ErrorPolicy_Igno>(dest.emitfac, "emitfac", db);
+ ReadField<ErrorPolicy_Igno>(dest.hardfac, "hardfac", db);
+ ReadField<ErrorPolicy_Igno>(dest.norfac, "norfac", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<TFace>(
+ TFace &dest,
+ const FileDatabase &db) const {
+
+ ReadFieldArray2<ErrorPolicy_Fail>(dest.uv, "uv", db);
+ ReadFieldArray<ErrorPolicy_Fail>(dest.col, "col", db);
+ ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+ ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
+ ReadField<ErrorPolicy_Igno>(dest.tile, "tile", db);
+ ReadField<ErrorPolicy_Igno>(dest.unwrap, "unwrap", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<SubsurfModifierData>(
+ SubsurfModifierData &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Fail>(dest.modifier, "modifier", db);
+ ReadField<ErrorPolicy_Warn>(dest.subdivType, "subdivType", db);
+ ReadField<ErrorPolicy_Fail>(dest.levels, "levels", db);
+ ReadField<ErrorPolicy_Igno>(dest.renderLevels, "renderLevels", db);
+ ReadField<ErrorPolicy_Igno>(dest.flags, "flags", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MFace>(
+ MFace &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Fail>(dest.v1, "v1", db);
+ ReadField<ErrorPolicy_Fail>(dest.v2, "v2", db);
+ ReadField<ErrorPolicy_Fail>(dest.v3, "v3", db);
+ ReadField<ErrorPolicy_Fail>(dest.v4, "v4", db);
+ ReadField<ErrorPolicy_Fail>(dest.mat_nr, "mat_nr", db);
+ ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Lamp>(
+ Lamp &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+ int temp = 0;
+ ReadField<ErrorPolicy_Fail>(temp, "type", db);
+ dest.type = static_cast<Assimp::Blender::Lamp::Type>(temp);
+ ReadField<ErrorPolicy_Igno>(dest.flags, "flag", db);
+ ReadField<ErrorPolicy_Igno>(dest.colormodel, "colormodel", db);
+ ReadField<ErrorPolicy_Igno>(dest.totex, "totex", db);
+ ReadField<ErrorPolicy_Warn>(dest.r, "r", db);
+ ReadField<ErrorPolicy_Warn>(dest.g, "g", db);
+ ReadField<ErrorPolicy_Warn>(dest.b, "b", db);
+ ReadField<ErrorPolicy_Warn>(dest.k, "k", db);
+ ReadField<ErrorPolicy_Igno>(dest.energy, "energy", db);
+ ReadField<ErrorPolicy_Warn>(dest.dist, "dist", db);
+ ReadField<ErrorPolicy_Igno>(dest.spotsize, "spotsize", db);
+ ReadField<ErrorPolicy_Igno>(dest.spotblend, "spotblend", db);
+ ReadField<ErrorPolicy_Warn>(dest.constant_coefficient, "coeff_const", db);
+ ReadField<ErrorPolicy_Warn>(dest.linear_coefficient, "coeff_lin", db);
+ ReadField<ErrorPolicy_Warn>(dest.quadratic_coefficient, "coeff_quad", db);
+ ReadField<ErrorPolicy_Igno>(dest.att1, "att1", db);
+ ReadField<ErrorPolicy_Igno>(dest.att2, "att2", db);
+ ReadField<ErrorPolicy_Igno>(temp, "falloff_type", db);
+ dest.falloff_type = static_cast<Assimp::Blender::Lamp::FalloffType>(temp);
+ ReadField<ErrorPolicy_Igno>(dest.sun_brightness, "sun_brightness", db);
+ ReadField<ErrorPolicy_Igno>(dest.area_size, "area_size", db);
+ ReadField<ErrorPolicy_Igno>(dest.area_sizey, "area_sizey", db);
+ ReadField<ErrorPolicy_Igno>(dest.area_sizez, "area_sizez", db);
+ ReadField<ErrorPolicy_Igno>(dest.area_shape, "area_shape", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MDeformWeight>(
+ MDeformWeight &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Fail>(dest.def_nr, "def_nr", db);
+ ReadField<ErrorPolicy_Fail>(dest.weight, "weight", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<PackedFile>(
+ PackedFile &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Warn>(dest.size, "size", db);
+ ReadField<ErrorPolicy_Warn>(dest.seek, "seek", db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.data, "*data", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Base>(
+ Base &dest,
+ const FileDatabase &db) const {
+ // note: as per https://github.com/assimp/assimp/issues/128,
+ // reading the Object linked list recursively is prone to stack overflow.
+ // This structure converter is therefore an hand-written exception that
+ // does it iteratively.
+
+ const int initial_pos = db.reader->GetCurrentPos();
+
+ std::pair<Base *, int> todo = std::make_pair(&dest, initial_pos);
+ for (;;) {
+
+ Base &cur_dest = *todo.first;
+ db.reader->SetCurrentPos(todo.second);
+
+ // we know that this is a double-linked, circular list which we never
+ // traverse backwards, so don't bother resolving the back links.
+ cur_dest.prev = nullptr;
+
+ ReadFieldPtr<ErrorPolicy_Warn>(cur_dest.object, "*object", db);
+
+ // the return value of ReadFieldPtr indicates whether the object
+ // was already cached. In this case, we don't need to resolve
+ // it again.
+ if (!ReadFieldPtr<ErrorPolicy_Warn>(cur_dest.next, "*next", db, true) && cur_dest.next) {
+ todo = std::make_pair(&*cur_dest.next, db.reader->GetCurrentPos());
+ continue;
+ }
+ break;
+ }
+
+ db.reader->SetCurrentPos(initial_pos + size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MTFace>(
+ MTFace &dest,
+ const FileDatabase &db) const {
+
+ ReadFieldArray2<ErrorPolicy_Fail>(dest.uv, "uv", db);
+ ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+ ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
+ ReadField<ErrorPolicy_Igno>(dest.tile, "tile", db);
+ ReadField<ErrorPolicy_Igno>(dest.unwrap, "unwrap", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Material>(
+ Material &dest,
+ const FileDatabase &db) const {
+ ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+ ReadField<ErrorPolicy_Warn>(dest.r, "r", db);
+ ReadField<ErrorPolicy_Warn>(dest.g, "g", db);
+ ReadField<ErrorPolicy_Warn>(dest.b, "b", db);
+ ReadField<ErrorPolicy_Warn>(dest.specr, "specr", db);
+ ReadField<ErrorPolicy_Warn>(dest.specg, "specg", db);
+ ReadField<ErrorPolicy_Warn>(dest.specb, "specb", db);
+ ReadField<ErrorPolicy_Igno>(dest.har, "har", db);
+ ReadField<ErrorPolicy_Warn>(dest.ambr, "ambr", db);
+ ReadField<ErrorPolicy_Warn>(dest.ambg, "ambg", db);
+ ReadField<ErrorPolicy_Warn>(dest.ambb, "ambb", db);
+ ReadField<ErrorPolicy_Igno>(dest.mirr, "mirr", db);
+ ReadField<ErrorPolicy_Igno>(dest.mirg, "mirg", db);
+ ReadField<ErrorPolicy_Igno>(dest.mirb, "mirb", db);
+ ReadField<ErrorPolicy_Warn>(dest.emit, "emit", db);
+ ReadField<ErrorPolicy_Igno>(dest.ray_mirror, "ray_mirror", db);
+ ReadField<ErrorPolicy_Warn>(dest.alpha, "alpha", db);
+ ReadField<ErrorPolicy_Igno>(dest.ref, "ref", db);
+ ReadField<ErrorPolicy_Igno>(dest.translucency, "translucency", db);
+ ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
+ ReadField<ErrorPolicy_Igno>(dest.roughness, "roughness", db);
+ ReadField<ErrorPolicy_Igno>(dest.darkness, "darkness", db);
+ ReadField<ErrorPolicy_Igno>(dest.refrac, "refrac", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.group, "*group", db);
+ ReadField<ErrorPolicy_Warn>(dest.diff_shader, "diff_shader", db);
+ ReadField<ErrorPolicy_Warn>(dest.spec_shader, "spec_shader", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mtex, "*mtex", db);
+
+ ReadField<ErrorPolicy_Igno>(dest.amb, "amb", db);
+ ReadField<ErrorPolicy_Igno>(dest.ang, "ang", db);
+ ReadField<ErrorPolicy_Igno>(dest.spectra, "spectra", db);
+ ReadField<ErrorPolicy_Igno>(dest.spec, "spec", db);
+ ReadField<ErrorPolicy_Igno>(dest.zoffs, "zoffs", db);
+ ReadField<ErrorPolicy_Igno>(dest.add, "add", db);
+ ReadField<ErrorPolicy_Igno>(dest.fresnel_mir, "fresnel_mir", db);
+ ReadField<ErrorPolicy_Igno>(dest.fresnel_mir_i, "fresnel_mir_i", db);
+ ReadField<ErrorPolicy_Igno>(dest.fresnel_tra, "fresnel_tra", db);
+ ReadField<ErrorPolicy_Igno>(dest.fresnel_tra_i, "fresnel_tra_i", db);
+ ReadField<ErrorPolicy_Igno>(dest.filter, "filter", db);
+ ReadField<ErrorPolicy_Igno>(dest.tx_limit, "tx_limit", db);
+ ReadField<ErrorPolicy_Igno>(dest.tx_falloff, "tx_falloff", db);
+ ReadField<ErrorPolicy_Igno>(dest.gloss_mir, "gloss_mir", db);
+ ReadField<ErrorPolicy_Igno>(dest.gloss_tra, "gloss_tra", db);
+ ReadField<ErrorPolicy_Igno>(dest.adapt_thresh_mir, "adapt_thresh_mir", db);
+ ReadField<ErrorPolicy_Igno>(dest.adapt_thresh_tra, "adapt_thresh_tra", db);
+ ReadField<ErrorPolicy_Igno>(dest.aniso_gloss_mir, "aniso_gloss_mir", db);
+ ReadField<ErrorPolicy_Igno>(dest.dist_mir, "dist_mir", db);
+ ReadField<ErrorPolicy_Igno>(dest.hasize, "hasize", db);
+ ReadField<ErrorPolicy_Igno>(dest.flaresize, "flaresize", db);
+ ReadField<ErrorPolicy_Igno>(dest.subsize, "subsize", db);
+ ReadField<ErrorPolicy_Igno>(dest.flareboost, "flareboost", db);
+ ReadField<ErrorPolicy_Igno>(dest.strand_sta, "strand_sta", db);
+ ReadField<ErrorPolicy_Igno>(dest.strand_end, "strand_end", db);
+ ReadField<ErrorPolicy_Igno>(dest.strand_ease, "strand_ease", db);
+ ReadField<ErrorPolicy_Igno>(dest.strand_surfnor, "strand_surfnor", db);
+ ReadField<ErrorPolicy_Igno>(dest.strand_min, "strand_min", db);
+ ReadField<ErrorPolicy_Igno>(dest.strand_widthfade, "strand_widthfade", db);
+ ReadField<ErrorPolicy_Igno>(dest.sbias, "sbias", db);
+ ReadField<ErrorPolicy_Igno>(dest.lbias, "lbias", db);
+ ReadField<ErrorPolicy_Igno>(dest.shad_alpha, "shad_alpha", db);
+ ReadField<ErrorPolicy_Igno>(dest.param, "param", db);
+ ReadField<ErrorPolicy_Igno>(dest.rms, "rms", db);
+ ReadField<ErrorPolicy_Igno>(dest.rampfac_col, "rampfac_col", db);
+ ReadField<ErrorPolicy_Igno>(dest.rampfac_spec, "rampfac_spec", db);
+ ReadField<ErrorPolicy_Igno>(dest.friction, "friction", db);
+ ReadField<ErrorPolicy_Igno>(dest.fh, "fh", db);
+ ReadField<ErrorPolicy_Igno>(dest.reflect, "reflect", db);
+ ReadField<ErrorPolicy_Igno>(dest.fhdist, "fhdist", db);
+ ReadField<ErrorPolicy_Igno>(dest.xyfrict, "xyfrict", db);
+ ReadField<ErrorPolicy_Igno>(dest.sss_radius, "sss_radius", db);
+ ReadField<ErrorPolicy_Igno>(dest.sss_col, "sss_col", db);
+ ReadField<ErrorPolicy_Igno>(dest.sss_error, "sss_error", db);
+ ReadField<ErrorPolicy_Igno>(dest.sss_scale, "sss_scale", db);
+ ReadField<ErrorPolicy_Igno>(dest.sss_ior, "sss_ior", db);
+ ReadField<ErrorPolicy_Igno>(dest.sss_colfac, "sss_colfac", db);
+ ReadField<ErrorPolicy_Igno>(dest.sss_texfac, "sss_texfac", db);
+ ReadField<ErrorPolicy_Igno>(dest.sss_front, "sss_front", db);
+ ReadField<ErrorPolicy_Igno>(dest.sss_back, "sss_back", db);
+
+ ReadField<ErrorPolicy_Igno>(dest.material_type, "material_type", db);
+ ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+ ReadField<ErrorPolicy_Igno>(dest.ray_depth, "ray_depth", db);
+ ReadField<ErrorPolicy_Igno>(dest.ray_depth_tra, "ray_depth_tra", db);
+ ReadField<ErrorPolicy_Igno>(dest.samp_gloss_mir, "samp_gloss_mir", db);
+ ReadField<ErrorPolicy_Igno>(dest.samp_gloss_tra, "samp_gloss_tra", db);
+ ReadField<ErrorPolicy_Igno>(dest.fadeto_mir, "fadeto_mir", db);
+ ReadField<ErrorPolicy_Igno>(dest.shade_flag, "shade_flag", db);
+ ReadField<ErrorPolicy_Igno>(dest.flarec, "flarec", db);
+ ReadField<ErrorPolicy_Igno>(dest.starc, "starc", db);
+ ReadField<ErrorPolicy_Igno>(dest.linec, "linec", db);
+ ReadField<ErrorPolicy_Igno>(dest.ringc, "ringc", db);
+ ReadField<ErrorPolicy_Igno>(dest.pr_lamp, "pr_lamp", db);
+ ReadField<ErrorPolicy_Igno>(dest.pr_texture, "pr_texture", db);
+ ReadField<ErrorPolicy_Igno>(dest.ml_flag, "ml_flag", db);
+ ReadField<ErrorPolicy_Igno>(dest.diff_shader, "diff_shader", db);
+ ReadField<ErrorPolicy_Igno>(dest.spec_shader, "spec_shader", db);
+ ReadField<ErrorPolicy_Igno>(dest.texco, "texco", db);
+ ReadField<ErrorPolicy_Igno>(dest.mapto, "mapto", db);
+ ReadField<ErrorPolicy_Igno>(dest.ramp_show, "ramp_show", db);
+ ReadField<ErrorPolicy_Igno>(dest.pad3, "pad3", db);
+ ReadField<ErrorPolicy_Igno>(dest.dynamode, "dynamode", db);
+ ReadField<ErrorPolicy_Igno>(dest.pad2, "pad2", db);
+ ReadField<ErrorPolicy_Igno>(dest.sss_flag, "sss_flag", db);
+ ReadField<ErrorPolicy_Igno>(dest.sss_preset, "sss_preset", db);
+ ReadField<ErrorPolicy_Igno>(dest.shadowonly_flag, "shadowonly_flag", db);
+ ReadField<ErrorPolicy_Igno>(dest.index, "index", db);
+ ReadField<ErrorPolicy_Igno>(dest.vcol_alpha, "vcol_alpha", db);
+ ReadField<ErrorPolicy_Igno>(dest.pad4, "pad4", db);
+
+ ReadField<ErrorPolicy_Igno>(dest.seed1, "seed1", db);
+ ReadField<ErrorPolicy_Igno>(dest.seed2, "seed2", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MTexPoly>(
+ MTexPoly &dest,
+ const FileDatabase &db) const {
+
+ {
+ std::shared_ptr<Image> tpage;
+ ReadFieldPtr<ErrorPolicy_Igno>(tpage, "*tpage", db);
+ dest.tpage = tpage.get();
+ }
+ ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+ ReadField<ErrorPolicy_Igno>(dest.transp, "transp", db);
+ ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
+ ReadField<ErrorPolicy_Igno>(dest.tile, "tile", db);
+ ReadField<ErrorPolicy_Igno>(dest.pad, "pad", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Mesh>(
+ Mesh &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+ ReadField<ErrorPolicy_Fail>(dest.totface, "totface", db);
+ ReadField<ErrorPolicy_Fail>(dest.totedge, "totedge", db);
+ ReadField<ErrorPolicy_Fail>(dest.totvert, "totvert", db);
+ ReadField<ErrorPolicy_Igno>(dest.totloop, "totloop", db);
+ ReadField<ErrorPolicy_Igno>(dest.totpoly, "totpoly", db);
+ ReadField<ErrorPolicy_Igno>(dest.subdiv, "subdiv", db);
+ ReadField<ErrorPolicy_Igno>(dest.subdivr, "subdivr", db);
+ ReadField<ErrorPolicy_Igno>(dest.subsurftype, "subsurftype", db);
+ ReadField<ErrorPolicy_Igno>(dest.smoothresh, "smoothresh", db);
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.mface, "*mface", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mtface, "*mtface", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.tface, "*tface", db);
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.mvert, "*mvert", db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.medge, "*medge", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mloop, "*mloop", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mloopuv, "*mloopuv", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mloopcol, "*mloopcol", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mpoly, "*mpoly", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mtpoly, "*mtpoly", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.dvert, "*dvert", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mcol, "*mcol", db);
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.mat, "**mat", db);
+
+ ReadField<ErrorPolicy_Igno>(dest.vdata, "vdata", db);
+ ReadField<ErrorPolicy_Igno>(dest.edata, "edata", db);
+ ReadField<ErrorPolicy_Igno>(dest.fdata, "fdata", db);
+ ReadField<ErrorPolicy_Igno>(dest.pdata, "pdata", db);
+ ReadField<ErrorPolicy_Warn>(dest.ldata, "ldata", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MDeformVert>(
+ MDeformVert &dest,
+ const FileDatabase &db) const {
+
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.dw, "*dw", db);
+ ReadField<ErrorPolicy_Igno>(dest.totweight, "totweight", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<World>(
+ World &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MLoopCol>(
+ MLoopCol &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Igno>(dest.r, "r", db);
+ ReadField<ErrorPolicy_Igno>(dest.g, "g", db);
+ ReadField<ErrorPolicy_Igno>(dest.b, "b", db);
+ ReadField<ErrorPolicy_Igno>(dest.a, "a", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MVert>(
+ MVert &dest,
+ const FileDatabase &db) const {
+
+ ReadFieldArray<ErrorPolicy_Fail>(dest.co, "co", db);
+ ReadFieldArray<ErrorPolicy_Fail>(dest.no, "no", db);
+ ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+ //ReadField<ErrorPolicy_Warn>(dest.mat_nr,"mat_nr",db);
+ ReadField<ErrorPolicy_Igno>(dest.bweight, "bweight", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MEdge>(
+ MEdge &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Fail>(dest.v1, "v1", db);
+ ReadField<ErrorPolicy_Fail>(dest.v2, "v2", db);
+ ReadField<ErrorPolicy_Igno>(dest.crease, "crease", db);
+ ReadField<ErrorPolicy_Igno>(dest.bweight, "bweight", db);
+ ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MLoopUV>(
+ MLoopUV &dest,
+ const FileDatabase &db) const {
+
+ ReadFieldArray<ErrorPolicy_Igno>(dest.uv, "uv", db);
+ ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<GroupObject>(
+ GroupObject &dest,
+ const FileDatabase &db) const {
+
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.prev, "*prev", db);
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.next, "*next", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.ob, "*ob", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<ListBase>(
+ ListBase &dest,
+ const FileDatabase &db) const {
+
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.first, "*first", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.last, "*last", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MLoop>(
+ MLoop &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Igno>(dest.v, "v", db);
+ ReadField<ErrorPolicy_Igno>(dest.e, "e", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<ModifierData>(
+ ModifierData &dest,
+ const FileDatabase &db) const {
+
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.next, "*next", db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.prev, "*prev", db);
+ ReadField<ErrorPolicy_Igno>(dest.type, "type", db);
+ ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
+ ReadFieldArray<ErrorPolicy_Igno>(dest.name, "name", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<ID>(
+ ID &dest,
+ const FileDatabase &db) const {
+
+ ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
+ ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MCol>(
+ MCol &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Fail>(dest.r, "r", db);
+ ReadField<ErrorPolicy_Fail>(dest.g, "g", db);
+ ReadField<ErrorPolicy_Fail>(dest.b, "b", db);
+ ReadField<ErrorPolicy_Fail>(dest.a, "a", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MPoly>(
+ MPoly &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Igno>(dest.loopstart, "loopstart", db);
+ ReadField<ErrorPolicy_Igno>(dest.totloop, "totloop", db);
+ ReadField<ErrorPolicy_Igno>(dest.mat_nr, "mat_nr", db);
+ ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Scene>(
+ Scene &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.camera, "*camera", db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.world, "*world", db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.basact, "*basact", db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.master_collection, "*master_collection", db);
+ ReadField<ErrorPolicy_Igno>(dest.base, "base", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Library>(
+ Library &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+ ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
+ ReadFieldArray<ErrorPolicy_Fail>(dest.filename, "filename", db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.parent, "*parent", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Tex>(
+ Tex &dest,
+ const FileDatabase &db) const {
+ short temp_short = 0;
+ ReadField<ErrorPolicy_Igno>(temp_short, "imaflag", db);
+ dest.imaflag = static_cast<Assimp::Blender::Tex::ImageFlags>(temp_short);
+ int temp = 0;
+ ReadField<ErrorPolicy_Fail>(temp, "type", db);
+ dest.type = static_cast<Assimp::Blender::Tex::Type>(temp);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.ima, "*ima", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Camera>(
+ Camera &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+ int temp = 0;
+ ReadField<ErrorPolicy_Warn>(temp, "type", db);
+ dest.type = static_cast<Assimp::Blender::Camera::Type>(temp);
+ ReadField<ErrorPolicy_Warn>(temp, "flag", db);
+ dest.flag = static_cast<Assimp::Blender::Camera::Type>(temp);
+ ReadField<ErrorPolicy_Warn>(dest.lens, "lens", db);
+ ReadField<ErrorPolicy_Warn>(dest.sensor_x, "sensor_x", db);
+ ReadField<ErrorPolicy_Igno>(dest.clipsta, "clipsta", db);
+ ReadField<ErrorPolicy_Igno>(dest.clipend, "clipend", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MirrorModifierData>(
+ MirrorModifierData &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Fail>(dest.modifier, "modifier", db);
+ ReadField<ErrorPolicy_Igno>(dest.axis, "axis", db);
+ ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+ ReadField<ErrorPolicy_Igno>(dest.tolerance, "tolerance", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mirror_ob, "*mirror_ob", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Image>(
+ Image &dest,
+ const FileDatabase &db) const {
+
+ ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+ ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
+ ReadField<ErrorPolicy_Igno>(dest.ok, "ok", db);
+ ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+ ReadField<ErrorPolicy_Igno>(dest.source, "source", db);
+ ReadField<ErrorPolicy_Igno>(dest.type, "type", db);
+ ReadField<ErrorPolicy_Igno>(dest.pad, "pad", db);
+ ReadField<ErrorPolicy_Igno>(dest.pad1, "pad1", db);
+ ReadField<ErrorPolicy_Igno>(dest.lastframe, "lastframe", db);
+ ReadField<ErrorPolicy_Igno>(dest.tpageflag, "tpageflag", db);
+ ReadField<ErrorPolicy_Igno>(dest.totbind, "totbind", db);
+ ReadField<ErrorPolicy_Igno>(dest.xrep, "xrep", db);
+ ReadField<ErrorPolicy_Igno>(dest.yrep, "yrep", db);
+ ReadField<ErrorPolicy_Igno>(dest.twsta, "twsta", db);
+ ReadField<ErrorPolicy_Igno>(dest.twend, "twend", db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.packedfile, "*packedfile", db);
+ ReadField<ErrorPolicy_Igno>(dest.lastupdate, "lastupdate", db);
+ ReadField<ErrorPolicy_Igno>(dest.lastused, "lastused", db);
+ ReadField<ErrorPolicy_Igno>(dest.animspeed, "animspeed", db);
+ ReadField<ErrorPolicy_Igno>(dest.gen_x, "gen_x", db);
+ ReadField<ErrorPolicy_Igno>(dest.gen_y, "gen_y", db);
+ ReadField<ErrorPolicy_Igno>(dest.gen_type, "gen_type", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure::Convert<CustomData>(
+ CustomData &dest,
+ const FileDatabase &db) const {
+ ReadFieldArray<ErrorPolicy_Warn>(dest.typemap, "typemap", db);
+ ReadField<ErrorPolicy_Warn>(dest.totlayer, "totlayer", db);
+ ReadField<ErrorPolicy_Warn>(dest.maxlayer, "maxlayer", db);
+ ReadField<ErrorPolicy_Warn>(dest.totsize, "totsize", db);
+ ReadFieldPtrVector<ErrorPolicy_Warn>(dest.layers, "*layers", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure::Convert<CustomDataLayer>(
+ CustomDataLayer &dest,
+ const FileDatabase &db) const {
+ ReadField<ErrorPolicy_Fail>(dest.type, "type", db);
+ ReadField<ErrorPolicy_Fail>(dest.offset, "offset", db);
+ ReadField<ErrorPolicy_Fail>(dest.flag, "flag", db);
+ ReadField<ErrorPolicy_Fail>(dest.active, "active", db);
+ ReadField<ErrorPolicy_Fail>(dest.active_rnd, "active_rnd", db);
+ ReadField<ErrorPolicy_Fail>(dest.active_clone, "active_clone", db);
+ ReadField<ErrorPolicy_Fail>(dest.active_mask, "active_mask", db);
+ ReadField<ErrorPolicy_Fail>(dest.uid, "uid", db);
+ ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
+ ReadCustomDataPtr<ErrorPolicy_Fail>(dest.data, dest.type, "*data", db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+void DNA::RegisterConverters() {
+
+ converters["Object"] = DNA::FactoryPair(&Structure::Allocate<Object>, &Structure::Convert<Object>);
+ converters["Group"] = DNA::FactoryPair(&Structure::Allocate<Group>, &Structure::Convert<Group>);
+ converters["MTex"] = DNA::FactoryPair(&Structure::Allocate<MTex>, &Structure::Convert<MTex>);
+ converters["TFace"] = DNA::FactoryPair(&Structure::Allocate<TFace>, &Structure::Convert<TFace>);
+ converters["SubsurfModifierData"] = DNA::FactoryPair(&Structure::Allocate<SubsurfModifierData>, &Structure::Convert<SubsurfModifierData>);
+ converters["MFace"] = DNA::FactoryPair(&Structure::Allocate<MFace>, &Structure::Convert<MFace>);
+ converters["Lamp"] = DNA::FactoryPair(&Structure::Allocate<Lamp>, &Structure::Convert<Lamp>);
+ converters["MDeformWeight"] = DNA::FactoryPair(&Structure::Allocate<MDeformWeight>, &Structure::Convert<MDeformWeight>);
+ converters["PackedFile"] = DNA::FactoryPair(&Structure::Allocate<PackedFile>, &Structure::Convert<PackedFile>);
+ converters["Base"] = DNA::FactoryPair(&Structure::Allocate<Base>, &Structure::Convert<Base>);
+ converters["MTFace"] = DNA::FactoryPair(&Structure::Allocate<MTFace>, &Structure::Convert<MTFace>);
+ converters["Material"] = DNA::FactoryPair(&Structure::Allocate<Material>, &Structure::Convert<Material>);
+ converters["MTexPoly"] = DNA::FactoryPair(&Structure::Allocate<MTexPoly>, &Structure::Convert<MTexPoly>);
+ converters["Mesh"] = DNA::FactoryPair(&Structure::Allocate<Mesh>, &Structure::Convert<Mesh>);
+ converters["MDeformVert"] = DNA::FactoryPair(&Structure::Allocate<MDeformVert>, &Structure::Convert<MDeformVert>);
+ converters["World"] = DNA::FactoryPair(&Structure::Allocate<World>, &Structure::Convert<World>);
+ converters["MLoopCol"] = DNA::FactoryPair(&Structure::Allocate<MLoopCol>, &Structure::Convert<MLoopCol>);
+ converters["MVert"] = DNA::FactoryPair(&Structure::Allocate<MVert>, &Structure::Convert<MVert>);
+ converters["MEdge"] = DNA::FactoryPair(&Structure::Allocate<MEdge>, &Structure::Convert<MEdge>);
+ converters["MLoopUV"] = DNA::FactoryPair(&Structure::Allocate<MLoopUV>, &Structure::Convert<MLoopUV>);
+ converters["GroupObject"] = DNA::FactoryPair(&Structure::Allocate<GroupObject>, &Structure::Convert<GroupObject>);
+ converters["ListBase"] = DNA::FactoryPair(&Structure::Allocate<ListBase>, &Structure::Convert<ListBase>);
+ converters["MLoop"] = DNA::FactoryPair(&Structure::Allocate<MLoop>, &Structure::Convert<MLoop>);
+ converters["ModifierData"] = DNA::FactoryPair(&Structure::Allocate<ModifierData>, &Structure::Convert<ModifierData>);
+ converters["ID"] = DNA::FactoryPair(&Structure::Allocate<ID>, &Structure::Convert<ID>);
+ converters["MCol"] = DNA::FactoryPair(&Structure::Allocate<MCol>, &Structure::Convert<MCol>);
+ converters["MPoly"] = DNA::FactoryPair(&Structure::Allocate<MPoly>, &Structure::Convert<MPoly>);
+ converters["Scene"] = DNA::FactoryPair(&Structure::Allocate<Scene>, &Structure::Convert<Scene>);
+ converters["Library"] = DNA::FactoryPair(&Structure::Allocate<Library>, &Structure::Convert<Library>);
+ converters["Tex"] = DNA::FactoryPair(&Structure::Allocate<Tex>, &Structure::Convert<Tex>);
+ converters["Camera"] = DNA::FactoryPair(&Structure::Allocate<Camera>, &Structure::Convert<Camera>);
+ converters["MirrorModifierData"] = DNA::FactoryPair(&Structure::Allocate<MirrorModifierData>, &Structure::Convert<MirrorModifierData>);
+ converters["Image"] = DNA::FactoryPair(&Structure::Allocate<Image>, &Structure::Convert<Image>);
+ converters["CustomData"] = DNA::FactoryPair(&Structure::Allocate<CustomData>, &Structure::Convert<CustomData>);
+ converters["CustomDataLayer"] = DNA::FactoryPair(&Structure::Allocate<CustomDataLayer>, &Structure::Convert<CustomDataLayer>);
+ converters["Collection"] = DNA::FactoryPair(&Structure::Allocate<Collection>, &Structure::Convert<Collection>);
+ converters["CollectionChild"] = DNA::FactoryPair(&Structure::Allocate<CollectionChild>, &Structure::Convert<CollectionChild>);
+ converters["CollectionObject"] = DNA::FactoryPair(&Structure::Allocate<CollectionObject>, &Structure::Convert<CollectionObject>);
+}
+
+} // namespace Blender
+} //namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderScene.h b/libs/assimp/code/AssetLib/Blender/BlenderScene.h
new file mode 100644
index 0000000..c153d3c
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderScene.h
@@ -0,0 +1,983 @@
+/*
+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 BlenderScene.h
+ * @brief Intermediate representation of a BLEND scene.
+ */
+#ifndef INCLUDED_AI_BLEND_SCENE_H
+#define INCLUDED_AI_BLEND_SCENE_H
+
+#include "BlenderDNA.h"
+
+namespace Assimp {
+namespace Blender {
+
+// Minor parts of this file are extracts from blender data structures,
+// declared in the ./source/blender/makesdna directory.
+// Stuff that is not used by Assimp is commented.
+
+// NOTE
+// this file serves as input data to the `./scripts/genblenddna.py`
+// script. This script generates the actual binding code to read a
+// blender file with a possibly different DNA into our structures.
+// Only `struct` declarations are considered and the following
+// rules must be obeyed in order for the script to work properly:
+//
+// * C++ style comments only
+//
+// * Structures may include the primitive types char, int, short,
+// float, double. Signed specifiers are not allowed on
+// integers. Enum types are allowed, but they must have been
+// defined in this header.
+//
+// * Structures may aggregate other structures, unless not defined
+// in this header.
+//
+// * Pointers to other structures or primitive types are allowed.
+// No references or double pointers or arrays of pointers.
+// A pointer to a T is normally written as std::shared_ptr, while a
+// pointer to an array of elements is written as boost::
+// shared_array. To avoid cyclic pointers, use raw pointers in
+// one direction.
+//
+// * Arrays can have maximally two-dimensions. Any non-pointer
+// type can form them.
+//
+// * Multiple fields can be declare in a single line (i.e `int a,b;`)
+// provided they are neither pointers nor arrays.
+//
+// * One of WARN, FAIL can be appended to the declaration (
+// prior to the semicolon to specify the error handling policy if
+// this field is missing in the input DNA). If none of those
+// is specified the default policy is to substitute a default
+// value for the field.
+//
+
+// warn if field is missing, substitute default value
+#ifdef WARN
+#undef WARN
+#endif
+#define WARN
+
+// fail the import if the field does not exist
+#ifdef FAIL
+#undef FAIL
+#endif
+#define FAIL
+
+struct Object;
+struct MTex;
+struct Image;
+struct Collection;
+
+#include <memory>
+
+#define AI_BLEND_MESH_MAX_VERTS 2000000000L
+
+static const size_t MaxNameLen = 1024;
+
+// -------------------------------------------------------------------------------
+struct ID : ElemBase {
+ char name[MaxNameLen] WARN;
+ short flag;
+};
+
+// -------------------------------------------------------------------------------
+struct ListBase : ElemBase {
+ std::shared_ptr<ElemBase> first;
+ std::shared_ptr<ElemBase> last;
+};
+
+// -------------------------------------------------------------------------------
+struct PackedFile : ElemBase {
+ int size WARN;
+ int seek WARN;
+ std::shared_ptr<FileOffset> data WARN;
+};
+
+// -------------------------------------------------------------------------------
+struct GroupObject : ElemBase {
+ std::shared_ptr<GroupObject> prev, next FAIL;
+ std::shared_ptr<Object> ob;
+};
+
+// -------------------------------------------------------------------------------
+struct Group : ElemBase {
+ ID id FAIL;
+ int layer;
+
+ std::shared_ptr<GroupObject> gobject;
+};
+
+// -------------------------------------------------------------------------------
+struct CollectionObject : ElemBase {
+ //CollectionObject* prev;
+ std::shared_ptr<CollectionObject> next;
+ Object *ob;
+};
+
+// -------------------------------------------------------------------------------
+struct CollectionChild : ElemBase {
+ std::shared_ptr<CollectionChild> next, prev;
+ std::shared_ptr<Collection> collection;
+};
+
+// -------------------------------------------------------------------------------
+struct Collection : ElemBase {
+ ID id FAIL;
+ ListBase gobject; // CollectionObject
+ ListBase children; // CollectionChild
+};
+
+// -------------------------------------------------------------------------------
+struct World : ElemBase {
+ ID id FAIL;
+};
+
+// -------------------------------------------------------------------------------
+struct MVert : ElemBase {
+ float co[3] FAIL;
+ float no[3] FAIL; // read as short and divided through / 32767.f
+ char flag;
+ int mat_nr WARN;
+ int bweight;
+
+ MVert() :
+ ElemBase(), flag(0), mat_nr(0), bweight(0) {}
+};
+
+// -------------------------------------------------------------------------------
+struct MEdge : ElemBase {
+ int v1, v2 FAIL;
+ char crease, bweight;
+ short flag;
+};
+
+// -------------------------------------------------------------------------------
+struct MLoop : ElemBase {
+ int v, e;
+};
+
+// -------------------------------------------------------------------------------
+struct MLoopUV : ElemBase {
+ float uv[2];
+ int flag;
+};
+
+// -------------------------------------------------------------------------------
+// Note that red and blue are not swapped, as with MCol
+struct MLoopCol : ElemBase {
+ unsigned char r, g, b, a;
+};
+
+// -------------------------------------------------------------------------------
+struct MPoly : ElemBase {
+ int loopstart;
+ int totloop;
+ short mat_nr;
+ char flag;
+};
+
+// -------------------------------------------------------------------------------
+struct MTexPoly : ElemBase {
+ Image *tpage;
+ char flag, transp;
+ short mode, tile, pad;
+};
+
+// -------------------------------------------------------------------------------
+struct MCol : ElemBase {
+ char r, g, b, a FAIL;
+};
+
+// -------------------------------------------------------------------------------
+struct MFace : ElemBase {
+ int v1, v2, v3, v4 FAIL;
+ int mat_nr FAIL;
+ char flag;
+};
+
+// -------------------------------------------------------------------------------
+struct TFace : ElemBase {
+ float uv[4][2] FAIL;
+ int col[4] FAIL;
+ char flag;
+ short mode;
+ short tile;
+ short unwrap;
+};
+
+// -------------------------------------------------------------------------------
+struct MTFace : ElemBase {
+ MTFace() :
+ flag(0),
+ mode(0),
+ tile(0),
+ unwrap(0) {
+ }
+
+ float uv[4][2] FAIL;
+ char flag;
+ short mode;
+ short tile;
+ short unwrap;
+
+ // std::shared_ptr<Image> tpage;
+};
+
+// -------------------------------------------------------------------------------
+struct MDeformWeight : ElemBase {
+ int def_nr FAIL;
+ float weight FAIL;
+};
+
+// -------------------------------------------------------------------------------
+struct MDeformVert : ElemBase {
+ vector<MDeformWeight> dw WARN;
+ int totweight;
+};
+
+// -------------------------------------------------------------------------------
+#define MA_RAYMIRROR 0x40000
+#define MA_TRANSPARENCY 0x10000
+#define MA_RAYTRANSP 0x20000
+#define MA_ZTRANSP 0x00040
+
+struct Material : ElemBase {
+ ID id FAIL;
+
+ float r, g, b WARN;
+ float specr, specg, specb WARN;
+ short har;
+ float ambr, ambg, ambb WARN;
+ float mirr, mirg, mirb;
+ float emit WARN;
+ float ray_mirror;
+ float alpha WARN;
+ float ref;
+ float translucency;
+ int mode;
+ float roughness;
+ float darkness;
+ float refrac;
+
+ float amb;
+ float ang;
+ float spectra;
+ float spec;
+ float zoffs;
+ float add;
+ float fresnel_mir;
+ float fresnel_mir_i;
+ float fresnel_tra;
+ float fresnel_tra_i;
+ float filter;
+ float tx_limit;
+ float tx_falloff;
+ float gloss_mir;
+ float gloss_tra;
+ float adapt_thresh_mir;
+ float adapt_thresh_tra;
+ float aniso_gloss_mir;
+ float dist_mir;
+ float hasize;
+ float flaresize;
+ float subsize;
+ float flareboost;
+ float strand_sta;
+ float strand_end;
+ float strand_ease;
+ float strand_surfnor;
+ float strand_min;
+ float strand_widthfade;
+ float sbias;
+ float lbias;
+ float shad_alpha;
+ float param;
+ float rms;
+ float rampfac_col;
+ float rampfac_spec;
+ float friction;
+ float fh;
+ float reflect;
+ float fhdist;
+ float xyfrict;
+ float sss_radius;
+ float sss_col;
+ float sss_error;
+ float sss_scale;
+ float sss_ior;
+ float sss_colfac;
+ float sss_texfac;
+ float sss_front;
+ float sss_back;
+
+ short material_type;
+ short flag;
+ short ray_depth;
+ short ray_depth_tra;
+ short samp_gloss_mir;
+ short samp_gloss_tra;
+ short fadeto_mir;
+ short shade_flag;
+ short flarec;
+ short starc;
+ short linec;
+ short ringc;
+ short pr_lamp;
+ short pr_texture;
+ short ml_flag;
+ short texco;
+ short mapto;
+ short ramp_show;
+ short pad3;
+ short dynamode;
+ short pad2;
+ short sss_flag;
+ short sss_preset;
+ short shadowonly_flag;
+ short index;
+ short vcol_alpha;
+ short pad4;
+
+ char seed1;
+ char seed2;
+
+ std::shared_ptr<Group> group;
+
+ short diff_shader WARN;
+ short spec_shader WARN;
+
+ std::shared_ptr<MTex> mtex[18];
+};
+
+/*
+CustomDataLayer 104
+
+ int type 0 4
+ int offset 4 4
+ int flag 8 4
+ int active 12 4
+ int active_rnd 16 4
+ int active_clone 20 4
+ int active_mask 24 4
+ int uid 28 4
+ char name 32 64
+ void *data 96 8
+*/
+struct CustomDataLayer : ElemBase {
+ int type;
+ int offset;
+ int flag;
+ int active;
+ int active_rnd;
+ int active_clone;
+ int active_mask;
+ int uid;
+ char name[64];
+ std::shared_ptr<ElemBase> data; // must be converted to real type according type member
+
+ CustomDataLayer() :
+ ElemBase(),
+ type(0),
+ offset(0),
+ flag(0),
+ active(0),
+ active_rnd(0),
+ active_clone(0),
+ active_mask(0),
+ uid(0),
+ data(nullptr) {
+ memset(name, 0, sizeof name);
+ }
+};
+
+/*
+CustomData 208
+
+ CustomDataLayer *layers 0 8
+ int typemap 8 168
+ int pad_i1 176 4
+ int totlayer 180 4
+ int maxlayer 184 4
+ int totsize 188 4
+ BLI_mempool *pool 192 8
+ CustomDataExternal *external 200 8
+*/
+struct CustomData : ElemBase {
+ vector<std::shared_ptr<struct CustomDataLayer>> layers;
+ int typemap[42]; // CD_NUMTYPES
+ int totlayer;
+ int maxlayer;
+ int totsize;
+ /*
+ std::shared_ptr<BLI_mempool> pool;
+ std::shared_ptr<CustomDataExternal> external;
+ */
+};
+
+// -------------------------------------------------------------------------------
+struct Mesh : ElemBase {
+ ID id FAIL;
+
+ int totface FAIL;
+ int totedge FAIL;
+ int totvert FAIL;
+ int totloop;
+ int totpoly;
+
+ short subdiv;
+ short subdivr;
+ short subsurftype;
+ short smoothresh;
+
+ vector<MFace> mface FAIL;
+ vector<MTFace> mtface;
+ vector<TFace> tface;
+ vector<MVert> mvert FAIL;
+ vector<MEdge> medge WARN;
+ vector<MLoop> mloop;
+ vector<MLoopUV> mloopuv;
+ vector<MLoopCol> mloopcol;
+ vector<MPoly> mpoly;
+ vector<MTexPoly> mtpoly;
+ vector<MDeformVert> dvert;
+ vector<MCol> mcol;
+
+ vector<std::shared_ptr<Material>> mat FAIL;
+
+ struct CustomData vdata;
+ struct CustomData edata;
+ struct CustomData fdata;
+ struct CustomData pdata;
+ struct CustomData ldata;
+};
+
+// -------------------------------------------------------------------------------
+struct Library : ElemBase {
+ ID id FAIL;
+
+ char name[240] WARN;
+ char filename[240] FAIL;
+ std::shared_ptr<Library> parent WARN;
+};
+
+// -------------------------------------------------------------------------------
+struct Camera : ElemBase {
+ enum Type {
+ Type_PERSP = 0,
+ Type_ORTHO = 1
+ };
+
+ ID id FAIL;
+
+ Type type, flag WARN;
+ float lens WARN;
+ float sensor_x WARN;
+ float clipsta, clipend;
+};
+
+// -------------------------------------------------------------------------------
+struct Lamp : ElemBase {
+
+ enum FalloffType {
+ FalloffType_Constant = 0x0,
+ FalloffType_InvLinear = 0x1,
+ FalloffType_InvSquare = 0x2
+ //,FalloffType_Curve = 0x3
+ //,FalloffType_Sliders = 0x4
+ };
+
+ enum Type {
+ Type_Local = 0x0,
+ Type_Sun = 0x1,
+ Type_Spot = 0x2,
+ Type_Hemi = 0x3,
+ Type_Area = 0x4
+ //,Type_YFPhoton = 0x5
+ };
+
+ ID id FAIL;
+ //AnimData *adt;
+
+ Type type FAIL;
+ short flags;
+
+ //int mode;
+
+ short colormodel, totex;
+ float r, g, b, k WARN;
+ //float shdwr, shdwg, shdwb;
+
+ float energy, dist, spotsize, spotblend;
+ //float haint;
+
+ float constant_coefficient;
+ float linear_coefficient;
+ float quadratic_coefficient;
+
+ float att1, att2;
+ //struct CurveMapping *curfalloff;
+ FalloffType falloff_type;
+
+ //float clipsta, clipend, shadspotsize;
+ //float bias, soft, compressthresh;
+ //short bufsize, samp, buffers, filtertype;
+ //char bufflag, buftype;
+
+ //short ray_samp, ray_sampy, ray_sampz;
+ //short ray_samp_type;
+ short area_shape;
+ float area_size, area_sizey, area_sizez;
+ //float adapt_thresh;
+ //short ray_samp_method;
+
+ //short texact, shadhalostep;
+
+ //short sun_effect_type;
+ //short skyblendtype;
+ //float horizon_brightness;
+ //float spread;
+ float sun_brightness;
+ //float sun_size;
+ //float backscattered_light;
+ //float sun_intensity;
+ //float atm_turbidity;
+ //float atm_inscattering_factor;
+ //float atm_extinction_factor;
+ //float atm_distance_factor;
+ //float skyblendfac;
+ //float sky_exposure;
+ //short sky_colorspace;
+
+ // int YF_numphotons, YF_numsearch;
+ // short YF_phdepth, YF_useqmc, YF_bufsize, YF_pad;
+ // float YF_causticblur, YF_ltradius;
+
+ // float YF_glowint, YF_glowofs;
+ // short YF_glowtype, YF_pad2;
+
+ //struct Ipo *ipo;
+ //struct MTex *mtex[18];
+ // short pr_texture;
+
+ //struct PreviewImage *preview;
+};
+
+// -------------------------------------------------------------------------------
+struct ModifierData : ElemBase {
+ enum ModifierType {
+ eModifierType_None = 0,
+ eModifierType_Subsurf,
+ eModifierType_Lattice,
+ eModifierType_Curve,
+ eModifierType_Build,
+ eModifierType_Mirror,
+ eModifierType_Decimate,
+ eModifierType_Wave,
+ eModifierType_Armature,
+ eModifierType_Hook,
+ eModifierType_Softbody,
+ eModifierType_Boolean,
+ eModifierType_Array,
+ eModifierType_EdgeSplit,
+ eModifierType_Displace,
+ eModifierType_UVProject,
+ eModifierType_Smooth,
+ eModifierType_Cast,
+ eModifierType_MeshDeform,
+ eModifierType_ParticleSystem,
+ eModifierType_ParticleInstance,
+ eModifierType_Explode,
+ eModifierType_Cloth,
+ eModifierType_Collision,
+ eModifierType_Bevel,
+ eModifierType_Shrinkwrap,
+ eModifierType_Fluidsim,
+ eModifierType_Mask,
+ eModifierType_SimpleDeform,
+ eModifierType_Multires,
+ eModifierType_Surface,
+ eModifierType_Smoke,
+ eModifierType_ShapeKey
+ };
+
+ std::shared_ptr<ElemBase> next WARN;
+ std::shared_ptr<ElemBase> prev WARN;
+
+ int type, mode;
+ char name[32];
+};
+
+// -------------------------------------------------------------------------------
+struct SubsurfModifierData : ElemBase {
+
+ enum Type {
+
+ TYPE_CatmullClarke = 0x0,
+ TYPE_Simple = 0x1
+ };
+
+ enum Flags {
+ // some omitted
+ FLAGS_SubsurfUV = 1 << 3
+ };
+
+ ModifierData modifier FAIL;
+ short subdivType WARN;
+ short levels FAIL;
+ short renderLevels;
+ short flags;
+};
+
+// -------------------------------------------------------------------------------
+struct MirrorModifierData : ElemBase {
+
+ enum Flags {
+ Flags_CLIPPING = 1 << 0,
+ Flags_MIRROR_U = 1 << 1,
+ Flags_MIRROR_V = 1 << 2,
+ Flags_AXIS_X = 1 << 3,
+ Flags_AXIS_Y = 1 << 4,
+ Flags_AXIS_Z = 1 << 5,
+ Flags_VGROUP = 1 << 6
+ };
+
+ ModifierData modifier FAIL;
+
+ short axis, flag;
+ float tolerance;
+ std::shared_ptr<Object> mirror_ob;
+};
+
+// -------------------------------------------------------------------------------
+struct Object : ElemBase {
+ ID id FAIL;
+
+ enum Type {
+ Type_EMPTY = 0,
+ Type_MESH = 1,
+ Type_CURVE = 2,
+ Type_SURF = 3,
+ Type_FONT = 4,
+ Type_MBALL = 5
+
+ ,
+ Type_LAMP = 10,
+ Type_CAMERA = 11
+
+ ,
+ Type_WAVE = 21,
+ Type_LATTICE = 22
+ };
+
+ Type type FAIL;
+ float obmat[4][4] WARN;
+ float parentinv[4][4] WARN;
+ char parsubstr[32] WARN;
+
+ Object *parent WARN;
+ std::shared_ptr<Object> track WARN;
+
+ std::shared_ptr<Object> proxy, proxy_from, proxy_group WARN;
+ std::shared_ptr<Group> dup_group WARN;
+ std::shared_ptr<ElemBase> data FAIL;
+
+ ListBase modifiers;
+
+ Object() :
+ ElemBase(), type(Type_EMPTY), parent(nullptr), track(), proxy(), proxy_from(), data() {
+ // empty
+ }
+};
+
+// -------------------------------------------------------------------------------
+struct Base : ElemBase {
+ Base *prev WARN;
+ std::shared_ptr<Base> next WARN;
+ std::shared_ptr<Object> object WARN;
+
+ Base() :
+ ElemBase(), prev(nullptr), next(), object() {
+ // empty
+ // empty
+ }
+};
+
+// -------------------------------------------------------------------------------
+struct Scene : ElemBase {
+ ID id FAIL;
+
+ std::shared_ptr<Object> camera WARN;
+ std::shared_ptr<World> world WARN;
+ std::shared_ptr<Base> basact WARN;
+ std::shared_ptr<Collection> master_collection WARN;
+
+ ListBase base;
+
+ Scene() :
+ ElemBase(), camera(), world(), basact(), master_collection() {
+ // empty
+ }
+};
+
+// -------------------------------------------------------------------------------
+struct Image : ElemBase {
+ ID id FAIL;
+
+ char name[240] WARN;
+
+ //struct anim *anim;
+
+ short ok, flag;
+ short source, type, pad, pad1;
+ int lastframe;
+
+ short tpageflag, totbind;
+ short xrep, yrep;
+ short twsta, twend;
+ //unsigned int bindcode;
+ //unsigned int *repbind;
+
+ std::shared_ptr<PackedFile> packedfile;
+ //struct PreviewImage * preview;
+
+ float lastupdate;
+ int lastused;
+ short animspeed;
+
+ short gen_x, gen_y, gen_type;
+
+ Image() :
+ ElemBase() {
+ // empty
+ }
+};
+
+// -------------------------------------------------------------------------------
+struct Tex : ElemBase {
+
+ // actually, the only texture type we support is Type_IMAGE
+ enum Type {
+ Type_CLOUDS = 1,
+ Type_WOOD = 2,
+ Type_MARBLE = 3,
+ Type_MAGIC = 4,
+ Type_BLEND = 5,
+ Type_STUCCI = 6,
+ Type_NOISE = 7,
+ Type_IMAGE = 8,
+ Type_PLUGIN = 9,
+ Type_ENVMAP = 10,
+ Type_MUSGRAVE = 11,
+ Type_VORONOI = 12,
+ Type_DISTNOISE = 13,
+ Type_POINTDENSITY = 14,
+ Type_VOXELDATA = 15
+ };
+
+ enum ImageFlags {
+ ImageFlags_INTERPOL = 1,
+ ImageFlags_USEALPHA = 2,
+ ImageFlags_MIPMAP = 4,
+ ImageFlags_IMAROT = 16,
+ ImageFlags_CALCALPHA = 32,
+ ImageFlags_NORMALMAP = 2048,
+ ImageFlags_GAUSS_MIP = 4096,
+ ImageFlags_FILTER_MIN = 8192,
+ ImageFlags_DERIVATIVEMAP = 16384
+ };
+
+ ID id FAIL;
+ // AnimData *adt;
+
+ //float noisesize, turbul;
+ //float bright, contrast, rfac, gfac, bfac;
+ //float filtersize;
+
+ //float mg_H, mg_lacunarity, mg_octaves, mg_offset, mg_gain;
+ //float dist_amount, ns_outscale;
+
+ //float vn_w1;
+ //float vn_w2;
+ //float vn_w3;
+ //float vn_w4;
+ //float vn_mexp;
+ //short vn_distm, vn_coltype;
+
+ //short noisedepth, noisetype;
+ //short noisebasis, noisebasis2;
+
+ //short flag;
+ ImageFlags imaflag;
+ Type type FAIL;
+ //short stype;
+
+ //float cropxmin, cropymin, cropxmax, cropymax;
+ //int texfilter;
+ //int afmax;
+ //short xrepeat, yrepeat;
+ //short extend;
+
+ //short fie_ima;
+ //int len;
+ //int frames, offset, sfra;
+
+ //float checkerdist, nabla;
+ //float norfac;
+
+ //ImageUser iuser;
+
+ //bNodeTree *nodetree;
+ //Ipo *ipo;
+ std::shared_ptr<Image> ima WARN;
+ //PluginTex *plugin;
+ //ColorBand *coba;
+ //EnvMap *env;
+ //PreviewImage * preview;
+ //PointDensity *pd;
+ //VoxelData *vd;
+
+ //char use_nodes;
+
+ Tex() :
+ ElemBase(), imaflag(ImageFlags_INTERPOL), type(Type_CLOUDS), ima() {
+ // empty
+ }
+};
+
+// -------------------------------------------------------------------------------
+struct MTex : ElemBase {
+
+ enum Projection {
+ Proj_N = 0,
+ Proj_X = 1,
+ Proj_Y = 2,
+ Proj_Z = 3
+ };
+
+ enum Flag {
+ Flag_RGBTOINT = 0x1,
+ Flag_STENCIL = 0x2,
+ Flag_NEGATIVE = 0x4,
+ Flag_ALPHAMIX = 0x8,
+ Flag_VIEWSPACE = 0x10
+ };
+
+ enum BlendType {
+ BlendType_BLEND = 0,
+ BlendType_MUL = 1,
+ BlendType_ADD = 2,
+ BlendType_SUB = 3,
+ BlendType_DIV = 4,
+ BlendType_DARK = 5,
+ BlendType_DIFF = 6,
+ BlendType_LIGHT = 7,
+ BlendType_SCREEN = 8,
+ BlendType_OVERLAY = 9,
+ BlendType_BLEND_HUE = 10,
+ BlendType_BLEND_SAT = 11,
+ BlendType_BLEND_VAL = 12,
+ BlendType_BLEND_COLOR = 13
+ };
+
+ enum MapType {
+ MapType_COL = 1,
+ MapType_NORM = 2,
+ MapType_COLSPEC = 4,
+ MapType_COLMIR = 8,
+ MapType_REF = 16,
+ MapType_SPEC = 32,
+ MapType_EMIT = 64,
+ MapType_ALPHA = 128,
+ MapType_HAR = 256,
+ MapType_RAYMIRR = 512,
+ MapType_TRANSLU = 1024,
+ MapType_AMB = 2048,
+ MapType_DISPLACE = 4096,
+ MapType_WARP = 8192
+ };
+
+ // short texco, maptoneg;
+ MapType mapto;
+
+ BlendType blendtype;
+ std::shared_ptr<Object> object;
+ std::shared_ptr<Tex> tex;
+ char uvname[32];
+
+ Projection projx, projy, projz;
+ char mapping;
+ float ofs[3], size[3], rot;
+
+ int texflag;
+ short colormodel, pmapto, pmaptoneg;
+ //short normapspace, which_output;
+ //char brush_map_mode;
+ float r, g, b, k WARN;
+ //float def_var, rt;
+
+ //float colfac, varfac;
+
+ float norfac;
+ //float dispfac, warpfac;
+ float colspecfac, mirrfac, alphafac;
+ float difffac, specfac, emitfac, hardfac;
+ //float raymirrfac, translfac, ambfac;
+ //float colemitfac, colreflfac, coltransfac;
+ //float densfac, scatterfac, reflfac;
+
+ //float timefac, lengthfac, clumpfac;
+ //float kinkfac, roughfac, padensfac;
+ //float lifefac, sizefac, ivelfac, pvelfac;
+ //float shadowfac;
+ //float zenupfac, zendownfac, blendfac;
+
+ MTex() :
+ ElemBase() {
+ // empty
+ }
+};
+
+} // namespace Blender
+} // namespace Assimp
+#endif
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderSceneGen.h b/libs/assimp/code/AssetLib/Blender/BlenderSceneGen.h
new file mode 100644
index 0000000..762fdd3
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderSceneGen.h
@@ -0,0 +1,272 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, ASSIMP Development 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 Development 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 BlenderSceneGen.h
+ * @brief MACHINE GENERATED BY ./scripts/BlenderImporter/genblenddna.py
+ */
+#ifndef INCLUDED_AI_BLEND_SCENEGEN_H
+#define INCLUDED_AI_BLEND_SCENEGEN_H
+
+#include "BlenderDNA.h"
+#include "BlenderScene.h"
+
+namespace Assimp {
+namespace Blender {
+
+template <> void Structure :: Convert<Object> (
+ Object& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Group> (
+ Group& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure::Convert<Collection>(
+ Collection& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MTex> (
+ MTex& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<TFace> (
+ TFace& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<SubsurfModifierData> (
+ SubsurfModifierData& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MFace> (
+ MFace& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Lamp> (
+ Lamp& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MDeformWeight> (
+ MDeformWeight& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<PackedFile> (
+ PackedFile& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Base> (
+ Base& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MTFace> (
+ MTFace& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Material> (
+ Material& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MTexPoly> (
+ MTexPoly& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Mesh> (
+ Mesh& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MDeformVert> (
+ MDeformVert& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<World> (
+ World& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MLoopCol> (
+ MLoopCol& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MVert> (
+ MVert& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MEdge> (
+ MEdge& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MLoopUV> (
+ MLoopUV& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<GroupObject> (
+ GroupObject& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<ListBase> (
+ ListBase& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MLoop> (
+ MLoop& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<ModifierData> (
+ ModifierData& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<ID> (
+ ID& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MCol> (
+ MCol& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MPoly> (
+ MPoly& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Scene> (
+ Scene& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Library> (
+ Library& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Tex> (
+ Tex& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Camera> (
+ Camera& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MirrorModifierData> (
+ MirrorModifierData& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Image> (
+ Image& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure::Convert<CustomData>(
+ CustomData& dest,
+ const FileDatabase& db
+ ) const
+ ;
+
+template <> void Structure::Convert<CustomDataLayer>(
+ CustomDataLayer& dest,
+ const FileDatabase& db
+ ) const
+ ;
+
+ }
+}
+
+#endif
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderTessellator.cpp b/libs/assimp/code/AssetLib/Blender/BlenderTessellator.cpp
new file mode 100644
index 0000000..d3d463e
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderTessellator.cpp
@@ -0,0 +1,532 @@
+/*
+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 BlenderTessellator.cpp
+ * @brief A simple tessellation wrapper
+ */
+
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+
+#include "BlenderDNA.h"
+#include "BlenderScene.h"
+#include "BlenderBMesh.h"
+#include "BlenderTessellator.h"
+
+#include <stddef.h>
+
+static const unsigned int BLEND_TESS_MAGIC = 0x83ed9ac3;
+
+#if ASSIMP_BLEND_WITH_GLU_TESSELLATE
+
+namspace Assimp
+{
+ template< > const char* LogFunctions< BlenderTessellatorGL >::Prefix()
+ {
+ static auto prefix = "BLEND_TESS_GL: ";
+ return prefix;
+ }
+}
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+
+#ifndef CALLBACK
+#define CALLBACK
+#endif
+
+// ------------------------------------------------------------------------------------------------
+BlenderTessellatorGL::BlenderTessellatorGL( BlenderBMeshConverter& converter ):
+ converter( &converter )
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+BlenderTessellatorGL::~BlenderTessellatorGL( )
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::Tessellate( const MLoop* polyLoop, int vertexCount, const std::vector< MVert >& vertices )
+{
+ AssertVertexCount( vertexCount );
+
+ std::vector< VertexGL > polyLoopGL;
+ GenerateLoopVerts( polyLoopGL, polyLoop, vertexCount, vertices );
+
+ TessDataGL tessData;
+ Tesssellate( polyLoopGL, tessData );
+
+ TriangulateDrawCalls( tessData );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::AssertVertexCount( int vertexCount )
+{
+ if ( vertexCount <= 4 )
+ {
+ ThrowException( "Expected more than 4 vertices for tessellation" );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::GenerateLoopVerts( std::vector< VertexGL >& polyLoopGL, const MLoop* polyLoop, int vertexCount, const std::vector< MVert >& vertices )
+{
+ for ( int i = 0; i < vertexCount; ++i )
+ {
+ const MLoop& loopItem = polyLoop[ i ];
+ const MVert& vertex = vertices[ loopItem.v ];
+ polyLoopGL.push_back( VertexGL( vertex.co[ 0 ], vertex.co[ 1 ], vertex.co[ 2 ], loopItem.v, BLEND_TESS_MAGIC ) );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::Tesssellate( std::vector< VertexGL >& polyLoopGL, TessDataGL& tessData )
+{
+ GLUtesselator* tessellator = gluNewTess( );
+ gluTessCallback( tessellator, GLU_TESS_BEGIN_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateBegin ) );
+ gluTessCallback( tessellator, GLU_TESS_END_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateEnd ) );
+ gluTessCallback( tessellator, GLU_TESS_VERTEX_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateVertex ) );
+ gluTessCallback( tessellator, GLU_TESS_COMBINE_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateCombine ) );
+ gluTessCallback( tessellator, GLU_TESS_EDGE_FLAG_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateEdgeFlag ) );
+ gluTessCallback( tessellator, GLU_TESS_ERROR_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateError ) );
+ gluTessProperty( tessellator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO );
+
+ gluTessBeginPolygon( tessellator, &tessData );
+ gluTessBeginContour( tessellator );
+
+ for ( unsigned int i = 0; i < polyLoopGL.size( ); ++i )
+ {
+ gluTessVertex( tessellator, reinterpret_cast< GLdouble* >( &polyLoopGL[ i ] ), &polyLoopGL[ i ] );
+ }
+
+ gluTessEndContour( tessellator );
+ gluTessEndPolygon( tessellator );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TriangulateDrawCalls( const TessDataGL& tessData )
+{
+ // NOTE - Because we are supplying a callback to GLU_TESS_EDGE_FLAG_DATA we don't technically
+ // need support for GL_TRIANGLE_STRIP and GL_TRIANGLE_FAN but we'll keep it here in case
+ // GLU tessellate changes or tri-strips and fans are wanted.
+ // See: http://www.opengl.org/sdk/docs/man2/xhtml/gluTessCallback.xml
+ for ( unsigned int i = 0; i < tessData.drawCalls.size( ); ++i )
+ {
+ const DrawCallGL& drawCallGL = tessData.drawCalls[ i ];
+ const VertexGL* vertices = &tessData.vertices[ drawCallGL.baseVertex ];
+ if ( drawCallGL.drawMode == GL_TRIANGLES )
+ {
+ MakeFacesFromTris( vertices, drawCallGL.vertexCount );
+ }
+ else if ( drawCallGL.drawMode == GL_TRIANGLE_STRIP )
+ {
+ MakeFacesFromTriStrip( vertices, drawCallGL.vertexCount );
+ }
+ else if ( drawCallGL.drawMode == GL_TRIANGLE_FAN )
+ {
+ MakeFacesFromTriFan( vertices, drawCallGL.vertexCount );
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::MakeFacesFromTris( const VertexGL* vertices, int vertexCount )
+{
+ const int triangleCount = vertexCount / 3;
+ for ( int i = 0; i < triangleCount; ++i )
+ {
+ int vertexBase = i * 3;
+ converter->AddFace( vertices[ vertexBase + 0 ].index, vertices[ vertexBase + 1 ].index, vertices[ vertexBase + 2 ].index );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::MakeFacesFromTriStrip( const VertexGL* vertices, int vertexCount )
+{
+ const int triangleCount = vertexCount - 2;
+ for ( int i = 0; i < triangleCount; ++i )
+ {
+ int vertexBase = i;
+ converter->AddFace( vertices[ vertexBase + 0 ].index, vertices[ vertexBase + 1 ].index, vertices[ vertexBase + 2 ].index );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::MakeFacesFromTriFan( const VertexGL* vertices, int vertexCount )
+{
+ const int triangleCount = vertexCount - 2;
+ for ( int i = 0; i < triangleCount; ++i )
+ {
+ int vertexBase = i;
+ converter->AddFace( vertices[ 0 ].index, vertices[ vertexBase + 1 ].index, vertices[ vertexBase + 2 ].index );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateBegin( GLenum drawModeGL, void* userData )
+{
+ TessDataGL& tessData = *reinterpret_cast< TessDataGL* >( userData );
+ tessData.drawCalls.push_back( DrawCallGL( drawModeGL, tessData.vertices.size( ) ) );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateEnd( void* )
+{
+ // Do nothing
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateVertex( const void* vtxData, void* userData )
+{
+ TessDataGL& tessData = *reinterpret_cast< TessDataGL* >( userData );
+
+ const VertexGL& vertex = *reinterpret_cast< const VertexGL* >( vtxData );
+ if ( vertex.magic != BLEND_TESS_MAGIC )
+ {
+ ThrowException( "Point returned by GLU Tessellate was probably not one of ours. This indicates we need a new way to store vertex information" );
+ }
+ tessData.vertices.push_back( vertex );
+ if ( tessData.drawCalls.size( ) == 0 )
+ {
+ ThrowException( "\"Vertex\" callback received before \"Begin\"" );
+ }
+ ++( tessData.drawCalls.back( ).vertexCount );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateCombine( const GLdouble intersection[ 3 ], const GLdouble* [ 4 ], const GLfloat [ 4 ], GLdouble** out, void* userData )
+{
+ ThrowException( "Intersected polygon loops are not yet supported" );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateEdgeFlag( GLboolean, void* )
+{
+ // Do nothing
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateError( GLenum errorCode, void* )
+{
+ ThrowException( reinterpret_cast< const char* >( gluErrorString( errorCode ) ) );
+}
+
+#endif // ASSIMP_BLEND_WITH_GLU_TESSELLATE
+
+#if ASSIMP_BLEND_WITH_POLY_2_TRI
+
+namespace Assimp
+{
+ template< > const char* LogFunctions< BlenderTessellatorP2T >::Prefix()
+ {
+ static auto prefix = "BLEND_TESS_P2T: ";
+ return prefix;
+ }
+}
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+
+// ------------------------------------------------------------------------------------------------
+BlenderTessellatorP2T::BlenderTessellatorP2T( BlenderBMeshConverter& converter ):
+ converter( &converter )
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+BlenderTessellatorP2T::~BlenderTessellatorP2T( )
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::Tessellate( const MLoop* polyLoop, int vertexCount, const std::vector< MVert >& vertices )
+{
+ AssertVertexCount( vertexCount );
+
+ // NOTE - We have to hope that points in a Blender polygon are roughly on the same plane.
+ // There may be some triangulation artifacts if they are wildly different.
+
+ std::vector< PointP2T > points;
+ Copy3DVertices( polyLoop, vertexCount, vertices, points );
+
+ PlaneP2T plane = FindLLSQPlane( points );
+
+ aiMatrix4x4 transform = GeneratePointTransformMatrix( plane );
+
+ TransformAndFlattenVectices( transform, points );
+
+ std::vector< p2t::Point* > pointRefs;
+ ReferencePoints( points, pointRefs );
+
+ p2t::CDT cdt( pointRefs );
+
+ cdt.Triangulate( );
+ std::vector< p2t::Triangle* > triangles = cdt.GetTriangles( );
+
+ MakeFacesFromTriangles( triangles );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::AssertVertexCount( int vertexCount )
+{
+ if ( vertexCount <= 4 )
+ {
+ ThrowException( "Expected more than 4 vertices for tessellation" );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::Copy3DVertices( const MLoop* polyLoop, int vertexCount, const std::vector< MVert >& vertices, std::vector< PointP2T >& points ) const
+{
+ points.resize( vertexCount );
+ for ( int i = 0; i < vertexCount; ++i )
+ {
+ const MLoop& loop = polyLoop[ i ];
+ const MVert& vert = vertices[ loop.v ];
+
+ PointP2T& point = points[ i ];
+ point.point3D.Set( vert.co[ 0 ], vert.co[ 1 ], vert.co[ 2 ] );
+ point.index = loop.v;
+ point.magic = BLEND_TESS_MAGIC;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+aiMatrix4x4 BlenderTessellatorP2T::GeneratePointTransformMatrix( const Blender::PlaneP2T& plane ) const
+{
+ aiVector3D sideA( 1.0f, 0.0f, 0.0f );
+ if ( std::fabs( plane.normal * sideA ) > 0.999f )
+ {
+ sideA = aiVector3D( 0.0f, 1.0f, 0.0f );
+ }
+
+ aiVector3D sideB( plane.normal ^ sideA );
+ sideB.Normalize( );
+ sideA = sideB ^ plane.normal;
+
+ aiMatrix4x4 result;
+ result.a1 = sideA.x;
+ result.a2 = sideA.y;
+ result.a3 = sideA.z;
+ result.b1 = sideB.x;
+ result.b2 = sideB.y;
+ result.b3 = sideB.z;
+ result.c1 = plane.normal.x;
+ result.c2 = plane.normal.y;
+ result.c3 = plane.normal.z;
+ result.a4 = plane.centre.x;
+ result.b4 = plane.centre.y;
+ result.c4 = plane.centre.z;
+ result.Inverse( );
+
+ return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::TransformAndFlattenVectices( const aiMatrix4x4& transform, std::vector< Blender::PointP2T >& vertices ) const
+{
+ for ( size_t i = 0; i < vertices.size( ); ++i )
+ {
+ PointP2T& point = vertices[ i ];
+ point.point3D = transform * point.point3D;
+ point.point2D.set( point.point3D.y, point.point3D.z );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::ReferencePoints( std::vector< Blender::PointP2T >& points, std::vector< p2t::Point* >& pointRefs ) const
+{
+ pointRefs.resize( points.size( ) );
+ for ( size_t i = 0; i < points.size( ); ++i )
+ {
+ pointRefs[ i ] = &points[ i ].point2D;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+inline PointP2T& BlenderTessellatorP2T::GetActualPointStructure( p2t::Point& point ) const
+{
+#if defined __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Winvalid-offsetof"
+#endif // __clang__
+ unsigned int pointOffset = offsetof( PointP2T, point2D );
+#if defined __clang__
+# pragma clang diagnostic pop
+#endif
+ PointP2T& pointStruct = *reinterpret_cast< PointP2T* >( reinterpret_cast< char* >( &point ) - pointOffset );
+ if ( pointStruct.magic != static_cast<int>( BLEND_TESS_MAGIC ) )
+ {
+ ThrowException( "Point returned by poly2tri was probably not one of ours. This indicates we need a new way to store vertex information" );
+ }
+ return pointStruct;
+}
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::MakeFacesFromTriangles( std::vector< p2t::Triangle* >& triangles ) const
+{
+ for ( size_t i = 0; i < triangles.size( ); ++i )
+ {
+ p2t::Triangle& Triangle = *triangles[ i ];
+
+ PointP2T& pointA = GetActualPointStructure( *Triangle.GetPoint( 0 ) );
+ PointP2T& pointB = GetActualPointStructure( *Triangle.GetPoint( 1 ) );
+ PointP2T& pointC = GetActualPointStructure( *Triangle.GetPoint( 2 ) );
+
+ converter->AddFace( pointA.index, pointB.index, pointC.index );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+inline float p2tMax( float a, float b )
+{
+ return a > b ? a : b;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Adapted from: http://missingbytes.blogspot.co.uk/2012/06/fitting-plane-to-point-cloud.html
+float BlenderTessellatorP2T::FindLargestMatrixElem( const aiMatrix3x3& mtx ) const
+{
+ float result = 0.0f;
+
+ for ( unsigned int x = 0; x < 3; ++x )
+ {
+ for ( unsigned int y = 0; y < 3; ++y )
+ {
+ result = p2tMax( std::fabs( mtx[ x ][ y ] ), result );
+ }
+ }
+
+ return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Apparently Assimp doesn't have matrix scaling
+aiMatrix3x3 BlenderTessellatorP2T::ScaleMatrix( const aiMatrix3x3& mtx, float scale ) const
+{
+ aiMatrix3x3 result;
+
+ for ( unsigned int x = 0; x < 3; ++x )
+ {
+ for ( unsigned int y = 0; y < 3; ++y )
+ {
+ result[ x ][ y ] = mtx[ x ][ y ] * scale;
+ }
+ }
+
+ return result;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// Adapted from: http://missingbytes.blogspot.co.uk/2012/06/fitting-plane-to-point-cloud.html
+aiVector3D BlenderTessellatorP2T::GetEigenVectorFromLargestEigenValue( const aiMatrix3x3& mtx ) const
+{
+ const float scale = FindLargestMatrixElem( mtx );
+ aiMatrix3x3 mc = ScaleMatrix( mtx, 1.0f / scale );
+ mc = mc * mc * mc;
+
+ aiVector3D v( 1.0f );
+ aiVector3D lastV = v;
+ for ( int i = 0; i < 100; ++i )
+ {
+ v = mc * v;
+ v.Normalize( );
+ if ( ( v - lastV ).SquareLength( ) < 1e-16f )
+ {
+ break;
+ }
+ lastV = v;
+ }
+ return v;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Adapted from: http://missingbytes.blogspot.co.uk/2012/06/fitting-plane-to-point-cloud.html
+PlaneP2T BlenderTessellatorP2T::FindLLSQPlane( const std::vector< PointP2T >& points ) const
+{
+ PlaneP2T result;
+
+ aiVector3D sum( 0.0 );
+ for ( size_t i = 0; i < points.size( ); ++i )
+ {
+ sum += points[ i ].point3D;
+ }
+ result.centre = sum * (ai_real)( 1.0 / points.size( ) );
+
+ ai_real sumXX = 0.0;
+ ai_real sumXY = 0.0;
+ ai_real sumXZ = 0.0;
+ ai_real sumYY = 0.0;
+ ai_real sumYZ = 0.0;
+ ai_real sumZZ = 0.0;
+ for ( size_t i = 0; i < points.size( ); ++i )
+ {
+ aiVector3D offset = points[ i ].point3D - result.centre;
+ sumXX += offset.x * offset.x;
+ sumXY += offset.x * offset.y;
+ sumXZ += offset.x * offset.z;
+ sumYY += offset.y * offset.y;
+ sumYZ += offset.y * offset.z;
+ sumZZ += offset.z * offset.z;
+ }
+
+ aiMatrix3x3 mtx( sumXX, sumXY, sumXZ, sumXY, sumYY, sumYZ, sumXZ, sumYZ, sumZZ );
+
+ const ai_real det = mtx.Determinant( );
+ if ( det == 0.0f )
+ {
+ result.normal = aiVector3D( 0.0f );
+ }
+ else
+ {
+ aiMatrix3x3 invMtx = mtx;
+ invMtx.Inverse( );
+ result.normal = GetEigenVectorFromLargestEigenValue( invMtx );
+ }
+
+ return result;
+}
+
+#endif // ASSIMP_BLEND_WITH_POLY_2_TRI
+
+#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderTessellator.h b/libs/assimp/code/AssetLib/Blender/BlenderTessellator.h
new file mode 100644
index 0000000..0d0ba32
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Blender/BlenderTessellator.h
@@ -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.
+
+----------------------------------------------------------------------
+*/
+
+/** @file BlenderTessellator.h
+ * @brief A simple tessellation wrapper
+ */
+#ifndef INCLUDED_AI_BLEND_TESSELLATOR_H
+#define INCLUDED_AI_BLEND_TESSELLATOR_H
+
+// Use these to toggle between GLU Tessellate or poly2tri
+// Note (acg) keep GLU Tessellate disabled by default - if it is turned on,
+// assimp needs to be linked against GLU, which is currently not yet
+// made configurable in CMake and potentially not wanted by most users
+// as it requires a Gl environment.
+#ifndef ASSIMP_BLEND_WITH_GLU_TESSELLATE
+# define ASSIMP_BLEND_WITH_GLU_TESSELLATE 0
+#endif
+
+#ifndef ASSIMP_BLEND_WITH_POLY_2_TRI
+# define ASSIMP_BLEND_WITH_POLY_2_TRI 1
+#endif
+
+#include <assimp/LogAux.h>
+
+#if ASSIMP_BLEND_WITH_GLU_TESSELLATE
+
+#if defined( WIN32 ) || defined( _WIN32 ) || defined( _MSC_VER )
+#include <windows.h>
+#endif
+#include <GL/glu.h>
+
+namespace Assimp
+{
+ class BlenderBMeshConverter;
+
+ // TinyFormatter.h
+ namespace Formatter
+ {
+ template < typename T,typename TR, typename A > class basic_formatter;
+ typedef class basic_formatter< char, std::char_traits< char >, std::allocator< char > > format;
+ }
+
+ // BlenderScene.h
+ namespace Blender
+ {
+ struct MLoop;
+ struct MVert;
+
+ struct VertexGL
+ {
+ GLdouble X;
+ GLdouble Y;
+ GLdouble Z;
+ int index;
+ int magic;
+
+ VertexGL( GLdouble X, GLdouble Y, GLdouble Z, int index, int magic ): X( X ), Y( Y ), Z( Z ), index( index ), magic( magic ) { }
+ };
+
+ struct DrawCallGL
+ {
+ GLenum drawMode;
+ int baseVertex;
+ int vertexCount;
+
+ DrawCallGL( GLenum drawMode, int baseVertex ): drawMode( drawMode ), baseVertex( baseVertex ), vertexCount( 0 ) { }
+ };
+
+ struct TessDataGL
+ {
+ std::vector< DrawCallGL > drawCalls;
+ std::vector< VertexGL > vertices;
+ };
+ }
+
+ class BlenderTessellatorGL: public LogFunctions< BlenderTessellatorGL >
+ {
+ public:
+ BlenderTessellatorGL( BlenderBMeshConverter& converter );
+ ~BlenderTessellatorGL( );
+
+ void Tessellate( const Blender::MLoop* polyLoop, int vertexCount, const std::vector< Blender::MVert >& vertices );
+
+ private:
+ void AssertVertexCount( int vertexCount );
+ void GenerateLoopVerts( std::vector< Blender::VertexGL >& polyLoopGL, const Blender::MLoop* polyLoop, int vertexCount, const std::vector< Blender::MVert >& vertices );
+ void Tesssellate( std::vector< Blender::VertexGL >& polyLoopGL, Blender::TessDataGL& tessData );
+ void TriangulateDrawCalls( const Blender::TessDataGL& tessData );
+ void MakeFacesFromTris( const Blender::VertexGL* vertices, int vertexCount );
+ void MakeFacesFromTriStrip( const Blender::VertexGL* vertices, int vertexCount );
+ void MakeFacesFromTriFan( const Blender::VertexGL* vertices, int vertexCount );
+
+ static void TessellateBegin( GLenum drawModeGL, void* userData );
+ static void TessellateEnd( void* userData );
+ static void TessellateVertex( const void* vtxData, void* userData );
+ static void TessellateCombine( const GLdouble intersection[ 3 ], const GLdouble* [ 4 ], const GLfloat [ 4 ], GLdouble** out, void* userData );
+ static void TessellateEdgeFlag( GLboolean edgeFlag, void* userData );
+ static void TessellateError( GLenum errorCode, void* userData );
+
+ BlenderBMeshConverter* converter;
+ };
+} // end of namespace Assimp
+
+#endif // ASSIMP_BLEND_WITH_GLU_TESSELLATE
+
+#if ASSIMP_BLEND_WITH_POLY_2_TRI
+
+#ifdef ASSIMP_USE_HUNTER
+# include <poly2tri/poly2tri.h>
+#else
+# include "../contrib/poly2tri/poly2tri/poly2tri.h"
+#endif
+
+namespace Assimp
+{
+ class BlenderBMeshConverter;
+
+ // TinyFormatter.h
+ namespace Formatter
+ {
+ template < typename T,typename TR, typename A > class basic_formatter;
+ typedef class basic_formatter< char, std::char_traits< char >, std::allocator< char > > format;
+ }
+
+ // BlenderScene.h
+ namespace Blender
+ {
+ struct MLoop;
+ struct MVert;
+
+ struct PointP2T
+ {
+ aiVector3D point3D;
+ p2t::Point point2D;
+ int magic;
+ int index;
+ };
+
+ struct PlaneP2T
+ {
+ aiVector3D centre;
+ aiVector3D normal;
+ };
+ }
+
+ class BlenderTessellatorP2T: public LogFunctions< BlenderTessellatorP2T >
+ {
+ public:
+ BlenderTessellatorP2T( BlenderBMeshConverter& converter );
+ ~BlenderTessellatorP2T( );
+
+ void Tessellate( const Blender::MLoop* polyLoop, int vertexCount, const std::vector< Blender::MVert >& vertices );
+
+ private:
+ void AssertVertexCount( int vertexCount );
+ void Copy3DVertices( const Blender::MLoop* polyLoop, int vertexCount, const std::vector< Blender::MVert >& vertices, std::vector< Blender::PointP2T >& targetVertices ) const;
+ aiMatrix4x4 GeneratePointTransformMatrix( const Blender::PlaneP2T& plane ) const;
+ void TransformAndFlattenVectices( const aiMatrix4x4& transform, std::vector< Blender::PointP2T >& vertices ) const;
+ void ReferencePoints( std::vector< Blender::PointP2T >& points, std::vector< p2t::Point* >& pointRefs ) const;
+ inline Blender::PointP2T& GetActualPointStructure( p2t::Point& point ) const;
+ void MakeFacesFromTriangles( std::vector< p2t::Triangle* >& triangles ) const;
+
+ // Adapted from: http://missingbytes.blogspot.co.uk/2012/06/fitting-plane-to-point-cloud.html
+ float FindLargestMatrixElem( const aiMatrix3x3& mtx ) const;
+ aiMatrix3x3 ScaleMatrix( const aiMatrix3x3& mtx, float scale ) const;
+ aiVector3D GetEigenVectorFromLargestEigenValue( const aiMatrix3x3& mtx ) const;
+ Blender::PlaneP2T FindLLSQPlane( const std::vector< Blender::PointP2T >& points ) const;
+
+ BlenderBMeshConverter* converter;
+ };
+} // end of namespace Assimp
+
+#endif // ASSIMP_BLEND_WITH_POLY_2_TRI
+
+#endif // INCLUDED_AI_BLEND_TESSELLATOR_H