Add first derivative calculation utilities for Curve
This commit is contained in:
@@ -84,21 +84,21 @@ namespace AnimationUtils
|
||||
}
|
||||
|
||||
template<class T>
|
||||
FORCE_INLINE static void Interpolate(const T& a, const T& b, float alpha, T& result)
|
||||
FORCE_INLINE static void Interpolate(const T& a, const T& b, float t, T& result)
|
||||
{
|
||||
result = (T)(a + alpha * (b - a));
|
||||
result = (T)(a + t * (b - a));
|
||||
}
|
||||
|
||||
template<>
|
||||
FORCE_INLINE void Interpolate<Vector3>(const Vector3& a, const Vector3& b, float alpha, Vector3& result)
|
||||
FORCE_INLINE void Interpolate<Vector3>(const Vector3& a, const Vector3& b, float t, Vector3& result)
|
||||
{
|
||||
Vector3::Lerp(a, b, alpha, result);
|
||||
Vector3::Lerp(a, b, t, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
FORCE_INLINE void Interpolate<Quaternion>(const Quaternion& a, const Quaternion& b, float alpha, Quaternion& result)
|
||||
FORCE_INLINE void Interpolate<Quaternion>(const Quaternion& a, const Quaternion& b, float t, Quaternion& result)
|
||||
{
|
||||
Quaternion::Slerp(a, b, alpha, result);
|
||||
Quaternion::Slerp(a, b, t, result);
|
||||
}
|
||||
|
||||
static void WrapTime(float& time, float start, float end, bool loop)
|
||||
@@ -134,98 +134,111 @@ namespace AnimationUtils
|
||||
/// Evaluates a cubic Hermite curve at a specific point.
|
||||
/// </summary>
|
||||
/// <param name="t">The time parameter that at which to evaluate the curve, in range [0, 1].</param>
|
||||
/// <param name="pointA">The starting point (at t=0).</param>
|
||||
/// <param name="pointB">The ending point (at t=1).</param>
|
||||
/// <param name="tangentA">The starting tangent (at t=0).</param>
|
||||
/// <param name="tangentB">The ending tangent (at t = 1).</param>
|
||||
/// <param name="p0">The starting point (at t=0).</param>
|
||||
/// <param name="p1">The ending point (at t=1).</param>
|
||||
/// <param name="t0">The starting tangent (at t=0).</param>
|
||||
/// <param name="t1">The ending tangent (at t = 1).</param>
|
||||
/// <param name="result">The evaluated value.</param>
|
||||
template<class T>
|
||||
static void CubicHermite(const float t, const T& pointA, const T& pointB, const T& tangentA, const T& tangentB, T* result)
|
||||
static void CubicHermite(const T& p0, const T& p1, const T& t0, const T& t1, float t, T& result)
|
||||
{
|
||||
const float t2 = t * t;
|
||||
const float t3 = t2 * t;
|
||||
|
||||
float a = 2 * t3 - 3 * t2 + 1;
|
||||
float b = t3 - 2 * t2 + t;
|
||||
float c = -2 * t3 + 3 * t2;
|
||||
float d = t3 - t2;
|
||||
|
||||
*result = a * pointA + b * tangentA + c * pointB + d * tangentB;
|
||||
const float tt = t * t;
|
||||
const float ttt = tt * t;
|
||||
result = (2 * ttt - 3 * tt + 1) * p0 + (ttt - 2 * tt + t) * t0 + (-2 * ttt + 3 * tt) * p1 + (ttt - tt) * t1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the first derivative of a cubic Hermite curve at a specific point.
|
||||
/// </summary>
|
||||
/// <param name="t">The time parameter that at which to evaluate the curve, in range [0, 1].</param>
|
||||
/// <param name="pointA">The starting point (at t=0).</param>
|
||||
/// <param name="pointB">The ending point (at t=1).</param>
|
||||
/// <param name="tangentA">The starting tangent (at t=0).</param>
|
||||
/// <param name="tangentB">The ending tangent (at t = 1).</param>
|
||||
/// <param name="p0">The starting point (at t=0).</param>
|
||||
/// <param name="p1">The ending point (at t=1).</param>
|
||||
/// <param name="t0">The starting tangent (at t=0).</param>
|
||||
/// <param name="t1">The ending tangent (at t=1).</param>
|
||||
/// <param name="result">The evaluated value.</param>
|
||||
template<class T>
|
||||
static void CubicHermiteD1(const float t, const T& pointA, const T& pointB, const T& tangentA, const T& tangentB, T* result)
|
||||
static void CubicHermiteFirstDerivative(const T& p0, const T& p1, const T& t0, const T& t1, float t, T& result)
|
||||
{
|
||||
const float t2 = t * t;
|
||||
|
||||
float a = 6 * t2 - 6 * t;
|
||||
float b = 3 * t2 - 4 * t + 1;
|
||||
float c = -6 * t2 + 6 * t;
|
||||
float d = 3 * t2 - 2 * t;
|
||||
|
||||
*result = a * pointA + b * tangentA + c * pointB + d * tangentB;
|
||||
const float tt = t * t;
|
||||
result = (6 * tt - 6 * t) * p0 + (3 * tt - 4 * t + 1) * t0 + (-6 * tt + 6 * t) * p1 + (3 * tt - 2 * t) * t1;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void Bezier(const T& p0, const T& p1, const T& p2, const T& p3, float alpha, T& result)
|
||||
static void Bezier(const T& p0, const T& p1, const T& p2, const T& p3, float t, T& result)
|
||||
{
|
||||
T p01, p12, p23, p012, p123;
|
||||
Interpolate(p0, p1, alpha, p01);
|
||||
Interpolate(p1, p2, alpha, p12);
|
||||
Interpolate(p2, p3, alpha, p23);
|
||||
Interpolate(p01, p12, alpha, p012);
|
||||
Interpolate(p12, p23, alpha, p123);
|
||||
Interpolate(p012, p123, alpha, result);
|
||||
Interpolate(p0, p1, t, p01);
|
||||
Interpolate(p1, p2, t, p12);
|
||||
Interpolate(p2, p3, t, p23);
|
||||
Interpolate(p01, p12, t, p012);
|
||||
Interpolate(p12, p23, t, p123);
|
||||
Interpolate(p012, p123, t, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Bezier<Vector2>(const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& p3, float alpha, Vector2& result)
|
||||
void Bezier<Vector2>(const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& p3, float t, Vector2& result)
|
||||
{
|
||||
const float u = 1.0f - alpha;
|
||||
const float tt = alpha * alpha;
|
||||
const float u = 1.0f - t;
|
||||
const float tt = t * t;
|
||||
const float uu = u * u;
|
||||
const float uuu = uu * u;
|
||||
const float ttt = tt * alpha;
|
||||
result = uuu * p0 + 3 * uu * alpha * p1 + 3 * u * tt * p2 + ttt * p3;
|
||||
const float ttt = tt * t;
|
||||
result = uuu * p0 + 3 * uu * t * p1 + 3 * u * tt * p2 + ttt * p3;
|
||||
}
|
||||
|
||||
template<>
|
||||
void Bezier<Vector3>(const Vector3& p0, const Vector3& p1, const Vector3& p2, const Vector3& p3, float alpha, Vector3& result)
|
||||
void Bezier<Vector3>(const Vector3& p0, const Vector3& p1, const Vector3& p2, const Vector3& p3, float t, Vector3& result)
|
||||
{
|
||||
const float u = 1.0f - alpha;
|
||||
const float tt = alpha * alpha;
|
||||
const float u = 1.0f - t;
|
||||
const float tt = t * t;
|
||||
const float uu = u * u;
|
||||
const float uuu = uu * u;
|
||||
const float ttt = tt * alpha;
|
||||
result = uuu * p0 + 3 * uu * alpha * p1 + 3 * u * tt * p2 + ttt * p3;
|
||||
const float ttt = tt * t;
|
||||
result = uuu * p0 + 3 * uu * t * p1 + 3 * u * tt * p2 + ttt * p3;
|
||||
}
|
||||
|
||||
template<>
|
||||
void Bezier<Quaternion>(const Quaternion& p0, const Quaternion& p1, const Quaternion& p2, const Quaternion& p3, float alpha, Quaternion& result)
|
||||
void Bezier<Quaternion>(const Quaternion& p0, const Quaternion& p1, const Quaternion& p2, const Quaternion& p3, float t, Quaternion& result)
|
||||
{
|
||||
Quaternion p01, p12, p23, p012, p123;
|
||||
Quaternion::Slerp(p0, p1, alpha, p01);
|
||||
Quaternion::Slerp(p1, p2, alpha, p12);
|
||||
Quaternion::Slerp(p2, p3, alpha, p23);
|
||||
Quaternion::Slerp(p01, p12, alpha, p012);
|
||||
Quaternion::Slerp(p12, p23, alpha, p123);
|
||||
Quaternion::Slerp(p012, p123, alpha, result);
|
||||
Quaternion::Slerp(p0, p1, t, p01);
|
||||
Quaternion::Slerp(p1, p2, t, p12);
|
||||
Quaternion::Slerp(p2, p3, t, p23);
|
||||
Quaternion::Slerp(p01, p12, t, p012);
|
||||
Quaternion::Slerp(p12, p23, t, p123);
|
||||
Quaternion::Slerp(p012, p123, t, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Bezier<Transform>(const Transform& p0, const Transform& p1, const Transform& p2, const Transform& p3, float alpha, Transform& result)
|
||||
void Bezier<Transform>(const Transform& p0, const Transform& p1, const Transform& p2, const Transform& p3, float t, Transform& result)
|
||||
{
|
||||
Bezier<Vector3>(p0.Translation, p1.Translation, p2.Translation, p3.Translation, alpha, result.Translation);
|
||||
Bezier<Quaternion>(p0.Orientation, p1.Orientation, p2.Orientation, p3.Orientation, alpha, result.Orientation);
|
||||
Bezier<Vector3>(p0.Scale, p1.Scale, p2.Scale, p3.Scale, alpha, result.Scale);
|
||||
Bezier<Vector3>(p0.Translation, p1.Translation, p2.Translation, p3.Translation, t, result.Translation);
|
||||
Bezier<Quaternion>(p0.Orientation, p1.Orientation, p2.Orientation, p3.Orientation, t, result.Orientation);
|
||||
Bezier<Vector3>(p0.Scale, p1.Scale, p2.Scale, p3.Scale, t, result.Scale);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void BezierFirstDerivative(const T& p0, const T& p1, const T& p2, const T& p3, float t, T& result)
|
||||
{
|
||||
const float u = 1.0f - t;
|
||||
const float tt = t * t;
|
||||
const float uu = u * u;
|
||||
result = 3.0f * uu * (p1 - p0) + 6.0f * u * t * (p2 - p1) + 3.0f * tt * (p3 - p2);
|
||||
}
|
||||
|
||||
template<>
|
||||
static void BezierFirstDerivative<Quaternion>(const Quaternion& p0, const Quaternion& p1, const Quaternion& p2, const Quaternion& p3, float t, Quaternion& result)
|
||||
{
|
||||
Vector3 euler;
|
||||
BezierFirstDerivative<Vector3>(p0.GetEuler(), p1.GetEuler(), p2.GetEuler(), p3.GetEuler(), t, euler);
|
||||
result = Quaternion::Euler(euler);
|
||||
}
|
||||
|
||||
template<>
|
||||
static void BezierFirstDerivative<Transform>(const Transform& p0, const Transform& p1, const Transform& p2, const Transform& p3, float t, Transform& result)
|
||||
{
|
||||
BezierFirstDerivative<Vector3>(p0.Translation, p1.Translation, p2.Translation, p3.Translation, t, result.Translation);
|
||||
BezierFirstDerivative<Quaternion>(p0.Orientation, p1.Orientation, p2.Orientation, p3.Orientation, t, result.Orientation);
|
||||
BezierFirstDerivative<Vector3>(p0.Scale, p1.Scale, p2.Scale, p3.Scale, t, result.Scale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,11 @@ public:
|
||||
result = a.Value;
|
||||
}
|
||||
|
||||
FORCE_INLINE static void InterpolateFirstDerivative(const StepCurveKeyframe& a, const StepCurveKeyframe& b, float alpha, float length, T& result)
|
||||
{
|
||||
result = AnimationUtils::GetZero<T>();
|
||||
}
|
||||
|
||||
FORCE_INLINE static void InterpolateKey(const StepCurveKeyframe& a, const StepCurveKeyframe& b, float alpha, float length, StepCurveKeyframe& result)
|
||||
{
|
||||
result = a;
|
||||
@@ -97,6 +102,11 @@ public:
|
||||
AnimationUtils::Interpolate(a.Value, b.Value, alpha, result);
|
||||
}
|
||||
|
||||
FORCE_INLINE static void InterpolateFirstDerivative(const LinearCurveKeyframe& a, const LinearCurveKeyframe& b, float alpha, float length, T& result)
|
||||
{
|
||||
result = b.Value - a.Value;
|
||||
}
|
||||
|
||||
FORCE_INLINE static void InterpolateKey(const LinearCurveKeyframe& a, const LinearCurveKeyframe& b, float alpha, float length, LinearCurveKeyframe& result)
|
||||
{
|
||||
result.Time = a.Time + (b.Time - a.Time) * alpha;
|
||||
@@ -157,20 +167,23 @@ public:
|
||||
{
|
||||
T leftTangent = a.Value + a.TangentOut * length;
|
||||
T rightTangent = b.Value + b.TangentIn * length;
|
||||
AnimationUtils::CubicHermite( a.Value, b.Value, leftTangent, rightTangent, alpha, result);
|
||||
}
|
||||
|
||||
AnimationUtils::CubicHermite(alpha, a.Value, b.Value, leftTangent, rightTangent, result);
|
||||
static void InterpolateFirstDerivative(const HermiteCurveKeyframe& a, const HermiteCurveKeyframe& b, float alpha, float length, T& result)
|
||||
{
|
||||
T leftTangent = a.Value + a.TangentOut * length;
|
||||
T rightTangent = b.Value + b.TangentIn * length;
|
||||
AnimationUtils::CubicHermiteFirstDerivative( a.Value, b.Value, leftTangent, rightTangent, alpha, result);
|
||||
}
|
||||
|
||||
static void InterpolateKey(const HermiteCurveKeyframe& a, const HermiteCurveKeyframe& b, float alpha, float length, HermiteCurveKeyframe& result)
|
||||
{
|
||||
result.Time = a.Time + length * alpha;
|
||||
|
||||
T leftTangent = a.Value + a.TangentOut * length;
|
||||
T rightTangent = b.Value + b.TangentIn * length;
|
||||
|
||||
AnimationUtils::CubicHermite(alpha, a.Value, b.Value, leftTangent, rightTangent, result.Value);
|
||||
AnimationUtils::CubicHermiteD1(alpha, a.Value, b.Value, leftTangent, rightTangent, result.TangentIn);
|
||||
|
||||
AnimationUtils::CubicHermite(a.Value, b.Value, leftTangent, rightTangent, alpha, result.Value);
|
||||
AnimationUtils::CubicHermiteFirstDerivative(a.Value, b.Value, leftTangent, rightTangent, alpha, result.TangentIn);
|
||||
result.TangentIn /= length;
|
||||
result.TangentOut = result.TangentIn;
|
||||
}
|
||||
@@ -238,20 +251,24 @@ public:
|
||||
T leftTangent, rightTangent;
|
||||
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
|
||||
|
||||
AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result);
|
||||
}
|
||||
|
||||
static void InterpolateFirstDerivative(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, T& result)
|
||||
{
|
||||
T leftTangent, rightTangent;
|
||||
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
|
||||
AnimationUtils::BezierFirstDerivative(a.Value, leftTangent, rightTangent, b.Value, alpha, result);
|
||||
}
|
||||
|
||||
static void InterpolateKey(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, BezierCurveKeyframe& result)
|
||||
{
|
||||
result.Time = a.Time + length * alpha;
|
||||
|
||||
T leftTangent, rightTangent;
|
||||
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
|
||||
|
||||
AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result.Value);
|
||||
|
||||
result.TangentIn = a.TangentOut;
|
||||
result.TangentOut = b.TangentIn;
|
||||
}
|
||||
@@ -366,6 +383,48 @@ public:
|
||||
KeyFrame::Interpolate(leftKey, rightKey, t, length, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the first derivative of the animation curve at the specified time (aka velocity).
|
||||
/// </summary>
|
||||
/// <param name="data">The keyframes data container.</param>
|
||||
/// <param name="result">The calculated first derivative from the curve at provided time.</param>
|
||||
/// <param name="time">The time to evaluate the curve at.</param>
|
||||
/// <param name="loop">If true the curve will loop when it goes past the end or beginning. Otherwise the curve value will be clamped.</param>
|
||||
void EvaluateFirstDerivative(const KeyFrameData& data, T& result, float time, bool loop = true) const
|
||||
{
|
||||
const int32 count = data.Length();
|
||||
if (count == 0)
|
||||
{
|
||||
result = _default;
|
||||
return;
|
||||
}
|
||||
|
||||
const float start = 0;
|
||||
const float end = data[count - 1].Time;
|
||||
AnimationUtils::WrapTime(time, start, end, loop);
|
||||
|
||||
int32 leftKeyIdx;
|
||||
int32 rightKeyIdx;
|
||||
FindKeys(data, time, leftKeyIdx, rightKeyIdx);
|
||||
|
||||
const KeyFrame& leftKey = data[leftKeyIdx];
|
||||
const KeyFrame& rightKey = data[rightKeyIdx];
|
||||
|
||||
if (leftKeyIdx == rightKeyIdx)
|
||||
{
|
||||
result = leftKey.Value;
|
||||
return;
|
||||
}
|
||||
|
||||
const float length = rightKey.Time - leftKey.Time;
|
||||
|
||||
// Scale from arbitrary range to [0, 1]
|
||||
float t = Math::NearEqual(length, 0.0f) ? 0.0f : (time - leftKey.Time) / length;
|
||||
|
||||
// Evaluate the derivative at the curve
|
||||
KeyFrame::InterpolateFirstDerivative(leftKey, rightKey, t, length, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the animation curve key at the specified time.
|
||||
/// </summary>
|
||||
@@ -586,6 +645,18 @@ public:
|
||||
Base::Evaluate(data, result, time, loop);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the first derivative of the animation curve at the specified time (aka velocity).
|
||||
/// </summary>
|
||||
/// <param name="result">The calculated first derivative from the curve at provided time.</param>
|
||||
/// <param name="time">The time to evaluate the curve at.</param>
|
||||
/// <param name="loop">If true the curve will loop when it goes past the end or beginning. Otherwise the curve value will be clamped.</param>
|
||||
void EvaluateFirstDerivative(T& result, float time, bool loop = true) const
|
||||
{
|
||||
typename Base::KeyFrameData data(_keyframes);
|
||||
Base::EvaluateFirstDerivative(data, result, time, loop);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the animation curve key at the specified time.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user