/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "motion/quaternion_channel.h" using namespace GS; //------------------------------------------------------------------------------ static float range(float v, float lo, float hi, int *i) { float r = hi - lo; if (!r) { if (i) *i = 0; return lo; } float v2 = v - lo; if (v2 >= 0) v2 = lo + v2 - r * Math::Floor(v2 / r); else v2 = hi + v2 - r * Math::Ceil(v2 / r); if (i) *i = - (int)((v2 - v) / r + (v2 > v ? 0.5f : -0.5f)); return Types::Clamp(v2, lo, hi); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void QuaternionChannel::Evaluate(Time t, Quaternion &p, Curve::LoopMode loop, Time loop_start, Time loop_end) const { if (keys.GetCount() == 0) return; QuaternionKey *skey = keys[0], *ekey = keys[keys.GetCount() - 1]; loop_start = (loop_start == Time::Inf) ? skey->t : Types::Clamp(loop_start, skey->t, ekey->t); loop_end = (loop_end == Time::Inf) ? ekey->t : Types::Clamp(loop_end, skey->t, ekey->t); int noff = 0; if (t < loop_start) { switch (loop) { default: case Curve::Constant: Evaluate(loop_start, p, loop, loop_start, loop_end); return; case Curve::Reset: p.Set(0, 0, 0, 1); return; case Curve::Repeat: case Curve::OffsetAndRepeat: t.setSec(range(t.toSec(), loop_start.toSec(), loop_end.toSec(), NULL)); break; case Curve::Oscillate: t.setSec(range(t.toSec(), loop_start.toSec(), loop_end.toSec(), &noff)); if (noff % 2) t = loop_end + loop_start - t; break; } } else if (t > loop_end) { switch (loop) { default: case Curve::Constant: Evaluate(loop_end, p, loop, loop_start, loop_end); return; case Curve::Reset: p.Set(0, 0, 0, 1); return; case Curve::Repeat: case Curve::OffsetAndRepeat: t.setSec(range(t.toSec(), loop_start.toSec(), loop_end.toSec(), NULL)); break; case Curve::Oscillate: t.setSec(range(t.toSec(), loop_start.toSec(), loop_end.toSec(), &noff)); if (noff % 2) t = loop_end + loop_start - t; break; } } // Evaluate (t is guaranteed to be in range). int ikey0; #if 1 { uint lo = 0, hi = keys.GetCount() - 1; forever { uint mid = (lo + hi) / 2; if (keys[mid]->t > t) hi = mid; else { if (lo == mid) { ikey0 = lo; break; } else lo = mid; } } } #else for (ikey0 = 1; ikey0 < int(keys.GetCount()); ikey0++) if (keys[ikey0]->t > t) break; --ikey0; #endif // Slerp. if (ikey0 == (int(keys.GetCount()) - 1)) p = keys[ikey0]->q; else { const float k = (t - keys[ikey0]->t).toSec() / (keys[ikey0 + 1]->t - keys[ikey0]->t).toSec(); p = Quaternion::Slerp(k, keys[ikey0]->q, keys[ikey0 + 1]->q).Normalize(); } } uint QuaternionChannel::Optimize(float threshold) { return 0; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ TimeRange QuaternionChannel::GetTimeRange() const { Time min, max; if (keys.GetCount() > 0) { min = max = keys[0]->t; for (uint n = 1; n < keys.GetCount(); ++n) { min = Types::Min(min, keys[n]->t); max = Types::Max(max, keys[n]->t); } } return TimeRange(min, max); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool QuaternionChannel::Insert(const QuaternionKey &key) { uint n = 0; for (; n < keys.GetCount(); ++n) if (keys[n]->t > key.t) break; return keys.Insert(new QuaternionKey(key), n); } void QuaternionChannel::Clear() { ArrayListDeleteAllPtr(QuaternionKey *, keys) } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ QuaternionChannel::QuaternionChannel() {} QuaternionChannel::~QuaternionChannel() { Clear(); } //------------------------------------------------------------------------------