diff --git a/code/ColladaHelper.h b/code/ColladaHelper.h index 2e78de938..c279c68b9 100644 --- a/code/ColladaHelper.h +++ b/code/ColladaHelper.h @@ -82,6 +82,7 @@ enum InputType /** Contains all data for one of the different transformation types */ struct Transform { + std::string mID; ///< SID of the transform step, by which anim channels address their target node TransformType mType; float f[16]; ///< Interpretation of data depends on the type of the transformation }; @@ -543,6 +544,40 @@ struct Image }; +/** An animation channel. */ +struct AnimationChannel +{ + /** URL of the data to animate. Could be about anything, but we support only the + * "NodeID/TransformID.SubElement" notation + */ + std::string mTarget; + + /** Source URL of the time values. Collada calls them "input". Meh. */ + std::string mSourceTimes; + /** Source URL of the value values. Collada calls them "output". */ + std::string mSourceValues; +}; + +/** An animation. Container for 0-x animation channels or 0-x animations */ +struct Animation +{ + /** Anim name */ + std::string mName; + + /** the animation channels, if any */ + std::vector mChannels; + + /** the sub-animations, if any */ + std::vector mSubAnims; + + /** Destructor */ + ~Animation() + { + for( std::vector::iterator it = mSubAnims.begin(); it != mSubAnims.end(); ++it) + delete *it; + } +}; + } // end of namespace Collada } // end of namespace Assimp diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index 7fb45e0f7..4df44aa36 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -157,6 +157,8 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I // store all cameras StoreSceneCameras( pScene); + + StoreAnimations( pScene, parser); } // ------------------------------------------------------------------------------------------------ @@ -639,9 +641,11 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: size_t jointIndex = iit->first; size_t vertexIndex = iit->second; + float weight = ReadFloat( weightsAcc, weights, vertexIndex, 0); + aiVertexWeight w; w.mVertexId = a - pStartVertex; - w.mWeight = weights.mValues[vertexIndex]; + w.mWeight = weight; dstBones[jointIndex].push_back( w); } } @@ -664,19 +668,19 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: // create bone with its weights aiBone* bone = new aiBone; - bone->mName = jointNames.mStrings[a]; - bone->mOffsetMatrix.a1 = jointMatrices.mValues[a*16 + 0]; - bone->mOffsetMatrix.a2 = jointMatrices.mValues[a*16 + 1]; - bone->mOffsetMatrix.a3 = jointMatrices.mValues[a*16 + 2]; - bone->mOffsetMatrix.a4 = jointMatrices.mValues[a*16 + 3]; - bone->mOffsetMatrix.b1 = jointMatrices.mValues[a*16 + 4]; - bone->mOffsetMatrix.b2 = jointMatrices.mValues[a*16 + 5]; - bone->mOffsetMatrix.b3 = jointMatrices.mValues[a*16 + 6]; - bone->mOffsetMatrix.b4 = jointMatrices.mValues[a*16 + 7]; - bone->mOffsetMatrix.c1 = jointMatrices.mValues[a*16 + 8]; - bone->mOffsetMatrix.c2 = jointMatrices.mValues[a*16 + 9]; - bone->mOffsetMatrix.c3 = jointMatrices.mValues[a*16 + 10]; - bone->mOffsetMatrix.c4 = jointMatrices.mValues[a*16 + 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 = dstBones[a].size(); bone->mWeights = new aiVertexWeight[bone->mNumWeights]; std::copy( dstBones[a].begin(), dstBones[a].end(), bone->mWeights); @@ -756,6 +760,280 @@ void ColladaLoader::StoreSceneMaterials( aiScene* pScene) } } +// ------------------------------------------------------------------------------------------------ +// Stores all animations +void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser) +{ + // recursivly collect all animations from the collada scene + StoreAnimations( pScene, pParser, &pParser.mAnims, ""); + + // now store all anims in the scene + if( !mAnims.empty()) + { + pScene->mNumAnimations = mAnims.size(); + pScene->mAnimations = new aiAnimation*[mAnims.size()]; + std::copy( mAnims.begin(), mAnims.end(), pScene->mAnimations); + } +} + +// ------------------------------------------------------------------------------------------------ +// Constructs the animations for the given source anim +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); + + // create animation channels, if any + if( !pSrcAnim->mChannels.empty()) + CreateAnimation( pScene, pParser, pSrcAnim, animName); +} + +/** Description of a collada animation channel which has been determined to affect the current node */ +struct ChannelEntry +{ + const Collada::AnimationChannel* mChannel; ///> the source channel + std::string mTransformId; // the ID of the transformation step of the node which is influenced + size_t mTransformIndex; // Index into the node's transform chain to apply the channel to + size_t mSubElement; // starting index inside the transform data + + // resolved data references + const Collada::Accessor* mTimeAccessor; ///> Collada accessor to the time values + const Collada::Data* mTimeData; ///> Source data array for the time values + const Collada::Accessor* mValueAccessor; ///> Collada accessor to the key value values + const Collada::Data* mValueData; ///> Source datat array for the key value values + + ChannelEntry() { mChannel = NULL; mSubElement = 0; } +}; + +// ------------------------------------------------------------------------------------------------ +// Constructs the animation for the given source anim +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); + + std::vector anims; + 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) + continue; + + // now check all channels if they affect the current node + for( std::vector::const_iterator cit = pSrcAnim->mChannels.begin(); + cit != pSrcAnim->mChannels.end(); ++cit) + { + const Collada::AnimationChannel& srcChannel = *cit; + ChannelEntry entry; + + // we except 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) + continue; + if( srcChannel.mTarget.find( '/', slashPos+1) != std::string::npos) + continue; + std::string targetName = srcChannel.mTarget.substr( 0, slashPos); + if( targetName != nodeName) + 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) + { + if( srcChannel.mTarget.find( '.', dotPos+1) != std::string::npos) + continue; + + entry.mTransformId = srcChannel.mTarget.substr( slashPos+1, dotPos - slashPos - 1); + + std::string subElement = srcChannel.mTarget.substr( dotPos+1); + if( subElement == "ANGLE") + entry.mSubElement = 3; // last number in an Axis-Angle-Transform is the angle + else + DefaultLogger::get()->warn( boost::str( boost::format( "Unknown anim subelement \"%s\". Ignoring") % subElement)); + } else + { + // no subelement following, transformId is remaining string + entry.mTransformId = srcChannel.mTarget.substr( slashPos+1); + } + + // determine which transform step is affected by this channel + entry.mTransformIndex = -1; + for( size_t a = 0; a < srcNode->mTransforms.size(); ++a) + if( srcNode->mTransforms[a].mID == entry.mTransformId) + entry.mTransformIndex = a; + + if( entry.mTransformIndex == -1) + continue; + + entry.mChannel = &(*cit); + entries.push_back( entry); + } + + // if there's no channel affecting the current node, we skip it + if( entries.empty()) + continue; + + // resolve the data pointers for all anim channels. Find the minimum time while we're at it + float startTime = 1e20, endTime = -1e20; + for( std::vector::iterator it = entries.begin(); it != entries.end(); ++it) + { + 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); + + // time count and value count must match + if( e.mTimeAccessor->mCount != e.mValueAccessor->mCount) + throw new ImportErrorException( boost::str( boost::format( "Time count / value count mismatch in animation channel \"%s\".") % e.mChannel->mTarget)); + + // 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)); + } + + // 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. + float time = startTime; + std::vector resultTrafos; + while( 1) + { + for( std::vector::iterator it = entries.begin(); it != entries.end(); ++it) + { + ChannelEntry& e = *it; + + // find the keyframe behind the current point in time + size_t pos = 0; + float postTime; + 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); + + // read values from there + float temp[16]; + for( size_t c = 0; c < e.mValueAccessor->mParams.size(); ++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) + { + float preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0); + float factor = (time - postTime) / (preTime - postTime); + + for( size_t c = 0; c < e.mValueAccessor->mParams.size(); ++c) + { + float v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c); + temp[c] += (v - temp[6]) * factor; + } + } + + // Apply values to current transformation + std::copy( temp, temp + e.mValueAccessor->mParams.size(), transforms[e.mTransformIndex].f + e.mSubElement); + } + + // Calculate resulting transformation + aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms); + + // out of lazyness: 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 + float nextTime = 1e20; + for( std::vector::iterator it = entries.begin(); it != entries.end(); ++it) + { + ChannelEntry& e = *it; + + // find the next time value larger than the current + size_t pos = 0; + while( pos < e.mTimeAccessor->mCount) + { + float t = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); + if( t > time) + { + nextTime = std::min( nextTime, t); + break; + } + ++pos; + } + } + + // 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; + } + + // there should be some keyframes + ai_assert( resultTrafos.size() > 0); + + // build an animation channel for the given node out of these trafo keys + aiNodeAnim* dstAnim = new aiNodeAnim; + dstAnim->mNodeName = nodeName; + dstAnim->mNumPositionKeys = resultTrafos.size(); + dstAnim->mNumRotationKeys= resultTrafos.size(); + dstAnim->mNumScalingKeys = 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) + { + const aiMatrix4x4& mat = resultTrafos[a]; + double time = double( mat.d4); // remember? time is stored in mat.d4 + + 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); + } + + if( !anims.empty()) + { + aiAnimation* anim = new aiAnimation; + anim->mName.Set( pName); + anim->mNumChannels = anims.size(); + anim->mChannels = new aiNodeAnim*[anims.size()]; + std::copy( anims.begin(), anims.end(), anim->mChannels); + anim->mDuration = 0.0f; + 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->mTicksPerSecond = 1; + mAnims.push_back( anim); + } +} + // ------------------------------------------------------------------------------------------------ // Add a texture to a material structure void ColladaLoader::AddTexture ( Assimp::MaterialHelper& mat, const ColladaParser& pParser, @@ -1039,4 +1317,50 @@ void ColladaLoader::ConvertPath (aiString& ss) } } +// ------------------------------------------------------------------------------------------------ +// Reads a float value from an accessor and its data array. +float 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()); + 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 +{ + size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset; + 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 +{ + poNodes.push_back( pNode); + + for( size_t a = 0; a < pNode->mNumChildren; ++a) + CollectNodes( pNode->mChildren[a], poNodes); +} + +// ------------------------------------------------------------------------------------------------ +// Finds a node in the collada scene by the given name +const Collada::Node* ColladaLoader::FindNode( const Collada::Node* pNode, const std::string& pName) +{ + if( pNode->mName == pName) + return pNode; + + for( size_t a = 0; a < pNode->mChildren.size(); ++a) + { + const Collada::Node* node = FindNode( pNode->mChildren[a], pName); + if( node) + return node; + } + + return NULL; +} + #endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER diff --git a/code/ColladaLoader.h b/code/ColladaLoader.h index 544ae7098..5cf7713c6 100644 --- a/code/ColladaLoader.h +++ b/code/ColladaLoader.h @@ -141,6 +141,21 @@ protected: /** Stores all textures in the given scene */ void StoreSceneTextures( aiScene* pScene); + /** Stores all animations + * @param pScene target scene to store the anims + */ + void StoreAnimations( aiScene* pScene, const ColladaParser& pParser); + + /** Stores all animations for the given source anim and its nested child animations + * @param pScene target scene to store the anims + * @param pSrcAnim the source animation to process + * @param pPrefix Prefix to the name in case of nested animations + */ + void StoreAnimations( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string pPrefix); + + /** Constructs the animation for the given source anim */ + void CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName); + /** Constructs materials from the collada material definitions */ void BuildMaterials( const ColladaParser& pParser, aiScene* pScene); @@ -162,7 +177,30 @@ protected: const Collada::Effect& pEffect, const std::string& pName); /** Converts a path read from a collada file to the usual representation */ - void ConvertPath (aiString& ss); + void ConvertPath( aiString& ss); + + /** Reads a float value from an accessor and its data array. + * @param pAccessor The accessor to use for reading + * @param pData The data array to read from + * @param pIndex The index of the element to retrieve + * @param pOffset Offset into the element, for multipart elements such as vectors or matrices + * @return the specified value + */ + float ReadFloat( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const; + + /** Reads a string value from an accessor and its data array. + * @param pAccessor The accessor to use for reading + * @param pData The data array to read from + * @param pIndex The index of the element to retrieve + * @return the specified value + */ + const std::string& ReadString( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex) const; + + /** Recursively collects all nodes into the given array */ + void CollectNodes( const aiNode* pNode, std::vector& poNodes) const; + + /** Finds a node in the collada scene by the given name */ + const Collada::Node* FindNode( const Collada::Node* pNode, const std::string& pName); protected: /** Filename, for a verbose error message */ @@ -188,6 +226,9 @@ protected: /** Temporary texture list */ std::vector mTextures; + + /** Accumulated animations for the target scene */ + std::vector mAnims; }; } // end of namespace Assimp diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp index bd401aaef..3a23b6692 100644 --- a/code/ColladaParser.cpp +++ b/code/ColladaParser.cpp @@ -160,6 +160,8 @@ void ColladaParser::ReadStructure() if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { if( IsElement( "asset")) ReadAssetInfo(); + else if( IsElement( "library_animations")) + ReadAnimationLibrary(); else if( IsElement( "library_controllers")) ReadControllerLibrary(); else if( IsElement( "library_images")) @@ -235,6 +237,153 @@ void ColladaParser::ReadAssetInfo() } } +// ------------------------------------------------------------------------------------------------ +// Reads the animation library +void ColladaParser::ReadAnimationLibrary() +{ + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { + if( IsElement( "animation")) + { + // delegate the reading. Depending on the inner elements it will be a container or a anim channel + ReadAnimation( &mAnims); + } else + { + // ignore the rest + SkipElement(); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { + if( strcmp( mReader->getNodeName(), "library_animations") != 0) + ThrowException( "Expected end of \"library_animations\" element."); + + break; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads an animation into the given parent structure +void ColladaParser::ReadAnimation( Collada::Animation* pParent) +{ + // an element may be a container for grouping sub-elements or an animation channel + // this is the channel we're writing to, in case it's a channel + AnimationChannel channel; + // this is the anim container in case we're a container + Animation* anim = NULL; + + // optional name given as an attribute + std::string animName; + int indexName = TestAttribute( "name"); + int indexID = TestAttribute( "id"); + if( indexName >= 0) + animName = mReader->getAttributeValue( indexName); + else if( indexID >= 0) + animName = mReader->getAttributeValue( indexID); + else + animName = "animation"; + + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { + // we have subanimations + if( IsElement( "animation")) + { + // create container from our element + if( !anim) + { + anim = new Animation; + anim->mName = animName; + pParent->mSubAnims.push_back( anim); + } + + // recurse into the subelement + ReadAnimation( anim); + } + else if( IsElement( "source")) + { + // possible animation data - we'll never know. Better store it + ReadSource(); + } + else if( IsElement( "sampler")) + { + // have it read into our channel + ReadAnimationSampler( 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"); + channel.mTarget = mReader->getAttributeValue( indexTarget); + } + else + { + // ignore the rest + SkipElement(); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { + if( strcmp( mReader->getNodeName(), "animation") != 0) + ThrowException( "Expected end of \"animation\" element."); + + break; + } + } + + // it turned out to be a channel - add it + if( !channel.mTarget.empty()) + pParent->mChannels.push_back( channel); +} + +// ------------------------------------------------------------------------------------------------ +// Reads an animation sampler into the given anim channel +void ColladaParser::ReadAnimationSampler( Collada::AnimationChannel& pChannel) +{ + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { + if( IsElement( "input")) + { + int indexSemantic = GetAttribute( "semantic"); + const char* semantic = mReader->getAttributeValue( indexSemantic); + int indexSource = GetAttribute( "source"); + const char* source = mReader->getAttributeValue( indexSource); + if( source[0] != '#') + ThrowException( "Unsupported URL format"); + source++; + + if( strcmp( semantic, "INPUT") == 0) + pChannel.mSourceTimes = source; + else if( strcmp( semantic, "OUTPUT") == 0) + pChannel.mSourceValues = source; + + if( !mReader->isEmptyElement()) + SkipElement(); + } + else + { + // ignore the rest + SkipElement(); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { + if( strcmp( mReader->getNodeName(), "sampler") != 0) + ThrowException( "Expected end of \"sampler\" element."); + + break; + } + } +} + // ------------------------------------------------------------------------------------------------ // Reads the skeleton controller library void ColladaParser::ReadControllerLibrary() @@ -2072,13 +2221,19 @@ void ColladaParser::ReadNodeTransformation( Node* pNode, TransformType pType) { std::string tagName = mReader->getNodeName(); + Transform tf; + tf.mType = pType; + + // read SID + int indexSID = TestAttribute( "sid"); + if( indexSID >= 0) + tf.mID = mReader->getAttributeValue( indexSID); + // how many parameters to read per transformation type static const unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 }; const char* content = GetTextContent(); // read as many parameters and store in the transformation - Transform tf; - tf.mType = pType; for( unsigned int a = 0; a < sNumParameters[pType]; a++) { // read a number diff --git a/code/ColladaParser.h b/code/ColladaParser.h index 6cf995894..27292ed22 100644 --- a/code/ColladaParser.h +++ b/code/ColladaParser.h @@ -77,6 +77,15 @@ protected: /** Reads asset informations such as coordinate system informations and legal blah */ void ReadAssetInfo(); + /** Reads the animation library */ + void ReadAnimationLibrary(); + + /** Reads an animation into the given parent structure */ + void ReadAnimation( Collada::Animation* pParent); + + /** Reads an animation sampler into the given anim channel */ + void ReadAnimationSampler( Collada::AnimationChannel& pChannel); + /** Reads the skeleton controller library */ void ReadControllerLibrary(); @@ -292,6 +301,9 @@ protected: the nodes in the node library. */ Collada::Node* mRootNode; + /** Root animation container */ + Collada::Animation mAnims; + /** Size unit: how large compared to a meter */ float mUnitSize;