From e7665582ad9b855522b37d955997df527254a01d Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sat, 5 Jan 2013 22:51:06 -0800 Subject: [PATCH] reworked Nif::KeyListT into Nif::CurveT Renamed Nif:KeyListT to Nif::CurveT, moved it into its own file and changed its implementation so that on compatible platforms, the entire array of key-frames can be read in a single read call. Added a helper class called Nif::CurveT::interpolator to allow other code to easily evaluate the curve. Reworked part of the skeletonLoader code to use the interpolator to simplify its own logic. --- components/nif/curve.hpp | 424 +++++++++++++++++++++++++++ components/nif/data.hpp | 17 +- components/nif/niffile.hpp | 76 ----- components/nifogre/ogrenifloader.cpp | 113 ++----- components/nifogre/skeleton.cpp | 107 ++----- 5 files changed, 481 insertions(+), 256 deletions(-) create mode 100644 components/nif/curve.hpp diff --git a/components/nif/curve.hpp b/components/nif/curve.hpp new file mode 100644 index 0000000000..9a71f0923d --- /dev/null +++ b/components/nif/curve.hpp @@ -0,0 +1,424 @@ +#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 f1f34184ba..d46f99abbc 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -25,6 +25,7 @@ #define OPENMW_COMPONENTS_NIF_DATA_HPP #include "controlled.hpp" +#include "curve.hpp" #include #include @@ -211,7 +212,7 @@ public: class NiPosData : public Record { public: - Vector3KeyList mKeyList; + Vector3Curve mKeyList; void read(NIFStream *nif) { @@ -222,7 +223,7 @@ public: class NiUVData : public Record { public: - FloatKeyList mKeyList[4]; + FloatCurve mKeyList[4]; void read(NIFStream *nif) { @@ -234,7 +235,7 @@ public: class NiFloatData : public Record { public: - FloatKeyList mKeyList; + FloatCurve mKeyList; void read(NIFStream *nif) { @@ -284,7 +285,7 @@ public: class NiColorData : public Record { public: - Vector4KeyList mKeyList; + Vector4Curve mKeyList; void read(NIFStream *nif) { @@ -389,7 +390,7 @@ public: struct NiMorphData : public Record { struct MorphData { - FloatKeyList mData; + FloatCurve mData; std::vector mVertices; }; std::vector mMorphs; @@ -412,9 +413,9 @@ struct NiMorphData : public Record struct NiKeyframeData : public Record { - QuaternionKeyList mRotations; - Vector3KeyList mTranslations; - FloatKeyList mScales; + QuaternionCurve mRotations; + Vector3Curve mTranslations; + FloatCurve mScales; void read(NIFStream *nif) { diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 6e629772e6..83cc6ac8fd 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -132,81 +132,5 @@ 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 a26f431311..f381dc239f 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -166,9 +166,9 @@ public: class Value : public NodeTargetValue { private: - Nif::QuaternionKeyList mRotations; - Nif::Vector3KeyList mTranslations; - Nif::FloatKeyList mScales; + Nif::QuaternionCurve mRotations; + Nif::Vector3Curve mTranslations; + Nif::FloatCurve mScales; public: Value(Ogre::Node *target, const Nif::NiKeyframeData *data) @@ -186,68 +186,16 @@ public: virtual void setValue(Ogre::Real 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(mRotations) + mNode->setOrientation(mRotations.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(mTranslations) + mNode->setPosition(mTranslations.sample (time)); - 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) + if(mScales) { - 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; - } - } + float s = mScales.sample (time); + mNode->setScale(s, s, s); } } }; @@ -262,30 +210,14 @@ public: { private: Ogre::MaterialPtr mMaterial; - Nif::FloatKeyList mUTrans; - Nif::FloatKeyList mVTrans; - Nif::FloatKeyList mUScale; - Nif::FloatKeyList mVScale; + Nif::FloatCurve mUTrans; + Nif::FloatCurve mVTrans; + Nif::FloatCurve mUScale; + Nif::FloatCurve mVScale; - static float lookupValue(const Nif::FloatKeyList &keys, float time, float def) + static float lookupValue(const Nif::FloatCurve &keys, float time, float 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; + return keys ? keys.sample (time) : def; } public: @@ -392,18 +324,19 @@ class NIFObjectLoader const Nif::NiColorData *clrdata = cl->data.getPtr(); Ogre::ParticleAffector *affector = partsys->addAffector("ColourInterpolator"); - size_t num_colors = std::min(6, clrdata->mKeyList.mKeys.size()); + size_t num_colors = std::min(6, clrdata->mKeyList.mSize); for(size_t i = 0;i < num_colors;i++) { + Nif::Vector4Curve::BaseKey const * Key = clrdata->mKeyList.keyAtIndex (i); Ogre::ColourValue color; - 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]; + color.r = Key->mValue[0]; + color.g = Key->mValue[1]; + color.b = Key->mValue[2]; + color.a = Key->mValue[3]; affector->setParameter("colour"+Ogre::StringConverter::toString(i), Ogre::StringConverter::toString(color)); affector->setParameter("time"+Ogre::StringConverter::toString(i), - Ogre::StringConverter::toString(clrdata->mKeyList.mKeys[i].mTime)); + Ogre::StringConverter::toString(Key->mTime)); } } else if(e->recType == Nif::RC_NiParticleRotation) diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index e97e91ef03..66060b9676 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -10,6 +10,11 @@ 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) { @@ -22,15 +27,6 @@ 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 @@ -38,83 +34,30 @@ void NIFSkeletonLoader::buildAnimation(Ogre::Skeleton *skel, const std::string & anim->getNodeTrack(bone->getHandle()) : anim->createNodeTrack(bone->getHandle(), bone); - 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); + Nif::QuaternionCurve::interpolator rci (kf->mRotations); + Nif::Vector3Curve::interpolator tci (kf->mTranslations); + Nif::FloatCurve::interpolator sci (kf->mScales); - bool didlast = false; - while(!didlast) + float next_timestamp = startTime; + + for (;;) { - 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; - } - - // 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); - } + static const Ogre::Vector3 one (1,1,1); 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)); - } + 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); + + if (next_timestamp >= stopTime) + break; + + next_timestamp = min (stopTime, + rci.nextTime (), + tci.nextTime (), + sci.nextTime ()); } } anim->optimise();