180 lines
4.3 KiB
C++
180 lines
4.3 KiB
C++
/* -----------------------------------------------------------------------------
|
|
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(); }
|
|
//------------------------------------------------------------------------------
|