- Collada loader now supports node animations
git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@375 67173fc5-114c-0410-ac8e-9d2fd5bffc1fpull/1/head
parent
29c33760e4
commit
e471b966c5
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
@ -164,6 +179,29 @@ protected:
|
|||
/** Converts a path read from a collada file to the usual representation */
|
||||
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 */
|
||||
std::string mFileName;
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue