- Collada loader now supports node animations

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@375 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/1/head
ulfjorensen 2009-04-06 17:00:03 +00:00
parent 29c33760e4
commit e471b966c5
5 changed files with 584 additions and 17 deletions

View File

@ -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<AnimationChannel> mChannels;
/** the sub-animations, if any */
std::vector<Animation*> mSubAnims;
/** Destructor */
~Animation()
{
for( std::vector<Animation*>::iterator it = mSubAnims.begin(); it != mSubAnims.end(); ++it)
delete *it;
}
};
} // end of namespace Collada
} // end of namespace Assimp

View File

@ -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<Collada::Animation*>::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<const aiNode*> nodes;
CollectNodes( pScene->mRootNode, nodes);
std::vector<aiNodeAnim*> anims;
for( std::vector<const aiNode*>::const_iterator nit = nodes.begin(); nit != nodes.end(); ++nit)
{
// find all the collada anim channels which refer to the current node
std::vector<ChannelEntry> 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<Collada::AnimationChannel>::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<ChannelEntry>::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<Collada::Transform> 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<aiMatrix4x4> resultTrafos;
while( 1)
{
for( std::vector<ChannelEntry>::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<ChannelEntry>::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<const aiNode*>& 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

View File

@ -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<const aiNode*>& 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<aiTexture*> mTextures;
/** Accumulated animations for the target scene */
std::vector<aiAnimation*> mAnims;
};
} // end of namespace Assimp

View File

@ -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 <animation> 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

View File

@ -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;