From d64e1bde1383b19f3c17084eeea9b63541605556 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 12 Jul 2019 11:29:35 +0100 Subject: [PATCH] First pass of Collada ZAE support Reads the manifest and loads the DAE Does not yet load embedded textures --- code/Collada/ColladaLoader.cpp | 1125 ++++++----- code/Collada/ColladaLoader.h | 2 +- code/Collada/ColladaParser.cpp | 2442 ++++++++++++----------- code/Collada/ColladaParser.h | 4 + test/models/Collada/duck.zae | Bin 0 -> 132801 bytes test/models/Collada/duck_nomanifest.zae | Bin 0 -> 132634 bytes 6 files changed, 1852 insertions(+), 1721 deletions(-) create mode 100644 test/models/Collada/duck.zae create mode 100644 test/models/Collada/duck_nomanifest.zae diff --git a/code/Collada/ColladaLoader.cpp b/code/Collada/ColladaLoader.cpp index 81db957d5..40b2b0811 100644 --- a/code/Collada/ColladaLoader.cpp +++ b/code/Collada/ColladaLoader.cpp @@ -60,6 +60,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include "time.h" #include "math.h" @@ -75,30 +76,30 @@ static const aiImporterDesc desc = { "", "", "http://collada.org", - aiImporterFlags_SupportTextFlavour, + aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportCompressedFlavour, 1, 3, 1, 5, - "dae" + "dae zae" }; // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer ColladaLoader::ColladaLoader() -: mFileName() -, mMeshIndexByID() -, mMaterialIndexByName() -, mMeshes() -, newMats() -, mCameras() -, mLights() -, mTextures() -, mAnims() -, noSkeletonMesh( false ) -, ignoreUpDirection(false) -, useColladaName( false ) -, mNodeNameCounter( 0 ) { + : mFileName() + , mMeshIndexByID() + , mMaterialIndexByName() + , mMeshes() + , newMats() + , mCameras() + , mLights() + , mTextures() + , mAnims() + , noSkeletonMesh(false) + , ignoreUpDirection(false) + , useColladaName(false) + , mNodeNameCounter(0) { // empty } @@ -110,16 +111,27 @@ ColladaLoader::~ColladaLoader() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { +bool ColladaLoader::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { // check file extension std::string extension = GetExtension(pFile); - if (extension == "dae") { - return true; + bool readSig = checkSig && (pIOHandler != nullptr); + + if (!readSig) { + if (extension == "dae" || extension == "zae") { + return true; + } + } + + if (readSig) { + // Look for a DAE file inside, but don't extract it + ZipArchiveIOSystem zip_archive(pIOHandler, pFile); + if (zip_archive.isOpen()) + return !ColladaParser::ReadZaeManifest(zip_archive).empty(); } // XML - too generic, we need to open the file and search for typical keywords - if( extension == "xml" || !extension.length() || checkSig) { + if (extension == "xml" || !extension.length() || checkSig) { /* If CanRead() is called in order to check whether we * support a specific file extension in general pIOHandler * might be NULL and it's our duty to return true here. @@ -127,8 +139,8 @@ bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, boo if (!pIOHandler) { return true; } - static const char* tokens[] = {"GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; - ignoreUpDirection = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION,0) != 0; - useColladaName = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES,0) != 0; + noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES, 0) != 0; + ignoreUpDirection = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION, 0) != 0; + useColladaName = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES, 0) != 0; } // ------------------------------------------------------------------------------------------------ // Get file extension list -const aiImporterDesc* ColladaLoader::GetInfo () const { +const aiImporterDesc* ColladaLoader::GetInfo() const { return &desc; } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. -void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { +void ColladaLoader::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { mFileName = pFile; // clean all member arrays - just for safety, it should work even if we did not @@ -164,46 +176,46 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I mAnims.clear(); // parse the input file - ColladaParser parser( pIOHandler, pFile); + ColladaParser parser(pIOHandler, pFile); - if( !parser.mRootNode) - throw DeadlyImportError( "Collada: File came out empty. Something is wrong here."); + if (!parser.mRootNode) + throw DeadlyImportError("Collada: File came out empty. Something is wrong here."); // reserve some storage to avoid unnecessary reallocs - newMats.reserve(parser.mMaterialLibrary.size()*2); - mMeshes.reserve(parser.mMeshLibrary.size()*2); + newMats.reserve(parser.mMaterialLibrary.size() * 2); + mMeshes.reserve(parser.mMeshLibrary.size() * 2); mCameras.reserve(parser.mCameraLibrary.size()); mLights.reserve(parser.mLightLibrary.size()); // create the materials first, for the meshes to find - BuildMaterials( parser, pScene); + BuildMaterials(parser, pScene); // build the node hierarchy from it - pScene->mRootNode = BuildHierarchy( parser, parser.mRootNode); + pScene->mRootNode = BuildHierarchy(parser, parser.mRootNode); // ... then fill the materials with the now adjusted settings FillMaterials(parser, pScene); // Apply unit-size scale calculation - pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0, 0, 0, - 0, parser.mUnitSize, 0, 0, - 0, 0, parser.mUnitSize, 0, - 0, 0, 0, 1); - if( !ignoreUpDirection ) { - // Convert to Y_UP, if different orientation - if( parser.mUpDirection == ColladaParser::UP_X) - pScene->mRootNode->mTransformation *= aiMatrix4x4( - 0, -1, 0, 0, - 1, 0, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - else if( parser.mUpDirection == ColladaParser::UP_Z) - pScene->mRootNode->mTransformation *= aiMatrix4x4( - 1, 0, 0, 0, - 0, 0, 1, 0, - 0, -1, 0, 0, - 0, 0, 0, 1); + pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0, 0, 0, + 0, parser.mUnitSize, 0, 0, + 0, 0, parser.mUnitSize, 0, + 0, 0, 0, 1); + if (!ignoreUpDirection) { + // Convert to Y_UP, if different orientation + if (parser.mUpDirection == ColladaParser::UP_X) + pScene->mRootNode->mTransformation *= aiMatrix4x4( + 0, -1, 0, 0, + 1, 0, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + else if (parser.mUpDirection == ColladaParser::UP_Z) + pScene->mRootNode->mTransformation *= aiMatrix4x4( + 1, 0, 0, 0, + 0, 0, 1, 0, + 0, -1, 0, 0, + 0, 0, 0, 1); } // Store scene metadata @@ -218,19 +230,19 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I } // store all meshes - StoreSceneMeshes( pScene); + StoreSceneMeshes(pScene); // store all materials - StoreSceneMaterials( pScene); + StoreSceneMaterials(pScene); // store all lights - StoreSceneLights( pScene); + StoreSceneLights(pScene); // store all cameras - StoreSceneCameras( pScene); + StoreSceneCameras(pScene); // store all animations - StoreAnimations( pScene, parser); + StoreAnimations(pScene, parser); // If no meshes have been loaded, it's probably just an animated skeleton. @@ -245,56 +257,56 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I // ------------------------------------------------------------------------------------------------ // Recursively constructs a scene node for the given parser node and returns it. -aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode) { +aiNode* ColladaLoader::BuildHierarchy(const ColladaParser& pParser, const Collada::Node* pNode) { // create a node for it aiNode* node = new aiNode(); // find a name for the new node. It's more complicated than you might think - node->mName.Set( FindNameForNode( pNode)); + node->mName.Set(FindNameForNode(pNode)); // calculate the transformation matrix for it - node->mTransformation = pParser.CalculateResultTransform( pNode->mTransforms); + node->mTransformation = pParser.CalculateResultTransform(pNode->mTransforms); // now resolve node instances std::vector instances; - ResolveNodeInstances(pParser,pNode,instances); + ResolveNodeInstances(pParser, pNode, instances); // add children. first the *real* ones - node->mNumChildren = static_cast(pNode->mChildren.size()+instances.size()); + node->mNumChildren = static_cast(pNode->mChildren.size() + instances.size()); node->mChildren = new aiNode*[node->mNumChildren]; - for( size_t a = 0; a < pNode->mChildren.size(); ++a) { - node->mChildren[a] = BuildHierarchy( pParser, pNode->mChildren[a]); + for (size_t a = 0; a < pNode->mChildren.size(); ++a) { + node->mChildren[a] = BuildHierarchy(pParser, pNode->mChildren[a]); node->mChildren[a]->mParent = node; } // ... and finally the resolved node instances - for( size_t a = 0; a < instances.size(); ++a) { - node->mChildren[pNode->mChildren.size() + a] = BuildHierarchy( pParser, instances[a]); + for (size_t a = 0; a < instances.size(); ++a) { + node->mChildren[pNode->mChildren.size() + a] = BuildHierarchy(pParser, instances[a]); node->mChildren[pNode->mChildren.size() + a]->mParent = node; } // construct meshes - BuildMeshesForNode( pParser, pNode, node); + BuildMeshesForNode(pParser, pNode, node); // construct cameras BuildCamerasForNode(pParser, pNode, node); // construct lights BuildLightsForNode(pParser, pNode, node); - + return node; } // ------------------------------------------------------------------------------------------------ // Resolve node instances -void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode, - std::vector& resolved) { +void ColladaLoader::ResolveNodeInstances(const ColladaParser& pParser, const Collada::Node* pNode, + std::vector& resolved) { // reserve enough storage resolved.reserve(pNode->mNodeInstances.size()); // ... and iterate through all nodes to be instanced as children of pNode - for (const auto &nodeInst: pNode->mNodeInstances) { + for (const auto &nodeInst : pNode->mNodeInstances) { // find the corresponding node in the library const ColladaParser::NodeLibrary::const_iterator itt = pParser.mNodeLibrary.find(nodeInst.mNode); const Collada::Node* nd = itt == pParser.mNodeLibrary.end() ? NULL : (*itt).second; @@ -318,7 +330,7 @@ void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Co // ------------------------------------------------------------------------------------------------ // Resolve UV channels void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler, - const Collada::SemanticMappingTable& table) { + const Collada::SemanticMappingTable& table) { std::map::const_iterator it = table.mMap.find(sampler.mUVChannel); if (it != table.mMap.end()) { if (it->second.mType != Collada::IT_Texcoord) { @@ -331,12 +343,12 @@ void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler // ------------------------------------------------------------------------------------------------ // Builds lights for the given node and references them -void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget) { - for( const Collada::LightInstance& lid : pNode->mLights) { +void ColladaLoader::BuildLightsForNode(const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget) { + for (const Collada::LightInstance& lid : pNode->mLights) { // find the referred light - ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find( lid.mLight); - if( srcLightIt == pParser.mLightLibrary.end()) { - ASSIMP_LOG_WARN_F("Collada: Unable to find light for ID \"" , lid.mLight , "\". Skipping."); + ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find(lid.mLight); + if (srcLightIt == pParser.mLightLibrary.end()) { + ASSIMP_LOG_WARN_F("Collada: Unable to find light for ID \"", lid.mLight, "\". Skipping."); continue; } const Collada::Light* srcLight = &srcLightIt->second; @@ -347,7 +359,7 @@ void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Coll out->mType = (aiLightSourceType)srcLight->mType; // collada lights point in -Z by default, rest is specified in node transform - out->mDirection = aiVector3D(0.f,0.f,-1.f); + out->mDirection = aiVector3D(0.f, 0.f, -1.f); out->mAttenuationConstant = srcLight->mAttConstant; out->mAttenuationLinear = srcLight->mAttLinear; @@ -357,7 +369,8 @@ void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Coll if (out->mType == aiLightSource_AMBIENT) { out->mColorDiffuse = out->mColorSpecular = aiColor3D(0, 0, 0); out->mColorAmbient = srcLight->mColor*srcLight->mIntensity; - } else { + } + else { // collada doesn't differentiate between these color types out->mColorDiffuse = out->mColorSpecular = srcLight->mColor*srcLight->mIntensity; out->mColorAmbient = aiColor3D(0, 0, 0); @@ -365,23 +378,25 @@ void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Coll // convert falloff angle and falloff exponent in our representation, if given if (out->mType == aiLightSource_SPOT) { - out->mAngleInnerCone = AI_DEG_TO_RAD( srcLight->mFalloffAngle ); + out->mAngleInnerCone = AI_DEG_TO_RAD(srcLight->mFalloffAngle); // ... some extension magic. - if (srcLight->mOuterAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET*(1-1e-6f)) { + if (srcLight->mOuterAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET * (1 - 1e-6f)) { // ... some deprecation magic. - if (srcLight->mPenumbraAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET*(1-1e-6f)) { + if (srcLight->mPenumbraAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET * (1 - 1e-6f)) { // Need to rely on falloff_exponent. I don't know how to interpret it, so I need to guess .... // epsilon chosen to be 0.1 - out->mAngleOuterCone = std::acos(std::pow(0.1f,1.f/srcLight->mFalloffExponent))+ - out->mAngleInnerCone; - } else { - out->mAngleOuterCone = out->mAngleInnerCone + AI_DEG_TO_RAD( srcLight->mPenumbraAngle ); - if (out->mAngleOuterCone < out->mAngleInnerCone) - std::swap(out->mAngleInnerCone,out->mAngleOuterCone); + out->mAngleOuterCone = std::acos(std::pow(0.1f, 1.f / srcLight->mFalloffExponent)) + + out->mAngleInnerCone; } - } else { - out->mAngleOuterCone = AI_DEG_TO_RAD( srcLight->mOuterAngle ); + else { + out->mAngleOuterCone = out->mAngleInnerCone + AI_DEG_TO_RAD(srcLight->mPenumbraAngle); + if (out->mAngleOuterCone < out->mAngleInnerCone) + std::swap(out->mAngleInnerCone, out->mAngleOuterCone); + } + } + else { + out->mAngleOuterCone = AI_DEG_TO_RAD(srcLight->mOuterAngle); } } @@ -392,12 +407,12 @@ void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Coll // ------------------------------------------------------------------------------------------------ // Builds cameras for the given node and references them -void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget) { - for( const Collada::CameraInstance& cid : pNode->mCameras) { +void ColladaLoader::BuildCamerasForNode(const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget) { + for (const Collada::CameraInstance& cid : pNode->mCameras) { // find the referred light - ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find( cid.mCamera); - if( srcCameraIt == pParser.mCameraLibrary.end()) { - ASSIMP_LOG_WARN_F("Collada: Unable to find camera for ID \"" , cid.mCamera , "\". Skipping."); + ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find(cid.mCamera); + if (srcCameraIt == pParser.mCameraLibrary.end()) { + ASSIMP_LOG_WARN_F("Collada: Unable to find camera for ID \"", cid.mCamera, "\". Skipping."); continue; } const Collada::Camera* srcCamera = &srcCameraIt->second; @@ -412,7 +427,7 @@ void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Col out->mName = pTarget->mName; // collada cameras point in -Z by default, rest is specified in node transform - out->mLookAt = aiVector3D(0.f,0.f,-1.f); + out->mLookAt = aiVector3D(0.f, 0.f, -1.f); // near/far z is already ok out->mClipPlaneFar = srcCamera->mZFar; @@ -432,7 +447,7 @@ void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Col std::tan(AI_DEG_TO_RAD(srcCamera->mVerFov)); } } - else if (srcCamera->mAspect != 10e10f && srcCamera->mVerFov != 10e10f) { + else if (srcCamera->mAspect != 10e10f && srcCamera->mVerFov != 10e10f) { out->mHorizontalFOV = 2.0f * AI_RAD_TO_DEG(std::atan(srcCamera->mAspect * std::tan(AI_DEG_TO_RAD(srcCamera->mVerFov) * 0.5f))); } @@ -447,103 +462,106 @@ void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Col // ------------------------------------------------------------------------------------------------ // Builds meshes for the given node and references them -void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget) { +void ColladaLoader::BuildMeshesForNode(const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget) { // accumulated mesh references by this node std::vector newMeshRefs; newMeshRefs.reserve(pNode->mMeshes.size()); // add a mesh for each subgroup in each collada mesh - for( const Collada::MeshInstance& mid : pNode->mMeshes) { + for (const Collada::MeshInstance& mid : pNode->mMeshes) { const Collada::Mesh* srcMesh = nullptr; const Collada::Controller* srcController = nullptr; // find the referred mesh - ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find( mid.mMeshOrController); - if( srcMeshIt == pParser.mMeshLibrary.end()) { + ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find(mid.mMeshOrController); + if (srcMeshIt == pParser.mMeshLibrary.end()) { // if not found in the mesh-library, it might also be a controller referring to a mesh - ColladaParser::ControllerLibrary::const_iterator srcContrIt = pParser.mControllerLibrary.find( mid.mMeshOrController); - if( srcContrIt != pParser.mControllerLibrary.end()) { + ColladaParser::ControllerLibrary::const_iterator srcContrIt = pParser.mControllerLibrary.find(mid.mMeshOrController); + if (srcContrIt != pParser.mControllerLibrary.end()) { srcController = &srcContrIt->second; - srcMeshIt = pParser.mMeshLibrary.find( srcController->mMeshId); - if( srcMeshIt != pParser.mMeshLibrary.end()) { + srcMeshIt = pParser.mMeshLibrary.find(srcController->mMeshId); + if (srcMeshIt != pParser.mMeshLibrary.end()) { srcMesh = srcMeshIt->second; } } - if( !srcMesh) { - ASSIMP_LOG_WARN_F( "Collada: Unable to find geometry for ID \"", mid.mMeshOrController, "\". Skipping." ); + if (!srcMesh) { + ASSIMP_LOG_WARN_F("Collada: Unable to find geometry for ID \"", mid.mMeshOrController, "\". Skipping."); continue; } - } else { + } + else { // ID found in the mesh library -> direct reference to an unskinned mesh srcMesh = srcMeshIt->second; } // build a mesh for each of its subgroups size_t vertexStart = 0, faceStart = 0; - for( size_t sm = 0; sm < srcMesh->mSubMeshes.size(); ++sm) { + for (size_t sm = 0; sm < srcMesh->mSubMeshes.size(); ++sm) { const Collada::SubMesh& submesh = srcMesh->mSubMeshes[sm]; - if( submesh.mNumFaces == 0) { + if (submesh.mNumFaces == 0) { continue; } // find material assigned to this submesh std::string meshMaterial; - std::map::const_iterator meshMatIt = mid.mMaterials.find( submesh.mMaterial); + std::map::const_iterator meshMatIt = mid.mMaterials.find(submesh.mMaterial); const Collada::SemanticMappingTable* table = nullptr; - if( meshMatIt != mid.mMaterials.end()) { + if (meshMatIt != mid.mMaterials.end()) { table = &meshMatIt->second; meshMaterial = table->mMatName; - } else { - ASSIMP_LOG_WARN_F( "Collada: No material specified for subgroup <", submesh.mMaterial, "> in geometry <", - mid.mMeshOrController, ">." ); - if( !mid.mMaterials.empty() ) { + } + else { + ASSIMP_LOG_WARN_F("Collada: No material specified for subgroup <", submesh.mMaterial, "> in geometry <", + mid.mMeshOrController, ">."); + if (!mid.mMaterials.empty()) { meshMaterial = mid.mMaterials.begin()->second.mMatName; } } // OK ... here the *real* fun starts ... we have the vertex-input-to-effect-semantic-table // given. The only mapping stuff which we do actually support is the UV channel. - std::map::const_iterator matIt = mMaterialIndexByName.find( meshMaterial); + std::map::const_iterator matIt = mMaterialIndexByName.find(meshMaterial); unsigned int matIdx = 0; - if( matIt != mMaterialIndexByName.end()) { + if (matIt != mMaterialIndexByName.end()) { matIdx = static_cast(matIt->second); } - if (table && !table->mMap.empty() ) { + if (table && !table->mMap.empty()) { std::pair& mat = newMats[matIdx]; // Iterate through all texture channels assigned to the effect and // check whether we have mapping information for it. - ApplyVertexToEffectSemanticMapping(mat.first->mTexDiffuse, *table); - ApplyVertexToEffectSemanticMapping(mat.first->mTexAmbient, *table); - ApplyVertexToEffectSemanticMapping(mat.first->mTexSpecular, *table); - ApplyVertexToEffectSemanticMapping(mat.first->mTexEmissive, *table); - ApplyVertexToEffectSemanticMapping(mat.first->mTexTransparent,*table); - ApplyVertexToEffectSemanticMapping(mat.first->mTexBump, *table); + ApplyVertexToEffectSemanticMapping(mat.first->mTexDiffuse, *table); + ApplyVertexToEffectSemanticMapping(mat.first->mTexAmbient, *table); + ApplyVertexToEffectSemanticMapping(mat.first->mTexSpecular, *table); + ApplyVertexToEffectSemanticMapping(mat.first->mTexEmissive, *table); + ApplyVertexToEffectSemanticMapping(mat.first->mTexTransparent, *table); + ApplyVertexToEffectSemanticMapping(mat.first->mTexBump, *table); } // built lookup index of the Mesh-Submesh-Material combination - ColladaMeshIndex index( mid.mMeshOrController, sm, meshMaterial); + ColladaMeshIndex index(mid.mMeshOrController, sm, meshMaterial); // if we already have the mesh at the library, just add its index to the node's array - std::map::const_iterator dstMeshIt = mMeshIndexByID.find( index); - if( dstMeshIt != mMeshIndexByID.end()) { - newMeshRefs.push_back( dstMeshIt->second); - } else { + std::map::const_iterator dstMeshIt = mMeshIndexByID.find(index); + if (dstMeshIt != mMeshIndexByID.end()) { + newMeshRefs.push_back(dstMeshIt->second); + } + else { // else we have to add the mesh to the collection and store its newly assigned index at the node - aiMesh* dstMesh = CreateMesh( pParser, srcMesh, submesh, srcController, vertexStart, faceStart); + aiMesh* dstMesh = CreateMesh(pParser, srcMesh, submesh, srcController, vertexStart, faceStart); // store the mesh, and store its new index in the node - newMeshRefs.push_back( mMeshes.size()); + newMeshRefs.push_back(mMeshes.size()); mMeshIndexByID[index] = mMeshes.size(); - mMeshes.push_back( dstMesh); + mMeshes.push_back(dstMesh); vertexStart += dstMesh->mNumVertices; faceStart += submesh.mNumFaces; // assign the material index dstMesh->mMaterialIndex = matIdx; - if(dstMesh->mName.length == 0) { + if (dstMesh->mName.length == 0) { dstMesh->mName = mid.mMeshOrController; } } @@ -552,7 +570,7 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll // now place all mesh references we gathered in the target node pTarget->mNumMeshes = static_cast(newMeshRefs.size()); - if( newMeshRefs.size()) { + if (newMeshRefs.size()) { struct UIntTypeConverter { unsigned int operator()(const size_t& v) const { return static_cast(v); @@ -560,76 +578,76 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll }; pTarget->mMeshes = new unsigned int[pTarget->mNumMeshes]; - std::transform( newMeshRefs.begin(), newMeshRefs.end(), pTarget->mMeshes, UIntTypeConverter()); + std::transform(newMeshRefs.begin(), newMeshRefs.end(), pTarget->mMeshes, UIntTypeConverter()); } } // ------------------------------------------------------------------------------------------------ // Find mesh from either meshes or morph target meshes aiMesh *ColladaLoader::findMesh(std::string meshid) { - for (unsigned int i = 0; i < mMeshes.size(); ++i ) { + for (unsigned int i = 0; i < mMeshes.size(); ++i) { if (std::string(mMeshes[i]->mName.data) == meshid) { return mMeshes[i]; } } - for (unsigned int i = 0; i < mTargetMeshes.size(); ++i ) { + for (unsigned int i = 0; i < mTargetMeshes.size(); ++i) { if (std::string(mTargetMeshes[i]->mName.data) == meshid) { return mTargetMeshes[i]; } } - + return nullptr; } // ------------------------------------------------------------------------------------------------ // Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh -aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh, - const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace) { +aiMesh* ColladaLoader::CreateMesh(const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh, + const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace) { std::unique_ptr dstMesh(new aiMesh); dstMesh->mName = pSrcMesh->mName; // count the vertices addressed by its faces - const size_t numVertices = std::accumulate( pSrcMesh->mFaceSize.begin() + pStartFace, + const size_t numVertices = std::accumulate(pSrcMesh->mFaceSize.begin() + pStartFace, pSrcMesh->mFaceSize.begin() + pStartFace + pSubMesh.mNumFaces, size_t(0)); // copy positions dstMesh->mNumVertices = static_cast(numVertices); dstMesh->mVertices = new aiVector3D[numVertices]; - std::copy( pSrcMesh->mPositions.begin() + pStartVertex, pSrcMesh->mPositions.begin() + + std::copy(pSrcMesh->mPositions.begin() + pStartVertex, pSrcMesh->mPositions.begin() + pStartVertex + numVertices, dstMesh->mVertices); // normals, if given. HACK: (thom) Due to the glorious Collada spec we never // know if we have the same number of normals as there are positions. So we // also ignore any vertex attribute if it has a different count - if( pSrcMesh->mNormals.size() >= pStartVertex + numVertices) { + if (pSrcMesh->mNormals.size() >= pStartVertex + numVertices) { dstMesh->mNormals = new aiVector3D[numVertices]; - std::copy( pSrcMesh->mNormals.begin() + pStartVertex, pSrcMesh->mNormals.begin() + + std::copy(pSrcMesh->mNormals.begin() + pStartVertex, pSrcMesh->mNormals.begin() + pStartVertex + numVertices, dstMesh->mNormals); } // tangents, if given. - if( pSrcMesh->mTangents.size() >= pStartVertex + numVertices) { + if (pSrcMesh->mTangents.size() >= pStartVertex + numVertices) { dstMesh->mTangents = new aiVector3D[numVertices]; - std::copy( pSrcMesh->mTangents.begin() + pStartVertex, pSrcMesh->mTangents.begin() + + std::copy(pSrcMesh->mTangents.begin() + pStartVertex, pSrcMesh->mTangents.begin() + pStartVertex + numVertices, dstMesh->mTangents); } // bitangents, if given. - if( pSrcMesh->mBitangents.size() >= pStartVertex + numVertices) { + if (pSrcMesh->mBitangents.size() >= pStartVertex + numVertices) { dstMesh->mBitangents = new aiVector3D[numVertices]; - std::copy( pSrcMesh->mBitangents.begin() + pStartVertex, pSrcMesh->mBitangents.begin() + + std::copy(pSrcMesh->mBitangents.begin() + pStartVertex, pSrcMesh->mBitangents.begin() + pStartVertex + numVertices, dstMesh->mBitangents); } // same for texturecoords, as many as we have // empty slots are not allowed, need to pack and adjust UV indexes accordingly - for( size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) { - if( pSrcMesh->mTexCoords[a].size() >= pStartVertex + numVertices) { + for (size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { + if (pSrcMesh->mTexCoords[a].size() >= pStartVertex + numVertices) { dstMesh->mTextureCoords[real] = new aiVector3D[numVertices]; - for( size_t b = 0; b < numVertices; ++b) { - dstMesh->mTextureCoords[real][b] = pSrcMesh->mTexCoords[a][pStartVertex+b]; + for (size_t b = 0; b < numVertices; ++b) { + dstMesh->mTextureCoords[real][b] = pSrcMesh->mTexCoords[a][pStartVertex + b]; } dstMesh->mNumUVComponents[real] = pSrcMesh->mNumUVComponents[a]; @@ -638,10 +656,10 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: } // same for vertex colors, as many as we have. again the same packing to avoid empty slots - for( size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a ) { - if( pSrcMesh->mColors[a].size() >= pStartVertex + numVertices) { + for (size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) { + if (pSrcMesh->mColors[a].size() >= pStartVertex + numVertices) { dstMesh->mColors[real] = new aiColor4D[numVertices]; - std::copy( pSrcMesh->mColors[a].begin() + pStartVertex, pSrcMesh->mColors[a].begin() + pStartVertex + numVertices,dstMesh->mColors[real]); + std::copy(pSrcMesh->mColors[a].begin() + pStartVertex, pSrcMesh->mColors[a].begin() + pStartVertex + numVertices, dstMesh->mColors[real]); ++real; } } @@ -650,12 +668,12 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: size_t vertex = 0; dstMesh->mNumFaces = static_cast(pSubMesh.mNumFaces); dstMesh->mFaces = new aiFace[dstMesh->mNumFaces]; - for( size_t a = 0; a < dstMesh->mNumFaces; ++a) { - size_t s = pSrcMesh->mFaceSize[ pStartFace + a]; + for (size_t a = 0; a < dstMesh->mNumFaces; ++a) { + size_t s = pSrcMesh->mFaceSize[pStartFace + a]; aiFace& face = dstMesh->mFaces[a]; face.mNumIndices = static_cast(s); face.mIndices = new unsigned int[s]; - for( size_t b = 0; b < s; ++b) { + for (size_t b = 0; b < s; ++b) { face.mIndices[b] = static_cast(vertex++); } } @@ -665,25 +683,25 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: std::vector targetWeights; Collada::MorphMethod method = Collada::Normalized; - for(std::map::const_iterator it = pParser.mControllerLibrary.begin(); - it != pParser.mControllerLibrary.end(); it++) { + for (std::map::const_iterator it = pParser.mControllerLibrary.begin(); + it != pParser.mControllerLibrary.end(); it++) { const Collada::Controller &c = it->second; - const Collada::Mesh* baseMesh = pParser.ResolveLibraryReference( pParser.mMeshLibrary, c.mMeshId); + const Collada::Mesh* baseMesh = pParser.ResolveLibraryReference(pParser.mMeshLibrary, c.mMeshId); if (c.mType == Collada::Morph && baseMesh->mName == pSrcMesh->mName) { - const Collada::Accessor& targetAccessor = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, c.mMorphTarget); - const Collada::Accessor& weightAccessor = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, c.mMorphWeight); - const Collada::Data& targetData = pParser.ResolveLibraryReference( pParser.mDataLibrary, targetAccessor.mSource); - const Collada::Data& weightData = pParser.ResolveLibraryReference( pParser.mDataLibrary, weightAccessor.mSource); + const Collada::Accessor& targetAccessor = pParser.ResolveLibraryReference(pParser.mAccessorLibrary, c.mMorphTarget); + const Collada::Accessor& weightAccessor = pParser.ResolveLibraryReference(pParser.mAccessorLibrary, c.mMorphWeight); + const Collada::Data& targetData = pParser.ResolveLibraryReference(pParser.mDataLibrary, targetAccessor.mSource); + const Collada::Data& weightData = pParser.ResolveLibraryReference(pParser.mDataLibrary, weightAccessor.mSource); // take method method = c.mMethod; if (!targetData.mIsStringArray) { - throw DeadlyImportError( "target data must contain id. "); + throw DeadlyImportError("target data must contain id. "); } if (weightData.mIsStringArray) { - throw DeadlyImportError( "target weight data must not be textual "); + throw DeadlyImportError("target weight data must not be textual "); } for (unsigned int i = 0; i < targetData.mStrings.size(); ++i) { @@ -692,7 +710,7 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: aiMesh *aimesh = findMesh(targetMesh->mName); if (!aimesh) { if (targetMesh->mSubMeshes.size() > 1) { - throw DeadlyImportError( "Morhing target mesh must be a single"); + throw DeadlyImportError("Morhing target mesh must be a single"); } aimesh = CreateMesh(pParser, targetMesh, targetMesh->mSubMeshes.at(0), NULL, 0, 0); mTargetMeshes.push_back(aimesh); @@ -706,7 +724,7 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: } if (targetMeshes.size() > 0 && targetWeights.size() == targetMeshes.size()) { std::vector animMeshes; - for (unsigned int i = 0; i < targetMeshes.size(); ++i ) { + for (unsigned int i = 0; i < targetMeshes.size(); ++i) { aiMesh* targetMesh = targetMeshes.at(i); aiAnimMesh *animMesh = aiCreateAnimMesh(targetMesh); float weight = targetWeights[i]; @@ -715,54 +733,54 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: animMeshes.push_back(animMesh); } dstMesh->mMethod = (method == Collada::Relative) - ? aiMorphingMethod_MORPH_RELATIVE - : aiMorphingMethod_MORPH_NORMALIZED; + ? aiMorphingMethod_MORPH_RELATIVE + : aiMorphingMethod_MORPH_NORMALIZED; dstMesh->mAnimMeshes = new aiAnimMesh*[animMeshes.size()]; dstMesh->mNumAnimMeshes = static_cast(animMeshes.size()); - for (unsigned int i = 0; i < animMeshes.size(); ++i ) { + for (unsigned int i = 0; i < animMeshes.size(); ++i) { dstMesh->mAnimMeshes[i] = animMeshes.at(i); } } // create bones if given - if( pSrcController && pSrcController->mType == Collada::Skin) { + if (pSrcController && pSrcController->mType == Collada::Skin) { // resolve references - joint names - const Collada::Accessor& jointNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointNameSource); - const Collada::Data& jointNames = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointNamesAcc.mSource); + const Collada::Accessor& jointNamesAcc = pParser.ResolveLibraryReference(pParser.mAccessorLibrary, pSrcController->mJointNameSource); + const Collada::Data& jointNames = pParser.ResolveLibraryReference(pParser.mDataLibrary, jointNamesAcc.mSource); // joint offset matrices - const Collada::Accessor& jointMatrixAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointOffsetMatrixSource); - const Collada::Data& jointMatrices = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointMatrixAcc.mSource); + const Collada::Accessor& jointMatrixAcc = pParser.ResolveLibraryReference(pParser.mAccessorLibrary, pSrcController->mJointOffsetMatrixSource); + const Collada::Data& jointMatrices = pParser.ResolveLibraryReference(pParser.mDataLibrary, jointMatrixAcc.mSource); // joint vertex_weight name list - should refer to the same list as the joint names above. If not, report and reconsider - const Collada::Accessor& weightNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputJoints.mAccessor); - if( &weightNamesAcc != &jointNamesAcc) - throw DeadlyImportError( "Temporary implementational laziness. If you read this, please report to the author."); + const Collada::Accessor& weightNamesAcc = pParser.ResolveLibraryReference(pParser.mAccessorLibrary, pSrcController->mWeightInputJoints.mAccessor); + if (&weightNamesAcc != &jointNamesAcc) + throw DeadlyImportError("Temporary implementational laziness. If you read this, please report to the author."); // vertex weights - const Collada::Accessor& weightsAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputWeights.mAccessor); - const Collada::Data& weights = pParser.ResolveLibraryReference( pParser.mDataLibrary, weightsAcc.mSource); + const Collada::Accessor& weightsAcc = pParser.ResolveLibraryReference(pParser.mAccessorLibrary, pSrcController->mWeightInputWeights.mAccessor); + const Collada::Data& weights = pParser.ResolveLibraryReference(pParser.mDataLibrary, weightsAcc.mSource); - if( !jointNames.mIsStringArray || jointMatrices.mIsStringArray || weights.mIsStringArray) - throw DeadlyImportError( "Data type mismatch while resolving mesh joints"); + if (!jointNames.mIsStringArray || jointMatrices.mIsStringArray || weights.mIsStringArray) + throw DeadlyImportError("Data type mismatch while resolving mesh joints"); // sanity check: we rely on the vertex weights always coming as pairs of BoneIndex-WeightIndex - if( pSrcController->mWeightInputJoints.mOffset != 0 || pSrcController->mWeightInputWeights.mOffset != 1) - throw DeadlyImportError( "Unsupported vertex_weight addressing scheme. "); + if (pSrcController->mWeightInputJoints.mOffset != 0 || pSrcController->mWeightInputWeights.mOffset != 1) + throw DeadlyImportError("Unsupported vertex_weight addressing scheme. "); // create containers to collect the weights for each bone size_t numBones = jointNames.mStrings.size(); - std::vector > dstBones( numBones); + std::vector > dstBones(numBones); // build a temporary array of pointers to the start of each vertex's weights typedef std::vector< std::pair > IndexPairVector; std::vector weightStartPerVertex; - weightStartPerVertex.resize(pSrcController->mWeightCounts.size(),pSrcController->mWeights.end()); + weightStartPerVertex.resize(pSrcController->mWeightCounts.size(), pSrcController->mWeights.end()); IndexPairVector::const_iterator pit = pSrcController->mWeights.begin(); - for( size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a) { + for (size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a) { weightStartPerVertex[a] = pit; pit += pSrcController->mWeightCounts[a]; } // now for each vertex put the corresponding vertex weights into each bone's weight collection - for( size_t a = pStartVertex; a < pStartVertex + numVertices; ++a) { + for (size_t a = pStartVertex; a < pStartVertex + numVertices; ++a) { // which position index was responsible for this vertex? that's also the index by which // the controller assigns the vertex weights size_t orgIndex = pSrcMesh->mFacePosIndices[a]; @@ -770,58 +788,58 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: IndexPairVector::const_iterator iit = weightStartPerVertex[orgIndex]; size_t pairCount = pSrcController->mWeightCounts[orgIndex]; - for( size_t b = 0; b < pairCount; ++b, ++iit) + for (size_t b = 0; b < pairCount; ++b, ++iit) { size_t jointIndex = iit->first; size_t vertexIndex = iit->second; - ai_real weight = ReadFloat( weightsAcc, weights, vertexIndex, 0); + ai_real weight = ReadFloat(weightsAcc, weights, vertexIndex, 0); // one day I gonna kill that XSI Collada exporter - if( weight > 0.0f) + if (weight > 0.0f) { aiVertexWeight w; w.mVertexId = static_cast(a - pStartVertex); w.mWeight = weight; - dstBones[jointIndex].push_back( w); + dstBones[jointIndex].push_back(w); } } } // count the number of bones which influence vertices of the current submesh size_t numRemainingBones = 0; - for( std::vector >::const_iterator it = dstBones.begin(); it != dstBones.end(); ++it) - if( it->size() > 0) + for (std::vector >::const_iterator it = dstBones.begin(); it != dstBones.end(); ++it) + if (it->size() > 0) numRemainingBones++; // create bone array and copy bone weights one by one dstMesh->mNumBones = static_cast(numRemainingBones); dstMesh->mBones = new aiBone*[numRemainingBones]; size_t boneCount = 0; - for( size_t a = 0; a < numBones; ++a) + for (size_t a = 0; a < numBones; ++a) { // omit bones without weights - if( dstBones[a].size() == 0) + if (dstBones[a].size() == 0) continue; // create bone with its weights aiBone* bone = new aiBone; - bone->mName = ReadString( jointNamesAcc, jointNames, a); - bone->mOffsetMatrix.a1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 0); - bone->mOffsetMatrix.a2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 1); - bone->mOffsetMatrix.a3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 2); - bone->mOffsetMatrix.a4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 3); - bone->mOffsetMatrix.b1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 4); - bone->mOffsetMatrix.b2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 5); - bone->mOffsetMatrix.b3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 6); - bone->mOffsetMatrix.b4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 7); - bone->mOffsetMatrix.c1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 8); - bone->mOffsetMatrix.c2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 9); - bone->mOffsetMatrix.c3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 10); - bone->mOffsetMatrix.c4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 11); + bone->mName = ReadString(jointNamesAcc, jointNames, a); + bone->mOffsetMatrix.a1 = ReadFloat(jointMatrixAcc, jointMatrices, a, 0); + bone->mOffsetMatrix.a2 = ReadFloat(jointMatrixAcc, jointMatrices, a, 1); + bone->mOffsetMatrix.a3 = ReadFloat(jointMatrixAcc, jointMatrices, a, 2); + bone->mOffsetMatrix.a4 = ReadFloat(jointMatrixAcc, jointMatrices, a, 3); + bone->mOffsetMatrix.b1 = ReadFloat(jointMatrixAcc, jointMatrices, a, 4); + bone->mOffsetMatrix.b2 = ReadFloat(jointMatrixAcc, jointMatrices, a, 5); + bone->mOffsetMatrix.b3 = ReadFloat(jointMatrixAcc, jointMatrices, a, 6); + bone->mOffsetMatrix.b4 = ReadFloat(jointMatrixAcc, jointMatrices, a, 7); + bone->mOffsetMatrix.c1 = ReadFloat(jointMatrixAcc, jointMatrices, a, 8); + bone->mOffsetMatrix.c2 = ReadFloat(jointMatrixAcc, jointMatrices, a, 9); + bone->mOffsetMatrix.c3 = ReadFloat(jointMatrixAcc, jointMatrices, a, 10); + bone->mOffsetMatrix.c4 = ReadFloat(jointMatrixAcc, jointMatrices, a, 11); bone->mNumWeights = static_cast(dstBones[a].size()); bone->mWeights = new aiVertexWeight[bone->mNumWeights]; - std::copy( dstBones[a].begin(), dstBones[a].end(), bone->mWeights); + std::copy(dstBones[a].begin(), dstBones[a].end(), bone->mWeights); // apply bind shape matrix to offset matrix aiMatrix4x4 bindShapeMatrix; @@ -847,15 +865,15 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: // Therefore I added a little name replacement here: I search for the bone's node by either name, ID or SID, // and replace the bone's name by the node's name so that the user can use the standard // find-by-name method to associate nodes with bones. - const Collada::Node* bnode = FindNode( pParser.mRootNode, bone->mName.data); - if( !bnode) - bnode = FindNodeBySID( pParser.mRootNode, bone->mName.data); + const Collada::Node* bnode = FindNode(pParser.mRootNode, bone->mName.data); + if (!bnode) + bnode = FindNodeBySID(pParser.mRootNode, bone->mName.data); // assign the name that we would have assigned for the source node - if( bnode) - bone->mName.Set( FindNameForNode( bnode)); + if (bnode) + bone->mName.Set(FindNameForNode(bnode)); else - ASSIMP_LOG_WARN_F( "ColladaLoader::CreateMesh(): could not find corresponding node for joint \"", bone->mName.data, "\"." ); + ASSIMP_LOG_WARN_F("ColladaLoader::CreateMesh(): could not find corresponding node for joint \"", bone->mName.data, "\"."); // and insert bone dstMesh->mBones[boneCount++] = bone; @@ -867,65 +885,65 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: // ------------------------------------------------------------------------------------------------ // Stores all meshes in the given scene -void ColladaLoader::StoreSceneMeshes( aiScene* pScene) +void ColladaLoader::StoreSceneMeshes(aiScene* pScene) { pScene->mNumMeshes = static_cast(mMeshes.size()); - if( mMeshes.size() > 0) + if (mMeshes.size() > 0) { pScene->mMeshes = new aiMesh*[mMeshes.size()]; - std::copy( mMeshes.begin(), mMeshes.end(), pScene->mMeshes); + std::copy(mMeshes.begin(), mMeshes.end(), pScene->mMeshes); mMeshes.clear(); } } // ------------------------------------------------------------------------------------------------ // Stores all cameras in the given scene -void ColladaLoader::StoreSceneCameras( aiScene* pScene) +void ColladaLoader::StoreSceneCameras(aiScene* pScene) { pScene->mNumCameras = static_cast(mCameras.size()); - if( mCameras.size() > 0) + if (mCameras.size() > 0) { pScene->mCameras = new aiCamera*[mCameras.size()]; - std::copy( mCameras.begin(), mCameras.end(), pScene->mCameras); + std::copy(mCameras.begin(), mCameras.end(), pScene->mCameras); mCameras.clear(); } } // ------------------------------------------------------------------------------------------------ // Stores all lights in the given scene -void ColladaLoader::StoreSceneLights( aiScene* pScene) +void ColladaLoader::StoreSceneLights(aiScene* pScene) { pScene->mNumLights = static_cast(mLights.size()); - if( mLights.size() > 0) + if (mLights.size() > 0) { pScene->mLights = new aiLight*[mLights.size()]; - std::copy( mLights.begin(), mLights.end(), pScene->mLights); + std::copy(mLights.begin(), mLights.end(), pScene->mLights); mLights.clear(); } } // ------------------------------------------------------------------------------------------------ // Stores all textures in the given scene -void ColladaLoader::StoreSceneTextures( aiScene* pScene) +void ColladaLoader::StoreSceneTextures(aiScene* pScene) { pScene->mNumTextures = static_cast(mTextures.size()); - if( mTextures.size() > 0) + if (mTextures.size() > 0) { pScene->mTextures = new aiTexture*[mTextures.size()]; - std::copy( mTextures.begin(), mTextures.end(), pScene->mTextures); + std::copy(mTextures.begin(), mTextures.end(), pScene->mTextures); mTextures.clear(); } } // ------------------------------------------------------------------------------------------------ // Stores all materials in the given scene -void ColladaLoader::StoreSceneMaterials( aiScene* pScene) +void ColladaLoader::StoreSceneMaterials(aiScene* pScene) { pScene->mNumMaterials = static_cast(newMats.size()); if (newMats.size() > 0) { pScene->mMaterials = new aiMaterial*[newMats.size()]; - for (unsigned int i = 0; i < newMats.size();++i) + for (unsigned int i = 0; i < newMats.size(); ++i) pScene->mMaterials[i] = newMats[i].second; newMats.clear(); @@ -934,33 +952,33 @@ void ColladaLoader::StoreSceneMaterials( aiScene* pScene) // ------------------------------------------------------------------------------------------------ // Stores all animations -void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser) +void ColladaLoader::StoreAnimations(aiScene* pScene, const ColladaParser& pParser) { // recursively collect all animations from the collada scene - StoreAnimations( pScene, pParser, &pParser.mAnims, ""); + StoreAnimations(pScene, pParser, &pParser.mAnims, ""); // catch special case: many animations with the same length, each affecting only a single node. // we need to unite all those single-node-anims to a proper combined animation - for( size_t a = 0; a < mAnims.size(); ++a) + for (size_t a = 0; a < mAnims.size(); ++a) { aiAnimation* templateAnim = mAnims[a]; - if( templateAnim->mNumChannels == 1) + if (templateAnim->mNumChannels == 1) { // search for other single-channel-anims with the same duration std::vector collectedAnimIndices; - for( size_t b = a+1; b < mAnims.size(); ++b) + for (size_t b = a + 1; b < mAnims.size(); ++b) { aiAnimation* other = mAnims[b]; - if( other->mNumChannels == 1 && other->mDuration == templateAnim->mDuration && - other->mTicksPerSecond == templateAnim->mTicksPerSecond ) - collectedAnimIndices.push_back( b); + if (other->mNumChannels == 1 && other->mDuration == templateAnim->mDuration && + other->mTicksPerSecond == templateAnim->mTicksPerSecond) + collectedAnimIndices.push_back(b); } // if there are other animations which fit the template anim, combine all channels into a single anim - if( !collectedAnimIndices.empty() ) + if (!collectedAnimIndices.empty()) { aiAnimation* combinedAnim = new aiAnimation(); - combinedAnim->mName = aiString( std::string( "combinedAnim_") + char( '0' + a)); + combinedAnim->mName = aiString(std::string("combinedAnim_") + char('0' + a)); combinedAnim->mDuration = templateAnim->mDuration; combinedAnim->mTicksPerSecond = templateAnim->mTicksPerSecond; combinedAnim->mNumChannels = static_cast(collectedAnimIndices.size() + 1); @@ -973,7 +991,7 @@ void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pPars mAnims[a] = combinedAnim; // move the memory of all other anims to the combined anim and erase them from the source anims - for( size_t b = 0; b < collectedAnimIndices.size(); ++b) + for (size_t b = 0; b < collectedAnimIndices.size(); ++b) { aiAnimation* srcAnimation = mAnims[collectedAnimIndices[b]]; combinedAnim->mChannels[1 + b] = srcAnimation->mChannels[0]; @@ -983,9 +1001,9 @@ void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pPars // in a second go, delete all the single-channel-anims that we've stripped from their channels // back to front to preserve indices - you know, removing an element from a vector moves all elements behind the removed one - while( !collectedAnimIndices.empty() ) + while (!collectedAnimIndices.empty()) { - mAnims.erase( mAnims.begin() + collectedAnimIndices.back()); + mAnims.erase(mAnims.begin() + collectedAnimIndices.back()); collectedAnimIndices.pop_back(); } } @@ -993,11 +1011,11 @@ void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pPars } // now store all anims in the scene - if( !mAnims.empty()) + if (!mAnims.empty()) { pScene->mNumAnimations = static_cast(mAnims.size()); pScene->mAnimations = new aiAnimation*[mAnims.size()]; - std::copy( mAnims.begin(), mAnims.end(), pScene->mAnimations); + std::copy(mAnims.begin(), mAnims.end(), pScene->mAnimations); } mAnims.clear(); @@ -1005,17 +1023,17 @@ void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pPars // ------------------------------------------------------------------------------------------------ // Constructs the animations for the given source anim -void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string &pPrefix) +void ColladaLoader::StoreAnimations(aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string &pPrefix) { std::string animName = pPrefix.empty() ? pSrcAnim->mName : pPrefix + "_" + pSrcAnim->mName; // create nested animations, if given - for( std::vector::const_iterator it = pSrcAnim->mSubAnims.begin(); it != pSrcAnim->mSubAnims.end(); ++it) - StoreAnimations( pScene, pParser, *it, animName); + for (std::vector::const_iterator it = pSrcAnim->mSubAnims.begin(); it != pSrcAnim->mSubAnims.end(); ++it) + StoreAnimations(pScene, pParser, *it, animName); // create animation channels, if any - if( !pSrcAnim->mChannels.empty()) - CreateAnimation( pScene, pParser, pSrcAnim, animName); + if (!pSrcAnim->mChannels.empty()) + CreateAnimation(pScene, pParser, pSrcAnim, animName); } struct MorphTimeValues @@ -1056,7 +1074,8 @@ void insertMorphTimeValue(std::vector &values, float time, floa { values[i].mKeys.push_back(k); return; - } else if (time > values[i].mTime && time < values[i+1].mTime) + } + else if (time > values[i].mTime && time < values[i + 1].mTime) { MorphTimeValues val; val.mTime = time; @@ -1082,30 +1101,30 @@ float getWeightAtKey(const std::vector &values, int key, unsign // ------------------------------------------------------------------------------------------------ // Constructs the animation for the given source anim -void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName) +void ColladaLoader::CreateAnimation(aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName) { // collect a list of animatable nodes std::vector nodes; - CollectNodes( pScene->mRootNode, nodes); + CollectNodes(pScene->mRootNode, nodes); std::vector anims; std::vector morphAnims; - for( std::vector::const_iterator nit = nodes.begin(); nit != nodes.end(); ++nit) + for (std::vector::const_iterator nit = nodes.begin(); nit != nodes.end(); ++nit) { // find all the collada anim channels which refer to the current node std::vector entries; std::string nodeName = (*nit)->mName.data; // find the collada node corresponding to the aiNode - const Collada::Node* srcNode = FindNode( pParser.mRootNode, nodeName); -// ai_assert( srcNode != NULL); - if( !srcNode) + const Collada::Node* srcNode = FindNode(pParser.mRootNode, nodeName); + // ai_assert( srcNode != NULL); + if (!srcNode) continue; // now check all channels if they affect the current node std::string targetID, subElement; - for( std::vector::const_iterator cit = pSrcAnim->mChannels.begin(); + for (std::vector::const_iterator cit = pSrcAnim->mChannels.begin(); cit != pSrcAnim->mChannels.end(); ++cit) { const Collada::AnimationChannel& srcChannel = *cit; @@ -1113,8 +1132,8 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars // we expect the animation target to be of type "nodeName/transformID.subElement". Ignore all others // find the slash that separates the node name - there should be only one - std::string::size_type slashPos = srcChannel.mTarget.find( '/'); - if( slashPos == std::string::npos) + std::string::size_type slashPos = srcChannel.mTarget.find('/'); + if (slashPos == std::string::npos) { std::string::size_type targetPos = srcChannel.mTarget.find(srcNode->mID); if (targetPos == std::string::npos) @@ -1123,44 +1142,45 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars // not node transform, but something else. store as unknown animation channel for now entry.mChannel = &(*cit); entry.mTargetId = srcChannel.mTarget.substr(targetPos + pSrcAnim->mName.length(), - srcChannel.mTarget.length() - targetPos - pSrcAnim->mName.length()); + srcChannel.mTarget.length() - targetPos - pSrcAnim->mName.length()); if (entry.mTargetId.front() == '-') entry.mTargetId = entry.mTargetId.substr(1); entries.push_back(entry); continue; } - if( srcChannel.mTarget.find( '/', slashPos+1) != std::string::npos) + if (srcChannel.mTarget.find('/', slashPos + 1) != std::string::npos) continue; targetID.clear(); - targetID = srcChannel.mTarget.substr( 0, slashPos); - if( targetID != srcNode->mID) + targetID = srcChannel.mTarget.substr(0, slashPos); + if (targetID != srcNode->mID) continue; // find the dot that separates the transformID - there should be only one or zero - std::string::size_type dotPos = srcChannel.mTarget.find( '.'); - if( dotPos != std::string::npos) + std::string::size_type dotPos = srcChannel.mTarget.find('.'); + if (dotPos != std::string::npos) { - if( srcChannel.mTarget.find( '.', dotPos+1) != std::string::npos) + if (srcChannel.mTarget.find('.', dotPos + 1) != std::string::npos) continue; - entry.mTransformId = srcChannel.mTarget.substr( slashPos+1, dotPos - slashPos - 1); + entry.mTransformId = srcChannel.mTarget.substr(slashPos + 1, dotPos - slashPos - 1); subElement.clear(); - subElement = srcChannel.mTarget.substr( dotPos+1); - if( subElement == "ANGLE") + subElement = srcChannel.mTarget.substr(dotPos + 1); + if (subElement == "ANGLE") entry.mSubElement = 3; // last number in an Axis-Angle-Transform is the angle - else if( subElement == "X") + else if (subElement == "X") entry.mSubElement = 0; - else if( subElement == "Y") + else if (subElement == "Y") entry.mSubElement = 1; - else if( subElement == "Z") + else if (subElement == "Z") entry.mSubElement = 2; else - ASSIMP_LOG_WARN_F( "Unknown anim subelement <", subElement, ">. Ignoring" ); - } else { + ASSIMP_LOG_WARN_F("Unknown anim subelement <", subElement, ">. Ignoring"); + } + else { // no subelement following, transformId is remaining string - entry.mTransformId = srcChannel.mTarget.substr( slashPos+1); + entry.mTransformId = srcChannel.mTarget.substr(slashPos + 1); } std::string::size_type bracketPos = srcChannel.mTarget.find('('); @@ -1206,194 +1226,196 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars // determine which transform step is affected by this channel entry.mTransformIndex = SIZE_MAX; - for( size_t a = 0; a < srcNode->mTransforms.size(); ++a) - if( srcNode->mTransforms[a].mID == entry.mTransformId) + for (size_t a = 0; a < srcNode->mTransforms.size(); ++a) + if (srcNode->mTransforms[a].mID == entry.mTransformId) entry.mTransformIndex = a; - if( entry.mTransformIndex == SIZE_MAX) + if (entry.mTransformIndex == SIZE_MAX) { if (entry.mTransformId.find("morph-weights") != std::string::npos) { entry.mTargetId = entry.mTransformId; entry.mTransformId = ""; - } else + } + else continue; } entry.mChannel = &(*cit); - entries.push_back( entry); + entries.push_back(entry); } // if there's no channel affecting the current node, we skip it - if( entries.empty()) + if (entries.empty()) continue; // resolve the data pointers for all anim channels. Find the minimum time while we're at it - ai_real startTime = ai_real( 1e20 ), endTime = ai_real( -1e20 ); - for( std::vector::iterator it = entries.begin(); it != entries.end(); ++it) + ai_real startTime = ai_real(1e20), endTime = ai_real(-1e20); + for (std::vector::iterator it = entries.begin(); it != entries.end(); ++it) { Collada::ChannelEntry& e = *it; - e.mTimeAccessor = &pParser.ResolveLibraryReference( pParser.mAccessorLibrary, e.mChannel->mSourceTimes); - e.mTimeData = &pParser.ResolveLibraryReference( pParser.mDataLibrary, e.mTimeAccessor->mSource); - e.mValueAccessor = &pParser.ResolveLibraryReference( pParser.mAccessorLibrary, e.mChannel->mSourceValues); - e.mValueData = &pParser.ResolveLibraryReference( pParser.mDataLibrary, e.mValueAccessor->mSource); + e.mTimeAccessor = &pParser.ResolveLibraryReference(pParser.mAccessorLibrary, e.mChannel->mSourceTimes); + e.mTimeData = &pParser.ResolveLibraryReference(pParser.mDataLibrary, e.mTimeAccessor->mSource); + e.mValueAccessor = &pParser.ResolveLibraryReference(pParser.mAccessorLibrary, e.mChannel->mSourceValues); + e.mValueData = &pParser.ResolveLibraryReference(pParser.mDataLibrary, e.mValueAccessor->mSource); // time count and value count must match - if( e.mTimeAccessor->mCount != e.mValueAccessor->mCount) - throw DeadlyImportError( format() << "Time count / value count mismatch in animation channel \"" << e.mChannel->mTarget << "\"." ); + if (e.mTimeAccessor->mCount != e.mValueAccessor->mCount) + throw DeadlyImportError(format() << "Time count / value count mismatch in animation channel \"" << e.mChannel->mTarget << "\"."); - if( e.mTimeAccessor->mCount > 0 ) - { - // find bounding times - startTime = std::min( startTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, 0, 0)); - endTime = std::max( endTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount-1, 0)); - } + if (e.mTimeAccessor->mCount > 0) + { + // find bounding times + startTime = std::min(startTime, ReadFloat(*e.mTimeAccessor, *e.mTimeData, 0, 0)); + endTime = std::max(endTime, ReadFloat(*e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount - 1, 0)); + } } - std::vector resultTrafos; - if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 ) - { - // create a local transformation chain of the node's transforms - std::vector transforms = srcNode->mTransforms; + std::vector resultTrafos; + if (!entries.empty() && entries.front().mTimeAccessor->mCount > 0) + { + // create a local transformation chain of the node's transforms + std::vector transforms = srcNode->mTransforms; - // now for every unique point in time, find or interpolate the key values for that time - // and apply them to the transform chain. Then the node's present transformation can be calculated. - ai_real time = startTime; - while( 1) - { - for( std::vector::iterator it = entries.begin(); it != entries.end(); ++it) - { - Collada::ChannelEntry& e = *it; + // now for every unique point in time, find or interpolate the key values for that time + // and apply them to the transform chain. Then the node's present transformation can be calculated. + ai_real time = startTime; + while (1) + { + for (std::vector::iterator it = entries.begin(); it != entries.end(); ++it) + { + Collada::ChannelEntry& e = *it; - // find the keyframe behind the current point in time - size_t pos = 0; - ai_real postTime = 0.0; - while( 1) - { - if( pos >= e.mTimeAccessor->mCount) - break; - postTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); - if( postTime >= time) - break; - ++pos; - } + // find the keyframe behind the current point in time + size_t pos = 0; + ai_real postTime = 0.0; + while (1) + { + if (pos >= e.mTimeAccessor->mCount) + break; + postTime = ReadFloat(*e.mTimeAccessor, *e.mTimeData, pos, 0); + if (postTime >= time) + break; + ++pos; + } - pos = std::min( pos, e.mTimeAccessor->mCount-1); + pos = std::min(pos, e.mTimeAccessor->mCount - 1); - // read values from there - ai_real temp[16]; - for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) - temp[c] = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, c); + // read values from there + ai_real temp[16]; + for (size_t c = 0; c < e.mValueAccessor->mSize; ++c) + temp[c] = ReadFloat(*e.mValueAccessor, *e.mValueData, pos, c); - // if not exactly at the key time, interpolate with previous value set - if( postTime > time && pos > 0) - { - ai_real preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0); - ai_real factor = (time - postTime) / (preTime - postTime); + // if not exactly at the key time, interpolate with previous value set + if (postTime > time && pos > 0) + { + ai_real preTime = ReadFloat(*e.mTimeAccessor, *e.mTimeData, pos - 1, 0); + ai_real factor = (time - postTime) / (preTime - postTime); - for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) - { - ai_real v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c); - temp[c] += (v - temp[c]) * factor; - } - } + for (size_t c = 0; c < e.mValueAccessor->mSize; ++c) + { + ai_real v = ReadFloat(*e.mValueAccessor, *e.mValueData, pos - 1, c); + temp[c] += (v - temp[c]) * factor; + } + } - // Apply values to current transformation - std::copy( temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement); - } + // Apply values to current transformation + std::copy(temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement); + } - // Calculate resulting transformation - aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms); + // Calculate resulting transformation + aiMatrix4x4 mat = pParser.CalculateResultTransform(transforms); - // out of laziness: we store the time in matrix.d4 - mat.d4 = time; - resultTrafos.push_back( mat); + // out of laziness: we store the time in matrix.d4 + mat.d4 = time; + resultTrafos.push_back(mat); - // find next point in time to evaluate. That's the closest frame larger than the current in any channel - ai_real nextTime = ai_real( 1e20 ); - for( std::vector::iterator it = entries.begin(); it != entries.end(); ++it) - { - Collada::ChannelEntry& channelElement = *it; + // find next point in time to evaluate. That's the closest frame larger than the current in any channel + ai_real nextTime = ai_real(1e20); + for (std::vector::iterator it = entries.begin(); it != entries.end(); ++it) + { + Collada::ChannelEntry& channelElement = *it; - // find the next time value larger than the current - size_t pos = 0; - while( pos < channelElement.mTimeAccessor->mCount) - { - const ai_real t = ReadFloat( *channelElement.mTimeAccessor, *channelElement.mTimeData, pos, 0); - if( t > time) - { - nextTime = std::min( nextTime, t); - break; - } - ++pos; - } + // find the next time value larger than the current + size_t pos = 0; + while (pos < channelElement.mTimeAccessor->mCount) + { + const ai_real t = ReadFloat(*channelElement.mTimeAccessor, *channelElement.mTimeData, pos, 0); + if (t > time) + { + nextTime = std::min(nextTime, t); + break; + } + ++pos; + } - // https://github.com/assimp/assimp/issues/458 - // Sub-sample axis-angle channels if the delta between two consecutive - // key-frame angles is >= 180 degrees. - if (transforms[channelElement.mTransformIndex].mType == Collada::TF_ROTATE && channelElement.mSubElement == 3 && pos > 0 && pos < channelElement.mTimeAccessor->mCount) { - const ai_real cur_key_angle = ReadFloat(*channelElement.mValueAccessor, *channelElement.mValueData, pos, 0); - const ai_real last_key_angle = ReadFloat(*channelElement.mValueAccessor, *channelElement.mValueData, pos - 1, 0); - const ai_real cur_key_time = ReadFloat(*channelElement.mTimeAccessor, *channelElement.mTimeData, pos, 0); - const ai_real last_key_time = ReadFloat(*channelElement.mTimeAccessor, *channelElement.mTimeData, pos - 1, 0); - const ai_real last_eval_angle = last_key_angle + (cur_key_angle - last_key_angle) * (time - last_key_time) / (cur_key_time - last_key_time); - const ai_real delta = std::abs(cur_key_angle - last_eval_angle); - if (delta >= 180.0) { - const int subSampleCount = static_cast(std::floor(delta / 90.0)); - if (cur_key_time != time) { - const ai_real nextSampleTime = time + (cur_key_time - time) / subSampleCount; - nextTime = std::min(nextTime, nextSampleTime); - } - } - } - } + // https://github.com/assimp/assimp/issues/458 + // Sub-sample axis-angle channels if the delta between two consecutive + // key-frame angles is >= 180 degrees. + if (transforms[channelElement.mTransformIndex].mType == Collada::TF_ROTATE && channelElement.mSubElement == 3 && pos > 0 && pos < channelElement.mTimeAccessor->mCount) { + const ai_real cur_key_angle = ReadFloat(*channelElement.mValueAccessor, *channelElement.mValueData, pos, 0); + const ai_real last_key_angle = ReadFloat(*channelElement.mValueAccessor, *channelElement.mValueData, pos - 1, 0); + const ai_real cur_key_time = ReadFloat(*channelElement.mTimeAccessor, *channelElement.mTimeData, pos, 0); + const ai_real last_key_time = ReadFloat(*channelElement.mTimeAccessor, *channelElement.mTimeData, pos - 1, 0); + const ai_real last_eval_angle = last_key_angle + (cur_key_angle - last_key_angle) * (time - last_key_time) / (cur_key_time - last_key_time); + const ai_real delta = std::abs(cur_key_angle - last_eval_angle); + if (delta >= 180.0) { + const int subSampleCount = static_cast(std::floor(delta / 90.0)); + if (cur_key_time != time) { + const ai_real nextSampleTime = time + (cur_key_time - time) / subSampleCount; + nextTime = std::min(nextTime, nextSampleTime); + } + } + } + } - // no more keys on any channel after the current time -> we're done - if( nextTime > 1e19) - break; + // no more keys on any channel after the current time -> we're done + if (nextTime > 1e19) + break; - // else construct next keyframe at this following time point - time = nextTime; - } - } + // else construct next keyframe at this following time point + time = nextTime; + } + } // there should be some keyframes, but we aren't that fixated on valid input data // ai_assert( resultTrafos.size() > 0); // build an animation channel for the given node out of these trafo keys - if( !resultTrafos.empty() ) + if (!resultTrafos.empty()) { - aiNodeAnim* dstAnim = new aiNodeAnim; - dstAnim->mNodeName = nodeName; - dstAnim->mNumPositionKeys = static_cast(resultTrafos.size()); - dstAnim->mNumRotationKeys = static_cast(resultTrafos.size()); - dstAnim->mNumScalingKeys = static_cast(resultTrafos.size()); - dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()]; - dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()]; - dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()]; + aiNodeAnim* dstAnim = new aiNodeAnim; + dstAnim->mNodeName = nodeName; + dstAnim->mNumPositionKeys = static_cast(resultTrafos.size()); + dstAnim->mNumRotationKeys = static_cast(resultTrafos.size()); + dstAnim->mNumScalingKeys = static_cast(resultTrafos.size()); + dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()]; + dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()]; + dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()]; - for( size_t a = 0; a < resultTrafos.size(); ++a) - { - aiMatrix4x4 mat = resultTrafos[a]; - double time = double( mat.d4); // remember? time is stored in mat.d4 - mat.d4 = 1.0f; + for (size_t a = 0; a < resultTrafos.size(); ++a) + { + aiMatrix4x4 mat = resultTrafos[a]; + double time = double(mat.d4); // remember? time is stored in mat.d4 + mat.d4 = 1.0f; - dstAnim->mPositionKeys[a].mTime = time; - dstAnim->mRotationKeys[a].mTime = time; - dstAnim->mScalingKeys[a].mTime = time; - mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue); - } + dstAnim->mPositionKeys[a].mTime = time; + dstAnim->mRotationKeys[a].mTime = time; + dstAnim->mScalingKeys[a].mTime = time; + mat.Decompose(dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue); + } - anims.push_back( dstAnim); - } else + anims.push_back(dstAnim); + } + else { - ASSIMP_LOG_WARN( "Collada loader: found empty animation channel, ignored. Please check your exporter."); + ASSIMP_LOG_WARN("Collada loader: found empty animation channel, ignored. Please check your exporter."); } - if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 ) + if (!entries.empty() && entries.front().mTimeAccessor->mCount > 0) { std::vector morphChannels; - for( std::vector::iterator it = entries.begin(); it != entries.end(); ++it) + for (std::vector::iterator it = entries.begin(); it != entries.end(); ++it) { Collada::ChannelEntry& e = *it; @@ -1416,7 +1438,7 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars std::vector morphTimeValues; int morphAnimChannelIndex = 0; - for( std::vector::iterator it = morphChannels.begin(); it != morphChannels.end(); ++it) + for (std::vector::iterator it = morphChannels.begin(); it != morphChannels.end(); ++it) { Collada::ChannelEntry& e = *it; std::string::size_type apos = e.mTargetId.find('('); @@ -1438,8 +1460,8 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars for (unsigned int key = 0; key < morphAnim->mNumKeys; key++) { morphAnim->mKeys[key].mNumValuesAndWeights = static_cast(morphChannels.size()); - morphAnim->mKeys[key].mValues = new unsigned int [morphChannels.size()]; - morphAnim->mKeys[key].mWeights = new double [morphChannels.size()]; + morphAnim->mKeys[key].mValues = new unsigned int[morphChannels.size()]; + morphAnim->mKeys[key].mWeights = new double[morphChannels.size()]; morphAnim->mKeys[key].mTime = morphTimeValues[key].mTime; for (unsigned int valueIndex = 0; valueIndex < morphChannels.size(); valueIndex++) @@ -1454,48 +1476,48 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars } } - if( !anims.empty() || !morphAnims.empty()) + if (!anims.empty() || !morphAnims.empty()) { aiAnimation* anim = new aiAnimation; - anim->mName.Set( pName); + anim->mName.Set(pName); anim->mNumChannels = static_cast(anims.size()); if (anim->mNumChannels > 0) { anim->mChannels = new aiNodeAnim*[anims.size()]; - std::copy( anims.begin(), anims.end(), anim->mChannels); + std::copy(anims.begin(), anims.end(), anim->mChannels); } anim->mNumMorphMeshChannels = static_cast(morphAnims.size()); if (anim->mNumMorphMeshChannels > 0) { anim->mMorphMeshChannels = new aiMeshMorphAnim*[anim->mNumMorphMeshChannels]; - std::copy( morphAnims.begin(), morphAnims.end(), anim->mMorphMeshChannels); + std::copy(morphAnims.begin(), morphAnims.end(), anim->mMorphMeshChannels); } anim->mDuration = 0.0f; - for( size_t a = 0; a < anims.size(); ++a) + for (size_t a = 0; a < anims.size(); ++a) { - anim->mDuration = std::max( anim->mDuration, anims[a]->mPositionKeys[anims[a]->mNumPositionKeys-1].mTime); - anim->mDuration = std::max( anim->mDuration, anims[a]->mRotationKeys[anims[a]->mNumRotationKeys-1].mTime); - anim->mDuration = std::max( anim->mDuration, anims[a]->mScalingKeys[anims[a]->mNumScalingKeys-1].mTime); + anim->mDuration = std::max(anim->mDuration, anims[a]->mPositionKeys[anims[a]->mNumPositionKeys - 1].mTime); + anim->mDuration = std::max(anim->mDuration, anims[a]->mRotationKeys[anims[a]->mNumRotationKeys - 1].mTime); + anim->mDuration = std::max(anim->mDuration, anims[a]->mScalingKeys[anims[a]->mNumScalingKeys - 1].mTime); } for (size_t a = 0; a < morphAnims.size(); ++a) { - anim->mDuration = std::max(anim->mDuration, morphAnims[a]->mKeys[morphAnims[a]->mNumKeys-1].mTime); + anim->mDuration = std::max(anim->mDuration, morphAnims[a]->mKeys[morphAnims[a]->mNumKeys - 1].mTime); } anim->mTicksPerSecond = 1; - mAnims.push_back( anim); + mAnims.push_back(anim); } } // ------------------------------------------------------------------------------------------------ // Add a texture to a material structure -void ColladaLoader::AddTexture ( aiMaterial& mat, const ColladaParser& pParser, +void ColladaLoader::AddTexture(aiMaterial& mat, const ColladaParser& pParser, const Collada::Effect& effect, const Collada::Sampler& sampler, aiTextureType type, unsigned int idx) { // first of all, basic file name - const aiString name = FindFilenameForEffectTexture( pParser, effect, sampler.mName ); - mat.AddProperty( &name, _AI_MATKEY_TEXTURE_BASE, type, idx ); + const aiString name = FindFilenameForEffectTexture(pParser, effect, sampler.mName); + mat.AddProperty(&name, _AI_MATKEY_TEXTURE_BASE, type, idx); // mapping mode int map = aiTextureMapMode_Clamp; @@ -1504,7 +1526,7 @@ void ColladaLoader::AddTexture ( aiMaterial& mat, const ColladaParser& pParser, if (sampler.mWrapU && sampler.mMirrorU) map = aiTextureMapMode_Mirror; - mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_U_BASE, type, idx); + mat.AddProperty(&map, 1, _AI_MATKEY_MAPPINGMODE_U_BASE, type, idx); map = aiTextureMapMode_Clamp; if (sampler.mWrapV) @@ -1512,18 +1534,18 @@ void ColladaLoader::AddTexture ( aiMaterial& mat, const ColladaParser& pParser, if (sampler.mWrapV && sampler.mMirrorV) map = aiTextureMapMode_Mirror; - mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_V_BASE, type, idx); + mat.AddProperty(&map, 1, _AI_MATKEY_MAPPINGMODE_V_BASE, type, idx); // UV transformation mat.AddProperty(&sampler.mTransform, 1, _AI_MATKEY_UVTRANSFORM_BASE, type, idx); // Blend mode - mat.AddProperty((int*)&sampler.mOp , 1, + mat.AddProperty((int*)&sampler.mOp, 1, _AI_MATKEY_TEXBLEND_BASE, type, idx); // Blend factor - mat.AddProperty((ai_real*)&sampler.mWeighting , 1, + mat.AddProperty((ai_real*)&sampler.mWeighting, 1, _AI_MATKEY_TEXBLEND_BASE, type, idx); // UV source index ... if we didn't resolve the mapping, it is actually just @@ -1535,7 +1557,7 @@ void ColladaLoader::AddTexture ( aiMaterial& mat, const ColladaParser& pParser, map = sampler.mUVId; else { map = -1; - for (std::string::const_iterator it = sampler.mUVChannel.begin();it != sampler.mUVChannel.end(); ++it){ + for (std::string::const_iterator it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) { if (IsNumeric(*it)) { map = strtoul10(&(*it)); break; @@ -1546,12 +1568,12 @@ void ColladaLoader::AddTexture ( aiMaterial& mat, const ColladaParser& pParser, map = 0; } } - mat.AddProperty(&map,1,_AI_MATKEY_UVWSRC_BASE,type,idx); + mat.AddProperty(&map, 1, _AI_MATKEY_UVWSRC_BASE, type, idx); } // ------------------------------------------------------------------------------------------------ // Fills materials from the collada material definitions -void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene* /*pScene*/) +void ColladaLoader::FillMaterials(const ColladaParser& pParser, aiScene* /*pScene*/) { for (auto &elem : newMats) { @@ -1563,7 +1585,7 @@ void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene* /*pSce if (effect.mFaceted) /* fixme */ shadeMode = aiShadingMode_Flat; else { - switch( effect.mShadeType) + switch (effect.mShadeType) { case Collada::Shade_Constant: shadeMode = aiShadingMode_NoShading; @@ -1584,56 +1606,57 @@ void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene* /*pSce break; } } - mat.AddProperty( &shadeMode, 1, AI_MATKEY_SHADING_MODEL); + mat.AddProperty(&shadeMode, 1, AI_MATKEY_SHADING_MODEL); // double-sided? shadeMode = effect.mDoubleSided; - mat.AddProperty( &shadeMode, 1, AI_MATKEY_TWOSIDED); + mat.AddProperty(&shadeMode, 1, AI_MATKEY_TWOSIDED); // wireframe? shadeMode = effect.mWireframe; - mat.AddProperty( &shadeMode, 1, AI_MATKEY_ENABLE_WIREFRAME); + mat.AddProperty(&shadeMode, 1, AI_MATKEY_ENABLE_WIREFRAME); // add material colors - mat.AddProperty( &effect.mAmbient, 1,AI_MATKEY_COLOR_AMBIENT); - mat.AddProperty( &effect.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); - mat.AddProperty( &effect.mSpecular, 1,AI_MATKEY_COLOR_SPECULAR); - mat.AddProperty( &effect.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); - mat.AddProperty( &effect.mReflective, 1, AI_MATKEY_COLOR_REFLECTIVE); + mat.AddProperty(&effect.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); + mat.AddProperty(&effect.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + mat.AddProperty(&effect.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); + mat.AddProperty(&effect.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); + mat.AddProperty(&effect.mReflective, 1, AI_MATKEY_COLOR_REFLECTIVE); // scalar properties - mat.AddProperty( &effect.mShininess, 1, AI_MATKEY_SHININESS); - mat.AddProperty( &effect.mReflectivity, 1, AI_MATKEY_REFLECTIVITY); - mat.AddProperty( &effect.mRefractIndex, 1, AI_MATKEY_REFRACTI); + mat.AddProperty(&effect.mShininess, 1, AI_MATKEY_SHININESS); + mat.AddProperty(&effect.mReflectivity, 1, AI_MATKEY_REFLECTIVITY); + mat.AddProperty(&effect.mRefractIndex, 1, AI_MATKEY_REFRACTI); // transparency, a very hard one. seemingly not all files are following the // specification here (1.0 transparency => completely opaque)... // therefore, we let the opportunity for the user to manually invert // the transparency if necessary and we add preliminary support for RGB_ZERO mode - if(effect.mTransparency >= 0.f && effect.mTransparency <= 1.f) { + if (effect.mTransparency >= 0.f && effect.mTransparency <= 1.f) { // handle RGB transparency completely, cf Collada specs 1.5.0 pages 249 and 304 - if(effect.mRGBTransparency) { - // use luminance as defined by ISO/CIE color standards (see ITU-R Recommendation BT.709-4) + if (effect.mRGBTransparency) { + // use luminance as defined by ISO/CIE color standards (see ITU-R Recommendation BT.709-4) effect.mTransparency *= ( 0.212671f * effect.mTransparent.r + 0.715160f * effect.mTransparent.g + 0.072169f * effect.mTransparent.b - ); + ); effect.mTransparent.a = 1.f; - mat.AddProperty( &effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT ); - } else { - effect.mTransparency *= effect.mTransparent.a; + mat.AddProperty(&effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT); + } + else { + effect.mTransparency *= effect.mTransparent.a; } - if(effect.mInvertTransparency) { + if (effect.mInvertTransparency) { effect.mTransparency = 1.f - effect.mTransparency; } // Is the material finally transparent ? if (effect.mHasTransparency || effect.mTransparency < 1.f) { - mat.AddProperty( &effect.mTransparency, 1, AI_MATKEY_OPACITY ); + mat.AddProperty(&effect.mTransparency, 1, AI_MATKEY_OPACITY); } } @@ -1643,87 +1666,87 @@ void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene* /*pSce AddTexture(mat, pParser, effect, effect.mTexAmbient, aiTextureType_LIGHTMAP); } - if( !effect.mTexEmissive.mName.empty()) - AddTexture( mat, pParser, effect, effect.mTexEmissive, aiTextureType_EMISSIVE); + if (!effect.mTexEmissive.mName.empty()) + AddTexture(mat, pParser, effect, effect.mTexEmissive, aiTextureType_EMISSIVE); - if( !effect.mTexSpecular.mName.empty()) - AddTexture( mat, pParser, effect, effect.mTexSpecular, aiTextureType_SPECULAR); + if (!effect.mTexSpecular.mName.empty()) + AddTexture(mat, pParser, effect, effect.mTexSpecular, aiTextureType_SPECULAR); - if( !effect.mTexDiffuse.mName.empty()) - AddTexture( mat, pParser, effect, effect.mTexDiffuse, aiTextureType_DIFFUSE); + if (!effect.mTexDiffuse.mName.empty()) + AddTexture(mat, pParser, effect, effect.mTexDiffuse, aiTextureType_DIFFUSE); - if( !effect.mTexBump.mName.empty()) - AddTexture( mat, pParser, effect, effect.mTexBump, aiTextureType_NORMALS); + if (!effect.mTexBump.mName.empty()) + AddTexture(mat, pParser, effect, effect.mTexBump, aiTextureType_NORMALS); - if( !effect.mTexTransparent.mName.empty()) - AddTexture( mat, pParser, effect, effect.mTexTransparent, aiTextureType_OPACITY); + if (!effect.mTexTransparent.mName.empty()) + AddTexture(mat, pParser, effect, effect.mTexTransparent, aiTextureType_OPACITY); - if( !effect.mTexReflective.mName.empty()) - AddTexture( mat, pParser, effect, effect.mTexReflective, aiTextureType_REFLECTION); + if (!effect.mTexReflective.mName.empty()) + AddTexture(mat, pParser, effect, effect.mTexReflective, aiTextureType_REFLECTION); } } // ------------------------------------------------------------------------------------------------ // Constructs materials from the collada material definitions -void ColladaLoader::BuildMaterials( ColladaParser& pParser, aiScene* /*pScene*/) +void ColladaLoader::BuildMaterials(ColladaParser& pParser, aiScene* /*pScene*/) { newMats.reserve(pParser.mMaterialLibrary.size()); - for( ColladaParser::MaterialLibrary::const_iterator matIt = pParser.mMaterialLibrary.begin(); + for (ColladaParser::MaterialLibrary::const_iterator matIt = pParser.mMaterialLibrary.begin(); matIt != pParser.mMaterialLibrary.end(); ++matIt) { const Collada::Material& material = matIt->second; // a material is only a reference to an effect - ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find( material.mEffect); - if( effIt == pParser.mEffectLibrary.end()) + ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find(material.mEffect); + if (effIt == pParser.mEffectLibrary.end()) continue; Collada::Effect& effect = effIt->second; // create material aiMaterial* mat = new aiMaterial; - aiString name( material.mName.empty() ? matIt->first : material.mName ); - mat->AddProperty(&name,AI_MATKEY_NAME); + aiString name(material.mName.empty() ? matIt->first : material.mName); + mat->AddProperty(&name, AI_MATKEY_NAME); // store the material mMaterialIndexByName[matIt->first] = newMats.size(); - newMats.push_back( std::pair( &effect,mat) ); + newMats.push_back(std::pair(&effect, mat)); } // ScenePreprocessor generates a default material automatically if none is there. // All further code here in this loader works well without a valid material so // we can safely let it to ScenePreprocessor. #if 0 - if( newMats.size() == 0) + if (newMats.size() == 0) { aiMaterial* mat = new aiMaterial; - aiString name( AI_DEFAULT_MATERIAL_NAME ); - mat->AddProperty( &name, AI_MATKEY_NAME); + aiString name(AI_DEFAULT_MATERIAL_NAME); + mat->AddProperty(&name, AI_MATKEY_NAME); const int shadeMode = aiShadingMode_Phong; - mat->AddProperty( &shadeMode, 1, AI_MATKEY_SHADING_MODEL); - aiColor4D colAmbient( 0.2, 0.2, 0.2, 1.0), colDiffuse( 0.8, 0.8, 0.8, 1.0), colSpecular( 0.5, 0.5, 0.5, 0.5); - mat->AddProperty( &colAmbient, 1, AI_MATKEY_COLOR_AMBIENT); - mat->AddProperty( &colDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); - mat->AddProperty( &colSpecular, 1, AI_MATKEY_COLOR_SPECULAR); + mat->AddProperty(&shadeMode, 1, AI_MATKEY_SHADING_MODEL); + aiColor4D colAmbient(0.2, 0.2, 0.2, 1.0), colDiffuse(0.8, 0.8, 0.8, 1.0), colSpecular(0.5, 0.5, 0.5, 0.5); + mat->AddProperty(&colAmbient, 1, AI_MATKEY_COLOR_AMBIENT); + mat->AddProperty(&colDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + mat->AddProperty(&colSpecular, 1, AI_MATKEY_COLOR_SPECULAR); const ai_real specExp = 5.0; - mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS); + mat->AddProperty(&specExp, 1, AI_MATKEY_SHININESS); } #endif } // ------------------------------------------------------------------------------------------------ // Resolves the texture name for the given effect texture entry -aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pParser, +aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParser, const Collada::Effect& pEffect, const std::string& pName) { aiString result; // recurse through the param references until we end up at an image std::string name = pName; - while( 1) + while (1) { // the given string is a param entry. Find it - Collada::Effect::ParamLibrary::const_iterator it = pEffect.mParams.find( name); + Collada::Effect::ParamLibrary::const_iterator it = pEffect.mParams.find(name); // if not found, we're at the end of the recursion. The resulting string should be the image ID - if( it == pEffect.mParams.end()) + if (it == pEffect.mParams.end()) break; // else recurse on @@ -1731,8 +1754,8 @@ aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pPars } // find the image referred by this name in the image library of the scene - ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find( name); - if( imIt == pParser.mImageLibrary.end()) + ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find(name); + if (imIt == pParser.mImageLibrary.end()) { ASSIMP_LOG_WARN_F("Collada: Unable to resolve effect texture entry \"", pName, "\", ended up at ID \"", name, "\"."); @@ -1745,7 +1768,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pPars // if this is an embedded texture image setup an aiTexture for it if (imIt->second.mFileName.empty()) { - if (imIt->second.mImageData.empty()) { + if (imIt->second.mImageData.empty()) { throw DeadlyImportError("Collada: Invalid texture, no data or file reference given"); } @@ -1755,13 +1778,13 @@ aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pPars if (imIt->second.mEmbeddedFormat.length() > 3) { ASSIMP_LOG_WARN("Collada: texture format hint is too long, truncating to 3 characters"); } - strncpy(tex->achFormatHint,imIt->second.mEmbeddedFormat.c_str(),3); + strncpy(tex->achFormatHint, imIt->second.mEmbeddedFormat.c_str(), 3); // and copy texture data tex->mHeight = 0; tex->mWidth = static_cast(imIt->second.mImageData.size()); tex->pcData = (aiTexel*)new char[tex->mWidth]; - memcpy(tex->pcData,&imIt->second.mImageData[0],tex->mWidth); + memcpy(tex->pcData, &imIt->second.mImageData[0], tex->mWidth); // TODO: check the possibility of using the flag "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING" // In FBX files textures are now stored internally by Assimp with their filename included @@ -1770,14 +1793,14 @@ aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pPars // This may occur on this case too, it has to be studied // setup texture reference string result.data[0] = '*'; - result.length = 1 + ASSIMP_itoa10(result.data+1,static_cast(MAXLEN-1),static_cast(mTextures.size())); + result.length = 1 + ASSIMP_itoa10(result.data + 1, static_cast(MAXLEN - 1), static_cast(mTextures.size())); // and add this texture to the list mTextures.push_back(tex); } else { - result.Set( imIt->second.mFileName ); + result.Set(imIt->second.mFileName); ConvertPath(result); } return result; @@ -1785,77 +1808,78 @@ aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pPars // ------------------------------------------------------------------------------------------------ // Convert a path read from a collada file to the usual representation -void ColladaLoader::ConvertPath (aiString& ss) +void ColladaLoader::ConvertPath(aiString& ss) { // TODO: collada spec, p 22. Handle URI correctly. // For the moment we're just stripping the file:// away to make it work. // Windows doesn't seem to be able to find stuff like // 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg' - if (0 == strncmp(ss.data,"file://",7)) + if (0 == strncmp(ss.data, "file://", 7)) { ss.length -= 7; - memmove(ss.data,ss.data+7,ss.length); + memmove(ss.data, ss.data + 7, ss.length); ss.data[ss.length] = '\0'; } - // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes... - // I need to filter it without destroying linux paths starting with "/somewhere" + // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes... + // I need to filter it without destroying linux paths starting with "/somewhere" #if defined( _MSC_VER ) - if( ss.data[0] == '/' && isalpha( (unsigned char) ss.data[1]) && ss.data[2] == ':' ) { + if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') { #else - if (ss.data[ 0 ] == '/' && isalpha( ss.data[ 1 ] ) && ss.data[ 2 ] == ':') { + if (ss.data[0] == '/' && isalpha(ss.data[1]) && ss.data[2] == ':') { #endif - --ss.length; - ::memmove( ss.data, ss.data+1, ss.length); - ss.data[ss.length] = 0; - } - - // find and convert all %xy special chars - char* out = ss.data; - for( const char* it = ss.data; it != ss.data + ss.length; /**/ ) - { - if( *it == '%' && (it + 3) < ss.data + ss.length ) - { - // separate the number to avoid dragging in chars from behind into the parsing - char mychar[3] = { it[1], it[2], 0 }; - size_t nbr = strtoul16( mychar); - it += 3; - *out++ = (char)(nbr & 0xFF); - } else - { - *out++ = *it++; + --ss.length; + ::memmove(ss.data, ss.data + 1, ss.length); + ss.data[ss.length] = 0; } - } - // adjust length and terminator of the shortened string - *out = 0; - ss.length = (ptrdiff_t) (out - ss.data); + // find and convert all %xy special chars + char* out = ss.data; + for (const char* it = ss.data; it != ss.data + ss.length; /**/) + { + if (*it == '%' && (it + 3) < ss.data + ss.length) + { + // separate the number to avoid dragging in chars from behind into the parsing + char mychar[3] = { it[1], it[2], 0 }; + size_t nbr = strtoul16(mychar); + it += 3; + *out++ = (char)(nbr & 0xFF); + } + else + { + *out++ = *it++; + } + } + + // adjust length and terminator of the shortened string + *out = 0; + ss.length = (ptrdiff_t)(out - ss.data); } // ------------------------------------------------------------------------------------------------ // Reads a float value from an accessor and its data array. -ai_real ColladaLoader::ReadFloat( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const +ai_real ColladaLoader::ReadFloat(const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const { // FIXME: (thom) Test for data type here in every access? For the moment, I leave this to the caller size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset + pOffset; - ai_assert( pos < pData.mValues.size()); + ai_assert(pos < pData.mValues.size()); return pData.mValues[pos]; } // ------------------------------------------------------------------------------------------------ // Reads a string value from an accessor and its data array. -const std::string& ColladaLoader::ReadString( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex) const +const std::string& ColladaLoader::ReadString(const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex) const { size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset; - ai_assert( pos < pData.mStrings.size()); + ai_assert(pos < pData.mStrings.size()); return pData.mStrings[pos]; } // ------------------------------------------------------------------------------------------------ // Collects all nodes into the given array -void ColladaLoader::CollectNodes( const aiNode* pNode, std::vector& poNodes) const +void ColladaLoader::CollectNodes(const aiNode* pNode, std::vector& poNodes) const { - poNodes.push_back( pNode); + poNodes.push_back(pNode); for (size_t a = 0; a < pNode->mNumChildren; ++a) { CollectNodes(pNode->mChildren[a], poNodes); } @@ -1863,15 +1887,15 @@ void ColladaLoader::CollectNodes( const aiNode* pNode, std::vectormName == pName || pNode->mID == pName) + if (pNode->mName == pName || pNode->mID == pName) return pNode; - for( size_t a = 0; a < pNode->mChildren.size(); ++a) + for (size_t a = 0; a < pNode->mChildren.size(); ++a) { - const Collada::Node* node = FindNode( pNode->mChildren[a], pName); - if( node) + const Collada::Node* node = FindNode(pNode->mChildren[a], pName); + if (node) return node; } @@ -1880,32 +1904,33 @@ const Collada::Node* ColladaLoader::FindNode( const Collada::Node* pNode, const // ------------------------------------------------------------------------------------------------ // Finds a node in the collada scene by the given SID -const Collada::Node* ColladaLoader::FindNodeBySID( const Collada::Node* pNode, const std::string& pSID) const +const Collada::Node* ColladaLoader::FindNodeBySID(const Collada::Node* pNode, const std::string& pSID) const { - if( pNode->mSID == pSID) - return pNode; + if (pNode->mSID == pSID) + return pNode; - for( size_t a = 0; a < pNode->mChildren.size(); ++a) - { - const Collada::Node* node = FindNodeBySID( pNode->mChildren[a], pSID); - if( node) - return node; - } + for (size_t a = 0; a < pNode->mChildren.size(); ++a) + { + const Collada::Node* node = FindNodeBySID(pNode->mChildren[a], pSID); + if (node) + return node; + } - return NULL; + return NULL; } // ------------------------------------------------------------------------------------------------ // Finds a proper unique name for a node derived from the collada-node's properties. // The name must be unique for proper node-bone association. -std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode) +std::string ColladaLoader::FindNameForNode(const Collada::Node* pNode) { // If explicitly requested, just use the collada name. if (useColladaName) { if (!pNode->mName.empty()) { return pNode->mName; - } else { + } + else { return format() << "$ColladaAutoName$_" << mNodeNameCounter++; } } diff --git a/code/Collada/ColladaLoader.h b/code/Collada/ColladaLoader.h index 72c2dd8e7..ad606ea74 100644 --- a/code/Collada/ColladaLoader.h +++ b/code/Collada/ColladaLoader.h @@ -94,7 +94,7 @@ public: public: /** Returns whether the class can handle the format of the given file. * See BaseImporter::CanRead() for details. */ - bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const override; protected: /** Return importer meta information. diff --git a/code/Collada/ColladaParser.cpp b/code/Collada/ColladaParser.cpp index 860ae2ae9..560c0cc9d 100644 --- a/code/Collada/ColladaParser.cpp +++ b/code/Collada/ColladaParser.cpp @@ -57,6 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include @@ -66,9 +67,9 @@ using namespace Assimp::Formatter; // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer -ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile) - : mFileName( pFile ) - , mReader( nullptr ) +ColladaParser::ColladaParser(IOSystem* pIOHandler, const std::string& pFile) + : mFileName(pFile) + , mReader(nullptr) , mDataLibrary() , mAccessorLibrary() , mMeshLibrary() @@ -79,32 +80,60 @@ ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile) , mLightLibrary() , mCameraLibrary() , mControllerLibrary() - , mRootNode( nullptr ) + , mRootNode(nullptr) , mAnims() - , mUnitSize( 1.0f ) - , mUpDirection( UP_Y ) - , mFormat(FV_1_5_n ) // We assume the newest file format by default + , mUnitSize(1.0f) + , mUpDirection(UP_Y) + , mFormat(FV_1_5_n) // We assume the newest file format by default { // validate io-handler instance - if (nullptr == pIOHandler ) { - throw DeadlyImportError("IOSystem is NULL." ); + if (nullptr == pIOHandler) { + throw DeadlyImportError("IOSystem is NULL."); } - // open the file - std::unique_ptr file( pIOHandler->Open(pFile ) ); - if (file.get() == nullptr) { - throw DeadlyImportError( "Failed to open file " + pFile + "." ); + std::unique_ptr daefile; + std::unique_ptr zip_archive; + + // Determine type + std::string extension = BaseImporter::GetExtension(pFile); + if (extension != "dae") { + zip_archive.reset(new ZipArchiveIOSystem(pIOHandler, pFile)); + } + + if (zip_archive && zip_archive->isOpen()) { + std::string dae_filename = ReadZaeManifest(*zip_archive); + + if (dae_filename.empty()) { + ThrowException(std::string("Invalid ZAE")); + } + + daefile.reset(zip_archive->Open(dae_filename.c_str())); + if (daefile == nullptr) { + ThrowException(std::string("Invalid ZAE manifest: '") + std::string(dae_filename) + std::string("' is missing")); + } + } + else { + // attempt to open the file directly + daefile.reset(pIOHandler->Open(pFile)); + if (daefile.get() == nullptr) { + throw DeadlyImportError("Failed to open file '" + pFile + "'."); + } } // generate a XML reader for it - std::unique_ptr mIOWrapper(new CIrrXML_IOStreamReader(file.get())); - mReader = irr::io::createIrrXMLReader( mIOWrapper.get()); + std::unique_ptr mIOWrapper(new CIrrXML_IOStreamReader(daefile.get())); + mReader = irr::io::createIrrXMLReader(mIOWrapper.get()); if (!mReader) { - ThrowException("Collada: Unable to open file."); + ThrowException("Unable to read file, malformed XML"); } // start reading ReadContents(); + + // read embedded textures + if (zip_archive && zip_archive->isOpen()) { + // TODO + } } // ------------------------------------------------------------------------------------------------ @@ -112,18 +141,61 @@ ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile) ColladaParser::~ColladaParser() { delete mReader; - for( NodeLibrary::iterator it = mNodeLibrary.begin(); it != mNodeLibrary.end(); ++it) + for (NodeLibrary::iterator it = mNodeLibrary.begin(); it != mNodeLibrary.end(); ++it) delete it->second; - for( MeshLibrary::iterator it = mMeshLibrary.begin(); it != mMeshLibrary.end(); ++it) + for (MeshLibrary::iterator it = mMeshLibrary.begin(); it != mMeshLibrary.end(); ++it) delete it->second; } +// ------------------------------------------------------------------------------------------------ +// Read a ZAE manifest and return the filename to attempt to open +std::string ColladaParser::ReadZaeManifest(ZipArchiveIOSystem &zip_archive) { + // Open the manifest + std::unique_ptr manifestfile(zip_archive.Open("manifest.xml")); + if (manifestfile == nullptr) + { + // No manifest, hope there is only one .DAE inside + std::vector file_list; + zip_archive.getFileListExtension(file_list, "dae"); + + if (file_list.empty()) + return std::string(); + + return file_list.front(); + } + + std::unique_ptr mIOWrapper(new CIrrXML_IOStreamReader(manifestfile.get())); + irr::io::IrrXMLReader* manifest_reader = irr::io::createIrrXMLReader(mIOWrapper.get()); + + while (manifest_reader->read()) + { + // find the manifest "dae_root" element + if (manifest_reader->getNodeType() == irr::io::EXN_ELEMENT) + { + if (::strcmp(manifest_reader->getNodeName(), "dae_root") == 0) + { + if (!manifest_reader->read()) + return std::string(); + if (manifest_reader->getNodeType() != irr::io::EXN_TEXT && manifest_reader->getNodeType() != irr::io::EXN_CDATA) + return std::string(); + + const char* filepath = manifest_reader->getNodeData(); + if (filepath == nullptr) + return std::string(); + + return std::string(filepath); + } + } + } + return std::string(); +} + // ------------------------------------------------------------------------------------------------ // Read bool from text contents of current element bool ColladaParser::ReadBoolFromTextContent() { const char* cur = GetTextContent(); - return (!ASSIMP_strincmp(cur,"true",4) || '0' != *cur); + return (!ASSIMP_strincmp(cur, "true", 4) || '0' != *cur); } // ------------------------------------------------------------------------------------------------ @@ -138,39 +210,41 @@ ai_real ColladaParser::ReadFloatFromTextContent() // Reads the contents of the file void ColladaParser::ReadContents() { - while( mReader->read()) + while (mReader->read()) { // handle the root element "COLLADA" - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "COLLADA")) + 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; + if (!::strncmp(version, "1.5", 3)) { + mFormat = FV_1_5_n; ASSIMP_LOG_DEBUG("Collada schema version is 1.5.n"); } - else if (!::strncmp(version,"1.4",3)) { - mFormat = FV_1_4_n; + else if (!::strncmp(version, "1.4", 3)) { + mFormat = FV_1_4_n; ASSIMP_LOG_DEBUG("Collada schema version is 1.4.n"); } - else if (!::strncmp(version,"1.3",3)) { - mFormat = FV_1_3_n; + else if (!::strncmp(version, "1.3", 3)) { + mFormat = FV_1_3_n; ASSIMP_LOG_DEBUG("Collada schema version is 1.3.n"); } } ReadStructure(); - } else + } + else { - ASSIMP_LOG_DEBUG_F( "Ignoring global element <", mReader->getNodeName(), ">." ); + ASSIMP_LOG_DEBUG_F("Ignoring global element <", mReader->getNodeName(), ">."); SkipElement(); } - } else + } + else { // skip everything else silently } @@ -181,47 +255,47 @@ void ColladaParser::ReadContents() // Reads the structure of the file void ColladaParser::ReadStructure() { - while( mReader->read()) + while (mReader->read()) { // beginning of elements - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "asset")) + if (IsElement("asset")) ReadAssetInfo(); - else if( IsElement( "library_animations")) + else if (IsElement("library_animations")) ReadAnimationLibrary(); - else if (IsElement("library_animation_clips")) - ReadAnimationClipLibrary(); - else if( IsElement( "library_controllers")) + else if (IsElement("library_animation_clips")) + ReadAnimationClipLibrary(); + else if (IsElement("library_controllers")) ReadControllerLibrary(); - else if( IsElement( "library_images")) + else if (IsElement("library_images")) ReadImageLibrary(); - else if( IsElement( "library_materials")) + else if (IsElement("library_materials")) ReadMaterialLibrary(); - else if( IsElement( "library_effects")) + else if (IsElement("library_effects")) ReadEffectLibrary(); - else if( IsElement( "library_geometries")) + else if (IsElement("library_geometries")) ReadGeometryLibrary(); - else if( IsElement( "library_visual_scenes")) + else if (IsElement("library_visual_scenes")) ReadSceneLibrary(); - else if( IsElement( "library_lights")) + else if (IsElement("library_lights")) ReadLightLibrary(); - else if( IsElement( "library_cameras")) + else if (IsElement("library_cameras")) ReadCameraLibrary(); - else if( IsElement( "library_nodes")) + else if (IsElement("library_nodes")) ReadSceneNode(NULL); /* some hacking to reuse this piece of code */ - else if( IsElement( "scene")) + else if (IsElement("scene")) ReadScene(); else SkipElement(); } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { break; } } - PostProcessRootAnimations(); + PostProcessRootAnimations(); PostProcessControllers(); } @@ -229,43 +303,43 @@ void ColladaParser::ReadStructure() // Reads asset information such as coordinate system information and legal blah void ColladaParser::ReadAssetInfo() { - if( mReader->isEmptyElement()) + if (mReader->isEmptyElement()) return; - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "unit")) + if (IsElement("unit")) { // read unit data from the element's attributes - const int attrIndex = TestAttribute( "meter"); + const int attrIndex = TestAttribute("meter"); if (attrIndex == -1) { mUnitSize = 1.f; } else { - mUnitSize = mReader->getAttributeValueAsFloat( attrIndex); + mUnitSize = mReader->getAttributeValueAsFloat(attrIndex); } // consume the trailing stuff - if( !mReader->isEmptyElement()) + if (!mReader->isEmptyElement()) SkipElement(); } - else if( IsElement( "up_axis")) + else if (IsElement("up_axis")) { // read content, strip whitespace, compare const char* content = GetTextContent(); - if( strncmp( content, "X_UP", 4) == 0) + if (strncmp(content, "X_UP", 4) == 0) mUpDirection = UP_X; - else if( strncmp( content, "Z_UP", 4) == 0) + else if (strncmp(content, "Z_UP", 4) == 0) mUpDirection = UP_Z; else mUpDirection = UP_Y; // check element end - TestClosing( "up_axis"); + TestClosing("up_axis"); } - else if(IsElement("contributor")) + else if (IsElement("contributor")) { ReadContributorInfo(); } @@ -274,10 +348,10 @@ void ColladaParser::ReadAssetInfo() ReadMetaDataItem(mAssetMetaData); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if (strcmp( mReader->getNodeName(), "asset") != 0) - ThrowException( "Expected end of element."); + if (strcmp(mReader->getNodeName(), "asset") != 0) + ThrowException("Expected end of element."); break; } @@ -355,82 +429,82 @@ void ColladaParser::ToCamelCase(std::string &text) // Reads the animation clips void ColladaParser::ReadAnimationClipLibrary() { - if (mReader->isEmptyElement()) - return; + if (mReader->isEmptyElement()) + return; - while (mReader->read()) - { - if (mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if (IsElement("animation_clip")) - { - // 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 = std::string("animation_") + to_string(mAnimationClipLibrary.size()); + while (mReader->read()) + { + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) + { + if (IsElement("animation_clip")) + { + // 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 = std::string("animation_") + to_string(mAnimationClipLibrary.size()); - std::pair > clip; + std::pair > clip; - clip.first = animName; + clip.first = animName; - while (mReader->read()) - { - if (mReader->getNodeType() == irr::io::EXN_ELEMENT) - { - if (IsElement("instance_animation")) - { - int indexUrl = TestAttribute("url"); - if (indexUrl >= 0) - { - const char* url = mReader->getAttributeValue(indexUrl); - if (url[0] != '#') - ThrowException("Unknown reference format"); + while (mReader->read()) + { + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) + { + if (IsElement("instance_animation")) + { + int indexUrl = TestAttribute("url"); + if (indexUrl >= 0) + { + const char* url = mReader->getAttributeValue(indexUrl); + if (url[0] != '#') + ThrowException("Unknown reference format"); - url++; + url++; - clip.second.push_back(url); - } - } - else - { - // ignore the rest - SkipElement(); - } - } - else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if (strcmp(mReader->getNodeName(), "animation_clip") != 0) - ThrowException("Expected end of element."); + clip.second.push_back(url); + } + } + else + { + // ignore the rest + SkipElement(); + } + } + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { + if (strcmp(mReader->getNodeName(), "animation_clip") != 0) + ThrowException("Expected end of element."); - break; - } - } + break; + } + } - if (clip.second.size() > 0) - { - mAnimationClipLibrary.push_back(clip); - } - } - else - { - // ignore the rest - SkipElement(); - } - } - else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - if (strcmp(mReader->getNodeName(), "library_animation_clips") != 0) - ThrowException("Expected end of element."); + if (clip.second.size() > 0) + { + mAnimationClipLibrary.push_back(clip); + } + } + else + { + // ignore the rest + SkipElement(); + } + } + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { + if (strcmp(mReader->getNodeName(), "library_animation_clips") != 0) + ThrowException("Expected end of element."); - break; - } - } + break; + } + } } void ColladaParser::PostProcessControllers() @@ -439,11 +513,11 @@ void ColladaParser::PostProcessControllers() for (ControllerLibrary::iterator it = mControllerLibrary.begin(); it != mControllerLibrary.end(); ++it) { meshId = it->second.mMeshId; ControllerLibrary::iterator findItr = mControllerLibrary.find(meshId); - while(findItr != mControllerLibrary.end()) { + while (findItr != mControllerLibrary.end()) { meshId = findItr->second.mMeshId; findItr = mControllerLibrary.find(meshId); } - + it->second.mMeshId = meshId; } } @@ -452,43 +526,43 @@ void ColladaParser::PostProcessControllers() // Re-build animations from animation clip library, if present, otherwise combine single-channel animations void ColladaParser::PostProcessRootAnimations() { - if (mAnimationClipLibrary.size() > 0) - { - Animation temp; + if (mAnimationClipLibrary.size() > 0) + { + Animation temp; - for (AnimationClipLibrary::iterator it = mAnimationClipLibrary.begin(); it != mAnimationClipLibrary.end(); ++it) - { - std::string clipName = it->first; + for (AnimationClipLibrary::iterator it = mAnimationClipLibrary.begin(); it != mAnimationClipLibrary.end(); ++it) + { + std::string clipName = it->first; - Animation *clip = new Animation(); - clip->mName = clipName; + Animation *clip = new Animation(); + clip->mName = clipName; - temp.mSubAnims.push_back(clip); + temp.mSubAnims.push_back(clip); - for (std::vector::iterator a = it->second.begin(); a != it->second.end(); ++a) - { - std::string animationID = *a; + for (std::vector::iterator a = it->second.begin(); a != it->second.end(); ++a) + { + std::string animationID = *a; - AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID); + AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID); - if (animation != mAnimationLibrary.end()) - { - Animation *pSourceAnimation = animation->second; + if (animation != mAnimationLibrary.end()) + { + Animation *pSourceAnimation = animation->second; - pSourceAnimation->CollectChannelsRecursively(clip->mChannels); - } - } - } + pSourceAnimation->CollectChannelsRecursively(clip->mChannels); + } + } + } - mAnims = temp; + mAnims = temp; - // Ensure no double deletes. - temp.mSubAnims.clear(); - } - else - { - mAnims.CombineSingleChannelAnimations(); - } + // Ensure no double deletes. + temp.mSubAnims.clear(); + } + else + { + mAnims.CombineSingleChannelAnimations(); + } } // ------------------------------------------------------------------------------------------------ @@ -498,24 +572,25 @@ void ColladaParser::ReadAnimationLibrary() if (mReader->isEmptyElement()) return; - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "animation")) + if (IsElement("animation")) { // delegate the reading. Depending on the inner elements it will be a container or a anim channel - ReadAnimation( &mAnims); - } else + ReadAnimation(&mAnims); + } + else { // ignore the rest SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "library_animations") != 0) - ThrowException( "Expected end of element."); + if (strcmp(mReader->getNodeName(), "library_animations") != 0) + ThrowException("Expected end of element."); break; } @@ -524,9 +599,9 @@ void ColladaParser::ReadAnimationLibrary() // ------------------------------------------------------------------------------------------------ // Reads an animation into the given parent structure -void ColladaParser::ReadAnimation( Collada::Animation* pParent) +void ColladaParser::ReadAnimation(Collada::Animation* pParent) { - if( mReader->isEmptyElement()) + if (mReader->isEmptyElement()) return; // an element may be a container for grouping sub-elements or an animation channel @@ -538,68 +613,68 @@ void ColladaParser::ReadAnimation( Collada::Animation* pParent) // optional name given as an attribute std::string animName; - std::string animID; - int indexName = TestAttribute( "name"); - int indexID = TestAttribute( "id"); + std::string animID; + int indexName = TestAttribute("name"); + int indexID = TestAttribute("id"); - if (indexID >= 0) - animID = mReader->getAttributeValue(indexID); + if (indexID >= 0) + animID = mReader->getAttributeValue(indexID); - if( indexName >= 0) - animName = mReader->getAttributeValue( indexName); - else if( indexID >= 0) + if (indexName >= 0) + animName = mReader->getAttributeValue(indexName); + else if (indexID >= 0) animName = animID; else animName = "animation"; - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { // we have subanimations - if( IsElement( "animation")) + if (IsElement("animation")) { // create container from our element - if( !anim) + if (!anim) { anim = new Animation; anim->mName = animName; - pParent->mSubAnims.push_back( anim); + pParent->mSubAnims.push_back(anim); } // recurse into the subelement - ReadAnimation( anim); + ReadAnimation(anim); } - else if( IsElement( "source")) + else if (IsElement("source")) { // possible animation data - we'll never know. Better store it ReadSource(); } - else if( IsElement( "sampler")) + 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; + 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); + ReadAnimationSampler(newChannel->second); } - else if( IsElement( "channel")) + 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] == '#') + 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); + ChannelMap::iterator cit = channels.find(sourceId); + if (cit != channels.end()) + cit->second.mTarget = mReader->getAttributeValue(indexTarget); - if( !mReader->isEmptyElement()) + if (!mReader->isEmptyElement()) SkipElement(); } else @@ -608,24 +683,24 @@ void ColladaParser::ReadAnimation( Collada::Animation* pParent) SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "animation") != 0) - ThrowException( "Expected end of element."); + if (strcmp(mReader->getNodeName(), "animation") != 0) + ThrowException("Expected end of element."); break; } } // it turned out to have channels - add them - if( !channels.empty()) + if (!channels.empty()) { - // FIXME: Is this essentially doing the same as "single-anim-node" codepath in - // ColladaLoader::StoreAnimations? For now, this has been deferred to after - // all animations and all clips have been read. Due to handling of - // this cannot be done here, as the channel owner - // is lost, and some exporters make up animations by referring to multiple - // single-channel animations from an . + // FIXME: Is this essentially doing the same as "single-anim-node" codepath in + // ColladaLoader::StoreAnimations? For now, this has been deferred to after + // all animations and all clips have been read. Due to handling of + // this cannot be done here, as the channel owner + // is lost, and some exporters make up animations by referring to multiple + // single-channel animations from an . /* // special filtering for stupid exporters packing each channel into a separate animation if( channels.size() == 1) @@ -635,53 +710,53 @@ void ColladaParser::ReadAnimation( Collada::Animation* pParent) */ { // else create the animation, if not done yet, and store the channels - if( !anim) + if (!anim) { anim = new Animation; anim->mName = animName; - pParent->mSubAnims.push_back( anim); + pParent->mSubAnims.push_back(anim); } - for( ChannelMap::const_iterator it = channels.begin(); it != channels.end(); ++it) - anim->mChannels.push_back( it->second); + for (ChannelMap::const_iterator it = channels.begin(); it != channels.end(); ++it) + anim->mChannels.push_back(it->second); - if (indexID >= 0) - { - mAnimationLibrary[animID] = anim; - } + if (indexID >= 0) + { + mAnimationLibrary[animID] = anim; + } } } } // ------------------------------------------------------------------------------------------------ // Reads an animation sampler into the given anim channel -void ColladaParser::ReadAnimationSampler( Collada::AnimationChannel& pChannel) +void ColladaParser::ReadAnimationSampler(Collada::AnimationChannel& pChannel) { - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "input")) + 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"); + 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) + if (strcmp(semantic, "INPUT") == 0) pChannel.mSourceTimes = source; - else if( strcmp( semantic, "OUTPUT") == 0) + else if (strcmp(semantic, "OUTPUT") == 0) pChannel.mSourceValues = source; - else if( strcmp( semantic, "IN_TANGENT") == 0) + else if (strcmp(semantic, "IN_TANGENT") == 0) pChannel.mInTanValues = source; - else if( strcmp( semantic, "OUT_TANGENT") == 0) + else if (strcmp(semantic, "OUT_TANGENT") == 0) pChannel.mOutTanValues = source; - else if( strcmp( semantic, "INTERPOLATION") == 0) + else if (strcmp(semantic, "INTERPOLATION") == 0) pChannel.mInterpolationValues = source; - if( !mReader->isEmptyElement()) + if (!mReader->isEmptyElement()) SkipElement(); } else @@ -690,10 +765,10 @@ void ColladaParser::ReadAnimationSampler( Collada::AnimationChannel& pChannel) SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "sampler") != 0) - ThrowException( "Expected end of element."); + if (strcmp(mReader->getNodeName(), "sampler") != 0) + ThrowException("Expected end of element."); break; } @@ -707,31 +782,32 @@ void ColladaParser::ReadControllerLibrary() if (mReader->isEmptyElement()) return; - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "controller")) + if (IsElement("controller")) { // read ID. Ask the spec if it's necessary or optional... you might be surprised. - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); + 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 + ReadController(mControllerLibrary[id]); + } + else { // ignore the rest SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "library_controllers") != 0) - ThrowException( "Expected end of element."); + if (strcmp(mReader->getNodeName(), "library_controllers") != 0) + ThrowException("Expected end of element."); break; } @@ -740,17 +816,17 @@ void ColladaParser::ReadControllerLibrary() // ------------------------------------------------------------------------------------------------ // Reads a controller into the given mesh structure -void ColladaParser::ReadController( Collada::Controller& pController) +void ColladaParser::ReadController(Collada::Controller& pController) { // initial values pController.mType = Skin; pController.mMethod = Normalized; - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + 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")) + if (IsElement("morph")) { pController.mType = Morph; int baseIndex = GetAttribute("source"); @@ -762,47 +838,47 @@ void ColladaParser::ReadController( Collada::Controller& pController) pController.mMethod = Relative; } } - else if( IsElement( "skin")) + 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; + int sourceIndex = GetAttribute("source"); + pController.mMeshId = mReader->getAttributeValue(sourceIndex) + 1; } - else if( IsElement( "bind_shape_matrix")) + 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++) + for (unsigned int a = 0; a < 16; a++) { // read a number - content = fast_atoreal_move( content, pController.mBindShapeMatrix[a]); + content = fast_atoreal_move(content, pController.mBindShapeMatrix[a]); // skip whitespace after it - SkipSpacesAndLineEnd( &content); + SkipSpacesAndLineEnd(&content); } - TestClosing( "bind_shape_matrix"); + TestClosing("bind_shape_matrix"); } - else if( IsElement( "source")) + else if (IsElement("source")) { // data array - we have specialists to handle this ReadSource(); } - else if( IsElement( "joints")) + else if (IsElement("joints")) { - ReadControllerJoints( pController); + ReadControllerJoints(pController); } - else if( IsElement( "vertex_weights")) + else if (IsElement("vertex_weights")) { - ReadControllerWeights( pController); + ReadControllerWeights(pController); } - else if ( IsElement( "targets" )) + else if (IsElement("targets")) { while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if ( IsElement( "input")) { + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if (IsElement("input")) { int semanticsIndex = GetAttribute("semantic"); int sourceIndex = GetAttribute("source"); @@ -816,11 +892,12 @@ void ColladaParser::ReadController( Collada::Controller& pController) pController.mMorphWeight = source + 1; } } - } else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "targets") == 0) + } + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if (strcmp(mReader->getNodeName(), "targets") == 0) break; else - ThrowException( "Expected end of element."); + ThrowException("Expected end of element."); } } } @@ -830,47 +907,47 @@ void ColladaParser::ReadController( Collada::Controller& pController) SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "controller") == 0) + if (strcmp(mReader->getNodeName(), "controller") == 0) break; - else if( strcmp( mReader->getNodeName(), "skin") != 0 && strcmp( mReader->getNodeName(), "morph") != 0) - ThrowException( "Expected end of element."); + else if (strcmp(mReader->getNodeName(), "skin") != 0 && strcmp(mReader->getNodeName(), "morph") != 0) + ThrowException("Expected end of element."); } } } // ------------------------------------------------------------------------------------------------ // Reads the joint definitions for the given controller -void ColladaParser::ReadControllerJoints( Collada::Controller& pController) +void ColladaParser::ReadControllerJoints(Collada::Controller& pController) { - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { // Input channels for joint data. Two possible semantics: "JOINT" and "INV_BIND_MATRIX" - if( IsElement( "input")) + if (IsElement("input")) { - int indexSemantic = GetAttribute( "semantic"); - const char* attrSemantic = mReader->getAttributeValue( indexSemantic); - int indexSource = GetAttribute( "source"); - const char* attrSource = mReader->getAttributeValue( indexSource); + 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( format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of data element" ); + if (attrSource[0] != '#') + ThrowException(format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of data element"); attrSource++; // parse source URL to corresponding source - if( strcmp( attrSemantic, "JOINT") == 0) + if (strcmp(attrSemantic, "JOINT") == 0) pController.mJointNameSource = attrSource; - else if( strcmp( attrSemantic, "INV_BIND_MATRIX") == 0) + else if (strcmp(attrSemantic, "INV_BIND_MATRIX") == 0) pController.mJointOffsetMatrixSource = attrSource; else - ThrowException( format() << "Unknown semantic \"" << attrSemantic << "\" in data element" ); + ThrowException(format() << "Unknown semantic \"" << attrSemantic << "\" in data element"); // skip inner data, if present - if( !mReader->isEmptyElement()) + if (!mReader->isEmptyElement()) SkipElement(); } else @@ -879,10 +956,10 @@ void ColladaParser::ReadControllerJoints( Collada::Controller& pController) SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "joints") != 0) - ThrowException( "Expected end of element."); + if (strcmp(mReader->getNodeName(), "joints") != 0) + ThrowException("Expected end of element."); break; } @@ -891,85 +968,85 @@ void ColladaParser::ReadControllerJoints( Collada::Controller& pController) // ------------------------------------------------------------------------------------------------ // Reads the joint weights for the given controller -void ColladaParser::ReadControllerWeights( Collada::Controller& pController) +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); + int indexCount = GetAttribute("count"); + size_t vertexCount = (size_t)mReader->getAttributeValueAsInt(indexCount); + pController.mWeightCounts.resize(vertexCount); - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { // Input channels for weight data. Two possible semantics: "JOINT" and "WEIGHT" - if( IsElement( "input") && vertexCount > 0 ) + 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); + 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( format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of data element" ); + if (attrSource[0] != '#') + ThrowException(format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of data element"); channel.mAccessor = attrSource + 1; // parse source URL to corresponding source - if( strcmp( attrSemantic, "JOINT") == 0) + if (strcmp(attrSemantic, "JOINT") == 0) pController.mWeightInputJoints = channel; - else if( strcmp( attrSemantic, "WEIGHT") == 0) + else if (strcmp(attrSemantic, "WEIGHT") == 0) pController.mWeightInputWeights = channel; else - ThrowException( format() << "Unknown semantic \"" << attrSemantic << "\" in data element" ); + ThrowException(format() << "Unknown semantic \"" << attrSemantic << "\" in data element"); // skip inner data, if present - if( !mReader->isEmptyElement()) + if (!mReader->isEmptyElement()) SkipElement(); } - else if( IsElement( "vcount") && vertexCount > 0 ) + 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) + for (std::vector::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it) { - if( *text == 0) - ThrowException( "Out of data while reading "); + if (*text == 0) + ThrowException("Out of data while reading "); - *it = strtoul10( text, &text); + *it = strtoul10(text, &text); numWeights += *it; - SkipSpacesAndLineEnd( &text); + SkipSpacesAndLineEnd(&text); } - TestClosing( "vcount"); + TestClosing("vcount"); // reserve weight count - pController.mWeights.resize( numWeights); + pController.mWeights.resize(numWeights); } - else if( IsElement( "v") && vertexCount > 0 ) + 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) + 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); + 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"); + TestClosing("v"); } else { @@ -977,10 +1054,10 @@ void ColladaParser::ReadControllerWeights( Collada::Controller& pController) SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "vertex_weights") != 0) - ThrowException( "Expected end of element."); + if (strcmp(mReader->getNodeName(), "vertex_weights") != 0) + ThrowException("Expected end of element."); break; } @@ -991,32 +1068,33 @@ void ColladaParser::ReadControllerWeights( Collada::Controller& pController) // Reads the image library contents void ColladaParser::ReadImageLibrary() { - if( mReader->isEmptyElement()) + if (mReader->isEmptyElement()) return; - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "image")) + 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); + 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 + 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."); + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if (strcmp(mReader->getNodeName(), "library_images") != 0) + ThrowException("Expected end of element."); break; } @@ -1025,16 +1103,16 @@ void ColladaParser::ReadImageLibrary() // ------------------------------------------------------------------------------------------------ // Reads an image entry into the given image -void ColladaParser::ReadImage( Collada::Image& pImage) +void ColladaParser::ReadImage(Collada::Image& pImage) { - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT){ + 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")) + else if (IsElement("init_from")) { if (mFormat == FV_1_4_n) { @@ -1043,7 +1121,7 @@ void ColladaParser::ReadImage( Collada::Image& pImage) // element content is filename - hopefully const char* sz = TestTextContent(); if (sz)pImage.mFileName = sz; - TestClosing( "init_from"); + TestClosing("init_from"); } if (!pImage.mFileName.length()) { pImage.mFileName = "unknown_texture"; @@ -1071,14 +1149,14 @@ void ColladaParser::ReadImage( Collada::Image& pImage) } else if (mFormat == FV_1_5_n) { - if( IsElement( "ref")) + if (IsElement("ref")) { // element content is filename - hopefully const char* sz = TestTextContent(); if (sz)pImage.mFileName = sz; - TestClosing( "ref"); + TestClosing("ref"); } - else if( IsElement( "hex") && !pImage.mFileName.length()) + else if (IsElement("hex") && !pImage.mFileName.length()) { // embedded image. get format const int attrib = TestAttribute("format"); @@ -1093,12 +1171,12 @@ void ColladaParser::ReadImage( Collada::Image& pImage) const char* cur = data; while (!IsSpaceOrNewLine(*cur)) cur++; - const unsigned int size = (unsigned int)(cur-data) * 2; + 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)); + for (unsigned int i = 0; i < size; ++i) + pImage.mImageData[i] = HexOctetToDecimal(data + (i << 1)); - TestClosing( "hex"); + TestClosing("hex"); } } else @@ -1107,8 +1185,8 @@ void ColladaParser::ReadImage( Collada::Image& pImage) SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "image") == 0) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if (strcmp(mReader->getNodeName(), "image") == 0) break; } } @@ -1118,36 +1196,36 @@ void ColladaParser::ReadImage( Collada::Image& pImage) // Reads the material library void ColladaParser::ReadMaterialLibrary() { - if( mReader->isEmptyElement()) + if (mReader->isEmptyElement()) return; std::map names; - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "material")) + if (IsElement("material")) { // read ID. By now you probably know my opinion about this "specification" - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); + int attrID = GetAttribute("id"); + std::string id = mReader->getAttributeValue(attrID); std::string name; int attrName = TestAttribute("name"); if (attrName >= 0) - name = mReader->getAttributeValue( attrName); + name = mReader->getAttributeValue(attrName); // create an entry and store it in the library under its ID mMaterialLibrary[id] = Material(); - if( !name.empty()) + if (!name.empty()) { - std::map::iterator it = names.find( name); - if( it != names.end()) + std::map::iterator it = names.find(name); + if (it != names.end()) { std::ostringstream strStream; strStream << ++it->second; - name.append( " " + strStream.str()); + name.append(" " + strStream.str()); } else { @@ -1157,17 +1235,18 @@ void ColladaParser::ReadMaterialLibrary() mMaterialLibrary[id].mName = name; } - ReadMaterial( mMaterialLibrary[id]); - } else + ReadMaterial(mMaterialLibrary[id]); + } + else { // ignore the rest SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "library_materials") != 0) - ThrowException( "Expected end of element."); + if (strcmp(mReader->getNodeName(), "library_materials") != 0) + ThrowException("Expected end of element."); break; } @@ -1178,30 +1257,31 @@ void ColladaParser::ReadMaterialLibrary() // Reads the light library void ColladaParser::ReadLightLibrary() { - if( mReader->isEmptyElement()) + if (mReader->isEmptyElement()) return; - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "light")) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if (IsElement("light")) { // read ID. By now you probably know my opinion about this "specification" - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); + 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 + } + 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."); + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if (strcmp(mReader->getNodeName(), "library_lights") != 0) + ThrowException("Expected end of element."); break; } @@ -1212,35 +1292,36 @@ void ColladaParser::ReadLightLibrary() // Reads the camera library void ColladaParser::ReadCameraLibrary() { - if( mReader->isEmptyElement()) + if (mReader->isEmptyElement()) return; - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "camera")) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if (IsElement("camera")) { // read ID. By now you probably know my opinion about this "specification" - int attrID = GetAttribute( "id"); - std::string id = mReader->getAttributeValue( attrID); + 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"); + attrID = TestAttribute("name"); if (attrID != -1) - cam.mName = mReader->getAttributeValue( attrID); + cam.mName = mReader->getAttributeValue(attrID); ReadCamera(cam); - } else + } + 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."); + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if (strcmp(mReader->getNodeName(), "library_cameras") != 0) + ThrowException("Expected end of element."); break; } @@ -1249,34 +1330,35 @@ void ColladaParser::ReadCameraLibrary() // ------------------------------------------------------------------------------------------------ // Reads a material entry into the given material -void ColladaParser::ReadMaterial( Collada::Material& pMaterial) +void ColladaParser::ReadMaterial(Collada::Material& pMaterial) { - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { if (IsElement("material")) { SkipElement(); } - else if( IsElement( "instance_effect")) + 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"); + int attrUrl = GetAttribute("url"); + const char* url = mReader->getAttributeValue(attrUrl); + if (url[0] != '#') + ThrowException("Unknown reference format"); - pMaterial.mEffect = url+1; + pMaterial.mEffect = url + 1; SkipElement(); - } else + } + 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."); + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if (strcmp(mReader->getNodeName(), "material") != 0) + ThrowException("Expected end of element."); break; } @@ -1285,11 +1367,11 @@ void ColladaParser::ReadMaterial( Collada::Material& pMaterial) // ------------------------------------------------------------------------------------------------ // Reads a light entry into the given light -void ColladaParser::ReadLight( Collada::Light& pLight) +void ColladaParser::ReadLight(Collada::Light& pLight) { - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { if (IsElement("light")) { SkipElement(); } @@ -1309,16 +1391,16 @@ void ColladaParser::ReadLight( Collada::Light& pLight) // text content contains 3 floats const char* content = GetTextContent(); - content = fast_atoreal_move( content, (ai_real&)pLight.mColor.r); - SkipSpacesAndLineEnd( &content); + content = fast_atoreal_move(content, (ai_real&)pLight.mColor.r); + SkipSpacesAndLineEnd(&content); - content = fast_atoreal_move( content, (ai_real&)pLight.mColor.g); - SkipSpacesAndLineEnd( &content); + content = fast_atoreal_move(content, (ai_real&)pLight.mColor.g); + SkipSpacesAndLineEnd(&content); - content = fast_atoreal_move( content, (ai_real&)pLight.mColor.b); - SkipSpacesAndLineEnd( &content); + content = fast_atoreal_move(content, (ai_real&)pLight.mColor.b); + SkipSpacesAndLineEnd(&content); - TestClosing( "color"); + TestClosing("color"); } else if (IsElement("constant_attenuation")) { pLight.mAttConstant = ReadFloatFromTextContent(); @@ -1370,8 +1452,8 @@ void ColladaParser::ReadLight( Collada::Light& pLight) TestClosing("decay_falloff"); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "light") == 0) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if (strcmp(mReader->getNodeName(), "light") == 0) break; } } @@ -1379,11 +1461,11 @@ void ColladaParser::ReadLight( Collada::Light& pLight) // ------------------------------------------------------------------------------------------------ // Reads a camera entry into the given light -void ColladaParser::ReadCamera( Collada::Camera& pCamera) +void ColladaParser::ReadCamera(Collada::Camera& pCamera) { - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { if (IsElement("camera")) { SkipElement(); } @@ -1411,8 +1493,8 @@ void ColladaParser::ReadCamera( Collada::Camera& pCamera) TestClosing("zfar"); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "camera") == 0) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if (strcmp(mReader->getNodeName(), "camera") == 0) break; } } @@ -1426,28 +1508,29 @@ void ColladaParser::ReadEffectLibrary() return; } - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "effect")) + 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); + 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 + 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."); + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if (strcmp(mReader->getNodeName(), "library_effects") != 0) + ThrowException("Expected end of element."); break; } @@ -1456,22 +1539,22 @@ void ColladaParser::ReadEffectLibrary() // ------------------------------------------------------------------------------------------------ // Reads an effect entry into the given effect -void ColladaParser::ReadEffect( Collada::Effect& pEffect) +void ColladaParser::ReadEffect(Collada::Effect& pEffect) { // for the moment we don't support any other type of effect. - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "profile_COMMON")) - ReadEffectProfileCommon( pEffect); + if (IsElement("profile_COMMON")) + ReadEffectProfileCommon(pEffect); else SkipElement(); } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "effect") != 0) - ThrowException( "Expected end of element."); + if (strcmp(mReader->getNodeName(), "effect") != 0) + ThrowException("Expected end of element."); break; } @@ -1480,107 +1563,107 @@ void ColladaParser::ReadEffect( Collada::Effect& pEffect) // ------------------------------------------------------------------------------------------------ // Reads an COMMON effect profile -void ColladaParser::ReadEffectProfileCommon( Collada::Effect& pEffect) +void ColladaParser::ReadEffectProfileCommon(Collada::Effect& pEffect) { - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "newparam")) { + if (IsElement("newparam")) { // save ID - int attrSID = GetAttribute( "sid"); - std::string sid = mReader->getAttributeValue( attrSID); + int attrSID = GetAttribute("sid"); + std::string sid = mReader->getAttributeValue(attrSID); pEffect.mParams[sid] = EffectParam(); - ReadEffectParam( pEffect.mParams[sid]); + ReadEffectParam(pEffect.mParams[sid]); } - else if( IsElement( "technique") || IsElement( "extra")) + else if (IsElement("technique") || IsElement("extra")) { // just syntactic sugar } - else if( mFormat == FV_1_4_n && IsElement( "image")) + 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); + 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]); + ReadImage(mImageLibrary[id]); } /* Shading modes */ - else if( IsElement( "phong")) + else if (IsElement("phong")) pEffect.mShadeType = Shade_Phong; - else if( IsElement( "constant")) + else if (IsElement("constant")) pEffect.mShadeType = Shade_Constant; - else if( IsElement( "lambert")) + else if (IsElement("lambert")) pEffect.mShadeType = Shade_Lambert; - else if( IsElement( "blinn")) + 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("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")) { + else if (IsElement("transparent")) { pEffect.mHasTransparency = true; const char* opaque = mReader->getAttributeValueSafe("opaque"); - if(::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "RGB_ONE") == 0) { + if (::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "RGB_ONE") == 0) { pEffect.mRGBTransparency = true; } // In RGB_ZERO mode, the transparency is interpreted in reverse, go figure... - if(::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "A_ZERO") == 0) { - pEffect.mInvertTransparency = true; - } + if (::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "A_ZERO") == 0) { + pEffect.mInvertTransparency = true; + } - ReadEffectColor( pEffect.mTransparent,pEffect.mTexTransparent); + ReadEffectColor(pEffect.mTransparent, pEffect.mTexTransparent); } - else if( IsElement( "shininess")) - ReadEffectFloat( pEffect.mShininess); - else if( IsElement( "reflectivity")) - ReadEffectFloat( pEffect.mReflectivity); + 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); + else if (IsElement("transparency")) + ReadEffectFloat(pEffect.mTransparency); + else if (IsElement("index_of_refraction")) + ReadEffectFloat(pEffect.mRefractIndex); // GOOGLEEARTH/OKINO extensions // ------------------------------------------------------- - else if( IsElement( "double_sided")) + else if (IsElement("double_sided")) pEffect.mDoubleSided = ReadBoolFromTextContent(); // FCOLLADA extensions // ------------------------------------------------------- - else if( IsElement( "bump")) { + else if (IsElement("bump")) { aiColor4D dummy; - ReadEffectColor( dummy,pEffect.mTexBump); + ReadEffectColor(dummy, pEffect.mTexBump); } // MAX3D extensions // ------------------------------------------------------- - else if( IsElement( "wireframe")) { + else if (IsElement("wireframe")) { pEffect.mWireframe = ReadBoolFromTextContent(); - TestClosing( "wireframe"); + TestClosing("wireframe"); } - else if( IsElement( "faceted")) { + else if (IsElement("faceted")) { pEffect.mFaceted = ReadBoolFromTextContent(); - TestClosing( "faceted"); + TestClosing("faceted"); } else { @@ -1588,8 +1671,8 @@ void ColladaParser::ReadEffectProfileCommon( Collada::Effect& pEffect) SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "profile_COMMON") == 0) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if (strcmp(mReader->getNodeName(), "profile_COMMON") == 0) { break; } @@ -1599,92 +1682,92 @@ void ColladaParser::ReadEffectProfileCommon( Collada::Effect& pEffect) // ------------------------------------------------------------------------------------------------ // Read texture wrapping + UV transform settings from a profile==Maya chunk -void ColladaParser::ReadSamplerProperties( Sampler& out ) +void ColladaParser::ReadSamplerProperties(Sampler& out) { if (mReader->isEmptyElement()) { return; } - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { // MAYA extensions // ------------------------------------------------------- - if( IsElement( "wrapU")) { + if (IsElement("wrapU")) { out.mWrapU = ReadBoolFromTextContent(); - TestClosing( "wrapU"); + TestClosing("wrapU"); } - else if( IsElement( "wrapV")) { + else if (IsElement("wrapV")) { out.mWrapV = ReadBoolFromTextContent(); - TestClosing( "wrapV"); + TestClosing("wrapV"); } - else if( IsElement( "mirrorU")) { + else if (IsElement("mirrorU")) { out.mMirrorU = ReadBoolFromTextContent(); - TestClosing( "mirrorU"); + TestClosing("mirrorU"); } - else if( IsElement( "mirrorV")) { + else if (IsElement("mirrorV")) { out.mMirrorV = ReadBoolFromTextContent(); - TestClosing( "mirrorV"); + TestClosing("mirrorV"); } - else if( IsElement( "repeatU")) { + else if (IsElement("repeatU")) { out.mTransform.mScaling.x = ReadFloatFromTextContent(); - TestClosing( "repeatU"); + TestClosing("repeatU"); } - else if( IsElement( "repeatV")) { + else if (IsElement("repeatV")) { out.mTransform.mScaling.y = ReadFloatFromTextContent(); - TestClosing( "repeatV"); + TestClosing("repeatV"); } - else if( IsElement( "offsetU")) { + else if (IsElement("offsetU")) { out.mTransform.mTranslation.x = ReadFloatFromTextContent(); - TestClosing( "offsetU"); + TestClosing("offsetU"); } - else if( IsElement( "offsetV")) { + else if (IsElement("offsetV")) { out.mTransform.mTranslation.y = ReadFloatFromTextContent(); - TestClosing( "offsetV"); + TestClosing("offsetV"); } - else if( IsElement( "rotateUV")) { + else if (IsElement("rotateUV")) { out.mTransform.mRotation = ReadFloatFromTextContent(); - TestClosing( "rotateUV"); + TestClosing("rotateUV"); } - else if( IsElement( "blend_mode")) { + 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)) + if (0 == ASSIMP_strincmp(sz, "ADD", 3)) out.mOp = aiTextureOp_Add; - else if (0 == ASSIMP_strincmp(sz,"SUBTRACT",8)) + else if (0 == ASSIMP_strincmp(sz, "SUBTRACT", 8)) out.mOp = aiTextureOp_Subtract; - else if (0 == ASSIMP_strincmp(sz,"MULTIPLY",8)) + else if (0 == ASSIMP_strincmp(sz, "MULTIPLY", 8)) out.mOp = aiTextureOp_Multiply; - else { + else { ASSIMP_LOG_WARN("Collada: Unsupported MAYA texture blend mode"); } - TestClosing( "blend_mode"); + TestClosing("blend_mode"); } // OKINO extensions // ------------------------------------------------------- - else if( IsElement( "weighting")) { + else if (IsElement("weighting")) { out.mWeighting = ReadFloatFromTextContent(); - TestClosing( "weighting"); + TestClosing("weighting"); } - else if( IsElement( "mix_with_previous_layer")) { + else if (IsElement("mix_with_previous_layer")) { out.mMixWithPrevious = ReadFloatFromTextContent(); - TestClosing( "mix_with_previous_layer"); + TestClosing("mix_with_previous_layer"); } // MAX3D extensions // ------------------------------------------------------- - else if( IsElement( "amount")) { + else if (IsElement("amount")) { out.mWeighting = ReadFloatFromTextContent(); - TestClosing( "amount"); + TestClosing("amount"); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "technique") == 0) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if (strcmp(mReader->getNodeName(), "technique") == 0) break; } } @@ -1692,7 +1775,7 @@ void ColladaParser::ReadSamplerProperties( Sampler& out ) // ------------------------------------------------------------------------------------------------ // Reads an effect entry containing a color or a texture defining that color -void ColladaParser::ReadEffectColor( aiColor4D& pColor, Sampler& pSampler) +void ColladaParser::ReadEffectColor(aiColor4D& pColor, Sampler& pSampler) { if (mReader->isEmptyElement()) return; @@ -1700,64 +1783,64 @@ void ColladaParser::ReadEffectColor( aiColor4D& pColor, Sampler& pSampler) // Save current element name const std::string curElem = mReader->getNodeName(); - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "color")) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if (IsElement("color")) { // text content contains 4 floats const char* content = GetTextContent(); - content = fast_atoreal_move( content, (ai_real&)pColor.r); - SkipSpacesAndLineEnd( &content); + content = fast_atoreal_move(content, (ai_real&)pColor.r); + SkipSpacesAndLineEnd(&content); - content = fast_atoreal_move( content, (ai_real&)pColor.g); - SkipSpacesAndLineEnd( &content); + content = fast_atoreal_move(content, (ai_real&)pColor.g); + SkipSpacesAndLineEnd(&content); - content = fast_atoreal_move( content, (ai_real&)pColor.b); - SkipSpacesAndLineEnd( &content); + content = fast_atoreal_move(content, (ai_real&)pColor.b); + SkipSpacesAndLineEnd(&content); - content = fast_atoreal_move( content, (ai_real&)pColor.a); - SkipSpacesAndLineEnd( &content); - TestClosing( "color"); + content = fast_atoreal_move(content, (ai_real&)pColor.a); + SkipSpacesAndLineEnd(&content); + TestClosing("color"); } - else if( IsElement( "texture")) + else if (IsElement("texture")) { // get name of source texture/sampler - int attrTex = GetAttribute( "texture"); - pSampler.mName = mReader->getAttributeValue( attrTex); + 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); + 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")) + else if (IsElement("technique")) { - const int _profile = GetAttribute( "profile"); - const char* profile = mReader->getAttributeValue( _profile ); + 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")) + if (!::strcmp(profile, "MAYA") || !::strcmp(profile, "MAX3D") || !::strcmp(profile, "OKINO")) { // get more information on this sampler ReadSamplerProperties(pSampler); } else SkipElement(); } - else if( !IsElement( "extra")) + else if (!IsElement("extra")) { // ignore the rest SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){ + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { if (mReader->getNodeName() == curElem) break; } @@ -1766,26 +1849,27 @@ void ColladaParser::ReadEffectColor( aiColor4D& pColor, Sampler& pSampler) // ------------------------------------------------------------------------------------------------ // Reads an effect entry containing a float -void ColladaParser::ReadEffectFloat( ai_real& pFloat) +void ColladaParser::ReadEffectFloat(ai_real& pFloat) { - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT){ - if( IsElement( "float")) + 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); + content = fast_atoreal_move(content, pFloat); + SkipSpacesAndLineEnd(&content); - TestClosing( "float"); - } else + TestClosing("float"); + } + else { // ignore the rest SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){ + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { break; } } @@ -1793,54 +1877,55 @@ void ColladaParser::ReadEffectFloat( ai_real& pFloat) // ------------------------------------------------------------------------------------------------ // Reads an effect parameter specification of any kind -void ColladaParser::ReadEffectParam( Collada::EffectParam& pParam) +void ColladaParser::ReadEffectParam(Collada::EffectParam& pParam) { - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "surface")) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if (IsElement("surface")) { // image ID given inside tags - TestOpening( "init_from"); + TestOpening("init_from"); const char* content = GetTextContent(); pParam.mType = Param_Surface; pParam.mReference = content; - TestClosing( "init_from"); + TestClosing("init_from"); // don't care for remaining stuff - SkipElement( "surface"); + SkipElement("surface"); } - else if( IsElement( "sampler2D") && (FV_1_4_n == mFormat || FV_1_3_n == mFormat)) + else if (IsElement("sampler2D") && (FV_1_4_n == mFormat || FV_1_3_n == mFormat)) { // surface ID is given inside tags - TestOpening( "source"); + TestOpening("source"); const char* content = GetTextContent(); pParam.mType = Param_Sampler; pParam.mReference = content; - TestClosing( "source"); + TestClosing("source"); // don't care for remaining stuff - SkipElement( "sampler2D"); + SkipElement("sampler2D"); } - else if( IsElement( "sampler2D")) + else if (IsElement("sampler2D")) { // surface ID is given inside tags - TestOpening( "instance_image"); + TestOpening("instance_image"); int attrURL = GetAttribute("url"); - const char* url = mReader->getAttributeValue( attrURL); - if( url[0] != '#') - ThrowException( "Unsupported URL format in instance_image"); + const char* url = mReader->getAttributeValue(attrURL); + if (url[0] != '#') + ThrowException("Unsupported URL format in instance_image"); url++; pParam.mType = Param_Sampler; pParam.mReference = url; - SkipElement( "sampler2D"); - } else + SkipElement("sampler2D"); + } + else { // ignore unknown element SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { break; } } @@ -1850,18 +1935,18 @@ void ColladaParser::ReadEffectParam( Collada::EffectParam& pParam) // Reads the geometry library contents void ColladaParser::ReadGeometryLibrary() { - if( mReader->isEmptyElement()) + if (mReader->isEmptyElement()) return; - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "geometry")) + 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); + int indexID = GetAttribute("id"); + std::string id = mReader->getAttributeValue(indexID); // TODO: (thom) support SIDs // ai_assert( TestAttribute( "sid") == -1); @@ -1872,23 +1957,24 @@ void ColladaParser::ReadGeometryLibrary() // read the mesh name if it exists const int nameIndex = TestAttribute("name"); - if(nameIndex != -1) + if (nameIndex != -1) { mesh->mName = mReader->getAttributeValue(nameIndex); } // read on from there - ReadGeometry( mesh); - } else + ReadGeometry(mesh); + } + else { // ignore the rest SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "library_geometries") != 0) - ThrowException( "Expected end of element."); + if (strcmp(mReader->getNodeName(), "library_geometries") != 0) + ThrowException("Expected end of element."); break; } @@ -1897,29 +1983,30 @@ void ColladaParser::ReadGeometryLibrary() // ------------------------------------------------------------------------------------------------ // Reads a geometry from the geometry library. -void ColladaParser::ReadGeometry( Collada::Mesh* pMesh) +void ColladaParser::ReadGeometry(Collada::Mesh* pMesh) { - if( mReader->isEmptyElement()) + if (mReader->isEmptyElement()) return; - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "mesh")) + if (IsElement("mesh")) { // read on from there - ReadMesh( pMesh); - } else + ReadMesh(pMesh); + } + else { // ignore the rest SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "geometry") != 0) - ThrowException( "Expected end of element."); + if (strcmp(mReader->getNodeName(), "geometry") != 0) + ThrowException("Expected end of element."); break; } @@ -1928,50 +2015,52 @@ void ColladaParser::ReadGeometry( Collada::Mesh* pMesh) // ------------------------------------------------------------------------------------------------ // Reads a mesh from the geometry library -void ColladaParser::ReadMesh( Mesh* pMesh) +void ColladaParser::ReadMesh(Mesh* pMesh) { - if( mReader->isEmptyElement()) + if (mReader->isEmptyElement()) return; - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "source")) + if (IsElement("source")) { // we have professionals dealing with this ReadSource(); } - else if( IsElement( "vertices")) + else if (IsElement("vertices")) { // read per-vertex mesh data - ReadVertexData( pMesh); + ReadVertexData(pMesh); } - else if( IsElement( "triangles") || IsElement( "lines") || IsElement( "linestrips") - || IsElement( "polygons") || IsElement( "polylist") || IsElement( "trifans") || IsElement( "tristrips")) + 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 + ReadIndexData(pMesh); + } + else { // ignore the restf SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "technique_common") == 0) + if (strcmp(mReader->getNodeName(), "technique_common") == 0) { // end of another meaningless element - read over it } - else if( strcmp( mReader->getNodeName(), "mesh") == 0) + else if (strcmp(mReader->getNodeName(), "mesh") == 0) { // end of element - we're done here break; - } else + } + else { // everything else should be punished - ThrowException( "Expected end of element."); + ThrowException("Expected end of element."); } } } @@ -1981,44 +2070,46 @@ void ColladaParser::ReadMesh( Mesh* pMesh) // Reads a source element void ColladaParser::ReadSource() { - int indexID = GetAttribute( "id"); - std::string sourceID = mReader->getAttributeValue( indexID); + int indexID = GetAttribute("id"); + std::string sourceID = mReader->getAttributeValue(indexID); - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "float_array") || IsElement( "IDREF_array") || IsElement( "Name_array")) + if (IsElement("float_array") || IsElement("IDREF_array") || IsElement("Name_array")) { ReadDataArray(); } - else if( IsElement( "technique_common")) + else if (IsElement("technique_common")) { // I don't care for your profiles } - else if( IsElement( "accessor")) + else if (IsElement("accessor")) { - ReadAccessor( sourceID); - } else + ReadAccessor(sourceID); + } + else { // ignore the rest SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "source") == 0) + if (strcmp(mReader->getNodeName(), "source") == 0) { // end of - we're done break; } - else if( strcmp( mReader->getNodeName(), "technique_common") == 0) + else if (strcmp(mReader->getNodeName(), "technique_common") == 0) { // end of another meaningless element - read over it - } else + } + else { // everything else should be punished - ThrowException( "Expected end of element."); + ThrowException("Expected end of element."); } } } @@ -2030,83 +2121,84 @@ void ColladaParser::ReadDataArray() { std::string elmName = mReader->getNodeName(); bool isStringArray = (elmName == "IDREF_array" || elmName == "Name_array"); - bool isEmptyElement = mReader->isEmptyElement(); + 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); + 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; + // 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) + // 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); + data.mStrings.reserve(count); std::string s; - for( unsigned int a = 0; a < count; a++) + for (unsigned int a = 0; a < count; a++) { - if( *content == 0) - ThrowException( "Expected more values while reading IDREF_array contents."); + if (*content == 0) + ThrowException("Expected more values while reading IDREF_array contents."); s.clear(); - while( !IsSpaceOrNewLine( *content)) + while (!IsSpaceOrNewLine(*content)) s += *content++; - data.mStrings.push_back( s); + data.mStrings.push_back(s); - SkipSpacesAndLineEnd( &content); + SkipSpacesAndLineEnd(&content); } - } else + } + else { - data.mValues.reserve( count); + data.mValues.reserve(count); - for( unsigned int a = 0; a < count; a++) + for (unsigned int a = 0; a < count; a++) { - if( *content == 0) - ThrowException( "Expected more values while reading float_array contents."); + if (*content == 0) + ThrowException("Expected more values while reading float_array contents."); ai_real value; // read a number - content = fast_atoreal_move( content, value); - data.mValues.push_back( value); + content = fast_atoreal_move(content, value); + data.mValues.push_back(value); // skip whitespace after it - SkipSpacesAndLineEnd( &content); + SkipSpacesAndLineEnd(&content); } } } - // test for closing tag - if( !isEmptyElement ) - TestClosing( elmName.c_str()); + // 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) +void ColladaParser::ReadAccessor(const std::string& pID) { // read accessor attributes - int attrSource = GetAttribute( "source"); - const char* source = mReader->getAttributeValue( attrSource); - if( source[0] != '#') - ThrowException( format() << "Unknown reference format in url \"" << source << "\" in source attribute of element." ); - int attrCount = GetAttribute( "count"); - unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( attrCount); - int attrOffset = TestAttribute( "offset"); + int attrSource = GetAttribute("source"); + const char* source = mReader->getAttributeValue(attrSource); + if (source[0] != '#') + ThrowException(format() << "Unknown reference format in url \"" << source << "\" in source attribute of element."); + 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"); + 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); + if (attrStride > -1) + stride = (unsigned int)mReader->getAttributeValueAsInt(attrStride); // store in the library under the given ID mAccessorLibrary[pID] = Accessor(); @@ -2114,77 +2206,78 @@ void ColladaParser::ReadAccessor( const std::string& pID) acc.mCount = count; acc.mOffset = offset; acc.mStride = stride; - acc.mSource = source+1; // ignore the leading '#' + acc.mSource = source + 1; // ignore the leading '#' acc.mSize = 0; // gets incremented with every param // and read the components - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "param")) + if (IsElement("param")) { // read data param - int attrName = TestAttribute( "name"); + int attrName = TestAttribute("name"); std::string name; - if( attrName > -1) + if (attrName > -1) { - name = mReader->getAttributeValue( attrName); + 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(); + 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(); + 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 */ + 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(); + /* 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( format() << "Unknown accessor parameter \"" << name << "\". Ignoring data channel." ); } // read data type - int attrType = TestAttribute( "type"); - if( attrType > -1) + 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") + std::string type = mReader->getAttributeValue(attrType); + if (type == "float4x4") acc.mSize += 16; else acc.mSize += 1; } - acc.mParams.push_back( name); + acc.mParams.push_back(name); // skip remaining stuff of this element, if any SkipElement(); - } else + } + else { - ThrowException( format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag " ); + ThrowException(format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag "); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "accessor") != 0) - ThrowException( "Expected end of element."); + if (strcmp(mReader->getNodeName(), "accessor") != 0) + ThrowException("Expected end of element."); break; } } @@ -2192,29 +2285,30 @@ void ColladaParser::ReadAccessor( const std::string& pID) // ------------------------------------------------------------------------------------------------ // Reads input declarations of per-vertex mesh data into the given mesh -void ColladaParser::ReadVertexData( Mesh* pMesh) +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); + int attrID = GetAttribute("id"); + pMesh->mVertexID = mReader->getAttributeValue(attrID); // a number of elements - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "input")) + if (IsElement("input")) { - ReadInputChannel( pMesh->mPerVertexData); - } else + ReadInputChannel(pMesh->mPerVertexData); + } + else { - ThrowException( format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag " ); + ThrowException(format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag "); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "vertices") != 0) - ThrowException( "Expected end of element."); + if (strcmp(mReader->getNodeName(), "vertices") != 0) + ThrowException("Expected end of element."); break; } @@ -2223,79 +2317,79 @@ void ColladaParser::ReadVertexData( Mesh* pMesh) // ------------------------------------------------------------------------------------------------ // Reads input declarations of per-index mesh data into the given mesh -void ColladaParser::ReadIndexData( Mesh* pMesh) +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); + 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"); + int attrMaterial = TestAttribute("material"); SubMesh subgroup; - if( attrMaterial > -1) - subgroup.mMaterial = mReader->getAttributeValue( attrMaterial); + 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")) + if (IsElement("lines")) primType = Prim_Lines; - else if( IsElement( "linestrips")) + else if (IsElement("linestrips")) primType = Prim_LineStrip; - else if( IsElement( "polygons")) + else if (IsElement("polygons")) primType = Prim_Polygon; - else if( IsElement( "polylist")) + else if (IsElement("polylist")) primType = Prim_Polylist; - else if( IsElement( "triangles")) + else if (IsElement("triangles")) primType = Prim_Triangles; - else if( IsElement( "trifans")) + else if (IsElement("trifans")) primType = Prim_TriFans; - else if( IsElement( "tristrips")) + else if (IsElement("tristrips")) primType = Prim_TriStrips; - ai_assert( primType != Prim_Invalid); + ai_assert(primType != Prim_Invalid); // also a number of elements, but in addition a

primitive collection and probably index counts for all primitives - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "input")) + if (IsElement("input")) { - ReadInputChannel( perIndexData); + ReadInputChannel(perIndexData); } - else if( IsElement( "vcount")) + else if (IsElement("vcount")) { - if( !mReader->isEmptyElement()) + 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++) + vcount.reserve(numPrimitives); + for (unsigned int a = 0; a < numPrimitives; a++) { - if( *content == 0) - ThrowException( "Expected more values while reading contents."); + if (*content == 0) + ThrowException("Expected more values while reading contents."); // read a number - vcount.push_back( (size_t) strtoul10( content, &content)); + vcount.push_back((size_t)strtoul10(content, &content)); // skip whitespace after it - SkipSpacesAndLineEnd( &content); + SkipSpacesAndLineEnd(&content); } } - TestClosing( "vcount"); + TestClosing("vcount"); } } - else if( IsElement( "p")) + else if (IsElement("p")) { - if( !mReader->isEmptyElement()) + 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); @@ -2304,23 +2398,25 @@ void ColladaParser::ReadIndexData( Mesh* pMesh) else if (IsElement("extra")) { SkipElement("extra"); - } else if ( IsElement("ph")) { + } + else if (IsElement("ph")) { SkipElement("ph"); - } else { - ThrowException( format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <" << elementName << ">" ); + } + else { + ThrowException(format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <" << elementName << ">"); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( mReader->getNodeName() != elementName) - ThrowException( format() << "Expected end of <" << elementName << "> element." ); + if (mReader->getNodeName() != elementName) + ThrowException(format() << "Expected end of <" << elementName << "> element."); break; } } #ifdef ASSIMP_BUILD_DEBUG - if (primType != Prim_TriFans && primType != Prim_TriStrips && primType != Prim_LineStrip && + if (primType != Prim_TriFans && primType != Prim_TriStrips && primType != Prim_LineStrip && 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); } @@ -2333,42 +2429,42 @@ void ColladaParser::ReadIndexData( Mesh* pMesh) // ------------------------------------------------------------------------------------------------ // Reads a single input channel element and stores it in the given array, if valid -void ColladaParser::ReadInputChannel( std::vector& poChannels) +void ColladaParser::ReadInputChannel(std::vector& poChannels) { InputChannel channel; // read semantic - int attrSemantic = GetAttribute( "semantic"); - std::string semantic = mReader->getAttributeValue( attrSemantic); - channel.mType = GetTypeForSemantic( 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( format() << "Unknown reference format in url \"" << source << "\" in source attribute of element." ); - channel.mAccessor = source+1; // skipping the leading #, hopefully the remaining text is the accessor ID only + int attrSource = GetAttribute("source"); + const char* source = mReader->getAttributeValue(attrSource); + if (source[0] != '#') + ThrowException(format() << "Unknown reference format in url \"" << source << "\" in source attribute of element."); + 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); + 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){ + if (channel.mType == IT_Texcoord || channel.mType == IT_Color) { int attrSet = TestAttribute("set"); - if(attrSet > -1){ - attrSet = mReader->getAttributeValueAsInt( attrSet); - if(attrSet < 0) - ThrowException( format() << "Invalid index \"" << (attrSet) << "\" in set attribute of element" ); + if (attrSet > -1) { + attrSet = mReader->getAttributeValueAsInt(attrSet); + if (attrSet < 0) + ThrowException(format() << "Invalid index \"" << (attrSet) << "\" in set attribute of element"); channel.mIndex = attrSet; } } // store, if valid type - if( channel.mType != IT_Invalid) - poChannels.push_back( channel); + if (channel.mType != IT_Invalid) + poChannels.push_back(channel); // skip remaining stuff of this element, if any SkipElement(); @@ -2376,116 +2472,118 @@ void ColladaParser::ReadInputChannel( std::vector& poChannels) // ------------------------------------------------------------------------------------------------ // 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 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 - for( const InputChannel& channel : pPerIndexChannels) + for (const InputChannel& channel : pPerIndexChannels) { - numOffsets = std::max( numOffsets, channel.mOffset+1); - if( channel.mType == IT_Vertex) + 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) + switch (pPrimType) { - case Prim_Polylist: - { - for( 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; + case Prim_Polylist: + { + for (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 (expectedPointCount > 0) + indices.reserve(expectedPointCount * numOffsets); if (pNumPrimitives > 0) // It is possible to not contain any indices { const char* content = GetTextContent(); - while( *content != 0) + 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)); + int value = std::max(0, strtol10(content, &content)); + indices.push_back(size_t(value)); // skip whitespace after it - SkipSpacesAndLineEnd( &content); + SkipSpacesAndLineEnd(&content); } } - // complain if the index count doesn't fit - if( expectedPointCount > 0 && indices.size() != expectedPointCount * numOffsets) { + // 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, %zu instead of %zu.", indices.size(), expectedPointCount * numOffsets); + ReportWarning("Expected different index count in

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

element."); + } + else + ThrowException("Expected different index count in

element."); - } else if( expectedPointCount == 0 && (indices.size() % numOffsets) != 0) - 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) + // 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) + if (input.mResolved) continue; // find accessor - input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor); + input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor); // resolve accessor's data pointer as well, if necessary const Accessor* acc = input.mResolved; - if( !acc->mData) - acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource); + 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) + for (std::vector::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) { InputChannel& input = *it; - if( input.mResolved) + if (input.mResolved) continue; // ignore vertex pointer, it doesn't refer to an accessor - if( input.mType == IT_Vertex) + 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."); + if (input.mAccessor != pMesh->mVertexID) + ThrowException("Unsupported vertex referencing scheme."); continue; } // find accessor - input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor); + input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor); // resolve accessor's data pointer as well, if necessary const Accessor* acc = input.mResolved; - if( !acc->mData) - acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource); + 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) + 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){ + if (pPrimType == Prim_TriStrips) { size_t numberOfVertices = indices.size() / numOffsets; numPrimitives = numberOfVertices - 2; } @@ -2494,66 +2592,66 @@ size_t ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector& pP numPrimitives = numberOfVertices - 1; } - pMesh->mFaceSize.reserve( numPrimitives); - pMesh->mFacePosIndices.reserve( indices.size() / numOffsets); + 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) + 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_LineStrip: - numPoints = 2; - for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++) - CopyVertex(currentVertex, numOffsets, 1, 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; + 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_LineStrip: + numPoints = 2; + for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++) + CopyVertex(currentVertex, numOffsets, 1, 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); + pMesh->mFaceSize.push_back(numPoints); } // if I ever get my hands on that guy who invented this steaming pile of indirection... - TestClosing( "p"); + TestClosing("p"); return numPrimitives; } ///@note This function willn't work correctly if both PerIndex and PerVertex channels have same channels. ///For example if TEXCOORD present in both and tags this function will create wrong uv coordinates. ///It's not clear from COLLADA documentation is this allowed or not. For now only exporter fixed to avoid such behavior -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){ +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; @@ -2571,8 +2669,8 @@ void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t n 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){ +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); @@ -2587,108 +2685,110 @@ void ColladaParser::ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, // ------------------------------------------------------------------------------------------------ // 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) +void ColladaParser::ExtractDataObjectFromChannel(const InputChannel& pInput, size_t pLocalIndex, Mesh* pMesh) { // ignore vertex referrer - we handle them that separate - if( pInput.mType == IT_Vertex) + if (pInput.mType == IT_Vertex) return; const Accessor& acc = *pInput.mResolved; - if( pLocalIndex >= acc.mCount) - ThrowException( format() << "Invalid data index (" << pLocalIndex << "/" << acc.mCount << ") in primitive specification" ); + if (pLocalIndex >= acc.mCount) + ThrowException(format() << "Invalid data index (" << pLocalIndex << "/" << acc.mCount << ") in primitive specification"); // get a pointer to the start of the data object referred to by the accessor and the local index - const ai_real* dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex* acc.mStride; + const ai_real* 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 ai_real obj[4]; - for( size_t c = 0; c < 4; ++c) + 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) + 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 - ASSIMP_LOG_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)); + 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 + ASSIMP_LOG_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 - ASSIMP_LOG_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 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 + ASSIMP_LOG_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 - ASSIMP_LOG_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 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 + ASSIMP_LOG_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 - ASSIMP_LOG_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) + // 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 + ASSIMP_LOG_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 + { + ASSIMP_LOG_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) { - // 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 - { - ASSIMP_LOG_ERROR("Collada: too many texture coordinate sets. Skipping."); + result[static_cast(i)] = obj[pInput.mResolved->mSubOffset[i]]; } - 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)); + pMesh->mColors[pInput.mIndex].push_back(result); + } + else + { + ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping."); + } - aiColor4D result(0, 0, 0, 1); - for (size_t i = 0; i < pInput.mResolved->mSize; ++i) - { - result[static_cast(i)] = obj[pInput.mResolved->mSubOffset[i]]; - } - pMesh->mColors[pInput.mIndex].push_back(result); - } else - { - ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping."); - } - - break; - default: - // IT_Invalid and IT_Vertex - ai_assert(false && "shouldn't ever get here"); + break; + default: + // IT_Invalid and IT_Vertex + ai_assert(false && "shouldn't ever get here"); } } @@ -2696,25 +2796,25 @@ void ColladaParser::ExtractDataObjectFromChannel( const InputChannel& pInput, si // Reads the library of node hierarchies and scene parts void ColladaParser::ReadSceneLibrary() { - if( mReader->isEmptyElement()) + if (mReader->isEmptyElement()) return; - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + 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")) + 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); + int indexID = GetAttribute("id"); + const char* attrID = mReader->getAttributeValue(indexID); // read name if given. - int indexName = TestAttribute( "name"); + int indexName = TestAttribute("name"); const char* attrName = "unnamed"; - if( indexName > -1) - attrName = mReader->getAttributeValue( indexName); + if (indexName > -1) + attrName = mReader->getAttributeValue(indexName); // create a node and store it in the library under its ID Node* node = new Node; @@ -2722,55 +2822,56 @@ void ColladaParser::ReadSceneLibrary() node->mName = attrName; mNodeLibrary[node->mID] = node; - ReadSceneNode( node); - } else + ReadSceneNode(node); + } + else { // ignore the rest SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "library_visual_scenes") == 0) + if (strcmp(mReader->getNodeName(), "library_visual_scenes") == 0) //ThrowException( "Expected end of \"library_visual_scenes\" element."); - break; + break; } } } // ------------------------------------------------------------------------------------------------ // Reads a scene node's contents including children and stores it in the given node -void ColladaParser::ReadSceneNode( Node* pNode) +void ColladaParser::ReadSceneNode(Node* pNode) { // quit immediately on elements - if( mReader->isEmptyElement()) + if (mReader->isEmptyElement()) return; - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "node")) + 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 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); + 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); + pNode->mChildren.push_back(child); child->mParent = pNode; } else @@ -2781,26 +2882,26 @@ void ColladaParser::ReadSceneNode( Node* pNode) } // read on recursively from there - ReadSceneNode( child); + 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()) + 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 @@ -2813,14 +2914,14 @@ void ColladaParser::ReadSceneNode( Node* pNode) if (s[0] != '#') ASSIMP_LOG_ERROR("Collada: Unresolved reference format of camera"); else - pNode->mPrimaryCamera = s+1; + pNode->mPrimaryCamera = s + 1; } } - else if( IsElement( "instance_node")) + else if (IsElement("instance_node")) { // find the node in the library - int attrID = TestAttribute( "url"); - if( attrID != -1) + int attrID = TestAttribute("url"); + if (attrID != -1) { const char* s = mReader->getAttributeValue(attrID); if (s[0] != '#') @@ -2828,16 +2929,16 @@ void ColladaParser::ReadSceneNode( Node* pNode) else { pNode->mNodeInstances.push_back(NodeInstance()); - pNode->mNodeInstances.back().mNode = s+1; + pNode->mNodeInstances.back().mNode = s + 1; } } } - else if( IsElement( "instance_geometry") || IsElement( "instance_controller")) + else if (IsElement("instance_geometry") || IsElement("instance_controller")) { // Reference to a mesh or controller, with possible material associations - ReadNodeGeometry( pNode); + ReadNodeGeometry(pNode); } - else if( IsElement( "instance_light")) + else if (IsElement("instance_light")) { // Reference to a light, name given in 'url' attribute int attrID = TestAttribute("url"); @@ -2845,15 +2946,15 @@ void ColladaParser::ReadSceneNode( Node* pNode) ASSIMP_LOG_WARN("Collada: Expected url attribute in element"); else { - const char* url = mReader->getAttributeValue( attrID); - if( url[0] != '#') - ThrowException( "Unknown reference format in element"); + 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; + pNode->mLights.back().mLight = url + 1; } } - else if( IsElement( "instance_camera")) + else if (IsElement("instance_camera")) { // Reference to a camera, name given in 'url' attribute int attrID = TestAttribute("url"); @@ -2861,12 +2962,12 @@ void ColladaParser::ReadSceneNode( Node* pNode) ASSIMP_LOG_WARN("Collada: Expected url attribute in element"); else { - const char* url = mReader->getAttributeValue( attrID); - if( url[0] != '#') - ThrowException( "Unknown reference format in element"); + 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; + pNode->mCameras.back().mCamera = url + 1; } } else @@ -2875,7 +2976,7 @@ void ColladaParser::ReadSceneNode( Node* pNode) SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { break; } } @@ -2883,9 +2984,9 @@ void ColladaParser::ReadSceneNode( Node* pNode) // ------------------------------------------------------------------------------------------------ // 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) +void ColladaParser::ReadNodeTransformation(Node* pNode, TransformType pType) { - if( mReader->isEmptyElement()) + if (mReader->isEmptyElement()) return; std::string tagName = mReader->getNodeName(); @@ -2894,38 +2995,38 @@ void ColladaParser::ReadNodeTransformation( Node* pNode, TransformType pType) tf.mType = pType; // read SID - int indexSID = TestAttribute( "sid"); - if( indexSID >= 0) - tf.mID = mReader->getAttributeValue( indexSID); + 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++) + for (unsigned int a = 0; a < sNumParameters[pType]; a++) { // read a number - content = fast_atoreal_move( content, tf.f[a]); + content = fast_atoreal_move(content, tf.f[a]); // skip whitespace after it - SkipSpacesAndLineEnd( &content); + SkipSpacesAndLineEnd(&content); } // place the transformation at the queue of the node - pNode->mTransforms.push_back( tf); + pNode->mTransforms.push_back(tf); // and consume the closing tag - TestClosing( tagName.c_str()); + TestClosing(tagName.c_str()); } // ------------------------------------------------------------------------------------------------ // Processes bind_vertex_input and bind elements -void ColladaParser::ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl) +void ColladaParser::ReadMaterialVertexInputBinding(Collada::SemanticMappingTable& tbl) { - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "bind_vertex_input")) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if (IsElement("bind_vertex_input")) { Collada::InputSemanticMapEntry vn; @@ -2935,7 +3036,7 @@ void ColladaParser::ReadMaterialVertexInputBinding( Collada::SemanticMappingTabl // input semantic n = GetAttribute("input_semantic"); - vn.mType = GetTypeForSemantic( mReader->getAttributeValue(n) ); + vn.mType = GetTypeForSemantic(mReader->getAttributeValue(n)); // index of input set n = TestAttribute("input_set"); @@ -2944,12 +3045,12 @@ void ColladaParser::ReadMaterialVertexInputBinding( Collada::SemanticMappingTabl tbl.mMap[s] = vn; } - else if( IsElement( "bind")) { + else if (IsElement("bind")) { ASSIMP_LOG_WARN("Collada: Found unsupported element"); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "instance_material") == 0) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if (strcmp(mReader->getNodeName(), "instance_material") == 0) break; } } @@ -2957,90 +3058,91 @@ void ColladaParser::ReadMaterialVertexInputBinding( Collada::SemanticMappingTabl // ------------------------------------------------------------------------------------------------ // Reads a mesh reference in a node and adds it to the node's mesh list -void ColladaParser::ReadNodeGeometry( Node* pNode) +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"); + 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 # + instance.mMeshOrController = url + 1; // skipping the leading # - if( !mReader->isEmptyElement()) + if (!mReader->isEmptyElement()) { // read material associations. Ignore additional elements in between - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "instance_material")) + 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); + 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] == '#') + if (urlMat[0] == '#') urlMat++; s.mMatName = urlMat; // resolve further material details + THIS UGLY AND NASTY semantic mapping stuff - if( !mReader->isEmptyElement()) + if (!mReader->isEmptyElement()) ReadMaterialVertexInputBinding(s); // store the association instance.mMaterials[group] = s; } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "instance_geometry") == 0 - || strcmp( mReader->getNodeName(), "instance_controller") == 0) + if (strcmp(mReader->getNodeName(), "instance_geometry") == 0 + || strcmp(mReader->getNodeName(), "instance_controller") == 0) break; } } } // store it - pNode->mMeshes.push_back( instance); + pNode->mMeshes.push_back(instance); } // ------------------------------------------------------------------------------------------------ // Reads the collada scene void ColladaParser::ReadScene() { - if( mReader->isEmptyElement()) + if (mReader->isEmptyElement()) return; - while( mReader->read()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { - if( IsElement( "instance_visual_scene")) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if (IsElement("instance_visual_scene")) { // should be the first and only occurrence - if( mRootNode) - ThrowException( "Invalid scene containing multiple root nodes in element"); + 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"); + 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."); + 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 { + } + else { SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){ + else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { break; } } @@ -3048,23 +3150,23 @@ void ColladaParser::ReadScene() // ------------------------------------------------------------------------------------------------ // Aborts the file reading with an exception -AI_WONT_RETURN void ColladaParser::ThrowException( const std::string& pError) const +AI_WONT_RETURN void ColladaParser::ThrowException(const std::string& pError) const { - throw DeadlyImportError( format() << "Collada: " << mFileName << " - " << pError ); + throw DeadlyImportError(format() << "Collada: " << mFileName << " - " << pError); } -void ColladaParser::ReportWarning(const char* msg,...) +void ColladaParser::ReportWarning(const char* msg, ...) { ai_assert(NULL != msg); va_list args; - va_start(args,msg); + va_start(args, msg); char szBuffer[3000]; - const int iLen = vsprintf(szBuffer,msg,args); + const int iLen = vsprintf(szBuffer, msg, args); ai_assert(iLen > 0); va_end(args); - ASSIMP_LOG_WARN_F("Validation warning: ", std::string(szBuffer,iLen)); + ASSIMP_LOG_WARN_F("Validation warning: ", std::string(szBuffer, iLen)); } // ------------------------------------------------------------------------------------------------ @@ -3072,84 +3174,84 @@ void ColladaParser::ReportWarning(const char* msg,...) void ColladaParser::SkipElement() { // nothing to skip if it's an - if( mReader->isEmptyElement()) + if (mReader->isEmptyElement()) return; // reroute - SkipElement( mReader->getNodeName()); + SkipElement(mReader->getNodeName()); } // ------------------------------------------------------------------------------------------------ // Skips all data until the end node of the given element -void ColladaParser::SkipElement( const char* pElement) +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()) + while (mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - if( mReader->getNodeName() == element) + 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) +void ColladaParser::TestOpening(const char* pName) { // read element start - if( !mReader->read()) - ThrowException( format() << "Unexpected end of file while beginning of <" << pName << "> element." ); + if (!mReader->read()) + ThrowException(format() << "Unexpected end of file while beginning of <" << pName << "> element."); // whitespace in front is ok, just read again if found - if( mReader->getNodeType() == irr::io::EXN_TEXT) - if( !mReader->read()) - ThrowException( format() << "Unexpected end of file while reading beginning of <" << pName << "> element." ); + if (mReader->getNodeType() == irr::io::EXN_TEXT) + if (!mReader->read()) + ThrowException(format() << "Unexpected end of file while reading beginning of <" << pName << "> element."); - if( mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp( mReader->getNodeName(), pName) != 0) - ThrowException( format() << "Expected start of <" << pName << "> element." ); + if (mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp(mReader->getNodeName(), pName) != 0) + ThrowException(format() << "Expected start of <" << pName << "> element."); } // ------------------------------------------------------------------------------------------------ // Tests for the closing tag of the given element, throws an exception if not found -void ColladaParser::TestClosing( const char* pName) +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) + if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END && strcmp(mReader->getNodeName(), pName) == 0) return; // if not, read some more - if( !mReader->read()) - ThrowException( format() << "Unexpected end of file while reading end of <" << pName << "> element." ); + if (!mReader->read()) + ThrowException(format() << "Unexpected end of file while reading end of <" << pName << "> element."); // whitespace in front is ok, just read again if found - if( mReader->getNodeType() == irr::io::EXN_TEXT) - if( !mReader->read()) - ThrowException( format() << "Unexpected end of file while reading end of <" << pName << "> element." ); + if (mReader->getNodeType() == irr::io::EXN_TEXT) + if (!mReader->read()) + ThrowException(format() << "Unexpected end of file while reading end of <" << pName << "> element."); // 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( format() << "Expected end of <" << pName << "> element." ); + if (mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp(mReader->getNodeName(), pName) != 0) + ThrowException(format() << "Expected end of <" << pName << "> element."); } // ------------------------------------------------------------------------------------------------ // 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 ColladaParser::GetAttribute(const char* pAttr) const { - int index = TestAttribute( pAttr); - if( index != -1) + int index = TestAttribute(pAttr); + if (index != -1) return index; // attribute not found -> throw an exception - ThrowException( format() << "Expected attribute \"" << pAttr << "\" for element <" << mReader->getNodeName() << ">." ); + ThrowException(format() << "Expected attribute \"" << pAttr << "\" for element <" << 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 +int ColladaParser::TestAttribute(const char* pAttr) const { - for( int a = 0; a < mReader->getAttributeCount(); a++) - if( strcmp( mReader->getAttributeName( a), pAttr) == 0) + for (int a = 0; a < mReader->getAttributeCount(); a++) + if (strcmp(mReader->getAttributeName(a), pAttr) == 0) return a; return -1; @@ -3160,8 +3262,8 @@ int ColladaParser::TestAttribute( const char* pAttr) const const char* ColladaParser::GetTextContent() { const char* sz = TestTextContent(); - if(!sz) { - ThrowException( "Invalid contents in element \"n\"."); + if (!sz) { + ThrowException("Invalid contents in element \"n\"."); } return sz; } @@ -3171,85 +3273,85 @@ const char* ColladaParser::GetTextContent() const char* ColladaParser::TestTextContent() { // present node should be the beginning of an element - if( mReader->getNodeType() != irr::io::EXN_ELEMENT || mReader->isEmptyElement()) + if (mReader->getNodeType() != irr::io::EXN_ELEMENT || mReader->isEmptyElement()) return NULL; // read contents of the element - if( !mReader->read() ) + if (!mReader->read()) return NULL; - if( mReader->getNodeType() != irr::io::EXN_TEXT && mReader->getNodeType() != irr::io::EXN_CDATA) + if (mReader->getNodeType() != irr::io::EXN_TEXT && mReader->getNodeType() != irr::io::EXN_CDATA) return NULL; // skip leading whitespace const char* text = mReader->getNodeData(); - SkipSpacesAndLineEnd( &text); + SkipSpacesAndLineEnd(&text); return text; } // ------------------------------------------------------------------------------------------------ // Calculates the resulting transformation fromm all the given transform steps -aiMatrix4x4 ColladaParser::CalculateResultTransform( const std::vector& pTransforms) const +aiMatrix4x4 ColladaParser::CalculateResultTransform(const std::vector& pTransforms) const { aiMatrix4x4 res; - for( std::vector::const_iterator it = pTransforms.begin(); it != pTransforms.end(); ++it) + for (std::vector::const_iterator it = pTransforms.begin(); it != pTransforms.end(); ++it) { const Transform& tf = *it; - switch( tf.mType) + 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(); + 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; - ai_real angle = tf.f[3] * ai_real( AI_MATH_PI) / ai_real( 180.0 ); - 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; + 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; + ai_real angle = tf.f[3] * ai_real(AI_MATH_PI) / ai_real(180.0); + 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; } } @@ -3258,29 +3360,29 @@ aiMatrix4x4 ColladaParser::CalculateResultTransform( const std::vectorMsIK9a%(Cq>7|$t1&-*@_8Z; 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

Vi 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 =Geb5c%^8 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

>38$c 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

cXK<%%4toD?kC_HmG#_L9 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 PnSpQ}kp2slXJ7yTqc8-m literal 0 HcmV?d00001 diff --git a/test/models/Collada/duck_nomanifest.zae b/test/models/Collada/duck_nomanifest.zae new file mode 100644 index 0000000000000000000000000000000000000000..d5559ccb2f93d22385783c35e9d47ca8dbf11e1a GIT binary patch literal 132634 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

Vi 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 =Geb5c%^8 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

>38$c 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

cXK<%%4toD?kC_HmG#_L9 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 literal 0 HcmV?d00001