فهرست منبع

Fixing euler <-> quaternion curve conversion tangent calculation
Animation properties can now be removed from a clip
Extra empty keyframes are no longer inserted when saving animation curves

BearishSun 9 سال پیش
والد
کامیت
55a6d68e29

+ 1 - 0
Source/BansheeCore/Source/BsAnimationManager.cpp

@@ -169,6 +169,7 @@ namespace BansheeEngine
 					{
 						const TAnimationCurve<Quaternion>& curve = state.curves->rotation[curveIdx].curve;
 						anim->sceneObjectPose.rotations[curveIdx] = curve.evaluate(state.time, state.rotationCaches[curveIdx], state.loop);
+						anim->sceneObjectPose.rotations[curveIdx].normalize();
 					}
 					else
 						anim->sceneObjectPose.rotations[curveIdx] = Quaternion::ZERO;

+ 55 - 11
Source/BansheeCore/Source/BsAnimationUtility.cpp

@@ -6,6 +6,37 @@
 
 namespace BansheeEngine
 {
+	void setStepTangent(const TKeyframe<Vector3>& lhsIn, const TKeyframe<Vector3>& rhsIn, 
+		TKeyframe<Quaternion>& lhsOut, TKeyframe<Quaternion>& rhsOut)
+	{
+		for (UINT32 i = 0; i < 3; i++)
+		{
+			if (lhsIn.outTangent[i] != std::numeric_limits<float>::infinity() &&
+				rhsIn.inTangent[i] != std::numeric_limits<float>::infinity())
+				continue;
+
+			lhsOut.outTangent[i] = std::numeric_limits<float>::infinity();
+			rhsOut.inTangent[i] = std::numeric_limits<float>::infinity();
+		}
+	}
+
+	void setStepTangent(const TKeyframe<Quaternion>& lhsIn, const TKeyframe<Quaternion>& rhsIn, 
+		TKeyframe<Vector3>& lhsOut, TKeyframe<Vector3>& rhsOut)
+	{
+		for (UINT32 i = 0; i < 4; i++)
+		{
+			if (lhsIn.outTangent[i] != std::numeric_limits<float>::infinity() &&
+				rhsIn.inTangent[i] != std::numeric_limits<float>::infinity())
+				continue;
+
+			if (i < 3)
+			{
+				lhsOut.outTangent[i] = std::numeric_limits<float>::infinity();
+				rhsOut.inTangent[i] = std::numeric_limits<float>::infinity();
+			}
+		}
+	}
+
 	void AnimationUtility::wrapTime(float& time, float start, float end, bool loop)
 	{
 		float length = end - start;
@@ -35,9 +66,8 @@ namespace BansheeEngine
 		// a curve.
 		const float FIT_TIME = 0.001f;
 
-		auto eulerToQuaternion = [&](INT32 keyIdx, float time, const Quaternion& lastQuat)
+		auto eulerToQuaternion = [&](INT32 keyIdx, Vector3& angles, const Quaternion& lastQuat)
 		{
-			Vector3 angles = eulerCurve.evaluate(time, false);
 			Quaternion quat(
 				Degree(angles.x),
 				Degree(angles.y),
@@ -62,7 +92,8 @@ namespace BansheeEngine
 		for (INT32 i = 0; i < numKeys; i++)
 		{
 			float time = eulerCurve.getKeyFrame(i).time;
-			Quaternion quat = eulerToQuaternion(i, time, lastQuat);
+			Vector3 angles = eulerCurve.getKeyFrame(i).value;
+			Quaternion quat = eulerToQuaternion(i, angles, lastQuat);
 
 			quatKeyframes[i].time = time;
 			quatKeyframes[i].value = quat;
@@ -79,16 +110,23 @@ namespace BansheeEngine
 			TKeyframe<Quaternion>& currentKey = quatKeyframes[i];
 			TKeyframe<Quaternion>& nextKey = quatKeyframes[i + 1];
 
+			const TKeyframe<Vector3>& currentEulerKey = eulerCurve.getKeyFrame(i);
+			const TKeyframe<Vector3>& nextEulerKey = eulerCurve.getKeyFrame(i + 1);
+
 			float dt = nextKey.time - currentKey.time;
 			float startFitTime = currentKey.time + dt * FIT_TIME;
 			float endFitTime = currentKey.time + dt * (1.0f - FIT_TIME);
 
-			Quaternion startFitValue = eulerToQuaternion(i, startFitTime, currentKey.value);
-			Quaternion endFitValue = eulerToQuaternion(i, endFitTime, startFitValue);
+			Vector3 anglesStart = eulerCurve.evaluate(startFitTime, false);
+			Vector3 anglesEnd = eulerCurve.evaluate(endFitTime, false);
+			Quaternion startFitValue = eulerToQuaternion(i, anglesStart, currentKey.value);
+			Quaternion endFitValue = eulerToQuaternion(i, anglesEnd, startFitValue);
 
 			float invFitTime = 1.0f / (dt * FIT_TIME);
 			currentKey.outTangent = (startFitValue - currentKey.value) * invFitTime;
 			nextKey.inTangent = (nextKey.value - endFitValue) * invFitTime;
+
+			setStepTangent(currentEulerKey, nextEulerKey, currentKey, nextKey);
 		}
 
 		return TAnimationCurve<Quaternion>(quatKeyframes);
@@ -100,10 +138,8 @@ namespace BansheeEngine
 		// a curve.
 		const float FIT_TIME = 0.001f;
 
-		auto quaternionToEuler = [&](float time)
+		auto quaternionToEuler = [&](const Quaternion& quat)
 		{
-			Quaternion quat = quatCurve.evaluate(time, false);
-
 			Radian x, y, z;
 			quat.toEulerAngles(x, y, z);
 
@@ -123,7 +159,8 @@ namespace BansheeEngine
 		for (INT32 i = 0; i < numKeys; i++)
 		{
 			float time = quatCurve.getKeyFrame(i).time;
-			Vector3 euler = quaternionToEuler(time);
+			Quaternion quat = quatCurve.getKeyFrame(i).value;
+			Vector3 euler = quaternionToEuler(quat);
 
 			eulerKeyframes[i].time = time;
 			eulerKeyframes[i].value = euler;
@@ -138,12 +175,17 @@ namespace BansheeEngine
 			TKeyframe<Vector3>& currentKey = eulerKeyframes[i];
 			TKeyframe<Vector3>& nextKey = eulerKeyframes[i + 1];
 
+			const TKeyframe<Quaternion>& currentQuatKey = quatCurve.getKeyFrame(i);
+			const TKeyframe<Quaternion>& nextQuatKey = quatCurve.getKeyFrame(i + 1);
+
 			float dt = nextKey.time - currentKey.time;
 			float startFitTime = currentKey.time + dt * FIT_TIME;
 			float endFitTime = currentKey.time + dt * (1.0f - FIT_TIME);
 
-			Vector3 startFitValue = quaternionToEuler(startFitTime);
-			Vector3 endFitValue = quaternionToEuler(endFitTime);
+			Quaternion startQuat = Quaternion::normalize(quatCurve.evaluate(startFitTime, false));
+			Quaternion endQuat = Quaternion::normalize(quatCurve.evaluate(endFitTime, false));
+			Vector3 startFitValue = quaternionToEuler(startQuat);
+			Vector3 endFitValue = quaternionToEuler(endQuat);
 
 			// If fit values rotate for more than 180 degrees, wrap them so they use the shortest path
 			for(int j = 0; j < 3; j++)
@@ -155,6 +197,8 @@ namespace BansheeEngine
 			float invFitTime = 1.0f / (dt * FIT_TIME);
 			currentKey.outTangent = (startFitValue - currentKey.value) * invFitTime;
 			nextKey.inTangent = (nextKey.value - endFitValue) * invFitTime;
+
+			setStepTangent(currentQuatKey, nextQuatKey, currentKey, nextKey);
 		}
 
 		return TAnimationCurve<Vector3>(eulerKeyframes);

+ 0 - 3
Source/MBansheeEditor/Windows/AnimationWindow.cs

@@ -907,10 +907,7 @@ namespace BansheeEditor
         private void RemoveSelectedFields()
         {
             for (int i = 0; i < selectedFields.Count; i++)
-            {
-                selectedFields.Remove(selectedFields[i]);
                 clipInfo.curves.Remove(GetSubPathParent(selectedFields[i]));
-            }
 
             UpdateCurveColors();
             UpdateDisplayedFields();

+ 6 - 1
Source/SBansheeEngine/Source/BsScriptAnimationCurves.cpp

@@ -64,6 +64,9 @@ namespace BansheeEngine
 				quatRotation.name = eulerRotation.name;
 				quatRotation.curve = AnimationUtility::eulerToQuaternionCurve(eulerRotation.curve);
 
+				// DEBUG ONLY
+				TAnimationCurve<Vector3> eulerRotation2 = AnimationUtility::quaternionToEulerCurve(quatRotation.curve);
+
 				output->rotation.push_back(quatRotation);
 			}
 		}
@@ -219,6 +222,7 @@ namespace BansheeEngine
 
 		// Populate keyframe values
 		Vector<TKeyframe<Vector3>> keyframeList(keyFrames.size());
+		UINT32 idx = 0;
 		for(auto& entry : keyFrames)
 		{
 			TKeyframe<Vector3>& keyFrame = entry.second;
@@ -231,7 +235,8 @@ namespace BansheeEngine
 				keyFrame.outTangent[j] = currentKey.outTangent;
 			}
 
-			keyframeList.push_back(keyFrame);
+			keyframeList[idx] = keyFrame;
+			idx++;
 		}
 
 		output.curve = TAnimationCurve<Vector3>(keyframeList);