summaryrefslogtreecommitdiff
path: root/libs/assimp/code/AssetLib/Irr
diff options
context:
space:
mode:
Diffstat (limited to 'libs/assimp/code/AssetLib/Irr')
-rw-r--r--libs/assimp/code/AssetLib/Irr/IRRLoader.cpp1359
-rw-r--r--libs/assimp/code/AssetLib/Irr/IRRLoader.h284
-rw-r--r--libs/assimp/code/AssetLib/Irr/IRRMeshLoader.cpp502
-rw-r--r--libs/assimp/code/AssetLib/Irr/IRRMeshLoader.h94
-rw-r--r--libs/assimp/code/AssetLib/Irr/IRRShared.cpp387
-rw-r--r--libs/assimp/code/AssetLib/Irr/IRRShared.h119
6 files changed, 2745 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/Irr/IRRLoader.cpp b/libs/assimp/code/AssetLib/Irr/IRRLoader.cpp
new file mode 100644
index 0000000..0061634
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Irr/IRRLoader.cpp
@@ -0,0 +1,1359 @@
+/*
+---------------------------------------------------------------------------
+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 IRRLoader.cpp
+ * @brief Implementation of the Irr importer class
+ */
+
+#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER
+
+#include "AssetLib/Irr/IRRLoader.h"
+#include "Common/Importer.h"
+
+#include <assimp/GenericProperty.h>
+#include <assimp/MathFunctions.h>
+#include <assimp/ParsingUtils.h>
+#include <assimp/SceneCombiner.h>
+#include <assimp/StandardShapes.h>
+#include <assimp/fast_atof.h>
+#include <assimp/importerdesc.h>
+#include <assimp/material.h>
+#include <assimp/mesh.h>
+#include <assimp/postprocess.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/IOSystem.hpp>
+
+#include <memory>
+
+using namespace Assimp;
+
+static const aiImporterDesc desc = {
+ "Irrlicht Scene Reader",
+ "",
+ "",
+ "http://irrlicht.sourceforge.net/",
+ aiImporterFlags_SupportTextFlavour,
+ 0,
+ 0,
+ 0,
+ 0,
+ "irr xml"
+};
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+IRRImporter::IRRImporter() :
+ fps(), configSpeedFlag() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+IRRImporter::~IRRImporter() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool IRRImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+ static const char *tokens[] = { "irr_scene" };
+ return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiImporterDesc *IRRImporter::GetInfo() const {
+ return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::SetupProperties(const Importer *pImp) {
+ // read the output frame rate of all node animation channels
+ fps = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_IRR_ANIM_FPS, 100);
+ if (fps < 10.) {
+ ASSIMP_LOG_ERROR("IRR: Invalid FPS configuration");
+ fps = 100;
+ }
+
+ // AI_CONFIG_FAVOUR_SPEED
+ configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED, 0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a mesh that consists of a single squad (a side of a skybox)
+aiMesh *IRRImporter::BuildSingleQuadMesh(const SkyboxVertex &v1,
+ const SkyboxVertex &v2,
+ const SkyboxVertex &v3,
+ const SkyboxVertex &v4) {
+ // allocate and prepare the mesh
+ aiMesh *out = new aiMesh();
+
+ out->mPrimitiveTypes = aiPrimitiveType_POLYGON;
+ out->mNumFaces = 1;
+
+ // build the face
+ out->mFaces = new aiFace[1];
+ aiFace &face = out->mFaces[0];
+
+ face.mNumIndices = 4;
+ face.mIndices = new unsigned int[4];
+ for (unsigned int i = 0; i < 4; ++i)
+ face.mIndices[i] = i;
+
+ out->mNumVertices = 4;
+
+ // copy vertex positions
+ aiVector3D *vec = out->mVertices = new aiVector3D[4];
+ *vec++ = v1.position;
+ *vec++ = v2.position;
+ *vec++ = v3.position;
+ *vec = v4.position;
+
+ // copy vertex normals
+ vec = out->mNormals = new aiVector3D[4];
+ *vec++ = v1.normal;
+ *vec++ = v2.normal;
+ *vec++ = v3.normal;
+ *vec = v4.normal;
+
+ // copy texture coordinates
+ vec = out->mTextureCoords[0] = new aiVector3D[4];
+ *vec++ = v1.uv;
+ *vec++ = v2.uv;
+ *vec++ = v3.uv;
+ *vec = v4.uv;
+ return out;
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::BuildSkybox(std::vector<aiMesh *> &meshes, std::vector<aiMaterial *> materials) {
+ // Update the material of the skybox - replace the name and disable shading for skyboxes.
+ for (unsigned int i = 0; i < 6; ++i) {
+ aiMaterial *out = (aiMaterial *)(*(materials.end() - (6 - i)));
+
+ aiString s;
+ s.length = ::ai_snprintf(s.data, MAXLEN, "SkyboxSide_%u", i);
+ out->AddProperty(&s, AI_MATKEY_NAME);
+
+ int shading = aiShadingMode_NoShading;
+ out->AddProperty(&shading, 1, AI_MATKEY_SHADING_MODEL);
+ }
+
+ // Skyboxes are much more difficult. They are represented
+ // by six single planes with different textures, so we'll
+ // need to build six meshes.
+
+ const ai_real l = 10.0; // the size used by Irrlicht
+
+ // FRONT SIDE
+ meshes.push_back(BuildSingleQuadMesh(
+ SkyboxVertex(-l, -l, -l, 0, 0, 1, 1.0, 1.0),
+ SkyboxVertex(l, -l, -l, 0, 0, 1, 0.0, 1.0),
+ SkyboxVertex(l, l, -l, 0, 0, 1, 0.0, 0.0),
+ SkyboxVertex(-l, l, -l, 0, 0, 1, 1.0, 0.0)));
+ meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 6u);
+
+ // LEFT SIDE
+ meshes.push_back(BuildSingleQuadMesh(
+ SkyboxVertex(l, -l, -l, -1, 0, 0, 1.0, 1.0),
+ SkyboxVertex(l, -l, l, -1, 0, 0, 0.0, 1.0),
+ SkyboxVertex(l, l, l, -1, 0, 0, 0.0, 0.0),
+ SkyboxVertex(l, l, -l, -1, 0, 0, 1.0, 0.0)));
+ meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 5u);
+
+ // BACK SIDE
+ meshes.push_back(BuildSingleQuadMesh(
+ SkyboxVertex(l, -l, l, 0, 0, -1, 1.0, 1.0),
+ SkyboxVertex(-l, -l, l, 0, 0, -1, 0.0, 1.0),
+ SkyboxVertex(-l, l, l, 0, 0, -1, 0.0, 0.0),
+ SkyboxVertex(l, l, l, 0, 0, -1, 1.0, 0.0)));
+ meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 4u);
+
+ // RIGHT SIDE
+ meshes.push_back(BuildSingleQuadMesh(
+ SkyboxVertex(-l, -l, l, 1, 0, 0, 1.0, 1.0),
+ SkyboxVertex(-l, -l, -l, 1, 0, 0, 0.0, 1.0),
+ SkyboxVertex(-l, l, -l, 1, 0, 0, 0.0, 0.0),
+ SkyboxVertex(-l, l, l, 1, 0, 0, 1.0, 0.0)));
+ meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 3u);
+
+ // TOP SIDE
+ meshes.push_back(BuildSingleQuadMesh(
+ SkyboxVertex(l, l, -l, 0, -1, 0, 1.0, 1.0),
+ SkyboxVertex(l, l, l, 0, -1, 0, 0.0, 1.0),
+ SkyboxVertex(-l, l, l, 0, -1, 0, 0.0, 0.0),
+ SkyboxVertex(-l, l, -l, 0, -1, 0, 1.0, 0.0)));
+ meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 2u);
+
+ // BOTTOM SIDE
+ meshes.push_back(BuildSingleQuadMesh(
+ SkyboxVertex(l, -l, l, 0, 1, 0, 0.0, 0.0),
+ SkyboxVertex(l, -l, -l, 0, 1, 0, 1.0, 0.0),
+ SkyboxVertex(-l, -l, -l, 0, 1, 0, 1.0, 1.0),
+ SkyboxVertex(-l, -l, l, 0, 1, 0, 0.0, 1.0)));
+ meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 1u);
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::CopyMaterial(std::vector<aiMaterial *> &materials,
+ std::vector<std::pair<aiMaterial *, unsigned int>> &inmaterials,
+ unsigned int &defMatIdx,
+ aiMesh *mesh) {
+ if (inmaterials.empty()) {
+ // Do we have a default material? If not we need to create one
+ if (UINT_MAX == defMatIdx) {
+ defMatIdx = (unsigned int)materials.size();
+ //TODO: add this materials to someone?
+ /*aiMaterial* mat = new aiMaterial();
+
+ aiString s;
+ s.Set(AI_DEFAULT_MATERIAL_NAME);
+ mat->AddProperty(&s,AI_MATKEY_NAME);
+
+ aiColor3D c(0.6f,0.6f,0.6f);
+ mat->AddProperty(&c,1,AI_MATKEY_COLOR_DIFFUSE);*/
+ }
+ mesh->mMaterialIndex = defMatIdx;
+ return;
+ } else if (inmaterials.size() > 1) {
+ ASSIMP_LOG_INFO("IRR: Skipping additional materials");
+ }
+
+ mesh->mMaterialIndex = (unsigned int)materials.size();
+ materials.push_back(inmaterials[0].first);
+}
+
+// ------------------------------------------------------------------------------------------------
+inline int ClampSpline(int idx, int size) {
+ return (idx < 0 ? size + idx : (idx >= size ? idx - size : idx));
+}
+
+// ------------------------------------------------------------------------------------------------
+inline void FindSuitableMultiple(int &angle) {
+ if (angle < 3)
+ angle = 3;
+ else if (angle < 10)
+ angle = 10;
+ else if (angle < 20)
+ angle = 20;
+ else if (angle < 30)
+ angle = 30;
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::ComputeAnimations(Node *root, aiNode *real, std::vector<aiNodeAnim *> &anims) {
+ ai_assert(nullptr != root && nullptr != real);
+
+ // XXX totally WIP - doesn't produce proper results, need to evaluate
+ // whether there's any use for Irrlicht's proprietary scene format
+ // outside Irrlicht ...
+ // This also applies to the above function of FindSuitableMultiple and ClampSpline which are
+ // solely used in this function
+
+ if (root->animators.empty()) {
+ return;
+ }
+ unsigned int total(0);
+ for (std::list<Animator>::iterator it = root->animators.begin(); it != root->animators.end(); ++it) {
+ if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER) {
+ ASSIMP_LOG_WARN("IRR: Skipping unknown or unsupported animator");
+ continue;
+ }
+ ++total;
+ }
+ if (!total) {
+ return;
+ } else if (1 == total) {
+ ASSIMP_LOG_WARN("IRR: Adding dummy nodes to simulate multiple animators");
+ }
+
+ // NOTE: 1 tick == i millisecond
+
+ unsigned int cur = 0;
+ for (std::list<Animator>::iterator it = root->animators.begin();
+ it != root->animators.end(); ++it) {
+ if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER) continue;
+
+ Animator &in = *it;
+ aiNodeAnim *anim = new aiNodeAnim();
+
+ if (cur != total - 1) {
+ // Build a new name - a prefix instead of a suffix because it is
+ // easier to check against
+ anim->mNodeName.length = ::ai_snprintf(anim->mNodeName.data, MAXLEN,
+ "$INST_DUMMY_%i_%s", total - 1,
+ (root->name.length() ? root->name.c_str() : ""));
+
+ // we'll also need to insert a dummy in the node hierarchy.
+ aiNode *dummy = new aiNode();
+
+ for (unsigned int i = 0; i < real->mParent->mNumChildren; ++i)
+ if (real->mParent->mChildren[i] == real)
+ real->mParent->mChildren[i] = dummy;
+
+ dummy->mParent = real->mParent;
+ dummy->mName = anim->mNodeName;
+
+ dummy->mNumChildren = 1;
+ dummy->mChildren = new aiNode *[dummy->mNumChildren];
+ dummy->mChildren[0] = real;
+
+ // the transformation matrix of the dummy node is the identity
+
+ real->mParent = dummy;
+ } else
+ anim->mNodeName.Set(root->name);
+ ++cur;
+
+ switch (in.type) {
+ case Animator::ROTATION: {
+ // -----------------------------------------------------
+ // find out how long a full rotation will take
+ // This is the least common multiple of 360.f and all
+ // three euler angles. Although we'll surely find a
+ // possible multiple (haha) it could be somewhat large
+ // for our purposes. So we need to modify the angles
+ // here in order to get good results.
+ // -----------------------------------------------------
+ int angles[3];
+ angles[0] = (int)(in.direction.x * 100);
+ angles[1] = (int)(in.direction.y * 100);
+ angles[2] = (int)(in.direction.z * 100);
+
+ angles[0] %= 360;
+ angles[1] %= 360;
+ angles[2] %= 360;
+
+ if ((angles[0] * angles[1]) != 0 && (angles[1] * angles[2]) != 0) {
+ FindSuitableMultiple(angles[0]);
+ FindSuitableMultiple(angles[1]);
+ FindSuitableMultiple(angles[2]);
+ }
+
+ int lcm = 360;
+
+ if (angles[0])
+ lcm = Math::lcm(lcm, angles[0]);
+
+ if (angles[1])
+ lcm = Math::lcm(lcm, angles[1]);
+
+ if (angles[2])
+ lcm = Math::lcm(lcm, angles[2]);
+
+ if (360 == lcm)
+ break;
+
+
+ // find out how many time units we'll need for the finest
+ // track (in seconds) - this defines the number of output
+ // keys (fps * seconds)
+ float max = 0.f;
+ if (angles[0])
+ max = (float)lcm / angles[0];
+ if (angles[1])
+ max = std::max(max, (float)lcm / angles[1]);
+ if (angles[2])
+ max = std::max(max, (float)lcm / angles[2]);
+
+ anim->mNumRotationKeys = (unsigned int)(max * fps);
+ anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
+
+ // begin with a zero angle
+ aiVector3D angle;
+ for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
+ // build the quaternion for the given euler angles
+ aiQuatKey &q = anim->mRotationKeys[i];
+
+ q.mValue = aiQuaternion(angle.x, angle.y, angle.z);
+ q.mTime = (double)i;
+
+ // increase the angle
+ angle += in.direction;
+ }
+
+ // This animation is repeated and repeated ...
+ anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
+ } break;
+
+ case Animator::FLY_CIRCLE: {
+ // -----------------------------------------------------
+ // Find out how much time we'll need to perform a
+ // full circle.
+ // -----------------------------------------------------
+ const double seconds = (1. / in.speed) / 1000.;
+ const double tdelta = 1000. / fps;
+
+ anim->mNumPositionKeys = (unsigned int)(fps * seconds);
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+
+ // from Irrlicht, what else should we do than copying it?
+ aiVector3D vecU, vecV;
+ if (in.direction.y) {
+ vecV = aiVector3D(50, 0, 0) ^ in.direction;
+ } else
+ vecV = aiVector3D(0, 50, 00) ^ in.direction;
+ vecV.Normalize();
+ vecU = (vecV ^ in.direction).Normalize();
+
+ // build the output keys
+ for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
+ aiVectorKey &key = anim->mPositionKeys[i];
+ key.mTime = i * tdelta;
+
+ const ai_real t = (ai_real)(in.speed * key.mTime);
+ key.mValue = in.circleCenter + in.circleRadius * ((vecU * std::cos(t)) + (vecV * std::sin(t)));
+ }
+
+ // This animation is repeated and repeated ...
+ anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
+ } break;
+
+ case Animator::FLY_STRAIGHT: {
+ anim->mPostState = anim->mPreState = (in.loop ? aiAnimBehaviour_REPEAT : aiAnimBehaviour_CONSTANT);
+ const double seconds = in.timeForWay / 1000.;
+ const double tdelta = 1000. / fps;
+
+ anim->mNumPositionKeys = (unsigned int)(fps * seconds);
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+
+ aiVector3D diff = in.direction - in.circleCenter;
+ const ai_real lengthOfWay = diff.Length();
+ diff.Normalize();
+
+ const double timeFactor = lengthOfWay / in.timeForWay;
+
+ // build the output keys
+ for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
+ aiVectorKey &key = anim->mPositionKeys[i];
+ key.mTime = i * tdelta;
+ key.mValue = in.circleCenter + diff * ai_real(timeFactor * key.mTime);
+ }
+ } break;
+
+ case Animator::FOLLOW_SPLINE: {
+ // repeat outside the defined time range
+ anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
+ const int size = (int)in.splineKeys.size();
+ if (!size) {
+ // We have no point in the spline. That's bad. Really bad.
+ ASSIMP_LOG_WARN("IRR: Spline animators with no points defined");
+
+ delete anim;
+ anim = nullptr;
+ break;
+ } else if (size == 1) {
+ // We have just one point in the spline so we don't need the full calculation
+ anim->mNumPositionKeys = 1;
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+
+ anim->mPositionKeys[0].mValue = in.splineKeys[0].mValue;
+ anim->mPositionKeys[0].mTime = 0.f;
+ break;
+ }
+
+ unsigned int ticksPerFull = 15;
+ anim->mNumPositionKeys = (unsigned int)(ticksPerFull * fps);
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+
+ for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
+ aiVectorKey &key = anim->mPositionKeys[i];
+
+ const ai_real dt = (i * in.speed * ai_real(0.001));
+ const ai_real u = dt - std::floor(dt);
+ const int idx = (int)std::floor(dt) % size;
+
+ // get the 4 current points to evaluate the spline
+ const aiVector3D &p0 = in.splineKeys[ClampSpline(idx - 1, size)].mValue;
+ const aiVector3D &p1 = in.splineKeys[ClampSpline(idx + 0, size)].mValue;
+ const aiVector3D &p2 = in.splineKeys[ClampSpline(idx + 1, size)].mValue;
+ const aiVector3D &p3 = in.splineKeys[ClampSpline(idx + 2, size)].mValue;
+
+ // compute polynomials
+ const ai_real u2 = u * u;
+ const ai_real u3 = u2 * 2;
+
+ const ai_real h1 = ai_real(2.0) * u3 - ai_real(3.0) * u2 + ai_real(1.0);
+ const ai_real h2 = ai_real(-2.0) * u3 + ai_real(3.0) * u3;
+ const ai_real h3 = u3 - ai_real(2.0) * u3;
+ const ai_real h4 = u3 - u2;
+
+ // compute the spline tangents
+ const aiVector3D t1 = (p2 - p0) * in.tightness;
+ aiVector3D t2 = (p3 - p1) * in.tightness;
+
+ // and use them to get the interpolated point
+ t2 = (h1 * p1 + p2 * h2 + t1 * h3 + h4 * t2);
+
+ // build a simple translation matrix from it
+ key.mValue = t2;
+ key.mTime = (double)i;
+ }
+ } break;
+ default:
+ // UNKNOWN , OTHER
+ break;
+ };
+ if (anim) {
+ anims.push_back(anim);
+ ++total;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// This function is maybe more generic than we'd need it here
+void SetupMapping(aiMaterial *mat, aiTextureMapping mode, const aiVector3D &axis = aiVector3D(0.f, 0.f, -1.f)) {
+ if (nullptr == mat) {
+ return;
+ }
+
+ // Check whether there are texture properties defined - setup
+ // the desired texture mapping mode for all of them and ignore
+ // all UV settings we might encounter. WE HAVE NO UVS!
+
+ std::vector<aiMaterialProperty *> p;
+ p.reserve(mat->mNumProperties + 1);
+
+ for (unsigned int i = 0; i < mat->mNumProperties; ++i) {
+ aiMaterialProperty *prop = mat->mProperties[i];
+ if (!::strcmp(prop->mKey.data, "$tex.file")) {
+ // Setup the mapping key
+ aiMaterialProperty *m = new aiMaterialProperty();
+ m->mKey.Set("$tex.mapping");
+ m->mIndex = prop->mIndex;
+ m->mSemantic = prop->mSemantic;
+ m->mType = aiPTI_Integer;
+
+ m->mDataLength = 4;
+ m->mData = new char[4];
+ *((int *)m->mData) = mode;
+
+ p.push_back(prop);
+ p.push_back(m);
+
+ // Setup the mapping axis
+ if (mode == aiTextureMapping_CYLINDER || mode == aiTextureMapping_PLANE || mode == aiTextureMapping_SPHERE) {
+ m = new aiMaterialProperty();
+ m->mKey.Set("$tex.mapaxis");
+ m->mIndex = prop->mIndex;
+ m->mSemantic = prop->mSemantic;
+ m->mType = aiPTI_Float;
+
+ m->mDataLength = 12;
+ m->mData = new char[12];
+ *((aiVector3D *)m->mData) = axis;
+ p.push_back(m);
+ }
+ } else if (!::strcmp(prop->mKey.data, "$tex.uvwsrc")) {
+ delete mat->mProperties[i];
+ } else
+ p.push_back(prop);
+ }
+
+ if (p.empty()) return;
+
+ // rebuild the output array
+ if (p.size() > mat->mNumAllocated) {
+ delete[] mat->mProperties;
+ mat->mProperties = new aiMaterialProperty *[p.size() * 2];
+
+ mat->mNumAllocated = static_cast<unsigned int>(p.size() * 2);
+ }
+ mat->mNumProperties = (unsigned int)p.size();
+ ::memcpy(mat->mProperties, &p[0], sizeof(void *) * mat->mNumProperties);
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::GenerateGraph(Node *root, aiNode *rootOut, aiScene *scene,
+ BatchLoader &batch,
+ std::vector<aiMesh *> &meshes,
+ std::vector<aiNodeAnim *> &anims,
+ std::vector<AttachmentInfo> &attach,
+ std::vector<aiMaterial *> &materials,
+ unsigned int &defMatIdx) {
+ unsigned int oldMeshSize = (unsigned int)meshes.size();
+ //unsigned int meshTrafoAssign = 0;
+
+ // Now determine the type of the node
+ switch (root->type) {
+ case Node::ANIMMESH:
+ case Node::MESH: {
+ if (!root->meshPath.length())
+ break;
+
+ // Get the loaded mesh from the scene and add it to
+ // the list of all scenes to be attached to the
+ // graph we're currently building
+ aiScene *localScene = batch.GetImport(root->id);
+ if (!localScene) {
+ ASSIMP_LOG_ERROR("IRR: Unable to load external file: ", root->meshPath);
+ break;
+ }
+ attach.push_back(AttachmentInfo(localScene, rootOut));
+
+ // Now combine the material we've loaded for this mesh
+ // with the real materials we got from the file. As we
+ // don't execute any pp-steps on the file, the numbers
+ // should be equal. If they are not, we can impossibly
+ // do this ...
+ if (root->materials.size() != (unsigned int)localScene->mNumMaterials) {
+ ASSIMP_LOG_WARN("IRR: Failed to match imported materials "
+ "with the materials found in the IRR scene file");
+
+ break;
+ }
+ for (unsigned int i = 0; i < localScene->mNumMaterials; ++i) {
+ // Delete the old material, we don't need it anymore
+ delete localScene->mMaterials[i];
+
+ std::pair<aiMaterial *, unsigned int> &src = root->materials[i];
+ localScene->mMaterials[i] = src.first;
+ }
+
+ // NOTE: Each mesh should have exactly one material assigned,
+ // but we do it in a separate loop if this behavior changes
+ // in future.
+ for (unsigned int i = 0; i < localScene->mNumMeshes; ++i) {
+ // Process material flags
+ aiMesh *mesh = localScene->mMeshes[i];
+
+ // If "trans_vertex_alpha" mode is enabled, search all vertex colors
+ // and check whether they have a common alpha value. This is quite
+ // often the case so we can simply extract it to a shared oacity
+ // value.
+ std::pair<aiMaterial *, unsigned int> &src = root->materials[mesh->mMaterialIndex];
+ aiMaterial *mat = (aiMaterial *)src.first;
+
+ if (mesh->HasVertexColors(0) && src.second & AI_IRRMESH_MAT_trans_vertex_alpha) {
+ bool bdo = true;
+ for (unsigned int a = 1; a < mesh->mNumVertices; ++a) {
+
+ if (mesh->mColors[0][a].a != mesh->mColors[0][a - 1].a) {
+ bdo = false;
+ break;
+ }
+ }
+ if (bdo) {
+ ASSIMP_LOG_INFO("IRR: Replacing mesh vertex alpha with common opacity");
+
+ for (unsigned int a = 0; a < mesh->mNumVertices; ++a)
+ mesh->mColors[0][a].a = 1.f;
+
+ mat->AddProperty(&mesh->mColors[0][0].a, 1, AI_MATKEY_OPACITY);
+ }
+ }
+
+ // If we have a second texture coordinate set and a second texture
+ // (either light-map, normal-map, 2layered material) we need to
+ // setup the correct UV index for it. The texture can either
+ // be diffuse (light-map & 2layer) or a normal map (normal & parallax)
+ if (mesh->HasTextureCoords(1)) {
+
+ int idx = 1;
+ if (src.second & (AI_IRRMESH_MAT_solid_2layer | AI_IRRMESH_MAT_lightmap)) {
+ mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_DIFFUSE(0));
+ } else if (src.second & AI_IRRMESH_MAT_normalmap_solid) {
+ mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_NORMALS(0));
+ }
+ }
+ }
+ } break;
+
+ case Node::LIGHT:
+ case Node::CAMERA:
+
+ // We're already finished with lights and cameras
+ break;
+
+ case Node::SPHERE: {
+ // Generate the sphere model. Our input parameter to
+ // the sphere generation algorithm is the number of
+ // subdivisions of each triangle - but here we have
+ // the number of polygons on a specific axis. Just
+ // use some hard-coded limits to approximate this ...
+ unsigned int mul = root->spherePolyCountX * root->spherePolyCountY;
+ if (mul < 100)
+ mul = 2;
+ else if (mul < 300)
+ mul = 3;
+ else
+ mul = 4;
+
+ meshes.push_back(StandardShapes::MakeMesh(mul,
+ &StandardShapes::MakeSphere));
+
+ // Adjust scaling
+ root->scaling *= root->sphereRadius / 2;
+
+ // Copy one output material
+ CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
+
+ // Now adjust this output material - if there is a first texture
+ // set, setup spherical UV mapping around the Y axis.
+ SetupMapping((aiMaterial *)materials.back(), aiTextureMapping_SPHERE);
+ } break;
+
+ case Node::CUBE: {
+ // Generate an unit cube first
+ meshes.push_back(StandardShapes::MakeMesh(
+ &StandardShapes::MakeHexahedron));
+
+ // Adjust scaling
+ root->scaling *= root->sphereRadius;
+
+ // Copy one output material
+ CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
+
+ // Now adjust this output material - if there is a first texture
+ // set, setup cubic UV mapping
+ SetupMapping((aiMaterial *)materials.back(), aiTextureMapping_BOX);
+ } break;
+
+ case Node::SKYBOX: {
+ // A sky-box is defined by six materials
+ if (root->materials.size() < 6) {
+ ASSIMP_LOG_ERROR("IRR: There should be six materials for a skybox");
+ break;
+ }
+
+ // copy those materials and generate 6 meshes for our new sky-box
+ materials.reserve(materials.size() + 6);
+ for (unsigned int i = 0; i < 6; ++i)
+ materials.insert(materials.end(), root->materials[i].first);
+
+ BuildSkybox(meshes, materials);
+
+ // *************************************************************
+ // Skyboxes will require a different code path for rendering,
+ // so there must be a way for the user to add special support
+ // for IRR skyboxes. We add a 'IRR.SkyBox_' prefix to the node.
+ // *************************************************************
+ root->name = "IRR.SkyBox_" + root->name;
+ ASSIMP_LOG_INFO("IRR: Loading skybox, this will "
+ "require special handling to be displayed correctly");
+ } break;
+
+ case Node::TERRAIN: {
+ // to support terrains, we'd need to have a texture decoder
+ ASSIMP_LOG_ERROR("IRR: Unsupported node - TERRAIN");
+ } break;
+ default:
+ // DUMMY
+ break;
+ };
+
+ // Check whether we added a mesh (or more than one ...). In this case
+ // we'll also need to attach it to the node
+ if (oldMeshSize != (unsigned int)meshes.size()) {
+
+ rootOut->mNumMeshes = (unsigned int)meshes.size() - oldMeshSize;
+ rootOut->mMeshes = new unsigned int[rootOut->mNumMeshes];
+
+ for (unsigned int a = 0; a < rootOut->mNumMeshes; ++a) {
+ rootOut->mMeshes[a] = oldMeshSize + a;
+ }
+ }
+
+ // Setup the name of this node
+ rootOut->mName.Set(root->name);
+
+ // Now compute the final local transformation matrix of the
+ // node from the given translation, rotation and scaling values.
+ // (the rotation is given in Euler angles, XYZ order)
+ //std::swap((float&)root->rotation.z,(float&)root->rotation.y);
+ rootOut->mTransformation.FromEulerAnglesXYZ(AI_DEG_TO_RAD(root->rotation));
+
+ // apply scaling
+ aiMatrix4x4 &mat = rootOut->mTransformation;
+ mat.a1 *= root->scaling.x;
+ mat.b1 *= root->scaling.x;
+ mat.c1 *= root->scaling.x;
+ mat.a2 *= root->scaling.y;
+ mat.b2 *= root->scaling.y;
+ mat.c2 *= root->scaling.y;
+ mat.a3 *= root->scaling.z;
+ mat.b3 *= root->scaling.z;
+ mat.c3 *= root->scaling.z;
+
+ // apply translation
+ mat.a4 += root->position.x;
+ mat.b4 += root->position.y;
+ mat.c4 += root->position.z;
+
+ // now compute animations for the node
+ ComputeAnimations(root, rootOut, anims);
+
+ // Add all children recursively. First allocate enough storage
+ // for them, then call us again
+ rootOut->mNumChildren = (unsigned int)root->children.size();
+ if (rootOut->mNumChildren) {
+
+ rootOut->mChildren = new aiNode *[rootOut->mNumChildren];
+ for (unsigned int i = 0; i < rootOut->mNumChildren; ++i) {
+
+ aiNode *node = rootOut->mChildren[i] = new aiNode();
+ node->mParent = rootOut;
+ GenerateGraph(root->children[i], node, scene, batch, meshes,
+ anims, attach, materials, defMatIdx);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
+ std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
+
+ // Check whether we can read from the file
+ if (file.get() == nullptr) {
+ throw DeadlyImportError("Failed to open IRR file ", pFile);
+ }
+
+ // Construct the irrXML parser
+ XmlParser st;
+ if (!st.parse( file.get() )) {
+ throw DeadlyImportError("XML parse error while loading IRR file ", pFile);
+ }
+ pugi::xml_node rootElement = st.getRootNode();
+
+ // The root node of the scene
+ Node *root = new Node(Node::DUMMY);
+ root->parent = nullptr;
+ root->name = "<IRRSceneRoot>";
+
+ // Current node parent
+ Node *curParent = root;
+
+ // Scene-graph node we're currently working on
+ Node *curNode = nullptr;
+
+ // List of output cameras
+ std::vector<aiCamera *> cameras;
+
+ // List of output lights
+ std::vector<aiLight *> lights;
+
+ // Batch loader used to load external models
+ BatchLoader batch(pIOHandler);
+ // batch.SetBasePath(pFile);
+
+ cameras.reserve(5);
+ lights.reserve(5);
+
+ bool inMaterials = false, inAnimator = false;
+ unsigned int guessedAnimCnt = 0, guessedMeshCnt = 0, guessedMatCnt = 0;
+
+ // Parse the XML file
+
+ //while (reader->read()) {
+ for (pugi::xml_node child : rootElement.children())
+ switch (child.type()) {
+ case pugi::node_element:
+ if (!ASSIMP_stricmp(child.name(), "node")) {
+ // ***********************************************************************
+ /* What we're going to do with the node depends
+ * on its type:
+ *
+ * "mesh" - Load a mesh from an external file
+ * "cube" - Generate a cube
+ * "skybox" - Generate a skybox
+ * "light" - A light source
+ * "sphere" - Generate a sphere mesh
+ * "animatedMesh" - Load an animated mesh from an external file
+ * and join its animation channels with ours.
+ * "empty" - A dummy node
+ * "camera" - A camera
+ * "terrain" - a terrain node (data comes from a heightmap)
+ * "billboard", ""
+ *
+ * Each of these nodes can be animated and all can have multiple
+ * materials assigned (except lights, cameras and dummies, of course).
+ */
+ // ***********************************************************************
+ //const char *sz = reader->getAttributeValueSafe("type");
+ pugi::xml_attribute attrib = child.attribute("type");
+ Node *nd;
+ if (!ASSIMP_stricmp(attrib.name(), "mesh") || !ASSIMP_stricmp(attrib.name(), "octTree")) {
+ // OctTree's and meshes are treated equally
+ nd = new Node(Node::MESH);
+ } else if (!ASSIMP_stricmp(attrib.name(), "cube")) {
+ nd = new Node(Node::CUBE);
+ ++guessedMeshCnt;
+ } else if (!ASSIMP_stricmp(attrib.name(), "skybox")) {
+ nd = new Node(Node::SKYBOX);
+ guessedMeshCnt += 6;
+ } else if (!ASSIMP_stricmp(attrib.name(), "camera")) {
+ nd = new Node(Node::CAMERA);
+
+ // Setup a temporary name for the camera
+ aiCamera *cam = new aiCamera();
+ cam->mName.Set(nd->name);
+ cameras.push_back(cam);
+ } else if (!ASSIMP_stricmp(attrib.name(), "light")) {
+ nd = new Node(Node::LIGHT);
+
+ // Setup a temporary name for the light
+ aiLight *cam = new aiLight();
+ cam->mName.Set(nd->name);
+ lights.push_back(cam);
+ } else if (!ASSIMP_stricmp(attrib.name(), "sphere")) {
+ nd = new Node(Node::SPHERE);
+ ++guessedMeshCnt;
+ } else if (!ASSIMP_stricmp(attrib.name(), "animatedMesh")) {
+ nd = new Node(Node::ANIMMESH);
+ } else if (!ASSIMP_stricmp(attrib.name(), "empty")) {
+ nd = new Node(Node::DUMMY);
+ } else if (!ASSIMP_stricmp(attrib.name(), "terrain")) {
+ nd = new Node(Node::TERRAIN);
+ } else if (!ASSIMP_stricmp(attrib.name(), "billBoard")) {
+ // We don't support billboards, so ignore them
+ ASSIMP_LOG_ERROR("IRR: Billboards are not supported by Assimp");
+ nd = new Node(Node::DUMMY);
+ } else {
+ ASSIMP_LOG_WARN("IRR: Found unknown node: ", attrib.name());
+
+ /* We skip the contents of nodes we don't know.
+ * We parse the transformation and all animators
+ * and skip the rest.
+ */
+ nd = new Node(Node::DUMMY);
+ }
+
+ /* Attach the newly created node to the scene-graph
+ */
+ curNode = nd;
+ nd->parent = curParent;
+ curParent->children.push_back(nd);
+ } else if (!ASSIMP_stricmp(child.name(), "materials")) {
+ inMaterials = true;
+ } else if (!ASSIMP_stricmp(child.name(), "animators")) {
+ inAnimator = true;
+ } else if (!ASSIMP_stricmp(child.name(), "attributes")) {
+ // We should have a valid node here
+ // FIX: no ... the scene root node is also contained in an attributes block
+ if (!curNode) {
+ continue;
+ }
+
+ Animator *curAnim = nullptr;
+
+ // Materials can occur for nearly any type of node
+ if (inMaterials && curNode->type != Node::DUMMY) {
+ // This is a material description - parse it!
+ curNode->materials.push_back(std::pair<aiMaterial *, unsigned int>());
+ std::pair<aiMaterial *, unsigned int> &p = curNode->materials.back();
+
+ p.first = ParseMaterial(p.second);
+ ++guessedMatCnt;
+ continue;
+ } else if (inAnimator) {
+ // This is an animation path - add a new animator
+ // to the list.
+ curNode->animators.push_back(Animator());
+ curAnim = &curNode->animators.back();
+
+ ++guessedAnimCnt;
+ }
+
+ /* Parse all elements in the attributes block
+ * and process them.
+ */
+ // while (reader->read()) {
+ for (pugi::xml_node attrib : child.children()) {
+ if (attrib.type() == pugi::node_element) {
+ //if (reader->getNodeType() == EXN_ELEMENT) {
+ //if (!ASSIMP_stricmp(reader->getNodeName(), "vector3d")) {
+ if (!ASSIMP_stricmp(attrib.name(), "vector3d")) {
+ VectorProperty prop;
+ ReadVectorProperty(prop);
+
+ if (inAnimator) {
+ if (curAnim->type == Animator::ROTATION && prop.name == "Rotation") {
+ // We store the rotation euler angles in 'direction'
+ curAnim->direction = prop.value;
+ } else if (curAnim->type == Animator::FOLLOW_SPLINE) {
+ // Check whether the vector follows the PointN naming scheme,
+ // here N is the ONE-based index of the point
+ if (prop.name.length() >= 6 && prop.name.substr(0, 5) == "Point") {
+ // Add a new key to the list
+ curAnim->splineKeys.push_back(aiVectorKey());
+ aiVectorKey &key = curAnim->splineKeys.back();
+
+ // and parse its properties
+ key.mValue = prop.value;
+ key.mTime = strtoul10(&prop.name[5]);
+ }
+ } else if (curAnim->type == Animator::FLY_CIRCLE) {
+ if (prop.name == "Center") {
+ curAnim->circleCenter = prop.value;
+ } else if (prop.name == "Direction") {
+ curAnim->direction = prop.value;
+
+ // From Irrlicht's source - a workaround for backward compatibility with Irrlicht 1.1
+ if (curAnim->direction == aiVector3D()) {
+ curAnim->direction = aiVector3D(0.f, 1.f, 0.f);
+ } else
+ curAnim->direction.Normalize();
+ }
+ } else if (curAnim->type == Animator::FLY_STRAIGHT) {
+ if (prop.name == "Start") {
+ // We reuse the field here
+ curAnim->circleCenter = prop.value;
+ } else if (prop.name == "End") {
+ // We reuse the field here
+ curAnim->direction = prop.value;
+ }
+ }
+ } else {
+ if (prop.name == "Position") {
+ curNode->position = prop.value;
+ } else if (prop.name == "Rotation") {
+ curNode->rotation = prop.value;
+ } else if (prop.name == "Scale") {
+ curNode->scaling = prop.value;
+ } else if (Node::CAMERA == curNode->type) {
+ aiCamera *cam = cameras.back();
+ if (prop.name == "Target") {
+ cam->mLookAt = prop.value;
+ } else if (prop.name == "UpVector") {
+ cam->mUp = prop.value;
+ }
+ }
+ }
+ //} else if (!ASSIMP_stricmp(reader->getNodeName(), "bool")) {
+ } else if (!ASSIMP_stricmp(attrib.name(), "bool")) {
+ BoolProperty prop;
+ ReadBoolProperty(prop);
+
+ if (inAnimator && curAnim->type == Animator::FLY_CIRCLE && prop.name == "Loop") {
+ curAnim->loop = prop.value;
+ }
+ //} else if (!ASSIMP_stricmp(reader->getNodeName(), "float")) {
+ } else if (!ASSIMP_stricmp(attrib.name(), "float")) {
+ FloatProperty prop;
+ ReadFloatProperty(prop);
+
+ if (inAnimator) {
+ // The speed property exists for several animators
+ if (prop.name == "Speed") {
+ curAnim->speed = prop.value;
+ } else if (curAnim->type == Animator::FLY_CIRCLE && prop.name == "Radius") {
+ curAnim->circleRadius = prop.value;
+ } else if (curAnim->type == Animator::FOLLOW_SPLINE && prop.name == "Tightness") {
+ curAnim->tightness = prop.value;
+ }
+ } else {
+ if (prop.name == "FramesPerSecond" && Node::ANIMMESH == curNode->type) {
+ curNode->framesPerSecond = prop.value;
+ } else if (Node::CAMERA == curNode->type) {
+ /* This is the vertical, not the horizontal FOV.
+ * We need to compute the right FOV from the
+ * screen aspect which we don't know yet.
+ */
+ if (prop.name == "Fovy") {
+ cameras.back()->mHorizontalFOV = prop.value;
+ } else if (prop.name == "Aspect") {
+ cameras.back()->mAspect = prop.value;
+ } else if (prop.name == "ZNear") {
+ cameras.back()->mClipPlaneNear = prop.value;
+ } else if (prop.name == "ZFar") {
+ cameras.back()->mClipPlaneFar = prop.value;
+ }
+ } else if (Node::LIGHT == curNode->type) {
+ /* Additional light information
+ */
+ if (prop.name == "Attenuation") {
+ lights.back()->mAttenuationLinear = prop.value;
+ } else if (prop.name == "OuterCone") {
+ lights.back()->mAngleOuterCone = AI_DEG_TO_RAD(prop.value);
+ } else if (prop.name == "InnerCone") {
+ lights.back()->mAngleInnerCone = AI_DEG_TO_RAD(prop.value);
+ }
+ }
+ // radius of the sphere to be generated -
+ // or alternatively, size of the cube
+ else if ((Node::SPHERE == curNode->type && prop.name == "Radius") || (Node::CUBE == curNode->type && prop.name == "Size")) {
+
+ curNode->sphereRadius = prop.value;
+ }
+ }
+ //} else if (!ASSIMP_stricmp(reader->getNodeName(), "int")) {
+ } else if (!ASSIMP_stricmp(attrib.name(), "int")) {
+ IntProperty prop;
+ ReadIntProperty(prop);
+
+ if (inAnimator) {
+ if (curAnim->type == Animator::FLY_STRAIGHT && prop.name == "TimeForWay") {
+ curAnim->timeForWay = prop.value;
+ }
+ } else {
+ // sphere polygon numbers in each direction
+ if (Node::SPHERE == curNode->type) {
+
+ if (prop.name == "PolyCountX") {
+ curNode->spherePolyCountX = prop.value;
+ } else if (prop.name == "PolyCountY") {
+ curNode->spherePolyCountY = prop.value;
+ }
+ }
+ }
+ //} else if (!ASSIMP_stricmp(reader->getNodeName(), "string") || !ASSIMP_stricmp(reader->getNodeName(), "enum")) {
+ } else if (!ASSIMP_stricmp(attrib.name(), "string") || !ASSIMP_stricmp(attrib.name(), "enum")) {
+ StringProperty prop;
+ ReadStringProperty(prop);
+ if (prop.value.length()) {
+ if (prop.name == "Name") {
+ curNode->name = prop.value;
+
+ /* If we're either a camera or a light source
+ * we need to update the name in the aiLight/
+ * aiCamera structure, too.
+ */
+ if (Node::CAMERA == curNode->type) {
+ cameras.back()->mName.Set(prop.value);
+ } else if (Node::LIGHT == curNode->type) {
+ lights.back()->mName.Set(prop.value);
+ }
+ } else if (Node::LIGHT == curNode->type && "LightType" == prop.name) {
+ if (prop.value == "Spot")
+ lights.back()->mType = aiLightSource_SPOT;
+ else if (prop.value == "Point")
+ lights.back()->mType = aiLightSource_POINT;
+ else if (prop.value == "Directional")
+ lights.back()->mType = aiLightSource_DIRECTIONAL;
+ else {
+ // We won't pass the validation with aiLightSourceType_UNDEFINED,
+ // so we remove the light and replace it with a silly dummy node
+ delete lights.back();
+ lights.pop_back();
+ curNode->type = Node::DUMMY;
+
+ ASSIMP_LOG_ERROR("Ignoring light of unknown type: ", prop.value);
+ }
+ } else if ((prop.name == "Mesh" && Node::MESH == curNode->type) ||
+ Node::ANIMMESH == curNode->type) {
+ /* This is the file name of the mesh - either
+ * animated or not. We need to make sure we setup
+ * the correct post-processing settings here.
+ */
+ unsigned int pp = 0;
+ BatchLoader::PropertyMap map;
+
+ /* If the mesh is a static one remove all animations from the impor data
+ */
+ if (Node::ANIMMESH != curNode->type) {
+ pp |= aiProcess_RemoveComponent;
+ SetGenericProperty<int>(map.ints, AI_CONFIG_PP_RVC_FLAGS,
+ aiComponent_ANIMATIONS | aiComponent_BONEWEIGHTS);
+ }
+
+ /* TODO: maybe implement the protection against recursive
+ * loading calls directly in BatchLoader? The current
+ * implementation is not absolutely safe. A LWS and an IRR
+ * file referencing each other *could* cause the system to
+ * recurse forever.
+ */
+
+ const std::string extension = GetExtension(prop.value);
+ if ("irr" == extension) {
+ ASSIMP_LOG_ERROR("IRR: Can't load another IRR file recursively");
+ } else {
+ curNode->id = batch.AddLoadRequest(prop.value, pp, &map);
+ curNode->meshPath = prop.value;
+ }
+ } else if (inAnimator && prop.name == "Type") {
+ // type of the animator
+ if (prop.value == "rotation") {
+ curAnim->type = Animator::ROTATION;
+ } else if (prop.value == "flyCircle") {
+ curAnim->type = Animator::FLY_CIRCLE;
+ } else if (prop.value == "flyStraight") {
+ curAnim->type = Animator::FLY_CIRCLE;
+ } else if (prop.value == "followSpline") {
+ curAnim->type = Animator::FOLLOW_SPLINE;
+ } else {
+ ASSIMP_LOG_WARN("IRR: Ignoring unknown animator: ", prop.value);
+
+ curAnim->type = Animator::UNKNOWN;
+ }
+ }
+ }
+ }
+ //} else if (reader->getNodeType() == EXN_ELEMENT_END && !ASSIMP_stricmp(reader->getNodeName(), "attributes")) {
+ } else if (attrib.type() == pugi::node_null && !ASSIMP_stricmp(attrib.name(), "attributes")) {
+ break;
+ }
+ }
+ }
+ break;
+
+ /*case EXN_ELEMENT_END:
+
+ // If we reached the end of a node, we need to continue processing its parent
+ if (!ASSIMP_stricmp(reader->getNodeName(), "node")) {
+ if (!curNode) {
+ // currently is no node set. We need to go
+ // back in the node hierarchy
+ if (!curParent) {
+ curParent = root;
+ ASSIMP_LOG_ERROR("IRR: Too many closing <node> elements");
+ } else
+ curParent = curParent->parent;
+ } else
+ curNode = nullptr;
+ }
+ // clear all flags
+ else if (!ASSIMP_stricmp(reader->getNodeName(), "materials")) {
+ inMaterials = false;
+ } else if (!ASSIMP_stricmp(reader->getNodeName(), "animators")) {
+ inAnimator = false;
+ }
+ break;*/
+
+ default:
+ // GCC complains that not all enumeration values are handled
+ break;
+ }
+ //}
+
+ // Now iterate through all cameras and compute their final (horizontal) FOV
+ for (aiCamera *cam : cameras) {
+ // screen aspect could be missing
+ if (cam->mAspect) {
+ cam->mHorizontalFOV *= cam->mAspect;
+ } else {
+ ASSIMP_LOG_WARN("IRR: Camera aspect is not given, can't compute horizontal FOV");
+ }
+ }
+
+ batch.LoadAll();
+
+ // Allocate a temporary scene data structure
+ aiScene *tempScene = new aiScene();
+ tempScene->mRootNode = new aiNode();
+ tempScene->mRootNode->mName.Set("<IRRRoot>");
+
+ // Copy the cameras to the output array
+ if (!cameras.empty()) {
+ tempScene->mNumCameras = (unsigned int)cameras.size();
+ tempScene->mCameras = new aiCamera *[tempScene->mNumCameras];
+ ::memcpy(tempScene->mCameras, &cameras[0], sizeof(void *) * tempScene->mNumCameras);
+ }
+
+ // Copy the light sources to the output array
+ if (!lights.empty()) {
+ tempScene->mNumLights = (unsigned int)lights.size();
+ tempScene->mLights = new aiLight *[tempScene->mNumLights];
+ ::memcpy(tempScene->mLights, &lights[0], sizeof(void *) * tempScene->mNumLights);
+ }
+
+ // temporary data
+ std::vector<aiNodeAnim *> anims;
+ std::vector<aiMaterial *> materials;
+ std::vector<AttachmentInfo> attach;
+ std::vector<aiMesh *> meshes;
+
+ // try to guess how much storage we'll need
+ anims.reserve(guessedAnimCnt + (guessedAnimCnt >> 2));
+ meshes.reserve(guessedMeshCnt + (guessedMeshCnt >> 2));
+ materials.reserve(guessedMatCnt + (guessedMatCnt >> 2));
+
+ // Now process our scene-graph recursively: generate final
+ // meshes and generate animation channels for all nodes.
+ unsigned int defMatIdx = UINT_MAX;
+ GenerateGraph(root, tempScene->mRootNode, tempScene,
+ batch, meshes, anims, attach, materials, defMatIdx);
+
+ if (!anims.empty()) {
+ tempScene->mNumAnimations = 1;
+ tempScene->mAnimations = new aiAnimation *[tempScene->mNumAnimations];
+ aiAnimation *an = tempScene->mAnimations[0] = new aiAnimation();
+
+ // ***********************************************************
+ // This is only the global animation channel of the scene.
+ // If there are animated models, they will have separate
+ // animation channels in the scene. To display IRR scenes
+ // correctly, users will need to combine the global anim
+ // channel with all the local animations they want to play
+ // ***********************************************************
+ an->mName.Set("Irr_GlobalAnimChannel");
+
+ // copy all node animation channels to the global channel
+ an->mNumChannels = (unsigned int)anims.size();
+ an->mChannels = new aiNodeAnim *[an->mNumChannels];
+ ::memcpy(an->mChannels, &anims[0], sizeof(void *) * an->mNumChannels);
+ }
+ if (!meshes.empty()) {
+ // copy all meshes to the temporary scene
+ tempScene->mNumMeshes = (unsigned int)meshes.size();
+ tempScene->mMeshes = new aiMesh *[tempScene->mNumMeshes];
+ ::memcpy(tempScene->mMeshes, &meshes[0], tempScene->mNumMeshes * sizeof(void *));
+ }
+
+ // Copy all materials to the output array
+ if (!materials.empty()) {
+ tempScene->mNumMaterials = (unsigned int)materials.size();
+ tempScene->mMaterials = new aiMaterial *[tempScene->mNumMaterials];
+ ::memcpy(tempScene->mMaterials, &materials[0], sizeof(void *) * tempScene->mNumMaterials);
+ }
+
+ // Now merge all sub scenes and attach them to the correct
+ // attachment points in the scenegraph.
+ SceneCombiner::MergeScenes(&pScene, tempScene, attach,
+ AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (
+ AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) :
+ 0));
+
+ // If we have no meshes | no materials now set the INCOMPLETE
+ // scene flag. This is necessary if we failed to load all
+ // models from external files
+ if (!pScene->mNumMeshes || !pScene->mNumMaterials) {
+ ASSIMP_LOG_WARN("IRR: No meshes loaded, setting AI_SCENE_FLAGS_INCOMPLETE");
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ }
+
+ // Finished ... everything destructs automatically and all
+ // temporary scenes have already been deleted by MergeScenes()
+ delete root;
+}
+
+#endif // !! ASSIMP_BUILD_NO_IRR_IMPORTER
diff --git a/libs/assimp/code/AssetLib/Irr/IRRLoader.h b/libs/assimp/code/AssetLib/Irr/IRRLoader.h
new file mode 100644
index 0000000..d56c4ca
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Irr/IRRLoader.h
@@ -0,0 +1,284 @@
+/*
+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 IRRLoader.h
+ * @brief Declaration of the .irrMesh (Irrlight Engine Mesh Format)
+ * importer class.
+ */
+#ifndef AI_IRRLOADER_H_INCLUDED
+#define AI_IRRLOADER_H_INCLUDED
+
+#include "AssetLib/Irr/IRRShared.h"
+#include "Common/Importer.h"
+
+#include <assimp/SceneCombiner.h>
+#include <assimp/StringUtils.h>
+#include <assimp/anim.h>
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** Irr importer class.
+ *
+ * Irr is the native scene file format of the Irrlight engine and its editor
+ * irrEdit. As IrrEdit itself is capable of importing quite many file formats,
+ * it might be a good file format for data exchange.
+ */
+class IRRImporter : public BaseImporter, public IrrlichtBase {
+public:
+ IRRImporter();
+ ~IRRImporter() override;
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const override;
+
+protected:
+ const aiImporterDesc* GetInfo () const override;
+ void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) override;
+ void SetupProperties(const Importer* pImp) override;
+
+private:
+ /** Data structure for a scene-graph node animator
+ */
+ struct Animator {
+ // Type of the animator
+ enum AT {
+ UNKNOWN = 0x0,
+ ROTATION = 0x1,
+ FLY_CIRCLE = 0x2,
+ FLY_STRAIGHT = 0x3,
+ FOLLOW_SPLINE = 0x4,
+ OTHER = 0x5
+
+ } type;
+
+ explicit Animator(AT t = UNKNOWN)
+ : type (t)
+ , speed ( ai_real( 0.001 ) )
+ , direction ( ai_real( 0.0 ), ai_real( 1.0 ), ai_real( 0.0 ) )
+ , circleRadius ( ai_real( 1.0) )
+ , tightness ( ai_real( 0.5 ) )
+ , loop (true)
+ , timeForWay (100)
+ {
+ }
+
+
+ // common parameters
+ ai_real speed;
+ aiVector3D direction;
+
+ // FLY_CIRCLE
+ aiVector3D circleCenter;
+ ai_real circleRadius;
+
+ // FOLLOW_SPLINE
+ ai_real tightness;
+ std::vector<aiVectorKey> splineKeys;
+
+ // ROTATION (angles given in direction)
+
+ // FLY STRAIGHT
+ // circleCenter = start, direction = end
+ bool loop;
+ int timeForWay;
+ };
+
+ /** Data structure for a scene-graph node in an IRR file
+ */
+ struct Node
+ {
+ // Type of the node
+ enum ET
+ {
+ LIGHT,
+ CUBE,
+ MESH,
+ SKYBOX,
+ DUMMY,
+ CAMERA,
+ TERRAIN,
+ SPHERE,
+ ANIMMESH
+ } type;
+
+ explicit Node(ET t)
+ : type (t)
+ , scaling (1.0,1.0,1.0) // assume uniform scaling by default
+ , parent()
+ , framesPerSecond (0.0)
+ , id()
+ , sphereRadius (1.0)
+ , spherePolyCountX (100)
+ , spherePolyCountY (100)
+ {
+
+ // Generate a default name for the node
+ char buffer[128];
+ static int cnt;
+ ai_snprintf(buffer, 128, "IrrNode_%i",cnt++);
+ name = std::string(buffer);
+
+ // reserve space for up to 5 materials
+ materials.reserve(5);
+
+ // reserve space for up to 5 children
+ children.reserve(5);
+ }
+
+ // Transformation of the node
+ aiVector3D position, rotation, scaling;
+
+ // Name of the node
+ std::string name;
+
+ // List of all child nodes
+ std::vector<Node*> children;
+
+ // Parent node
+ Node* parent;
+
+ // Animated meshes: frames per second
+ // 0.f if not specified
+ ai_real framesPerSecond;
+
+ // Meshes: path to the mesh to be loaded
+ std::string meshPath;
+ unsigned int id;
+
+ // Meshes: List of materials to be assigned
+ // along with their corresponding material flags
+ std::vector< std::pair<aiMaterial*, unsigned int> > materials;
+
+ // Spheres: radius of the sphere to be generates
+ ai_real sphereRadius;
+
+ // Spheres: Number of polygons in the x,y direction
+ unsigned int spherePolyCountX,spherePolyCountY;
+
+ // List of all animators assigned to the node
+ std::list<Animator> animators;
+ };
+
+ /** Data structure for a vertex in an IRR skybox
+ */
+ struct SkyboxVertex
+ {
+ SkyboxVertex()
+ {}
+
+ //! Construction from single vertex components
+ SkyboxVertex(ai_real px, ai_real py, ai_real pz,
+ ai_real nx, ai_real ny, ai_real nz,
+ ai_real uvx, ai_real uvy)
+
+ : position (px,py,pz)
+ , normal (nx,ny,nz)
+ , uv (uvx,uvy,0.0)
+ {}
+
+ aiVector3D position, normal, uv;
+ };
+
+
+ // -------------------------------------------------------------------
+ /// Fill the scene-graph recursively
+ void GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
+ BatchLoader& batch,
+ std::vector<aiMesh*>& meshes,
+ std::vector<aiNodeAnim*>& anims,
+ std::vector<AttachmentInfo>& attach,
+ std::vector<aiMaterial*>& materials,
+ unsigned int& defaultMatIdx);
+
+ // -------------------------------------------------------------------
+ /// Generate a mesh that consists of just a single quad
+ aiMesh* BuildSingleQuadMesh(const SkyboxVertex& v1,
+ const SkyboxVertex& v2,
+ const SkyboxVertex& v3,
+ const SkyboxVertex& v4);
+
+ // -------------------------------------------------------------------
+ /// Build a sky-box
+ ///
+ /// @param meshes Receives 6 output meshes
+ /// @param materials The last 6 materials are assigned to the newly
+ /// created meshes. The names of the materials are adjusted.
+ void BuildSkybox(std::vector<aiMesh*>& meshes,
+ std::vector<aiMaterial*> materials);
+
+ // -------------------------------------------------------------------
+ /** Copy a material for a mesh to the output material list
+ *
+ * @param materials Receives an output material
+ * @param inmaterials List of input materials
+ * @param defMatIdx Default material index - UINT_MAX if not present
+ * @param mesh Mesh to work on
+ */
+ void CopyMaterial(std::vector<aiMaterial*>& materials,
+ std::vector< std::pair<aiMaterial*, unsigned int> >& inmaterials,
+ unsigned int& defMatIdx,
+ aiMesh* mesh);
+
+ // -------------------------------------------------------------------
+ /** Compute animations for a specific node
+ *
+ * @param root Node to be processed
+ * @param anims The list of output animations
+ */
+ void ComputeAnimations(Node* root, aiNode* real,
+ std::vector<aiNodeAnim*>& anims);
+
+private:
+ /// Configuration option: desired output FPS
+ double fps;
+
+ /// Configuration option: speed flag was set?
+ bool configSpeedFlag;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_IRRIMPORTER_H_INC
diff --git a/libs/assimp/code/AssetLib/Irr/IRRMeshLoader.cpp b/libs/assimp/code/AssetLib/Irr/IRRMeshLoader.cpp
new file mode 100644
index 0000000..90b4d85
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Irr/IRRMeshLoader.cpp
@@ -0,0 +1,502 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the IrrMesh importer class */
+
+#ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER
+
+#include "IRRMeshLoader.h"
+#include <assimp/ParsingUtils.h>
+#include <assimp/fast_atof.h>
+#include <assimp/importerdesc.h>
+#include <assimp/material.h>
+#include <assimp/mesh.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/IOSystem.hpp>
+#include <memory>
+
+using namespace Assimp;
+
+static const aiImporterDesc desc = {
+ "Irrlicht Mesh Reader",
+ "",
+ "",
+ "http://irrlicht.sourceforge.net/",
+ aiImporterFlags_SupportTextFlavour,
+ 0,
+ 0,
+ 0,
+ 0,
+ "xml irrmesh"
+};
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+IRRMeshImporter::IRRMeshImporter() :
+ BaseImporter(),
+ IrrlichtBase() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+IRRMeshImporter::~IRRMeshImporter() {}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool IRRMeshImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+ /* NOTE: A simple check for the file extension is not enough
+ * here. Irrmesh and irr are easy, but xml is too generic
+ * and could be collada, too. So we need to open the file and
+ * search for typical tokens.
+ */
+ static const char *tokens[] = { "irrmesh" };
+ return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a list of all file extensions which are handled by this class
+const aiImporterDesc *IRRMeshImporter::GetInfo() const {
+ return &desc;
+}
+
+static void releaseMaterial(aiMaterial **mat) {
+ if (*mat != nullptr) {
+ delete *mat;
+ *mat = nullptr;
+ }
+}
+
+static void releaseMesh(aiMesh **mesh) {
+ if (*mesh != nullptr) {
+ delete *mesh;
+ *mesh = nullptr;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void IRRMeshImporter::InternReadFile(const std::string &pFile,
+ aiScene *pScene, IOSystem *pIOHandler) {
+ std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
+
+ // Check whether we can read from the file
+ if (file.get() == NULL)
+ throw DeadlyImportError("Failed to open IRRMESH file ", pFile);
+
+ // Construct the irrXML parser
+ XmlParser parser;
+ if (!parser.parse( file.get() )) {
+ throw DeadlyImportError("XML parse error while loading IRRMESH file ", pFile);
+ }
+ XmlNode root = parser.getRootNode();
+
+ // final data
+ std::vector<aiMaterial *> materials;
+ std::vector<aiMesh *> meshes;
+ materials.reserve(5);
+ meshes.reserve(5);
+
+ // temporary data - current mesh buffer
+ aiMaterial *curMat = nullptr;
+ aiMesh *curMesh = nullptr;
+ unsigned int curMatFlags = 0;
+
+ std::vector<aiVector3D> curVertices, curNormals, curTangents, curBitangents;
+ std::vector<aiColor4D> curColors;
+ std::vector<aiVector3D> curUVs, curUV2s;
+
+ // some temporary variables
+ int textMeaning = 0;
+ int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents
+ bool useColors = false;
+
+ // Parse the XML file
+ for (pugi::xml_node child : root.children()) {
+ if (child.type() == pugi::node_element) {
+ if (!ASSIMP_stricmp(child.name(), "buffer") && (curMat || curMesh)) {
+ // end of previous buffer. A material and a mesh should be there
+ if (!curMat || !curMesh) {
+ ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material");
+ releaseMaterial(&curMat);
+ releaseMesh(&curMesh);
+ } else {
+ materials.push_back(curMat);
+ meshes.push_back(curMesh);
+ }
+ curMat = nullptr;
+ curMesh = nullptr;
+
+ curVertices.clear();
+ curColors.clear();
+ curNormals.clear();
+ curUV2s.clear();
+ curUVs.clear();
+ curTangents.clear();
+ curBitangents.clear();
+ }
+
+ if (!ASSIMP_stricmp(child.name(), "material")) {
+ if (curMat) {
+ ASSIMP_LOG_WARN("IRRMESH: Only one material description per buffer, please");
+ releaseMaterial(&curMat);
+ }
+ curMat = ParseMaterial(curMatFlags);
+ }
+ /* no else here! */ if (!ASSIMP_stricmp(child.name(), "vertices")) {
+ pugi::xml_attribute attr = child.attribute("vertexCount");
+ int num = attr.as_int();
+ //int num = reader->getAttributeValueAsInt("vertexCount");
+
+ if (!num) {
+ // This is possible ... remove the mesh from the list and skip further reading
+ ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero vertices");
+
+ releaseMaterial(&curMat);
+ releaseMesh(&curMesh);
+ textMeaning = 0;
+ continue;
+ }
+
+ curVertices.reserve(num);
+ curNormals.reserve(num);
+ curColors.reserve(num);
+ curUVs.reserve(num);
+
+ // Determine the file format
+ //const char *t = reader->getAttributeValueSafe("type");
+ pugi::xml_attribute t = child.attribute("type");
+ if (!ASSIMP_stricmp("2tcoords", t.name())) {
+ curUV2s.reserve(num);
+ vertexFormat = 1;
+
+ if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) {
+ // *********************************************************
+ // We have a second texture! So use this UV channel
+ // for it. The 2nd texture can be either a normal
+ // texture (solid_2layer or lightmap_xxx) or a normal
+ // map (normal_..., parallax_...)
+ // *********************************************************
+ int idx = 1;
+ aiMaterial *mat = (aiMaterial *)curMat;
+
+ if (curMatFlags & AI_IRRMESH_MAT_lightmap) {
+ mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_LIGHTMAP(0));
+ } else if (curMatFlags & AI_IRRMESH_MAT_normalmap_solid) {
+ mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_NORMALS(0));
+ } else if (curMatFlags & AI_IRRMESH_MAT_solid_2layer) {
+ mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_DIFFUSE(1));
+ }
+ }
+ } else if (!ASSIMP_stricmp("tangents", t.name())) {
+ curTangents.reserve(num);
+ curBitangents.reserve(num);
+ vertexFormat = 2;
+ } else if (ASSIMP_stricmp("standard", t.name())) {
+ releaseMaterial(&curMat);
+ ASSIMP_LOG_WARN("IRRMESH: Unknown vertex format");
+ } else
+ vertexFormat = 0;
+ textMeaning = 1;
+ } else if (!ASSIMP_stricmp(child.name(), "indices")) {
+ if (curVertices.empty() && curMat) {
+ releaseMaterial(&curMat);
+ throw DeadlyImportError("IRRMESH: indices must come after vertices");
+ }
+
+ textMeaning = 2;
+
+ // start a new mesh
+ curMesh = new aiMesh();
+
+ // allocate storage for all faces
+ pugi::xml_attribute attr = child.attribute("indexCount");
+ curMesh->mNumVertices = attr.as_int();
+ if (!curMesh->mNumVertices) {
+ // This is possible ... remove the mesh from the list and skip further reading
+ ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero indices");
+
+ // mesh - away
+ releaseMesh(&curMesh);
+
+ // material - away
+ releaseMaterial(&curMat);
+
+ textMeaning = 0;
+ continue;
+ }
+
+ if (curMesh->mNumVertices % 3) {
+ ASSIMP_LOG_WARN("IRRMESH: Number if indices isn't divisible by 3");
+ }
+
+ curMesh->mNumFaces = curMesh->mNumVertices / 3;
+ curMesh->mFaces = new aiFace[curMesh->mNumFaces];
+
+ // setup some members
+ curMesh->mMaterialIndex = (unsigned int)materials.size();
+ curMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ // allocate storage for all vertices
+ curMesh->mVertices = new aiVector3D[curMesh->mNumVertices];
+
+ if (curNormals.size() == curVertices.size()) {
+ curMesh->mNormals = new aiVector3D[curMesh->mNumVertices];
+ }
+ if (curTangents.size() == curVertices.size()) {
+ curMesh->mTangents = new aiVector3D[curMesh->mNumVertices];
+ }
+ if (curBitangents.size() == curVertices.size()) {
+ curMesh->mBitangents = new aiVector3D[curMesh->mNumVertices];
+ }
+ if (curColors.size() == curVertices.size() && useColors) {
+ curMesh->mColors[0] = new aiColor4D[curMesh->mNumVertices];
+ }
+ if (curUVs.size() == curVertices.size()) {
+ curMesh->mTextureCoords[0] = new aiVector3D[curMesh->mNumVertices];
+ }
+ if (curUV2s.size() == curVertices.size()) {
+ curMesh->mTextureCoords[1] = new aiVector3D[curMesh->mNumVertices];
+ }
+ }
+ //break;
+
+ //case EXN_TEXT: {
+ const char *sz = child.child_value();
+ if (textMeaning == 1) {
+ textMeaning = 0;
+
+ // read vertices
+ do {
+ SkipSpacesAndLineEnd(&sz);
+ aiVector3D temp;
+ aiColor4D c;
+
+ // Read the vertex position
+ sz = fast_atoreal_move<float>(sz, (float &)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atoreal_move<float>(sz, (float &)temp.y);
+ SkipSpaces(&sz);
+
+ sz = fast_atoreal_move<float>(sz, (float &)temp.z);
+ SkipSpaces(&sz);
+ curVertices.push_back(temp);
+
+ // Read the vertex normals
+ sz = fast_atoreal_move<float>(sz, (float &)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atoreal_move<float>(sz, (float &)temp.y);
+ SkipSpaces(&sz);
+
+ sz = fast_atoreal_move<float>(sz, (float &)temp.z);
+ SkipSpaces(&sz);
+ curNormals.push_back(temp);
+
+ // read the vertex colors
+ uint32_t clr = strtoul16(sz, &sz);
+ ColorFromARGBPacked(clr, c);
+
+ if (!curColors.empty() && c != *(curColors.end() - 1))
+ useColors = true;
+
+ curColors.push_back(c);
+ SkipSpaces(&sz);
+
+ // read the first UV coordinate set
+ sz = fast_atoreal_move<float>(sz, (float &)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atoreal_move<float>(sz, (float &)temp.y);
+ SkipSpaces(&sz);
+ temp.z = 0.f;
+ temp.y = 1.f - temp.y; // DX to OGL
+ curUVs.push_back(temp);
+
+ // read the (optional) second UV coordinate set
+ if (vertexFormat == 1) {
+ sz = fast_atoreal_move<float>(sz, (float &)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atoreal_move<float>(sz, (float &)temp.y);
+ temp.y = 1.f - temp.y; // DX to OGL
+ curUV2s.push_back(temp);
+ }
+ // read optional tangent and bitangent vectors
+ else if (vertexFormat == 2) {
+ // tangents
+ sz = fast_atoreal_move<float>(sz, (float &)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atoreal_move<float>(sz, (float &)temp.z);
+ SkipSpaces(&sz);
+
+ sz = fast_atoreal_move<float>(sz, (float &)temp.y);
+ SkipSpaces(&sz);
+ temp.y *= -1.0f;
+ curTangents.push_back(temp);
+
+ // bitangents
+ sz = fast_atoreal_move<float>(sz, (float &)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atoreal_move<float>(sz, (float &)temp.z);
+ SkipSpaces(&sz);
+
+ sz = fast_atoreal_move<float>(sz, (float &)temp.y);
+ SkipSpaces(&sz);
+ temp.y *= -1.0f;
+ curBitangents.push_back(temp);
+ }
+ }
+
+ /* IMPORTANT: We assume that each vertex is specified in one
+ line. So we can skip the rest of the line - unknown vertex
+ elements are ignored.
+ */
+
+ while (SkipLine(&sz));
+ } else if (textMeaning == 2) {
+ textMeaning = 0;
+
+ // read indices
+ aiFace *curFace = curMesh->mFaces;
+ aiFace *const faceEnd = curMesh->mFaces + curMesh->mNumFaces;
+
+ aiVector3D *pcV = curMesh->mVertices;
+ aiVector3D *pcN = curMesh->mNormals;
+ aiVector3D *pcT = curMesh->mTangents;
+ aiVector3D *pcB = curMesh->mBitangents;
+ aiColor4D *pcC0 = curMesh->mColors[0];
+ aiVector3D *pcT0 = curMesh->mTextureCoords[0];
+ aiVector3D *pcT1 = curMesh->mTextureCoords[1];
+
+ unsigned int curIdx = 0;
+ unsigned int total = 0;
+ while (SkipSpacesAndLineEnd(&sz)) {
+ if (curFace >= faceEnd) {
+ ASSIMP_LOG_ERROR("IRRMESH: Too many indices");
+ break;
+ }
+ if (!curIdx) {
+ curFace->mNumIndices = 3;
+ curFace->mIndices = new unsigned int[3];
+ }
+
+ unsigned int idx = strtoul10(sz, &sz);
+ if (idx >= curVertices.size()) {
+ ASSIMP_LOG_ERROR("IRRMESH: Index out of range");
+ idx = 0;
+ }
+
+ curFace->mIndices[curIdx] = total++;
+
+ *pcV++ = curVertices[idx];
+ if (pcN) *pcN++ = curNormals[idx];
+ if (pcT) *pcT++ = curTangents[idx];
+ if (pcB) *pcB++ = curBitangents[idx];
+ if (pcC0) *pcC0++ = curColors[idx];
+ if (pcT0) *pcT0++ = curUVs[idx];
+ if (pcT1) *pcT1++ = curUV2s[idx];
+
+ if (++curIdx == 3) {
+ ++curFace;
+ curIdx = 0;
+ }
+ }
+
+ if (curFace != faceEnd)
+ ASSIMP_LOG_ERROR("IRRMESH: Not enough indices");
+
+ // Finish processing the mesh - do some small material workarounds
+ if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors) {
+ // Take the opacity value of the current material
+ // from the common vertex color alpha
+ aiMaterial *mat = (aiMaterial *)curMat;
+ mat->AddProperty(&curColors[0].a, 1, AI_MATKEY_OPACITY);
+ }
+ }
+ }
+ }
+
+ // End of the last buffer. A material and a mesh should be there
+ if (curMat || curMesh) {
+ if (!curMat || !curMesh) {
+ ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material");
+ releaseMaterial(&curMat);
+ releaseMesh(&curMesh);
+ } else {
+ materials.push_back(curMat);
+ meshes.push_back(curMesh);
+ }
+ }
+
+ if (materials.empty()) {
+ throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file");
+ }
+
+ // now generate the output scene
+ pScene->mNumMeshes = (unsigned int)meshes.size();
+ pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+ pScene->mMeshes[i] = meshes[i];
+
+ // clean this value ...
+ pScene->mMeshes[i]->mNumUVComponents[3] = 0;
+ }
+
+ pScene->mNumMaterials = (unsigned int)materials.size();
+ pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
+ ::memcpy(pScene->mMaterials, &materials[0], sizeof(void *) * pScene->mNumMaterials);
+
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("<IRRMesh>");
+ pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
+ pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
+
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+ pScene->mRootNode->mMeshes[i] = i;
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_IRRMESH_IMPORTER
diff --git a/libs/assimp/code/AssetLib/Irr/IRRMeshLoader.h b/libs/assimp/code/AssetLib/Irr/IRRMeshLoader.h
new file mode 100644
index 0000000..79c1e48
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Irr/IRRMeshLoader.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 IRRMeshLoader.h
+ * @brief Declaration of the .irrMesh (Irrlight Engine Mesh Format)
+ * importer class.
+ */
+#ifndef AI_IRRMESHLOADER_H_INCLUDED
+#define AI_IRRMESHLOADER_H_INCLUDED
+
+#include "IRRShared.h"
+#include <assimp/BaseImporter.h>
+
+#ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** IrrMesh importer class.
+ *
+ * IrrMesh is the native file format of the Irrlight engine and its editor
+ * irrEdit. As IrrEdit itself is capable of importing quite many file formats,
+ * it might be a good file format for data exchange.
+ */
+class IRRMeshImporter : public BaseImporter, public IrrlichtBase {
+public:
+ IRRMeshImporter();
+ ~IRRMeshImporter() override;
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead(const std::string &pFile, IOSystem *pIOHandler,
+ bool checkSig) const override;
+
+protected:
+ // -------------------------------------------------------------------
+ /** Return importer meta information.
+ * See #BaseImporter::GetInfo for the details
+ */
+ const aiImporterDesc *GetInfo() const override;
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile(const std::string &pFile, aiScene *pScene,
+ IOSystem *pIOHandler) override;
+};
+
+} // end of namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_IRRMESH_IMPORTER
+
+#endif // AI_IRRMESHIMPORTER_H_INC
diff --git a/libs/assimp/code/AssetLib/Irr/IRRShared.cpp b/libs/assimp/code/AssetLib/Irr/IRRShared.cpp
new file mode 100644
index 0000000..8763b63
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Irr/IRRShared.cpp
@@ -0,0 +1,387 @@
+/*
+---------------------------------------------------------------------------
+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 IRRShared.cpp
+ * @brief Shared utilities for the IRR and IRRMESH loaders
+ */
+
+//This section should be excluded only if both the Irrlicht AND the Irrlicht Mesh importers were omitted.
+#if !(defined(ASSIMP_BUILD_NO_IRR_IMPORTER) && defined(ASSIMP_BUILD_NO_IRRMESH_IMPORTER))
+
+#include "IRRShared.h"
+#include <assimp/ParsingUtils.h>
+#include <assimp/fast_atof.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/material.h>
+
+using namespace Assimp;
+
+// Transformation matrix to convert from Assimp to IRR space
+const aiMatrix4x4 Assimp::AI_TO_IRR_MATRIX = aiMatrix4x4 (
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+
+// ------------------------------------------------------------------------------------------------
+// read a property in hexadecimal format (i.e. ffffffff)
+void IrrlichtBase::ReadHexProperty(HexProperty &out ) {
+ for (pugi::xml_attribute attrib : mNode->attributes()) {
+ if (!ASSIMP_stricmp(attrib.name(), "name")) {
+ out.name = std::string( attrib.value() );
+ } else if (!ASSIMP_stricmp(attrib.name(),"value")) {
+ // parse the hexadecimal value
+ out.value = strtoul16(attrib.name());
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read a decimal property
+void IrrlichtBase::ReadIntProperty(IntProperty & out) {
+ for (pugi::xml_attribute attrib : mNode->attributes()) {
+ if (!ASSIMP_stricmp(attrib.name(), "name")) {
+ out.name = std::string(attrib.value());
+ } else if (!ASSIMP_stricmp(attrib.value(),"value")) {
+ // parse the int value
+ out.value = strtol10(attrib.name());
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read a string property
+void IrrlichtBase::ReadStringProperty( StringProperty& out) {
+ for (pugi::xml_attribute attrib : mNode->attributes()) {
+ if (!ASSIMP_stricmp(attrib.name(), "name")) {
+ out.name = std::string(attrib.value());
+ } else if (!ASSIMP_stricmp(attrib.name(), "value")) {
+ // simple copy the string
+ out.value = std::string(attrib.value());
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read a boolean property
+void IrrlichtBase::ReadBoolProperty(BoolProperty &out) {
+ for (pugi::xml_attribute attrib : mNode->attributes()) {
+ if (!ASSIMP_stricmp(attrib.name(), "name")){
+ out.name = std::string(attrib.value());
+ } else if (!ASSIMP_stricmp(attrib.name(), "value")) {
+ // true or false, case insensitive
+ out.value = (ASSIMP_stricmp(attrib.value(), "true") ? false : true);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read a float property
+void IrrlichtBase::ReadFloatProperty(FloatProperty &out) {
+ for (pugi::xml_attribute attrib : mNode->attributes()) {
+ if (!ASSIMP_stricmp(attrib.name(), "name")) {
+ out.name = std::string(attrib.value());
+ } else if (!ASSIMP_stricmp(attrib.name(), "value")) {
+ // just parse the float
+ out.value = fast_atof(attrib.value());
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read a vector property
+void IrrlichtBase::ReadVectorProperty( VectorProperty &out ) {
+ for (pugi::xml_attribute attrib : mNode->attributes()) {
+ if (!ASSIMP_stricmp(attrib.name(), "name")) {
+ out.name = std::string(attrib.value());
+ } else if (!ASSIMP_stricmp(attrib.name(), "value")) {
+ // three floats, separated with commas
+ const char *ptr = attrib.value();
+
+ SkipSpaces(&ptr);
+ ptr = fast_atoreal_move<float>( ptr,(float&)out.value.x );
+ SkipSpaces(&ptr);
+ if (',' != *ptr) {
+ ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition");
+ } else {
+ SkipSpaces(ptr + 1, &ptr);
+ }
+ ptr = fast_atoreal_move<float>( ptr,(float&)out.value.y );
+ SkipSpaces(&ptr);
+ if (',' != *ptr) {
+ ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition");
+ } else {
+ SkipSpaces(ptr + 1, &ptr);
+ }
+ ptr = fast_atoreal_move<float>( ptr,(float&)out.value.z );
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a string to a proper aiMappingMode
+int ConvertMappingMode(const std::string& mode) {
+ if (mode == "texture_clamp_repeat") {
+ return aiTextureMapMode_Wrap;
+ } else if (mode == "texture_clamp_mirror") {
+ return aiTextureMapMode_Mirror;
+ }
+
+ return aiTextureMapMode_Clamp;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse a material from the XML file
+aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
+ aiMaterial* mat = new aiMaterial();
+ aiColor4D clr;
+ aiString s;
+
+ matFlags = 0; // zero output flags
+ int cnt = 0; // number of used texture channels
+ unsigned int nd = 0;
+
+ for (pugi::xml_node child : mNode->children()) {
+ if (!ASSIMP_stricmp(child.name(), "color")) { // Hex properties
+ HexProperty prop;
+ ReadHexProperty(prop);
+ if (prop.name == "Diffuse") {
+ ColorFromARGBPacked(prop.value, clr);
+ mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_DIFFUSE);
+ } else if (prop.name == "Ambient") {
+ ColorFromARGBPacked(prop.value, clr);
+ mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_AMBIENT);
+ } else if (prop.name == "Specular") {
+ ColorFromARGBPacked(prop.value, clr);
+ mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_SPECULAR);
+ }
+
+ // NOTE: The 'emissive' property causes problems. It is
+ // often != 0, even if there is obviously no light
+ // emitted by the described surface. In fact I think
+ // IRRLICHT ignores this property, too.
+#if 0
+ else if (prop.name == "Emissive") {
+ ColorFromARGBPacked(prop.value,clr);
+ mat->AddProperty(&clr,1,AI_MATKEY_COLOR_EMISSIVE);
+ }
+#endif
+ } else if (!ASSIMP_stricmp(child.name(), "float")) { // Float properties
+ FloatProperty prop;
+ ReadFloatProperty(prop);
+ if (prop.name == "Shininess") {
+ mat->AddProperty(&prop.value, 1, AI_MATKEY_SHININESS);
+ }
+ } else if (!ASSIMP_stricmp(child.name(), "bool")) { // Bool properties
+ BoolProperty prop;
+ ReadBoolProperty(prop);
+ if (prop.name == "Wireframe") {
+ int val = (prop.value ? true : false);
+ mat->AddProperty(&val, 1, AI_MATKEY_ENABLE_WIREFRAME);
+ } else if (prop.name == "GouraudShading") {
+ int val = (prop.value ? aiShadingMode_Gouraud : aiShadingMode_NoShading);
+ mat->AddProperty(&val, 1, AI_MATKEY_SHADING_MODEL);
+ } else if (prop.name == "BackfaceCulling") {
+ int val = (!prop.value);
+ mat->AddProperty(&val, 1, AI_MATKEY_TWOSIDED);
+ }
+ } else if (!ASSIMP_stricmp(child.name(), "texture") ||
+ !ASSIMP_stricmp(child.name(), "enum")) { // String properties - textures and texture related properties
+ StringProperty prop;
+ ReadStringProperty(prop);
+ if (prop.value.length()) {
+ // material type (shader)
+ if (prop.name == "Type") {
+ if (prop.value == "solid") {
+ // default material ...
+ } else if (prop.value == "trans_vertex_alpha") {
+ matFlags = AI_IRRMESH_MAT_trans_vertex_alpha;
+ } else if (prop.value == "lightmap") {
+ matFlags = AI_IRRMESH_MAT_lightmap;
+ } else if (prop.value == "solid_2layer") {
+ matFlags = AI_IRRMESH_MAT_solid_2layer;
+ } else if (prop.value == "lightmap_m2") {
+ matFlags = AI_IRRMESH_MAT_lightmap_m2;
+ } else if (prop.value == "lightmap_m4") {
+ matFlags = AI_IRRMESH_MAT_lightmap_m4;
+ } else if (prop.value == "lightmap_light") {
+ matFlags = AI_IRRMESH_MAT_lightmap_light;
+ } else if (prop.value == "lightmap_light_m2") {
+ matFlags = AI_IRRMESH_MAT_lightmap_light_m2;
+ } else if (prop.value == "lightmap_light_m4") {
+ matFlags = AI_IRRMESH_MAT_lightmap_light_m4;
+ } else if (prop.value == "lightmap_add") {
+ matFlags = AI_IRRMESH_MAT_lightmap_add;
+ } else if (prop.value == "normalmap_solid" ||
+ prop.value == "parallaxmap_solid") { // Normal and parallax maps are treated equally
+ matFlags = AI_IRRMESH_MAT_normalmap_solid;
+ } else if (prop.value == "normalmap_trans_vertex_alpha" ||
+ prop.value == "parallaxmap_trans_vertex_alpha") {
+ matFlags = AI_IRRMESH_MAT_normalmap_tva;
+ } else if (prop.value == "normalmap_trans_add" ||
+ prop.value == "parallaxmap_trans_add") {
+ matFlags = AI_IRRMESH_MAT_normalmap_ta;
+ } else {
+ ASSIMP_LOG_WARN("IRRMat: Unrecognized material type: ", prop.value);
+ }
+ }
+
+ // Up to 4 texture channels are supported
+ if (prop.name == "Texture1") {
+ // Always accept the primary texture channel
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0));
+ } else if (prop.name == "Texture2" && cnt == 1) {
+ // 2-layer material lightmapped?
+ if (matFlags & AI_IRRMESH_MAT_lightmap) {
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s, AI_MATKEY_TEXTURE_LIGHTMAP(0));
+
+ // set the corresponding material flag
+ matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
+ } else if (matFlags & AI_IRRMESH_MAT_normalmap_solid) { // alternatively: normal or parallax mapping
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s, AI_MATKEY_TEXTURE_NORMALS(0));
+
+ // set the corresponding material flag
+ matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
+ } else if (matFlags & AI_IRRMESH_MAT_solid_2layer) { // or just as second diffuse texture
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(1));
+ ++nd;
+
+ // set the corresponding material flag
+ matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
+ } else {
+ ASSIMP_LOG_WARN("IRRmat: Skipping second texture");
+ }
+ } else if (prop.name == "Texture3" && cnt == 2) {
+ // Irrlicht does not seem to use these channels.
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(nd + 1));
+ } else if (prop.name == "Texture4" && cnt == 3) {
+ // Irrlicht does not seem to use these channels.
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(nd + 2));
+ }
+
+ // Texture mapping options
+ if (prop.name == "TextureWrap1" && cnt >= 1) {
+ int map = ConvertMappingMode(prop.value);
+ mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
+ mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
+ } else if (prop.name == "TextureWrap2" && cnt >= 2) {
+ int map = ConvertMappingMode(prop.value);
+ if (matFlags & AI_IRRMESH_MAT_lightmap) {
+ mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_LIGHTMAP(0));
+ mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_LIGHTMAP(0));
+ } else if (matFlags & (AI_IRRMESH_MAT_normalmap_solid)) {
+ mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_NORMALS(0));
+ mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_NORMALS(0));
+ } else if (matFlags & AI_IRRMESH_MAT_solid_2layer) {
+ mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(1));
+ mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(1));
+ }
+ } else if (prop.name == "TextureWrap3" && cnt >= 3) {
+ int map = ConvertMappingMode(prop.value);
+ mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(nd + 1));
+ mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(nd + 1));
+ } else if (prop.name == "TextureWrap4" && cnt >= 4) {
+ int map = ConvertMappingMode(prop.value);
+ mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(nd + 2));
+ mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(nd + 2));
+ }
+ }
+ }
+ //break;
+ /*case EXN_ELEMENT_END:
+
+ // Assume there are no further nested nodes in <material> elements
+ if ( !ASSIMP_stricmp(reader->getNodeName(),"material") ||
+ !ASSIMP_stricmp(reader->getNodeName(),"attributes"))
+ {
+ // Now process lightmapping flags
+ // We should have at least one textur to do that ..
+ if (cnt && matFlags & AI_IRRMESH_MAT_lightmap)
+ {
+ float f = 1.f;
+ unsigned int unmasked = matFlags&~AI_IRRMESH_MAT_lightmap;
+
+ // Additive lightmap?
+ int op = (unmasked & AI_IRRMESH_MAT_lightmap_add
+ ? aiTextureOp_Add : aiTextureOp_Multiply);
+
+ // Handle Irrlicht's lightmapping scaling factor
+ if (unmasked & AI_IRRMESH_MAT_lightmap_m2 ||
+ unmasked & AI_IRRMESH_MAT_lightmap_light_m2)
+ {
+ f = 2.f;
+ }
+ else if (unmasked & AI_IRRMESH_MAT_lightmap_m4 ||
+ unmasked & AI_IRRMESH_MAT_lightmap_light_m4)
+ {
+ f = 4.f;
+ }
+ mat->AddProperty( &f, 1, AI_MATKEY_TEXBLEND_LIGHTMAP(0));
+ mat->AddProperty( &op,1, AI_MATKEY_TEXOP_LIGHTMAP(0));
+ }
+
+ return mat;
+ }
+ default:
+
+ // GCC complains here ...
+ break;
+ }
+ }*/
+ }
+ ASSIMP_LOG_ERROR("IRRMESH: Unexpected end of file. Material is not complete");
+
+ return mat;
+}
+
+#endif // !(defined(ASSIMP_BUILD_NO_IRR_IMPORTER) && defined(ASSIMP_BUILD_NO_IRRMESH_IMPORTER))
diff --git a/libs/assimp/code/AssetLib/Irr/IRRShared.h b/libs/assimp/code/AssetLib/Irr/IRRShared.h
new file mode 100644
index 0000000..90e212d
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Irr/IRRShared.h
@@ -0,0 +1,119 @@
+
+
+/** @file IRRShared.h
+ * @brief Shared utilities for the IRR and IRRMESH loaders
+ */
+
+#ifndef INCLUDED_AI_IRRSHARED_H
+#define INCLUDED_AI_IRRSHARED_H
+
+#include <assimp/BaseImporter.h>
+#include <assimp/XmlParser.h>
+#include <cstdint>
+
+struct aiMaterial;
+
+namespace Assimp {
+
+/** @brief Matrix to convert from Assimp to IRR and backwards
+ */
+extern const aiMatrix4x4 AI_TO_IRR_MATRIX;
+
+// Default: 0 = solid, one texture
+#define AI_IRRMESH_MAT_solid_2layer 0x10000
+
+// Transparency flags
+#define AI_IRRMESH_MAT_trans_vertex_alpha 0x1
+#define AI_IRRMESH_MAT_trans_add 0x2
+
+// Lightmapping flags
+#define AI_IRRMESH_MAT_lightmap 0x2
+#define AI_IRRMESH_MAT_lightmap_m2 (AI_IRRMESH_MAT_lightmap | 0x4)
+#define AI_IRRMESH_MAT_lightmap_m4 (AI_IRRMESH_MAT_lightmap | 0x8)
+#define AI_IRRMESH_MAT_lightmap_light (AI_IRRMESH_MAT_lightmap | 0x10)
+#define AI_IRRMESH_MAT_lightmap_light_m2 (AI_IRRMESH_MAT_lightmap | 0x20)
+#define AI_IRRMESH_MAT_lightmap_light_m4 (AI_IRRMESH_MAT_lightmap | 0x40)
+#define AI_IRRMESH_MAT_lightmap_add (AI_IRRMESH_MAT_lightmap | 0x80)
+
+// Standard NormalMap (or Parallax map, they're treated equally)
+#define AI_IRRMESH_MAT_normalmap_solid (0x100)
+
+// Normal map combined with vertex alpha
+#define AI_IRRMESH_MAT_normalmap_tva \
+ (AI_IRRMESH_MAT_normalmap_solid | AI_IRRMESH_MAT_trans_vertex_alpha)
+
+// Normal map combined with additive transparency
+#define AI_IRRMESH_MAT_normalmap_ta \
+ (AI_IRRMESH_MAT_normalmap_solid | AI_IRRMESH_MAT_trans_add)
+
+// Special flag. It indicates a second texture has been found
+// Its type depends ... either a normal textue or a normal map
+#define AI_IRRMESH_EXTRA_2ND_TEXTURE 0x100000
+
+// ---------------------------------------------------------------------------
+/** Base class for the Irr and IrrMesh importers.
+ *
+ * Declares some irrlight-related xml parsing utilities and provides tools
+ * to load materials from IRR and IRRMESH files.
+ */
+class IrrlichtBase {
+protected:
+ IrrlichtBase() :
+ mNode(nullptr) {
+ // empty
+ }
+
+ ~IrrlichtBase() {
+ // empty
+ }
+
+ /** @brief Data structure for a simple name-value property
+ */
+ template <class T>
+ struct Property {
+ std::string name;
+ T value;
+ };
+
+ typedef Property<uint32_t> HexProperty;
+ typedef Property<std::string> StringProperty;
+ typedef Property<bool> BoolProperty;
+ typedef Property<float> FloatProperty;
+ typedef Property<aiVector3D> VectorProperty;
+ typedef Property<int> IntProperty;
+
+ /// XML reader instance
+ XmlParser mParser;
+ pugi::xml_node *mNode;
+
+ // -------------------------------------------------------------------
+ /** Parse a material description from the XML
+ * @return The created material
+ * @param matFlags Receives AI_IRRMESH_MAT_XX flags
+ */
+ aiMaterial *ParseMaterial(unsigned int &matFlags);
+
+ // -------------------------------------------------------------------
+ /** Read a property of the specified type from the current XML element.
+ * @param out Receives output data
+ */
+ void ReadHexProperty(HexProperty &out);
+ void ReadStringProperty(StringProperty &out);
+ void ReadBoolProperty(BoolProperty &out);
+ void ReadFloatProperty(FloatProperty &out);
+ void ReadVectorProperty(VectorProperty &out);
+ void ReadIntProperty(IntProperty &out);
+};
+
+// ------------------------------------------------------------------------------------------------
+// Unpack a hex color, e.g. 0xdcdedfff
+inline void ColorFromARGBPacked(uint32_t in, aiColor4D &clr) {
+ clr.a = ((in >> 24) & 0xff) / 255.f;
+ clr.r = ((in >> 16) & 0xff) / 255.f;
+ clr.g = ((in >> 8) & 0xff) / 255.f;
+ clr.b = ((in)&0xff) / 255.f;
+}
+
+} // end namespace Assimp
+
+#endif // !! INCLUDED_AI_IRRSHARED_H