From 261c7769b869bfc34b69b960b75a20f944ff28bb Mon Sep 17 00:00:00 2001 From: Marco Feuerstein Date: Tue, 27 Jun 2023 21:25:11 +0200 Subject: [PATCH 01/12] Replace static list of supported file extensions with the actually supported ones. --- port/PyAssimp/pyassimp/core.py | 13 ++++++++++ port/PyAssimp/pyassimp/formats.py | 41 ------------------------------- 2 files changed, 13 insertions(+), 41 deletions(-) delete mode 100644 port/PyAssimp/pyassimp/formats.py diff --git a/port/PyAssimp/pyassimp/core.py b/port/PyAssimp/pyassimp/core.py index edde8b29a..af5138581 100644 --- a/port/PyAssimp/pyassimp/core.py +++ b/port/PyAssimp/pyassimp/core.py @@ -390,6 +390,19 @@ def export_blob(scene, raise AssimpError('Could not export scene to blob!') return exportBlobPtr +def available_formats(): + """ + Return a list of file format extensions supported to import. + + Returns + --------- + A list of upper-case file extensions, e.g. [3DS, OBJ] + """ + from ctypes import byref + extension_list = structs.String() + _assimp_lib.dll.aiGetExtensionList(byref(extension_list)) + return [e[2:].upper() for e in str(extension_list.data, "utf-8").split(";") if e] + def _finalize_texture(tex, target): setattr(target, "achformathint", tex.achFormatHint) if numpy: diff --git a/port/PyAssimp/pyassimp/formats.py b/port/PyAssimp/pyassimp/formats.py deleted file mode 100644 index 5d454e5b7..000000000 --- a/port/PyAssimp/pyassimp/formats.py +++ /dev/null @@ -1,41 +0,0 @@ -FORMATS = ["CSM", - "LWS", - "B3D", - "COB", - "PLY", - "IFC", - "OFF", - "SMD", - "IRRMESH", - "3D", - "DAE", - "MDL", - "HMP", - "TER", - "WRL", - "XML", - "NFF", - "AC", - "OBJ", - "3DS", - "STL", - "IRR", - "Q3O", - "Q3D", - "MS3D", - "Q3S", - "ZGL", - "MD2", - "X", - "BLEND", - "XGL", - "MD5MESH", - "MAX", - "LXO", - "DXF", - "BVH", - "LWO", - "NDO"] - -def available_formats(): - return FORMATS From 59f262016411bb3188baea2b05a2bb616290b07f Mon Sep 17 00:00:00 2001 From: Marco Feuerstein Date: Wed, 28 Jun 2023 08:20:49 +0200 Subject: [PATCH 02/12] Remove unneeded check for empty extension. --- port/PyAssimp/pyassimp/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/PyAssimp/pyassimp/core.py b/port/PyAssimp/pyassimp/core.py index af5138581..690a34e59 100644 --- a/port/PyAssimp/pyassimp/core.py +++ b/port/PyAssimp/pyassimp/core.py @@ -401,7 +401,7 @@ def available_formats(): from ctypes import byref extension_list = structs.String() _assimp_lib.dll.aiGetExtensionList(byref(extension_list)) - return [e[2:].upper() for e in str(extension_list.data, "utf-8").split(";") if e] + return [e[2:].upper() for e in str(extension_list.data, "utf-8").split(";")] def _finalize_texture(tex, target): setattr(target, "achformathint", tex.achFormatHint) From b2cad5c58deb5eed413c9a6a34f53911c0818d95 Mon Sep 17 00:00:00 2001 From: Marco Feuerstein Date: Thu, 29 Jun 2023 14:56:11 +0200 Subject: [PATCH 03/12] Use file system encoding. --- port/PyAssimp/pyassimp/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/PyAssimp/pyassimp/core.py b/port/PyAssimp/pyassimp/core.py index 690a34e59..c7ceaa9cd 100644 --- a/port/PyAssimp/pyassimp/core.py +++ b/port/PyAssimp/pyassimp/core.py @@ -401,7 +401,7 @@ def available_formats(): from ctypes import byref extension_list = structs.String() _assimp_lib.dll.aiGetExtensionList(byref(extension_list)) - return [e[2:].upper() for e in str(extension_list.data, "utf-8").split(";")] + return [e[2:].upper() for e in str(extension_list.data, sys.getfilesystemencoding()).split(";")] def _finalize_texture(tex, target): setattr(target, "achformathint", tex.achFormatHint) From 3838a8aaf85bd2345e841b210be14a394e3bf9d4 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 2 Jun 2023 11:33:54 +0000 Subject: [PATCH 04/12] Fix unknown write in Assimp::ObjFileMtlImporter::getFloatValue --- code/AssetLib/Obj/ObjFileMtlImporter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/Obj/ObjFileMtlImporter.cpp b/code/AssetLib/Obj/ObjFileMtlImporter.cpp index 2c3ce9ee5..f7d66d11b 100644 --- a/code/AssetLib/Obj/ObjFileMtlImporter.cpp +++ b/code/AssetLib/Obj/ObjFileMtlImporter.cpp @@ -252,9 +252,9 @@ void ObjFileMtlImporter::load() { case 'a': // Anisotropy { ++m_DataIt; - getFloatValue(m_pModel->mCurrentMaterial->anisotropy); if (m_pModel->mCurrentMaterial != nullptr) - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + getFloatValue(m_pModel->mCurrentMaterial->anisotropy); + m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); } break; default: { From 6f3bfb5b60d45e7c53f504d04ed58b0019a01c13 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 2 Jun 2023 11:34:07 +0000 Subject: [PATCH 05/12] Fix memory leak --- code/AssetLib/Obj/ObjFileMtlImporter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/AssetLib/Obj/ObjFileMtlImporter.cpp b/code/AssetLib/Obj/ObjFileMtlImporter.cpp index f7d66d11b..f8e3e1cde 100644 --- a/code/AssetLib/Obj/ObjFileMtlImporter.cpp +++ b/code/AssetLib/Obj/ObjFileMtlImporter.cpp @@ -371,6 +371,7 @@ void ObjFileMtlImporter::getTexture() { if (m_pModel->mCurrentMaterial == nullptr) { m_pModel->mCurrentMaterial = new ObjFile::Material(); m_pModel->mCurrentMaterial->MaterialName.Set("Empty_Material"); + m_pModel->mMaterialMap["Empty_Material"] = m_pModel->mCurrentMaterial; } const char *pPtr(&(*m_DataIt)); From 537b445a590499c0577a11e2a10073f8dde2ce21 Mon Sep 17 00:00:00 2001 From: PencilAmazing <16854231+PencilAmazing@users.noreply.github.com> Date: Sun, 2 Jul 2023 14:17:52 -0400 Subject: [PATCH 06/12] Apply clang-format to files --- code/AssetLib/Irr/IRRLoader.cpp | 2063 +++++++++++++-------------- code/AssetLib/Irr/IRRLoader.h | 121 +- code/AssetLib/Irr/IRRMeshLoader.cpp | 678 ++++----- code/AssetLib/Irr/IRRShared.cpp | 406 +++--- code/AssetLib/Irr/IRRShared.h | 4 +- 5 files changed, 1628 insertions(+), 1644 deletions(-) diff --git a/code/AssetLib/Irr/IRRLoader.cpp b/code/AssetLib/Irr/IRRLoader.cpp index ca93f4309..823f64c6a 100644 --- a/code/AssetLib/Irr/IRRLoader.cpp +++ b/code/AssetLib/Irr/IRRLoader.cpp @@ -67,23 +67,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; 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 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. 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 { - return &desc; + return &desc; } // ------------------------------------------------------------------------------------------------ void IRRImporter::SetupProperties(const Importer *pImp) { - // read the output frame rate of all node animation channels - fps = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_IRR_ANIM_FPS, 100); - if (fps < 10.) { - ASSIMP_LOG_ERROR("IRR: Invalid FPS configuration"); - fps = 100; - } + // 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)); + // AI_CONFIG_FAVOUR_SPEED + configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED, 0)); } // ------------------------------------------------------------------------------------------------ // Build a mesh that consists of a single squad (a side of a skybox) aiMesh *IRRImporter::BuildSingleQuadMesh(const SkyboxVertex &v1, - const SkyboxVertex &v2, - const SkyboxVertex &v3, - const SkyboxVertex &v4) { - // allocate and prepare the mesh - aiMesh *out = new aiMesh(); + 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; + out->mPrimitiveTypes = aiPrimitiveType_POLYGON; + out->mNumFaces = 1; - // build the face - out->mFaces = new aiFace[1]; - aiFace &face = out->mFaces[0]; + // 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; + face.mNumIndices = 4; + face.mIndices = new unsigned int[4]; + for (unsigned int i = 0; i < 4; ++i) + face.mIndices[i] = i; - out->mNumVertices = 4; + 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 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 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; + // 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 &meshes, std::vector 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))); + // 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); + 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); - } + 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. + // 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 + 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(materials.size() - 6u); + // 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(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(materials.size() - 5u); + // 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(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(materials.size() - 4u); + // 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(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(materials.size() - 3u); + // 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(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(materials.size() - 2u); + // 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(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(materials.size() - 1u); + // 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(materials.size() - 1u); } // ------------------------------------------------------------------------------------------------ void IRRImporter::CopyMaterial(std::vector &materials, - std::vector> &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> &inmaterials, + unsigned int &defMatIdx, + aiMesh *mesh) { + if (inmaterials.empty()) { + // Do we have a default material? If not we need to create one + if (UINT_MAX == defMatIdx) { + defMatIdx = (unsigned int)materials.size(); + // TODO: add this materials to someone? + /*aiMaterial* mat = new aiMaterial(); aiString s; s.Set(AI_DEFAULT_MATERIAL_NAME); @@ -248,1110 +248,1109 @@ void IRRImporter::CopyMaterial(std::vector &materials, aiColor3D c(0.6f,0.6f,0.6f); mat->AddProperty(&c,1,AI_MATKEY_COLOR_DIFFUSE);*/ - } - mesh->mMaterialIndex = defMatIdx; - return; - } else if (inmaterials.size() > 1) { - ASSIMP_LOG_INFO("IRR: Skipping additional materials"); - } + } + mesh->mMaterialIndex = 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 = (unsigned int)materials.size(); + materials.push_back(inmaterials[0].first); } // ------------------------------------------------------------------------------------------------ inline int ClampSpline(int idx, int size) { - return (idx < 0 ? size + idx : (idx >= size ? idx - size : idx)); + return (idx < 0 ? size + idx : (idx >= size ? idx - size : idx)); } // ------------------------------------------------------------------------------------------------ inline void FindSuitableMultiple(int &angle) { - if (angle < 3) - angle = 3; - else if (angle < 10) - angle = 10; - else if (angle < 20) - angle = 20; - else if (angle < 30) - angle = 30; + 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 &anims) { - ai_assert(nullptr != root && nullptr != real); + 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 + // 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::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"); - } + if (root->animators.empty()) { + return; + } + unsigned int total(0); + for (std::list::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 + // NOTE: 1 tick == i millisecond - unsigned int cur = 0; - for (std::list::iterator it = root->animators.begin(); - it != root->animators.end(); ++it) { - if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER) continue; + unsigned int cur = 0; + for (std::list::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(); + 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() : "")); + 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(); + // 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; + 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->mParent = real->mParent; + dummy->mName = anim->mNodeName; - dummy->mNumChildren = 1; - dummy->mChildren = new aiNode *[dummy->mNumChildren]; - dummy->mChildren[0] = real; + dummy->mNumChildren = 1; + dummy->mChildren = new aiNode *[dummy->mNumChildren]; + dummy->mChildren[0] = real; - // the transformation matrix of the dummy node is the identity + // the transformation matrix of the dummy node is the identity - real->mParent = dummy; - } else - anim->mNodeName.Set(root->name); - ++cur; + 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); + 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; + 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]); - } + if ((angles[0] * angles[1]) != 0 && (angles[1] * angles[2]) != 0) { + FindSuitableMultiple(angles[0]); + FindSuitableMultiple(angles[1]); + FindSuitableMultiple(angles[2]); + } - int lcm = 360; + int lcm = 360; - if (angles[0]) - lcm = Math::lcm(lcm, angles[0]); + if (angles[0]) + lcm = Math::lcm(lcm, angles[0]); - if (angles[1]) - lcm = Math::lcm(lcm, angles[1]); + if (angles[1]) + lcm = Math::lcm(lcm, angles[1]); - if (angles[2]) - lcm = Math::lcm(lcm, angles[2]); + if (angles[2]) + lcm = Math::lcm(lcm, angles[2]); - if (360 == lcm) - break; + 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]); - // 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]; - 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]; - // 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; - q.mValue = aiQuaternion(angle.x, angle.y, angle.z); - q.mTime = (double)i; + // increase the angle + angle += in.direction; + } - // increase the angle - angle += in.direction; - } + // This animation is repeated and repeated ... + anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT; + } break; - // 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; - 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]; - 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(); - // 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; - // 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))); + } - 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; - // 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; - 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]; - 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(); - aiVector3D diff = in.direction - in.circleCenter; - const ai_real lengthOfWay = diff.Length(); - diff.Normalize(); + const double timeFactor = lengthOfWay / in.timeForWay; - 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; - // 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"); - 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]; - 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; + } - 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]; - 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]; - 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; - 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; - // 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; - // 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; - 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; - // 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); - // 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; - } - } + // build a simple translation matrix from it + key.mValue = t2; + key.mTime = (double)i; + } + } break; + default: + // UNKNOWN , OTHER + break; + }; + if (anim) { + anims.push_back(anim); + ++total; + } + } } // ------------------------------------------------------------------------------------------------ // This function is maybe more generic than we'd need it here void SetupMapping(aiMaterial *mat, aiTextureMapping mode, const aiVector3D &axis = aiVector3D(0.f, 0.f, -1.f)) { - if (nullptr == mat) { - return; - } + if (nullptr == mat) { + return; + } // Check whether there are texture properties defined - setup - // the desired texture mapping mode for all of them and ignore - // all UV settings we might encounter. WE HAVE NO UVS! + // the desired texture mapping mode for all of them and ignore + // all UV settings we might encounter. WE HAVE NO UVS! - std::vector p; - p.reserve(mat->mNumProperties + 1); + std::vector 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; + 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; + m->mDataLength = 4; + m->mData = new char[4]; + *((int *)m->mData) = mode; - p.push_back(prop); - p.push_back(m); + 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; + // 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); - } + 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; + if (p.empty()) return; - // rebuild the output array - if (p.size() > mat->mNumAllocated) { - delete[] mat->mProperties; - mat->mProperties = new aiMaterialProperty *[p.size() * 2]; + // rebuild the output array + if (p.size() > mat->mNumAllocated) { + delete[] mat->mProperties; + mat->mProperties = new aiMaterialProperty *[p.size() * 2]; - mat->mNumAllocated = static_cast(p.size() * 2); - } - mat->mNumProperties = (unsigned int)p.size(); - ::memcpy(mat->mProperties, &p[0], sizeof(void *) * mat->mNumProperties); + mat->mNumAllocated = static_cast(p.size() * 2); + } + mat->mNumProperties = (unsigned int)p.size(); + ::memcpy(mat->mProperties, &p[0], sizeof(void *) * mat->mNumProperties); } // ------------------------------------------------------------------------------------------------ void IRRImporter::GenerateGraph(Node *root, aiNode *rootOut, aiScene *scene, - BatchLoader &batch, - std::vector &meshes, - std::vector &anims, - std::vector &attach, - std::vector &materials, - unsigned int &defMatIdx) { - unsigned int oldMeshSize = (unsigned int)meshes.size(); - //unsigned int meshTrafoAssign = 0; + BatchLoader &batch, + std::vector &meshes, + std::vector &anims, + std::vector &attach, + std::vector &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; + // 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); + // 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"); + // 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]; + 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 &src = root->materials[i]; - localScene->mMaterials[i] = src.first; - } + std::pair &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]; + // 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 &src = root->materials[mesh->mMaterialIndex]; - aiMaterial *mat = (aiMaterial *)src.first; + // 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 &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->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"); + 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; + 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); - } - } + 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)) { + // 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; + 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: + case Node::LIGHT: + case Node::CAMERA: - // We're already finished with lights and cameras - break; + // 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; + 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)); + meshes.push_back(StandardShapes::MakeMesh(mul, + &StandardShapes::MakeSphere)); - // Adjust scaling - root->scaling *= root->sphereRadius / 2; + // Adjust scaling + root->scaling *= root->sphereRadius / 2; - // Copy one output material - CopyMaterial(materials, root->materials, defMatIdx, meshes.back()); + // 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; + // 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)); + case Node::CUBE: { + // Generate an unit cube first + meshes.push_back(StandardShapes::MakeMesh( + &StandardShapes::MakeHexahedron)); - // Adjust scaling - root->scaling *= root->sphereRadius; + // Adjust scaling + root->scaling *= root->sphereRadius; - // Copy one output material - CopyMaterial(materials, root->materials, defMatIdx, meshes.back()); + // 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; + // 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; - } + 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); + // 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); + 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; + // ************************************************************* + // 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; - }; + 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()) { + // 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]; + 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; - } - } + 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); + // 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)); + // 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 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; + // 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); + // 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) { + // 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) { + 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); - } - } + aiNode *node = rootOut->mChildren[i] = new aiNode(); + node->mParent = rootOut; + GenerateGraph(root->children[i], node, scene, batch, meshes, + anims, attach, materials, defMatIdx); + } + } } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { - std::unique_ptr file(pIOHandler->Open(pFile)); + std::unique_ptr file(pIOHandler->Open(pFile)); - // Check whether we can read from the file + // Check whether we can read from the file if (file == nullptr) { throw DeadlyImportError("Failed to open IRR file ", pFile); } // 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); } pugi::xml_node rootElement = st.getRootNode(); - // The root node of the scene - Node *root = new Node(Node::DUMMY); - root->parent = nullptr; - root->name = ""; + // The root node of the scene + Node *root = new Node(Node::DUMMY); + root->parent = nullptr; + root->name = ""; - // Current node parent - Node *curParent = root; + // Current node parent + Node *curParent = root; - // Scene-graph node we're currently working on - Node *curNode = nullptr; + // Scene-graph node we're currently working on + Node *curNode = nullptr; - // List of output cameras - std::vector cameras; + // List of output cameras + std::vector cameras; - // List of output lights - std::vector lights; + // List of output lights + std::vector lights; - // Batch loader used to load external models - BatchLoader batch(pIOHandler); - //batch.SetBasePath(pFile); + // Batch loader used to load external models + BatchLoader batch(pIOHandler); + // batch.SetBasePath(pFile); - cameras.reserve(5); - lights.reserve(5); + cameras.reserve(5); + lights.reserve(5); - bool inMaterials = false, inAnimator = false; - unsigned int guessedAnimCnt = 0, guessedMeshCnt = 0, guessedMatCnt = 0; + bool inMaterials = false, inAnimator = false; + unsigned int guessedAnimCnt = 0, guessedMeshCnt = 0, guessedMatCnt = 0; - // Parse the XML file + // 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). + // 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. */ - // *********************************************************************** - //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); + nd = new Node(Node::DUMMY); + } - // 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); + /* 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; + } - // 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()); + Animator *curAnim = nullptr; - /* 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); - } + // 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 &p = curNode->materials.back(); - /* 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; - } + 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(); - Animator *curAnim = nullptr; + ++guessedAnimCnt; + } - // 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 &p = curNode->materials.back(); + /* 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); - 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(); + 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(); - ++guessedAnimCnt; - } + // 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; - /* 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); + // 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) { - 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(); + 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); - // 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 (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 == "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")) { + 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); + 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 (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 (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 we're either a camera or a light source * we need to update the name in the aiLight/ * aiCamera structure, too. */ - if (Node::CAMERA == curNode->type) { - cameras.back()->mName.Set(prop.value); - } else if (Node::LIGHT == curNode->type) { - lights.back()->mName.Set(prop.value); - } - } else if (Node::LIGHT == curNode->type && "LightType" == prop.name) { - if (prop.value == "Spot") - lights.back()->mType = aiLightSource_SPOT; - else if (prop.value == "Point") - lights.back()->mType = aiLightSource_POINT; - else if (prop.value == "Directional") - lights.back()->mType = aiLightSource_DIRECTIONAL; - else { - // We won't pass the validation with aiLightSourceType_UNDEFINED, - // so we remove the light and replace it with a silly dummy node - delete lights.back(); - lights.pop_back(); - curNode->type = Node::DUMMY; + 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 + ASSIMP_LOG_ERROR("Ignoring light of unknown type: ", prop.value); + } + } else if ((prop.name == "Mesh" && Node::MESH == curNode->type) || + Node::ANIMMESH == curNode->type) { + /* This is the file name of the mesh - either * animated or not. We need to make sure we setup * the correct post-processing settings here. */ - unsigned int pp = 0; - BatchLoader::PropertyMap map; + 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(map.ints, AI_CONFIG_PP_RVC_FLAGS, - aiComponent_ANIMATIONS | aiComponent_BONEWEIGHTS); - } + if (Node::ANIMMESH != curNode->type) { + pp |= aiProcess_RemoveComponent; + SetGenericProperty(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. - */ + /* 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); + 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; + 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: + /*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 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;*/ + // 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 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; - } - //} + 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"); - } - } + // 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(); + batch.LoadAll(); - // Allocate a temporary scene data structure - aiScene *tempScene = new aiScene(); - tempScene->mRootNode = new aiNode(); - tempScene->mRootNode->mName.Set(""); + // Allocate a temporary scene data structure + aiScene *tempScene = new aiScene(); + tempScene->mRootNode = new aiNode(); + tempScene->mRootNode->mName.Set(""); - // 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 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); - } + // 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 anims; - std::vector materials; - std::vector attach; - std::vector meshes; + // temporary data + std::vector anims; + std::vector materials; + std::vector attach; + std::vector 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)); + // 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); + // 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(); + 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"); + // *********************************************************** + // 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 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); - } + // 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)); + // 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; - } + // 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; + // Finished ... everything destructs automatically and all + // temporary scenes have already been deleted by MergeScenes() + delete root; } #endif // !! ASSIMP_BUILD_NO_IRR_IMPORTER diff --git a/code/AssetLib/Irr/IRRLoader.h b/code/AssetLib/Irr/IRRLoader.h index 7fa239395..a9e70cc1e 100644 --- a/code/AssetLib/Irr/IRRLoader.h +++ b/code/AssetLib/Irr/IRRLoader.h @@ -53,7 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -namespace Assimp { +namespace Assimp { // --------------------------------------------------------------------------- /** Irr importer class. @@ -71,13 +71,13 @@ public: /** Returns whether the class can handle the format of the given file. * See BaseImporter::CanRead() for details. */ - bool CanRead( const std::string& pFile, IOSystem* pIOHandler, - bool checkSig) const override; + bool CanRead(const std::string &pFile, IOSystem *pIOHandler, + bool checkSig) const override; protected: - const aiImporterDesc* GetInfo () const override; - void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) override; - void SetupProperties(const Importer* pImp) override; + const aiImporterDesc *GetInfo() const override; + void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override; + void SetupProperties(const Importer *pImp) override; private: /** Data structure for a scene-graph node animator @@ -85,27 +85,19 @@ private: struct Animator { // Type of the animator 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, - OTHER = 0x5 + OTHER = 0x5 } type; - explicit Animator(AT t = UNKNOWN) - : type (t) - , speed ( ai_real( 0.001 ) ) - , direction ( ai_real( 0.0 ), ai_real( 1.0 ), ai_real( 0.0 ) ) - , circleRadius ( ai_real( 1.0) ) - , tightness ( ai_real( 0.5 ) ) - , loop (true) - , timeForWay (100) - { + explicit Animator(AT t = UNKNOWN) : + type(t), speed(ai_real(0.001)), direction(ai_real(0.0), ai_real(1.0), ai_real(0.0)), circleRadius(ai_real(1.0)), tightness(ai_real(0.5)), loop(true), timeForWay(100) { } - // common parameters ai_real speed; aiVector3D direction; @@ -128,11 +120,9 @@ private: /** Data structure for a scene-graph node in an IRR file */ - struct Node - { + struct Node { // Type of the node - enum ET - { + enum ET { LIGHT, CUBE, MESH, @@ -144,21 +134,20 @@ private: ANIMMESH } type; - explicit Node(ET t) - : type (t) - , scaling (1.0,1.0,1.0) // assume uniform scaling by default - , parent() - , framesPerSecond (0.0) - , id() - , sphereRadius (1.0) - , spherePolyCountX (100) - , spherePolyCountY (100) - { + explicit Node(ET t) : + type(t), scaling(1.0, 1.0, 1.0) // assume uniform scaling by default + , + parent(), + framesPerSecond(0.0), + id(), + sphereRadius(1.0), + spherePolyCountX(100), + spherePolyCountY(100) { // Generate a default name for the node char buffer[128]; static int cnt; - ai_snprintf(buffer, 128, "IrrNode_%i",cnt++); + ai_snprintf(buffer, 128, "IrrNode_%i", cnt++); name = std::string(buffer); // reserve space for up to 5 materials @@ -175,10 +164,10 @@ private: std::string name; // List of all child nodes - std::vector children; + std::vector children; // Parent node - Node* parent; + Node *parent; // Animated meshes: frames per second // 0.f if not specified @@ -190,13 +179,13 @@ private: // Meshes: List of materials to be assigned // along with their corresponding material flags - std::vector< std::pair > materials; + std::vector> materials; // Spheres: radius of the sphere to be generates ai_real sphereRadius; // Spheres: Number of polygons in the x,y direction - unsigned int spherePolyCountX,spherePolyCountY; + unsigned int spherePolyCountX, spherePolyCountY; // List of all animators assigned to the node std::list animators; @@ -204,40 +193,36 @@ private: /** Data structure for a vertex in an IRR skybox */ - struct SkyboxVertex - { + struct SkyboxVertex { SkyboxVertex() = default; //! Construction from single vertex components SkyboxVertex(ai_real px, ai_real py, ai_real pz, - ai_real nx, ai_real ny, ai_real nz, - ai_real uvx, ai_real uvy) + 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; }; - // ------------------------------------------------------------------- /// Fill the scene-graph recursively - void GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene, - BatchLoader& batch, - std::vector& meshes, - std::vector& anims, - std::vector& attach, - std::vector& materials, - unsigned int& defaultMatIdx); + void GenerateGraph(Node *root, aiNode *rootOut, aiScene *scene, + BatchLoader &batch, + std::vector &meshes, + std::vector &anims, + std::vector &attach, + std::vector &materials, + unsigned int &defaultMatIdx); // ------------------------------------------------------------------- /// Generate a mesh that consists of just a single quad - aiMesh* BuildSingleQuadMesh(const SkyboxVertex& v1, - const SkyboxVertex& v2, - const SkyboxVertex& v3, - const SkyboxVertex& v4); + aiMesh *BuildSingleQuadMesh(const SkyboxVertex &v1, + const SkyboxVertex &v2, + const SkyboxVertex &v3, + const SkyboxVertex &v4); // ------------------------------------------------------------------- /// Build a sky-box @@ -245,8 +230,8 @@ private: /// @param meshes Receives 6 output meshes /// @param materials The last 6 materials are assigned to the newly /// created meshes. The names of the materials are adjusted. - void BuildSkybox(std::vector& meshes, - std::vector materials); + void BuildSkybox(std::vector &meshes, + std::vector materials); // ------------------------------------------------------------------- /** 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 mesh Mesh to work on */ - void CopyMaterial(std::vector& materials, - std::vector< std::pair >& inmaterials, - unsigned int& defMatIdx, - aiMesh* mesh); + void CopyMaterial(std::vector &materials, + std::vector> &inmaterials, + unsigned int &defMatIdx, + aiMesh *mesh); // ------------------------------------------------------------------- /** Compute animations for a specific node @@ -267,8 +252,8 @@ private: * @param root Node to be processed * @param anims The list of output animations */ - void ComputeAnimations(Node* root, aiNode* real, - std::vector& anims); + void ComputeAnimations(Node *root, aiNode *real, + std::vector &anims); private: /// Configuration option: desired output FPS diff --git a/code/AssetLib/Irr/IRRMeshLoader.cpp b/code/AssetLib/Irr/IRRMeshLoader.cpp index 52fb82ff9..84b8f80d4 100644 --- a/code/AssetLib/Irr/IRRMeshLoader.cpp +++ b/code/AssetLib/Irr/IRRMeshLoader.cpp @@ -57,16 +57,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; 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. 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 const aiImporterDesc *IRRMeshImporter::GetInfo() const { - return &desc; + return &desc; } static void releaseMaterial(aiMaterial **mat) { - if (*mat != nullptr) { - delete *mat; - *mat = nullptr; - } + if (*mat != nullptr) { + delete *mat; + *mat = nullptr; + } } 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. void IRRMeshImporter::InternReadFile(const std::string &pFile, - aiScene *pScene, IOSystem *pIOHandler) { - std::unique_ptr file(pIOHandler->Open(pFile)); + aiScene *pScene, IOSystem *pIOHandler) { + std::unique_ptr file(pIOHandler->Open(pFile)); - // Check whether we can read from the file - if (file == nullptr) - throw DeadlyImportError("Failed to open IRRMESH file ", 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(); + // 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 materials; - std::vector meshes; - materials.reserve(5); - meshes.reserve(5); + // final data + std::vector materials; + std::vector meshes; + materials.reserve(5); + meshes.reserve(5); - // temporary data - current mesh buffer - aiMaterial *curMat = nullptr; - aiMesh *curMesh = nullptr; - unsigned int curMatFlags = 0; + // temporary data - current mesh buffer + aiMaterial *curMat = nullptr; + aiMesh *curMesh = nullptr; + unsigned int curMatFlags = 0; - std::vector curVertices, curNormals, curTangents, curBitangents; - std::vector curColors; - std::vector curUVs, curUV2s; + std::vector curVertices, curNormals, curTangents, curBitangents; + std::vector curColors; + std::vector curUVs, curUV2s; - // some temporary variables - int textMeaning = 0; - int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents - bool useColors = false; + // 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; + // 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(); - } + 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 (!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"); + 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; - } + releaseMaterial(&curMat); + releaseMesh(&curMesh); + textMeaning = 0; + continue; + } - curVertices.reserve(num); - curNormals.reserve(num); - curColors.reserve(num); - curUVs.reserve(num); + curVertices.reserve(num); + curNormals.reserve(num); + curColors.reserve(num); + curUVs.reserve(num); - // Determine the file format - //const char *t = reader->getAttributeValueSafe("type"); + // Determine the file format + // const char *t = reader->getAttributeValueSafe("type"); pugi::xml_attribute t = child.attribute("type"); - if (!ASSIMP_stricmp("2tcoords", t.name())) { - curUV2s.reserve(num); - vertexFormat = 1; + if (!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_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"); - } + 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; + textMeaning = 2; - // start a new mesh - curMesh = new aiMesh(); + // 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"); + // 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); + // mesh - away + releaseMesh(&curMesh); - // material - away - releaseMaterial(&curMat); + // material - away + releaseMaterial(&curMat); - textMeaning = 0; - continue; - } + textMeaning = 0; + continue; + } - if (curMesh->mNumVertices % 3) { - ASSIMP_LOG_WARN("IRRMESH: Number if indices isn't divisible by 3"); - } + 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]; + curMesh->mNumFaces = curMesh->mNumVertices / 3; + curMesh->mFaces = new aiFace[curMesh->mNumFaces]; - // setup some members - curMesh->mMaterialIndex = (unsigned int)materials.size(); - curMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + // setup some members + curMesh->mMaterialIndex = (unsigned int)materials.size(); + curMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - // allocate storage for all vertices - curMesh->mVertices = new aiVector3D[curMesh->mNumVertices]; + // 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; + 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; + // case EXN_TEXT: { + const char *sz = child.child_value(); + if (textMeaning == 1) { + textMeaning = 0; - // read vertices - do { - SkipSpacesAndLineEnd(&sz); - aiVector3D temp; - aiColor4D c; + // read vertices + do { + SkipSpacesAndLineEnd(&sz); + aiVector3D temp; + aiColor4D c; - // Read the vertex position - sz = fast_atoreal_move(sz, (float &)temp.x); - SkipSpaces(&sz); + // Read the vertex position + sz = fast_atoreal_move(sz, (float &)temp.x); + SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float &)temp.y); - SkipSpaces(&sz); + sz = fast_atoreal_move(sz, (float &)temp.y); + SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float &)temp.z); - SkipSpaces(&sz); - curVertices.push_back(temp); + sz = fast_atoreal_move(sz, (float &)temp.z); + SkipSpaces(&sz); + curVertices.push_back(temp); - // Read the vertex normals - sz = fast_atoreal_move(sz, (float &)temp.x); - SkipSpaces(&sz); + // Read the vertex normals + sz = fast_atoreal_move(sz, (float &)temp.x); + SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float &)temp.y); - SkipSpaces(&sz); + sz = fast_atoreal_move(sz, (float &)temp.y); + SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float &)temp.z); - SkipSpaces(&sz); - curNormals.push_back(temp); + sz = fast_atoreal_move(sz, (float &)temp.z); + SkipSpaces(&sz); + curNormals.push_back(temp); - // read the vertex colors - uint32_t clr = strtoul16(sz, &sz); - ColorFromARGBPacked(clr, c); + // read the vertex colors + uint32_t clr = strtoul16(sz, &sz); + ColorFromARGBPacked(clr, c); - if (!curColors.empty() && c != *(curColors.end() - 1)) - useColors = true; + if (!curColors.empty() && c != *(curColors.end() - 1)) + useColors = true; - curColors.push_back(c); - SkipSpaces(&sz); + curColors.push_back(c); + SkipSpaces(&sz); - // read the first UV coordinate set - sz = fast_atoreal_move(sz, (float &)temp.x); - SkipSpaces(&sz); + // read the first UV coordinate set + sz = fast_atoreal_move(sz, (float &)temp.x); + SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float &)temp.y); - SkipSpaces(&sz); - temp.z = 0.f; - temp.y = 1.f - temp.y; // DX to OGL - curUVs.push_back(temp); + sz = fast_atoreal_move(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(sz, (float &)temp.x); - SkipSpaces(&sz); + // read the (optional) second UV coordinate set + if (vertexFormat == 1) { + sz = fast_atoreal_move(sz, (float &)temp.x); + SkipSpaces(&sz); - sz = fast_atoreal_move(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(sz, (float &)temp.x); - SkipSpaces(&sz); + sz = fast_atoreal_move(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(sz, (float &)temp.x); + SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float &)temp.z); - SkipSpaces(&sz); + sz = fast_atoreal_move(sz, (float &)temp.z); + SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float &)temp.y); - SkipSpaces(&sz); - temp.y *= -1.0f; - curTangents.push_back(temp); + sz = fast_atoreal_move(sz, (float &)temp.y); + SkipSpaces(&sz); + temp.y *= -1.0f; + curTangents.push_back(temp); - // bitangents - sz = fast_atoreal_move(sz, (float &)temp.x); - SkipSpaces(&sz); + // bitangents + sz = fast_atoreal_move(sz, (float &)temp.x); + SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float &)temp.z); - SkipSpaces(&sz); + sz = fast_atoreal_move(sz, (float &)temp.z); + SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float &)temp.y); - SkipSpaces(&sz); - temp.y *= -1.0f; - curBitangents.push_back(temp); - } - } + sz = fast_atoreal_move(sz, (float &)temp.y); + SkipSpaces(&sz); + temp.y *= -1.0f; + curBitangents.push_back(temp); + } + } - /* IMPORTANT: We assume that each vertex is specified in one + /* IMPORTANT: We assume that each vertex is specified in one line. So we can skip the rest of the line - unknown vertex elements are ignored. */ - while (SkipLine(&sz)); - } else if (textMeaning == 2) { - textMeaning = 0; + while (SkipLine(&sz)); + } else if (textMeaning == 2) { + textMeaning = 0; - // read indices - aiFace *curFace = curMesh->mFaces; - aiFace *const faceEnd = curMesh->mFaces + curMesh->mNumFaces; + // 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]; + 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 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; - } + unsigned int idx = strtoul10(sz, &sz); + if (idx >= curVertices.size()) { + ASSIMP_LOG_ERROR("IRRMESH: Index out of range"); + idx = 0; + } - curFace->mIndices[curIdx] = total++; + 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]; + *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 (++curIdx == 3) { + ++curFace; + curIdx = 0; + } + } - if (curFace != faceEnd) - ASSIMP_LOG_ERROR("IRRMESH: Not enough indices"); + 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); - } - } - } - } + // 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); - } - } + // 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"); - } + 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]; + // 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; - } + // 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->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(""); - pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; - pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; + pScene->mRootNode = new aiNode(); + pScene->mRootNode->mName.Set(""); + 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; - } + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + pScene->mRootNode->mMeshes[i] = i; + } } #endif // !! ASSIMP_BUILD_NO_IRRMESH_IMPORTER diff --git a/code/AssetLib/Irr/IRRShared.cpp b/code/AssetLib/Irr/IRRShared.cpp index 8763b63ae..7318b1d73 100644 --- a/code/AssetLib/Irr/IRRShared.cpp +++ b/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 */ -//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)) #include "IRRShared.h" #include #include -#include #include +#include using namespace Assimp; // Transformation matrix to convert from Assimp to IRR space -const aiMatrix4x4 Assimp::AI_TO_IRR_MATRIX = aiMatrix4x4 ( - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f); +const aiMatrix4x4 Assimp::AI_TO_IRR_MATRIX = aiMatrix4x4( + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); // ------------------------------------------------------------------------------------------------ // read a property in hexadecimal format (i.e. ffffffff) -void IrrlichtBase::ReadHexProperty(HexProperty &out ) { - for (pugi::xml_attribute attrib : mNode->attributes()) { +void IrrlichtBase::ReadHexProperty(HexProperty &out) { + for (pugi::xml_attribute attrib : mNode->attributes()) { if (!ASSIMP_stricmp(attrib.name(), "name")) { - out.name = std::string( attrib.value() ); - } else if (!ASSIMP_stricmp(attrib.name(),"value")) { + out.name = std::string(attrib.value()); + } else if (!ASSIMP_stricmp(attrib.name(), "value")) { // parse the hexadecimal value - out.value = strtoul16(attrib.name()); + out.value = strtoul16(attrib.name()); } } } // ------------------------------------------------------------------------------------------------ // read a decimal property -void IrrlichtBase::ReadIntProperty(IntProperty & out) { - for (pugi::xml_attribute attrib : mNode->attributes()) { - if (!ASSIMP_stricmp(attrib.name(), "name")) { - out.name = std::string(attrib.value()); - } else if (!ASSIMP_stricmp(attrib.value(),"value")) { +void IrrlichtBase::ReadIntProperty(IntProperty &out) { + for (pugi::xml_attribute attrib : mNode->attributes()) { + if (!ASSIMP_stricmp(attrib.name(), "name")) { + out.name = std::string(attrib.value()); + } else if (!ASSIMP_stricmp(attrib.value(), "value")) { // parse the int value - out.value = strtol10(attrib.name()); + out.value = strtol10(attrib.name()); } } } // ------------------------------------------------------------------------------------------------ // read a string property -void IrrlichtBase::ReadStringProperty( StringProperty& out) { - for (pugi::xml_attribute attrib : mNode->attributes()) { - if (!ASSIMP_stricmp(attrib.name(), "name")) { - out.name = std::string(attrib.value()); - } else if (!ASSIMP_stricmp(attrib.name(), "value")) { +void IrrlichtBase::ReadStringProperty(StringProperty &out) { + for (pugi::xml_attribute attrib : mNode->attributes()) { + if (!ASSIMP_stricmp(attrib.name(), "name")) { + out.name = std::string(attrib.value()); + } else if (!ASSIMP_stricmp(attrib.name(), "value")) { // simple copy the string - out.value = std::string(attrib.value()); + out.value = std::string(attrib.value()); } } } @@ -103,12 +103,12 @@ void IrrlichtBase::ReadStringProperty( StringProperty& out) { // ------------------------------------------------------------------------------------------------ // read a boolean property void IrrlichtBase::ReadBoolProperty(BoolProperty &out) { - for (pugi::xml_attribute attrib : mNode->attributes()) { - if (!ASSIMP_stricmp(attrib.name(), "name")){ - out.name = std::string(attrib.value()); - } else if (!ASSIMP_stricmp(attrib.name(), "value")) { + for (pugi::xml_attribute attrib : mNode->attributes()) { + if (!ASSIMP_stricmp(attrib.name(), "name")) { + out.name = std::string(attrib.value()); + } else if (!ASSIMP_stricmp(attrib.name(), "value")) { // true or false, case insensitive - out.value = (ASSIMP_stricmp(attrib.value(), "true") ? false : true); + out.value = (ASSIMP_stricmp(attrib.value(), "true") ? false : true); } } } @@ -116,229 +116,229 @@ void IrrlichtBase::ReadBoolProperty(BoolProperty &out) { // ------------------------------------------------------------------------------------------------ // read a float property void IrrlichtBase::ReadFloatProperty(FloatProperty &out) { - for (pugi::xml_attribute attrib : mNode->attributes()) { - if (!ASSIMP_stricmp(attrib.name(), "name")) { - out.name = std::string(attrib.value()); - } else if (!ASSIMP_stricmp(attrib.name(), "value")) { + for (pugi::xml_attribute attrib : mNode->attributes()) { + if (!ASSIMP_stricmp(attrib.name(), "name")) { + out.name = std::string(attrib.value()); + } else if (!ASSIMP_stricmp(attrib.name(), "value")) { // just parse the float - out.value = fast_atof(attrib.value()); + out.value = fast_atof(attrib.value()); } } } // ------------------------------------------------------------------------------------------------ // read a vector property -void IrrlichtBase::ReadVectorProperty( VectorProperty &out ) { - for (pugi::xml_attribute attrib : mNode->attributes()) { - if (!ASSIMP_stricmp(attrib.name(), "name")) { - out.name = std::string(attrib.value()); - } else if (!ASSIMP_stricmp(attrib.name(), "value")) { +void IrrlichtBase::ReadVectorProperty(VectorProperty &out) { + for (pugi::xml_attribute attrib : mNode->attributes()) { + if (!ASSIMP_stricmp(attrib.name(), "name")) { + out.name = std::string(attrib.value()); + } else if (!ASSIMP_stricmp(attrib.name(), "value")) { // three floats, separated with commas const char *ptr = attrib.value(); SkipSpaces(&ptr); - ptr = fast_atoreal_move( ptr,(float&)out.value.x ); + ptr = fast_atoreal_move(ptr, (float &)out.value.x); SkipSpaces(&ptr); if (',' != *ptr) { ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition"); - } else { - SkipSpaces(ptr + 1, &ptr); - } - ptr = fast_atoreal_move( ptr,(float&)out.value.y ); + } else { + SkipSpaces(ptr + 1, &ptr); + } + ptr = fast_atoreal_move(ptr, (float &)out.value.y); SkipSpaces(&ptr); if (',' != *ptr) { ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition"); - } else { - SkipSpaces(ptr + 1, &ptr); - } - ptr = fast_atoreal_move( ptr,(float&)out.value.z ); + } else { + SkipSpaces(ptr + 1, &ptr); + } + ptr = fast_atoreal_move(ptr, (float &)out.value.z); } } } // ------------------------------------------------------------------------------------------------ // Convert a string to a proper aiMappingMode -int ConvertMappingMode(const std::string& mode) { +int ConvertMappingMode(const std::string &mode) { if (mode == "texture_clamp_repeat") { return aiTextureMapMode_Wrap; - } else if (mode == "texture_clamp_mirror") { - return aiTextureMapMode_Mirror; - } + } else if (mode == "texture_clamp_mirror") { + return aiTextureMapMode_Mirror; + } return aiTextureMapMode_Clamp; } // ------------------------------------------------------------------------------------------------ // Parse a material from the XML file -aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) { - aiMaterial* mat = new aiMaterial(); +aiMaterial *IrrlichtBase::ParseMaterial(unsigned int &matFlags) { + aiMaterial *mat = new aiMaterial(); aiColor4D clr; aiString s; matFlags = 0; // zero output flags - int cnt = 0; // number of used texture channels + int cnt = 0; // number of used texture channels unsigned int nd = 0; for (pugi::xml_node child : mNode->children()) { - if (!ASSIMP_stricmp(child.name(), "color")) { // Hex properties - HexProperty prop; - ReadHexProperty(prop); - if (prop.name == "Diffuse") { - ColorFromARGBPacked(prop.value, clr); - mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_DIFFUSE); - } else if (prop.name == "Ambient") { - ColorFromARGBPacked(prop.value, clr); - mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_AMBIENT); - } else if (prop.name == "Specular") { - ColorFromARGBPacked(prop.value, clr); - mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_SPECULAR); - } + 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. + // NOTE: The 'emissive' property causes problems. It is + // often != 0, even if there is obviously no light + // emitted by the described surface. In fact I think + // IRRLICHT ignores this property, too. #if 0 else if (prop.name == "Emissive") { ColorFromARGBPacked(prop.value,clr); mat->AddProperty(&clr,1,AI_MATKEY_COLOR_EMISSIVE); } #endif - } else if (!ASSIMP_stricmp(child.name(), "float")) { // Float properties - FloatProperty prop; - ReadFloatProperty(prop); - if (prop.name == "Shininess") { - mat->AddProperty(&prop.value, 1, AI_MATKEY_SHININESS); - } - } else if (!ASSIMP_stricmp(child.name(), "bool")) { // Bool properties - BoolProperty prop; - ReadBoolProperty(prop); - if (prop.name == "Wireframe") { - int val = (prop.value ? true : false); - mat->AddProperty(&val, 1, AI_MATKEY_ENABLE_WIREFRAME); - } else if (prop.name == "GouraudShading") { - int val = (prop.value ? aiShadingMode_Gouraud : aiShadingMode_NoShading); - mat->AddProperty(&val, 1, AI_MATKEY_SHADING_MODEL); - } else if (prop.name == "BackfaceCulling") { - int val = (!prop.value); - mat->AddProperty(&val, 1, AI_MATKEY_TWOSIDED); - } - } else if (!ASSIMP_stricmp(child.name(), "texture") || - !ASSIMP_stricmp(child.name(), "enum")) { // String properties - textures and texture related properties - StringProperty prop; - ReadStringProperty(prop); - if (prop.value.length()) { - // material type (shader) - if (prop.name == "Type") { - if (prop.value == "solid") { - // default material ... - } else if (prop.value == "trans_vertex_alpha") { - matFlags = AI_IRRMESH_MAT_trans_vertex_alpha; - } else if (prop.value == "lightmap") { - matFlags = AI_IRRMESH_MAT_lightmap; - } else if (prop.value == "solid_2layer") { - matFlags = AI_IRRMESH_MAT_solid_2layer; - } else if (prop.value == "lightmap_m2") { - matFlags = AI_IRRMESH_MAT_lightmap_m2; - } else if (prop.value == "lightmap_m4") { - matFlags = AI_IRRMESH_MAT_lightmap_m4; - } else if (prop.value == "lightmap_light") { - matFlags = AI_IRRMESH_MAT_lightmap_light; - } else if (prop.value == "lightmap_light_m2") { - matFlags = AI_IRRMESH_MAT_lightmap_light_m2; - } else if (prop.value == "lightmap_light_m4") { - matFlags = AI_IRRMESH_MAT_lightmap_light_m4; - } else if (prop.value == "lightmap_add") { - matFlags = AI_IRRMESH_MAT_lightmap_add; - } else if (prop.value == "normalmap_solid" || - prop.value == "parallaxmap_solid") { // Normal and parallax maps are treated equally - matFlags = AI_IRRMESH_MAT_normalmap_solid; - } else if (prop.value == "normalmap_trans_vertex_alpha" || - prop.value == "parallaxmap_trans_vertex_alpha") { - matFlags = AI_IRRMESH_MAT_normalmap_tva; - } else if (prop.value == "normalmap_trans_add" || - prop.value == "parallaxmap_trans_add") { - matFlags = AI_IRRMESH_MAT_normalmap_ta; - } else { - ASSIMP_LOG_WARN("IRRMat: Unrecognized material type: ", prop.value); - } - } + } 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)); + // 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_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 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)); - } + // 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: + // 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 elements if ( !ASSIMP_stricmp(reader->getNodeName(),"material") || @@ -378,7 +378,7 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) { break; } }*/ - } + } ASSIMP_LOG_ERROR("IRRMESH: Unexpected end of file. Material is not complete"); return mat; diff --git a/code/AssetLib/Irr/IRRShared.h b/code/AssetLib/Irr/IRRShared.h index ee1309e14..d7fe93710 100644 --- a/code/AssetLib/Irr/IRRShared.h +++ b/code/AssetLib/Irr/IRRShared.h @@ -1,8 +1,8 @@ /** @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 #define INCLUDED_AI_IRRSHARED_H From 3e1fd749401b0e9812f8089bc2362da0b6e8969d Mon Sep 17 00:00:00 2001 From: dog Date: Mon, 26 Jun 2023 15:15:30 -0400 Subject: [PATCH 07/12] Fix up scene loading. pugixml is a breadth-first parser while irrxml is a depth first. This only parses scene structure, no mesh loading yet. --- code/AssetLib/Irr/IRRLoader.cpp | 794 ++++++++++++++-------------- code/AssetLib/Irr/IRRLoader.h | 24 + code/AssetLib/Irr/IRRMeshLoader.cpp | 6 +- code/AssetLib/Irr/IRRMeshLoader.h | 2 + code/AssetLib/Irr/IRRShared.cpp | 44 +- code/AssetLib/Irr/IRRShared.h | 19 +- 6 files changed, 467 insertions(+), 422 deletions(-) diff --git a/code/AssetLib/Irr/IRRLoader.cpp b/code/AssetLib/Irr/IRRLoader.cpp index 823f64c6a..86f744a64 100644 --- a/code/AssetLib/Irr/IRRLoader.cpp +++ b/code/AssetLib/Irr/IRRLoader.cpp @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @brief Implementation of the Irr importer class */ +#include "assimp/StringComparison.h" #ifndef ASSIMP_BUILD_NO_IRR_IMPORTER #include "AssetLib/Irr/IRRLoader.h" @@ -62,7 +63,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include +#include #include +#include +#include using namespace Assimp; @@ -835,11 +840,381 @@ void IRRImporter::GenerateGraph(Node *root, aiNode *rootOut, aiScene *scene, } } +void IRRImporter::ParseNodeAttributes(pugi::xml_node &attributesNode, IRRImporter::Node *nd, BatchLoader &batch) { + ai_assert(!ASSIMP_stricmp(attributesNode.name(), "attributes")); // Node must be + ai_assert(nd != nullptr); // dude + + // Big switch statement that tests for various tags inside + // and applies them to nd + // I don't believe nodes have boolean attributes + for (pugi::xml_node &attribute : attributesNode.children()) { + if (attribute.type() != pugi::node_element) continue; + if (!ASSIMP_stricmp(attribute.name(), "vector3d")) { // + VectorProperty prop; + ReadVectorProperty(prop, attribute); + if (prop.name == "Position") { + nd->position = prop.value; + } else if (prop.name == "Rotation") { + nd->rotation = prop.value; + } else if (prop.name == "Scale") { + nd->scaling = prop.value; + } else if (Node::CAMERA == nd->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(attribute.name(), "float")) { // + FloatProperty prop; + ReadFloatProperty(prop, attribute); + if (prop.name == "FramesPerSecond" && Node::ANIMMESH == nd->type) { + nd->framesPerSecond = prop.value; + } else if (Node::CAMERA == nd->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 == nd->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 == nd->type && prop.name == "Radius") || + (Node::CUBE == nd->type && prop.name == "Size")) { + nd->sphereRadius = prop.value; + } + } else if (!ASSIMP_stricmp(attribute.name(), "int")) { // + // Only sphere nodes make use of integer attributes + if (Node::SPHERE == nd->type) { + IntProperty prop; + ReadIntProperty(prop, attribute); + if (prop.name == "PolyCountX") { + nd->spherePolyCountX = prop.value; + } else if (prop.name == "PolyCountY") { + nd->spherePolyCountY = prop.value; + } + } + } else if (!ASSIMP_stricmp(attribute.name(), "string") || !ASSIMP_stricmp(attribute.name(), "enum")) { // or < enum /> + StringProperty prop; + ReadStringProperty(prop, attribute); + if (prop.value.length() == 0) continue; // skip empty strings + if (prop.name == "Name") { + nd->name = prop.value; + + /* If we're either a camera or a light source + * we need to update the name in the aiLight/ + * aiCamera structure, too. + */ + if (Node::CAMERA == nd->type) { + cameras.back()->mName.Set(prop.value); + } else if (Node::LIGHT == nd->type) { + lights.back()->mName.Set(prop.value); + } + } else if (Node::LIGHT == nd->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(); + nd->type = Node::DUMMY; + + ASSIMP_LOG_ERROR("Ignoring light of unknown type: ", prop.value); + } + } else if ((prop.name == "Mesh" && Node::MESH == nd->type) || + Node::ANIMMESH == nd->type) { + /* This is the file name of the mesh - either + * animated or not. We need to make sure we setup + * the correct post-processing settings here. + */ + unsigned int pp = 0; + BatchLoader::PropertyMap map; + + /* If the mesh is a static one remove all animations from the impor data + */ + if (Node::ANIMMESH != nd->type) { + pp |= aiProcess_RemoveComponent; + SetGenericProperty(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 { + nd->id = batch.AddLoadRequest(prop.value, pp, &map); + nd->meshPath = prop.value; + } + } + } + } +} + +void IRRImporter::ParseAnimators(pugi::xml_node &animatorNode, IRRImporter::Node *nd) { + Animator *curAnim = nullptr; + // Make empty animator + nd->animators.emplace_back(); + curAnim = &nd->animators.back(); // Push it back + pugi::xml_node attributes = animatorNode.child("attributes"); + if (!attributes) { + ASSIMP_LOG_WARN("Animator node does not contain attributes. "); + return; + } + + for (pugi::xml_node attrib : attributes.children()) { + // XML may contain useless noes like CDATA + if (!ASSIMP_stricmp(attrib.name(), "vector3d")) { + VectorProperty prop; + ReadVectorProperty(prop, attrib); + + 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 (!ASSIMP_stricmp(reader->getNodeName(), "bool")) { + } else if (!ASSIMP_stricmp(attrib.name(), "bool")) { + BoolProperty prop; + ReadBoolProperty(prop, attrib); + + if (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, attrib); + + // 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 (!ASSIMP_stricmp(reader->getNodeName(), "int")) { + } else if (!ASSIMP_stricmp(attrib.name(), "int")) { + IntProperty prop; + ReadIntProperty(prop, attrib); + + if (curAnim->type == Animator::FLY_STRAIGHT && prop.name == "TimeForWay") { + curAnim->timeForWay = 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, attrib); + + if (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; + } + } + } + } +} + +IRRImporter::Node *IRRImporter::ParseNode(pugi::xml_node &node, BatchLoader &batch) { + // Parse tags. + // tags have various types + // tags can contain , + // they can also contain other tags, (and can reference other files as well?) + // *********************************************************************** + /* 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). + * Said materials and animators are all collected at the bottom + */ + // *********************************************************************** + Node *nd; + pugi::xml_attribute nodeTypeAttrib = node.attribute("type"); + if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "mesh") || !ASSIMP_stricmp(nodeTypeAttrib.value(), "octTree")) { + // OctTree's and meshes are treated equally + nd = new Node(Node::MESH); + } else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "cube")) { + nd = new Node(Node::CUBE); + guessedMeshCnt += 1; // Cube is only one mesh + } else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "skybox")) { + nd = new Node(Node::SKYBOX); + guessedMeshCnt += 6; // Skybox is a box, with 6 meshes? + } else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "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(nodeTypeAttrib.value(), "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(nodeTypeAttrib.value(), "sphere")) { + nd = new Node(Node::SPHERE); + guessedMeshCnt += 1; + } else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "animatedMesh")) { + nd = new Node(Node::ANIMMESH); + } else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "empty")) { + nd = new Node(Node::DUMMY); + } else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "terrain")) { + nd = new Node(Node::TERRAIN); + } else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "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: ", nodeTypeAttrib.value()); + + /* 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); + } + + // TODO: consolidate all into one loop + // Collect node attributes first + for (pugi::xml_node attr_node : node.children()) { + if (!ASSIMP_stricmp(attr_node.name(), "attributes")) { + ParseNodeAttributes(attr_node, nd, batch); // Parse attributes into this node + } + } + + // Then parse any materials + // Materials are available to almost all node types + if (nd->type != Node::DUMMY) { + for (pugi::xml_node materialNode : node.children()) { + if (!ASSIMP_stricmp(materialNode.name(), "materials")) { + // Parse material description directly + // Each material should contain an node + // with everything specified + nd->materials.emplace_back(); + std::pair &p = nd->materials.back(); + p.first = ParseMaterial(materialNode, p.second); + guessedMatCnt += 1; + } + } + } + + // Then parse any animators + for (pugi::xml_node animatorNode : node.children()) { + if (!ASSIMP_stricmp(animatorNode.name(), "animators")) { + // All animators should contain an tag + // This is an animation path - add a new animator + // to the list. + ParseAnimators(animatorNode, nd); // Function modifies nd's animator vector + guessedAnimCnt += 1; + } + } + // Then parse any child nodes + /* Attach the newly created node to the scene-graph + */ + // curNode = nd; + // nd->parent = curParent; + // curParent->children.push_back(nd); + for (pugi::xml_node child : node.children()) { + if (!ASSIMP_stricmp(child.name(), "node")) { // Is a child node + Node *childNd = ParseNode(child, batch); // Repeat this function for all children + nd->children.push_back(childNd); + }; + } + + return nd; +} + // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { std::unique_ptr file(pIOHandler->Open(pFile)); - // Check whether we can read from the file if (file == nullptr) { throw DeadlyImportError("Failed to open IRR file ", pFile); @@ -852,405 +1227,47 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy } pugi::xml_node rootElement = st.getRootNode(); + std::stringstream ss; + ss << "Document name: " << rootElement.name() << std::endl; + ss << "Document content: " << std::endl; + rootElement.print(ss); + ss << std::endl; + std::cout << "IrrImporter with"; + std::cout << ss.str() << std::endl; // The root node of the scene + // TODO: Appearantly root node is specified somewhere? Node *root = new Node(Node::DUMMY); root->parent = nullptr; root->name = ""; - // Current node parent - Node *curParent = root; - - // Scene-graph node we're currently working on - Node *curNode = nullptr; - - // List of output cameras - std::vector cameras; - - // List of output lights - std::vector lights; - // Batch loader used to load external models BatchLoader batch(pIOHandler); // batch.SetBasePath(pFile); - cameras.reserve(5); + cameras.reserve(1); // Probably only one camera in entire scene lights.reserve(5); - bool inMaterials = false, inAnimator = false; - unsigned int guessedAnimCnt = 0, guessedMeshCnt = 0, guessedMatCnt = 0; + this->guessedAnimCnt = 0; + this->guessedMeshCnt = 0; + this->guessedMatCnt = 0; - // Parse the XML file - - // while (reader->read()) { - for (pugi::xml_node child : rootElement.children()) - switch (child.type()) { - case pugi::node_element: - if (!ASSIMP_stricmp(child.name(), "node")) { - // *********************************************************************** - /* What we're going to do with the node depends - * on its type: - * - * "mesh" - Load a mesh from an external file - * "cube" - Generate a cube - * "skybox" - Generate a skybox - * "light" - A light source - * "sphere" - Generate a sphere mesh - * "animatedMesh" - Load an animated mesh from an external file - * and join its animation channels with ours. - * "empty" - A dummy node - * "camera" - A camera - * "terrain" - a terrain node (data comes from a heightmap) - * "billboard", "" - * - * Each of these nodes can be animated and all can have multiple - * materials assigned (except lights, cameras and dummies, of course). - */ - // *********************************************************************** - // const char *sz = reader->getAttributeValueSafe("type"); - pugi::xml_attribute attrib = child.attribute("type"); - Node *nd; - if (!ASSIMP_stricmp(attrib.name(), "mesh") || !ASSIMP_stricmp(attrib.name(), "octTree")) { - // OctTree's and meshes are treated equally - nd = new Node(Node::MESH); - } else if (!ASSIMP_stricmp(attrib.name(), "cube")) { - nd = new Node(Node::CUBE); - ++guessedMeshCnt; - } else if (!ASSIMP_stricmp(attrib.name(), "skybox")) { - nd = new Node(Node::SKYBOX); - guessedMeshCnt += 6; - } else if (!ASSIMP_stricmp(attrib.name(), "camera")) { - nd = new Node(Node::CAMERA); - - // Setup a temporary name for the camera - aiCamera *cam = new aiCamera(); - cam->mName.Set(nd->name); - cameras.push_back(cam); - } else if (!ASSIMP_stricmp(attrib.name(), "light")) { - nd = new Node(Node::LIGHT); - - // Setup a temporary name for the light - aiLight *cam = new aiLight(); - cam->mName.Set(nd->name); - lights.push_back(cam); - } else if (!ASSIMP_stricmp(attrib.name(), "sphere")) { - nd = new Node(Node::SPHERE); - ++guessedMeshCnt; - } else if (!ASSIMP_stricmp(attrib.name(), "animatedMesh")) { - nd = new Node(Node::ANIMMESH); - } else if (!ASSIMP_stricmp(attrib.name(), "empty")) { - nd = new Node(Node::DUMMY); - } else if (!ASSIMP_stricmp(attrib.name(), "terrain")) { - nd = new Node(Node::TERRAIN); - } else if (!ASSIMP_stricmp(attrib.name(), "billBoard")) { - // We don't support billboards, so ignore them - ASSIMP_LOG_ERROR("IRR: Billboards are not supported by Assimp"); - nd = new Node(Node::DUMMY); - } else { - ASSIMP_LOG_WARN("IRR: Found unknown node: ", attrib.name()); - - /* We skip the contents of nodes we don't know. - * We parse the transformation and all animators - * and skip the rest. - */ - nd = new Node(Node::DUMMY); - } - - /* Attach the newly created node to the scene-graph - */ - curNode = nd; - nd->parent = curParent; - curParent->children.push_back(nd); - } else if (!ASSIMP_stricmp(child.name(), "materials")) { - inMaterials = true; - } else if (!ASSIMP_stricmp(child.name(), "animators")) { - inAnimator = true; - } else if (!ASSIMP_stricmp(child.name(), "attributes")) { - // We should have a valid node here - // FIX: no ... the scene root node is also contained in an attributes block - if (!curNode) { - continue; - } - - Animator *curAnim = nullptr; - - // Materials can occur for nearly any type of node - if (inMaterials && curNode->type != Node::DUMMY) { - // This is a material description - parse it! - curNode->materials.emplace_back(); - std::pair &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 - * we need to update the name in the aiLight/ - * aiCamera structure, too. - */ - if (Node::CAMERA == curNode->type) { - cameras.back()->mName.Set(prop.value); - } else if (Node::LIGHT == curNode->type) { - lights.back()->mName.Set(prop.value); - } - } else if (Node::LIGHT == curNode->type && "LightType" == prop.name) { - if (prop.value == "Spot") - lights.back()->mType = aiLightSource_SPOT; - else if (prop.value == "Point") - lights.back()->mType = aiLightSource_POINT; - else if (prop.value == "Directional") - lights.back()->mType = aiLightSource_DIRECTIONAL; - else { - // We won't pass the validation with aiLightSourceType_UNDEFINED, - // so we remove the light and replace it with a silly dummy node - delete lights.back(); - lights.pop_back(); - curNode->type = Node::DUMMY; - - ASSIMP_LOG_ERROR("Ignoring light of unknown type: ", prop.value); - } - } else if ((prop.name == "Mesh" && Node::MESH == curNode->type) || - Node::ANIMMESH == curNode->type) { - /* This is the file name of the mesh - either - * animated or not. We need to make sure we setup - * the correct post-processing settings here. - */ - unsigned int pp = 0; - BatchLoader::PropertyMap map; - - /* If the mesh is a static one remove all animations from the impor data - */ - if (Node::ANIMMESH != curNode->type) { - pp |= aiProcess_RemoveComponent; - SetGenericProperty(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 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; + // Parse the XML + // First node is the xml header. Awkwardly skip to sibling's children + // I don't like recursion + std::vector nextNodes; + for (auto &node : rootElement.children().begin()->next_sibling().children()) { + nextNodes.push_back(node); // Find second node, , and push it's children to queue + } + for (pugi::xml_node &child : nextNodes) { + if (child.type() != pugi::node_element) continue; // Only semantically valuable nodes + // XML elements are either nodes, animators, attributes, or materials + if (!ASSIMP_stricmp(child.name(), "node")) { + // Recursive ollect subtree children + Node *nd = ParseNode(child, batch); + // Attach to root + root->children.push_back(nd); } - //} + } // Now iterate through all cameras and compute their final (horizontal) FOV for (aiCamera *cam : cameras) { @@ -1336,8 +1353,7 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy // 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) : + 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 diff --git a/code/AssetLib/Irr/IRRLoader.h b/code/AssetLib/Irr/IRRLoader.h index a9e70cc1e..72ad5d35e 100644 --- a/code/AssetLib/Irr/IRRLoader.h +++ b/code/AssetLib/Irr/IRRLoader.h @@ -207,6 +207,24 @@ private: aiVector3D position, normal, uv; }; + // ------------------------------------------------------------------- + // Parse tag from XML file and extract child node + // @param node XML node + // @param guessedMeshesContained number of extra guessed meshes + IRRImporter::Node *ParseNode(pugi::xml_node &node, BatchLoader& batch); + + // ------------------------------------------------------------------- + // Parse tags within tags and apply to scene node + // @param attributeNode XML child node + // @param nd Attributed scene node + void ParseNodeAttributes(pugi::xml_node &attributeNode, IRRImporter::Node *nd, BatchLoader& batch); + + // ------------------------------------------------------------------- + // Parse an node and attach an animator to a node + // @param animatorNode XML animator node + // @param nd Animated scene node + void ParseAnimators(pugi::xml_node &animatorNode, IRRImporter::Node *nd); + // ------------------------------------------------------------------- /// Fill the scene-graph recursively void GenerateGraph(Node *root, aiNode *rootOut, aiScene *scene, @@ -261,6 +279,12 @@ private: /// Configuration option: speed flag was set? bool configSpeedFlag; + + std::vector cameras; + std::vector lights; + unsigned int guessedMeshCnt; + unsigned int guessedMatCnt; + unsigned int guessedAnimCnt; }; } // end of namespace Assimp diff --git a/code/AssetLib/Irr/IRRMeshLoader.cpp b/code/AssetLib/Irr/IRRMeshLoader.cpp index 84b8f80d4..6a0b99582 100644 --- a/code/AssetLib/Irr/IRRMeshLoader.cpp +++ b/code/AssetLib/Irr/IRRMeshLoader.cpp @@ -176,7 +176,7 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile, ASSIMP_LOG_WARN("IRRMESH: Only one material description per buffer, please"); releaseMaterial(&curMat); } - curMat = ParseMaterial(curMatFlags); + // curMat = ParseMaterial(curMatFlags); } /* no else here! */ if (!ASSIMP_stricmp(child.name(), "vertices")) { pugi::xml_attribute attr = child.attribute("vertexCount"); @@ -495,4 +495,8 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile, } } +void IRRMeshImporter::ParseMaterialBuffer(pugi::xml_node& bufferNode) { + +} + #endif // !! ASSIMP_BUILD_NO_IRRMESH_IMPORTER diff --git a/code/AssetLib/Irr/IRRMeshLoader.h b/code/AssetLib/Irr/IRRMeshLoader.h index 79c1e486b..c52aca1e4 100644 --- a/code/AssetLib/Irr/IRRMeshLoader.h +++ b/code/AssetLib/Irr/IRRMeshLoader.h @@ -85,6 +85,8 @@ protected: */ void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override; + private: + void ParseMaterialBuffer(pugi::xml_node& bufferNode); }; } // end of namespace Assimp diff --git a/code/AssetLib/Irr/IRRShared.cpp b/code/AssetLib/Irr/IRRShared.cpp index 7318b1d73..a47aeccba 100644 --- a/code/AssetLib/Irr/IRRShared.cpp +++ b/code/AssetLib/Irr/IRRShared.cpp @@ -63,34 +63,34 @@ const aiMatrix4x4 Assimp::AI_TO_IRR_MATRIX = aiMatrix4x4( // ------------------------------------------------------------------------------------------------ // 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, pugi::xml_node& hexnode) { + for (pugi::xml_attribute attrib : hexnode.attributes()) { if (!ASSIMP_stricmp(attrib.name(), "name")) { out.name = std::string(attrib.value()); } else if (!ASSIMP_stricmp(attrib.name(), "value")) { // parse the hexadecimal value - out.value = strtoul16(attrib.name()); + out.value = strtoul16(attrib.value()); } } } // ------------------------------------------------------------------------------------------------ // read a decimal property -void IrrlichtBase::ReadIntProperty(IntProperty &out) { - for (pugi::xml_attribute attrib : mNode->attributes()) { +void IrrlichtBase::ReadIntProperty(IntProperty &out, pugi::xml_node& intnode) { + for (pugi::xml_attribute attrib : intnode.attributes()) { if (!ASSIMP_stricmp(attrib.name(), "name")) { out.name = std::string(attrib.value()); - } else if (!ASSIMP_stricmp(attrib.value(), "value")) { + } else if (!ASSIMP_stricmp(attrib.name(), "value")) { // parse the int value - out.value = strtol10(attrib.name()); + out.value = strtol10(attrib.value()); } } } // ------------------------------------------------------------------------------------------------ // read a string property -void IrrlichtBase::ReadStringProperty(StringProperty &out) { - for (pugi::xml_attribute attrib : mNode->attributes()) { +void IrrlichtBase::ReadStringProperty(StringProperty &out, pugi::xml_node& stringnode) { + for (pugi::xml_attribute attrib : stringnode.attributes()) { if (!ASSIMP_stricmp(attrib.name(), "name")) { out.name = std::string(attrib.value()); } else if (!ASSIMP_stricmp(attrib.name(), "value")) { @@ -102,8 +102,8 @@ void IrrlichtBase::ReadStringProperty(StringProperty &out) { // ------------------------------------------------------------------------------------------------ // read a boolean property -void IrrlichtBase::ReadBoolProperty(BoolProperty &out) { - for (pugi::xml_attribute attrib : mNode->attributes()) { +void IrrlichtBase::ReadBoolProperty(BoolProperty &out, pugi::xml_node& boolnode) { + for (pugi::xml_attribute attrib : boolnode.attributes()) { if (!ASSIMP_stricmp(attrib.name(), "name")) { out.name = std::string(attrib.value()); } else if (!ASSIMP_stricmp(attrib.name(), "value")) { @@ -115,8 +115,8 @@ void IrrlichtBase::ReadBoolProperty(BoolProperty &out) { // ------------------------------------------------------------------------------------------------ // read a float property -void IrrlichtBase::ReadFloatProperty(FloatProperty &out) { - for (pugi::xml_attribute attrib : mNode->attributes()) { +void IrrlichtBase::ReadFloatProperty(FloatProperty &out, pugi::xml_node &floatnode) { + for (pugi::xml_attribute attrib : floatnode.attributes()) { if (!ASSIMP_stricmp(attrib.name(), "name")) { out.name = std::string(attrib.value()); } else if (!ASSIMP_stricmp(attrib.name(), "value")) { @@ -128,8 +128,8 @@ void IrrlichtBase::ReadFloatProperty(FloatProperty &out) { // ------------------------------------------------------------------------------------------------ // read a vector property -void IrrlichtBase::ReadVectorProperty(VectorProperty &out) { - for (pugi::xml_attribute attrib : mNode->attributes()) { +void IrrlichtBase::ReadVectorProperty(VectorProperty &out, pugi::xml_node& vectornode) { + for (pugi::xml_attribute attrib : vectornode.attributes()) { if (!ASSIMP_stricmp(attrib.name(), "name")) { out.name = std::string(attrib.value()); } else if (!ASSIMP_stricmp(attrib.name(), "value")) { @@ -170,7 +170,7 @@ int ConvertMappingMode(const std::string &mode) { // ------------------------------------------------------------------------------------------------ // Parse a material from the XML file -aiMaterial *IrrlichtBase::ParseMaterial(unsigned int &matFlags) { +aiMaterial *IrrlichtBase::ParseMaterial(pugi::xml_node& materialNode, unsigned int &matFlags) { aiMaterial *mat = new aiMaterial(); aiColor4D clr; aiString s; @@ -179,10 +179,10 @@ aiMaterial *IrrlichtBase::ParseMaterial(unsigned int &matFlags) { int cnt = 0; // number of used texture channels unsigned int nd = 0; - for (pugi::xml_node child : mNode->children()) { + for (pugi::xml_node child : materialNode.children()) { if (!ASSIMP_stricmp(child.name(), "color")) { // Hex properties HexProperty prop; - ReadHexProperty(prop); + ReadHexProperty(prop, child); if (prop.name == "Diffuse") { ColorFromARGBPacked(prop.value, clr); mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_DIFFUSE); @@ -206,13 +206,13 @@ aiMaterial *IrrlichtBase::ParseMaterial(unsigned int &matFlags) { #endif } else if (!ASSIMP_stricmp(child.name(), "float")) { // Float properties FloatProperty prop; - ReadFloatProperty(prop); + ReadFloatProperty(prop, child); 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); + ReadBoolProperty(prop, child); if (prop.name == "Wireframe") { int val = (prop.value ? true : false); mat->AddProperty(&val, 1, AI_MATKEY_ENABLE_WIREFRAME); @@ -226,7 +226,7 @@ aiMaterial *IrrlichtBase::ParseMaterial(unsigned int &matFlags) { } else if (!ASSIMP_stricmp(child.name(), "texture") || !ASSIMP_stricmp(child.name(), "enum")) { // String properties - textures and texture related properties StringProperty prop; - ReadStringProperty(prop); + ReadStringProperty(prop, child); if (prop.value.length()) { // material type (shader) if (prop.name == "Type") { @@ -379,7 +379,7 @@ aiMaterial *IrrlichtBase::ParseMaterial(unsigned int &matFlags) { } }*/ } - 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; } diff --git a/code/AssetLib/Irr/IRRShared.h b/code/AssetLib/Irr/IRRShared.h index d7fe93710..a8997451d 100644 --- a/code/AssetLib/Irr/IRRShared.h +++ b/code/AssetLib/Irr/IRRShared.h @@ -58,8 +58,7 @@ extern const aiMatrix4x4 AI_TO_IRR_MATRIX; */ class IrrlichtBase { protected: - IrrlichtBase() : - mNode(nullptr) { + IrrlichtBase() { // empty } @@ -82,25 +81,25 @@ protected: /// XML reader instance XmlParser mParser; - pugi::xml_node *mNode; // ------------------------------------------------------------------- /** Parse a material description from the XML * @return The created material * @param matFlags Receives AI_IRRMESH_MAT_XX flags */ - aiMaterial *ParseMaterial(unsigned int &matFlags); + aiMaterial *ParseMaterial(pugi::xml_node &materialNode, unsigned int &matFlags); // ------------------------------------------------------------------- /** Read a property of the specified type from the current XML element. * @param out Receives output data + * @param node XML attribute element data */ - void ReadHexProperty(HexProperty &out); - void ReadStringProperty(StringProperty &out); - void ReadBoolProperty(BoolProperty &out); - void ReadFloatProperty(FloatProperty &out); - void ReadVectorProperty(VectorProperty &out); - void ReadIntProperty(IntProperty &out); + void ReadHexProperty(HexProperty &out, pugi::xml_node& hexnode); + void ReadStringProperty(StringProperty &out, pugi::xml_node& stringnode); + void ReadBoolProperty(BoolProperty &out, pugi::xml_node& boolnode); + void ReadFloatProperty(FloatProperty &out, pugi::xml_node& floatnode); + void ReadVectorProperty(VectorProperty &out, pugi::xml_node& vectornode); + void ReadIntProperty(IntProperty &out, pugi::xml_node& intnode); }; // ------------------------------------------------------------------------------------------------ From 19da9cc84db1c27c111bfd79726debbcec339d33 Mon Sep 17 00:00:00 2001 From: PencilAmazing <16854231+PencilAmazing@users.noreply.github.com> Date: Thu, 29 Jun 2023 16:21:52 -0400 Subject: [PATCH 08/12] Port mesh loading to pugixml. Untested. --- code/AssetLib/Irr/IRRLoader.cpp | 3 +- code/AssetLib/Irr/IRRMeshLoader.cpp | 646 ++++++++++++++-------------- code/AssetLib/Irr/IRRMeshLoader.h | 15 +- 3 files changed, 347 insertions(+), 317 deletions(-) diff --git a/code/AssetLib/Irr/IRRLoader.cpp b/code/AssetLib/Irr/IRRLoader.cpp index 86f744a64..2ca6bfd65 100644 --- a/code/AssetLib/Irr/IRRLoader.cpp +++ b/code/AssetLib/Irr/IRRLoader.cpp @@ -66,8 +66,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include -#include using namespace Assimp; @@ -1254,6 +1252,7 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy // Parse the XML // First node is the xml header. Awkwardly skip to sibling's children // I don't like recursion + // TODO clean up std::vector nextNodes; for (auto &node : rootElement.children().begin()->next_sibling().children()) { nextNodes.push_back(node); // Find second node, , and push it's children to queue diff --git a/code/AssetLib/Irr/IRRMeshLoader.cpp b/code/AssetLib/Irr/IRRMeshLoader.cpp index 6a0b99582..88c4f6346 100644 --- a/code/AssetLib/Irr/IRRMeshLoader.cpp +++ b/code/AssetLib/Irr/IRRMeshLoader.cpp @@ -133,6 +133,7 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile, meshes.reserve(5); // temporary data - current mesh buffer + // TODO move all these to inside loop aiMaterial *curMat = nullptr; aiMesh *curMesh = nullptr; unsigned int curMatFlags = 0; @@ -142,321 +143,245 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile, std::vector curUVs, curUV2s; // some temporary variables - int textMeaning = 0; - int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents + // textMeaning is a 15 year old variable, that could've been an enum + // int textMeaning = 0; // 0=none? 1=vertices 2=indices + // int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents bool useColors = false; + /* + ** irrmesh files have a top level owning multiple nodes. + ** Each contains , , and + ** tags here directly owns the material data specs + ** are a vertex per line, contains position, UV1 coords, maybe UV2, normal, tangent, bitangent + ** is ignored, I think assimp recalculates those? + */ + // Parse the XML file - for (pugi::xml_node child : root.children()) { - if (child.type() == pugi::node_element) { - if (!ASSIMP_stricmp(child.name(), "buffer") && (curMat || curMesh)) { - // end of previous buffer. A material and a mesh should be there - if (!curMat || !curMesh) { - ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material"); - releaseMaterial(&curMat); - releaseMesh(&curMesh); - } else { - materials.push_back(curMat); - meshes.push_back(curMesh); - } - curMat = nullptr; - curMesh = nullptr; - - curVertices.clear(); - curColors.clear(); - curNormals.clear(); - curUV2s.clear(); - curUVs.clear(); - curTangents.clear(); - curBitangents.clear(); - } - - if (!ASSIMP_stricmp(child.name(), "material")) { - if (curMat) { - ASSIMP_LOG_WARN("IRRMESH: Only one material description per buffer, please"); - releaseMaterial(&curMat); - } - // curMat = ParseMaterial(curMatFlags); - } - /* no else here! */ if (!ASSIMP_stricmp(child.name(), "vertices")) { - pugi::xml_attribute attr = child.attribute("vertexCount"); - int num = attr.as_int(); - // int num = reader->getAttributeValueAsInt("vertexCount"); - - if (!num) { - // This is possible ... remove the mesh from the list and skip further reading - ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero vertices"); - - releaseMaterial(&curMat); - releaseMesh(&curMesh); - textMeaning = 0; - continue; - } - - curVertices.reserve(num); - curNormals.reserve(num); - curColors.reserve(num); - curUVs.reserve(num); - - // Determine the file format - // const char *t = reader->getAttributeValueSafe("type"); - pugi::xml_attribute t = child.attribute("type"); - if (!ASSIMP_stricmp("2tcoords", t.name())) { - curUV2s.reserve(num); - vertexFormat = 1; - - if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) { - // ********************************************************* - // We have a second texture! So use this UV channel - // for it. The 2nd texture can be either a normal - // texture (solid_2layer or lightmap_xxx) or a normal - // map (normal_..., parallax_...) - // ********************************************************* - int idx = 1; - aiMaterial *mat = (aiMaterial *)curMat; - - if (curMatFlags & AI_IRRMESH_MAT_lightmap) { - mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_LIGHTMAP(0)); - } else if (curMatFlags & AI_IRRMESH_MAT_normalmap_solid) { - mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_NORMALS(0)); - } else if (curMatFlags & AI_IRRMESH_MAT_solid_2layer) { - mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_DIFFUSE(1)); - } - } - } else if (!ASSIMP_stricmp("tangents", t.name())) { - curTangents.reserve(num); - curBitangents.reserve(num); - vertexFormat = 2; - } else if (ASSIMP_stricmp("standard", t.name())) { - releaseMaterial(&curMat); - ASSIMP_LOG_WARN("IRRMESH: Unknown vertex format"); - } else - vertexFormat = 0; - textMeaning = 1; - } else if (!ASSIMP_stricmp(child.name(), "indices")) { - if (curVertices.empty() && curMat) { - releaseMaterial(&curMat); - throw DeadlyImportError("IRRMESH: indices must come after vertices"); - } - - textMeaning = 2; - - // start a new mesh - curMesh = new aiMesh(); - - // allocate storage for all faces - pugi::xml_attribute attr = child.attribute("indexCount"); - curMesh->mNumVertices = attr.as_int(); - if (!curMesh->mNumVertices) { - // This is possible ... remove the mesh from the list and skip further reading - ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero indices"); - - // mesh - away - releaseMesh(&curMesh); - - // material - away - releaseMaterial(&curMat); - - textMeaning = 0; - continue; - } - - if (curMesh->mNumVertices % 3) { - ASSIMP_LOG_WARN("IRRMESH: Number if indices isn't divisible by 3"); - } - - curMesh->mNumFaces = curMesh->mNumVertices / 3; - curMesh->mFaces = new aiFace[curMesh->mNumFaces]; - - // setup some members - curMesh->mMaterialIndex = (unsigned int)materials.size(); - curMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - - // allocate storage for all vertices - curMesh->mVertices = new aiVector3D[curMesh->mNumVertices]; - - if (curNormals.size() == curVertices.size()) { - curMesh->mNormals = new aiVector3D[curMesh->mNumVertices]; - } - if (curTangents.size() == curVertices.size()) { - curMesh->mTangents = new aiVector3D[curMesh->mNumVertices]; - } - if (curBitangents.size() == curVertices.size()) { - curMesh->mBitangents = new aiVector3D[curMesh->mNumVertices]; - } - if (curColors.size() == curVertices.size() && useColors) { - curMesh->mColors[0] = new aiColor4D[curMesh->mNumVertices]; - } - if (curUVs.size() == curVertices.size()) { - curMesh->mTextureCoords[0] = new aiVector3D[curMesh->mNumVertices]; - } - if (curUV2s.size() == curVertices.size()) { - curMesh->mTextureCoords[1] = new aiVector3D[curMesh->mNumVertices]; - } - } - // break; - - // case EXN_TEXT: { - const char *sz = child.child_value(); - if (textMeaning == 1) { - textMeaning = 0; - - // read vertices - do { - SkipSpacesAndLineEnd(&sz); - aiVector3D temp; - aiColor4D c; - - // Read the vertex position - sz = fast_atoreal_move(sz, (float &)temp.x); - SkipSpaces(&sz); - - sz = fast_atoreal_move(sz, (float &)temp.y); - SkipSpaces(&sz); - - sz = fast_atoreal_move(sz, (float &)temp.z); - SkipSpaces(&sz); - curVertices.push_back(temp); - - // Read the vertex normals - sz = fast_atoreal_move(sz, (float &)temp.x); - SkipSpaces(&sz); - - sz = fast_atoreal_move(sz, (float &)temp.y); - SkipSpaces(&sz); - - sz = fast_atoreal_move(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(sz, (float &)temp.x); - SkipSpaces(&sz); - - sz = fast_atoreal_move(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(sz, (float &)temp.x); - SkipSpaces(&sz); - - sz = fast_atoreal_move(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(sz, (float &)temp.x); - SkipSpaces(&sz); - - sz = fast_atoreal_move(sz, (float &)temp.z); - SkipSpaces(&sz); - - sz = fast_atoreal_move(sz, (float &)temp.y); - SkipSpaces(&sz); - temp.y *= -1.0f; - curTangents.push_back(temp); - - // bitangents - sz = fast_atoreal_move(sz, (float &)temp.x); - SkipSpaces(&sz); - - sz = fast_atoreal_move(sz, (float &)temp.z); - SkipSpaces(&sz); - - sz = fast_atoreal_move(sz, (float &)temp.y); - SkipSpaces(&sz); - temp.y *= -1.0f; - curBitangents.push_back(temp); - } - } - - /* IMPORTANT: We assume that each vertex is specified in one - line. So we can skip the rest of the line - unknown vertex - elements are ignored. - */ - - while (SkipLine(&sz)); - } else if (textMeaning == 2) { - textMeaning = 0; - - // read indices - aiFace *curFace = curMesh->mFaces; - aiFace *const faceEnd = curMesh->mFaces + curMesh->mNumFaces; - - aiVector3D *pcV = curMesh->mVertices; - aiVector3D *pcN = curMesh->mNormals; - aiVector3D *pcT = curMesh->mTangents; - aiVector3D *pcB = curMesh->mBitangents; - aiColor4D *pcC0 = curMesh->mColors[0]; - aiVector3D *pcT0 = curMesh->mTextureCoords[0]; - aiVector3D *pcT1 = curMesh->mTextureCoords[1]; - - unsigned int curIdx = 0; - unsigned int total = 0; - while (SkipSpacesAndLineEnd(&sz)) { - if (curFace >= faceEnd) { - ASSIMP_LOG_ERROR("IRRMESH: Too many indices"); - break; - } - if (!curIdx) { - curFace->mNumIndices = 3; - curFace->mIndices = new unsigned int[3]; - } - - unsigned int idx = strtoul10(sz, &sz); - if (idx >= curVertices.size()) { - ASSIMP_LOG_ERROR("IRRMESH: Index out of range"); - idx = 0; - } - - curFace->mIndices[curIdx] = total++; - - *pcV++ = curVertices[idx]; - if (pcN) *pcN++ = curNormals[idx]; - if (pcT) *pcT++ = curTangents[idx]; - if (pcB) *pcB++ = curBitangents[idx]; - if (pcC0) *pcC0++ = curColors[idx]; - if (pcT0) *pcT0++ = curUVs[idx]; - if (pcT1) *pcT1++ = curUV2s[idx]; - - if (++curIdx == 3) { - ++curFace; - curIdx = 0; - } - } - - if (curFace != faceEnd) - ASSIMP_LOG_ERROR("IRRMESH: Not enough indices"); - - // Finish processing the mesh - do some small material workarounds - if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors) { - // Take the opacity value of the current material - // from the common vertex color alpha - aiMaterial *mat = (aiMaterial *)curMat; - mat->AddProperty(&curColors[0].a, 1, AI_MATKEY_OPACITY); - } - } + // FIXME get not root + for (pugi::xml_node bufferNode : root.children()) { + if (ASSIMP_stricmp(bufferNode.name(), "buffer")) { + // Might be a useless warning + ASSIMP_LOG_WARN("IRRMESH: Ignoring non buffer node <", bufferNode.name(), "> in mesh declaration"); + continue; } - } - // End of the last buffer. A material and a mesh should be there - if (curMat || curMesh) { + curMat = nullptr; + curMesh = nullptr; + + curVertices.clear(); + curColors.clear(); + curNormals.clear(); + curUV2s.clear(); + curUVs.clear(); + curTangents.clear(); + curBitangents.clear(); + + // TODO ensure all three nodes are present and populated + // before allocating everything + + // Get first material node + pugi::xml_node materialNode = bufferNode.child("material"); + if (materialNode) { + curMat = ParseMaterial(materialNode, curMatFlags); + // Warn if there's more materials + if (materialNode.next_sibling("material")) { + ASSIMP_LOG_WARN("IRRMESH: Only one material description per buffer, please"); + } + } else { + ASSIMP_LOG_ERROR("IRRMESH: Buffer must contain one material"); + continue; + } + + // Get first vertices node + pugi::xml_node verticesNode = bufferNode.child("vertices"); + if (verticesNode) { + pugi::xml_attribute vertexCountAttrib = verticesNode.attribute("vertexCount"); + int vertexCount = vertexCountAttrib.as_int(); + if (vertexCount == 0) { + // 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); + continue; // Bail out early + }; + + curVertices.reserve(vertexCount); + curNormals.reserve(vertexCount); + curColors.reserve(vertexCount); + curUVs.reserve(vertexCount); + + VertexFormat vertexFormat; + // Determine the file format + pugi::xml_attribute typeAttrib = verticesNode.attribute("type"); + if (!ASSIMP_stricmp("2tcoords", typeAttrib.value())) { + curUV2s.reserve(vertexCount); + vertexFormat = VertexFormat::t2coord; + 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", typeAttrib.value())) { + curTangents.reserve(vertexCount); + curBitangents.reserve(vertexCount); + vertexFormat = VertexFormat::tangent; + } else if (!ASSIMP_stricmp("standard", typeAttrib.value())) { + vertexFormat = VertexFormat::standard; + } else { + // Unsupported format, discard whole buffer/mesh + // Assuming we have a correct material, then release it + // We don't have a correct mesh for sure here + releaseMaterial(&curMat); + ASSIMP_LOG_ERROR("IRRMESH: Unknown vertex format"); + continue; // Skip rest of buffer + }; + + // We know what format buffer is, collect numbers + ParseBufferVertices(verticesNode.text().get(), vertexFormat, + curVertices, curNormals, + curTangents, curBitangents, + curUVs, curUV2s, curColors, useColors); + } + + // Get indices + // At this point we have some vertices and a valid material + // Collect indices and create aiMesh at the same time + pugi::xml_node indicesNode = bufferNode.child("indices"); + if (indicesNode) { + // start a new mesh + curMesh = new aiMesh(); + + // allocate storage for all faces + pugi::xml_attribute attr = indicesNode.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); + continue; // Go to next buffer + } + + 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]; + } + + // 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; + + // NOTE this might explode for UTF-16 and wchars + const char *sz = indicesNode.text().get(); + // For each index loop over aiMesh faces + while (SkipSpacesAndLineEnd(&sz)) { + if (curFace >= faceEnd) { + ASSIMP_LOG_ERROR("IRRMESH: Too many indices"); + break; + } + // if new face + if (!curIdx) { + curFace->mNumIndices = 3; + curFace->mIndices = new unsigned int[3]; + } + + // Read index base 10 + // function advances the pointer + unsigned int idx = strtoul10(sz, &sz); + if (idx >= curVertices.size()) { + ASSIMP_LOG_ERROR("IRRMESH: Index out of range"); + idx = 0; + } + + // make up our own indices? + curFace->mIndices[curIdx] = total++; + + // Copy over data to aiMesh + *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]; + + // start new face + if (++curIdx == 3) { + ++curFace; + curIdx = 0; + } + } + // We should be at the end of mFaces + 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); + } + // textMeaning = 2; + + // 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); @@ -467,7 +392,8 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile, } } - if (materials.empty()) { + // If one is empty then so is the other + if (materials.empty() || meshes.empty()) { throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file"); } @@ -492,11 +418,105 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile, for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { pScene->mRootNode->mMeshes[i] = i; - } + }; } -void IRRMeshImporter::ParseMaterialBuffer(pugi::xml_node& bufferNode) { +void IRRMeshImporter::ParseBufferVertices(const char *sz, VertexFormat vertexFormat, + std::vector &vertices, std::vector &normals, + std::vector &tangents, std::vector &bitangents, + std::vector &UVs, std::vector &UV2s, + std::vector &colors, bool &useColors) { + // read vertices + do { + SkipSpacesAndLineEnd(&sz); + aiVector3D temp; + aiColor4D c; + // Read the vertex position + sz = fast_atoreal_move(sz, (float &)temp.x); + SkipSpaces(&sz); + + sz = fast_atoreal_move(sz, (float &)temp.y); + SkipSpaces(&sz); + + sz = fast_atoreal_move(sz, (float &)temp.z); + SkipSpaces(&sz); + vertices.push_back(temp); + + // Read the vertex normals + sz = fast_atoreal_move(sz, (float &)temp.x); + SkipSpaces(&sz); + + sz = fast_atoreal_move(sz, (float &)temp.y); + SkipSpaces(&sz); + + sz = fast_atoreal_move(sz, (float &)temp.z); + SkipSpaces(&sz); + normals.push_back(temp); + + // read the vertex colors + uint32_t clr = strtoul16(sz, &sz); + ColorFromARGBPacked(clr, c); + + // If we're pushing more than one distinct color + if (!colors.empty() && c != *(colors.end() - 1)) + useColors = true; + + colors.push_back(c); + SkipSpaces(&sz); + + // read the first UV coordinate set + sz = fast_atoreal_move(sz, (float &)temp.x); + SkipSpaces(&sz); + + sz = fast_atoreal_move(sz, (float &)temp.y); + SkipSpaces(&sz); + temp.z = 0.f; + temp.y = 1.f - temp.y; // DX to OGL + UVs.push_back(temp); + + // NOTE these correspond to specific S3DVertex* structs in irr sourcecode + // So by definition, all buffers have either UV2 or tangents or neither + // read the (optional) second UV coordinate set + if (vertexFormat == VertexFormat::t2coord) { + sz = fast_atoreal_move(sz, (float &)temp.x); + SkipSpaces(&sz); + + sz = fast_atoreal_move(sz, (float &)temp.y); + temp.y = 1.f - temp.y; // DX to OGL + UV2s.push_back(temp); + } + // read optional tangent and bitangent vectors + else if (vertexFormat == VertexFormat::tangent) { + // tangents + sz = fast_atoreal_move(sz, (float &)temp.x); + SkipSpaces(&sz); + + sz = fast_atoreal_move(sz, (float &)temp.z); + SkipSpaces(&sz); + + sz = fast_atoreal_move(sz, (float &)temp.y); + SkipSpaces(&sz); + temp.y *= -1.0f; + tangents.push_back(temp); + + // bitangents + sz = fast_atoreal_move(sz, (float &)temp.x); + SkipSpaces(&sz); + + sz = fast_atoreal_move(sz, (float &)temp.z); + SkipSpaces(&sz); + + sz = fast_atoreal_move(sz, (float &)temp.y); + SkipSpaces(&sz); + temp.y *= -1.0f; + bitangents.push_back(temp); + } + } while (SkipLine(&sz)); + /* IMPORTANT: We assume that each vertex is specified in one + line. So we can skip the rest of the line - unknown vertex + elements are ignored. + */ } #endif // !! ASSIMP_BUILD_NO_IRRMESH_IMPORTER diff --git a/code/AssetLib/Irr/IRRMeshLoader.h b/code/AssetLib/Irr/IRRMeshLoader.h index c52aca1e4..620e40dba 100644 --- a/code/AssetLib/Irr/IRRMeshLoader.h +++ b/code/AssetLib/Irr/IRRMeshLoader.h @@ -85,8 +85,19 @@ protected: */ void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override; - private: - void ParseMaterialBuffer(pugi::xml_node& bufferNode); + +private: + enum class VertexFormat { + standard = 0, // "standard" - also noted as 'normal' format elsewhere + t2coord = 1, // "2tcoord" - standard + 2 UV maps + tangent = 2, // "tangents" - standard + tangents and bitangents + }; + + void ParseBufferVertices(const char *sz, VertexFormat vertexFormat, + std::vector &vertices, std::vector &normals, + std::vector &tangents, std::vector &bitangents, + std::vector &UVs, std::vector &UV2s, + std::vector &colors, bool &useColors); }; } // end of namespace Assimp From 0bacc7134db4ae3cf66ba3535a5fa9599eb2363a Mon Sep 17 00:00:00 2001 From: PencilAmazing <16854231+PencilAmazing@users.noreply.github.com> Date: Fri, 30 Jun 2023 19:57:09 -0400 Subject: [PATCH 09/12] Remove debugging code, clean up some notes --- code/AssetLib/Irr/IRRLoader.cpp | 77 ++++++++++------------------- code/AssetLib/Irr/IRRMeshLoader.cpp | 4 +- code/AssetLib/Irr/IRRShared.h | 2 +- 3 files changed, 30 insertions(+), 53 deletions(-) diff --git a/code/AssetLib/Irr/IRRLoader.cpp b/code/AssetLib/Irr/IRRLoader.cpp index 2ca6bfd65..ba6ebc964 100644 --- a/code/AssetLib/Irr/IRRLoader.cpp +++ b/code/AssetLib/Irr/IRRLoader.cpp @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @brief Implementation of the Irr importer class */ +#include "assimp/Exceptional.h" #include "assimp/StringComparison.h" #ifndef ASSIMP_BUILD_NO_IRR_IMPORTER @@ -63,10 +64,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include -#include -#include - using namespace Assimp; static const aiImporterDesc desc = { @@ -1160,45 +1157,37 @@ IRRImporter::Node *IRRImporter::ParseNode(pugi::xml_node &node, BatchLoader &bat } // TODO: consolidate all into one loop - // Collect node attributes first - for (pugi::xml_node attr_node : node.children()) { - if (!ASSIMP_stricmp(attr_node.name(), "attributes")) { - ParseNodeAttributes(attr_node, nd, batch); // Parse attributes into this node - } - } + for (pugi::xml_node subNode : node.children()) { + // Collect node attributes first + if (!ASSIMP_stricmp(subNode.name(), "attributes")) { + ParseNodeAttributes(subNode, nd, batch); // Parse attributes into this node + } else if (!ASSIMP_stricmp(subNode.name(), "animators")) { + // Then parse any animators + // All animators should contain an tag - // Then parse any materials - // Materials are available to almost all node types - if (nd->type != Node::DUMMY) { - for (pugi::xml_node materialNode : node.children()) { - if (!ASSIMP_stricmp(materialNode.name(), "materials")) { + // This is an animation path - add a new animator + // to the list. + ParseAnimators(subNode, nd); // Function modifies nd's animator vector + guessedAnimCnt += 1; + } + + // Then parse any materials + // Materials are available to almost all node types + if (nd->type != Node::DUMMY) { + if (!ASSIMP_stricmp(subNode.name(), "materials")) { // Parse material description directly // Each material should contain an node // with everything specified nd->materials.emplace_back(); std::pair &p = nd->materials.back(); - p.first = ParseMaterial(materialNode, p.second); + p.first = ParseMaterial(subNode, p.second); guessedMatCnt += 1; } } } - // Then parse any animators - for (pugi::xml_node animatorNode : node.children()) { - if (!ASSIMP_stricmp(animatorNode.name(), "animators")) { - // All animators should contain an tag - // This is an animation path - add a new animator - // to the list. - ParseAnimators(animatorNode, nd); // Function modifies nd's animator vector - guessedAnimCnt += 1; - } - } // Then parse any child nodes - /* Attach the newly created node to the scene-graph - */ - // curNode = nd; - // nd->parent = curParent; - // curParent->children.push_back(nd); + // Attach the newly created node to the scene-graph for (pugi::xml_node child : node.children()) { if (!ASSIMP_stricmp(child.name(), "node")) { // Is a child node Node *childNd = ParseNode(child, batch); // Repeat this function for all children @@ -1206,6 +1195,7 @@ IRRImporter::Node *IRRImporter::ParseNode(pugi::xml_node &node, BatchLoader &bat }; } + // Return fully specified node return nd; } @@ -1223,17 +1213,9 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy if (!st.parse(file.get())) { throw DeadlyImportError("XML parse error while loading IRR file ", pFile); } - pugi::xml_node rootElement = st.getRootNode(); + pugi::xml_node documentRoot = st.getRootNode(); - std::stringstream ss; - ss << "Document name: " << rootElement.name() << std::endl; - ss << "Document content: " << std::endl; - rootElement.print(ss); - ss << std::endl; - std::cout << "IrrImporter with"; - std::cout << ss.str() << std::endl; // The root node of the scene - // TODO: Appearantly root node is specified somewhere? Node *root = new Node(Node::DUMMY); root->parent = nullptr; root->name = ""; @@ -1250,18 +1232,13 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy this->guessedMatCnt = 0; // Parse the XML - // First node is the xml header. Awkwardly skip to sibling's children - // I don't like recursion - // TODO clean up - std::vector nextNodes; - for (auto &node : rootElement.children().begin()->next_sibling().children()) { - nextNodes.push_back(node); // Find second node, , and push it's children to queue - } - for (pugi::xml_node &child : nextNodes) { - if (child.type() != pugi::node_element) continue; // Only semantically valuable nodes + // Find the scene root from document root. + const pugi::xml_node &sceneRoot = documentRoot.child("irr_scene"); + if (!sceneRoot) throw new DeadlyImportError("IRR: not found in file"); + for (pugi::xml_node &child : sceneRoot.children()) { // XML elements are either nodes, animators, attributes, or materials if (!ASSIMP_stricmp(child.name(), "node")) { - // Recursive ollect subtree children + // Recursive collect subtree children Node *nd = ParseNode(child, batch); // Attach to root root->children.push_back(nd); diff --git a/code/AssetLib/Irr/IRRMeshLoader.cpp b/code/AssetLib/Irr/IRRMeshLoader.cpp index 88c4f6346..8161a2997 100644 --- a/code/AssetLib/Irr/IRRMeshLoader.cpp +++ b/code/AssetLib/Irr/IRRMeshLoader.cpp @@ -157,8 +157,8 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile, */ // Parse the XML file - // FIXME get not root - for (pugi::xml_node bufferNode : root.children()) { + pugi::xml_node const &meshNode = root.child("mesh"); + for (pugi::xml_node bufferNode : meshNode.children()) { if (ASSIMP_stricmp(bufferNode.name(), "buffer")) { // Might be a useless warning ASSIMP_LOG_WARN("IRRMESH: Ignoring non buffer node <", bufferNode.name(), "> in mesh declaration"); diff --git a/code/AssetLib/Irr/IRRShared.h b/code/AssetLib/Irr/IRRShared.h index a8997451d..c04d20e52 100644 --- a/code/AssetLib/Irr/IRRShared.h +++ b/code/AssetLib/Irr/IRRShared.h @@ -92,7 +92,7 @@ protected: // ------------------------------------------------------------------- /** Read a property of the specified type from the current XML element. * @param out Receives output data - * @param node XML attribute element data + * @param node XML attribute element containing data */ void ReadHexProperty(HexProperty &out, pugi::xml_node& hexnode); void ReadStringProperty(StringProperty &out, pugi::xml_node& stringnode); From 96eb87265e4510c055f13c6ce169f2a181082694 Mon Sep 17 00:00:00 2001 From: PencilAmazing <16854231+PencilAmazing@users.noreply.github.com> Date: Sun, 2 Jul 2023 14:35:22 -0400 Subject: [PATCH 10/12] Apply clang-format to unit test --- .../ImportExport/IRR/utIrrImportExport.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/unit/ImportExport/IRR/utIrrImportExport.cpp b/test/unit/ImportExport/IRR/utIrrImportExport.cpp index eaa4c8f4c..a7572c362 100644 --- a/test/unit/ImportExport/IRR/utIrrImportExport.cpp +++ b/test/unit/ImportExport/IRR/utIrrImportExport.cpp @@ -48,19 +48,19 @@ using namespace Assimp; class utIrrImportExport : public AbstractImportExportBase { public: - virtual bool importerTest() { - Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/IRR/box.irr", aiProcess_ValidateDataStructure); - return nullptr != scene; - } + virtual bool importerTest() { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/IRR/box.irr", aiProcess_ValidateDataStructure); + return nullptr != scene; + } }; TEST_F(utIrrImportExport, importSimpleIrrTest) { - EXPECT_TRUE(importerTest()); + EXPECT_TRUE(importerTest()); } TEST_F(utIrrImportExport, importSGIrrTest) { - Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/IRR/dawfInCellar_SameHierarchy.irr", aiProcess_ValidateDataStructure); - EXPECT_NE( nullptr,scene); + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/IRR/dawfInCellar_SameHierarchy.irr", aiProcess_ValidateDataStructure); + EXPECT_NE(nullptr, scene); } From 57306bcd63f2f134bd77c83e41d7c4d340f8be45 Mon Sep 17 00:00:00 2001 From: PencilAmazing <16854231+PencilAmazing@users.noreply.github.com> Date: Sun, 2 Jul 2023 14:55:25 -0400 Subject: [PATCH 11/12] Make unit test more useful in case importer breaks again --- test/unit/ImportExport/IRR/utIrrImportExport.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/unit/ImportExport/IRR/utIrrImportExport.cpp b/test/unit/ImportExport/IRR/utIrrImportExport.cpp index a7572c362..0d0cfa17b 100644 --- a/test/unit/ImportExport/IRR/utIrrImportExport.cpp +++ b/test/unit/ImportExport/IRR/utIrrImportExport.cpp @@ -51,7 +51,8 @@ public: virtual bool importerTest() { Assimp::Importer importer; const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/IRR/box.irr", aiProcess_ValidateDataStructure); - return nullptr != scene; + // Only one box thus only one mesh + return nullptr != scene && scene->mNumMeshes == 1; } }; @@ -63,4 +64,7 @@ TEST_F(utIrrImportExport, importSGIrrTest) { Assimp::Importer importer; const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/IRR/dawfInCellar_SameHierarchy.irr", aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); + EXPECT_EQ(scene->mNumMeshes, 2); + EXPECT_EQ(scene->mNumMaterials, 2); + EXPECT_GT(scene->mMeshes[0]->mNumVertices, 0); } From d1ad4e54d70751ecc69b153ea09c314ed2a8df31 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 3 Jul 2023 08:26:50 +0200 Subject: [PATCH 12/12] Create licence.md - closes https://github.com/assimp/assimp/issues/5144 --- test/models/Collada/licence.md | 73 ++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 test/models/Collada/licence.md diff --git a/test/models/Collada/licence.md b/test/models/Collada/licence.md new file mode 100644 index 000000000..bcdb7c0fc --- /dev/null +++ b/test/models/Collada/licence.md @@ -0,0 +1,73 @@ +SCEA Shared Source License 1.0 + +Terms and Conditions: + +1. Definitions: +"Software" shall mean the software and related documentation, whether in Source or Object Form, made available under this SCEA Shared Source license ("License"), +that is indicated by a copyright notice file included in the source files or attached or accompanying the source files. + +"Licensor" shall mean Sony Computer Entertainment America, Inc. (herein "SCEA") + +"Object Code" or "Object Form" shall mean any form that results from translation or transformation of Source Code, including but not limited to compiled object code +or conversions to other forms intended for machine execution. "Source Code" or "Source Form" shall have the plain meaning generally accepted in the software industry, +including but not limited to software source code, documentation source, header and configuration files. + +"You" or "Your" shall mean you as an individual or as a company, or whichever form under which you are exercising rights under this License. + +2. License Grant. +Licensor hereby grants to You, free of charge subject to the terms and conditions of this License, an irrevocable, non-exclusive, worldwide, perpetual, and royalty-free +license to use, modify, reproduce, distribute, publicly perform or display the Software in Object or Source Form . + +4. No Right to File for Patent. +In exchange for the rights that are granted to You free of charge under this License, You agree that You will not file for any patent application, seek copyright + protection or take any other action that might otherwise impair the ownership rights in and to the Software that may belong to SCEA or any of the other contributors/authors +of the Software. + +6. Contributions. +SCEA welcomes contributions in form of modifications, optimizations, tools or documentation designed to improve or expand the performance and scope of the Software +(collectively "Contributions"). Per the terms of this License You are free to modify the Software and those modifications would belong to You. You may however wish to +donate Your Contributions to SCEA for consideration for inclusion into the Software. For the avoidance of doubt, if You elect to send Your Contributions to SCEA, You are +doing so voluntarily and are giving the Contributions to SCEA and its parent company Sony Computer Entertainment, Inc., free of charge, to use, modify or distribute in +any form or in any manner. SCEA acknowledges that if You make a donation of Your Contributions to SCEA, such Contributions shall not exclusively belong to SCEA or its +parent company and such donation shall not be to Your exclusion. SCEA, in its sole discretion, shall determine whether or not to include Your donated Contributions into +the Software, in whole, in part, or as modified by SCEA. Should SCEA elect to include any such Contributions into the Software, it shall do so at its own risk and may elect +to give credit or special thanks to any such contributors in the attached copyright notice. However, if any of Your contributions are included into the Software, they will + become part of the Software and will be distributed under the terms and conditions of this License. Further, if Your donated Contributions are integrated into the Software + then Sony Computer Entertainment, Inc. shall become the copyright owner of the Software now containing Your contributions and SCEA would be the Licensor. + +8. Redistribution in Source Form +You may redistribute copies of the Software, modifications or derivatives thereof in Source Code Form, provided that You: + +a. Include a copy of this License and any copyright notices with source +b. Identify modifications if any were made to the Software +c. Include a copy of all documentation accompanying the Software and modifications made by You +6. Redistribution in Object Form +If You redistribute copies of the Software, modifications or derivatives thereof in Object Form only (as incorporated into finished goods, i.e. end user applications) then +You will not have a duty to include any copies of the code, this License, copyright notices, other attributions or documentation. + +7. No Warranty +THE SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT ANY REPRESENTATIONS OR WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY +WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. YOU ARE SOLELY RESPONSIBLE FOR DETERMINING THE APPROPRIATENESS OF USING, +MODIFYING OR REDISTRIBUTING THE SOFTWARE AND ASSUME ANY RISKS ASSOCIATED WITH YOUR EXERCISE OF PERMISSIONS UNDER THIS LICENSE. + +9. Limitation of Liability +UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY WILL EITHER PARTY BE LIABLE TO THE OTHER PARTY OR ANY THIRD PARTY FOR ANY DIRECT, INDIRECT, CONSEQUENTIAL, SPECIAL, + INCIDENTAL, OR EXEMPLARY DAMAGES WITH RESPECT TO ANY INJURY, LOSS, OR DAMAGE, ARISING UNDER OR IN CONNECTION WITH THIS LETTER AGREEMENT, WHETHER FORESEEABLE OR +UNFORESEEABLE, EVEN IF SUCH PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH INJURY, LOSS, OR DAMAGE. THE LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION SHALL + APPLY TO THE FULLEST EXTENT PERMISSIBLE AT LAW OR ANY GOVERMENTAL REGULATIONS. + +11. Governing Law and Consent to Jurisdiction +This Agreement shall be governed by and interpreted in accordance with the laws of the State of California, excluding that body of law related to choice of laws, +and of the United States of America. Any action or proceeding brought to enforce the terms of this Agreement or to adjudicate any dispute arising hereunder shall +be brought in the Superior Court of the County of San Mateo, State of California or the United States District Court for the Northern District of California. Each +of the parties hereby submits itself to the exclusive jurisdiction and venue of such courts for purposes of any such action. In addition, each party hereby waives the +right to a jury trial in any action or proceeding related to this Agreement. + +13. Copyright Notice for Redistribution of Source Code +Copyright 2005 Sony Computer Entertainment Inc. + +Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: +http://research.scea.com/scea_shared_source_license.html + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.