Bladeren bron

Apply clang-format to files

PencilAmazing 2 jaren geleden
bovenliggende
commit
537b445a59
5 gewijzigde bestanden met toevoegingen van 1860 en 1876 verwijderingen
  1. 1194 1195
      code/AssetLib/Irr/IRRLoader.cpp
  2. 53 68
      code/AssetLib/Irr/IRRLoader.h
  3. 402 402
      code/AssetLib/Irr/IRRMeshLoader.cpp
  4. 209 209
      code/AssetLib/Irr/IRRShared.cpp
  5. 2 2
      code/AssetLib/Irr/IRRShared.h

+ 1194 - 1195
code/AssetLib/Irr/IRRLoader.cpp

@@ -67,23 +67,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace Assimp;
 using namespace Assimp;
 
 
 static const aiImporterDesc desc = {
 static const aiImporterDesc desc = {
-	"Irrlicht Scene Reader",
-	"",
-	"",
-	"http://irrlicht.sourceforge.net/",
-	aiImporterFlags_SupportTextFlavour,
-	0,
-	0,
-	0,
-	0,
-	"irr xml"
+    "Irrlicht Scene Reader",
+    "",
+    "",
+    "http://irrlicht.sourceforge.net/",
+    aiImporterFlags_SupportTextFlavour,
+    0,
+    0,
+    0,
+    0,
+    "irr xml"
 };
 };
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 // Constructor to be privately used by Importer
 IRRImporter::IRRImporter() :
 IRRImporter::IRRImporter() :
-		fps(), configSpeedFlag() {
-	// empty
+        fps(), configSpeedFlag() {
+    // empty
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -93,154 +93,154 @@ IRRImporter::~IRRImporter() = default;
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
 // Returns whether the class can handle the format of the given file.
 bool IRRImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
 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));
+    static const char *tokens[] = { "irr_scene" };
+    return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 const aiImporterDesc *IRRImporter::GetInfo() const {
 const aiImporterDesc *IRRImporter::GetInfo() const {
-	return &desc;
+    return &desc;
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void IRRImporter::SetupProperties(const Importer *pImp) {
 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));
+    // 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)
 // Build a mesh that consists of a single squad (a side of a skybox)
 aiMesh *IRRImporter::BuildSingleQuadMesh(const SkyboxVertex &v1,
 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;
+        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) {
 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);
+    // 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,
 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();
+        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;
             aiString s;
             s.Set(AI_DEFAULT_MATERIAL_NAME);
             s.Set(AI_DEFAULT_MATERIAL_NAME);
@@ -248,1110 +248,1109 @@ void IRRImporter::CopyMaterial(std::vector<aiMaterial *> &materials,
 
 
             aiColor3D c(0.6f,0.6f,0.6f);
             aiColor3D c(0.6f,0.6f,0.6f);
             mat->AddProperty(&c,1,AI_MATKEY_COLOR_DIFFUSE);*/
             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);
+        }
+        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) {
 inline int ClampSpline(int idx, int size) {
-	return (idx < 0 ? size + idx : (idx >= size ? idx - size : idx));
+    return (idx < 0 ? size + idx : (idx >= size ? idx - size : idx));
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 inline void FindSuitableMultiple(int &angle) {
 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;
+    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) {
 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;
-		}
-	}
+    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
 // 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)) {
 void SetupMapping(aiMaterial *mat, aiTextureMapping mode, const aiVector3D &axis = aiVector3D(0.f, 0.f, -1.f)) {
-	if (nullptr == mat) {
-		return;
-	}
+    if (nullptr == mat) {
+        return;
+    }
 
 
     // Check whether there are texture properties defined - setup
     // 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);
+    // 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,
 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.emplace_back(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);
-		}
-	}
+        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.emplace_back(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.
 // Imports the given file into the given scene structure.
 void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
 void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
-	std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
+    std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
 
 
-	// Check whether we can read from the file
+    // Check whether we can read from the file
     if (file == nullptr) {
     if (file == nullptr) {
         throw DeadlyImportError("Failed to open IRR file ", pFile);
         throw DeadlyImportError("Failed to open IRR file ", pFile);
     }
     }
 
 
     // Construct the irrXML parser
     // Construct the irrXML parser
-	XmlParser st;
-    if (!st.parse( file.get() )) {
+    XmlParser st;
+    if (!st.parse(file.get())) {
         throw DeadlyImportError("XML parse error while loading IRR file ", pFile);
         throw DeadlyImportError("XML parse error while loading IRR file ", pFile);
     }
     }
     pugi::xml_node rootElement = st.getRootNode();
     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
+    // 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.
                      */
                      */
-					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.emplace_back();
-						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.emplace_back();
-						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.emplace_back();
-											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
+                    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.emplace_back();
+                    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.emplace_back();
+                    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.emplace_back();
+                                        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
+                                    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/
                                      *  we need to update the name in the aiLight/
                                      *  aiCamera structure, too.
                                      *  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
+                                    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
                                      *  animated or not. We need to make sure we setup
                                      *  the correct post-processing settings here.
                                      *  the correct post-processing settings here.
                                      */
                                      */
-										unsigned int pp = 0;
-										BatchLoader::PropertyMap map;
+                                    unsigned int pp = 0;
+                                    BatchLoader::PropertyMap map;
 
 
-										/* If the mesh is a static one remove all animations from the impor data
+                                    /* 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.
                                      */
                                      */
-										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;
+
+                                    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
 #endif // !! ASSIMP_BUILD_NO_IRR_IMPORTER

+ 53 - 68
code/AssetLib/Irr/IRRLoader.h

@@ -53,7 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/StringUtils.h>
 #include <assimp/StringUtils.h>
 #include <assimp/anim.h>
 #include <assimp/anim.h>
 
 
-namespace Assimp    {
+namespace Assimp {
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** Irr importer class.
 /** Irr importer class.
@@ -71,13 +71,13 @@ public:
     /** Returns whether the class can handle the format of the given file.
     /** Returns whether the class can handle the format of the given file.
      *  See BaseImporter::CanRead() for details.
      *  See BaseImporter::CanRead() for details.
      */
      */
-    bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
-        bool checkSig) const override;
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler,
+            bool checkSig) const override;
 
 
 protected:
 protected:
-    const aiImporterDesc* GetInfo () const override;
-    void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) override;
-    void SetupProperties(const Importer* pImp) override;
+    const aiImporterDesc *GetInfo() const override;
+    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
+    void SetupProperties(const Importer *pImp) override;
 
 
 private:
 private:
     /** Data structure for a scene-graph node animator
     /** Data structure for a scene-graph node animator
@@ -85,27 +85,19 @@ private:
     struct Animator {
     struct Animator {
         // Type of the animator
         // Type of the animator
         enum AT {
         enum AT {
-            UNKNOWN       = 0x0,
-            ROTATION      = 0x1,
-            FLY_CIRCLE    = 0x2,
-            FLY_STRAIGHT  = 0x3,
+            UNKNOWN = 0x0,
+            ROTATION = 0x1,
+            FLY_CIRCLE = 0x2,
+            FLY_STRAIGHT = 0x3,
             FOLLOW_SPLINE = 0x4,
             FOLLOW_SPLINE = 0x4,
-            OTHER         = 0x5
+            OTHER = 0x5
 
 
         } type;
         } 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)
-        {
+        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
         // common parameters
         ai_real speed;
         ai_real speed;
         aiVector3D direction;
         aiVector3D direction;
@@ -128,11 +120,9 @@ private:
 
 
     /** Data structure for a scene-graph node in an IRR file
     /** Data structure for a scene-graph node in an IRR file
      */
      */
-    struct Node
-    {
+    struct Node {
         // Type of the node
         // Type of the node
-        enum ET
-        {
+        enum ET {
             LIGHT,
             LIGHT,
             CUBE,
             CUBE,
             MESH,
             MESH,
@@ -144,21 +134,20 @@ private:
             ANIMMESH
             ANIMMESH
         } type;
         } 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)
-        {
+        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
             // Generate a default name for the node
             char buffer[128];
             char buffer[128];
             static int cnt;
             static int cnt;
-            ai_snprintf(buffer, 128, "IrrNode_%i",cnt++);
+            ai_snprintf(buffer, 128, "IrrNode_%i", cnt++);
             name = std::string(buffer);
             name = std::string(buffer);
 
 
             // reserve space for up to 5 materials
             // reserve space for up to 5 materials
@@ -175,10 +164,10 @@ private:
         std::string name;
         std::string name;
 
 
         // List of all child nodes
         // List of all child nodes
-        std::vector<Node*> children;
+        std::vector<Node *> children;
 
 
         // Parent node
         // Parent node
-        Node* parent;
+        Node *parent;
 
 
         // Animated meshes: frames per second
         // Animated meshes: frames per second
         // 0.f if not specified
         // 0.f if not specified
@@ -190,13 +179,13 @@ private:
 
 
         // Meshes: List of materials to be assigned
         // Meshes: List of materials to be assigned
         // along with their corresponding material flags
         // along with their corresponding material flags
-        std::vector< std::pair<aiMaterial*, unsigned int> > materials;
+        std::vector<std::pair<aiMaterial *, unsigned int>> materials;
 
 
         // Spheres: radius of the sphere to be generates
         // Spheres: radius of the sphere to be generates
         ai_real sphereRadius;
         ai_real sphereRadius;
 
 
         // Spheres: Number of polygons in the x,y direction
         // Spheres: Number of polygons in the x,y direction
-        unsigned int spherePolyCountX,spherePolyCountY;
+        unsigned int spherePolyCountX, spherePolyCountY;
 
 
         // List of all animators assigned to the node
         // List of all animators assigned to the node
         std::list<Animator> animators;
         std::list<Animator> animators;
@@ -204,40 +193,36 @@ private:
 
 
     /** Data structure for a vertex in an IRR skybox
     /** Data structure for a vertex in an IRR skybox
      */
      */
-    struct SkyboxVertex
-    {
+    struct SkyboxVertex {
         SkyboxVertex() = default;
         SkyboxVertex() = default;
 
 
         //! Construction from single vertex components
         //! Construction from single vertex components
         SkyboxVertex(ai_real px, ai_real py, ai_real pz,
         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)
+                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)
-        {}
+                :
+                position(px, py, pz), normal(nx, ny, nz), uv(uvx, uvy, 0.0) {}
 
 
         aiVector3D position, normal, uv;
         aiVector3D position, normal, uv;
     };
     };
 
 
-
     // -------------------------------------------------------------------
     // -------------------------------------------------------------------
     /// Fill the scene-graph recursively
     /// 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);
+    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
     /// Generate a mesh that consists of just a single quad
-    aiMesh* BuildSingleQuadMesh(const SkyboxVertex& v1,
-        const SkyboxVertex& v2,
-        const SkyboxVertex& v3,
-        const SkyboxVertex& v4);
+    aiMesh *BuildSingleQuadMesh(const SkyboxVertex &v1,
+            const SkyboxVertex &v2,
+            const SkyboxVertex &v3,
+            const SkyboxVertex &v4);
 
 
     // -------------------------------------------------------------------
     // -------------------------------------------------------------------
     /// Build a sky-box
     /// Build a sky-box
@@ -245,8 +230,8 @@ private:
     /// @param meshes Receives 6 output meshes
     /// @param meshes Receives 6 output meshes
     /// @param materials The last 6 materials are assigned to the newly
     /// @param materials The last 6 materials are assigned to the newly
     ///                  created meshes. The names of the materials are adjusted.
     ///                  created meshes. The names of the materials are adjusted.
-    void BuildSkybox(std::vector<aiMesh*>& meshes,
-        std::vector<aiMaterial*> materials);
+    void BuildSkybox(std::vector<aiMesh *> &meshes,
+            std::vector<aiMaterial *> materials);
 
 
     // -------------------------------------------------------------------
     // -------------------------------------------------------------------
     /** Copy a material for a mesh to the output material list
     /** Copy a material for a mesh to the output material list
@@ -256,10 +241,10 @@ private:
      *  @param defMatIdx Default material index - UINT_MAX if not present
      *  @param defMatIdx Default material index - UINT_MAX if not present
      *  @param mesh Mesh to work on
      *  @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);
+    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
     /** Compute animations for a specific node
@@ -267,8 +252,8 @@ private:
      *  @param root Node to be processed
      *  @param root Node to be processed
      *  @param anims The list of output animations
      *  @param anims The list of output animations
      */
      */
-    void ComputeAnimations(Node* root, aiNode* real,
-        std::vector<aiNodeAnim*>& anims);
+    void ComputeAnimations(Node *root, aiNode *real,
+            std::vector<aiNodeAnim *> &anims);
 
 
 private:
 private:
     /// Configuration option: desired output FPS
     /// Configuration option: desired output FPS

+ 402 - 402
code/AssetLib/Irr/IRRMeshLoader.cpp

@@ -57,16 +57,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace Assimp;
 using namespace Assimp;
 
 
 static const aiImporterDesc desc = {
 static const aiImporterDesc desc = {
-	"Irrlicht Mesh Reader",
-	"",
-	"",
-	"http://irrlicht.sourceforge.net/",
-	aiImporterFlags_SupportTextFlavour,
-	0,
-	0,
-	0,
-	0,
-	"xml irrmesh"
+    "Irrlicht Mesh Reader",
+    "",
+    "",
+    "http://irrlicht.sourceforge.net/",
+    aiImporterFlags_SupportTextFlavour,
+    0,
+    0,
+    0,
+    0,
+    "xml irrmesh"
 };
 };
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -80,419 +80,419 @@ IRRMeshImporter::~IRRMeshImporter() = default;
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
 // Returns whether the class can handle the format of the given file.
 bool IRRMeshImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
 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));
+    /* 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
 // Get a list of all file extensions which are handled by this class
 const aiImporterDesc *IRRMeshImporter::GetInfo() const {
 const aiImporterDesc *IRRMeshImporter::GetInfo() const {
-	return &desc;
+    return &desc;
 }
 }
 
 
 static void releaseMaterial(aiMaterial **mat) {
 static void releaseMaterial(aiMaterial **mat) {
-	if (*mat != nullptr) {
-		delete *mat;
-		*mat = nullptr;
-	}
+    if (*mat != nullptr) {
+        delete *mat;
+        *mat = nullptr;
+    }
 }
 }
 
 
 static void releaseMesh(aiMesh **mesh) {
 static void releaseMesh(aiMesh **mesh) {
-	if (*mesh != nullptr) {
-		delete *mesh;
-		*mesh = nullptr;
-	}
+    if (*mesh != nullptr) {
+        delete *mesh;
+        *mesh = nullptr;
+    }
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
 // Imports the given file into the given scene structure.
 void IRRMeshImporter::InternReadFile(const std::string &pFile,
 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 == nullptr)
-		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");
+        aiScene *pScene, IOSystem *pIOHandler) {
+    std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
+
+    // Check whether we can read from the file
+    if (file == nullptr)
+        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");
                 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
+                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
                 line. So we can skip the rest of the line - unknown vertex
                 elements are ignored.
                 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;
-	}
+                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
 #endif // !! ASSIMP_BUILD_NO_IRRMESH_IMPORTER

+ 209 - 209
code/AssetLib/Irr/IRRShared.cpp

@@ -43,59 +43,59 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *  @brief Shared utilities for the IRR and IRRMESH loaders
  *  @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.
+// 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))
 #if !(defined(ASSIMP_BUILD_NO_IRR_IMPORTER) && defined(ASSIMP_BUILD_NO_IRRMESH_IMPORTER))
 
 
 #include "IRRShared.h"
 #include "IRRShared.h"
 #include <assimp/ParsingUtils.h>
 #include <assimp/ParsingUtils.h>
 #include <assimp/fast_atof.h>
 #include <assimp/fast_atof.h>
-#include <assimp/DefaultLogger.hpp>
 #include <assimp/material.h>
 #include <assimp/material.h>
+#include <assimp/DefaultLogger.hpp>
 
 
 using namespace Assimp;
 using namespace Assimp;
 
 
 // Transformation matrix to convert from Assimp to IRR space
 // 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);
+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)
 // read a property in hexadecimal format (i.e. ffffffff)
-void IrrlichtBase::ReadHexProperty(HexProperty &out ) {
-	for (pugi::xml_attribute attrib : mNode->attributes()) {
+void IrrlichtBase::ReadHexProperty(HexProperty &out) {
+    for (pugi::xml_attribute attrib : mNode->attributes()) {
         if (!ASSIMP_stricmp(attrib.name(), "name")) {
         if (!ASSIMP_stricmp(attrib.name(), "name")) {
-            out.name = std::string( attrib.value() );
-        } else if (!ASSIMP_stricmp(attrib.name(),"value")) {
+            out.name = std::string(attrib.value());
+        } else if (!ASSIMP_stricmp(attrib.name(), "value")) {
             // parse the hexadecimal value
             // parse the hexadecimal value
-			out.value = strtoul16(attrib.name());
+            out.value = strtoul16(attrib.name());
         }
         }
     }
     }
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // read a decimal property
 // 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")) {
+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
             // parse the int value
-			out.value = strtol10(attrib.name());
+            out.value = strtol10(attrib.name());
         }
         }
     }
     }
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // read a string property
 // 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")) {
+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
             // simple copy the string
-			out.value = std::string(attrib.value());
+            out.value = std::string(attrib.value());
         }
         }
     }
     }
 }
 }
@@ -103,12 +103,12 @@ void IrrlichtBase::ReadStringProperty( StringProperty& out) {
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // read a boolean property
 // read a boolean property
 void IrrlichtBase::ReadBoolProperty(BoolProperty &out) {
 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")) {
+    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
             // true or false, case insensitive
-			out.value = (ASSIMP_stricmp(attrib.value(), "true") ? false : true);
+            out.value = (ASSIMP_stricmp(attrib.value(), "true") ? false : true);
         }
         }
     }
     }
 }
 }
@@ -116,229 +116,229 @@ void IrrlichtBase::ReadBoolProperty(BoolProperty &out) {
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // read a float property
 // read a float property
 void IrrlichtBase::ReadFloatProperty(FloatProperty &out) {
 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")) {
+    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
             // just parse the float
-			out.value = fast_atof(attrib.value());
+            out.value = fast_atof(attrib.value());
         }
         }
     }
     }
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // read a vector property
 // 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")) {
+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
             // three floats, separated with commas
             const char *ptr = attrib.value();
             const char *ptr = attrib.value();
 
 
             SkipSpaces(&ptr);
             SkipSpaces(&ptr);
-            ptr = fast_atoreal_move<float>( ptr,(float&)out.value.x );
+            ptr = fast_atoreal_move<float>(ptr, (float &)out.value.x);
             SkipSpaces(&ptr);
             SkipSpaces(&ptr);
             if (',' != *ptr) {
             if (',' != *ptr) {
                 ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition");
                 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 );
+            } else {
+                SkipSpaces(ptr + 1, &ptr);
+            }
+            ptr = fast_atoreal_move<float>(ptr, (float &)out.value.y);
             SkipSpaces(&ptr);
             SkipSpaces(&ptr);
             if (',' != *ptr) {
             if (',' != *ptr) {
                 ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition");
                 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 );
+            } else {
+                SkipSpaces(ptr + 1, &ptr);
+            }
+            ptr = fast_atoreal_move<float>(ptr, (float &)out.value.z);
         }
         }
     }
     }
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Convert a string to a proper aiMappingMode
 // Convert a string to a proper aiMappingMode
-int ConvertMappingMode(const std::string& mode) {
+int ConvertMappingMode(const std::string &mode) {
     if (mode == "texture_clamp_repeat") {
     if (mode == "texture_clamp_repeat") {
         return aiTextureMapMode_Wrap;
         return aiTextureMapMode_Wrap;
-	} else if (mode == "texture_clamp_mirror") {
-		return aiTextureMapMode_Mirror;
-	}
+    } else if (mode == "texture_clamp_mirror") {
+        return aiTextureMapMode_Mirror;
+    }
 
 
     return aiTextureMapMode_Clamp;
     return aiTextureMapMode_Clamp;
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Parse a material from the XML file
 // Parse a material from the XML file
-aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
-    aiMaterial* mat = new aiMaterial();
+aiMaterial *IrrlichtBase::ParseMaterial(unsigned int &matFlags) {
+    aiMaterial *mat = new aiMaterial();
     aiColor4D clr;
     aiColor4D clr;
     aiString s;
     aiString s;
 
 
     matFlags = 0; // zero output flags
     matFlags = 0; // zero output flags
-    int cnt  = 0; // number of used texture channels
+    int cnt = 0; // number of used texture channels
     unsigned int nd = 0;
     unsigned int nd = 0;
 
 
     for (pugi::xml_node child : mNode->children()) {
     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 (!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
 #if 0
             else if (prop.name == "Emissive") {
             else if (prop.name == "Emissive") {
                 ColorFromARGBPacked(prop.value,clr);
                 ColorFromARGBPacked(prop.value,clr);
                 mat->AddProperty(&clr,1,AI_MATKEY_COLOR_EMISSIVE);
                 mat->AddProperty(&clr,1,AI_MATKEY_COLOR_EMISSIVE);
             }
             }
 #endif
 #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:
+        } 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
                 // Assume there are no further nested nodes in <material> elements
                 if ( !ASSIMP_stricmp(reader->getNodeName(),"material") ||
                 if ( !ASSIMP_stricmp(reader->getNodeName(),"material") ||
@@ -378,7 +378,7 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
                 break;
                 break;
         }
         }
     }*/
     }*/
-	}
+    }
     ASSIMP_LOG_ERROR("IRRMESH: Unexpected end of file. Material is not complete");
     ASSIMP_LOG_ERROR("IRRMESH: Unexpected end of file. Material is not complete");
 
 
     return mat;
     return mat;

+ 2 - 2
code/AssetLib/Irr/IRRShared.h

@@ -1,8 +1,8 @@
 
 
 
 
 /** @file  IRRShared.h
 /** @file  IRRShared.h
-  * @brief Shared utilities for the IRR and IRRMESH loaders
-  */
+ * @brief Shared utilities for the IRR and IRRMESH loaders
+ */
 
 
 #ifndef INCLUDED_AI_IRRSHARED_H
 #ifndef INCLUDED_AI_IRRSHARED_H
 #define INCLUDED_AI_IRRSHARED_H
 #define INCLUDED_AI_IRRSHARED_H