From 7baca30a1d10bd0cef7b49e59be39e7df92692e2 Mon Sep 17 00:00:00 2001
From: Chris Robinson <chris.kcat@gmail.com>
Date: Sun, 7 Apr 2013 16:21:45 -0700
Subject: [PATCH] Only get the non-accum root's keyframe when updating
 positions

The actual animation pose is now handled by the controllers, based on the
current animation time.
---
 apps/openmw/mwrender/animation.cpp | 80 ++++++++++++++----------------
 apps/openmw/mwrender/animation.hpp |  9 ++--
 2 files changed, 40 insertions(+), 49 deletions(-)

diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp
index fd7aeac1d..72f7ebaf2 100644
--- a/apps/openmw/mwrender/animation.cpp
+++ b/apps/openmw/mwrender/animation.cpp
@@ -263,28 +263,6 @@ void Animation::calcAnimVelocity()
     }
 }
 
-void Animation::applyAnimation(const Ogre::Animation *anim, float time, Ogre::SkeletonInstance *skel)
-{
-    Ogre::TimeIndex timeindex = anim->_getTimeIndex(time);
-    Ogre::Animation::NodeTrackIterator tracks = anim->getNodeTrackIterator();
-    while(tracks.hasMoreElements())
-    {
-        Ogre::NodeAnimationTrack *track = tracks.getNext();
-        const Ogre::String &targetname = track->getAssociatedNode()->getName();
-        if(!skel->hasBone(targetname))
-            continue;
-        Ogre::Bone *bone = skel->getBone(targetname);
-        bone->setOrientation(Ogre::Quaternion::IDENTITY);
-        bone->setPosition(Ogre::Vector3::ZERO);
-        bone->setScale(Ogre::Vector3::UNIT_SCALE);
-        track->applyToNode(bone, timeindex);
-    }
-
-    // HACK: Dirty the animation state set so that Ogre will apply the
-    // transformations to entities this skeleton instance is shared with.
-    mObjectList.mSkelBase->getAllAnimationStates()->_notifyDirty();
-}
-
 static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone)
 {
     if(skelsrc->hasBone(bone->getName()))
@@ -323,24 +301,29 @@ void Animation::updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Og
 }
 
 
-Ogre::Vector3 Animation::updatePosition(float time)
+Ogre::Vector3 Animation::updatePosition()
 {
-    if(mLooping)
-        mCurrentTime = std::fmod(std::max(time, 0.0f), mCurrentAnim->getLength());
-    else
-        mCurrentTime = std::min(mCurrentAnim->getLength(), std::max(time, 0.0f));
-    applyAnimation(mCurrentAnim, mCurrentTime, mObjectList.mSkelBase->getSkeleton());
+    Ogre::Vector3 posdiff;
 
-    Ogre::Vector3 posdiff = Ogre::Vector3::ZERO;
-    if(mNonAccumRoot)
+    Ogre::TransformKeyFrame kf(0, mCurrentTime);
+    Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator();
+    while(trackiter.hasMoreElements())
     {
-        /* Get the non-accumulation root's difference from the last update. */
-        posdiff = (mNonAccumRoot->getPosition() - mLastPosition) * mAccumulate;
-
-        /* Translate the accumulation root back to compensate for the move. */
-        mLastPosition += posdiff;
-        mAccumRoot->setPosition(-mLastPosition);
+        const Ogre::NodeAnimationTrack *track = trackiter.getNext();
+        if(track->getAssociatedNode()->getName() == mNonAccumRoot->getName())
+        {
+            track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf);
+            break;
+        }
     }
+
+    /* Get the non-accumulation root's difference from the last update. */
+    posdiff = (kf.getTranslate() - mLastPosition) * mAccumulate;
+
+    /* Translate the accumulation root back to compensate for the move. */
+    mLastPosition += posdiff;
+    mAccumRoot->setPosition(-mLastPosition);
+
     return posdiff;
 }
 
@@ -486,11 +469,14 @@ Ogre::Vector3 Animation::runAnimation(float timepassed)
     timepassed *= mAnimSpeedMult;
     while(mCurrentAnim && mPlaying)
     {
-        float targetTime = std::min(mStopTime, mCurrentTime+timepassed);
+        float targetTime = mCurrentTime + timepassed;
         if(mNextKey == mCurrentKeys->end() || mNextKey->first > targetTime)
         {
-            movement += updatePosition(targetTime);
-            mPlaying = (mLooping || mStopTime > targetTime);
+            mCurrentTime = std::min(mStopTime, targetTime);
+            if(mNonAccumRoot)
+                movement += updatePosition();
+            mPlaying = (mLooping || mStopTime > mCurrentTime);
+            timepassed = targetTime - mCurrentTime;
             break;
         }
 
@@ -498,10 +484,11 @@ Ogre::Vector3 Animation::runAnimation(float timepassed)
         const std::string &evt = mNextKey->second;
         mNextKey++;
 
-        movement += updatePosition(time);
-        mPlaying = (mLooping || mStopTime > time);
-
-        timepassed = targetTime - time;
+        mCurrentTime = time;
+        if(mNonAccumRoot)
+            movement += updatePosition();
+        mPlaying = (mLooping || mStopTime > mCurrentTime);
+        timepassed = targetTime - mCurrentTime;
 
         if(!handleEvent(time, evt))
             break;
@@ -509,6 +496,13 @@ Ogre::Vector3 Animation::runAnimation(float timepassed)
     for(size_t i = 0;i < mCurrentControllers->size();i++)
         (*mCurrentControllers)[i].update();
 
+    if(mObjectList.mSkelBase)
+    {
+        // HACK: Dirty the animation state set so that Ogre will apply the
+        // transformations to entities this skeleton instance is shared with.
+        mObjectList.mSkelBase->getAllAnimationStates()->_notifyDirty();
+    }
+
     return movement;
 }
 
diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp
index aee139bd6..2fcc1e8f7 100644
--- a/apps/openmw/mwrender/animation.hpp
+++ b/apps/openmw/mwrender/animation.hpp
@@ -63,16 +63,13 @@ protected:
 
     void calcAnimVelocity();
 
-    /* Applies the given animation to the given skeleton instance, using the specified time. */
-    void applyAnimation(const Ogre::Animation *anim, float time, Ogre::SkeletonInstance *skel);
-
     /* Updates a skeleton instance so that all bones matching the source skeleton (based on
      * bone names) are positioned identically. */
     void updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Ogre::SkeletonInstance *skel);
 
-    /* Updates the animation to the specified time, and returns the movement
-     * vector since the last update or reset. */
-    Ogre::Vector3 updatePosition(float time);
+    /* Updates the position of the accum root node for the current time, and
+     * returns the wanted movement vector from the previous update. */
+    Ogre::Vector3 updatePosition();
 
     /* Resets the animation to the time of the specified start marker, without
      * moving anything, and set the end time to the specified stop marker. If