diff --git a/Engine/source/ts/assimp/assimpAppNode.cpp b/Engine/source/ts/assimp/assimpAppNode.cpp index e09a8b374..686c5bfa2 100644 --- a/Engine/source/ts/assimp/assimpAppNode.cpp +++ b/Engine/source/ts/assimp/assimpAppNode.cpp @@ -31,6 +31,8 @@ #include #include +aiAnimation* AssimpAppNode::sActiveSequence = NULL; + AssimpAppNode::AssimpAppNode(const struct aiScene* scene, const struct aiNode* node, AssimpAppNode* parent) : mInvertMeshes(false), mLastTransformTime(TSShapeLoader::DefaultTime - 1), @@ -94,12 +96,131 @@ MatrixF AssimpAppNode::getTransform(F32 time) //mLastTransform.scale(ColladaUtils::getOptions().unit); } - mLastTransform.mul(mNodeTransform); + // If this node is animated in the active sequence, fetch the animated transform + if (sActiveSequence) + { + MatrixF mat(true); + getAnimatedTransform(mat, time, sActiveSequence); + mLastTransform.mul(mat); + } + else + mLastTransform.mul(mNodeTransform); + mLastTransformTime = time; - return mLastTransform; } +void AssimpAppNode::getAnimatedTransform(MatrixF& mat, F32 t, aiAnimation* animSeq) +{ + // Find the channel for this node + for (U32 i = 0; i < animSeq->mNumChannels; ++i) + { + if (strcmp(mName, animSeq->mChannels[i]->mNodeName.C_Str()) == 0) + { + aiNodeAnim *nodeAnim = animSeq->mChannels[i]; + Point3F trans(Point3F::Zero); + Point3F scale(Point3F::One); + QuatF rot; + rot.identity(); + + // Transform + if (nodeAnim->mNumPositionKeys == 1) + trans.set(nodeAnim->mPositionKeys[0].mValue.x, nodeAnim->mPositionKeys[0].mValue.y, nodeAnim->mPositionKeys[0].mValue.z); + else + { + Point3F curPos, lastPos; + F32 lastT = 0.0; + for (U32 key = 0; key < nodeAnim->mNumPositionKeys; ++key) + { + F32 curT = (F32)nodeAnim->mPositionKeys[key].mTime; + curPos.set(nodeAnim->mPositionKeys[key].mValue.x, nodeAnim->mPositionKeys[key].mValue.y, nodeAnim->mPositionKeys[key].mValue.z); + if (curT > t) + { + F32 factor = (t - lastT) / (curT - lastT); + trans.interpolate(lastPos, curPos, factor); + break; + } + else if ((curT == t) || (key == nodeAnim->mNumPositionKeys - 1)) + { + trans = curPos; + break; + } + + lastT = curT; + lastPos = curPos; + } + } + + // Rotation + if (nodeAnim->mNumRotationKeys == 1) + rot.set(nodeAnim->mRotationKeys[0].mValue.x, nodeAnim->mRotationKeys[0].mValue.y, + nodeAnim->mRotationKeys[0].mValue.z, nodeAnim->mRotationKeys[0].mValue.w); + else + { + QuatF curRot, lastRot; + F32 lastT = 0.0; + for (U32 key = 0; key < nodeAnim->mNumRotationKeys; ++key) + { + F32 curT = (F32)nodeAnim->mRotationKeys[key].mTime; + curRot.set(nodeAnim->mRotationKeys[key].mValue.x, nodeAnim->mRotationKeys[key].mValue.y, + nodeAnim->mRotationKeys[key].mValue.z, nodeAnim->mRotationKeys[key].mValue.w); + if (curT > t) + { + F32 factor = (t - lastT) / (curT - lastT); + rot.interpolate(lastRot, curRot, factor); + break; + } + else if ((curT == t) || (key == nodeAnim->mNumRotationKeys - 1)) + { + rot = curRot; + break; + } + + lastT = curT; + lastRot = curRot; + } + } + + // Scale + if (nodeAnim->mNumScalingKeys == 1) + scale.set(nodeAnim->mScalingKeys[0].mValue.x, nodeAnim->mScalingKeys[0].mValue.y, nodeAnim->mScalingKeys[0].mValue.z); + else + { + Point3F curScale, lastScale; + F32 lastT = 0.0; + for (U32 key = 0; key < nodeAnim->mNumScalingKeys; ++key) + { + F32 curT = (F32)nodeAnim->mScalingKeys[key].mTime; + curScale.set(nodeAnim->mScalingKeys[key].mValue.x, nodeAnim->mScalingKeys[key].mValue.y, nodeAnim->mScalingKeys[key].mValue.z); + if (curT > t) + { + F32 factor = (t - lastT) / (curT - lastT); + scale.interpolate(lastScale, curScale, factor); + break; + } + else if ((curT == t) || (key == nodeAnim->mNumScalingKeys - 1)) + { + scale = curScale; + break; + } + + lastT = curT; + lastScale = curScale; + } + } + + rot.setMatrix(&mat); + mat.inverse(); + mat.setPosition(trans); + mat.scale(scale); + return; + } + } + + // Node not found in the animation channels + mat = mNodeTransform; +} + bool AssimpAppNode::animatesTransform(const AppSequence* appSeq) { return false; diff --git a/Engine/source/ts/assimp/assimpAppNode.h b/Engine/source/ts/assimp/assimpAppNode.h index c54a65625..3fe04d43e 100644 --- a/Engine/source/ts/assimp/assimpAppNode.h +++ b/Engine/source/ts/assimp/assimpAppNode.h @@ -36,6 +36,7 @@ #ifndef AI_TYPES_H_INC #include #endif +#include class AssimpAppNode : public AppNode { @@ -43,6 +44,7 @@ class AssimpAppNode : public AppNode friend class AssimpAppMesh; MatrixF getTransform(F32 time); + void getAnimatedTransform(MatrixF& mat, F32 t, aiAnimation* animSeq); void buildMeshList(); void buildChildList(); @@ -67,6 +69,8 @@ public: // } + static aiAnimation* sActiveSequence; + //----------------------------------------------------------------------- const char *getName() { return mName; } const char *getParentName() { return mParentName; } diff --git a/Engine/source/ts/assimp/assimpAppSequence.cpp b/Engine/source/ts/assimp/assimpAppSequence.cpp index 1384e1fcd..a5a92460e 100644 --- a/Engine/source/ts/assimp/assimpAppSequence.cpp +++ b/Engine/source/ts/assimp/assimpAppSequence.cpp @@ -10,29 +10,72 @@ #include "console/persistenceManager.h" #include "ts/assimp/assimpAppMaterial.h" #include "ts/assimp/assimpAppSequence.h" +#include "ts/assimp/assimpAppNode.h" AssimpAppSequence::AssimpAppSequence(aiAnimation *a) : + seqStart(0.0f), mAnim(a) { - fps = mAnim->mTicksPerSecond; + // From: http://sir-kimmi.de/assimp/lib_html/data.html#anims + // An aiAnimation has a duration. The duration as well as all time stamps are given in ticks. + // To get the correct timing, all time stamp thus have to be divided by aiAnimation::mTicksPerSecond. + // Beware, though, that certain combinations of file format and exporter don't always store this + // information in the exported file. In this case, mTicksPerSecond is set to 0 to indicate the lack of knowledge. + fps = (mAnim->mTicksPerSecond > 0) ? mAnim->mTicksPerSecond : 30.0f; + + F32 maxEndTime = 0; + F32 minFrameTime = 1000.0f; + // Detect the frame rate (minimum time between keyframes) and max sequence time + for (U32 i = 0; i < mAnim->mNumChannels; ++i) + { + aiNodeAnim *nodeAnim = mAnim->mChannels[i]; + if (nodeAnim->mNumPositionKeys) + maxEndTime = getMax(maxEndTime, (F32) nodeAnim->mPositionKeys[nodeAnim->mNumPositionKeys-1].mTime); + if (nodeAnim->mNumRotationKeys) + maxEndTime = getMax(maxEndTime, (F32) nodeAnim->mRotationKeys[nodeAnim->mNumRotationKeys-1].mTime); + if (nodeAnim->mNumScalingKeys) + maxEndTime = getMax(maxEndTime, (F32) nodeAnim->mScalingKeys[nodeAnim->mNumScalingKeys-1].mTime); + + for (U32 key = 1; key < nodeAnim->mNumPositionKeys; ++key) + { + F32 deltaT = nodeAnim->mPositionKeys[key].mTime - nodeAnim->mPositionKeys[key-1].mTime; + minFrameTime = getMin(minFrameTime, deltaT); + } + for (U32 key = 1; key < nodeAnim->mNumRotationKeys; ++key) + { + F32 deltaT = nodeAnim->mRotationKeys[key].mTime - nodeAnim->mRotationKeys[key-1].mTime; + minFrameTime = getMin(minFrameTime, deltaT); + } + for (U32 key = 1; key < nodeAnim->mNumScalingKeys; ++key) + { + F32 deltaT = nodeAnim->mScalingKeys[key].mTime - nodeAnim->mScalingKeys[key-1].mTime; + minFrameTime = getMin(minFrameTime, deltaT); + } + } + + fps = (minFrameTime > 0.0f) ? 1.0f / minFrameTime : fps; + fps = mClamp(fps, TSShapeLoader::MinFrameRate, TSShapeLoader::MaxFrameRate); + seqEnd = maxEndTime; } AssimpAppSequence::~AssimpAppSequence() { } -F32 AssimpAppSequence::getStart() const -{ - return 0.0f; -} -F32 AssimpAppSequence::getEnd() const -{ - return (F32)mAnim->mDuration / fps; +void AssimpAppSequence::setActive(bool active) +{ + if (active) + AssimpAppNode::sActiveSequence = mAnim; + else + { + if (AssimpAppNode::sActiveSequence == mAnim) + AssimpAppNode::sActiveSequence = NULL; + } } U32 AssimpAppSequence::getFlags() const { - return TSShape::Blend; + return TSShape::Blend; } F32 AssimpAppSequence::getPriority() const { @@ -41,5 +84,4 @@ F32 AssimpAppSequence::getPriority() const F32 AssimpAppSequence::getBlendRefTime() const { return -1.0f; -} - +} \ No newline at end of file diff --git a/Engine/source/ts/assimp/assimpAppSequence.h b/Engine/source/ts/assimp/assimpAppSequence.h index 81f682ced..28072af7e 100644 --- a/Engine/source/ts/assimp/assimpAppSequence.h +++ b/Engine/source/ts/assimp/assimpAppSequence.h @@ -22,6 +22,9 @@ class AssimpAppSequence : public AppSequence { + F32 seqStart; + F32 seqEnd; + public: AssimpAppSequence(aiAnimation *a); @@ -29,18 +32,18 @@ public: aiAnimation *mAnim; - virtual void setActive(bool active) { } + virtual void setActive(bool active); virtual S32 getNumTriggers() const { return 0; } virtual void getTrigger(S32 index, TSShape::Trigger& trigger) const { trigger.state = 0; } virtual const char* getName() const { return mAnim->mName.C_Str(); } - virtual F32 getStart() const; - virtual F32 getEnd() const; + F32 getStart() const { return seqStart; } + F32 getEnd() const { return seqEnd; } + void setEnd(F32 end) { seqEnd = end; } virtual U32 getFlags() const; virtual F32 getPriority() const; virtual F32 getBlendRefTime() const; - }; \ No newline at end of file