T TAnimationCurve<T>::evaluate(float time, bool loop) const { if (mKeyframes.size() == 0) return T(); // Clamp to start or loop if (time < mStart) { if (loop) time = time - std::floor(time / mLength) * mLength; else // Clamping time = mStart; } // Clamp to end or loop if (time > mEnd) { if (loop) time = time - std::floor(time / mLength) * mLength; else // Clamping time = mEnd; } UINT32 leftKeyIdx; UINT32 rightKeyIdx; findKeys(time, leftKeyIdx, rightKeyIdx); // Evaluate curve as hermite cubic spline const KeyFrame& leftKey = mKeyframes[leftKeyIdx]; const KeyFrame& rightKey = mKeyframes[rightKeyIdx]; float length = rightKey.time - leftKey.time; float t; T leftTangent; T rightTangent; // TODO - Remove zero init for vectors/quaternions by default if (Math::approxEquals(length, 0.0f)) { t = 0.0f; leftTangent = T(); rightTangent = T(); } else { // Scale from arbitrary range to [0, 1] t = (time - leftKey.time) / length; leftTangent = leftKey.outTangent * length; rightTangent = rightKey.inTangent * length; } T output = Math::cubicHermite(t, leftKey.value, rightKey.value, leftTangent, rightTangent); setStepValue(leftKey, rightKey, output); return output; }
UINT32 TAnimationCurve<T>::findKey(float time) { UINT32 leftKeyIdx; UINT32 rightKeyIdx; findKeys(time, leftKeyIdx, rightKeyIdx); const KeyFrame& leftKey = mKeyframes[leftKeyIdx]; const KeyFrame& rightKey = mKeyframes[rightKeyIdx]; if (Math::abs(leftKey.time - time) <= Math::abs(rightKey.time - time)) return leftKeyIdx; return rightKeyIdx; }
void TAnimationCurve<T>::findKeys(float time, const TCurveCache<T>& animInstance, UINT32& leftKey, UINT32& rightKey) const { // Check nearby keys first if there is cached data if (animInstance.cachedKey != (UINT32)-1) { const KeyFrame& curKey = mKeyframes[animInstance.cachedKey]; if (time >= curKey.time) { UINT32 end = std::min((UINT32)mKeyframes.size(), animInstance.cachedKey + CACHE_LOOKAHEAD + 1); for (UINT32 i = animInstance.cachedKey + 1; i < end; i++) { const KeyFrame& nextKey = mKeyframes[i]; if (time < nextKey.time) { leftKey = i - 1; rightKey = i; animInstance.cachedKey = leftKey; return; } } } else { UINT32 start = (UINT32)std::max(0, (INT32)animInstance.cachedKey - (INT32)CACHE_LOOKAHEAD); for(UINT32 i = start; i < animInstance.cachedKey; i++) { const KeyFrame& prevKey = mKeyframes[i]; if (time >= prevKey.time) { leftKey = i; rightKey = i + 1; animInstance.cachedKey = leftKey; return; } } } } // Cannot find nearby ones, search all keys findKeys(time, leftKey, rightKey); animInstance.cachedKey = leftKey; }
TKeyframe<T> TAnimationCurve<T>::evaluateKey(float time, bool loop) const { if (mKeyframes.size() == 0) return TKeyframe<T>(); AnimationUtility::wrapTime(time, mStart, mEnd, loop); UINT32 leftKeyIdx; UINT32 rightKeyIdx; findKeys(time, leftKeyIdx, rightKeyIdx); const KeyFrame& leftKey = mKeyframes[leftKeyIdx]; const KeyFrame& rightKey = mKeyframes[rightKeyIdx]; return evaluateKey(leftKey, rightKey, time); }
T TAnimationCurve<T>::evaluate(float time, bool loop) const { if (mKeyframes.size() == 0) return T(); AnimationUtility::wrapTime(time, mStart, mEnd, loop); UINT32 leftKeyIdx; UINT32 rightKeyIdx; findKeys(time, leftKeyIdx, rightKeyIdx); // Evaluate curve as hermite cubic spline const KeyFrame& leftKey = mKeyframes[leftKeyIdx]; const KeyFrame& rightKey = mKeyframes[rightKeyIdx]; float length = rightKey.time - leftKey.time; float t; T leftTangent; T rightTangent; // TODO - Remove zero init for vectors/quaternions by default if (Math::approxEquals(length, 0.0f)) { t = 0.0f; leftTangent = T(); rightTangent = T(); } else { // Scale from arbitrary range to [0, 1] t = (time - leftKey.time) / length; leftTangent = leftKey.outTangent * length; rightTangent = rightKey.inTangent * length; } T output = Math::cubicHermite(t, leftKey.value, rightKey.value, leftTangent, rightTangent); setStepValue(leftKey, rightKey, output); return output; }
T TAnimationCurve<T>::evaluate(float time, const TCurveCache<T>& cache, bool loop) const { if (mKeyframes.size() == 0) return T(); // Wrap time if looping if(loop) { if (time < mStart) time = time - std::floor(time / mLength) * mLength; else if (time > mEnd) time = time - std::floor(time / mLength) * mLength; } // If time is within cache, evaluate it directly if (time >= cache.cachedCurveStart && time < cache.cachedCurveEnd) return evaluateCache(time, cache); // Clamp to start, cache constant of the first key and return if(time < mStart) { cache.cachedCurveStart = -std::numeric_limits<float>::infinity(); cache.cachedCurveEnd = mStart; cache.cachedKey = 0; cache.cachedCubicCoefficients[0] = T(); cache.cachedCubicCoefficients[1] = T(); cache.cachedCubicCoefficients[2] = T(); cache.cachedCubicCoefficients[3] = mKeyframes[0].value; return mKeyframes[0].value; } if(time > mEnd) // Clamp to end, cache constant of the final key and return { UINT32 lastKey = (UINT32)mKeyframes.size() - 1; cache.cachedCurveStart = mEnd; cache.cachedCurveEnd = std::numeric_limits<float>::infinity(); cache.cachedKey = lastKey; cache.cachedCubicCoefficients[0] = T(); cache.cachedCubicCoefficients[1] = T(); cache.cachedCubicCoefficients[2] = T(); cache.cachedCubicCoefficients[3] = mKeyframes[lastKey].value; return mKeyframes[lastKey].value; } // Since our value is not in cache, search for the valid pair of keys of interpolate UINT32 leftKeyIdx; UINT32 rightKeyIdx; findKeys(time, cache, leftKeyIdx, rightKeyIdx); // Calculate cubic hermite curve coefficients so we can store them in cache const KeyFrame& leftKey = mKeyframes[leftKeyIdx]; const KeyFrame& rightKey = mKeyframes[rightKeyIdx]; cache.cachedCurveStart = leftKey.time; cache.cachedCurveEnd = rightKey.time; float length = rightKey.time - leftKey.time; Math::cubicHermiteCoefficients(leftKey.value, rightKey.value, leftKey.outTangent, rightKey.inTangent, length, cache.cachedCubicCoefficients); setStepCoefficients(leftKey, rightKey, cache.cachedCubicCoefficients); T output = evaluateCache(time, cache); return output; }