diff options
author | sanine <sanine.not@pm.me> | 2022-04-16 11:55:09 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-04-16 11:55:09 -0500 |
commit | db81b925d776103326128bf629cbdda576a223e7 (patch) | |
tree | 58bea8155c686733310009f6bed7363f91fbeb9d /libs/assimp/code/AssetLib/Blender | |
parent | 55860037b14fb3893ba21cf2654c83d349cc1082 (diff) |
move 3rd-party librarys into libs/ and add built-in honeysuckle
Diffstat (limited to 'libs/assimp/code/AssetLib/Blender')
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 |