#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