From 59b08198666da731c80458322dc7b84577aab166 Mon Sep 17 00:00:00 2001 From: Andreas Henne Date: Thu, 19 Mar 2015 17:27:06 +0100 Subject: [PATCH 01/25] STL loader can now handle more than one mesh in a single ascii file. --- code/STLLoader.cpp | 308 ++++++++++++++++++++++++--------------------- 1 file changed, 168 insertions(+), 140 deletions(-) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index 987de89d0..8f2a4e18e 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -131,6 +131,19 @@ const aiImporterDesc* STLImporter::GetInfo () const return &desc; } +void addFacesToMesh(aiMesh* pMesh) +{ + pMesh->mFaces = new aiFace[pMesh->mNumFaces]; + for (unsigned int i = 0, p = 0; i < pMesh->mNumFaces;++i) { + + aiFace& face = pMesh->mFaces[i]; + face.mIndices = new unsigned int[face.mNumIndices = 3]; + for (unsigned int o = 0; o < 3;++o,++p) { + face.mIndices[o] = p; + } + } +} + // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void STLImporter::InternReadFile( const std::string& pFile, @@ -156,17 +169,8 @@ void STLImporter::InternReadFile( const std::string& pFile, // the default vertex color is light gray. clrColorDefault.r = clrColorDefault.g = clrColorDefault.b = clrColorDefault.a = 0.6f; - // allocate one mesh - pScene->mNumMeshes = 1; - pScene->mMeshes = new aiMesh*[1]; - aiMesh* pMesh = pScene->mMeshes[0] = new aiMesh(); - pMesh->mMaterialIndex = 0; - - // allocate a single node - pScene->mRootNode = new aiNode(); - pScene->mRootNode->mNumMeshes = 1; - pScene->mRootNode->mMeshes = new unsigned int[1]; - pScene->mRootNode->mMeshes[0] = 0; + // allocate a single node + pScene->mRootNode = new aiNode(); bool bMatClr = false; @@ -178,16 +182,12 @@ void STLImporter::InternReadFile( const std::string& pFile, throw DeadlyImportError( "Failed to determine STL storage representation for " + pFile + "."); } - // now copy faces - pMesh->mFaces = new aiFace[pMesh->mNumFaces]; - for (unsigned int i = 0, p = 0; i < pMesh->mNumFaces;++i) { - - aiFace& face = pMesh->mFaces[i]; - face.mIndices = new unsigned int[face.mNumIndices = 3]; - for (unsigned int o = 0; o < 3;++o,++p) { - face.mIndices[o] = p; - } - } + // add all created meshes to the single node + pScene->mRootNode = new aiNode(); + pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; + pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; + for (uint i = 0; i < pScene->mNumMeshes; i++) + pScene->mRootNode->mMeshes[i] = i; // create a single default material, using a light gray diffuse color for consistency with // other geometric types (e.g., PLY). @@ -213,140 +213,165 @@ void STLImporter::InternReadFile( const std::string& pFile, // Read an ASCII STL file void STLImporter::LoadASCIIFile() { - aiMesh* pMesh = pScene->mMeshes[0]; + std::vector meshes; + const char* sz = mBuffer; + const char* bufferEnd = mBuffer + fileSize; + std::vector positionBuffer; + std::vector normalBuffer; - const char* sz = mBuffer; - SkipSpaces(&sz); - ai_assert(!IsLineEnd(sz)); + // try to guess how many vertices we could have + // assume we'll need 160 bytes for each face + size_t sizeEstimate = std::max(1u, fileSize / 160u ) * 3; + positionBuffer.reserve(sizeEstimate); + normalBuffer.reserve(sizeEstimate); - sz += 5; // skip the "solid" - SkipSpaces(&sz); - const char* szMe = sz; - while (!::IsSpaceOrNewLine(*sz)) { - sz++; - } + while (IsAsciiSTL(sz, bufferEnd - sz)) + { + aiMesh* pMesh = new aiMesh(); + pMesh->mMaterialIndex = 0; + meshes.push_back(pMesh); - size_t temp; - // setup the name of the node - if ((temp = (size_t)(sz-szMe))) { - if (temp >= MAXLEN) { - throw DeadlyImportError( "STL: Node name too long" ); - } + SkipSpaces(&sz); + ai_assert(!IsLineEnd(sz)); - pScene->mRootNode->mName.length = temp; - memcpy(pScene->mRootNode->mName.data,szMe,temp); - pScene->mRootNode->mName.data[temp] = '\0'; - } - else pScene->mRootNode->mName.Set(""); + sz += 5; // skip the "solid" + SkipSpaces(&sz); + const char* szMe = sz; + while (!::IsSpaceOrNewLine(*sz)) { + sz++; + } - // try to guess how many vertices we could have - // assume we'll need 160 bytes for each face - pMesh->mNumVertices = ( pMesh->mNumFaces = std::max(1u,fileSize / 160u )) * 3; - pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; - pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; - - unsigned int curFace = 0, curVertex = 3; - for ( ;; ) - { - // go to the next token - if(!SkipSpacesAndLineEnd(&sz)) - { - // seems we're finished although there was no end marker - DefaultLogger::get()->warn("STL: unexpected EOF. \'endsolid\' keyword was expected"); - break; - } - // facet normal -0.13 -0.13 -0.98 - if (!strncmp(sz,"facet",5) && IsSpaceOrNewLine(*(sz+5))) { + size_t temp; + // setup the name of the node + if ((temp = (size_t)(sz-szMe))) { + if (temp >= MAXLEN) { + throw DeadlyImportError( "STL: Node name too long" ); + } - if (3 != curVertex) { - DefaultLogger::get()->warn("STL: A new facet begins but the old is not yet complete"); - } - if (pMesh->mNumFaces == curFace) { - ai_assert(pMesh->mNumFaces != 0); + pScene->mRootNode->mName.length = temp; + memcpy(pScene->mRootNode->mName.data,szMe,temp); + pScene->mRootNode->mName.data[temp] = '\0'; + } + else pScene->mRootNode->mName.Set(""); - // need to resize the arrays, our size estimate was wrong - unsigned int iNeededSize = (unsigned int)(sz-mBuffer) / pMesh->mNumFaces; - if (iNeededSize <= 160)iNeededSize >>= 1; // prevent endless looping - unsigned int add = (unsigned int)((mBuffer+fileSize)-sz) / iNeededSize; - add += add >> 3; // add 12.5% as buffer - iNeededSize = (pMesh->mNumFaces + add)*3; - aiVector3D* pv = new aiVector3D[iNeededSize]; - memcpy(pv,pMesh->mVertices,pMesh->mNumVertices*sizeof(aiVector3D)); - delete[] pMesh->mVertices; - pMesh->mVertices = pv; - pv = new aiVector3D[iNeededSize]; - memcpy(pv,pMesh->mNormals,pMesh->mNumVertices*sizeof(aiVector3D)); - delete[] pMesh->mNormals; - pMesh->mNormals = pv; + uint faceVertexCounter = 0; + for ( ;; ) + { + // go to the next token + if(!SkipSpacesAndLineEnd(&sz)) + { + // seems we're finished although there was no end marker + DefaultLogger::get()->warn("STL: unexpected EOF. \'endsolid\' keyword was expected"); + break; + } + // facet normal -0.13 -0.13 -0.98 + if (!strncmp(sz,"facet",5) && IsSpaceOrNewLine(*(sz+5))) { - pMesh->mNumVertices = iNeededSize; - pMesh->mNumFaces += add; - } - aiVector3D* vn = &pMesh->mNormals[curFace++*3]; + if (faceVertexCounter != 3) { + DefaultLogger::get()->warn("STL: A new facet begins but the old is not yet complete"); + } + faceVertexCounter = 0; + normalBuffer.push_back(aiVector3D()); + aiVector3D* vn = &normalBuffer.back(); - sz += 6; - curVertex = 0; - SkipSpaces(&sz); - if (strncmp(sz,"normal",6)) { - DefaultLogger::get()->warn("STL: a facet normal vector was expected but not found"); - } - else - { - sz += 7; - SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float&)vn->x ); - SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float&)vn->y ); - SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float&)vn->z ); - *(vn+1) = *vn; - *(vn+2) = *vn; - } - } - // vertex 1.50000 1.50000 0.00000 - else if (!strncmp(sz,"vertex",6) && ::IsSpaceOrNewLine(*(sz+6))) - { - if (3 == curVertex) { - DefaultLogger::get()->error("STL: a facet with more than 3 vertices has been found"); - ++sz; - } - else - { - sz += 7; - SkipSpaces(&sz); - aiVector3D* vn = &pMesh->mVertices[(curFace-1)*3 + curVertex++]; - sz = fast_atoreal_move(sz, (float&)vn->x ); - SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float&)vn->y ); - SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float&)vn->z ); - } - } - else if (!::strncmp(sz,"endsolid",8)) { - // finished! - break; - } - // else skip the whole identifier - else { - do { - ++sz; - } while (!::IsSpaceOrNewLine(*sz)); - } - } + sz += 6; + SkipSpaces(&sz); + if (strncmp(sz,"normal",6)) { + DefaultLogger::get()->warn("STL: a facet normal vector was expected but not found"); + } + else + { + sz += 7; + SkipSpaces(&sz); + sz = fast_atoreal_move(sz, (float&)vn->x ); + SkipSpaces(&sz); + sz = fast_atoreal_move(sz, (float&)vn->y ); + SkipSpaces(&sz); + sz = fast_atoreal_move(sz, (float&)vn->z ); + normalBuffer.push_back(*vn); + normalBuffer.push_back(*vn); + } + } + // vertex 1.50000 1.50000 0.00000 + else if (!strncmp(sz,"vertex",6) && ::IsSpaceOrNewLine(*(sz+6))) + { + if (faceVertexCounter >= 3) { + DefaultLogger::get()->error("STL: a facet with more than 3 vertices has been found"); + ++sz; + } + else + { + sz += 7; + SkipSpaces(&sz); + positionBuffer.push_back(aiVector3D()); + aiVector3D* vn = &positionBuffer.back(); + sz = fast_atoreal_move(sz, (float&)vn->x ); + SkipSpaces(&sz); + sz = fast_atoreal_move(sz, (float&)vn->y ); + SkipSpaces(&sz); + sz = fast_atoreal_move(sz, (float&)vn->z ); + faceVertexCounter++; + } + } + else if (!::strncmp(sz,"endsolid",8)) { + do { + ++sz; + } while (!::IsLineEnd(*sz)); + SkipSpacesAndLineEnd(&sz); + // finished! + break; + } + // else skip the whole identifier + else { + do { + ++sz; + } while (!::IsSpaceOrNewLine(*sz)); + } + } - if (!curFace) { - pMesh->mNumFaces = 0; - throw DeadlyImportError("STL: ASCII file is empty or invalid; no data loaded"); - } - pMesh->mNumFaces = curFace; - pMesh->mNumVertices = curFace*3; - // we are finished! + if (positionBuffer.empty()) { + pMesh->mNumFaces = 0; + throw DeadlyImportError("STL: ASCII file is empty or invalid; no data loaded"); + } + if (positionBuffer.size() % 3 != 0) { + pMesh->mNumFaces = 0; + throw DeadlyImportError("STL: Invalid number of vertices"); + } + if (normalBuffer.size() != positionBuffer.size()) { + pMesh->mNumFaces = 0; + throw DeadlyImportError("Normal buffer size does not match position buffer size"); + } + pMesh->mNumFaces = positionBuffer.size() / 3; + pMesh->mNumVertices = positionBuffer.size(); + pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; + memcpy(pMesh->mVertices, &positionBuffer[0].x, pMesh->mNumVertices * sizeof(aiVector3D)); + positionBuffer.clear(); + pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + memcpy(pMesh->mNormals, &normalBuffer[0].x, pMesh->mNumVertices * sizeof(aiVector3D)); + normalBuffer.clear(); + + // now copy faces + addFacesToMesh(pMesh); + } + // now add the loaded meshes + pScene->mNumMeshes = (unsigned int)meshes.size(); + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + for (size_t i = 0; i < meshes.size(); i++) + { + pScene->mMeshes[i] = meshes[i]; + } } // ------------------------------------------------------------------------------------------------ // Read a binary STL file bool STLImporter::LoadBinaryFile() { + // allocate one mesh + pScene->mNumMeshes = 1; + pScene->mMeshes = new aiMesh*[1]; + aiMesh* pMesh = pScene->mMeshes[0] = new aiMesh(); + pMesh->mMaterialIndex = 0; + // skip the first 80 bytes if (fileSize < 84) { throw DeadlyImportError("STL: file is too small for the header"); @@ -374,7 +399,6 @@ bool STLImporter::LoadBinaryFile() const unsigned char* sz = (const unsigned char*)mBuffer + 80; // now read the number of facets - aiMesh* pMesh = pScene->mMeshes[0]; pScene->mRootNode->mName.Set(""); pMesh->mNumFaces = *((uint32_t*)sz); @@ -447,6 +471,10 @@ bool STLImporter::LoadBinaryFile() *(clr+2) = *clr; } } + + // now copy faces + addFacesToMesh(pMesh); + if (bIsMaterialise && !pMesh->mColors[0]) { // use the color as diffuse material color From 816ceeda6915e025cb11af959e0652b015a1c930 Mon Sep 17 00:00:00 2001 From: Andreas Henne Date: Fri, 20 Mar 2015 14:18:26 +0100 Subject: [PATCH 02/25] Eliminated error in MSVC. --- code/STLLoader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index 8f2a4e18e..6b9df9d91 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -186,7 +186,7 @@ void STLImporter::InternReadFile( const std::string& pFile, pScene->mRootNode = new aiNode(); pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; - for (uint i = 0; i < pScene->mNumMeshes; i++) + for (unsigned int i = 0; i < pScene->mNumMeshes; i++) pScene->mRootNode->mMeshes[i] = i; // create a single default material, using a light gray diffuse color for consistency with @@ -254,7 +254,7 @@ void STLImporter::LoadASCIIFile() } else pScene->mRootNode->mName.Set(""); - uint faceVertexCounter = 0; + unsigned int faceVertexCounter = 0; for ( ;; ) { // go to the next token From fb9e4d126673adf722dc00924dd1dd51487fadc3 Mon Sep 17 00:00:00 2001 From: Andreas Henne Date: Thu, 6 Aug 2015 13:30:49 +0200 Subject: [PATCH 03/25] Ply export now uses uchar as type for the number of vertices per polygon and int as vertex index type. --- code/PlyExporter.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/code/PlyExporter.cpp b/code/PlyExporter.cpp index 9268794f0..f8ff34fc2 100644 --- a/code/PlyExporter.cpp +++ b/code/PlyExporter.cpp @@ -189,7 +189,12 @@ PlyExporter::PlyExporter(const char* _filename, const aiScene* pScene, bool bina } mOutput << "element face " << faces << endl; - mOutput << "property list uint uint vertex_index" << endl; + + // uchar seems to be the most common type for the number of indices per polygon and int seems to be most common for the vertex indices. + // For instance, MeshLab fails to load meshes in which both types are uint. Houdini seems to have problems as well. + // Obviously, using unchar will not work for meshes with polygons with more than 255 indices, but how realistic is this case? + mOutput << "property list uchar int vertex_index" << endl; + mOutput << "end_header" << endl; for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { @@ -342,16 +347,24 @@ void PlyExporter::WriteMeshIndices(const aiMesh* m, unsigned int offset) } } -void PlyExporter::WriteMeshIndicesBinary(const aiMesh* m, unsigned int offset) +// Generic method in case we want to use different data types for the indices or make this configurable. +template +void WriteMeshIndicesBinary_Generic(const aiMesh* m, unsigned int offset, std::ostringstream& output) { for (unsigned int i = 0; i < m->mNumFaces; ++i) { const aiFace& f = m->mFaces[i]; - mOutput.write(reinterpret_cast(&f.mNumIndices), 4); + NumIndicesType numIndices = static_cast(f.mNumIndices); + output.write(reinterpret_cast(&numIndices), sizeof(NumIndicesType)); for (unsigned int c = 0; c < f.mNumIndices; ++c) { - unsigned int index = f.mIndices[c] + offset; - mOutput.write(reinterpret_cast(&index), 4); + IndexType index = f.mIndices[c] + offset; + output.write(reinterpret_cast(&index), sizeof(IndexType)); } } } +void PlyExporter::WriteMeshIndicesBinary(const aiMesh* m, unsigned int offset) +{ + WriteMeshIndicesBinary_Generic(m, offset, mOutput); +} + #endif From 5030fe8c7e7479f83b58e92f83b5604986b00baf Mon Sep 17 00:00:00 2001 From: Andreas Henne Date: Thu, 6 Aug 2015 13:37:16 +0200 Subject: [PATCH 04/25] Formatting changes. --- code/STLLoader.cpp | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index 79ed37c66..df15bdbfa 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -3,12 +3,12 @@ Open Asset Import Library (assimp) --------------------------------------------------------------------------- -Copyright (c) 2006-2012, assimp team +Copyright (c) 2006-2015, assimp team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above @@ -25,16 +25,16 @@ conditions are met: derived from this software without specific prior written permission of the assimp team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ @@ -53,6 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../include/assimp/scene.h" #include "../include/assimp/DefaultLogger.hpp" + using namespace Assimp; namespace { @@ -111,19 +112,19 @@ STLImporter::STLImporter() {} // ------------------------------------------------------------------------------------------------ -// Destructor, private as well +// Destructor, private as well STLImporter::~STLImporter() {} // ------------------------------------------------------------------------------------------------ -// Returns whether the class can handle the format of the given file. +// Returns whether the class can handle the format of the given file. bool STLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { const std::string extension = GetExtension(pFile); if (extension == "stl") return true; - else if (!extension.length() || checkSig) { + else if (!extension.length() || checkSig) { if (!pIOHandler) return true; const char* tokens[] = {"STL","solid"}; @@ -152,14 +153,14 @@ void addFacesToMesh(aiMesh* pMesh) } // ------------------------------------------------------------------------------------------------ -// Imports the given file into the given scene structure. -void STLImporter::InternReadFile( const std::string& pFile, +// Imports the given file into the given scene structure. +void STLImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { boost::scoped_ptr file( pIOHandler->Open( pFile, "rb")); // Check whether we can read from the file - if( file.get() == NULL) { + if( file.get() == NULL) { throw DeadlyImportError( "Failed to open STL file " + pFile + "."); } @@ -388,7 +389,7 @@ bool STLImporter::LoadBinaryFile() // search for an occurence of "COLOR=" in the header const unsigned char* sz2 = (const unsigned char*)mBuffer; const unsigned char* const szEnd = sz2+80; - while (sz2 < szEnd) { + while (sz2 < szEnd) { if ('C' == *sz2++ && 'O' == *sz2++ && 'L' == *sz2++ && 'O' == *sz2++ && 'R' == *sz2++ && '=' == *sz2++) { @@ -425,7 +426,7 @@ bool STLImporter::LoadBinaryFile() vp = pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; vn = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; - for (unsigned int i = 0; i < pMesh->mNumFaces;++i) { + for (unsigned int i = 0; i < pMesh->mNumFaces;++i) { // NOTE: Blender sometimes writes empty normals ... this is not // our fault ... the RemoveInvalidData helper step should fix that From 295a9abbdcfb86e77cf6afcd1e7f268a7f25670c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 10 Aug 2015 13:13:01 +0200 Subject: [PATCH 05/25] Update CMakeLists.txt Fix for https://github.com/assimp/assimp/issues/166 --- code/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index c3302ee98..351ce8da2 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -656,6 +656,11 @@ if ( MSVC ) ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS ) endif ( MSVC ) +if (APPLE) + SET_TARGET_PROPERTIES( assimp PROPERTIES + INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}" + ) +endif() if (UNZIP_FOUND) SET (unzip_compile_SRCS "") else (UNZIP_FOUND) From b39446b7bbd357f9d78c6e759d3af1b16cba540f Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Mon, 10 Aug 2015 15:48:26 +0300 Subject: [PATCH 06/25] Avoid reading past EOF when encountering a malformed STL file Since IsSpaceOrNewLine returns true on '\0' we might try to read past end of buffer on line 310. Add explicit check to avoid this. --- code/STLLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index ad445efdb..de70dd7a3 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -279,7 +279,7 @@ void STLImporter::LoadASCIIFile() break; } // facet normal -0.13 -0.13 -0.98 - if (!strncmp(sz,"facet",5) && IsSpaceOrNewLine(*(sz+5))) { + if (!strncmp(sz,"facet",5) && IsSpaceOrNewLine(*(sz+5)) && *(sz + 5) != '\0') { if (3 != curVertex) { DefaultLogger::get()->warn("STL: A new facet begins but the old is not yet complete"); From bcf3f985fb06ca1fff76e3df4c7c1ac0fdf6cb43 Mon Sep 17 00:00:00 2001 From: Andreas Henne Date: Thu, 13 Aug 2015 13:22:21 +0200 Subject: [PATCH 07/25] Fixed spelling error. --- code/PlyExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/PlyExporter.cpp b/code/PlyExporter.cpp index f8ff34fc2..0a23e4b75 100644 --- a/code/PlyExporter.cpp +++ b/code/PlyExporter.cpp @@ -192,7 +192,7 @@ PlyExporter::PlyExporter(const char* _filename, const aiScene* pScene, bool bina // uchar seems to be the most common type for the number of indices per polygon and int seems to be most common for the vertex indices. // For instance, MeshLab fails to load meshes in which both types are uint. Houdini seems to have problems as well. - // Obviously, using unchar will not work for meshes with polygons with more than 255 indices, but how realistic is this case? + // Obviously, using uchar will not work for meshes with polygons with more than 255 indices, but how realistic is this case? mOutput << "property list uchar int vertex_index" << endl; mOutput << "end_header" << endl; From 4cc716a0f5198f9ad9e32d79de14db535ae51c12 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 11 Aug 2015 14:32:26 +0300 Subject: [PATCH 08/25] MDL: Fix read past end of buffer with malformed input --- code/MDLLoader.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/MDLLoader.cpp b/code/MDLLoader.cpp index 60c92661b..c5b8d63c3 100644 --- a/code/MDLLoader.cpp +++ b/code/MDLLoader.cpp @@ -355,6 +355,9 @@ void MDLImporter::InternReadFile_Quake1( ) for (unsigned int i = 0; i < (unsigned int)pcHeader->num_skins;++i) { union{BE_NCONST MDL::Skin* pcSkin;BE_NCONST MDL::GroupSkin* pcGroupSkin;}; + if (szCurrent + sizeof(MDL::Skin) > this->mBuffer + this->iFileSize) { + throw DeadlyImportError("[Quake 1 MDL] Unexpected EOF"); + } pcSkin = (BE_NCONST MDL::Skin*)szCurrent; AI_SWAP4( pcSkin->group ); From d185cea81c669cc0a2330c97a54a669892867599 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 11 Aug 2015 15:44:51 +0300 Subject: [PATCH 09/25] AC3D: Fix read past end of buffer --- code/ACLoader.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/ACLoader.cpp b/code/ACLoader.cpp index dac85b8db..642fd5d24 100644 --- a/code/ACLoader.cpp +++ b/code/ACLoader.cpp @@ -89,6 +89,9 @@ static const aiImporterDesc desc = { // ------------------------------------------------------------------------------------------------ // read a string (may be enclosed in double quotation marks). buffer must point to " #define AI_AC_GET_STRING(out) \ + if (*buffer == '\0') { \ + throw DeadlyImportError("AC3D: Unexpected EOF in string"); \ + } \ ++buffer; \ const char* sz = buffer; \ while ('\"' != *buffer) \ From e5ddb98dde79530d3bacd83c806d36924d2accf8 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 11 Aug 2015 15:53:16 +0300 Subject: [PATCH 10/25] STL: Fix another read past EOF --- code/STLLoader.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index 1bf47dcf2..fc6ca0661 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -304,6 +304,9 @@ void STLImporter::LoadASCIIFile() } else { + if (sz[6] == '\0') { + throw DeadlyImportError("STL: unexpected EOF while parsing facet"); + } sz += 7; SkipSpaces(&sz); sz = fast_atoreal_move(sz, (float&)vn->x ); @@ -324,6 +327,9 @@ void STLImporter::LoadASCIIFile() } else { + if (sz[6] == '\0') { + throw DeadlyImportError("STL: unexpected EOF while parsing facet"); + } sz += 7; SkipSpaces(&sz); positionBuffer.push_back(aiVector3D()); From 5575a54466ea54902c837b9846b44a1c89d777fa Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 11 Aug 2015 16:09:19 +0300 Subject: [PATCH 11/25] Add various checks to avoid either too large or zero-sized memory allocations --- code/ACLoader.cpp | 10 ++++++++++ code/MD3Loader.cpp | 5 +++++ code/ObjFileImporter.cpp | 5 +++++ 3 files changed, 20 insertions(+) diff --git a/code/ACLoader.cpp b/code/ACLoader.cpp index 642fd5d24..8d2fac171 100644 --- a/code/ACLoader.cpp +++ b/code/ACLoader.cpp @@ -587,9 +587,19 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object, // allocate storage for vertices and normals mesh->mNumFaces = (*cit).first; + if (mesh->mNumFaces == 0) { + throw DeadlyImportError("AC3D: No faces"); + } else if (mesh->mNumFaces > std::numeric_limits::max() / sizeof(aiFace)) { + throw DeadlyImportError("AC3D: Too many faces, would run out of memory"); + } aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces]; mesh->mNumVertices = (*cit).second; + if (mesh->mNumVertices == 0) { + throw DeadlyImportError("AC3D: No vertices"); + } else if (mesh->mNumVertices > std::numeric_limits::max() / sizeof(aiVector3D)) { + throw DeadlyImportError("AC3D: Too many vertices, would run out of memory"); + } aiVector3D* vertices = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; unsigned int cur = 0; diff --git a/code/MD3Loader.cpp b/code/MD3Loader.cpp index f7fafefdd..5e9a1a3dc 100644 --- a/code/MD3Loader.cpp +++ b/code/MD3Loader.cpp @@ -783,6 +783,11 @@ void MD3Importer::InternReadFile( const std::string& pFile, // Allocate output storage pScene->mNumMeshes = pcHeader->NUM_SURFACES; + if (pcHeader->NUM_SURFACES == 0) { + throw DeadlyImportError("MD3: No surfaces"); + } else if (pcHeader->NUM_SURFACES > std::numeric_limits::max() / sizeof(aiMesh)) { + throw DeadlyImportError("MD3: Too many surfaces, would run out of memory"); + } pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; pScene->mNumMaterials = pcHeader->NUM_SURFACES; diff --git a/code/ObjFileImporter.cpp b/code/ObjFileImporter.cpp index 02232fcd1..7ade10f62 100644 --- a/code/ObjFileImporter.cpp +++ b/code/ObjFileImporter.cpp @@ -380,6 +380,11 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel, // Copy vertices of this mesh instance pMesh->mNumVertices = numIndices; + if (pMesh->mNumVertices == 0) { + throw DeadlyImportError( "OBJ: no vertices" ); + } else if (pMesh->mNumVertices > std::numeric_limits::max() / sizeof(aiVector3D)) { + throw DeadlyImportError( "OBJ: Too many vertices, would run out of memory" ); + } pMesh->mVertices = new aiVector3D[ pMesh->mNumVertices ]; // Allocate buffer for normal vectors From 0b0ba2ec4dd161bfe7a16e6d5133c7e285c5a242 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 13 Aug 2015 13:01:49 +0300 Subject: [PATCH 12/25] Refactor logic which checks for too large allocations It's now easier to change the limit --- code/ACLoader.cpp | 6 +++--- code/MD3Loader.cpp | 4 +++- code/ObjFileImporter.cpp | 2 +- include/assimp/defs.h | 8 ++++++++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/code/ACLoader.cpp b/code/ACLoader.cpp index 8d2fac171..8e62da442 100644 --- a/code/ACLoader.cpp +++ b/code/ACLoader.cpp @@ -296,7 +296,7 @@ void AC3DImporter::LoadObjectSection(std::vector& objects) SkipSpaces(&buffer); unsigned int t = strtoul10(buffer,&buffer); - if (t >= std::numeric_limits::max() / sizeof(aiVector3D)) { + if (t >= AI_MAX_ALLOC(aiVector3D)) { throw DeadlyImportError("AC3D: Too many vertices, would run out of memory"); } obj.vertices.reserve(t); @@ -589,7 +589,7 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object, mesh->mNumFaces = (*cit).first; if (mesh->mNumFaces == 0) { throw DeadlyImportError("AC3D: No faces"); - } else if (mesh->mNumFaces > std::numeric_limits::max() / sizeof(aiFace)) { + } else if (mesh->mNumFaces > AI_MAX_ALLOC(aiFace)) { throw DeadlyImportError("AC3D: Too many faces, would run out of memory"); } aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces]; @@ -597,7 +597,7 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object, mesh->mNumVertices = (*cit).second; if (mesh->mNumVertices == 0) { throw DeadlyImportError("AC3D: No vertices"); - } else if (mesh->mNumVertices > std::numeric_limits::max() / sizeof(aiVector3D)) { + } else if (mesh->mNumVertices > AI_MAX_ALLOC(aiVector3D)) { throw DeadlyImportError("AC3D: Too many vertices, would run out of memory"); } aiVector3D* vertices = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; diff --git a/code/MD3Loader.cpp b/code/MD3Loader.cpp index 5e9a1a3dc..3852a64a2 100644 --- a/code/MD3Loader.cpp +++ b/code/MD3Loader.cpp @@ -785,7 +785,9 @@ void MD3Importer::InternReadFile( const std::string& pFile, pScene->mNumMeshes = pcHeader->NUM_SURFACES; if (pcHeader->NUM_SURFACES == 0) { throw DeadlyImportError("MD3: No surfaces"); - } else if (pcHeader->NUM_SURFACES > std::numeric_limits::max() / sizeof(aiMesh)) { + } else if (pcHeader->NUM_SURFACES > AI_MAX_ALLOC(aiMesh)) { + // We allocate pointers but check against the size of aiMesh + // since those pointers will eventually have to point to real objects throw DeadlyImportError("MD3: Too many surfaces, would run out of memory"); } pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; diff --git a/code/ObjFileImporter.cpp b/code/ObjFileImporter.cpp index 7ade10f62..e4046b53b 100644 --- a/code/ObjFileImporter.cpp +++ b/code/ObjFileImporter.cpp @@ -382,7 +382,7 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel, pMesh->mNumVertices = numIndices; if (pMesh->mNumVertices == 0) { throw DeadlyImportError( "OBJ: no vertices" ); - } else if (pMesh->mNumVertices > std::numeric_limits::max() / sizeof(aiVector3D)) { + } else if (pMesh->mNumVertices > AI_MAX_ALLOC(aiVector3D)) { throw DeadlyImportError( "OBJ: Too many vertices, would run out of memory" ); } pMesh->mVertices = new aiVector3D[ pMesh->mNumVertices ]; diff --git a/include/assimp/defs.h b/include/assimp/defs.h index b0954920e..3129a027b 100644 --- a/include/assimp/defs.h +++ b/include/assimp/defs.h @@ -276,4 +276,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # define AI_BUILD_BIG_ENDIAN #endif + +/* To avoid running out of memory + * This can be adjusted for specific use cases + * It's NOT a total limit, just a limit for individual allocations + */ +#define AI_MAX_ALLOC(type) ((256U * 1024 * 1024) / sizeof(type)) + + #endif // !! INCLUDED_AI_DEFINES_H From 4540250116ee27bd571923f620d07f90617cda4c Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Fri, 14 Aug 2015 13:26:45 +0300 Subject: [PATCH 13/25] IFC: Don't store temporary values by reference This is not legal in any way and should never have worked. --- code/IFCOpenings.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/IFCOpenings.cpp b/code/IFCOpenings.cpp index db2187e49..b2a6bf6c5 100644 --- a/code/IFCOpenings.cpp +++ b/code/IFCOpenings.cpp @@ -1227,7 +1227,7 @@ bool GenerateOpenings(std::vector& openings, bool side_flag = true; if (!is_2d_source) { - const IfcVector3& face_nor = ((profile_verts[vi_total+2] - profile_verts[vi_total]) ^ + const IfcVector3 face_nor = ((profile_verts[vi_total+2] - profile_verts[vi_total]) ^ (profile_verts[vi_total+1] - profile_verts[vi_total])).Normalize(); const IfcFloat abs_dot_face_nor = std::abs(nor * face_nor); @@ -1692,7 +1692,7 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: BOOST_FOREACH(p2t::Triangle* tri, tris) { for(int i = 0; i < 3; ++i) { - const IfcVector2& v = IfcVector2( + const IfcVector2 v = IfcVector2( static_cast( tri->GetPoint(i)->x ), static_cast( tri->GetPoint(i)->y ) ); From e67bcca744ac854b7631c4e83ad4b968cdb42255 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Fri, 14 Aug 2015 14:02:16 +0300 Subject: [PATCH 14/25] IFC: Fix more bad references --- code/IFCOpenings.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/code/IFCOpenings.cpp b/code/IFCOpenings.cpp index b2a6bf6c5..d1dd9f50c 100644 --- a/code/IFCOpenings.cpp +++ b/code/IFCOpenings.cpp @@ -602,12 +602,12 @@ bool IntersectingLineSegments(const IfcVector2& n0, const IfcVector2& n1, const IfcVector2& m0, const IfcVector2& m1, IfcVector2& out0, IfcVector2& out1) { - const IfcVector2& n0_to_n1 = n1 - n0; + const IfcVector2 n0_to_n1 = n1 - n0; - const IfcVector2& n0_to_m0 = m0 - n0; - const IfcVector2& n1_to_m1 = m1 - n1; + const IfcVector2 n0_to_m0 = m0 - n0; + const IfcVector2 n1_to_m1 = m1 - n1; - const IfcVector2& n0_to_m1 = m1 - n0; + const IfcVector2 n0_to_m1 = m1 - n0; const IfcFloat e = 1e-5f; const IfcFloat smalle = 1e-9f; @@ -927,7 +927,7 @@ size_t CloseWindows(ContourVector& contours, IfcFloat best = static_cast(1e10); IfcVector3 bestv; - const IfcVector3& world_point = minv * IfcVector3(proj_point.x,proj_point.y,0.0f); + const IfcVector3 world_point = minv * IfcVector3(proj_point.x,proj_point.y,0.0f); BOOST_FOREACH(const TempOpening* opening, refs) { BOOST_FOREACH(const IfcVector3& other, opening->wallPoints) { @@ -1066,7 +1066,7 @@ IfcMatrix4 ProjectOntoPlane(std::vector& out_contour, const TempMesh // Project all points into the new coordinate system, collect min/max verts on the way BOOST_FOREACH(const IfcVector3& x, in_verts) { - const IfcVector3& vv = m * x; + const IfcVector3 vv = m * x; // keep Z offset in the plane coordinate system. Ignoring precision issues // (which are present, of course), this should be the same value for // all polygon vertices (assuming the polygon is planar). @@ -1144,7 +1144,7 @@ bool GenerateOpenings(std::vector& openings, std::vector contour_flat; IfcVector3 nor; - const IfcMatrix4& m = ProjectOntoPlane(contour_flat, curmesh, ok, nor); + const IfcMatrix4 m = ProjectOntoPlane(contour_flat, curmesh, ok, nor); if(!ok) { return false; } @@ -1242,7 +1242,7 @@ bool GenerateOpenings(std::vector& openings, for (unsigned int vi = 0, vend = profile_vertcnts[f]; vi < vend; ++vi, ++vi_total) { const IfcVector3& x = profile_verts[vi_total]; - const IfcVector3& v = m * x; + const IfcVector3 v = m * x; IfcVector2 vv(v.x, v.y); //if(check_intersection) { @@ -1322,7 +1322,7 @@ bool GenerateOpenings(std::vector& openings, MakeDisjunctWindowContours(other, temp_contour, poly); if(poly.size() == 1) { - const BoundingBox& newbb = GetBoundingBox(poly[0].outer); + const BoundingBox newbb = GetBoundingBox(poly[0].outer); if (!BoundingBoxesOverlapping(ibb, newbb )) { // Good guy bounding box bb = newbb ; @@ -1438,7 +1438,7 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: // working coordinate system. bool ok; IfcVector3 nor; - const IfcMatrix3& m = DerivePlaneCoordinateSpace(curmesh, ok, nor); + const IfcMatrix3 m = DerivePlaneCoordinateSpace(curmesh, ok, nor); if (!ok) { return false; } @@ -1686,7 +1686,7 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: continue; } - const std::vector& tris = cdt->GetTriangles(); + const std::vector tris = cdt->GetTriangles(); // Collect the triangles we just produced BOOST_FOREACH(p2t::Triangle* tri, tris) { From ec43e082959b632764ffaa71519f1bc57247a82f Mon Sep 17 00:00:00 2001 From: Andreas Henne Date: Fri, 14 Aug 2015 13:37:41 +0200 Subject: [PATCH 15/25] Fixed issue in STLLoader that lead to wrong node names. --- code/STLLoader.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index 1bf47dcf2..a9fe79f27 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -206,7 +206,6 @@ void STLImporter::InternReadFile( const std::string& pFile, } // add all created meshes to the single node - pScene->mRootNode = new aiNode(); pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; for (unsigned int i = 0; i < pScene->mNumMeshes; i++) From 9f157ed9b88aeeda56863aebacac37f96fff341c Mon Sep 17 00:00:00 2001 From: Wolfgang Herget Date: Wed, 26 Aug 2015 12:36:57 +0200 Subject: [PATCH 16/25] CMake: Don't try to set property on target before it is defined. The exact same code this commit removes is repeated in line 748. There, it actually works, since the "assimp" target is defined there. --- code/CMakeLists.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 351ce8da2..c3302ee98 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -656,11 +656,6 @@ if ( MSVC ) ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS ) endif ( MSVC ) -if (APPLE) - SET_TARGET_PROPERTIES( assimp PROPERTIES - INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}" - ) -endif() if (UNZIP_FOUND) SET (unzip_compile_SRCS "") else (UNZIP_FOUND) From 9885c3e55152b350161414cbe970db95ac4f0465 Mon Sep 17 00:00:00 2001 From: Gargaj Date: Fri, 28 Aug 2015 16:20:17 +0200 Subject: [PATCH 17/25] add opencollada extension --- code/ColladaParser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp index ddfab79f1..cdc3cedeb 100644 --- a/code/ColladaParser.cpp +++ b/code/ColladaParser.cpp @@ -1067,6 +1067,12 @@ void ColladaParser::ReadLight( Collada::Light& pLight) pLight.mFalloffAngle = ReadFloatFromTextContent(); TestClosing("hotspot_beam"); } + // OpenCOLLADA extensions + // ------------------------------------------------------- + else if (IsElement("decay_falloff")) { + pLight.mOuterAngle = ReadFloatFromTextContent(); + TestClosing("decay_falloff"); + } } else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { if( strcmp( mReader->getNodeName(), "light") == 0) From 1f6cead81ba463e0104a66ca1d246210196f5267 Mon Sep 17 00:00:00 2001 From: Gargaj Date: Sat, 29 Aug 2015 13:39:43 +0200 Subject: [PATCH 18/25] remove junk --- code/ColladaParser upstream.cpp | 2928 ------------------------------ code/ColladaParser upstream.h | 352 ---- code/ColladaParser wil.cpp | 2933 ------------------------------- 3 files changed, 6213 deletions(-) delete mode 100644 code/ColladaParser upstream.cpp delete mode 100644 code/ColladaParser upstream.h delete mode 100644 code/ColladaParser wil.cpp diff --git a/code/ColladaParser upstream.cpp b/code/ColladaParser upstream.cpp deleted file mode 100644 index d223f8184..000000000 --- a/code/ColladaParser upstream.cpp +++ /dev/null @@ -1,2928 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file ColladaParser.cpp - * @brief Implementation of the Collada parser helper - */ - - -#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER - -#include -#include "ColladaParser.h" -#include "fast_atof.h" -#include "ParsingUtils.h" -#include -#include -#include "../include/assimp/DefaultLogger.hpp" -#include "../include/assimp/IOSystem.hpp" -#include "../include/assimp/light.h" - - -using namespace Assimp; -using namespace Assimp::Collada; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile) - : mFileName( pFile) -{ - mRootNode = NULL; - mUnitSize = 1.0f; - mUpDirection = UP_Y; - - // We assume the newest file format by default - mFormat = FV_1_5_n; - - // open the file - boost::scoped_ptr file( pIOHandler->Open( pFile)); - if( file.get() == NULL) - throw DeadlyImportError( "Failed to open file " + pFile + "."); - - // generate a XML reader for it - boost::scoped_ptr mIOWrapper( new CIrrXML_IOStreamReader( file.get())); - mReader = irr::io::createIrrXMLReader( mIOWrapper.get()); - if( !mReader) - ThrowException( "Collada: Unable to open file."); - - // start reading - ReadContents(); -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -ColladaParser::~ColladaParser() -{ - delete mReader; - for( NodeLibrary::iterator it = mNodeLibrary.begin(); it != mNodeLibrary.end(); ++it) - delete it->second; - for( MeshLibrary::iterator it = mMeshLibrary.begin(); it != mMeshLibrary.end(); ++it) - delete it->second; -} - -// ------------------------------------------------------------------------------------------------ -// Read bool from text contents of current element -bool ColladaParser::ReadBoolFromTextContent() -{ - const char* cur = GetTextContent(); - return (!ASSIMP_strincmp(cur,"true",4) || '0' != *cur); -} - -// ------------------------------------------------------------------------------------------------ -// Read float from text contents of current element -float ColladaParser::ReadFloatFromTextContent() -{ - const char* cur = GetTextContent(); - return fast_atof(cur); -} - -// ------------------------------------------------------------------------------------------------ -// Reads the contents of the file -void ColladaParser::ReadContents() -{ - while( mReader->read()) - { - // handle the root element "COLLADA" - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "COLLADA")) - { - // check for 'version' attribute - const int attrib = TestAttribute("version"); - if (attrib != -1) { - const char* version = mReader->getAttributeValue(attrib); - - if (!::strncmp(version,"1.5",3)) { - mFormat = FV_1_5_n; - DefaultLogger::get()->debug("Collada schema version is 1.5.n"); - } - else if (!::strncmp(version,"1.4",3)) { - mFormat = FV_1_4_n; - DefaultLogger::get()->debug("Collada schema version is 1.4.n"); - } - else if (!::strncmp(version,"1.3",3)) { - mFormat = FV_1_3_n; - DefaultLogger::get()->debug("Collada schema version is 1.3.n"); - } - } - - ReadStructure(); - } else - { - DefaultLogger::get()->debug( boost::str( boost::format( "Ignoring global element <%s>.") % mReader->getNodeName())); - SkipElement(); - } - } else - { - // skip everything else silently - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the structure of the file -void ColladaParser::ReadStructure() -{ - while( mReader->read()) - { - // beginning of elements - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "asset")) - ReadAssetInfo(); - else if( IsElement( "library_animations")) - ReadAnimationLibrary(); - else if( IsElement( "library_controllers")) - ReadControllerLibrary(); - else if( IsElement( "library_images")) - ReadImageLibrary(); - else if( IsElement( "library_materials")) - ReadMaterialLibrary(); - else if( IsElement( "library_effects")) - ReadEffectLibrary(); - else if( IsElement( "library_geometries")) - ReadGeometryLibrary(); - else if( IsElement( "library_visual_scenes")) - ReadSceneLibrary(); - else if( IsElement( "library_lights")) - ReadLightLibrary(); - else if( IsElement( "library_cameras")) - ReadCameraLibrary(); - else if( IsElement( "library_nodes")) - ReadSceneNode(NULL); /* some hacking to reuse this piece of code */ - else if( IsElement( "scene")) - ReadScene(); - else - SkipElement(); - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads asset informations such as coordinate system informations and legal blah -void ColladaParser::ReadAssetInfo() -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "unit")) - { - // read unit data from the element's attributes - const int attrIndex = TestAttribute( "meter"); - if (attrIndex == -1) { - mUnitSize = 1.f; - } - else { - mUnitSize = mReader->getAttributeValueAsFloat( attrIndex); - } - - // consume the trailing stuff - if( !mReader->isEmptyElement()) - SkipElement(); - } - else if( IsElement( "up_axis")) - { - // read content, strip whitespace, compare - const char* content = GetTextContent(); - if( strncmp( content, "X_UP", 4) == 0) - mUpDirection = UP_X; - else if( strncmp( content, "Z_UP", 4) == 0) - mUpDirection = UP_Z; - else - mUpDirection = UP_Y; - - // check element end - TestClosing( "up_axis"); - } else - { - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "asset") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the animation library -void ColladaParser::ReadAnimationLibrary() -{ - if (mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "animation")) - { - // delegate the reading. Depending on the inner elements it will be a container or a anim channel - ReadAnimation( &mAnims); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "library_animations") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an animation into the given parent structure -void ColladaParser::ReadAnimation( Collada::Animation* pParent) -{ - if( mReader->isEmptyElement()) - return; - - // an element may be a container for grouping sub-elements or an animation channel - // this is the channel collection by ID, in case it has channels - typedef std::map ChannelMap; - ChannelMap channels; - // this is the anim container in case we're a container - Animation* anim = NULL; - - // optional name given as an attribute - std::string animName; - int indexName = TestAttribute( "name"); - int indexID = TestAttribute( "id"); - if( indexName >= 0) - animName = mReader->getAttributeValue( indexName); - else if( indexID >= 0) - animName = mReader->getAttributeValue( indexID); - else - animName = "animation"; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - // we have subanimations - if( IsElement( "animation")) - { - // create container from our element - if( !anim) - { - anim = new Animation; - anim->mName = animName; - pParent->mSubAnims.push_back( anim); - } - - // recurse into the subelement - ReadAnimation( anim); - } - else if( IsElement( "source")) - { - // possible animation data - we'll never know. Better store it - ReadSource(); - } - else if( IsElement( "sampler")) - { - // read the ID to assign the corresponding collada channel afterwards. - int indexID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( indexID); - ChannelMap::iterator newChannel = channels.insert( std::make_pair( id, AnimationChannel())).first; - - // have it read into a channel - ReadAnimationSampler( newChannel->second); - } - else if( IsElement( "channel")) - { - // the binding element whose whole purpose is to provide the target to animate - // Thanks, Collada! A directly posted information would have been too simple, I guess. - // Better add another indirection to that! Can't have enough of those. - int indexTarget = GetAttribute( "target"); - int indexSource = GetAttribute( "source"); - const char* sourceId = mReader->getAttributeValue( indexSource); - if( sourceId[0] == '#') - sourceId++; - ChannelMap::iterator cit = channels.find( sourceId); - if( cit != channels.end()) - cit->second.mTarget = mReader->getAttributeValue( indexTarget); - - if( !mReader->isEmptyElement()) - SkipElement(); - } - else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "animation") != 0) - ThrowException( "Expected end of element."); - - break; - } - } - - // it turned out to have channels - add them - if( !channels.empty()) - { - // special filtering for stupid exporters packing each channel into a separate animation - if( channels.size() == 1) - { - pParent->mChannels.push_back( channels.begin()->second); - } else - { - // else create the animation, if not done yet, and store the channels - if( !anim) - { - anim = new Animation; - anim->mName = animName; - pParent->mSubAnims.push_back( anim); - } - for( ChannelMap::const_iterator it = channels.begin(); it != channels.end(); ++it) - anim->mChannels.push_back( it->second); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an animation sampler into the given anim channel -void ColladaParser::ReadAnimationSampler( Collada::AnimationChannel& pChannel) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "input")) - { - int indexSemantic = GetAttribute( "semantic"); - const char* semantic = mReader->getAttributeValue( indexSemantic); - int indexSource = GetAttribute( "source"); - const char* source = mReader->getAttributeValue( indexSource); - if( source[0] != '#') - ThrowException( "Unsupported URL format"); - source++; - - if( strcmp( semantic, "INPUT") == 0) - pChannel.mSourceTimes = source; - else if( strcmp( semantic, "OUTPUT") == 0) - pChannel.mSourceValues = source; - - if( !mReader->isEmptyElement()) - SkipElement(); - } - else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "sampler") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the skeleton controller library -void ColladaParser::ReadControllerLibrary() -{ - if (mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "controller")) - { - // read ID. Ask the spec if it's neccessary or optional... you might be surprised. - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); - - // create an entry and store it in the library under its ID - mControllerLibrary[id] = Controller(); - - // read on from there - ReadController( mControllerLibrary[id]); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "library_controllers") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a controller into the given mesh structure -void ColladaParser::ReadController( Collada::Controller& pController) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - // two types of controllers: "skin" and "morph". Only the first one is relevant, we skip the other - if( IsElement( "morph")) - { - // should skip everything inside, so there's no danger of catching elements inbetween - SkipElement(); - } - else if( IsElement( "skin")) - { - // read the mesh it refers to. According to the spec this could also be another - // controller, but I refuse to implement every single idea they've come up with - int sourceIndex = GetAttribute( "source"); - pController.mMeshId = mReader->getAttributeValue( sourceIndex) + 1; - } - else if( IsElement( "bind_shape_matrix")) - { - // content is 16 floats to define a matrix... it seems to be important for some models - const char* content = GetTextContent(); - - // read the 16 floats - for( unsigned int a = 0; a < 16; a++) - { - // read a number - content = fast_atoreal_move( content, pController.mBindShapeMatrix[a]); - // skip whitespace after it - SkipSpacesAndLineEnd( &content); - } - - TestClosing( "bind_shape_matrix"); - } - else if( IsElement( "source")) - { - // data array - we have specialists to handle this - ReadSource(); - } - else if( IsElement( "joints")) - { - ReadControllerJoints( pController); - } - else if( IsElement( "vertex_weights")) - { - ReadControllerWeights( pController); - } - else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "controller") == 0) - break; - else if( strcmp( mReader->getNodeName(), "skin") != 0) - ThrowException( "Expected end of element."); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the joint definitions for the given controller -void ColladaParser::ReadControllerJoints( Collada::Controller& pController) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - // Input channels for joint data. Two possible semantics: "JOINT" and "INV_BIND_MATRIX" - if( IsElement( "input")) - { - int indexSemantic = GetAttribute( "semantic"); - const char* attrSemantic = mReader->getAttributeValue( indexSemantic); - int indexSource = GetAttribute( "source"); - const char* attrSource = mReader->getAttributeValue( indexSource); - - // local URLS always start with a '#'. We don't support global URLs - if( attrSource[0] != '#') - ThrowException( boost::str( boost::format( "Unsupported URL format in \"%s\" in source attribute of data element") % attrSource)); - attrSource++; - - // parse source URL to corresponding source - if( strcmp( attrSemantic, "JOINT") == 0) - pController.mJointNameSource = attrSource; - else if( strcmp( attrSemantic, "INV_BIND_MATRIX") == 0) - pController.mJointOffsetMatrixSource = attrSource; - else - ThrowException( boost::str( boost::format( "Unknown semantic \"%s\" in data element") % attrSemantic)); - - // skip inner data, if present - if( !mReader->isEmptyElement()) - SkipElement(); - } - else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "joints") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the joint weights for the given controller -void ColladaParser::ReadControllerWeights( Collada::Controller& pController) -{ - // read vertex count from attributes and resize the array accordingly - int indexCount = GetAttribute( "count"); - size_t vertexCount = (size_t) mReader->getAttributeValueAsInt( indexCount); - pController.mWeightCounts.resize( vertexCount); - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - // Input channels for weight data. Two possible semantics: "JOINT" and "WEIGHT" - if( IsElement( "input") && vertexCount > 0 ) - { - InputChannel channel; - - int indexSemantic = GetAttribute( "semantic"); - const char* attrSemantic = mReader->getAttributeValue( indexSemantic); - int indexSource = GetAttribute( "source"); - const char* attrSource = mReader->getAttributeValue( indexSource); - int indexOffset = TestAttribute( "offset"); - if( indexOffset >= 0) - channel.mOffset = mReader->getAttributeValueAsInt( indexOffset); - - // local URLS always start with a '#'. We don't support global URLs - if( attrSource[0] != '#') - ThrowException( boost::str( boost::format( "Unsupported URL format in \"%s\" in source attribute of data element") % attrSource)); - channel.mAccessor = attrSource + 1; - - // parse source URL to corresponding source - if( strcmp( attrSemantic, "JOINT") == 0) - pController.mWeightInputJoints = channel; - else if( strcmp( attrSemantic, "WEIGHT") == 0) - pController.mWeightInputWeights = channel; - else - ThrowException( boost::str( boost::format( "Unknown semantic \"%s\" in data element") % attrSemantic)); - - // skip inner data, if present - if( !mReader->isEmptyElement()) - SkipElement(); - } - else if( IsElement( "vcount") && vertexCount > 0 ) - { - // read weight count per vertex - const char* text = GetTextContent(); - size_t numWeights = 0; - for( std::vector::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it) - { - if( *text == 0) - ThrowException( "Out of data while reading "); - - *it = strtoul10( text, &text); - numWeights += *it; - SkipSpacesAndLineEnd( &text); - } - - TestClosing( "vcount"); - - // reserve weight count - pController.mWeights.resize( numWeights); - } - else if( IsElement( "v") && vertexCount > 0 ) - { - // read JointIndex - WeightIndex pairs - const char* text = GetTextContent(); - - for( std::vector< std::pair >::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it) - { - if( *text == 0) - ThrowException( "Out of data while reading "); - it->first = strtoul10( text, &text); - SkipSpacesAndLineEnd( &text); - if( *text == 0) - ThrowException( "Out of data while reading "); - it->second = strtoul10( text, &text); - SkipSpacesAndLineEnd( &text); - } - - TestClosing( "v"); - } - else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "vertex_weights") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the image library contents -void ColladaParser::ReadImageLibrary() -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "image")) - { - // read ID. Another entry which is "optional" by design but obligatory in reality - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); - - // create an entry and store it in the library under its ID - mImageLibrary[id] = Image(); - - // read on from there - ReadImage( mImageLibrary[id]); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "library_images") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an image entry into the given image -void ColladaParser::ReadImage( Collada::Image& pImage) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT){ - // Need to run different code paths here, depending on the Collada XSD version - if (IsElement("image")) { - SkipElement(); - } - else if( IsElement( "init_from")) - { - if (mFormat == FV_1_4_n) - { - // FIX: C4D exporter writes empty tags - if (!mReader->isEmptyElement()) { - // element content is filename - hopefully - const char* sz = TestTextContent(); - if (sz)pImage.mFileName = sz; - TestClosing( "init_from"); - } - if (!pImage.mFileName.length()) { - pImage.mFileName = "unknown_texture"; - } - } - else if (mFormat == FV_1_5_n) - { - // make sure we skip over mip and array initializations, which - // we don't support, but which could confuse the loader if - // they're not skipped. - int attrib = TestAttribute("array_index"); - if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) { - DefaultLogger::get()->warn("Collada: Ignoring texture array index"); - continue; - } - - attrib = TestAttribute("mip_index"); - if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) { - DefaultLogger::get()->warn("Collada: Ignoring MIP map layer"); - continue; - } - - // TODO: correctly jump over cube and volume maps? - } - } - else if (mFormat == FV_1_5_n) - { - if( IsElement( "ref")) - { - // element content is filename - hopefully - const char* sz = TestTextContent(); - if (sz)pImage.mFileName = sz; - TestClosing( "ref"); - } - else if( IsElement( "hex") && !pImage.mFileName.length()) - { - // embedded image. get format - const int attrib = TestAttribute("format"); - if (-1 == attrib) - DefaultLogger::get()->warn("Collada: Unknown image file format"); - else pImage.mEmbeddedFormat = mReader->getAttributeValue(attrib); - - const char* data = GetTextContent(); - - // hexadecimal-encoded binary octets. First of all, find the - // required buffer size to reserve enough storage. - const char* cur = data; - while (!IsSpaceOrNewLine(*cur)) cur++; - - const unsigned int size = (unsigned int)(cur-data) * 2; - pImage.mImageData.resize(size); - for (unsigned int i = 0; i < size;++i) - pImage.mImageData[i] = HexOctetToDecimal(data+(i<<1)); - - TestClosing( "hex"); - } - } - else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "image") == 0) - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the material library -void ColladaParser::ReadMaterialLibrary() -{ - if( mReader->isEmptyElement()) - return; - - std::map names; - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "material")) - { - // read ID. By now you propably know my opinion about this "specification" - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); - - std::string name; - int attrName = TestAttribute("name"); - if (attrName >= 0) - name = mReader->getAttributeValue( attrName); - - // create an entry and store it in the library under its ID - mMaterialLibrary[id] = Material(); - - if( !name.empty()) - { - std::map::iterator it = names.find( name); - if( it != names.end()) - { - std::ostringstream strStream; - strStream << ++it->second; - name.append( " " + strStream.str()); - } - else - { - names[name] = 0; - } - - mMaterialLibrary[id].mName = name; - } - - ReadMaterial( mMaterialLibrary[id]); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "library_materials") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the light library -void ColladaParser::ReadLightLibrary() -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "light")) - { - // read ID. By now you propably know my opinion about this "specification" - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); - - // create an entry and store it in the library under its ID - ReadLight(mLightLibrary[id] = Light()); - - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "library_lights") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the camera library -void ColladaParser::ReadCameraLibrary() -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "camera")) - { - // read ID. By now you propably know my opinion about this "specification" - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); - - // create an entry and store it in the library under its ID - Camera& cam = mCameraLibrary[id]; - attrID = TestAttribute( "name"); - if (attrID != -1) - cam.mName = mReader->getAttributeValue( attrID); - - ReadCamera(cam); - - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "library_cameras") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a material entry into the given material -void ColladaParser::ReadMaterial( Collada::Material& pMaterial) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if (IsElement("material")) { - SkipElement(); - } - else if( IsElement( "instance_effect")) - { - // referred effect by URL - int attrUrl = GetAttribute( "url"); - const char* url = mReader->getAttributeValue( attrUrl); - if( url[0] != '#') - ThrowException( "Unknown reference format"); - - pMaterial.mEffect = url+1; - - SkipElement(); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "material") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a light entry into the given light -void ColladaParser::ReadLight( Collada::Light& pLight) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if (IsElement("light")) { - SkipElement(); - } - else if (IsElement("spot")) { - pLight.mType = aiLightSource_SPOT; - } - else if (IsElement("ambient")) { - pLight.mType = aiLightSource_AMBIENT; - } - else if (IsElement("directional")) { - pLight.mType = aiLightSource_DIRECTIONAL; - } - else if (IsElement("point")) { - pLight.mType = aiLightSource_POINT; - } - else if (IsElement("color")) { - // text content contains 3 floats - const char* content = GetTextContent(); - - content = fast_atoreal_move( content, (float&)pLight.mColor.r); - SkipSpacesAndLineEnd( &content); - - content = fast_atoreal_move( content, (float&)pLight.mColor.g); - SkipSpacesAndLineEnd( &content); - - content = fast_atoreal_move( content, (float&)pLight.mColor.b); - SkipSpacesAndLineEnd( &content); - - TestClosing( "color"); - } - else if (IsElement("constant_attenuation")) { - pLight.mAttConstant = ReadFloatFromTextContent(); - TestClosing("constant_attenuation"); - } - else if (IsElement("linear_attenuation")) { - pLight.mAttLinear = ReadFloatFromTextContent(); - TestClosing("linear_attenuation"); - } - else if (IsElement("quadratic_attenuation")) { - pLight.mAttQuadratic = ReadFloatFromTextContent(); - TestClosing("quadratic_attenuation"); - } - else if (IsElement("falloff_angle")) { - pLight.mFalloffAngle = ReadFloatFromTextContent(); - TestClosing("falloff_angle"); - } - else if (IsElement("falloff_exponent")) { - pLight.mFalloffExponent = ReadFloatFromTextContent(); - TestClosing("falloff_exponent"); - } - // FCOLLADA extensions - // ------------------------------------------------------- - else if (IsElement("outer_cone")) { - pLight.mOuterAngle = ReadFloatFromTextContent(); - TestClosing("outer_cone"); - } - // ... and this one is even deprecated - else if (IsElement("penumbra_angle")) { - pLight.mPenumbraAngle = ReadFloatFromTextContent(); - TestClosing("penumbra_angle"); - } - else if (IsElement("intensity")) { - pLight.mIntensity = ReadFloatFromTextContent(); - TestClosing("intensity"); - } - else if (IsElement("falloff")) { - pLight.mOuterAngle = ReadFloatFromTextContent(); - TestClosing("falloff"); - } - else if (IsElement("hotspot_beam")) { - pLight.mFalloffAngle = ReadFloatFromTextContent(); - TestClosing("hotspot_beam"); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "light") == 0) - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a camera entry into the given light -void ColladaParser::ReadCamera( Collada::Camera& pCamera) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if (IsElement("camera")) { - SkipElement(); - } - else if (IsElement("orthographic")) { - pCamera.mOrtho = true; - } - else if (IsElement("xfov") || IsElement("xmag")) { - pCamera.mHorFov = ReadFloatFromTextContent(); - TestClosing((pCamera.mOrtho ? "xmag" : "xfov")); - } - else if (IsElement("yfov") || IsElement("ymag")) { - pCamera.mVerFov = ReadFloatFromTextContent(); - TestClosing((pCamera.mOrtho ? "ymag" : "yfov")); - } - else if (IsElement("aspect_ratio")) { - pCamera.mAspect = ReadFloatFromTextContent(); - TestClosing("aspect_ratio"); - } - else if (IsElement("znear")) { - pCamera.mZNear = ReadFloatFromTextContent(); - TestClosing("znear"); - } - else if (IsElement("zfar")) { - pCamera.mZFar = ReadFloatFromTextContent(); - TestClosing("zfar"); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "camera") == 0) - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the effect library -void ColladaParser::ReadEffectLibrary() -{ - if (mReader->isEmptyElement()) { - return; - } - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "effect")) - { - // read ID. Do I have to repeat my ranting about "optional" attributes? - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); - - // create an entry and store it in the library under its ID - mEffectLibrary[id] = Effect(); - // read on from there - ReadEffect( mEffectLibrary[id]); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "library_effects") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an effect entry into the given effect -void ColladaParser::ReadEffect( Collada::Effect& pEffect) -{ - // for the moment we don't support any other type of effect. - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "profile_COMMON")) - ReadEffectProfileCommon( pEffect); - else - SkipElement(); - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "effect") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an COMMON effect profile -void ColladaParser::ReadEffectProfileCommon( Collada::Effect& pEffect) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "newparam")) { - // save ID - int attrSID = GetAttribute( "sid"); - std::string sid = mReader->getAttributeValue( attrSID); - pEffect.mParams[sid] = EffectParam(); - ReadEffectParam( pEffect.mParams[sid]); - } - else if( IsElement( "technique") || IsElement( "extra")) - { - // just syntactic sugar - } - - else if( mFormat == FV_1_4_n && IsElement( "image")) - { - // read ID. Another entry which is "optional" by design but obligatory in reality - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); - - // create an entry and store it in the library under its ID - mImageLibrary[id] = Image(); - - // read on from there - ReadImage( mImageLibrary[id]); - } - - /* Shading modes */ - else if( IsElement( "phong")) - pEffect.mShadeType = Shade_Phong; - else if( IsElement( "constant")) - pEffect.mShadeType = Shade_Constant; - else if( IsElement( "lambert")) - pEffect.mShadeType = Shade_Lambert; - else if( IsElement( "blinn")) - pEffect.mShadeType = Shade_Blinn; - - /* Color + texture properties */ - else if( IsElement( "emission")) - ReadEffectColor( pEffect.mEmissive, pEffect.mTexEmissive); - else if( IsElement( "ambient")) - ReadEffectColor( pEffect.mAmbient, pEffect.mTexAmbient); - else if( IsElement( "diffuse")) - ReadEffectColor( pEffect.mDiffuse, pEffect.mTexDiffuse); - else if( IsElement( "specular")) - ReadEffectColor( pEffect.mSpecular, pEffect.mTexSpecular); - else if( IsElement( "reflective")) { - ReadEffectColor( pEffect.mReflective, pEffect.mTexReflective); - } - else if( IsElement( "transparent")) { - pEffect.mHasTransparency = true; - - // In RGB_ZERO mode, the transparency is interpreted in reverse, go figure... - if(::strcmp(mReader->getAttributeValueSafe("opaque"), "RGB_ZERO") == 0) { - // TODO: handle RGB_ZERO mode completely - pEffect.mRGBTransparency = true; - } - - ReadEffectColor( pEffect.mTransparent,pEffect.mTexTransparent); - } - else if( IsElement( "shininess")) - ReadEffectFloat( pEffect.mShininess); - else if( IsElement( "reflectivity")) - ReadEffectFloat( pEffect.mReflectivity); - - /* Single scalar properties */ - else if( IsElement( "transparency")) - ReadEffectFloat( pEffect.mTransparency); - else if( IsElement( "index_of_refraction")) - ReadEffectFloat( pEffect.mRefractIndex); - - // GOOGLEEARTH/OKINO extensions - // ------------------------------------------------------- - else if( IsElement( "double_sided")) - pEffect.mDoubleSided = ReadBoolFromTextContent(); - - // FCOLLADA extensions - // ------------------------------------------------------- - else if( IsElement( "bump")) { - aiColor4D dummy; - ReadEffectColor( dummy,pEffect.mTexBump); - } - - // MAX3D extensions - // ------------------------------------------------------- - else if( IsElement( "wireframe")) { - pEffect.mWireframe = ReadBoolFromTextContent(); - TestClosing( "wireframe"); - } - else if( IsElement( "faceted")) { - pEffect.mFaceted = ReadBoolFromTextContent(); - TestClosing( "faceted"); - } - else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "profile_COMMON") == 0) - { - break; - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Read texture wrapping + UV transform settings from a profile==Maya chunk -void ColladaParser::ReadSamplerProperties( Sampler& out ) -{ - if (mReader->isEmptyElement()) { - return; - } - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - - // MAYA extensions - // ------------------------------------------------------- - if( IsElement( "wrapU")) { - out.mWrapU = ReadBoolFromTextContent(); - TestClosing( "wrapU"); - } - else if( IsElement( "wrapV")) { - out.mWrapV = ReadBoolFromTextContent(); - TestClosing( "wrapV"); - } - else if( IsElement( "mirrorU")) { - out.mMirrorU = ReadBoolFromTextContent(); - TestClosing( "mirrorU"); - } - else if( IsElement( "mirrorV")) { - out.mMirrorV = ReadBoolFromTextContent(); - TestClosing( "mirrorV"); - } - else if( IsElement( "repeatU")) { - out.mTransform.mScaling.x = ReadFloatFromTextContent(); - TestClosing( "repeatU"); - } - else if( IsElement( "repeatV")) { - out.mTransform.mScaling.y = ReadFloatFromTextContent(); - TestClosing( "repeatV"); - } - else if( IsElement( "offsetU")) { - out.mTransform.mTranslation.x = ReadFloatFromTextContent(); - TestClosing( "offsetU"); - } - else if( IsElement( "offsetV")) { - out.mTransform.mTranslation.y = ReadFloatFromTextContent(); - TestClosing( "offsetV"); - } - else if( IsElement( "rotateUV")) { - out.mTransform.mRotation = ReadFloatFromTextContent(); - TestClosing( "rotateUV"); - } - else if( IsElement( "blend_mode")) { - - const char* sz = GetTextContent(); - // http://www.feelingsoftware.com/content/view/55/72/lang,en/ - // NONE, OVER, IN, OUT, ADD, SUBTRACT, MULTIPLY, DIFFERENCE, LIGHTEN, DARKEN, SATURATE, DESATURATE and ILLUMINATE - if (0 == ASSIMP_strincmp(sz,"ADD",3)) - out.mOp = aiTextureOp_Add; - - else if (0 == ASSIMP_strincmp(sz,"SUBTRACT",8)) - out.mOp = aiTextureOp_Subtract; - - else if (0 == ASSIMP_strincmp(sz,"MULTIPLY",8)) - out.mOp = aiTextureOp_Multiply; - - else { - DefaultLogger::get()->warn("Collada: Unsupported MAYA texture blend mode"); - } - TestClosing( "blend_mode"); - } - // OKINO extensions - // ------------------------------------------------------- - else if( IsElement( "weighting")) { - out.mWeighting = ReadFloatFromTextContent(); - TestClosing( "weighting"); - } - else if( IsElement( "mix_with_previous_layer")) { - out.mMixWithPrevious = ReadFloatFromTextContent(); - TestClosing( "mix_with_previous_layer"); - } - // MAX3D extensions - // ------------------------------------------------------- - else if( IsElement( "amount")) { - out.mWeighting = ReadFloatFromTextContent(); - TestClosing( "amount"); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "technique") == 0) - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an effect entry containing a color or a texture defining that color -void ColladaParser::ReadEffectColor( aiColor4D& pColor, Sampler& pSampler) -{ - if (mReader->isEmptyElement()) - return; - - // Save current element name - const std::string curElem = mReader->getNodeName(); - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "color")) - { - // text content contains 4 floats - const char* content = GetTextContent(); - - content = fast_atoreal_move( content, (float&)pColor.r); - SkipSpacesAndLineEnd( &content); - - content = fast_atoreal_move( content, (float&)pColor.g); - SkipSpacesAndLineEnd( &content); - - content = fast_atoreal_move( content, (float&)pColor.b); - SkipSpacesAndLineEnd( &content); - - content = fast_atoreal_move( content, (float&)pColor.a); - SkipSpacesAndLineEnd( &content); - TestClosing( "color"); - } - else if( IsElement( "texture")) - { - // get name of source textur/sampler - int attrTex = GetAttribute( "texture"); - pSampler.mName = mReader->getAttributeValue( attrTex); - - // get name of UV source channel. Specification demands it to be there, but some exporters - // don't write it. It will be the default UV channel in case it's missing. - attrTex = TestAttribute( "texcoord"); - if( attrTex >= 0 ) - pSampler.mUVChannel = mReader->getAttributeValue( attrTex); - //SkipElement(); - - // as we've read texture, the color needs to be 1,1,1,1 - pColor = aiColor4D(1.f, 1.f, 1.f, 1.f); - } - else if( IsElement( "technique")) - { - const int _profile = GetAttribute( "profile"); - const char* profile = mReader->getAttributeValue( _profile ); - - // Some extensions are quite useful ... ReadSamplerProperties processes - // several extensions in MAYA, OKINO and MAX3D profiles. - if (!::strcmp(profile,"MAYA") || !::strcmp(profile,"MAX3D") || !::strcmp(profile,"OKINO")) - { - // get more information on this sampler - ReadSamplerProperties(pSampler); - } - else SkipElement(); - } - else if( !IsElement( "extra")) - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){ - if (mReader->getNodeName() == curElem) - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an effect entry containing a float -void ColladaParser::ReadEffectFloat( float& pFloat) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT){ - if( IsElement( "float")) - { - // text content contains a single floats - const char* content = GetTextContent(); - content = fast_atoreal_move( content, pFloat); - SkipSpacesAndLineEnd( &content); - - TestClosing( "float"); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){ - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an effect parameter specification of any kind -void ColladaParser::ReadEffectParam( Collada::EffectParam& pParam) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "surface")) - { - // image ID given inside tags - TestOpening( "init_from"); - const char* content = GetTextContent(); - pParam.mType = Param_Surface; - pParam.mReference = content; - TestClosing( "init_from"); - - // don't care for remaining stuff - SkipElement( "surface"); - } - else if( IsElement( "sampler2D")) - { - // surface ID is given inside tags - TestOpening( "source"); - const char* content = GetTextContent(); - pParam.mType = Param_Sampler; - pParam.mReference = content; - TestClosing( "source"); - - // don't care for remaining stuff - SkipElement( "sampler2D"); - } else - { - // ignore unknown element - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the geometry library contents -void ColladaParser::ReadGeometryLibrary() -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "geometry")) - { - // read ID. Another entry which is "optional" by design but obligatory in reality - int indexID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( indexID); - - // TODO: (thom) support SIDs - // ai_assert( TestAttribute( "sid") == -1); - - // create a mesh and store it in the library under its ID - Mesh* mesh = new Mesh; - mMeshLibrary[id] = mesh; - - // read the mesh name if it exists - const int nameIndex = TestAttribute("name"); - if(nameIndex != -1) - { - mesh->mName = mReader->getAttributeValue(nameIndex); - } - - // read on from there - ReadGeometry( mesh); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "library_geometries") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a geometry from the geometry library. -void ColladaParser::ReadGeometry( Collada::Mesh* pMesh) -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "mesh")) - { - // read on from there - ReadMesh( pMesh); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "geometry") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a mesh from the geometry library -void ColladaParser::ReadMesh( Mesh* pMesh) -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "source")) - { - // we have professionals dealing with this - ReadSource(); - } - else if( IsElement( "vertices")) - { - // read per-vertex mesh data - ReadVertexData( pMesh); - } - else if( IsElement( "triangles") || IsElement( "lines") || IsElement( "linestrips") - || IsElement( "polygons") || IsElement( "polylist") || IsElement( "trifans") || IsElement( "tristrips")) - { - // read per-index mesh data and faces setup - ReadIndexData( pMesh); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "technique_common") == 0) - { - // end of another meaningless element - read over it - } - else if( strcmp( mReader->getNodeName(), "mesh") == 0) - { - // end of element - we're done here - break; - } else - { - // everything else should be punished - ThrowException( "Expected end of element."); - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a source element -void ColladaParser::ReadSource() -{ - int indexID = GetAttribute( "id"); - std::string sourceID = mReader->getAttributeValue( indexID); - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "float_array") || IsElement( "IDREF_array") || IsElement( "Name_array")) - { - ReadDataArray(); - } - else if( IsElement( "technique_common")) - { - // I don't care for your profiles - } - else if( IsElement( "accessor")) - { - ReadAccessor( sourceID); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "source") == 0) - { - // end of - we're done - break; - } - else if( strcmp( mReader->getNodeName(), "technique_common") == 0) - { - // end of another meaningless element - read over it - } else - { - // everything else should be punished - ThrowException( "Expected end of element."); - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a data array holding a number of floats, and stores it in the global library -void ColladaParser::ReadDataArray() -{ - std::string elmName = mReader->getNodeName(); - bool isStringArray = (elmName == "IDREF_array" || elmName == "Name_array"); - bool isEmptyElement = mReader->isEmptyElement(); - - // read attributes - int indexID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( indexID); - int indexCount = GetAttribute( "count"); - unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( indexCount); - const char* content = TestTextContent(); - - // read values and store inside an array in the data library - mDataLibrary[id] = Data(); - Data& data = mDataLibrary[id]; - data.mIsStringArray = isStringArray; - - // some exporters write empty data arrays, but we need to conserve them anyways because others might reference them - if (content) - { - if( isStringArray) - { - data.mStrings.reserve( count); - std::string s; - - for( unsigned int a = 0; a < count; a++) - { - if( *content == 0) - ThrowException( "Expected more values while reading IDREF_array contents."); - - s.clear(); - while( !IsSpaceOrNewLine( *content)) - s += *content++; - data.mStrings.push_back( s); - - SkipSpacesAndLineEnd( &content); - } - } else - { - data.mValues.reserve( count); - - for( unsigned int a = 0; a < count; a++) - { - if( *content == 0) - ThrowException( "Expected more values while reading float_array contents."); - - float value; - // read a number - content = fast_atoreal_move( content, value); - data.mValues.push_back( value); - // skip whitespace after it - SkipSpacesAndLineEnd( &content); - } - } - } - - // test for closing tag - if( !isEmptyElement ) - TestClosing( elmName.c_str()); -} - -// ------------------------------------------------------------------------------------------------ -// Reads an accessor and stores it in the global library -void ColladaParser::ReadAccessor( const std::string& pID) -{ - // read accessor attributes - int attrSource = GetAttribute( "source"); - const char* source = mReader->getAttributeValue( attrSource); - if( source[0] != '#') - ThrowException( boost::str( boost::format( "Unknown reference format in url \"%s\" in source attribute of element.") % source)); - int attrCount = GetAttribute( "count"); - unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( attrCount); - int attrOffset = TestAttribute( "offset"); - unsigned int offset = 0; - if( attrOffset > -1) - offset = (unsigned int) mReader->getAttributeValueAsInt( attrOffset); - int attrStride = TestAttribute( "stride"); - unsigned int stride = 1; - if( attrStride > -1) - stride = (unsigned int) mReader->getAttributeValueAsInt( attrStride); - - // store in the library under the given ID - mAccessorLibrary[pID] = Accessor(); - Accessor& acc = mAccessorLibrary[pID]; - acc.mCount = count; - acc.mOffset = offset; - acc.mStride = stride; - acc.mSource = source+1; // ignore the leading '#' - acc.mSize = 0; // gets incremented with every param - - // and read the components - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "param")) - { - // read data param - int attrName = TestAttribute( "name"); - std::string name; - if( attrName > -1) - { - name = mReader->getAttributeValue( attrName); - - // analyse for common type components and store it's sub-offset in the corresponding field - - /* Cartesian coordinates */ - if( name == "X") acc.mSubOffset[0] = acc.mParams.size(); - else if( name == "Y") acc.mSubOffset[1] = acc.mParams.size(); - else if( name == "Z") acc.mSubOffset[2] = acc.mParams.size(); - - /* RGBA colors */ - else if( name == "R") acc.mSubOffset[0] = acc.mParams.size(); - else if( name == "G") acc.mSubOffset[1] = acc.mParams.size(); - else if( name == "B") acc.mSubOffset[2] = acc.mParams.size(); - else if( name == "A") acc.mSubOffset[3] = acc.mParams.size(); - - /* UVWQ (STPQ) texture coordinates */ - else if( name == "S") acc.mSubOffset[0] = acc.mParams.size(); - else if( name == "T") acc.mSubOffset[1] = acc.mParams.size(); - else if( name == "P") acc.mSubOffset[2] = acc.mParams.size(); - // else if( name == "Q") acc.mSubOffset[3] = acc.mParams.size(); - /* 4D uv coordinates are not supported in Assimp */ - - /* Generic extra data, interpreted as UV data, too*/ - else if( name == "U") acc.mSubOffset[0] = acc.mParams.size(); - else if( name == "V") acc.mSubOffset[1] = acc.mParams.size(); - //else - // DefaultLogger::get()->warn( boost::str( boost::format( "Unknown accessor parameter \"%s\". Ignoring data channel.") % name)); - } - - // read data type - int attrType = TestAttribute( "type"); - if( attrType > -1) - { - // for the moment we only distinguish between a 4x4 matrix and anything else. - // TODO: (thom) I don't have a spec here at work. Check if there are other multi-value types - // which should be tested for here. - std::string type = mReader->getAttributeValue( attrType); - if( type == "float4x4") - acc.mSize += 16; - else - acc.mSize += 1; - } - - acc.mParams.push_back( name); - - // skip remaining stuff of this element, if any - SkipElement(); - } else - { - ThrowException( boost::str( boost::format( "Unexpected sub element <%s> in tag ") % mReader->getNodeName())); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "accessor") != 0) - ThrowException( "Expected end of element."); - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads input declarations of per-vertex mesh data into the given mesh -void ColladaParser::ReadVertexData( Mesh* pMesh) -{ - // extract the ID of the element. Not that we care, but to catch strange referencing schemes we should warn about - int attrID= GetAttribute( "id"); - pMesh->mVertexID = mReader->getAttributeValue( attrID); - - // a number of elements - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "input")) - { - ReadInputChannel( pMesh->mPerVertexData); - } else - { - ThrowException( boost::str( boost::format( "Unexpected sub element <%s> in tag ") % mReader->getNodeName())); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "vertices") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads input declarations of per-index mesh data into the given mesh -void ColladaParser::ReadIndexData( Mesh* pMesh) -{ - std::vector vcount; - std::vector perIndexData; - - // read primitive count from the attribute - int attrCount = GetAttribute( "count"); - size_t numPrimitives = (size_t) mReader->getAttributeValueAsInt( attrCount); - // some mesh types (e.g. tristrips) don't specify primitive count upfront, - // so we need to sum up the actual number of primitives while we read the

-tags - size_t actualPrimitives = 0; - - // material subgroup - int attrMaterial = TestAttribute( "material"); - SubMesh subgroup; - if( attrMaterial > -1) - subgroup.mMaterial = mReader->getAttributeValue( attrMaterial); - - // distinguish between polys and triangles - std::string elementName = mReader->getNodeName(); - PrimitiveType primType = Prim_Invalid; - if( IsElement( "lines")) - primType = Prim_Lines; - else if( IsElement( "linestrips")) - primType = Prim_LineStrip; - else if( IsElement( "polygons")) - primType = Prim_Polygon; - else if( IsElement( "polylist")) - primType = Prim_Polylist; - else if( IsElement( "triangles")) - primType = Prim_Triangles; - else if( IsElement( "trifans")) - primType = Prim_TriFans; - else if( IsElement( "tristrips")) - primType = Prim_TriStrips; - - ai_assert( primType != Prim_Invalid); - - // also a number of elements, but in addition a

primitive collection and propably index counts for all primitives - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "input")) - { - ReadInputChannel( perIndexData); - } - else if( IsElement( "vcount")) - { - if( !mReader->isEmptyElement()) - { - if (numPrimitives) // It is possible to define a mesh without any primitives - { - // case - specifies the number of indices for each polygon - const char* content = GetTextContent(); - vcount.reserve( numPrimitives); - for( unsigned int a = 0; a < numPrimitives; a++) - { - if( *content == 0) - ThrowException( "Expected more values while reading contents."); - // read a number - vcount.push_back( (size_t) strtoul10( content, &content)); - // skip whitespace after it - SkipSpacesAndLineEnd( &content); - } - } - - TestClosing( "vcount"); - } - } - else if( IsElement( "p")) - { - if( !mReader->isEmptyElement()) - { - // now here the actual fun starts - these are the indices to construct the mesh data from - actualPrimitives += ReadPrimitives(pMesh, perIndexData, numPrimitives, vcount, primType); - } - } - else if (IsElement("extra")) - { - SkipElement("extra"); - } else - { - ThrowException( boost::str( boost::format( "Unexpected sub element <%s> in tag <%s>") % mReader->getNodeName() % elementName)); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( mReader->getNodeName() != elementName) - ThrowException( boost::str( boost::format( "Expected end of <%s> element.") % elementName)); - - break; - } - } - -#ifdef ASSIMP_BUILD_DEBUG - if (primType != Prim_TriFans && primType != Prim_TriStrips) { - ai_assert(actualPrimitives == numPrimitives); - } -#endif - - // only when we're done reading all

tags (and thus know the final vertex count) can we commit the submesh - subgroup.mNumFaces = actualPrimitives; - pMesh->mSubMeshes.push_back(subgroup); -} - -// ------------------------------------------------------------------------------------------------ -// Reads a single input channel element and stores it in the given array, if valid -void ColladaParser::ReadInputChannel( std::vector& poChannels) -{ - InputChannel channel; - - // read semantic - int attrSemantic = GetAttribute( "semantic"); - std::string semantic = mReader->getAttributeValue( attrSemantic); - channel.mType = GetTypeForSemantic( semantic); - - // read source - int attrSource = GetAttribute( "source"); - const char* source = mReader->getAttributeValue( attrSource); - if( source[0] != '#') - ThrowException( boost::str( boost::format( "Unknown reference format in url \"%s\" in source attribute of element.") % source)); - channel.mAccessor = source+1; // skipping the leading #, hopefully the remaining text is the accessor ID only - - // read index offset, if per-index - int attrOffset = TestAttribute( "offset"); - if( attrOffset > -1) - channel.mOffset = mReader->getAttributeValueAsInt( attrOffset); - - // read set if texture coordinates - if(channel.mType == IT_Texcoord || channel.mType == IT_Color){ - int attrSet = TestAttribute("set"); - if(attrSet > -1){ - attrSet = mReader->getAttributeValueAsInt( attrSet); - if(attrSet < 0) - ThrowException( boost::str( boost::format( "Invalid index \"%i\" in set attribute of element") % (attrSet))); - - channel.mIndex = attrSet; - } - } - - // store, if valid type - if( channel.mType != IT_Invalid) - poChannels.push_back( channel); - - // skip remaining stuff of this element, if any - SkipElement(); -} - -// ------------------------------------------------------------------------------------------------ -// Reads a

primitive index list and assembles the mesh data into the given mesh -size_t ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector& pPerIndexChannels, - size_t pNumPrimitives, const std::vector& pVCount, PrimitiveType pPrimType) -{ - // determine number of indices coming per vertex - // find the offset index for all per-vertex channels - size_t numOffsets = 1; - size_t perVertexOffset = SIZE_MAX; // invalid value - BOOST_FOREACH( const InputChannel& channel, pPerIndexChannels) - { - numOffsets = std::max( numOffsets, channel.mOffset+1); - if( channel.mType == IT_Vertex) - perVertexOffset = channel.mOffset; - } - - // determine the expected number of indices - size_t expectedPointCount = 0; - switch( pPrimType) - { - case Prim_Polylist: - { - BOOST_FOREACH( size_t i, pVCount) - expectedPointCount += i; - break; - } - case Prim_Lines: - expectedPointCount = 2 * pNumPrimitives; - break; - case Prim_Triangles: - expectedPointCount = 3 * pNumPrimitives; - break; - default: - // other primitive types don't state the index count upfront... we need to guess - break; - } - - // and read all indices into a temporary array - std::vector indices; - if( expectedPointCount > 0) - indices.reserve( expectedPointCount * numOffsets); - - if (pNumPrimitives > 0) // It is possible to not contain any indicies - { - const char* content = GetTextContent(); - while( *content != 0) - { - // read a value. - // Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways. - int value = std::max( 0, strtol10( content, &content)); - indices.push_back( size_t( value)); - // skip whitespace after it - SkipSpacesAndLineEnd( &content); - } - } - - // complain if the index count doesn't fit - if( expectedPointCount > 0 && indices.size() != expectedPointCount * numOffsets) - ThrowException( "Expected different index count in

element."); - else if( expectedPointCount == 0 && (indices.size() % numOffsets) != 0) - ThrowException( "Expected different index count in

element."); - - // find the data for all sources - for( std::vector::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it) - { - InputChannel& input = *it; - if( input.mResolved) - continue; - - // find accessor - input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor); - // resolve accessor's data pointer as well, if neccessary - const Accessor* acc = input.mResolved; - if( !acc->mData) - acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource); - } - // and the same for the per-index channels - for( std::vector::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) - { - InputChannel& input = *it; - if( input.mResolved) - continue; - - // ignore vertex pointer, it doesn't refer to an accessor - if( input.mType == IT_Vertex) - { - // warn if the vertex channel does not refer to the element in the same mesh - if( input.mAccessor != pMesh->mVertexID) - ThrowException( "Unsupported vertex referencing scheme."); - continue; - } - - // find accessor - input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor); - // resolve accessor's data pointer as well, if neccessary - const Accessor* acc = input.mResolved; - if( !acc->mData) - acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource); - } - - // For continued primitives, the given count does not come all in one

, but only one primitive per

- size_t numPrimitives = pNumPrimitives; - if( pPrimType == Prim_TriFans || pPrimType == Prim_Polygon) - numPrimitives = 1; - // For continued primitives, the given count is actually the number of

's inside the parent tag - if ( pPrimType == Prim_TriStrips){ - size_t numberOfVertices = indices.size() / numOffsets; - numPrimitives = numberOfVertices - 2; - } - - pMesh->mFaceSize.reserve( numPrimitives); - pMesh->mFacePosIndices.reserve( indices.size() / numOffsets); - - size_t polylistStartVertex = 0; - for (size_t currentPrimitive = 0; currentPrimitive < numPrimitives; currentPrimitive++) - { - // determine number of points for this primitive - size_t numPoints = 0; - switch( pPrimType) - { - case Prim_Lines: - numPoints = 2; - for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++) - CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - break; - case Prim_Triangles: - numPoints = 3; - for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++) - CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - break; - case Prim_TriStrips: - numPoints = 3; - ReadPrimTriStrips(numOffsets, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - break; - case Prim_Polylist: - numPoints = pVCount[currentPrimitive]; - for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++) - CopyVertex(polylistStartVertex + currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, 0, indices); - polylistStartVertex += numPoints; - break; - case Prim_TriFans: - case Prim_Polygon: - numPoints = indices.size() / numOffsets; - for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++) - CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - break; - default: - // LineStrip is not supported due to expected index unmangling - ThrowException( "Unsupported primitive type."); - break; - } - - // store the face size to later reconstruct the face from - pMesh->mFaceSize.push_back( numPoints); - } - - // if I ever get my hands on that guy who invented this steaming pile of indirection... - TestClosing( "p"); - return numPrimitives; -} - -void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset, Mesh* pMesh, std::vector& pPerIndexChannels, size_t currentPrimitive, const std::vector& indices){ - // calculate the base offset of the vertex whose attributes we ant to copy - size_t baseOffset = currentPrimitive * numOffsets * numPoints + currentVertex * numOffsets; - - // don't overrun the boundaries of the index list - size_t maxIndexRequested = baseOffset + numOffsets - 1; - ai_assert(maxIndexRequested < indices.size()); - - // extract per-vertex channels using the global per-vertex offset - for (std::vector::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it) - ExtractDataObjectFromChannel(*it, indices[baseOffset + perVertexOffset], pMesh); - // and extract per-index channels using there specified offset - for (std::vector::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) - ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh); - - // store the vertex-data index for later assignment of bone vertex weights - pMesh->mFacePosIndices.push_back(indices[baseOffset + perVertexOffset]); -} - -void ColladaParser::ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Mesh* pMesh, std::vector& pPerIndexChannels, size_t currentPrimitive, const std::vector& indices){ - if (currentPrimitive % 2 != 0){ - //odd tristrip triangles need their indices mangled, to preserve winding direction - CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - } - else {//for non tristrips or even tristrip triangles - CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - } -} - -// ------------------------------------------------------------------------------------------------ -// Extracts a single object from an input channel and stores it in the appropriate mesh data array -void ColladaParser::ExtractDataObjectFromChannel( const InputChannel& pInput, size_t pLocalIndex, Mesh* pMesh) -{ - // ignore vertex referrer - we handle them that separate - if( pInput.mType == IT_Vertex) - return; - - const Accessor& acc = *pInput.mResolved; - if( pLocalIndex >= acc.mCount) - ThrowException( boost::str( boost::format( "Invalid data index (%d/%d) in primitive specification") % pLocalIndex % acc.mCount)); - - // get a pointer to the start of the data object referred to by the accessor and the local index - const float* dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex* acc.mStride; - - // assemble according to the accessors component sub-offset list. We don't care, yet, - // what kind of object exactly we're extracting here - float obj[4]; - for( size_t c = 0; c < 4; ++c) - obj[c] = dataObject[acc.mSubOffset[c]]; - - // now we reinterpret it according to the type we're reading here - switch( pInput.mType) - { - case IT_Position: // ignore all position streams except 0 - there can be only one position - if( pInput.mIndex == 0) - pMesh->mPositions.push_back( aiVector3D( obj[0], obj[1], obj[2])); - else - DefaultLogger::get()->error("Collada: just one vertex position stream supported"); - break; - case IT_Normal: - // pad to current vertex count if necessary - if( pMesh->mNormals.size() < pMesh->mPositions.size()-1) - pMesh->mNormals.insert( pMesh->mNormals.end(), pMesh->mPositions.size() - pMesh->mNormals.size() - 1, aiVector3D( 0, 1, 0)); - - // ignore all normal streams except 0 - there can be only one normal - if( pInput.mIndex == 0) - pMesh->mNormals.push_back( aiVector3D( obj[0], obj[1], obj[2])); - else - DefaultLogger::get()->error("Collada: just one vertex normal stream supported"); - break; - case IT_Tangent: - // pad to current vertex count if necessary - if( pMesh->mTangents.size() < pMesh->mPositions.size()-1) - pMesh->mTangents.insert( pMesh->mTangents.end(), pMesh->mPositions.size() - pMesh->mTangents.size() - 1, aiVector3D( 1, 0, 0)); - - // ignore all tangent streams except 0 - there can be only one tangent - if( pInput.mIndex == 0) - pMesh->mTangents.push_back( aiVector3D( obj[0], obj[1], obj[2])); - else - DefaultLogger::get()->error("Collada: just one vertex tangent stream supported"); - break; - case IT_Bitangent: - // pad to current vertex count if necessary - if( pMesh->mBitangents.size() < pMesh->mPositions.size()-1) - pMesh->mBitangents.insert( pMesh->mBitangents.end(), pMesh->mPositions.size() - pMesh->mBitangents.size() - 1, aiVector3D( 0, 0, 1)); - - // ignore all bitangent streams except 0 - there can be only one bitangent - if( pInput.mIndex == 0) - pMesh->mBitangents.push_back( aiVector3D( obj[0], obj[1], obj[2])); - else - DefaultLogger::get()->error("Collada: just one vertex bitangent stream supported"); - break; - case IT_Texcoord: - // up to 4 texture coord sets are fine, ignore the others - if( pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS) - { - // pad to current vertex count if necessary - if( pMesh->mTexCoords[pInput.mIndex].size() < pMesh->mPositions.size()-1) - pMesh->mTexCoords[pInput.mIndex].insert( pMesh->mTexCoords[pInput.mIndex].end(), - pMesh->mPositions.size() - pMesh->mTexCoords[pInput.mIndex].size() - 1, aiVector3D( 0, 0, 0)); - - pMesh->mTexCoords[pInput.mIndex].push_back( aiVector3D( obj[0], obj[1], obj[2])); - if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) /* hack ... consider cleaner solution */ - pMesh->mNumUVComponents[pInput.mIndex]=3; - } else - { - DefaultLogger::get()->error("Collada: too many texture coordinate sets. Skipping."); - } - break; - case IT_Color: - // up to 4 color sets are fine, ignore the others - if( pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS) - { - // pad to current vertex count if necessary - if( pMesh->mColors[pInput.mIndex].size() < pMesh->mPositions.size()-1) - pMesh->mColors[pInput.mIndex].insert( pMesh->mColors[pInput.mIndex].end(), - pMesh->mPositions.size() - pMesh->mColors[pInput.mIndex].size() - 1, aiColor4D( 0, 0, 0, 1)); - - aiColor4D result(0, 0, 0, 1); - for (size_t i = 0; i < pInput.mResolved->mSize; ++i) - { - result[i] = obj[pInput.mResolved->mSubOffset[i]]; - } - pMesh->mColors[pInput.mIndex].push_back(result); - } else - { - DefaultLogger::get()->error("Collada: too many vertex color sets. Skipping."); - } - - break; - default: - // IT_Invalid and IT_Vertex - ai_assert(false && "shouldn't ever get here"); - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the library of node hierarchies and scene parts -void ColladaParser::ReadSceneLibrary() -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - // a visual scene - generate root node under its ID and let ReadNode() do the recursive work - if( IsElement( "visual_scene")) - { - // read ID. Is optional according to the spec, but how on earth should a scene_instance refer to it then? - int indexID = GetAttribute( "id"); - const char* attrID = mReader->getAttributeValue( indexID); - - // read name if given. - int indexName = TestAttribute( "name"); - const char* attrName = "unnamed"; - if( indexName > -1) - attrName = mReader->getAttributeValue( indexName); - - // create a node and store it in the library under its ID - Node* node = new Node; - node->mID = attrID; - node->mName = attrName; - mNodeLibrary[node->mID] = node; - - ReadSceneNode( node); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "library_visual_scenes") == 0) - //ThrowException( "Expected end of \"library_visual_scenes\" element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a scene node's contents including children and stores it in the given node -void ColladaParser::ReadSceneNode( Node* pNode) -{ - // quit immediately on elements - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "node")) - { - Node* child = new Node; - int attrID = TestAttribute( "id"); - if( attrID > -1) - child->mID = mReader->getAttributeValue( attrID); - int attrSID = TestAttribute( "sid"); - if( attrSID > -1) - child->mSID = mReader->getAttributeValue( attrSID); - - int attrName = TestAttribute( "name"); - if( attrName > -1) - child->mName = mReader->getAttributeValue( attrName); - - // TODO: (thom) support SIDs - // ai_assert( TestAttribute( "sid") == -1); - - if (pNode) - { - pNode->mChildren.push_back( child); - child->mParent = pNode; - } - else - { - // no parent node given, probably called from element. - // create new node in node library - mNodeLibrary[child->mID] = child; - } - - // read on recursively from there - ReadSceneNode( child); - continue; - } - // For any further stuff we need a valid node to work on - else if (!pNode) - continue; - - if( IsElement( "lookat")) - ReadNodeTransformation( pNode, TF_LOOKAT); - else if( IsElement( "matrix")) - ReadNodeTransformation( pNode, TF_MATRIX); - else if( IsElement( "rotate")) - ReadNodeTransformation( pNode, TF_ROTATE); - else if( IsElement( "scale")) - ReadNodeTransformation( pNode, TF_SCALE); - else if( IsElement( "skew")) - ReadNodeTransformation( pNode, TF_SKEW); - else if( IsElement( "translate")) - ReadNodeTransformation( pNode, TF_TRANSLATE); - else if( IsElement( "render") && pNode->mParent == NULL && 0 == pNode->mPrimaryCamera.length()) - { - // ... scene evaluation or, in other words, postprocessing pipeline, - // or, again in other words, a turing-complete description how to - // render a Collada scene. The only thing that is interesting for - // us is the primary camera. - int attrId = TestAttribute("camera_node"); - if (-1 != attrId) - { - const char* s = mReader->getAttributeValue(attrId); - if (s[0] != '#') - DefaultLogger::get()->error("Collada: Unresolved reference format of camera"); - else - pNode->mPrimaryCamera = s+1; - } - } - else if( IsElement( "instance_node")) - { - // find the node in the library - int attrID = TestAttribute( "url"); - if( attrID != -1) - { - const char* s = mReader->getAttributeValue(attrID); - if (s[0] != '#') - DefaultLogger::get()->error("Collada: Unresolved reference format of node"); - else - { - pNode->mNodeInstances.push_back(NodeInstance()); - pNode->mNodeInstances.back().mNode = s+1; - } - } - } - else if( IsElement( "instance_geometry") || IsElement( "instance_controller")) - { - // Reference to a mesh or controller, with possible material associations - ReadNodeGeometry( pNode); - } - else if( IsElement( "instance_light")) - { - // Reference to a light, name given in 'url' attribute - int attrID = TestAttribute("url"); - if (-1 == attrID) - DefaultLogger::get()->warn("Collada: Expected url attribute in element"); - else - { - const char* url = mReader->getAttributeValue( attrID); - if( url[0] != '#') - ThrowException( "Unknown reference format in element"); - - pNode->mLights.push_back(LightInstance()); - pNode->mLights.back().mLight = url+1; - } - } - else if( IsElement( "instance_camera")) - { - // Reference to a camera, name given in 'url' attribute - int attrID = TestAttribute("url"); - if (-1 == attrID) - DefaultLogger::get()->warn("Collada: Expected url attribute in element"); - else - { - const char* url = mReader->getAttributeValue( attrID); - if( url[0] != '#') - ThrowException( "Unknown reference format in element"); - - pNode->mCameras.push_back(CameraInstance()); - pNode->mCameras.back().mCamera = url+1; - } - } - else - { - // skip everything else for the moment - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a node transformation entry of the given type and adds it to the given node's transformation list. -void ColladaParser::ReadNodeTransformation( Node* pNode, TransformType pType) -{ - if( mReader->isEmptyElement()) - return; - - std::string tagName = mReader->getNodeName(); - - Transform tf; - tf.mType = pType; - - // read SID - int indexSID = TestAttribute( "sid"); - if( indexSID >= 0) - tf.mID = mReader->getAttributeValue( indexSID); - - // how many parameters to read per transformation type - static const unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 }; - const char* content = GetTextContent(); - - // read as many parameters and store in the transformation - for( unsigned int a = 0; a < sNumParameters[pType]; a++) - { - // read a number - content = fast_atoreal_move( content, tf.f[a]); - // skip whitespace after it - SkipSpacesAndLineEnd( &content); - } - - // place the transformation at the queue of the node - pNode->mTransforms.push_back( tf); - - // and consume the closing tag - TestClosing( tagName.c_str()); -} - -// ------------------------------------------------------------------------------------------------ -// Processes bind_vertex_input and bind elements -void ColladaParser::ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "bind_vertex_input")) - { - Collada::InputSemanticMapEntry vn; - - // effect semantic - int n = GetAttribute("semantic"); - std::string s = mReader->getAttributeValue(n); - - // input semantic - n = GetAttribute("input_semantic"); - vn.mType = GetTypeForSemantic( mReader->getAttributeValue(n) ); - - // index of input set - n = TestAttribute("input_set"); - if (-1 != n) - vn.mSet = mReader->getAttributeValueAsInt(n); - - tbl.mMap[s] = vn; - } - else if( IsElement( "bind")) { - DefaultLogger::get()->warn("Collada: Found unsupported element"); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "instance_material") == 0) - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a mesh reference in a node and adds it to the node's mesh list -void ColladaParser::ReadNodeGeometry( Node* pNode) -{ - // referred mesh is given as an attribute of the element - int attrUrl = GetAttribute( "url"); - const char* url = mReader->getAttributeValue( attrUrl); - if( url[0] != '#') - ThrowException( "Unknown reference format"); - - Collada::MeshInstance instance; - instance.mMeshOrController = url+1; // skipping the leading # - - if( !mReader->isEmptyElement()) - { - // read material associations. Ignore additional elements inbetween - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "instance_material")) - { - // read ID of the geometry subgroup and the target material - int attrGroup = GetAttribute( "symbol"); - std::string group = mReader->getAttributeValue( attrGroup); - int attrMaterial = GetAttribute( "target"); - const char* urlMat = mReader->getAttributeValue( attrMaterial); - Collada::SemanticMappingTable s; - if( urlMat[0] == '#') - urlMat++; - - s.mMatName = urlMat; - - // resolve further material details + THIS UGLY AND NASTY semantic mapping stuff - if( !mReader->isEmptyElement()) - ReadMaterialVertexInputBinding(s); - - // store the association - instance.mMaterials[group] = s; - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "instance_geometry") == 0 - || strcmp( mReader->getNodeName(), "instance_controller") == 0) - break; - } - } - } - - // store it - pNode->mMeshes.push_back( instance); -} - -// ------------------------------------------------------------------------------------------------ -// Reads the collada scene -void ColladaParser::ReadScene() -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "instance_visual_scene")) - { - // should be the first and only occurence - if( mRootNode) - ThrowException( "Invalid scene containing multiple root nodes in element"); - - // read the url of the scene to instance. Should be of format "#some_name" - int urlIndex = GetAttribute( "url"); - const char* url = mReader->getAttributeValue( urlIndex); - if( url[0] != '#') - ThrowException( "Unknown reference format in element"); - - // find the referred scene, skip the leading # - NodeLibrary::const_iterator sit = mNodeLibrary.find( url+1); - if( sit == mNodeLibrary.end()) - ThrowException( "Unable to resolve visual_scene reference \"" + std::string(url) + "\" in element."); - mRootNode = sit->second; - } else { - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){ - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Aborts the file reading with an exception -AI_WONT_RETURN void ColladaParser::ThrowException( const std::string& pError) const -{ - throw DeadlyImportError( boost::str( boost::format( "Collada: %s - %s") % mFileName % pError)); -} - -// ------------------------------------------------------------------------------------------------ -// Skips all data until the end node of the current element -void ColladaParser::SkipElement() -{ - // nothing to skip if it's an - if( mReader->isEmptyElement()) - return; - - // reroute - SkipElement( mReader->getNodeName()); -} - -// ------------------------------------------------------------------------------------------------ -// Skips all data until the end node of the given element -void ColladaParser::SkipElement( const char* pElement) -{ - // copy the current node's name because it'a pointer to the reader's internal buffer, - // which is going to change with the upcoming parsing - std::string element = pElement; - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - if( mReader->getNodeName() == element) - break; - } -} - -// ------------------------------------------------------------------------------------------------ -// Tests for an opening element of the given name, throws an exception if not found -void ColladaParser::TestOpening( const char* pName) -{ - // read element start - if( !mReader->read()) - ThrowException( boost::str( boost::format( "Unexpected end of file while beginning of <%s> element.") % pName)); - // whitespace in front is ok, just read again if found - if( mReader->getNodeType() == irr::io::EXN_TEXT) - if( !mReader->read()) - ThrowException( boost::str( boost::format( "Unexpected end of file while reading beginning of <%s> element.") % pName)); - - if( mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp( mReader->getNodeName(), pName) != 0) - ThrowException( boost::str( boost::format( "Expected start of <%s> element.") % pName)); -} - -// ------------------------------------------------------------------------------------------------ -// Tests for the closing tag of the given element, throws an exception if not found -void ColladaParser::TestClosing( const char* pName) -{ - // check if we're already on the closing tag and return right away - if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END && strcmp( mReader->getNodeName(), pName) == 0) - return; - - // if not, read some more - if( !mReader->read()) - ThrowException( boost::str( boost::format( "Unexpected end of file while reading end of <%s> element.") % pName)); - // whitespace in front is ok, just read again if found - if( mReader->getNodeType() == irr::io::EXN_TEXT) - if( !mReader->read()) - ThrowException( boost::str( boost::format( "Unexpected end of file while reading end of <%s> element.") % pName)); - - // but this has the be the closing tag, or we're lost - if( mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp( mReader->getNodeName(), pName) != 0) - ThrowException( boost::str( boost::format( "Expected end of <%s> element.") % pName)); -} - -// ------------------------------------------------------------------------------------------------ -// Returns the index of the named attribute or -1 if not found. Does not throw, therefore useful for optional attributes -int ColladaParser::GetAttribute( const char* pAttr) const -{ - int index = TestAttribute( pAttr); - if( index != -1) - return index; - - // attribute not found -> throw an exception - ThrowException( boost::str( boost::format( "Expected attribute \"%s\" for element <%s>.") % pAttr % mReader->getNodeName())); - return -1; -} - -// ------------------------------------------------------------------------------------------------ -// Tests the present element for the presence of one attribute, returns its index or throws an exception if not found -int ColladaParser::TestAttribute( const char* pAttr) const -{ - for( int a = 0; a < mReader->getAttributeCount(); a++) - if( strcmp( mReader->getAttributeName( a), pAttr) == 0) - return a; - - return -1; -} - -// ------------------------------------------------------------------------------------------------ -// Reads the text contents of an element, throws an exception if not given. Skips leading whitespace. -const char* ColladaParser::GetTextContent() -{ - const char* sz = TestTextContent(); - if(!sz) { - ThrowException( "Invalid contents in element \"n\"."); - } - return sz; -} - -// ------------------------------------------------------------------------------------------------ -// Reads the text contents of an element, returns NULL if not given. Skips leading whitespace. -const char* ColladaParser::TestTextContent() -{ - // present node should be the beginning of an element - if( mReader->getNodeType() != irr::io::EXN_ELEMENT || mReader->isEmptyElement()) - return NULL; - - // read contents of the element - if( !mReader->read() ) - return NULL; - if( mReader->getNodeType() != irr::io::EXN_TEXT) - return NULL; - - // skip leading whitespace - const char* text = mReader->getNodeData(); - SkipSpacesAndLineEnd( &text); - - return text; -} - -// ------------------------------------------------------------------------------------------------ -// Calculates the resulting transformation fromm all the given transform steps -aiMatrix4x4 ColladaParser::CalculateResultTransform( const std::vector& pTransforms) const -{ - aiMatrix4x4 res; - - for( std::vector::const_iterator it = pTransforms.begin(); it != pTransforms.end(); ++it) - { - const Transform& tf = *it; - switch( tf.mType) - { - case TF_LOOKAT: - { - aiVector3D pos( tf.f[0], tf.f[1], tf.f[2]); - aiVector3D dstPos( tf.f[3], tf.f[4], tf.f[5]); - aiVector3D up = aiVector3D( tf.f[6], tf.f[7], tf.f[8]).Normalize(); - aiVector3D dir = aiVector3D( dstPos - pos).Normalize(); - aiVector3D right = (dir ^ up).Normalize(); - - res *= aiMatrix4x4( - right.x, up.x, -dir.x, pos.x, - right.y, up.y, -dir.y, pos.y, - right.z, up.z, -dir.z, pos.z, - 0, 0, 0, 1); - break; - } - case TF_ROTATE: - { - aiMatrix4x4 rot; - float angle = tf.f[3] * float( AI_MATH_PI) / 180.0f; - aiVector3D axis( tf.f[0], tf.f[1], tf.f[2]); - aiMatrix4x4::Rotation( angle, axis, rot); - res *= rot; - break; - } - case TF_TRANSLATE: - { - aiMatrix4x4 trans; - aiMatrix4x4::Translation( aiVector3D( tf.f[0], tf.f[1], tf.f[2]), trans); - res *= trans; - break; - } - case TF_SCALE: - { - aiMatrix4x4 scale( tf.f[0], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[1], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[2], 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f); - res *= scale; - break; - } - case TF_SKEW: - // TODO: (thom) - ai_assert( false); - break; - case TF_MATRIX: - { - aiMatrix4x4 mat( tf.f[0], tf.f[1], tf.f[2], tf.f[3], tf.f[4], tf.f[5], tf.f[6], tf.f[7], - tf.f[8], tf.f[9], tf.f[10], tf.f[11], tf.f[12], tf.f[13], tf.f[14], tf.f[15]); - res *= mat; - break; - } - default: - ai_assert( false); - break; - } - } - - return res; -} - -// ------------------------------------------------------------------------------------------------ -// Determines the input data type for the given semantic string -Collada::InputType ColladaParser::GetTypeForSemantic( const std::string& pSemantic) -{ - if( pSemantic == "POSITION") - return IT_Position; - else if( pSemantic == "TEXCOORD") - return IT_Texcoord; - else if( pSemantic == "NORMAL") - return IT_Normal; - else if( pSemantic == "COLOR") - return IT_Color; - else if( pSemantic == "VERTEX") - return IT_Vertex; - else if( pSemantic == "BINORMAL" || pSemantic == "TEXBINORMAL") - return IT_Bitangent; - else if( pSemantic == "TANGENT" || pSemantic == "TEXTANGENT") - return IT_Tangent; - - DefaultLogger::get()->warn( boost::str( boost::format( "Unknown vertex input type \"%s\". Ignoring.") % pSemantic)); - return IT_Invalid; -} - -#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER diff --git a/code/ColladaParser upstream.h b/code/ColladaParser upstream.h deleted file mode 100644 index 6f99681be..000000000 --- a/code/ColladaParser upstream.h +++ /dev/null @@ -1,352 +0,0 @@ -/* - Open Asset Import Library (assimp) - ---------------------------------------------------------------------- - - Copyright (c) 2006-2015, assimp team - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - ---------------------------------------------------------------------- - */ - -/** @file ColladaParser.h - * @brief Defines the parser helper class for the collada loader - */ - -#ifndef AI_COLLADAPARSER_H_INC -#define AI_COLLADAPARSER_H_INC - -#include "irrXMLWrapper.h" -#include "ColladaHelper.h" -#include "../include/assimp/ai_assert.h" -#include - -namespace Assimp -{ - - // ------------------------------------------------------------------------------------------ - /** Parser helper class for the Collada loader. - * - * Does all the XML reading and builds internal data structures from it, - * but leaves the resolving of all the references to the loader. - */ - class ColladaParser - { - friend class ColladaLoader; - - protected: - /** Constructor from XML file */ - ColladaParser( IOSystem* pIOHandler, const std::string& pFile); - - /** Destructor */ - ~ColladaParser(); - - /** Reads the contents of the file */ - void ReadContents(); - - /** Reads the structure of the file */ - void ReadStructure(); - - /** Reads asset informations such as coordinate system informations and legal blah */ - void ReadAssetInfo(); - - /** Reads the animation library */ - void ReadAnimationLibrary(); - - /** Reads an animation into the given parent structure */ - void ReadAnimation( Collada::Animation* pParent); - - /** Reads an animation sampler into the given anim channel */ - void ReadAnimationSampler( Collada::AnimationChannel& pChannel); - - /** Reads the skeleton controller library */ - void ReadControllerLibrary(); - - /** Reads a controller into the given mesh structure */ - void ReadController( Collada::Controller& pController); - - /** Reads the joint definitions for the given controller */ - void ReadControllerJoints( Collada::Controller& pController); - - /** Reads the joint weights for the given controller */ - void ReadControllerWeights( Collada::Controller& pController); - - /** Reads the image library contents */ - void ReadImageLibrary(); - - /** Reads an image entry into the given image */ - void ReadImage( Collada::Image& pImage); - - /** Reads the material library */ - void ReadMaterialLibrary(); - - /** Reads a material entry into the given material */ - void ReadMaterial( Collada::Material& pMaterial); - - /** Reads the camera library */ - void ReadCameraLibrary(); - - /** Reads a camera entry into the given camera */ - void ReadCamera( Collada::Camera& pCamera); - - /** Reads the light library */ - void ReadLightLibrary(); - - /** Reads a light entry into the given light */ - void ReadLight( Collada::Light& pLight); - - /** Reads the effect library */ - void ReadEffectLibrary(); - - /** Reads an effect entry into the given effect*/ - void ReadEffect( Collada::Effect& pEffect); - - /** Reads an COMMON effect profile */ - void ReadEffectProfileCommon( Collada::Effect& pEffect); - - /** Read sampler properties */ - void ReadSamplerProperties( Collada::Sampler& pSampler); - - /** Reads an effect entry containing a color or a texture defining that color */ - void ReadEffectColor( aiColor4D& pColor, Collada::Sampler& pSampler); - - /** Reads an effect entry containing a float */ - void ReadEffectFloat( float& pFloat); - - /** Reads an effect parameter specification of any kind */ - void ReadEffectParam( Collada::EffectParam& pParam); - - /** Reads the geometry library contents */ - void ReadGeometryLibrary(); - - /** Reads a geometry from the geometry library. */ - void ReadGeometry( Collada::Mesh* pMesh); - - /** Reads a mesh from the geometry library */ - void ReadMesh( Collada::Mesh* pMesh); - - /** Reads a source element - a combination of raw data and an accessor defining - * things that should not be redefinable. Yes, that's another rant. - */ - void ReadSource(); - - /** Reads a data array holding a number of elements, and stores it in the global library. - * Currently supported are array of floats and arrays of strings. - */ - void ReadDataArray(); - - /** Reads an accessor and stores it in the global library under the given ID - - * accessors use the ID of the parent element - */ - void ReadAccessor( const std::string& pID); - - /** Reads input declarations of per-vertex mesh data into the given mesh */ - void ReadVertexData( Collada::Mesh* pMesh); - - /** Reads input declarations of per-index mesh data into the given mesh */ - void ReadIndexData( Collada::Mesh* pMesh); - - /** Reads a single input channel element and stores it in the given array, if valid */ - void ReadInputChannel( std::vector& poChannels); - - /** Reads a

primitive index list and assembles the mesh data into the given mesh */ - size_t ReadPrimitives( Collada::Mesh* pMesh, std::vector& pPerIndexChannels, - size_t pNumPrimitives, const std::vector& pVCount, Collada::PrimitiveType pPrimType); - - /** Copies the data for a single primitive into the mesh, based on the InputChannels */ - void CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset, - Collada::Mesh* pMesh, std::vector& pPerIndexChannels, - size_t currentPrimitive, const std::vector& indices); - - /** Reads one triangle of a tristrip into the mesh */ - void ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Collada::Mesh* pMesh, - std::vector& pPerIndexChannels, size_t currentPrimitive, const std::vector& indices); - - /** Extracts a single object from an input channel and stores it in the appropriate mesh data array */ - void ExtractDataObjectFromChannel( const Collada::InputChannel& pInput, size_t pLocalIndex, Collada::Mesh* pMesh); - - /** Reads the library of node hierarchies and scene parts */ - void ReadSceneLibrary(); - - /** Reads a scene node's contents including children and stores it in the given node */ - void ReadSceneNode( Collada::Node* pNode); - - /** Reads a node transformation entry of the given type and adds it to the given node's transformation list. */ - void ReadNodeTransformation( Collada::Node* pNode, Collada::TransformType pType); - - /** Reads a mesh reference in a node and adds it to the node's mesh list */ - void ReadNodeGeometry( Collada::Node* pNode); - - /** Reads the collada scene */ - void ReadScene(); - - // Processes bind_vertex_input and bind elements - void ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl); - - protected: - /** Aborts the file reading with an exception */ - AI_WONT_RETURN void ThrowException( const std::string& pError) const AI_WONT_RETURN_SUFFIX; - - /** Skips all data until the end node of the current element */ - void SkipElement(); - - /** Skips all data until the end node of the given element */ - void SkipElement( const char* pElement); - - /** Compares the current xml element name to the given string and returns true if equal */ - bool IsElement( const char* pName) const; - - /** Tests for the opening tag of the given element, throws an exception if not found */ - void TestOpening( const char* pName); - - /** Tests for the closing tag of the given element, throws an exception if not found */ - void TestClosing( const char* pName); - - /** Checks the present element for the presence of the attribute, returns its index - or throws an exception if not found */ - int GetAttribute( const char* pAttr) const; - - /** Returns the index of the named attribute or -1 if not found. Does not throw, - therefore useful for optional attributes */ - int TestAttribute( const char* pAttr) const; - - /** Reads the text contents of an element, throws an exception if not given. - Skips leading whitespace. */ - const char* GetTextContent(); - - /** Reads the text contents of an element, returns NULL if not given. - Skips leading whitespace. */ - const char* TestTextContent(); - - /** Reads a single bool from current text content */ - bool ReadBoolFromTextContent(); - - /** Reads a single float from current text content */ - float ReadFloatFromTextContent(); - - /** Calculates the resulting transformation from all the given transform steps */ - aiMatrix4x4 CalculateResultTransform( const std::vector& pTransforms) const; - - /** Determines the input data type for the given semantic string */ - Collada::InputType GetTypeForSemantic( const std::string& pSemantic); - - /** Finds the item in the given library by its reference, throws if not found */ - template const Type& ResolveLibraryReference( - const std::map& pLibrary, const std::string& pURL) const; - - protected: - /** Filename, for a verbose error message */ - std::string mFileName; - - /** XML reader, member for everyday use */ - irr::io::IrrXMLReader* mReader; - - /** All data arrays found in the file by ID. Might be referred to by actually - everyone. Collada, you are a steaming pile of indirection. */ - typedef std::map DataLibrary; - DataLibrary mDataLibrary; - - /** Same for accessors which define how the data in a data array is accessed. */ - typedef std::map AccessorLibrary; - AccessorLibrary mAccessorLibrary; - - /** Mesh library: mesh by ID */ - typedef std::map MeshLibrary; - MeshLibrary mMeshLibrary; - - /** node library: root node of the hierarchy part by ID */ - typedef std::map NodeLibrary; - NodeLibrary mNodeLibrary; - - /** Image library: stores texture properties by ID */ - typedef std::map ImageLibrary; - ImageLibrary mImageLibrary; - - /** Effect library: surface attributes by ID */ - typedef std::map EffectLibrary; - EffectLibrary mEffectLibrary; - - /** Material library: surface material by ID */ - typedef std::map MaterialLibrary; - MaterialLibrary mMaterialLibrary; - - /** Light library: surface light by ID */ - typedef std::map LightLibrary; - LightLibrary mLightLibrary; - - /** Camera library: surface material by ID */ - typedef std::map CameraLibrary; - CameraLibrary mCameraLibrary; - - /** Controller library: joint controllers by ID */ - typedef std::map ControllerLibrary; - ControllerLibrary mControllerLibrary; - - /** Pointer to the root node. Don't delete, it just points to one of - the nodes in the node library. */ - Collada::Node* mRootNode; - - /** Root animation container */ - Collada::Animation mAnims; - - /** Size unit: how large compared to a meter */ - float mUnitSize; - - /** Which is the up vector */ - enum { UP_X, UP_Y, UP_Z } mUpDirection; - - /** Collada file format version */ - Collada::FormatVersion mFormat; - }; - - // ------------------------------------------------------------------------------------------------ - // Check for element match - inline bool ColladaParser::IsElement( const char* pName) const - { - ai_assert( mReader->getNodeType() == irr::io::EXN_ELEMENT); - return ::strcmp( mReader->getNodeName(), pName) == 0; - } - - // ------------------------------------------------------------------------------------------------ - // Finds the item in the given library by its reference, throws if not found - template - const Type& ColladaParser::ResolveLibraryReference( const std::map& pLibrary, const std::string& pURL) const - { - typename std::map::const_iterator it = pLibrary.find( pURL); - if( it == pLibrary.end()) - ThrowException( boost::str( boost::format( "Unable to resolve library reference \"%s\".") % pURL)); - return it->second; - } - -} // end of namespace Assimp - -#endif // AI_COLLADAPARSER_H_INC diff --git a/code/ColladaParser wil.cpp b/code/ColladaParser wil.cpp deleted file mode 100644 index daea1d70e..000000000 --- a/code/ColladaParser wil.cpp +++ /dev/null @@ -1,2933 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2012, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file ColladaParser.cpp - * @brief Implementation of the Collada parser helper - */ - -#include "AssimpPCH.h" -#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER - -#include "ColladaParser.h" -#include "fast_atof.h" -#include "ParsingUtils.h" - -using namespace Assimp; -using namespace Assimp::Collada; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile) - : mFileName( pFile) -{ - mRootNode = NULL; - mUnitSize = 1.0f; - mUpDirection = UP_Y; - - // We assume the newest file format by default - mFormat = FV_1_5_n; - - // open the file - boost::scoped_ptr file( pIOHandler->Open( pFile)); - if( file.get() == NULL) - throw DeadlyImportError( "Failed to open file " + pFile + "."); - - // generate a XML reader for it - boost::scoped_ptr mIOWrapper( new CIrrXML_IOStreamReader( file.get())); - mReader = irr::io::createIrrXMLReader( mIOWrapper.get()); - if( !mReader) - ThrowException( "Collada: Unable to open file."); - - // start reading - ReadContents(); -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -ColladaParser::~ColladaParser() -{ - delete mReader; - for( NodeLibrary::iterator it = mNodeLibrary.begin(); it != mNodeLibrary.end(); ++it) - delete it->second; - for( MeshLibrary::iterator it = mMeshLibrary.begin(); it != mMeshLibrary.end(); ++it) - delete it->second; -} - -// ------------------------------------------------------------------------------------------------ -// Read bool from text contents of current element -bool ColladaParser::ReadBoolFromTextContent() -{ - const char* cur = GetTextContent(); - return (!ASSIMP_strincmp(cur,"true",4) || '0' != *cur); -} - -// ------------------------------------------------------------------------------------------------ -// Read float from text contents of current element -float ColladaParser::ReadFloatFromTextContent() -{ - const char* cur = GetTextContent(); - return fast_atof(cur); -} - -// ------------------------------------------------------------------------------------------------ -// Reads the contents of the file -void ColladaParser::ReadContents() -{ - while( mReader->read()) - { - // handle the root element "COLLADA" - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "COLLADA")) - { - // check for 'version' attribute - const int attrib = TestAttribute("version"); - if (attrib != -1) { - const char* version = mReader->getAttributeValue(attrib); - - if (!::strncmp(version,"1.5",3)) { - mFormat = FV_1_5_n; - DefaultLogger::get()->debug("Collada schema version is 1.5.n"); - } - else if (!::strncmp(version,"1.4",3)) { - mFormat = FV_1_4_n; - DefaultLogger::get()->debug("Collada schema version is 1.4.n"); - } - else if (!::strncmp(version,"1.3",3)) { - mFormat = FV_1_3_n; - DefaultLogger::get()->debug("Collada schema version is 1.3.n"); - } - } - - ReadStructure(); - } else - { - DefaultLogger::get()->debug( boost::str( boost::format( "Ignoring global element <%s>.") % mReader->getNodeName())); - SkipElement(); - } - } else - { - // skip everything else silently - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the structure of the file -void ColladaParser::ReadStructure() -{ - while( mReader->read()) - { - // beginning of elements - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "asset")) - ReadAssetInfo(); - else if( IsElement( "library_animations")) - ReadAnimationLibrary(); - else if( IsElement( "library_controllers")) - ReadControllerLibrary(); - else if( IsElement( "library_images")) - ReadImageLibrary(); - else if( IsElement( "library_materials")) - ReadMaterialLibrary(); - else if( IsElement( "library_effects")) - ReadEffectLibrary(); - else if( IsElement( "library_geometries")) - ReadGeometryLibrary(); - else if( IsElement( "library_visual_scenes")) - ReadSceneLibrary(); - else if( IsElement( "library_lights")) - ReadLightLibrary(); - else if( IsElement( "library_cameras")) - ReadCameraLibrary(); - else if( IsElement( "library_nodes")) - ReadSceneNode(NULL); /* some hacking to reuse this piece of code */ - else if( IsElement( "scene")) - ReadScene(); - else - SkipElement(); - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads asset informations such as coordinate system informations and legal blah -void ColladaParser::ReadAssetInfo() -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "unit")) - { - // read unit data from the element's attributes - const int attrIndex = TestAttribute( "meter"); - if (attrIndex == -1) { - mUnitSize = 1.f; - } - else { - mUnitSize = mReader->getAttributeValueAsFloat( attrIndex); - } - - // consume the trailing stuff - if( !mReader->isEmptyElement()) - SkipElement(); - } - else if( IsElement( "up_axis")) - { - // read content, strip whitespace, compare - const char* content = GetTextContent(); - if( strncmp( content, "X_UP", 4) == 0) - mUpDirection = UP_X; - else if( strncmp( content, "Z_UP", 4) == 0) - mUpDirection = UP_Z; - else - mUpDirection = UP_Y; - - // check element end - TestClosing( "up_axis"); - } else - { - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "asset") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the animation library -void ColladaParser::ReadAnimationLibrary() -{ - if (mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "animation")) - { - // delegate the reading. Depending on the inner elements it will be a container or a anim channel - ReadAnimation( &mAnims); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "library_animations") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an animation into the given parent structure -void ColladaParser::ReadAnimation( Collada::Animation* pParent) -{ - if( mReader->isEmptyElement()) - return; - - // an element may be a container for grouping sub-elements or an animation channel - // this is the channel collection by ID, in case it has channels - typedef std::map ChannelMap; - ChannelMap channels; - // this is the anim container in case we're a container - Animation* anim = NULL; - - // optional name given as an attribute - std::string animName; - int indexName = TestAttribute( "name"); - int indexID = TestAttribute( "id"); - if( indexName >= 0) - animName = mReader->getAttributeValue( indexName); - else if( indexID >= 0) - animName = mReader->getAttributeValue( indexID); - else - animName = "animation"; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - // we have subanimations - if( IsElement( "animation")) - { - // create container from our element - if( !anim) - { - anim = new Animation; - anim->mName = animName; - pParent->mSubAnims.push_back( anim); - } - - // recurse into the subelement - ReadAnimation( anim); - } - else if( IsElement( "source")) - { - // possible animation data - we'll never know. Better store it - ReadSource(); - } - else if( IsElement( "sampler")) - { - // read the ID to assign the corresponding collada channel afterwards. - int indexID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( indexID); - ChannelMap::iterator newChannel = channels.insert( std::make_pair( id, AnimationChannel())).first; - - // have it read into a channel - ReadAnimationSampler( newChannel->second); - } - else if( IsElement( "channel")) - { - // the binding element whose whole purpose is to provide the target to animate - // Thanks, Collada! A directly posted information would have been too simple, I guess. - // Better add another indirection to that! Can't have enough of those. - int indexTarget = GetAttribute( "target"); - int indexSource = GetAttribute( "source"); - const char* sourceId = mReader->getAttributeValue( indexSource); - if( sourceId[0] == '#') - sourceId++; - ChannelMap::iterator cit = channels.find( sourceId); - if( cit != channels.end()) - cit->second.mTarget = mReader->getAttributeValue( indexTarget); - - if( !mReader->isEmptyElement()) - SkipElement(); - } - else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "animation") != 0) - ThrowException( "Expected end of element."); - - break; - } - } - - // it turned out to have channels - add them - if( !channels.empty()) - { - // special filtering for stupid exporters packing each channel into a separate animation - if( channels.size() == 1) - { - pParent->mChannels.push_back( channels.begin()->second); - } else - { - // else create the animation, if not done yet, and store the channels - if( !anim) - { - anim = new Animation; - anim->mName = animName; - pParent->mSubAnims.push_back( anim); - } - for( ChannelMap::const_iterator it = channels.begin(); it != channels.end(); ++it) - anim->mChannels.push_back( it->second); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an animation sampler into the given anim channel -void ColladaParser::ReadAnimationSampler( Collada::AnimationChannel& pChannel) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "input")) - { - int indexSemantic = GetAttribute( "semantic"); - const char* semantic = mReader->getAttributeValue( indexSemantic); - int indexSource = GetAttribute( "source"); - const char* source = mReader->getAttributeValue( indexSource); - if( source[0] != '#') - ThrowException( "Unsupported URL format"); - source++; - - if( strcmp( semantic, "INPUT") == 0) - pChannel.mSourceTimes = source; - else if( strcmp( semantic, "OUTPUT") == 0) - pChannel.mSourceValues = source; - - if( !mReader->isEmptyElement()) - SkipElement(); - } - else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "sampler") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the skeleton controller library -void ColladaParser::ReadControllerLibrary() -{ - if (mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "controller")) - { - // read ID. Ask the spec if it's neccessary or optional... you might be surprised. - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); - - // create an entry and store it in the library under its ID - mControllerLibrary[id] = Controller(); - - // read on from there - ReadController( mControllerLibrary[id]); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "library_controllers") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a controller into the given mesh structure -void ColladaParser::ReadController( Collada::Controller& pController) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - // two types of controllers: "skin" and "morph". Only the first one is relevant, we skip the other - if( IsElement( "morph")) - { - // should skip everything inside, so there's no danger of catching elements inbetween - SkipElement(); - } - else if( IsElement( "skin")) - { - // read the mesh it refers to. According to the spec this could also be another - // controller, but I refuse to implement every single idea they've come up with - int sourceIndex = GetAttribute( "source"); - pController.mMeshId = mReader->getAttributeValue( sourceIndex) + 1; - } - else if( IsElement( "bind_shape_matrix")) - { - // content is 16 floats to define a matrix... it seems to be important for some models - const char* content = GetTextContent(); - - // read the 16 floats - for( unsigned int a = 0; a < 16; a++) - { - // read a number - content = fast_atoreal_move( content, pController.mBindShapeMatrix[a]); - // skip whitespace after it - SkipSpacesAndLineEnd( &content); - } - - TestClosing( "bind_shape_matrix"); - } - else if( IsElement( "source")) - { - // data array - we have specialists to handle this - ReadSource(); - } - else if( IsElement( "joints")) - { - ReadControllerJoints( pController); - } - else if( IsElement( "vertex_weights")) - { - ReadControllerWeights( pController); - } - else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "controller") == 0) - break; - else if( strcmp( mReader->getNodeName(), "skin") != 0) - ThrowException( "Expected end of element."); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the joint definitions for the given controller -void ColladaParser::ReadControllerJoints( Collada::Controller& pController) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - // Input channels for joint data. Two possible semantics: "JOINT" and "INV_BIND_MATRIX" - if( IsElement( "input")) - { - int indexSemantic = GetAttribute( "semantic"); - const char* attrSemantic = mReader->getAttributeValue( indexSemantic); - int indexSource = GetAttribute( "source"); - const char* attrSource = mReader->getAttributeValue( indexSource); - - // local URLS always start with a '#'. We don't support global URLs - if( attrSource[0] != '#') - ThrowException( boost::str( boost::format( "Unsupported URL format in \"%s\" in source attribute of data element") % attrSource)); - attrSource++; - - // parse source URL to corresponding source - if( strcmp( attrSemantic, "JOINT") == 0) - pController.mJointNameSource = attrSource; - else if( strcmp( attrSemantic, "INV_BIND_MATRIX") == 0) - pController.mJointOffsetMatrixSource = attrSource; - else - ThrowException( boost::str( boost::format( "Unknown semantic \"%s\" in data element") % attrSemantic)); - - // skip inner data, if present - if( !mReader->isEmptyElement()) - SkipElement(); - } - else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "joints") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the joint weights for the given controller -void ColladaParser::ReadControllerWeights( Collada::Controller& pController) -{ - // read vertex count from attributes and resize the array accordingly - int indexCount = GetAttribute( "count"); - size_t vertexCount = (size_t) mReader->getAttributeValueAsInt( indexCount); - pController.mWeightCounts.resize( vertexCount); - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - // Input channels for weight data. Two possible semantics: "JOINT" and "WEIGHT" - if( IsElement( "input") && vertexCount > 0 ) - { - InputChannel channel; - - int indexSemantic = GetAttribute( "semantic"); - const char* attrSemantic = mReader->getAttributeValue( indexSemantic); - int indexSource = GetAttribute( "source"); - const char* attrSource = mReader->getAttributeValue( indexSource); - int indexOffset = TestAttribute( "offset"); - if( indexOffset >= 0) - channel.mOffset = mReader->getAttributeValueAsInt( indexOffset); - - // local URLS always start with a '#'. We don't support global URLs - if( attrSource[0] != '#') - ThrowException( boost::str( boost::format( "Unsupported URL format in \"%s\" in source attribute of data element") % attrSource)); - channel.mAccessor = attrSource + 1; - - // parse source URL to corresponding source - if( strcmp( attrSemantic, "JOINT") == 0) - pController.mWeightInputJoints = channel; - else if( strcmp( attrSemantic, "WEIGHT") == 0) - pController.mWeightInputWeights = channel; - else - ThrowException( boost::str( boost::format( "Unknown semantic \"%s\" in data element") % attrSemantic)); - - // skip inner data, if present - if( !mReader->isEmptyElement()) - SkipElement(); - } - else if( IsElement( "vcount") && vertexCount > 0 ) - { - // read weight count per vertex - const char* text = GetTextContent(); - size_t numWeights = 0; - for( std::vector::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it) - { - if( *text == 0) - ThrowException( "Out of data while reading "); - - *it = strtoul10( text, &text); - numWeights += *it; - SkipSpacesAndLineEnd( &text); - } - - TestClosing( "vcount"); - - // reserve weight count - pController.mWeights.resize( numWeights); - } - else if( IsElement( "v") && vertexCount > 0 ) - { - // read JointIndex - WeightIndex pairs - const char* text = GetTextContent(); - - for( std::vector< std::pair >::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it) - { - if( *text == 0) - ThrowException( "Out of data while reading "); - it->first = strtoul10( text, &text); - SkipSpacesAndLineEnd( &text); - if( *text == 0) - ThrowException( "Out of data while reading "); - it->second = strtoul10( text, &text); - SkipSpacesAndLineEnd( &text); - } - - TestClosing( "v"); - } - else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "vertex_weights") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the image library contents -void ColladaParser::ReadImageLibrary() -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "image")) - { - // read ID. Another entry which is "optional" by design but obligatory in reality - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); - - // create an entry and store it in the library under its ID - mImageLibrary[id] = Image(); - - // read on from there - ReadImage( mImageLibrary[id]); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "library_images") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an image entry into the given image -void ColladaParser::ReadImage( Collada::Image& pImage) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT){ - // Need to run different code paths here, depending on the Collada XSD version - if (IsElement("image")) { - SkipElement(); - } - else if( IsElement( "init_from")) - { - if (mFormat == FV_1_4_n) - { - // FIX: C4D exporter writes empty tags - if (!mReader->isEmptyElement()) { - // element content is filename - hopefully - const char* sz = TestTextContent(); - if (sz)pImage.mFileName = sz; - TestClosing( "init_from"); - } - if (!pImage.mFileName.length()) { - pImage.mFileName = "unknown_texture"; - } - } - else if (mFormat == FV_1_5_n) - { - // make sure we skip over mip and array initializations, which - // we don't support, but which could confuse the loader if - // they're not skipped. - int attrib = TestAttribute("array_index"); - if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) { - DefaultLogger::get()->warn("Collada: Ignoring texture array index"); - continue; - } - - attrib = TestAttribute("mip_index"); - if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) { - DefaultLogger::get()->warn("Collada: Ignoring MIP map layer"); - continue; - } - - // TODO: correctly jump over cube and volume maps? - } - } - else if (mFormat == FV_1_5_n) - { - if( IsElement( "ref")) - { - // element content is filename - hopefully - const char* sz = TestTextContent(); - if (sz)pImage.mFileName = sz; - TestClosing( "ref"); - } - else if( IsElement( "hex") && !pImage.mFileName.length()) - { - // embedded image. get format - const int attrib = TestAttribute("format"); - if (-1 == attrib) - DefaultLogger::get()->warn("Collada: Unknown image file format"); - else pImage.mEmbeddedFormat = mReader->getAttributeValue(attrib); - - const char* data = GetTextContent(); - - // hexadecimal-encoded binary octets. First of all, find the - // required buffer size to reserve enough storage. - const char* cur = data; - while (!IsSpaceOrNewLine(*cur)) cur++; - - const unsigned int size = (unsigned int)(cur-data) * 2; - pImage.mImageData.resize(size); - for (unsigned int i = 0; i < size;++i) - pImage.mImageData[i] = HexOctetToDecimal(data+(i<<1)); - - TestClosing( "hex"); - } - } - else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "image") == 0) - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the material library -void ColladaParser::ReadMaterialLibrary() -{ - if( mReader->isEmptyElement()) - return; - - std::map names; - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "material")) - { - // read ID. By now you propably know my opinion about this "specification" - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); - - std::string name; - int attrName = TestAttribute("name"); - if (attrName >= 0) - name = mReader->getAttributeValue( attrName); - - // create an entry and store it in the library under its ID - mMaterialLibrary[id] = Material(); - - if( !name.empty()) - { - std::map::iterator it = names.find( name); - if( it != names.end()) - { - std::ostringstream strStream; - strStream << ++it->second; - name.append( " " + strStream.str()); - } - else - { - names[name] = 0; - } - - mMaterialLibrary[id].mName = name; - } - - ReadMaterial( mMaterialLibrary[id]); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "library_materials") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the light library -void ColladaParser::ReadLightLibrary() -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "light")) - { - // read ID. By now you propably know my opinion about this "specification" - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); - - // create an entry and store it in the library under its ID - ReadLight(mLightLibrary[id] = Light()); - - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "library_lights") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the camera library -void ColladaParser::ReadCameraLibrary() -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "camera")) - { - // read ID. By now you propably know my opinion about this "specification" - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); - - // create an entry and store it in the library under its ID - Camera& cam = mCameraLibrary[id]; - attrID = TestAttribute( "name"); - if (attrID != -1) - cam.mName = mReader->getAttributeValue( attrID); - - ReadCamera(cam); - - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "library_cameras") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a material entry into the given material -void ColladaParser::ReadMaterial( Collada::Material& pMaterial) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if (IsElement("material")) { - SkipElement(); - } - else if( IsElement( "instance_effect")) - { - // referred effect by URL - int attrUrl = GetAttribute( "url"); - const char* url = mReader->getAttributeValue( attrUrl); - if( url[0] != '#') - ThrowException( "Unknown reference format"); - - pMaterial.mEffect = url+1; - - SkipElement(); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "material") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a light entry into the given light -void ColladaParser::ReadLight( Collada::Light& pLight) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if (IsElement("light")) { - SkipElement(); - } - else if (IsElement("spot")) { - pLight.mType = aiLightSource_SPOT; - } - else if (IsElement("ambient")) { - pLight.mType = aiLightSource_AMBIENT; - } - else if (IsElement("directional")) { - pLight.mType = aiLightSource_DIRECTIONAL; - } - else if (IsElement("point")) { - pLight.mType = aiLightSource_POINT; - } - else if (IsElement("color")) { - // text content contains 3 floats - const char* content = GetTextContent(); - - content = fast_atoreal_move( content, (float&)pLight.mColor.r); - SkipSpacesAndLineEnd( &content); - - content = fast_atoreal_move( content, (float&)pLight.mColor.g); - SkipSpacesAndLineEnd( &content); - - content = fast_atoreal_move( content, (float&)pLight.mColor.b); - SkipSpacesAndLineEnd( &content); - - TestClosing( "color"); - } - else if (IsElement("constant_attenuation")) { - pLight.mAttConstant = ReadFloatFromTextContent(); - TestClosing("constant_attenuation"); - } - else if (IsElement("linear_attenuation")) { - pLight.mAttLinear = ReadFloatFromTextContent(); - TestClosing("linear_attenuation"); - } - else if (IsElement("quadratic_attenuation")) { - pLight.mAttQuadratic = ReadFloatFromTextContent(); - TestClosing("quadratic_attenuation"); - } - else if (IsElement("falloff_angle")) { - pLight.mFalloffAngle = ReadFloatFromTextContent(); - TestClosing("falloff_angle"); - } - else if (IsElement("falloff_exponent")) { - pLight.mFalloffExponent = ReadFloatFromTextContent(); - TestClosing("falloff_exponent"); - } - // FCOLLADA extensions - // ------------------------------------------------------- - else if (IsElement("outer_cone")) { - pLight.mOuterAngle = ReadFloatFromTextContent(); - TestClosing("outer_cone"); - } - // ... and this one is even deprecated - else if (IsElement("penumbra_angle")) { - pLight.mPenumbraAngle = ReadFloatFromTextContent(); - TestClosing("penumbra_angle"); - } - else if (IsElement("intensity")) { - pLight.mIntensity = ReadFloatFromTextContent(); - TestClosing("intensity"); - } - else if (IsElement("falloff")) { - pLight.mOuterAngle = ReadFloatFromTextContent(); - TestClosing("falloff"); - } - else if (IsElement("hotspot_beam")) { - pLight.mFalloffAngle = ReadFloatFromTextContent(); - TestClosing("hotspot_beam"); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "light") == 0) - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a camera entry into the given light -void ColladaParser::ReadCamera( Collada::Camera& pCamera) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if (IsElement("camera")) { - SkipElement(); - } - else if (IsElement("orthographic")) { - pCamera.mOrtho = true; - } - else if (IsElement("xfov") || IsElement("xmag")) { - pCamera.mHorFov = ReadFloatFromTextContent(); - TestClosing((pCamera.mOrtho ? "xmag" : "xfov")); - } - else if (IsElement("yfov") || IsElement("ymag")) { - pCamera.mVerFov = ReadFloatFromTextContent(); - TestClosing((pCamera.mOrtho ? "ymag" : "yfov")); - } - else if (IsElement("aspect_ratio")) { - pCamera.mAspect = ReadFloatFromTextContent(); - TestClosing("aspect_ratio"); - } - else if (IsElement("znear")) { - pCamera.mZNear = ReadFloatFromTextContent(); - TestClosing("znear"); - } - else if (IsElement("zfar")) { - pCamera.mZFar = ReadFloatFromTextContent(); - TestClosing("zfar"); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "camera") == 0) - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the effect library -void ColladaParser::ReadEffectLibrary() -{ - if (mReader->isEmptyElement()) { - return; - } - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "effect")) - { - // read ID. Do I have to repeat my ranting about "optional" attributes? - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); - - // create an entry and store it in the library under its ID - mEffectLibrary[id] = Effect(); - // read on from there - ReadEffect( mEffectLibrary[id]); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "library_effects") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an effect entry into the given effect -void ColladaParser::ReadEffect( Collada::Effect& pEffect) -{ - // for the moment we don't support any other type of effect. - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "profile_COMMON")) - ReadEffectProfileCommon( pEffect); - else - SkipElement(); - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "effect") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an COMMON effect profile -void ColladaParser::ReadEffectProfileCommon( Collada::Effect& pEffect) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "newparam")) { - // save ID - int attrSID = GetAttribute( "sid"); - std::string sid = mReader->getAttributeValue( attrSID); - pEffect.mParams[sid] = EffectParam(); - ReadEffectParam( pEffect.mParams[sid]); - } - else if( IsElement( "technique") || IsElement( "extra")) - { - // just syntactic sugar - } - - else if( mFormat == FV_1_4_n && IsElement( "image")) - { - // read ID. Another entry which is "optional" by design but obligatory in reality - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); - - // create an entry and store it in the library under its ID - mImageLibrary[id] = Image(); - - // read on from there - ReadImage( mImageLibrary[id]); - } - - /* Shading modes */ - else if( IsElement( "phong")) - pEffect.mShadeType = Shade_Phong; - else if( IsElement( "constant")) - pEffect.mShadeType = Shade_Constant; - else if( IsElement( "lambert")) - pEffect.mShadeType = Shade_Lambert; - else if( IsElement( "blinn")) - pEffect.mShadeType = Shade_Blinn; - - /* Color + texture properties */ - else if( IsElement( "emission")) - ReadEffectColor( pEffect.mEmissive, pEffect.mTexEmissive); - else if( IsElement( "ambient")) - ReadEffectColor( pEffect.mAmbient, pEffect.mTexAmbient); - else if( IsElement( "diffuse")) - ReadEffectColor( pEffect.mDiffuse, pEffect.mTexDiffuse); - else if( IsElement( "specular")) - ReadEffectColor( pEffect.mSpecular, pEffect.mTexSpecular); - else if( IsElement( "reflective")) { - ReadEffectColor( pEffect.mReflective, pEffect.mTexReflective); - } - else if( IsElement( "transparent")) { - ReadEffectColor( pEffect.mTransparent,pEffect.mTexTransparent); - } - else if( IsElement( "shininess")) - ReadEffectFloat( pEffect.mShininess); - else if( IsElement( "reflectivity")) - ReadEffectFloat( pEffect.mReflectivity); - - /* Single scalar properties */ - else if( IsElement( "transparency")) - ReadEffectFloat( pEffect.mTransparency); - else if( IsElement( "index_of_refraction")) - ReadEffectFloat( pEffect.mRefractIndex); - - // GOOGLEEARTH/OKINO extensions - // ------------------------------------------------------- - else if( IsElement( "double_sided")) - pEffect.mDoubleSided = ReadBoolFromTextContent(); - - // FCOLLADA extensions - // ------------------------------------------------------- - else if( IsElement( "bump")) { - aiColor4D dummy; - ReadEffectColor( dummy,pEffect.mTexBump); - } - - // MAX3D extensions - // ------------------------------------------------------- - else if( IsElement( "wireframe")) { - pEffect.mWireframe = ReadBoolFromTextContent(); - TestClosing( "wireframe"); - } - else if( IsElement( "faceted")) { - pEffect.mFaceted = ReadBoolFromTextContent(); - TestClosing( "faceted"); - } - else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "profile_COMMON") == 0) - { - break; - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Read texture wrapping + UV transform settings from a profile==Maya chunk -void ColladaParser::ReadSamplerProperties( Sampler& out ) -{ - if (mReader->isEmptyElement()) { - return; - } - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - - // MAYA extensions - // ------------------------------------------------------- - if( IsElement( "wrapU")) { - out.mWrapU = ReadBoolFromTextContent(); - TestClosing( "wrapU"); - } - else if( IsElement( "wrapV")) { - out.mWrapV = ReadBoolFromTextContent(); - TestClosing( "wrapV"); - } - else if( IsElement( "mirrorU")) { - out.mMirrorU = ReadBoolFromTextContent(); - TestClosing( "mirrorU"); - } - else if( IsElement( "mirrorV")) { - out.mMirrorV = ReadBoolFromTextContent(); - TestClosing( "mirrorV"); - } - else if( IsElement( "repeatU")) { - out.mTransform.mScaling.x = ReadFloatFromTextContent(); - TestClosing( "repeatU"); - } - else if( IsElement( "repeatV")) { - out.mTransform.mScaling.y = ReadFloatFromTextContent(); - TestClosing( "repeatV"); - } - else if( IsElement( "offsetU")) { - out.mTransform.mTranslation.x = ReadFloatFromTextContent(); - TestClosing( "offsetU"); - } - else if( IsElement( "offsetV")) { - out.mTransform.mTranslation.y = ReadFloatFromTextContent(); - TestClosing( "offsetV"); - } - else if( IsElement( "rotateUV")) { - out.mTransform.mRotation = ReadFloatFromTextContent(); - TestClosing( "rotateUV"); - } - else if( IsElement( "blend_mode")) { - - const char* sz = GetTextContent(); - // http://www.feelingsoftware.com/content/view/55/72/lang,en/ - // NONE, OVER, IN, OUT, ADD, SUBTRACT, MULTIPLY, DIFFERENCE, LIGHTEN, DARKEN, SATURATE, DESATURATE and ILLUMINATE - if (0 == ASSIMP_strincmp(sz,"ADD",3)) - out.mOp = aiTextureOp_Add; - - else if (0 == ASSIMP_strincmp(sz,"SUBTRACT",8)) - out.mOp = aiTextureOp_Subtract; - - else if (0 == ASSIMP_strincmp(sz,"MULTIPLY",8)) - out.mOp = aiTextureOp_Multiply; - - else { - DefaultLogger::get()->warn("Collada: Unsupported MAYA texture blend mode"); - } - TestClosing( "blend_mode"); - } - // OKINO extensions - // ------------------------------------------------------- - else if( IsElement( "weighting")) { - out.mWeighting = ReadFloatFromTextContent(); - TestClosing( "weighting"); - } - else if( IsElement( "mix_with_previous_layer")) { - out.mMixWithPrevious = ReadFloatFromTextContent(); - TestClosing( "mix_with_previous_layer"); - } - // MAX3D extensions - // ------------------------------------------------------- - else if( IsElement( "amount")) { - out.mWeighting = ReadFloatFromTextContent(); - TestClosing( "amount"); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "technique") == 0) - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an effect entry containing a color or a texture defining that color -void ColladaParser::ReadEffectColor( aiColor4D& pColor, Sampler& pSampler) -{ - if (mReader->isEmptyElement()) - return; - - // Save current element name - const std::string curElem = mReader->getNodeName(); - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "color")) - { - // text content contains 4 floats - const char* content = GetTextContent(); - - content = fast_atoreal_move( content, (float&)pColor.r); - SkipSpacesAndLineEnd( &content); - - content = fast_atoreal_move( content, (float&)pColor.g); - SkipSpacesAndLineEnd( &content); - - content = fast_atoreal_move( content, (float&)pColor.b); - SkipSpacesAndLineEnd( &content); - - content = fast_atoreal_move( content, (float&)pColor.a); - SkipSpacesAndLineEnd( &content); - TestClosing( "color"); - } - else if( IsElement( "texture")) - { - // get name of source textur/sampler - int attrTex = GetAttribute( "texture"); - pSampler.mName = mReader->getAttributeValue( attrTex); - - // get name of UV source channel. Specification demands it to be there, but some exporters - // don't write it. It will be the default UV channel in case it's missing. - attrTex = TestAttribute( "texcoord"); - if( attrTex >= 0 ) - pSampler.mUVChannel = mReader->getAttributeValue( attrTex); - //SkipElement(); - - // as we've read texture, the color needs to be 1,1,1,1 - pColor = aiColor4D(1.f, 1.f, 1.f, 1.f); - } - else if( IsElement( "technique")) - { - const int _profile = GetAttribute( "profile"); - const char* profile = mReader->getAttributeValue( _profile ); - - // Some extensions are quite useful ... ReadSamplerProperties processes - // several extensions in MAYA, OKINO and MAX3D profiles. - if (!::strcmp(profile,"MAYA") || !::strcmp(profile,"MAX3D") || !::strcmp(profile,"OKINO")) - { - // get more information on this sampler - ReadSamplerProperties(pSampler); - } - else SkipElement(); - } - else if( !IsElement( "extra")) - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){ - if (mReader->getNodeName() == curElem) - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an effect entry containing a float -void ColladaParser::ReadEffectFloat( float& pFloat) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT){ - if( IsElement( "float")) - { - // text content contains a single floats - const char* content = GetTextContent(); - content = fast_atoreal_move( content, pFloat); - SkipSpacesAndLineEnd( &content); - - TestClosing( "float"); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){ - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an effect parameter specification of any kind -void ColladaParser::ReadEffectParam( Collada::EffectParam& pParam) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "surface")) - { - // image ID given inside tags - TestOpening( "init_from"); - const char* content = GetTextContent(); - pParam.mType = Param_Surface; - pParam.mReference = content; - TestClosing( "init_from"); - - // don't care for remaining stuff - SkipElement( "surface"); - } - else if( IsElement( "sampler2D")) - { - // surface ID is given inside tags - TestOpening( "source"); - const char* content = GetTextContent(); - pParam.mType = Param_Sampler; - pParam.mReference = content; - TestClosing( "source"); - - // don't care for remaining stuff - SkipElement( "sampler2D"); - } else - { - // ignore unknown element - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the geometry library contents -void ColladaParser::ReadGeometryLibrary() -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "geometry")) - { - // read ID. Another entry which is "optional" by design but obligatory in reality - int indexID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( indexID); - - // TODO: (thom) support SIDs - // ai_assert( TestAttribute( "sid") == -1); - - // create a mesh and store it in the library under its ID - Mesh* mesh = new Mesh; - mMeshLibrary[id] = mesh; - - // read the mesh name if it exists - const int nameIndex = TestAttribute("name"); - if(nameIndex != -1) - { - mesh->mName = mReader->getAttributeValue(nameIndex); - } - - // read on from there - ReadGeometry( mesh); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "library_geometries") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a geometry from the geometry library. -void ColladaParser::ReadGeometry( Collada::Mesh* pMesh) -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "mesh")) - { - // read on from there - ReadMesh( pMesh); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "geometry") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a mesh from the geometry library -void ColladaParser::ReadMesh( Mesh* pMesh) -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "source")) - { - // we have professionals dealing with this - ReadSource(); - } - else if( IsElement( "vertices")) - { - // read per-vertex mesh data - ReadVertexData( pMesh); - } - else if( IsElement( "triangles") || IsElement( "lines") || IsElement( "linestrips") - || IsElement( "polygons") || IsElement( "polylist") || IsElement( "trifans") || IsElement( "tristrips")) - { - // read per-index mesh data and faces setup - ReadIndexData( pMesh); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "technique_common") == 0) - { - // end of another meaningless element - read over it - } - else if( strcmp( mReader->getNodeName(), "mesh") == 0) - { - // end of element - we're done here - break; - } else - { - // everything else should be punished - ThrowException( "Expected end of element."); - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a source element -void ColladaParser::ReadSource() -{ - int indexID = GetAttribute( "id"); - std::string sourceID = mReader->getAttributeValue( indexID); - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "float_array") || IsElement( "IDREF_array") || IsElement( "Name_array")) - { - ReadDataArray(); - } - else if( IsElement( "technique_common")) - { - // I don't care for your profiles - } - else if( IsElement( "accessor")) - { - ReadAccessor( sourceID); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "source") == 0) - { - // end of - we're done - break; - } - else if( strcmp( mReader->getNodeName(), "technique_common") == 0) - { - // end of another meaningless element - read over it - } else - { - // everything else should be punished - ThrowException( "Expected end of element."); - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a data array holding a number of floats, and stores it in the global library -void ColladaParser::ReadDataArray() -{ - std::string elmName = mReader->getNodeName(); - bool isStringArray = (elmName == "IDREF_array" || elmName == "Name_array"); - bool isEmptyElement = mReader->isEmptyElement(); - - // read attributes - int indexID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( indexID); - int indexCount = GetAttribute( "count"); - unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( indexCount); - const char* content = TestTextContent(); - - // read values and store inside an array in the data library - mDataLibrary[id] = Data(); - Data& data = mDataLibrary[id]; - data.mIsStringArray = isStringArray; - - // some exporters write empty data arrays, but we need to conserve them anyways because others might reference them - if (content) - { - if( isStringArray) - { - data.mStrings.reserve( count); - std::string s; - - for( unsigned int a = 0; a < count; a++) - { - if( *content == 0) - ThrowException( "Expected more values while reading IDREF_array contents."); - - s.clear(); - while( !IsSpaceOrNewLine( *content)) - s += *content++; - data.mStrings.push_back( s); - - SkipSpacesAndLineEnd( &content); - } - } else - { - data.mValues.reserve( count); - - for( unsigned int a = 0; a < count; a++) - { - if( *content == 0) - ThrowException( "Expected more values while reading float_array contents."); - - float value; - // read a number - content = fast_atoreal_move( content, value); - data.mValues.push_back( value); - // skip whitespace after it - SkipSpacesAndLineEnd( &content); - } - } - } - - // test for closing tag - if( !isEmptyElement ) - TestClosing( elmName.c_str()); -} - -// ------------------------------------------------------------------------------------------------ -// Reads an accessor and stores it in the global library -void ColladaParser::ReadAccessor( const std::string& pID) -{ - // read accessor attributes - int attrSource = GetAttribute( "source"); - const char* source = mReader->getAttributeValue( attrSource); - if( source[0] != '#') - ThrowException( boost::str( boost::format( "Unknown reference format in url \"%s\" in source attribute of element.") % source)); - int attrCount = GetAttribute( "count"); - unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( attrCount); - int attrOffset = TestAttribute( "offset"); - unsigned int offset = 0; - if( attrOffset > -1) - offset = (unsigned int) mReader->getAttributeValueAsInt( attrOffset); - int attrStride = TestAttribute( "stride"); - unsigned int stride = 1; - if( attrStride > -1) - stride = (unsigned int) mReader->getAttributeValueAsInt( attrStride); - - // store in the library under the given ID - mAccessorLibrary[pID] = Accessor(); - Accessor& acc = mAccessorLibrary[pID]; - acc.mCount = count; - acc.mOffset = offset; - acc.mStride = stride; - acc.mSource = source+1; // ignore the leading '#' - acc.mSize = 0; // gets incremented with every param - - // and read the components - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "param")) - { - // read data param - int attrName = TestAttribute( "name"); - std::string name; - if( attrName > -1) - { - name = mReader->getAttributeValue( attrName); - - // analyse for common type components and store it's sub-offset in the corresponding field - - /* Cartesian coordinates */ - if( name == "X") acc.mSubOffset[0] = acc.mParams.size(); - else if( name == "Y") acc.mSubOffset[1] = acc.mParams.size(); - else if( name == "Z") acc.mSubOffset[2] = acc.mParams.size(); - - /* RGBA colors */ - else if( name == "R") acc.mSubOffset[0] = acc.mParams.size(); - else if( name == "G") acc.mSubOffset[1] = acc.mParams.size(); - else if( name == "B") acc.mSubOffset[2] = acc.mParams.size(); - else if( name == "A") acc.mSubOffset[3] = acc.mParams.size(); - - /* UVWQ (STPQ) texture coordinates */ - else if( name == "S") acc.mSubOffset[0] = acc.mParams.size(); - else if( name == "T") acc.mSubOffset[1] = acc.mParams.size(); - else if( name == "P") acc.mSubOffset[2] = acc.mParams.size(); - // else if( name == "Q") acc.mSubOffset[3] = acc.mParams.size(); - /* 4D uv coordinates are not supported in Assimp */ - - /* Generic extra data, interpreted as UV data, too*/ - else if( name == "U") acc.mSubOffset[0] = acc.mParams.size(); - else if( name == "V") acc.mSubOffset[1] = acc.mParams.size(); - //else - // DefaultLogger::get()->warn( boost::str( boost::format( "Unknown accessor parameter \"%s\". Ignoring data channel.") % name)); - } - - // read data type - int attrType = TestAttribute( "type"); - if( attrType > -1) - { - // for the moment we only distinguish between a 4x4 matrix and anything else. - // TODO: (thom) I don't have a spec here at work. Check if there are other multi-value types - // which should be tested for here. - std::string type = mReader->getAttributeValue( attrType); - if( type == "float4x4") - acc.mSize += 16; - else - acc.mSize += 1; - } - - acc.mParams.push_back( name); - - // skip remaining stuff of this element, if any - SkipElement(); - } else - { - ThrowException( boost::str( boost::format( "Unexpected sub element <%s> in tag ") % mReader->getNodeName())); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "accessor") != 0) - ThrowException( "Expected end of element."); - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads input declarations of per-vertex mesh data into the given mesh -void ColladaParser::ReadVertexData( Mesh* pMesh) -{ - // extract the ID of the element. Not that we care, but to catch strange referencing schemes we should warn about - int attrID= GetAttribute( "id"); - pMesh->mVertexID = mReader->getAttributeValue( attrID); - - // a number of elements - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "input")) - { - ReadInputChannel( pMesh->mPerVertexData); - } else - { - ThrowException( boost::str( boost::format( "Unexpected sub element <%s> in tag ") % mReader->getNodeName())); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "vertices") != 0) - ThrowException( "Expected end of element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads input declarations of per-index mesh data into the given mesh -void ColladaParser::ReadIndexData( Mesh* pMesh) -{ - std::vector vcount; - std::vector perIndexData; - - // read primitive count from the attribute - int attrCount = GetAttribute( "count"); - size_t numPrimitives = (size_t) mReader->getAttributeValueAsInt( attrCount); - // some mesh types (e.g. tristrips) don't specify primitive count upfront, - // so we need to sum up the actual number of primitives while we read the

-tags - size_t actualPrimitives = 0; - - // material subgroup - int attrMaterial = TestAttribute( "material"); - SubMesh subgroup; - if( attrMaterial > -1) - subgroup.mMaterial = mReader->getAttributeValue( attrMaterial); - - // distinguish between polys and triangles - std::string elementName = mReader->getNodeName(); - PrimitiveType primType = Prim_Invalid; - if( IsElement( "lines")) - primType = Prim_Lines; - else if( IsElement( "linestrips")) - primType = Prim_LineStrip; - else if( IsElement( "polygons")) - primType = Prim_Polygon; - else if( IsElement( "polylist")) - primType = Prim_Polylist; - else if( IsElement( "triangles")) - primType = Prim_Triangles; - else if( IsElement( "trifans")) - primType = Prim_TriFans; - else if( IsElement( "tristrips")) - primType = Prim_TriStrips; - - ai_assert( primType != Prim_Invalid); - - // also a number of elements, but in addition a

primitive collection and propably index counts for all primitives - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "input")) - { - ReadInputChannel( perIndexData); - } - else if( IsElement( "vcount")) - { - if( !mReader->isEmptyElement()) - { - if (numPrimitives) // It is possible to define a mesh without any primitives - { - // case - specifies the number of indices for each polygon - const char* content = GetTextContent(); - vcount.reserve( numPrimitives); - for( unsigned int a = 0; a < numPrimitives; a++) - { - if( *content == 0) - ThrowException( "Expected more values while reading contents."); - // read a number - vcount.push_back( (size_t) strtoul10( content, &content)); - // skip whitespace after it - SkipSpacesAndLineEnd( &content); - } - } - - TestClosing( "vcount"); - } - } - else if( IsElement( "p")) - { - if( !mReader->isEmptyElement()) - { - // now here the actual fun starts - these are the indices to construct the mesh data from - actualPrimitives += ReadPrimitives(pMesh, perIndexData, numPrimitives, vcount, primType); - } - } - else if (IsElement("extra")) - { - SkipElement("extra"); - } else - { - ThrowException( boost::str( boost::format( "Unexpected sub element <%s> in tag <%s>") % mReader->getNodeName() % elementName)); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( mReader->getNodeName() != elementName) - ThrowException( boost::str( boost::format( "Expected end of <%s> element.") % elementName)); - - break; - } - } - - // small sanity check - if (primType != Prim_TriFans && primType != Prim_TriStrips && - primType != Prim_Lines) // this is ONLY to workaround a bug in SketchUp 15.3.331 where it writes the wrong 'count' when it writes out the 'lines'. - ai_assert(actualPrimitives == numPrimitives); - - // only when we're done reading all

tags (and thus know the final vertex count) can we commit the submesh - subgroup.mNumFaces = actualPrimitives; - pMesh->mSubMeshes.push_back(subgroup); -} - -// ------------------------------------------------------------------------------------------------ -// Reads a single input channel element and stores it in the given array, if valid -void ColladaParser::ReadInputChannel( std::vector& poChannels) -{ - InputChannel channel; - - // read semantic - int attrSemantic = GetAttribute( "semantic"); - std::string semantic = mReader->getAttributeValue( attrSemantic); - channel.mType = GetTypeForSemantic( semantic); - - // read source - int attrSource = GetAttribute( "source"); - const char* source = mReader->getAttributeValue( attrSource); - if( source[0] != '#') - ThrowException( boost::str( boost::format( "Unknown reference format in url \"%s\" in source attribute of element.") % source)); - channel.mAccessor = source+1; // skipping the leading #, hopefully the remaining text is the accessor ID only - - // read index offset, if per-index - int attrOffset = TestAttribute( "offset"); - if( attrOffset > -1) - channel.mOffset = mReader->getAttributeValueAsInt( attrOffset); - - // read set if texture coordinates - if(channel.mType == IT_Texcoord || channel.mType == IT_Color){ - int attrSet = TestAttribute("set"); - if(attrSet > -1){ - attrSet = mReader->getAttributeValueAsInt( attrSet); - if(attrSet < 0) - ThrowException( boost::str( boost::format( "Invalid index \"%i\" in set attribute of element") % (attrSet))); - - channel.mIndex = attrSet; - } - } - - // store, if valid type - if( channel.mType != IT_Invalid) - poChannels.push_back( channel); - - // skip remaining stuff of this element, if any - SkipElement(); -} - -// ------------------------------------------------------------------------------------------------ -// Reads a

primitive index list and assembles the mesh data into the given mesh -size_t ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector& pPerIndexChannels, - size_t pNumPrimitives, const std::vector& pVCount, PrimitiveType pPrimType) -{ - // determine number of indices coming per vertex - // find the offset index for all per-vertex channels - size_t numOffsets = 1; - size_t perVertexOffset = SIZE_MAX; // invalid value - BOOST_FOREACH( const InputChannel& channel, pPerIndexChannels) - { - numOffsets = std::max( numOffsets, channel.mOffset+1); - if( channel.mType == IT_Vertex) - perVertexOffset = channel.mOffset; - } - - // determine the expected number of indices - size_t expectedPointCount = 0; - switch( pPrimType) - { - case Prim_Polylist: - { - BOOST_FOREACH( size_t i, pVCount) - expectedPointCount += i; - break; - } - case Prim_Lines: - expectedPointCount = 2 * pNumPrimitives; - break; - case Prim_Triangles: - expectedPointCount = 3 * pNumPrimitives; - break; - default: - // other primitive types don't state the index count upfront... we need to guess - break; - } - - // and read all indices into a temporary array - std::vector indices; - if( expectedPointCount > 0) - indices.reserve( expectedPointCount * numOffsets); - - if (pNumPrimitives > 0) // It is possible to not contain any indicies - { - const char* content = GetTextContent(); - while( *content != 0) - { - // read a value. - // Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways. - int value = std::max( 0, strtol10( content, &content)); - indices.push_back( size_t( value)); - // skip whitespace after it - SkipSpacesAndLineEnd( &content); - } - } - - // complain if the index count doesn't fit - if( expectedPointCount > 0 && indices.size() != expectedPointCount * numOffsets) { - if (pPrimType == Prim_Lines) { - // HACK: We just fix this number since SketchUp 15.3.331 writes the wrong 'count' for 'lines' - ReportWarning( "Expected different index count in

element, %d instead of %d.", indices.size(), expectedPointCount * numOffsets); - pNumPrimitives = (indices.size() / numOffsets) / 2; - } else - ThrowException( "Expected different index count in

element."); - - } else if( expectedPointCount == 0 && (indices.size() % numOffsets) != 0) - ThrowException( "Expected different index count in

element."); - - // find the data for all sources - for( std::vector::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it) - { - InputChannel& input = *it; - if( input.mResolved) - continue; - - // find accessor - input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor); - // resolve accessor's data pointer as well, if neccessary - const Accessor* acc = input.mResolved; - if( !acc->mData) - acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource); - } - // and the same for the per-index channels - for( std::vector::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) - { - InputChannel& input = *it; - if( input.mResolved) - continue; - - // ignore vertex pointer, it doesn't refer to an accessor - if( input.mType == IT_Vertex) - { - // warn if the vertex channel does not refer to the element in the same mesh - if( input.mAccessor != pMesh->mVertexID) - ThrowException( "Unsupported vertex referencing scheme."); - continue; - } - - // find accessor - input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor); - // resolve accessor's data pointer as well, if neccessary - const Accessor* acc = input.mResolved; - if( !acc->mData) - acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource); - } - - // For continued primitives, the given count does not come all in one

, but only one primitive per

- size_t numPrimitives = pNumPrimitives; - if( pPrimType == Prim_TriFans || pPrimType == Prim_Polygon) - numPrimitives = 1; - // For continued primitives, the given count is actually the number of

's inside the parent tag - if ( pPrimType == Prim_TriStrips){ - size_t numberOfVertices = indices.size() / numOffsets; - numPrimitives = numberOfVertices - 2; - } - - pMesh->mFaceSize.reserve( numPrimitives); - pMesh->mFacePosIndices.reserve( indices.size() / numOffsets); - - size_t polylistStartVertex = 0; - for (size_t currentPrimitive = 0; currentPrimitive < numPrimitives; currentPrimitive++) - { - // determine number of points for this primitive - size_t numPoints = 0; - switch( pPrimType) - { - case Prim_Lines: - numPoints = 2; - for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++) - CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - break; - case Prim_Triangles: - numPoints = 3; - for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++) - CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - break; - case Prim_TriStrips: - numPoints = 3; - ReadPrimTriStrips(numOffsets, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - break; - case Prim_Polylist: - numPoints = pVCount[currentPrimitive]; - for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++) - CopyVertex(polylistStartVertex + currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, 0, indices); - polylistStartVertex += numPoints; - break; - case Prim_TriFans: - case Prim_Polygon: - numPoints = indices.size() / numOffsets; - for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++) - CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - break; - default: - // LineStrip is not supported due to expected index unmangling - ThrowException( "Unsupported primitive type."); - break; - } - - // store the face size to later reconstruct the face from - pMesh->mFaceSize.push_back( numPoints); - } - - // if I ever get my hands on that guy who invented this steaming pile of indirection... - TestClosing( "p"); - return numPrimitives; -} - -void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset, Mesh* pMesh, std::vector& pPerIndexChannels, size_t currentPrimitive, const std::vector& indices){ - // calculate the base offset of the vertex whose attributes we ant to copy - size_t baseOffset = currentPrimitive * numOffsets * numPoints + currentVertex * numOffsets; - - // don't overrun the boundaries of the index list - size_t maxIndexRequested = baseOffset + numOffsets - 1; - ai_assert(maxIndexRequested < indices.size()); - - // extract per-vertex channels using the global per-vertex offset - for (std::vector::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it) - ExtractDataObjectFromChannel(*it, indices[baseOffset + perVertexOffset], pMesh); - // and extract per-index channels using there specified offset - for (std::vector::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) - ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh); - - // store the vertex-data index for later assignment of bone vertex weights - pMesh->mFacePosIndices.push_back(indices[baseOffset + perVertexOffset]); -} - -void ColladaParser::ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Mesh* pMesh, std::vector& pPerIndexChannels, size_t currentPrimitive, const std::vector& indices){ - if (currentPrimitive % 2 != 0){ - //odd tristrip triangles need their indices mangled, to preserve winding direction - CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - } - else {//for non tristrips or even tristrip triangles - CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - } -} - -// ------------------------------------------------------------------------------------------------ -// Extracts a single object from an input channel and stores it in the appropriate mesh data array -void ColladaParser::ExtractDataObjectFromChannel( const InputChannel& pInput, size_t pLocalIndex, Mesh* pMesh) -{ - // ignore vertex referrer - we handle them that separate - if( pInput.mType == IT_Vertex) - return; - - const Accessor& acc = *pInput.mResolved; - if( pLocalIndex >= acc.mCount) - ThrowException( boost::str( boost::format( "Invalid data index (%d/%d) in primitive specification") % pLocalIndex % acc.mCount)); - - // get a pointer to the start of the data object referred to by the accessor and the local index - const float* dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex* acc.mStride; - - // assemble according to the accessors component sub-offset list. We don't care, yet, - // what kind of object exactly we're extracting here - float obj[4]; - for( size_t c = 0; c < 4; ++c) - obj[c] = dataObject[acc.mSubOffset[c]]; - - // now we reinterpret it according to the type we're reading here - switch( pInput.mType) - { - case IT_Position: // ignore all position streams except 0 - there can be only one position - if( pInput.mIndex == 0) - pMesh->mPositions.push_back( aiVector3D( obj[0], obj[1], obj[2])); - else - DefaultLogger::get()->error("Collada: just one vertex position stream supported"); - break; - case IT_Normal: - // pad to current vertex count if necessary - if( pMesh->mNormals.size() < pMesh->mPositions.size()-1) - pMesh->mNormals.insert( pMesh->mNormals.end(), pMesh->mPositions.size() - pMesh->mNormals.size() - 1, aiVector3D( 0, 1, 0)); - - // ignore all normal streams except 0 - there can be only one normal - if( pInput.mIndex == 0) - pMesh->mNormals.push_back( aiVector3D( obj[0], obj[1], obj[2])); - else - DefaultLogger::get()->error("Collada: just one vertex normal stream supported"); - break; - case IT_Tangent: - // pad to current vertex count if necessary - if( pMesh->mTangents.size() < pMesh->mPositions.size()-1) - pMesh->mTangents.insert( pMesh->mTangents.end(), pMesh->mPositions.size() - pMesh->mTangents.size() - 1, aiVector3D( 1, 0, 0)); - - // ignore all tangent streams except 0 - there can be only one tangent - if( pInput.mIndex == 0) - pMesh->mTangents.push_back( aiVector3D( obj[0], obj[1], obj[2])); - else - DefaultLogger::get()->error("Collada: just one vertex tangent stream supported"); - break; - case IT_Bitangent: - // pad to current vertex count if necessary - if( pMesh->mBitangents.size() < pMesh->mPositions.size()-1) - pMesh->mBitangents.insert( pMesh->mBitangents.end(), pMesh->mPositions.size() - pMesh->mBitangents.size() - 1, aiVector3D( 0, 0, 1)); - - // ignore all bitangent streams except 0 - there can be only one bitangent - if( pInput.mIndex == 0) - pMesh->mBitangents.push_back( aiVector3D( obj[0], obj[1], obj[2])); - else - DefaultLogger::get()->error("Collada: just one vertex bitangent stream supported"); - break; - case IT_Texcoord: - // up to 4 texture coord sets are fine, ignore the others - if( pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS) - { - // pad to current vertex count if necessary - if( pMesh->mTexCoords[pInput.mIndex].size() < pMesh->mPositions.size()-1) - pMesh->mTexCoords[pInput.mIndex].insert( pMesh->mTexCoords[pInput.mIndex].end(), - pMesh->mPositions.size() - pMesh->mTexCoords[pInput.mIndex].size() - 1, aiVector3D( 0, 0, 0)); - - pMesh->mTexCoords[pInput.mIndex].push_back( aiVector3D( obj[0], obj[1], obj[2])); - if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) /* hack ... consider cleaner solution */ - pMesh->mNumUVComponents[pInput.mIndex]=3; - } else - { - DefaultLogger::get()->error("Collada: too many texture coordinate sets. Skipping."); - } - break; - case IT_Color: - // up to 4 color sets are fine, ignore the others - if( pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS) - { - // pad to current vertex count if necessary - if( pMesh->mColors[pInput.mIndex].size() < pMesh->mPositions.size()-1) - pMesh->mColors[pInput.mIndex].insert( pMesh->mColors[pInput.mIndex].end(), - pMesh->mPositions.size() - pMesh->mColors[pInput.mIndex].size() - 1, aiColor4D( 0, 0, 0, 1)); - - aiColor4D result(0, 0, 0, 1); - for (size_t i = 0; i < pInput.mResolved->mSize; ++i) - { - result[i] = obj[pInput.mResolved->mSubOffset[i]]; - } - pMesh->mColors[pInput.mIndex].push_back(result); - } else - { - DefaultLogger::get()->error("Collada: too many vertex color sets. Skipping."); - } - - break; - default: - // IT_Invalid and IT_Vertex - ai_assert(false && "shouldn't ever get here"); - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the library of node hierarchies and scene parts -void ColladaParser::ReadSceneLibrary() -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - // a visual scene - generate root node under its ID and let ReadNode() do the recursive work - if( IsElement( "visual_scene")) - { - // read ID. Is optional according to the spec, but how on earth should a scene_instance refer to it then? - int indexID = GetAttribute( "id"); - const char* attrID = mReader->getAttributeValue( indexID); - - // read name if given. - int indexName = TestAttribute( "name"); - const char* attrName = "unnamed"; - if( indexName > -1) - attrName = mReader->getAttributeValue( indexName); - - // create a node and store it in the library under its ID - Node* node = new Node; - node->mID = attrID; - node->mName = attrName; - mNodeLibrary[node->mID] = node; - - ReadSceneNode( node); - } else - { - // ignore the rest - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "library_visual_scenes") == 0) - //ThrowException( "Expected end of \"library_visual_scenes\" element."); - - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a scene node's contents including children and stores it in the given node -void ColladaParser::ReadSceneNode( Node* pNode) -{ - // quit immediately on elements - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "node")) - { - Node* child = new Node; - int attrID = TestAttribute( "id"); - if( attrID > -1) - child->mID = mReader->getAttributeValue( attrID); - int attrSID = TestAttribute( "sid"); - if( attrSID > -1) - child->mSID = mReader->getAttributeValue( attrSID); - - int attrName = TestAttribute( "name"); - if( attrName > -1) - child->mName = mReader->getAttributeValue( attrName); - - // TODO: (thom) support SIDs - // ai_assert( TestAttribute( "sid") == -1); - - if (pNode) - { - pNode->mChildren.push_back( child); - child->mParent = pNode; - } - else - { - // no parent node given, probably called from element. - // create new node in node library - mNodeLibrary[child->mID] = child; - } - - // read on recursively from there - ReadSceneNode( child); - continue; - } - // For any further stuff we need a valid node to work on - else if (!pNode) - continue; - - if( IsElement( "lookat")) - ReadNodeTransformation( pNode, TF_LOOKAT); - else if( IsElement( "matrix")) - ReadNodeTransformation( pNode, TF_MATRIX); - else if( IsElement( "rotate")) - ReadNodeTransformation( pNode, TF_ROTATE); - else if( IsElement( "scale")) - ReadNodeTransformation( pNode, TF_SCALE); - else if( IsElement( "skew")) - ReadNodeTransformation( pNode, TF_SKEW); - else if( IsElement( "translate")) - ReadNodeTransformation( pNode, TF_TRANSLATE); - else if( IsElement( "render") && pNode->mParent == NULL && 0 == pNode->mPrimaryCamera.length()) - { - // ... scene evaluation or, in other words, postprocessing pipeline, - // or, again in other words, a turing-complete description how to - // render a Collada scene. The only thing that is interesting for - // us is the primary camera. - int attrId = TestAttribute("camera_node"); - if (-1 != attrId) - { - const char* s = mReader->getAttributeValue(attrId); - if (s[0] != '#') - DefaultLogger::get()->error("Collada: Unresolved reference format of camera"); - else - pNode->mPrimaryCamera = s+1; - } - } - else if( IsElement( "instance_node")) - { - // find the node in the library - int attrID = TestAttribute( "url"); - if( attrID != -1) - { - const char* s = mReader->getAttributeValue(attrID); - if (s[0] != '#') - DefaultLogger::get()->error("Collada: Unresolved reference format of node"); - else - { - pNode->mNodeInstances.push_back(NodeInstance()); - pNode->mNodeInstances.back().mNode = s+1; - } - } - } - else if( IsElement( "instance_geometry") || IsElement( "instance_controller")) - { - // Reference to a mesh or controller, with possible material associations - ReadNodeGeometry( pNode); - } - else if( IsElement( "instance_light")) - { - // Reference to a light, name given in 'url' attribute - int attrID = TestAttribute("url"); - if (-1 == attrID) - DefaultLogger::get()->warn("Collada: Expected url attribute in element"); - else - { - const char* url = mReader->getAttributeValue( attrID); - if( url[0] != '#') - ThrowException( "Unknown reference format in element"); - - pNode->mLights.push_back(LightInstance()); - pNode->mLights.back().mLight = url+1; - } - } - else if( IsElement( "instance_camera")) - { - // Reference to a camera, name given in 'url' attribute - int attrID = TestAttribute("url"); - if (-1 == attrID) - DefaultLogger::get()->warn("Collada: Expected url attribute in element"); - else - { - const char* url = mReader->getAttributeValue( attrID); - if( url[0] != '#') - ThrowException( "Unknown reference format in element"); - - pNode->mCameras.push_back(CameraInstance()); - pNode->mCameras.back().mCamera = url+1; - } - } - else - { - // skip everything else for the moment - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a node transformation entry of the given type and adds it to the given node's transformation list. -void ColladaParser::ReadNodeTransformation( Node* pNode, TransformType pType) -{ - if( mReader->isEmptyElement()) - return; - - std::string tagName = mReader->getNodeName(); - - Transform tf; - tf.mType = pType; - - // read SID - int indexSID = TestAttribute( "sid"); - if( indexSID >= 0) - tf.mID = mReader->getAttributeValue( indexSID); - - // how many parameters to read per transformation type - static const unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 }; - const char* content = GetTextContent(); - - // read as many parameters and store in the transformation - for( unsigned int a = 0; a < sNumParameters[pType]; a++) - { - // read a number - content = fast_atoreal_move( content, tf.f[a]); - // skip whitespace after it - SkipSpacesAndLineEnd( &content); - } - - // place the transformation at the queue of the node - pNode->mTransforms.push_back( tf); - - // and consume the closing tag - TestClosing( tagName.c_str()); -} - -// ------------------------------------------------------------------------------------------------ -// Processes bind_vertex_input and bind elements -void ColladaParser::ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl) -{ - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "bind_vertex_input")) - { - Collada::InputSemanticMapEntry vn; - - // effect semantic - int n = GetAttribute("semantic"); - std::string s = mReader->getAttributeValue(n); - - // input semantic - n = GetAttribute("input_semantic"); - vn.mType = GetTypeForSemantic( mReader->getAttributeValue(n) ); - - // index of input set - n = TestAttribute("input_set"); - if (-1 != n) - vn.mSet = mReader->getAttributeValueAsInt(n); - - tbl.mMap[s] = vn; - } - else if( IsElement( "bind")) { - DefaultLogger::get()->warn("Collada: Found unsupported element"); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "instance_material") == 0) - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a mesh reference in a node and adds it to the node's mesh list -void ColladaParser::ReadNodeGeometry( Node* pNode) -{ - // referred mesh is given as an attribute of the element - int attrUrl = GetAttribute( "url"); - const char* url = mReader->getAttributeValue( attrUrl); - if( url[0] != '#') - ThrowException( "Unknown reference format"); - - Collada::MeshInstance instance; - instance.mMeshOrController = url+1; // skipping the leading # - - if( !mReader->isEmptyElement()) - { - // read material associations. Ignore additional elements inbetween - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if( IsElement( "instance_material")) - { - // read ID of the geometry subgroup and the target material - int attrGroup = GetAttribute( "symbol"); - std::string group = mReader->getAttributeValue( attrGroup); - int attrMaterial = GetAttribute( "target"); - const char* urlMat = mReader->getAttributeValue( attrMaterial); - Collada::SemanticMappingTable s; - if( urlMat[0] == '#') - urlMat++; - - s.mMatName = urlMat; - - // resolve further material details + THIS UGLY AND NASTY semantic mapping stuff - if( !mReader->isEmptyElement()) - ReadMaterialVertexInputBinding(s); - - // store the association - instance.mMaterials[group] = s; - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if( strcmp( mReader->getNodeName(), "instance_geometry") == 0 - || strcmp( mReader->getNodeName(), "instance_controller") == 0) - break; - } - } - } - - // store it - pNode->mMeshes.push_back( instance); -} - -// ------------------------------------------------------------------------------------------------ -// Reads the collada scene -void ColladaParser::ReadScene() -{ - if( mReader->isEmptyElement()) - return; - - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "instance_visual_scene")) - { - // should be the first and only occurence - if( mRootNode) - ThrowException( "Invalid scene containing multiple root nodes in element"); - - // read the url of the scene to instance. Should be of format "#some_name" - int urlIndex = GetAttribute( "url"); - const char* url = mReader->getAttributeValue( urlIndex); - if( url[0] != '#') - ThrowException( "Unknown reference format in element"); - - // find the referred scene, skip the leading # - NodeLibrary::const_iterator sit = mNodeLibrary.find( url+1); - if( sit == mNodeLibrary.end()) - ThrowException( "Unable to resolve visual_scene reference \"" + std::string(url) + "\" in element."); - mRootNode = sit->second; - } else { - SkipElement(); - } - } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){ - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Aborts the file reading with an exception -AI_WONT_RETURN void ColladaParser::ThrowException( const std::string& pError) const -{ - throw DeadlyImportError( boost::str( boost::format( "Collada: %s - %s") % mFileName % pError)); -} - -void ColladaParser::ReportWarning(const char* msg,...) -{ - ai_assert(NULL != msg); - - va_list args; - va_start(args,msg); - - char szBuffer[3000]; - const int iLen = vsprintf(szBuffer,msg,args); - ai_assert(iLen > 0); - - va_end(args); - DefaultLogger::get()->warn("Validation warning: " + std::string(szBuffer,iLen)); -} - -// ------------------------------------------------------------------------------------------------ -// Skips all data until the end node of the current element -void ColladaParser::SkipElement() -{ - // nothing to skip if it's an - if( mReader->isEmptyElement()) - return; - - // reroute - SkipElement( mReader->getNodeName()); -} - -// ------------------------------------------------------------------------------------------------ -// Skips all data until the end node of the given element -void ColladaParser::SkipElement( const char* pElement) -{ - // copy the current node's name because it'a pointer to the reader's internal buffer, - // which is going to change with the upcoming parsing - std::string element = pElement; - while( mReader->read()) - { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - if( mReader->getNodeName() == element) - break; - } -} - -// ------------------------------------------------------------------------------------------------ -// Tests for an opening element of the given name, throws an exception if not found -void ColladaParser::TestOpening( const char* pName) -{ - // read element start - if( !mReader->read()) - ThrowException( boost::str( boost::format( "Unexpected end of file while beginning of <%s> element.") % pName)); - // whitespace in front is ok, just read again if found - if( mReader->getNodeType() == irr::io::EXN_TEXT) - if( !mReader->read()) - ThrowException( boost::str( boost::format( "Unexpected end of file while reading beginning of <%s> element.") % pName)); - - if( mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp( mReader->getNodeName(), pName) != 0) - ThrowException( boost::str( boost::format( "Expected start of <%s> element.") % pName)); -} - -// ------------------------------------------------------------------------------------------------ -// Tests for the closing tag of the given element, throws an exception if not found -void ColladaParser::TestClosing( const char* pName) -{ - // check if we're already on the closing tag and return right away - if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END && strcmp( mReader->getNodeName(), pName) == 0) - return; - - // if not, read some more - if( !mReader->read()) - ThrowException( boost::str( boost::format( "Unexpected end of file while reading end of <%s> element.") % pName)); - // whitespace in front is ok, just read again if found - if( mReader->getNodeType() == irr::io::EXN_TEXT) - if( !mReader->read()) - ThrowException( boost::str( boost::format( "Unexpected end of file while reading end of <%s> element.") % pName)); - - // but this has the be the closing tag, or we're lost - if( mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp( mReader->getNodeName(), pName) != 0) - ThrowException( boost::str( boost::format( "Expected end of <%s> element.") % pName)); -} - -// ------------------------------------------------------------------------------------------------ -// Returns the index of the named attribute or -1 if not found. Does not throw, therefore useful for optional attributes -int ColladaParser::GetAttribute( const char* pAttr) const -{ - int index = TestAttribute( pAttr); - if( index != -1) - return index; - - // attribute not found -> throw an exception - ThrowException( boost::str( boost::format( "Expected attribute \"%s\" for element <%s>.") % pAttr % mReader->getNodeName())); - return -1; -} - -// ------------------------------------------------------------------------------------------------ -// Tests the present element for the presence of one attribute, returns its index or throws an exception if not found -int ColladaParser::TestAttribute( const char* pAttr) const -{ - for( int a = 0; a < mReader->getAttributeCount(); a++) - if( strcmp( mReader->getAttributeName( a), pAttr) == 0) - return a; - - return -1; -} - -// ------------------------------------------------------------------------------------------------ -// Reads the text contents of an element, throws an exception if not given. Skips leading whitespace. -const char* ColladaParser::GetTextContent() -{ - const char* sz = TestTextContent(); - if(!sz) { - ThrowException( "Invalid contents in element \"n\"."); - } - return sz; -} - -// ------------------------------------------------------------------------------------------------ -// Reads the text contents of an element, returns NULL if not given. Skips leading whitespace. -const char* ColladaParser::TestTextContent() -{ - // present node should be the beginning of an element - if( mReader->getNodeType() != irr::io::EXN_ELEMENT || mReader->isEmptyElement()) - return NULL; - - // read contents of the element - if( !mReader->read() ) - return NULL; - if( mReader->getNodeType() != irr::io::EXN_TEXT) - return NULL; - - // skip leading whitespace - const char* text = mReader->getNodeData(); - SkipSpacesAndLineEnd( &text); - - return text; -} - -// ------------------------------------------------------------------------------------------------ -// Calculates the resulting transformation fromm all the given transform steps -aiMatrix4x4 ColladaParser::CalculateResultTransform( const std::vector& pTransforms) const -{ - aiMatrix4x4 res; - - for( std::vector::const_iterator it = pTransforms.begin(); it != pTransforms.end(); ++it) - { - const Transform& tf = *it; - switch( tf.mType) - { - case TF_LOOKAT: - { - aiVector3D pos( tf.f[0], tf.f[1], tf.f[2]); - aiVector3D dstPos( tf.f[3], tf.f[4], tf.f[5]); - aiVector3D up = aiVector3D( tf.f[6], tf.f[7], tf.f[8]).Normalize(); - aiVector3D dir = aiVector3D( dstPos - pos).Normalize(); - aiVector3D right = (dir ^ up).Normalize(); - - res *= aiMatrix4x4( - right.x, up.x, -dir.x, pos.x, - right.y, up.y, -dir.y, pos.y, - right.z, up.z, -dir.z, pos.z, - 0, 0, 0, 1); - break; - } - case TF_ROTATE: - { - aiMatrix4x4 rot; - float angle = tf.f[3] * float( AI_MATH_PI) / 180.0f; - aiVector3D axis( tf.f[0], tf.f[1], tf.f[2]); - aiMatrix4x4::Rotation( angle, axis, rot); - res *= rot; - break; - } - case TF_TRANSLATE: - { - aiMatrix4x4 trans; - aiMatrix4x4::Translation( aiVector3D( tf.f[0], tf.f[1], tf.f[2]), trans); - res *= trans; - break; - } - case TF_SCALE: - { - aiMatrix4x4 scale( tf.f[0], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[1], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[2], 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f); - res *= scale; - break; - } - case TF_SKEW: - // TODO: (thom) - ai_assert( false); - break; - case TF_MATRIX: - { - aiMatrix4x4 mat( tf.f[0], tf.f[1], tf.f[2], tf.f[3], tf.f[4], tf.f[5], tf.f[6], tf.f[7], - tf.f[8], tf.f[9], tf.f[10], tf.f[11], tf.f[12], tf.f[13], tf.f[14], tf.f[15]); - res *= mat; - break; - } - default: - ai_assert( false); - break; - } - } - - return res; -} - -// ------------------------------------------------------------------------------------------------ -// Determines the input data type for the given semantic string -Collada::InputType ColladaParser::GetTypeForSemantic( const std::string& pSemantic) -{ - if( pSemantic == "POSITION") - return IT_Position; - else if( pSemantic == "TEXCOORD") - return IT_Texcoord; - else if( pSemantic == "NORMAL") - return IT_Normal; - else if( pSemantic == "COLOR") - return IT_Color; - else if( pSemantic == "VERTEX") - return IT_Vertex; - else if( pSemantic == "BINORMAL" || pSemantic == "TEXBINORMAL") - return IT_Bitangent; - else if( pSemantic == "TANGENT" || pSemantic == "TEXTANGENT") - return IT_Tangent; - - DefaultLogger::get()->warn( boost::str( boost::format( "Unknown vertex input type \"%s\". Ignoring.") % pSemantic)); - return IT_Invalid; -} - -#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER From 9708a4db934d21fb2b5695c4db4770d29d65c4b8 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 30 Aug 2015 13:48:00 +0200 Subject: [PATCH 19/25] Subdivision: fix compiler warning from debug check. --- code/Subdivision.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/Subdivision.cpp b/code/Subdivision.cpp index 2c1c2ea7d..1443f46eb 100644 --- a/code/Subdivision.cpp +++ b/code/Subdivision.cpp @@ -399,10 +399,14 @@ void CatmullClarkSubdivider::InternSubdivide ( bool haveit = false; for (unsigned int i = 0; i < f.mNumIndices; ++i) { if (maptbl[FLATTEN_VERTEX_IDX(n,f.mIndices[i])]==(unsigned int)t) { - haveit = true; break; + haveit = true; + break; } } ai_assert(haveit); + if (!haveit) { + DefaultLogger::get()->debug("Catmull-Clark Subdivider: Index not used"); + } break; } } From e4510c26ba2a58fbd3a97ce8109d972d8b8c1eca Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 30 Aug 2015 15:21:53 +0200 Subject: [PATCH 20/25] Obj-Importer: fix https://github.com/assimp/assimp/issues/641 --- code/ObjFileImporter.cpp | 24 ++++++---- code/ObjFileParser.cpp | 60 ++++++++++++----------- code/ObjFileParser.h | 8 ++-- include/assimp/IOSystem.hpp | 51 +++++++++++++++++++- test/CMakeLists.txt | 1 + test/unit/utIOSystem.cpp | 94 +++++++++++++++++++++++++++++++++++++ 6 files changed, 197 insertions(+), 41 deletions(-) create mode 100644 test/unit/utIOSystem.cpp diff --git a/code/ObjFileImporter.cpp b/code/ObjFileImporter.cpp index e4046b53b..7bf6154df 100644 --- a/code/ObjFileImporter.cpp +++ b/code/ObjFileImporter.cpp @@ -133,15 +133,16 @@ void ObjFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, TextFileToBuffer(file.get(),m_Buffer); // Get the model name - std::string strModelName; + std::string modelName, folderName; std::string::size_type pos = pFile.find_last_of( "\\/" ); - if ( pos != std::string::npos ) - { - strModelName = pFile.substr(pos+1, pFile.size() - pos - 1); - } - else - { - strModelName = pFile; + if ( pos != std::string::npos ) { + modelName = pFile.substr(pos+1, pFile.size() - pos - 1); + folderName = pFile.substr( 0, pos ); + if ( folderName.empty() ) { + pIOHandler->PushDirectory( folderName ); + } + } else { + modelName = pFile; } // process all '\' @@ -161,13 +162,18 @@ void ObjFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, } // parse the file into a temporary representation - ObjFileParser parser(m_Buffer, strModelName, pIOHandler); + ObjFileParser parser(m_Buffer, modelName, pIOHandler); // And create the proper return structures out of it CreateDataFromImport(parser.GetModel(), pScene); // Clean up allocated storage for the next import m_Buffer.clear(); + + // Pop directory stack + if ( pIOHandler->StackSize() > 0 ) { + pIOHandler->PopDirectory(); + } } // ------------------------------------------------------------------------------------------------ diff --git a/code/ObjFileParser.cpp b/code/ObjFileParser.cpp index b2194a747..346c734fc 100644 --- a/code/ObjFileParser.cpp +++ b/code/ObjFileParser.cpp @@ -61,21 +61,21 @@ const std::string ObjFileParser::DEFAULT_MATERIAL = AI_DEFAULT_MATERIAL_NAME; // ------------------------------------------------------------------- // Constructor with loaded data and directories. -ObjFileParser::ObjFileParser(std::vector &Data,const std::string &strModelName, IOSystem *io ) : - m_DataIt(Data.begin()), - m_DataItEnd(Data.end()), +ObjFileParser::ObjFileParser(std::vector &data,const std::string &modelName, IOSystem *io ) : + m_DataIt(data.begin()), + m_DataItEnd(data.end()), m_pModel(NULL), m_uiLine(0), m_pIO( io ) { - std::fill_n(m_buffer,BUFFERSIZE,0); + std::fill_n(m_buffer,Buffersize,0); // Create the model instance to store all the data m_pModel = new ObjFile::Model(); - m_pModel->m_ModelName = strModelName; + m_pModel->m_ModelName = modelName; // create default material and store it - m_pModel->m_pDefaultMaterial = new ObjFile::Material(); + m_pModel->m_pDefaultMaterial = new ObjFile::Material; m_pModel->m_pDefaultMaterial->MaterialName.Set( DEFAULT_MATERIAL ); m_pModel->m_MaterialLib.push_back( DEFAULT_MATERIAL ); m_pModel->m_MaterialMap[ DEFAULT_MATERIAL ] = m_pModel->m_pDefaultMaterial; @@ -248,20 +248,20 @@ void ObjFileParser::getVector( std::vector &point3d_array ) { } float x, y, z; if( 2 == numComponents ) { - copyNextWord( m_buffer, BUFFERSIZE ); + copyNextWord( m_buffer, Buffersize ); x = ( float ) fast_atof( m_buffer ); - copyNextWord( m_buffer, BUFFERSIZE ); + copyNextWord( m_buffer, Buffersize ); y = ( float ) fast_atof( m_buffer ); z = 0.0; } else if( 3 == numComponents ) { - copyNextWord( m_buffer, BUFFERSIZE ); + copyNextWord( m_buffer, Buffersize ); x = ( float ) fast_atof( m_buffer ); - copyNextWord( m_buffer, BUFFERSIZE ); + copyNextWord( m_buffer, Buffersize ); y = ( float ) fast_atof( m_buffer ); - copyNextWord( m_buffer, BUFFERSIZE ); + copyNextWord( m_buffer, Buffersize ); z = ( float ) fast_atof( m_buffer ); } else { throw DeadlyImportError( "OBJ: Invalid number of components" ); @@ -274,13 +274,13 @@ void ObjFileParser::getVector( std::vector &point3d_array ) { // Get values for a new 3D vector instance void ObjFileParser::getVector3(std::vector &point3d_array) { float x, y, z; - copyNextWord(m_buffer, BUFFERSIZE); + copyNextWord(m_buffer, Buffersize); x = (float) fast_atof(m_buffer); - copyNextWord(m_buffer, BUFFERSIZE); + copyNextWord(m_buffer, Buffersize); y = (float) fast_atof(m_buffer); - copyNextWord( m_buffer, BUFFERSIZE ); + copyNextWord( m_buffer, Buffersize ); z = ( float ) fast_atof( m_buffer ); point3d_array.push_back( aiVector3D( x, y, z ) ); @@ -291,10 +291,10 @@ void ObjFileParser::getVector3(std::vector &point3d_array) { // Get values for a new 2D vector instance void ObjFileParser::getVector2( std::vector &point2d_array ) { float x, y; - copyNextWord(m_buffer, BUFFERSIZE); + copyNextWord(m_buffer, Buffersize); x = (float) fast_atof(m_buffer); - copyNextWord(m_buffer, BUFFERSIZE); + copyNextWord(m_buffer, Buffersize); y = (float) fast_atof(m_buffer); point2d_array.push_back(aiVector2D(x, y)); @@ -306,12 +306,12 @@ void ObjFileParser::getVector2( std::vector &point2d_array ) { // Get values for a new face instance void ObjFileParser::getFace(aiPrimitiveType type) { - copyNextLine(m_buffer, BUFFERSIZE); + copyNextLine(m_buffer, Buffersize); if (m_DataIt == m_DataItEnd) return; char *pPtr = m_buffer; - char *pEnd = &pPtr[BUFFERSIZE]; + char *pEnd = &pPtr[Buffersize]; pPtr = getNextToken(pPtr, pEnd); if (pPtr == pEnd || *pPtr == '\0') return; @@ -468,8 +468,9 @@ void ObjFileParser::getMaterialDesc() // Get next data for material data m_DataIt = getNextToken(m_DataIt, m_DataItEnd); - if (m_DataIt == m_DataItEnd) + if (m_DataIt == m_DataItEnd) { return; + } char *pStart = &(*m_DataIt); while( m_DataIt != m_DataItEnd && !IsLineEnd( *m_DataIt ) ) { @@ -483,14 +484,11 @@ void ObjFileParser::getMaterialDesc() // Search for material std::map::iterator it = m_pModel->m_MaterialMap.find( strName ); - if ( it == m_pModel->m_MaterialMap.end() ) - { + if ( it == m_pModel->m_MaterialMap.end() ) { // Not found, use default material m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial; DefaultLogger::get()->error("OBJ: failed to locate material " + strName + ", skipping"); - } - else - { + } else { // Found, using detected material m_pModel->m_pCurrentMaterial = (*it).second; if ( needsNewMesh( strName )) @@ -539,18 +537,26 @@ void ObjFileParser::getMaterialLib() // Check for existence const std::string strMatName(pStart, &(*m_DataIt)); - IOStream *pFile = m_pIO->Open(strMatName); + std::string absName; + if ( m_pIO->StackSize() > 0 ) { + const std::string &path = m_pIO->CurrentDirectory(); + absName = path + strMatName; + } else { + absName = strMatName; + } + //IOStream *pFile = m_pIO->Open( strMatName ); + IOStream *pFile = m_pIO->Open( absName ); if (!pFile ) { - DefaultLogger::get()->error("OBJ: Unable to locate material file " + strMatName); + DefaultLogger::get()->error("OBJ: Unable to locate material file " + strMatName ); m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); return; } // Import material library data from file std::vector buffer; - BaseImporter::TextFileToBuffer(pFile,buffer); + BaseImporter::TextFileToBuffer( pFile, buffer ); m_pIO->Close( pFile ); // Importing the material library diff --git a/code/ObjFileParser.h b/code/ObjFileParser.h index 85f555567..66f378f29 100644 --- a/code/ObjFileParser.h +++ b/code/ObjFileParser.h @@ -62,10 +62,9 @@ class IOSystem; /// \class ObjFileParser /// \brief Parser for a obj waveform file -class ObjFileParser -{ +class ObjFileParser { public: - static const size_t BUFFERSIZE = 4096; + static const size_t Buffersize = 4096; typedef std::vector DataArray; typedef std::vector::iterator DataArrayIt; typedef std::vector::const_iterator ConstDataArrayIt; @@ -137,9 +136,10 @@ private: //! Current line (for debugging) unsigned int m_uiLine; //! Helper buffer - char m_buffer[BUFFERSIZE]; + char m_buffer[Buffersize]; /// Pointer to IO system instance. IOSystem *m_pIO; + /// Path to the current model }; } // Namespace Assimp diff --git a/include/assimp/IOSystem.hpp b/include/assimp/IOSystem.hpp index 4b4d55fd4..81b3e910a 100644 --- a/include/assimp/IOSystem.hpp +++ b/include/assimp/IOSystem.hpp @@ -53,6 +53,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endif #include "types.h" + +#include + namespace Assimp { class IOStream; @@ -170,10 +173,19 @@ public: */ inline bool ComparePaths (const std::string& one, const std::string& second) const; + + virtual bool PushDirectory( const std::string &path ); + virtual const std::string &CurrentDirectory() const; + virtual size_t StackSize() const; + virtual bool PopDirectory(); + +private: + std::vector m_pathStack; }; // ---------------------------------------------------------------------------- -AI_FORCE_INLINE IOSystem::IOSystem() +AI_FORCE_INLINE IOSystem::IOSystem() : + m_pathStack() { // empty } @@ -220,6 +232,43 @@ inline bool IOSystem::ComparePaths (const std::string& one, } // ---------------------------------------------------------------------------- +inline bool IOSystem::PushDirectory( const std::string &path ) { + if ( path.empty() ) { + return false; + } + + m_pathStack.push_back( path ); + + return true; +} + +// ---------------------------------------------------------------------------- +inline const std::string &IOSystem::CurrentDirectory() const { + if ( m_pathStack.empty() ) { + static const std::string Dummy(""); + return Dummy; + } + return m_pathStack[ m_pathStack.size()-1 ]; +} + +// ---------------------------------------------------------------------------- +inline size_t IOSystem::StackSize() const { + return m_pathStack.size(); +} + +// ---------------------------------------------------------------------------- +inline bool IOSystem::PopDirectory() { + if ( m_pathStack.empty() ) { + return false; + } + + m_pathStack.pop_back(); + + return true; +} + +// ---------------------------------------------------------------------------- + } //!ns Assimp #endif //AI_IOSYSTEM_H_INC diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a93bcf91f..d3bd490e8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -24,6 +24,7 @@ SET( TEST_SRCS unit/utGenNormals.cpp unit/utImporter.cpp unit/utImproveCacheLocality.cpp + unit/utIOSystem.cpp unit/utJoinVertices.cpp unit/utLimitBoneWeights.cpp unit/utMaterialSystem.cpp diff --git a/test/unit/utIOSystem.cpp b/test/unit/utIOSystem.cpp new file mode 100644 index 000000000..b44976488 --- /dev/null +++ b/test/unit/utIOSystem.cpp @@ -0,0 +1,94 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2014, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +#include "UnitTestPCH.h" + +#include + +using namespace std; +using namespace Assimp; + +static const string Sep = "/"; +class TestIOSystem : public IOSystem { +public: + TestIOSystem() : IOSystem() {} + virtual ~TestIOSystem() {} + virtual bool Exists( const char* ) const { + return true; + } + virtual char getOsSeparator() const { + return Sep[ 0 ]; + } + + virtual IOStream* Open(const char* pFile, const char* pMode = "rb") { + return NULL; + } + + virtual void Close( IOStream* pFile) { + // empty + } +}; + +class IOSystemTest : public ::testing::Test { +public: + virtual void SetUp() { pImp = new TestIOSystem(); } + virtual void TearDown() { delete pImp; } + +protected: + TestIOSystem* pImp; +}; + +/* +virtual bool PushDirectory( const std::string &path ); +virtual const std::string &CurrentDirectory() const; +virtual bool PopDirectory(); +*/ + +TEST_F( IOSystemTest, accessDirectoryStackTest ) { + EXPECT_FALSE( pImp->PopDirectory() ); + EXPECT_EQ( 0, pImp->StackSize() ); + EXPECT_FALSE( pImp->PushDirectory( "" ) ); + std::string path = "test/"; + EXPECT_TRUE( pImp->PushDirectory( path ) ); + EXPECT_EQ( 1, pImp->StackSize() ); + EXPECT_EQ( path, pImp->CurrentDirectory() ); + EXPECT_TRUE( pImp->PopDirectory() ); + EXPECT_EQ( 0, pImp->StackSize() ); +} From e9937ab0f7e8352fbb0db9adeaf46437c193c2a7 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 30 Aug 2015 15:37:56 +0200 Subject: [PATCH 21/25] IOSystem: add missing documentation. --- code/ObjFileParser.cpp | 6 ++---- include/assimp/IOSystem.hpp | 28 ++++++++++++++++++++++------ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/code/ObjFileParser.cpp b/code/ObjFileParser.cpp index 346c734fc..027d99543 100644 --- a/code/ObjFileParser.cpp +++ b/code/ObjFileParser.cpp @@ -544,12 +544,10 @@ void ObjFileParser::getMaterialLib() } else { absName = strMatName; } - //IOStream *pFile = m_pIO->Open( strMatName ); IOStream *pFile = m_pIO->Open( absName ); - if (!pFile ) - { - DefaultLogger::get()->error("OBJ: Unable to locate material file " + strMatName ); + if (!pFile ) { + DefaultLogger::get()->error( "OBJ: Unable to locate material file " + strMatName ); m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); return; } diff --git a/include/assimp/IOSystem.hpp b/include/assimp/IOSystem.hpp index 81b3e910a..321509de6 100644 --- a/include/assimp/IOSystem.hpp +++ b/include/assimp/IOSystem.hpp @@ -105,18 +105,14 @@ public: * @param pFile Path to the file * @return true if there is a file with this path, else false. */ - virtual bool Exists( const char* pFile) const = 0; - - // ------------------------------------------------------------------- /** @brief Returns the system specific directory separator * @return System specific directory separator */ virtual char getOsSeparator() const = 0; - // ------------------------------------------------------------------- /** @brief Open a new file with a given path. * @@ -142,8 +138,6 @@ public: inline IOStream* Open(const std::string& pFile, const std::string& pMode = std::string("rb")); - - // ------------------------------------------------------------------- /** @brief Closes the given file and releases all resources * associated with it. @@ -174,9 +168,31 @@ public: inline bool ComparePaths (const std::string& one, const std::string& second) const; + // ------------------------------------------------------------------- + /** @brief Pushes a new directory onto the directory stack. + * @param path Path to push onto the stack. + * @return True, when push was successful, false if path is empty. + */ virtual bool PushDirectory( const std::string &path ); + + // ------------------------------------------------------------------- + /** @brief Returns the top directory from the stack. + * @return The directory on the top of the stack. + * Returns empty when no directory was pushed to the stack. + */ virtual const std::string &CurrentDirectory() const; + + // ------------------------------------------------------------------- + /** @brief Returns the number of directories stored on the stack. + * @return The number of directories of the stack. + */ virtual size_t StackSize() const; + + // ------------------------------------------------------------------- + /** @brief Pops the top directory from the stack. + * @return True, when a directory was on the stack. False if no + * directory was on the stack. + */ virtual bool PopDirectory(); private: From fff2c4141e33ab3edf4d7b7ba31c68f12c91c1c4 Mon Sep 17 00:00:00 2001 From: abma Date: Wed, 2 Sep 2015 09:56:58 +0200 Subject: [PATCH 22/25] fix #634 --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a1a13ef6f..166791a04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,7 +63,9 @@ if( CMAKE_COMPILER_IS_MINGW ) endif() if((CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) AND NOT CMAKE_COMPILER_IS_MINGW) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") # this is a very important switch and some libraries seem now to have it.... + if (BUILD_SHARED_LIBS AND CMAKE_SIZEOF_VOID_P EQUAL 8) # -fPIC is only required for shared libs on 64 bit + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + endif() # hide all not-exported symbols set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -Wall" ) elseif(MSVC) From d49e47c25cca464a3fe088d351f76cd006e09f24 Mon Sep 17 00:00:00 2001 From: abma Date: Wed, 2 Sep 2015 10:04:28 +0200 Subject: [PATCH 23/25] fix #431 --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 166791a04..3f68db817 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ execute_process( WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE GIT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET ) # Get the latest abbreviated commit hash of the working branch @@ -35,6 +36,7 @@ execute_process( WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE GIT_COMMIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET ) if(NOT GIT_COMMIT_HASH) From ba37e362e8e2d584b68c1374b10318a16d8f37af Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 4 Sep 2015 17:10:51 +0200 Subject: [PATCH 24/25] Sample: fix https://github.com/assimp/assimp/issues/113, remove deprecated makefile. --- samples/SimpleOpenGL/makefile | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 samples/SimpleOpenGL/makefile diff --git a/samples/SimpleOpenGL/makefile b/samples/SimpleOpenGL/makefile deleted file mode 100644 index d8c7425e4..000000000 --- a/samples/SimpleOpenGL/makefile +++ /dev/null @@ -1,28 +0,0 @@ - -# --------------------------------------------------------------------------- -# Makefile for assimp/Sample_SimpleOpenGL -# aramis_acg@users.sourceforge.net -# -# Usage: make - -# TARGETS: -# all Build the sample + assimp itself -# clean Cleanup all object files -# --------------------------------------------------------------------------- - -VPATH = ./usr/include/ -# Include flags for gcc -INCLUDEFLAGS = -I../../include - -# Library flags for gcc -LIBFLAGS = -L../../bin/gcc - -# Output name of executable -OUTPUT = samplegl - -all: $(OBJECTS) - cd ../../code/ && $(MAKE) static - gcc -o $(OUTPUT) $(INCLUDEFLAGS) -s Sample_SimpleOpenGL.c $(LIBFLAGS) -Wl,-Bstatic -lassimp -Wl,-Bdynamic -lstdc++ -lglut -.PHONY: clean -clean: - -rm *.o From e159728307f17888b8b3ab4f23cf2143562c1c10 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 15 Sep 2015 08:55:40 +0200 Subject: [PATCH 25/25] Update Readme.md Update contect info: now add an irc channel on freenode --- Readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Readme.md b/Readme.md index bc94885b3..ed1911a78 100644 --- a/Readme.md +++ b/Readme.md @@ -113,6 +113,7 @@ If the docs don't solve your problem, ask on [StackOverflow](http://stackoverflo For development discussions, there is also a (very low-volume) mailing list, _assimp-discussions_ [(subscribe here)]( https://lists.sourceforge.net/lists/listinfo/assimp-discussions) +And we also have an IRC-channel at freenode: #assetimporterlib . ### Contributing ### Contributions to assimp are highly appreciated. The easiest way to get involved is to submit