diff --git a/components/nif/curve.hpp b/components/nif/curve.hpp deleted file mode 100644 index 9a71f0923..000000000 --- a/components/nif/curve.hpp +++ /dev/null @@ -1,424 +0,0 @@ -#ifndef _NIF_KEYLIST_H_ -#define _NIF_KEYLIST_H_ - -#include - -namespace Nif -{ - -template -void bubble_sort (iterator begin, iterator end, predicate const & in_order) -{ - if (end > begin) - { - for (iterator i = begin; i != end - 1; ++i) - { - if (in_order (*(i+0), *(i+1))) - continue; - - for (iterator j = i; j >= begin; --j) - { - std::swap (*(j+0), *(j+1)); - - if (in_order (*(j+0), *(j+1))) - break; - } - } - } -} - -template -value_type linear_interpolate (float amount, value_type prev, value_type next) -{ - return prev + (next - prev) * amount; -} - -inline -Ogre::Quaternion linear_interpolate (float amount, Ogre::Quaternion prev, Ogre::Quaternion next) -{ - return Ogre::Quaternion::nlerp (amount, prev, next); -} - -template -struct KeyT { - - static const size_t EncodedLength = - NIFStream::handler ::EncodedLength + - NIFStream::handler ::EncodedLength - ; - - float mTime; - value_type mValue; - - void extract (NIFStream &nif) - { - nif.uncheckedRead (mTime); - nif.uncheckedRead (mValue); - } - - static bool in_order (KeyT const & l, KeyT const & r) - { - return l.mTime < r.mTime; - } - - template - struct NIFStream_handler - { - static const bool FixedLength = true; - static const size_t EncodedLength = derived_type::EncodedLength; - static const bool FileCompatibleLayout = true; - - static void extract (NIFStream& Stream, KeyT & Value) - { - static_cast (Value).extract (Stream); - } - }; -}; - -template -struct LinearKeyT : KeyT -{ - static T interpolate (LinearKeyT * prev, LinearKeyT * next, float amount) - { - return linear_interpolate (amount, prev->mValue, next->mValue); - } -}; - -template -struct QuadraticKeyT : KeyT -{ - static const size_t EncodedLength = - KeyT ::EncodedLength + - NIFStream::handler ::EncodedLength * 2 - ; - - T mForwardValue; - T mBackwardValue; - - static T interpolate (QuadraticKeyT * prev, QuadraticKeyT * next, float amount) - { - return linear_interpolate (amount, prev->mValue, next->mValue); - } - - void extract (NIFStream &nif) - { - KeyT::extract (nif); - - nif.uncheckedRead (mForwardValue); - nif.uncheckedRead (mBackwardValue); - } -}; - -template -struct TbcKeyT : KeyT -{ - static const size_t EncodedLength = - KeyT ::EncodedLength + - NIFStream::handler ::EncodedLength * 3 - ; - - float mTension; - float mBias; - float mContinuity; - - static T interpolate (TbcKeyT * prev, TbcKeyT * next, float amount) - { - return linear_interpolate (amount, prev->mValue, next->mValue); - } - - void extract (NIFStream &nif) - { - KeyT::extract (nif); - - nif.uncheckedRead (mTension); - nif.uncheckedRead (mBias); - nif.uncheckedRead (mContinuity); - } -}; - -// register NIFStream extraction handlers for KeyT derivatives -template struct NIFStream::handler < LinearKeyT > : KeyT ::template NIFStream_handler < LinearKeyT > {}; -template struct NIFStream::handler < QuadraticKeyT > : KeyT ::template NIFStream_handler < QuadraticKeyT > {}; -template struct NIFStream::handler < TbcKeyT > : KeyT ::template NIFStream_handler < TbcKeyT > {}; - -struct Curve -{ - static const int sLinearInterpolation = 1; - static const int sQuadraticInterpolation = 2; - static const int sTBCInterpolation = 3; -}; - -template -struct CurveT : Curve { - - typedef KeyT BaseKey; - typedef TbcKeyT TcbKey; - typedef LinearKeyT LinearKey; - typedef QuadraticKeyT QuadraticKey; - - union keys { - LinearKey* Linear; - QuadraticKey* Quadratic; - TcbKey* Tcb; - }; - - class interpolator; - - int mInterpolationType; - size_t mSize; - keys mKeys; - - value_type sample (float time) const; - - KeyT const * const & keyAtIndex (size_t Index) const - { - switch (mInterpolationType) - { - case sLinearInterpolation: return mKeys.Linear + Index; - case sQuadraticInterpolation: return mKeys.Quadratic + Index; - case sTBCInterpolation: return mKeys.Tcb + Index; - } - } - - void read(NIFStream *nif, bool force=false) - { - size_t count = nif->getInt(); - - mSize = 0; - - if(count > 0 || force) - { - mInterpolationType = nif->getInt(); - - assert (mInterpolationType >= sLinearInterpolation && mInterpolationType <= sTBCInterpolation); - - if (count > 0) - { - if(mInterpolationType == sLinearInterpolation) - read_keys (nif, mKeys.Linear, count); - else if(mInterpolationType == sQuadraticInterpolation) - read_keys (nif, mKeys.Quadratic, count); - else if(mInterpolationType == sTBCInterpolation) - read_keys (nif, mKeys.Tcb, count); - else - nif->file->warn("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType)); - } - } - else - mInterpolationType = sLinearInterpolation; - } - - CurveT () { init (); } - CurveT (CurveT const & k) { init (k); } - //CurveT (CurveT && k) { init (); swap (std::move (k)); } - ~CurveT () { dest (); } - - operator bool () const { return mSize > 0; } - - //void operator = (CurveT && k) { swap(k); } - void operator = (CurveT const & k) { dest (); init (k); } - - void swap (CurveT & k) - { - std::swap (mSize, k.mSize); - std::swap (mInterpolationType, k.mInterpolationType); - std::swap (mKeys, k.mKeys); - } - -private: - - void init () - { - mSize = 0; - } - - void init (CurveT const & k) - { - mInterpolationType = k.mInterpolationType; - switch (mInterpolationType) - { - default: - case sLinearInterpolation: - mKeys.Linear = new LinearKey [k.mSize]; - memcpy (mKeys.Linear, k.mKeys.Linear, sizeof (LinearKey) * k.mSize); - mSize = k.mSize; - break; - case sQuadraticInterpolation: - mKeys.Quadratic = new QuadraticKey [k.mSize]; - memcpy (mKeys.Quadratic, k.mKeys.Quadratic, sizeof (QuadraticKey) * k.mSize); - mSize = k.mSize; - break; - case sTBCInterpolation: - mKeys.Tcb = new TcbKey [k.mSize]; - memcpy (mKeys.Tcb, k.mKeys.Tcb, sizeof (TcbKey) * k.mSize); - mSize = k.mSize; - break; - } - } - - void dest () - { - if (mSize > 0) - { - switch (mInterpolationType) - { - case sLinearInterpolation: delete mKeys.Linear; break; - case sQuadraticInterpolation: delete mKeys.Quadratic; break; - case sTBCInterpolation: delete mKeys.Tcb; break; - } - } - } - - template - void read_keys (NIFStream *nif, T * & store, size_t count) - { - store = new T [count]; - - mSize = count; - - nif->getArray (store, count); - - //NOTE: Is this really necessary? It seems reasonable to assume that - // animation data is already sorted by time... - // verified no out of order frames in GOTY edition - bubble_sort (store, store+count, T::in_order); - } -}; - -template -class CurveT::interpolator -{ - template - struct impl - { - key_type *Cur, *End; - - void init (key_type * Beg, size_t Len) - { - if (Len > 0) - { - Cur = Beg; - End = Beg + Len - 1; - } - else - { - Cur = End = NULL; - } - } - - bool hasData () const - { - return Cur && Cur <= End; - } - - value_type valueAt (float time) - { - while ((Cur < End) && (time >= Cur [1].mTime)) - ++Cur; - - if (Cur < End) - { - if (time > Cur->mTime) - { - key_type * Nxt = Cur + 1; - - float offset = time - Cur->mTime; - float length = Nxt->mTime - Cur->mTime; - - return key_type::interpolate (Cur, Nxt, offset / length); - } - else - return Cur->mValue; - } - else - return End->mValue; - } - - float curTime () const - { - return (Cur != NULL) ? Cur->Time : FLT_MIN; - } - - float nextTime () const - { - return Cur < End ? (Cur + 1)->mTime : FLT_MAX; - } - }; - -public: - - int mInterpolationType; - union { - impl Linear; - impl Quadratic; - impl Tcb; - }; - - interpolator (CurveT const & Curve) - { - mInterpolationType = Curve.mInterpolationType; - - switch (mInterpolationType) - { - default: - case Curve::sLinearInterpolation: Linear .init (Curve.mKeys.Linear, Curve.mSize); break; - case Curve::sQuadraticInterpolation: Quadratic.init (Curve.mKeys.Quadratic, Curve.mSize); break; - case Curve::sTBCInterpolation: Tcb .init (Curve.mKeys.Tcb, Curve.mSize); break; - } - } - - // return true if there is any value(s) in this curve - float hasData () const - { - switch (mInterpolationType) - { - default: - case Curve::sLinearInterpolation: return Linear .hasData (); - case Curve::sQuadraticInterpolation: return Quadratic.hasData (); - case Curve::sTBCInterpolation: return Tcb .hasData (); - } - } - - // return the timestamp of the next key-frame, or FLT_MAX if - // there are no more key-frames, valid if hasData returns false - float nextTime () const - { - switch (mInterpolationType) - { - default: - case Curve::sLinearInterpolation: return Linear .nextTime (); - case Curve::sQuadraticInterpolation: return Quadratic.nextTime (); - case Curve::sTBCInterpolation: return Tcb .nextTime (); - } - } - - // return the value of the curve at the specified time - // the passed in time should never exceed the result of - // nextTime, not valid if hasData returns false - value_type valueAt (float time) - { - switch (mInterpolationType) - { - default: - case Curve::sLinearInterpolation: return Linear .valueAt (time); - case Curve::sQuadraticInterpolation: return Quadratic.valueAt (time); - case Curve::sTBCInterpolation: return Tcb .valueAt (time); - } - } -}; - -template -value_type CurveT::sample (float time) const -{ - interpolator i (*this); - return i.valueAt (time); -} - -typedef CurveT FloatCurve; -typedef CurveT Vector3Curve; -typedef CurveT Vector4Curve; -typedef CurveT QuaternionCurve; - -} - -#endif diff --git a/components/nif/data.hpp b/components/nif/data.hpp index d46f99abb..f1f34184b 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -25,7 +25,6 @@ #define OPENMW_COMPONENTS_NIF_DATA_HPP #include "controlled.hpp" -#include "curve.hpp" #include #include @@ -212,7 +211,7 @@ public: class NiPosData : public Record { public: - Vector3Curve mKeyList; + Vector3KeyList mKeyList; void read(NIFStream *nif) { @@ -223,7 +222,7 @@ public: class NiUVData : public Record { public: - FloatCurve mKeyList[4]; + FloatKeyList mKeyList[4]; void read(NIFStream *nif) { @@ -235,7 +234,7 @@ public: class NiFloatData : public Record { public: - FloatCurve mKeyList; + FloatKeyList mKeyList; void read(NIFStream *nif) { @@ -285,7 +284,7 @@ public: class NiColorData : public Record { public: - Vector4Curve mKeyList; + Vector4KeyList mKeyList; void read(NIFStream *nif) { @@ -390,7 +389,7 @@ public: struct NiMorphData : public Record { struct MorphData { - FloatCurve mData; + FloatKeyList mData; std::vector mVertices; }; std::vector mMorphs; @@ -413,9 +412,9 @@ struct NiMorphData : public Record struct NiKeyframeData : public Record { - QuaternionCurve mRotations; - Vector3Curve mTranslations; - FloatCurve mScales; + QuaternionKeyList mRotations; + Vector3KeyList mTranslations; + FloatKeyList mScales; void read(NIFStream *nif) { diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 83cc6ac8f..6e629772e 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -132,5 +132,81 @@ public: size_t numRoots() { return roots.size(); } }; + +template +struct KeyT { + float mTime; + T mValue; + T mForwardValue; // Only for Quadratic interpolation + T mBackwardValue; // Only for Quadratic interpolation + float mTension; // Only for TBC interpolation + float mBias; // Only for TBC interpolation + float mContinuity; // Only for TBC interpolation +}; +typedef KeyT FloatKey; +typedef KeyT Vector3Key; +typedef KeyT Vector4Key; +typedef KeyT QuaternionKey; + +template +struct KeyListT { + typedef std::vector< KeyT > VecType; + + static const int sLinearInterpolation = 1; + static const int sQuadraticInterpolation = 2; + static const int sTBCInterpolation = 3; + + int mInterpolationType; + VecType mKeys; + + void read(NIFStream *nif, bool force=false) + { + size_t count = nif->getInt(); + if(count == 0 && !force) + return; + + mInterpolationType = nif->getInt(); + mKeys.resize(count); + if(mInterpolationType == sLinearInterpolation) + { + for(size_t i = 0;i < count;i++) + { + KeyT &key = mKeys[i]; + key.mTime = nif->getFloat(); + key.mValue = (nif->*getValue)(); + } + } + else if(mInterpolationType == sQuadraticInterpolation) + { + for(size_t i = 0;i < count;i++) + { + KeyT &key = mKeys[i]; + key.mTime = nif->getFloat(); + key.mValue = (nif->*getValue)(); + key.mForwardValue = (nif->*getValue)(); + key.mBackwardValue = (nif->*getValue)(); + } + } + else if(mInterpolationType == sTBCInterpolation) + { + for(size_t i = 0;i < count;i++) + { + KeyT &key = mKeys[i]; + key.mTime = nif->getFloat(); + key.mValue = (nif->*getValue)(); + key.mTension = nif->getFloat(); + key.mBias = nif->getFloat(); + key.mContinuity = nif->getFloat(); + } + } + else + nif->file->warn("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType)); + } +}; +typedef KeyListT FloatKeyList; +typedef KeyListT Vector3KeyList; +typedef KeyListT Vector4KeyList; +typedef KeyListT QuaternionKeyList; + } // Namespace #endif diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index f381dc239..a26f43131 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -166,9 +166,9 @@ public: class Value : public NodeTargetValue { private: - Nif::QuaternionCurve mRotations; - Nif::Vector3Curve mTranslations; - Nif::FloatCurve mScales; + Nif::QuaternionKeyList mRotations; + Nif::Vector3KeyList mTranslations; + Nif::FloatKeyList mScales; public: Value(Ogre::Node *target, const Nif::NiKeyframeData *data) @@ -186,16 +186,68 @@ public: virtual void setValue(Ogre::Real time) { - if(mRotations) - mNode->setOrientation(mRotations.sample (time)); + if(mRotations.mKeys.size() > 0) + { + if(time <= mRotations.mKeys.front().mTime) + mNode->setOrientation(mRotations.mKeys.front().mValue); + else if(time >= mRotations.mKeys.back().mTime) + mNode->setOrientation(mRotations.mKeys.back().mValue); + else + { + Nif::QuaternionKeyList::VecType::const_iterator iter(mRotations.mKeys.begin()+1); + for(;iter != mRotations.mKeys.end();iter++) + { + if(iter->mTime < time) + continue; - if(mTranslations) - mNode->setPosition(mTranslations.sample (time)); + Nif::QuaternionKeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + mNode->setOrientation(Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue)); + break; + } + } + } + if(mTranslations.mKeys.size() > 0) + { + if(time <= mTranslations.mKeys.front().mTime) + mNode->setPosition(mTranslations.mKeys.front().mValue); + else if(time >= mTranslations.mKeys.back().mTime) + mNode->setPosition(mTranslations.mKeys.back().mValue); + else + { + Nif::Vector3KeyList::VecType::const_iterator iter(mTranslations.mKeys.begin()+1); + for(;iter != mTranslations.mKeys.end();iter++) + { + if(iter->mTime < time) + continue; - if(mScales) + Nif::Vector3KeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + mNode->setPosition(last->mValue + ((iter->mValue - last->mValue)*a)); + break; + } + } + } + if(mScales.mKeys.size() > 0) { - float s = mScales.sample (time); - mNode->setScale(s, s, s); + if(time <= mScales.mKeys.front().mTime) + mNode->setScale(Ogre::Vector3(mScales.mKeys.front().mValue)); + else if(time >= mScales.mKeys.back().mTime) + mNode->setScale(Ogre::Vector3(mScales.mKeys.back().mValue)); + else + { + Nif::FloatKeyList::VecType::const_iterator iter(mScales.mKeys.begin()+1); + for(;iter != mScales.mKeys.end();iter++) + { + if(iter->mTime < time) + continue; + + Nif::FloatKeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + mNode->setScale(Ogre::Vector3(last->mValue + ((iter->mValue - last->mValue)*a))); + break; + } + } } } }; @@ -210,14 +262,30 @@ public: { private: Ogre::MaterialPtr mMaterial; - Nif::FloatCurve mUTrans; - Nif::FloatCurve mVTrans; - Nif::FloatCurve mUScale; - Nif::FloatCurve mVScale; + Nif::FloatKeyList mUTrans; + Nif::FloatKeyList mVTrans; + Nif::FloatKeyList mUScale; + Nif::FloatKeyList mVScale; - static float lookupValue(const Nif::FloatCurve &keys, float time, float def) + static float lookupValue(const Nif::FloatKeyList &keys, float time, float def) { - return keys ? keys.sample (time) : def; + if(keys.mKeys.size() == 0) + return def; + + if(time <= keys.mKeys.front().mTime) + return keys.mKeys.front().mValue; + + Nif::FloatKeyList::VecType::const_iterator iter(keys.mKeys.begin()+1); + for(;iter != keys.mKeys.end();iter++) + { + if(iter->mTime < time) + continue; + + Nif::FloatKeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + return last->mValue + ((iter->mValue - last->mValue)*a); + } + return keys.mKeys.back().mValue; } public: @@ -324,19 +392,18 @@ class NIFObjectLoader const Nif::NiColorData *clrdata = cl->data.getPtr(); Ogre::ParticleAffector *affector = partsys->addAffector("ColourInterpolator"); - size_t num_colors = std::min(6, clrdata->mKeyList.mSize); + size_t num_colors = std::min(6, clrdata->mKeyList.mKeys.size()); for(size_t i = 0;i < num_colors;i++) { - Nif::Vector4Curve::BaseKey const * Key = clrdata->mKeyList.keyAtIndex (i); Ogre::ColourValue color; - color.r = Key->mValue[0]; - color.g = Key->mValue[1]; - color.b = Key->mValue[2]; - color.a = Key->mValue[3]; + color.r = clrdata->mKeyList.mKeys[i].mValue[0]; + color.g = clrdata->mKeyList.mKeys[i].mValue[1]; + color.b = clrdata->mKeyList.mKeys[i].mValue[2]; + color.a = clrdata->mKeyList.mKeys[i].mValue[3]; affector->setParameter("colour"+Ogre::StringConverter::toString(i), Ogre::StringConverter::toString(color)); affector->setParameter("time"+Ogre::StringConverter::toString(i), - Ogre::StringConverter::toString(Key->mTime)); + Ogre::StringConverter::toString(clrdata->mKeyList.mKeys[i].mTime)); } } else if(e->recType == Nif::RC_NiParticleRotation) diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index 66060b967..e97e91ef0 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -10,11 +10,6 @@ namespace NifOgre { -template -static value_type min (value_type V0, value_type V1, value_type V2, value_type V3) -{ - return std::min (std::min (V0, V1), std::min (V2, V3)); -} void NIFSkeletonLoader::buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector &ctrls, const std::vector &targets, float startTime, float stopTime) { @@ -27,6 +22,15 @@ void NIFSkeletonLoader::buildAnimation(Ogre::Skeleton *skel, const std::string & continue; const Nif::NiKeyframeData *kf = kfc->data.getPtr(); + /* Get the keyframes and make sure they're sorted first to last */ + const Nif::QuaternionKeyList &quatkeys = kf->mRotations; + const Nif::Vector3KeyList &trankeys = kf->mTranslations; + const Nif::FloatKeyList &scalekeys = kf->mScales; + + Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin(); + Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin(); + Nif::FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin(); + Ogre::Bone *bone = skel->getBone(targets[i]); // NOTE: For some reason, Ogre doesn't like the node track ID being different from // the bone ID @@ -34,30 +38,83 @@ void NIFSkeletonLoader::buildAnimation(Ogre::Skeleton *skel, const std::string & anim->getNodeTrack(bone->getHandle()) : anim->createNodeTrack(bone->getHandle(), bone); - Nif::QuaternionCurve::interpolator rci (kf->mRotations); - Nif::Vector3Curve::interpolator tci (kf->mTranslations); - Nif::FloatCurve::interpolator sci (kf->mScales); - - float next_timestamp = startTime; - - for (;;) + Ogre::Quaternion lastquat, curquat; + Ogre::Vector3 lasttrans(0.0f), curtrans(0.0f); + Ogre::Vector3 lastscale(1.0f), curscale(1.0f); + if(quatiter != quatkeys.mKeys.end()) + lastquat = curquat = quatiter->mValue; + if(traniter != trankeys.mKeys.end()) + lasttrans = curtrans = traniter->mValue; + if(scaleiter != scalekeys.mKeys.end()) + lastscale = curscale = Ogre::Vector3(scaleiter->mValue); + + bool didlast = false; + while(!didlast) { - static const Ogre::Vector3 one (1,1,1); - - Ogre::TransformKeyFrame *kframe; - kframe = nodetrack->createNodeKeyFrame (next_timestamp); - - if (rci.hasData ()) kframe->setRotation (rci.valueAt (next_timestamp)); - if (tci.hasData ()) kframe->setTranslate (tci.valueAt (next_timestamp)); - if (sci.hasData ()) kframe->setScale (sci.valueAt (next_timestamp)*one); + float curtime = std::numeric_limits::max(); + + //Get latest time + if(quatiter != quatkeys.mKeys.end()) + curtime = std::min(curtime, quatiter->mTime); + if(traniter != trankeys.mKeys.end()) + curtime = std::min(curtime, traniter->mTime); + if(scaleiter != scalekeys.mKeys.end()) + curtime = std::min(curtime, scaleiter->mTime); + + curtime = std::max(curtime, startTime); + if(curtime >= stopTime) + { + didlast = true; + curtime = stopTime; + } - if (next_timestamp >= stopTime) - break; + // Get the latest quaternions, translations, and scales for the + // current time + while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime) + { + lastquat = curquat; + if(++quatiter != quatkeys.mKeys.end()) + curquat = quatiter->mValue; + } + while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime) + { + lasttrans = curtrans; + if(++traniter != trankeys.mKeys.end()) + curtrans = traniter->mValue; + } + while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime) + { + lastscale = curscale; + if(++scaleiter != scalekeys.mKeys.end()) + curscale = Ogre::Vector3(scaleiter->mValue); + } - next_timestamp = min (stopTime, - rci.nextTime (), - tci.nextTime (), - sci.nextTime ()); + Ogre::TransformKeyFrame *kframe; + kframe = nodetrack->createNodeKeyFrame(curtime); + if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin()) + kframe->setRotation(curquat); + else + { + Nif::QuaternionKeyList::VecType::const_iterator last = quatiter-1; + float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime); + kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat)); + } + if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin()) + kframe->setTranslate(curtrans); + else + { + Nif::Vector3KeyList::VecType::const_iterator last = traniter-1; + float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime); + kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff)); + } + if(scaleiter == scalekeys.mKeys.end() || scaleiter == scalekeys.mKeys.begin()) + kframe->setScale(curscale); + else + { + Nif::FloatKeyList::VecType::const_iterator last = scaleiter-1; + float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime); + kframe->setScale(lastscale + ((curscale-lastscale)*diff)); + } } } anim->optimise();