Browse Source

Finish animation curve splitting
Added handling for stepped (constant) functions when evaluating animation curves

BearishSun 9 years ago
parent
commit
25f32bd929
2 changed files with 189 additions and 19 deletions
  1. 165 18
      Source/BansheeCore/Source/BsAnimationCurve.cpp
  2. 24 1
      Source/BansheeUtility/Include/BsMath.h

+ 165 - 18
Source/BansheeCore/Source/BsAnimationCurve.cpp

@@ -8,6 +8,120 @@
 
 namespace BansheeEngine
 {
+	/** 
+	 * Checks if any components of the keyframes are constant (step) functions and updates the hermite curve coefficients
+	 * accordingly.
+	 */
+	void setStepCoefficients(const TKeyframe<float>& lhs, const TKeyframe<float>& rhs, float (&coefficients)[4])
+	{
+		if (lhs.outTangent != std::numeric_limits<float>::infinity() &&
+			rhs.inTangent != std::numeric_limits<float>::infinity())
+			return;
+
+		coefficients[0] = 0.0f;
+		coefficients[1] = 0.0f;
+		coefficients[2] = 0.0f;
+		coefficients[3] = lhs.value;
+	}
+
+	void setStepCoefficients(const TKeyframe<Vector3>& lhs, const TKeyframe<Vector3>& rhs, Vector3(&coefficients)[4])
+	{
+		for(UINT32 i = 0; i < 3; i++)
+		{
+			if (lhs.outTangent[i] != std::numeric_limits<float>::infinity() &&
+				rhs.inTangent[i] != std::numeric_limits<float>::infinity())
+				continue;
+
+			coefficients[0][i] = 0.0f;
+			coefficients[1][i] = 0.0f;
+			coefficients[2][i] = 0.0f;
+			coefficients[3][i] = lhs.value[i];
+		}
+	}
+
+	void setStepCoefficients(const TKeyframe<Quaternion>& lhs, const TKeyframe<Quaternion>& rhs, Quaternion(&coefficients)[4])
+	{
+		for (UINT32 i = 0; i < 4; i++)
+		{
+			if (lhs.outTangent[i] != std::numeric_limits<float>::infinity() &&
+				rhs.inTangent[i] != std::numeric_limits<float>::infinity())
+				continue;
+
+			coefficients[0][i] = 0.0f;
+			coefficients[1][i] = 0.0f;
+			coefficients[2][i] = 0.0f;
+			coefficients[3][i] = lhs.value[i];
+		}
+	}
+
+	/** Checks if any components of the keyframes are constant (step) functions and updates the key value. */
+	void setStepValue(const TKeyframe<float>& lhs, const TKeyframe<float>& rhs, float& value)
+	{
+		if (lhs.outTangent != std::numeric_limits<float>::infinity() &&
+			rhs.inTangent != std::numeric_limits<float>::infinity())
+			return;
+
+		value = lhs.value;
+	}
+
+	void setStepValue(const TKeyframe<Vector3>& lhs, const TKeyframe<Vector3>& rhs, Vector3& value)
+	{
+		for (UINT32 i = 0; i < 3; i++)
+		{
+			if (lhs.outTangent[i] != std::numeric_limits<float>::infinity() &&
+				rhs.inTangent[i] != std::numeric_limits<float>::infinity())
+				continue;
+
+			value[i] = lhs.value[i];
+		}
+	}
+
+	void setStepValue(const TKeyframe<Quaternion>& lhs, const TKeyframe<Quaternion>& rhs, Quaternion& value)
+	{
+		for (UINT32 i = 0; i < 4; i++)
+		{
+			if (lhs.outTangent[i] != std::numeric_limits<float>::infinity() &&
+				rhs.inTangent[i] != std::numeric_limits<float>::infinity())
+				continue;
+
+			value[i] = lhs.value[i];
+		}
+	}
+
+	/** Checks if any components of the keyframes are constant (step) functions and updates the key tangent. */
+	void setStepTangent(const TKeyframe<float>& lhs, const TKeyframe<float>& rhs, float& tangent)
+	{
+		if (lhs.outTangent != std::numeric_limits<float>::infinity() &&
+			rhs.inTangent != std::numeric_limits<float>::infinity())
+			return;
+
+		tangent = std::numeric_limits<float>::infinity();
+	}
+
+	void setStepTangent(const TKeyframe<Vector3>& lhs, const TKeyframe<Vector3>& rhs, Vector3& tangent)
+	{
+		for (UINT32 i = 0; i < 3; i++)
+		{
+			if (lhs.outTangent[i] != std::numeric_limits<float>::infinity() &&
+				rhs.inTangent[i] != std::numeric_limits<float>::infinity())
+				continue;
+
+			tangent[i] = std::numeric_limits<float>::infinity();
+		}
+	}
+
+	void setStepTangent(const TKeyframe<Quaternion>& lhs, const TKeyframe<Quaternion>& rhs, Quaternion& tangent)
+	{
+		for (UINT32 i = 0; i < 4; i++)
+		{
+			if (lhs.outTangent[i] != std::numeric_limits<float>::infinity() &&
+				rhs.inTangent[i] != std::numeric_limits<float>::infinity())
+				continue;
+
+			tangent[i] = std::numeric_limits<float>::infinity();
+		}
+	}
+
 	template <class T>
 	const UINT32 TAnimationCurve<T>::CACHE_LOOKAHEAD = 3;
 
@@ -76,9 +190,9 @@ namespace BansheeEngine
 			animInstance.cachedCurveStart = -std::numeric_limits<float>::infinity();
 			animInstance.cachedCurveEnd = mStart;
 			animInstance.cachedKey = 0;
-			animInstance.cachedCubicCoefficients[0] = 0.0f;
-			animInstance.cachedCubicCoefficients[1] = 0.0f;
-			animInstance.cachedCubicCoefficients[2] = 0.0f;
+			animInstance.cachedCubicCoefficients[0] = T();
+			animInstance.cachedCubicCoefficients[1] = T();
+			animInstance.cachedCubicCoefficients[2] = T();
 			animInstance.cachedCubicCoefficients[3] = mKeyframes[0].value;
 
 			return mKeyframes[0].value;
@@ -91,9 +205,9 @@ namespace BansheeEngine
 			animInstance.cachedCurveStart = mEnd;
 			animInstance.cachedCurveEnd = std::numeric_limits<float>::infinity();
 			animInstance.cachedKey = lastKey;
-			animInstance.cachedCubicCoefficients[0] = 0.0f;
-			animInstance.cachedCubicCoefficients[1] = 0.0f;
-			animInstance.cachedCubicCoefficients[2] = 0.0f;
+			animInstance.cachedCubicCoefficients[0] = T();
+			animInstance.cachedCubicCoefficients[1] = T();
+			animInstance.cachedCubicCoefficients[2] = T();
 			animInstance.cachedCubicCoefficients[3] = mKeyframes[lastKey].value;
 
 			return mKeyframes[lastKey].value;
@@ -109,16 +223,16 @@ namespace BansheeEngine
 		const KeyFrame& leftKey = mKeyframes[leftKeyIdx];
 		const KeyFrame& rightKey = mKeyframes[rightKeyIdx];
 
-		float length = rightKey.time - leftKey.time;
-		
 		animInstance.cachedCurveStart = leftKey.time;
 		animInstance.cachedCurveEnd = rightKey.time;
+
+		float length = rightKey.time - leftKey.time;
 		Math::cubicHermiteCoefficients(leftKey.value, rightKey.value, leftKey.outTangent, rightKey.inTangent, length,
 			animInstance.cachedCubicCoefficients);
-		// TODO - Handle stepped curve - If tangents are infinite assume constant value from left key is used
 
-		T output = evaluateCache(animInstance);
+		setStepCoefficients(leftKey, rightKey, animInstance.cachedCubicCoefficients);
 
+		T output = evaluateCache(animInstance);
 		return output;
 	}
 
@@ -156,11 +270,11 @@ namespace BansheeEngine
 		const KeyFrame& rightKey = mKeyframes[rightKeyIdx];
 
 		float length = rightKey.time - leftKey.time;
-		float t = (time - leftKey.time) / length;
+		float t;
 		T leftTangent;
 		T rightTangent; // TODO - Remove zero init for vectors/quaternions by default
 
-		if (Math::approxEquals(t, 0.0f))
+		if (Math::approxEquals(length, 0.0f))
 		{
 			t = 0.0f;
 			leftTangent = T();
@@ -169,11 +283,15 @@ namespace BansheeEngine
 		else
 		{
 			// Resize tangents since we're not evaluating the curve over unit range
+			t = (time - leftKey.time) / length;
 			leftTangent = leftKey.outTangent * length;
 			rightTangent = rightKey.inTangent * length;
 		}
 
-		return Math::cubicHermite(t, leftKey.value, rightKey.value, leftTangent, rightTangent);
+		T output = Math::cubicHermite(t, leftKey.value, rightKey.value, leftTangent, rightTangent);
+		setStepValue(leftKey, rightKey, output);
+
+		return output;
 	}
 
 	template <class T>
@@ -280,10 +398,34 @@ namespace BansheeEngine
 	TKeyframe<T> TAnimationCurve<T>::evaluateKey(const KeyFrame& lhs, const KeyFrame& rhs, float time)
 	{
 		float length = rhs.time - lhs.time;
-		float t = (time - lhs.time) / length;
+		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
+		{
+			// Resize tangents since we're not evaluating the curve over unit range
+			t = (time - lhs.time) / length;
+			leftTangent = lhs.outTangent * length;
+			rightTangent = rhs.inTangent * length;
+		}
 
 		TKeyframe<T> output;
-		// TODO
+		output.time = time;
+		output.value = Math::cubicHermite(t, lhs.value, rhs.value, leftTangent, rightTangent);
+		output.inTangent = Math::cubicHermiteD1(t, lhs.value, rhs.value, leftTangent, rightTangent);
+		
+		setStepValue(lhs, rhs, output.value);
+		setStepTangent(lhs, rhs, output.inTangent);
+
+		output.outTangent = output.inTangent;
 
 		return output;
 	}
@@ -307,13 +449,18 @@ namespace BansheeEngine
 		const KeyFrame& startKey = mKeyframes[startKeyIdx];
 		const KeyFrame& endKey = mKeyframes[endKeyIdx];
 
-		if(!Math::approxEquals(startKey.time, start))
+		if (!Math::approxEquals(startKey.time, start))
 		{
 			keyFrames.push_back(evaluateKey(startKey, mKeyframes[startKeyIdx + 1], start));
 
-			if(start > startKey.time)
+			if (start > startKey.time)
 				startKeyIdx++;
 		}
+		else
+		{
+			keyFrames.push_back(startKey);
+			startKeyIdx++;
+		}
 
 		if(!Math::approxEquals(endKey.time, end))
 		{
@@ -323,7 +470,7 @@ namespace BansheeEngine
 				endKeyIdx--;
 		}
 
-		keyFrames.insert(keyFrames.begin(), mKeyframes.begin() + startKeyIdx, mKeyframes.begin() + endKeyIdx + 1);
+		keyFrames.insert(keyFrames.begin() + 1, mKeyframes.begin() + startKeyIdx, mKeyframes.begin() + endKeyIdx + 1);
 
 		for (auto& entry : keyFrames)
 			entry.time -= start;

+ 24 - 1
Source/BansheeUtility/Include/BsMath.h

@@ -614,7 +614,30 @@ namespace BansheeEngine
 			float c = -2 * t3 + 3 * t2;
 			float d = t3 - t2;
 
-			return a * pointA + b * tangentA + c * tangentB + d * pointB;
+			return a * pointA + b * tangentA + c * pointB + d * tangentB;
+		}
+
+		/**
+		 * Evaluates the first derivative of a cubic Hermite curve at a specific point.
+		 *
+		 * @param[in]	t			Parameter that at which to evaluate the curve, in range [0, 1].
+		 * @param[in]	pointA		Starting point (at t=0).
+		 * @param[in]	pointB		Ending point (at t=1).
+		 * @param[in]	tangentA	Starting tangent (at t=0).
+		 * @param[in]	tangentB	Ending tangent (at t = 1).
+		 * @return					Evaluated value at @p t.
+		 */
+		template<class T>
+		static T cubicHermiteD1(float t, const T& pointA, const T& pointB, const T& tangentA, const T& tangentB)
+		{
+			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;
+
+			return a * pointA + b * tangentA + c * pointB + d * tangentB;
 		}
 
 		/**