Просмотр исходного кода

Bugfix: Incorrectly animated meshes that had animation curves of varying lengths
- All animation curves will now made to have the length of the animation clip
- Animation resampling tweaked so it doesn't skip the final keyframe

BearishSun 8 лет назад
Родитель
Сommit
ab0602fb87

+ 74 - 31
Source/BansheeFBXImporter/BsFBXImporter.cpp

@@ -2004,7 +2004,7 @@ namespace bs
 
 	template<class T, int C>
 	TAnimationCurve<T> FBXImporter::importCurve(FbxAnimCurve*(&fbxCurve)[C], float (&defaultValues)[C],
-		FBXImportOptions& importOptions, float start, float end)
+		FBXImportOptions& importOptions, float clipStart, float clipEnd)
 	{
 		int keyCounts[C];
 		for (int i = 0; i < C; i++)
@@ -2027,12 +2027,58 @@ namespace bs
 			}
 		}
 
+		// Determine curve length
+		float curveStart = std::numeric_limits<float>::infinity();
+		float curveEnd = -std::numeric_limits<float>::infinity();
+
+		for (INT32 i = 0; i < C; i++)
+		{
+			if(fbxCurve[i] == nullptr)
+			{
+				curveStart = std::min(0.0f, curveStart);
+				curveEnd = std::max(0.0f, curveEnd);
+
+				continue;
+			}
+
+			int keyCount = keyCounts[i];
+			for (INT32 j = 0; j < keyCount; j++)
+			{
+				FbxTime fbxTime = fbxCurve[i]->KeyGetTime(j);
+				float time = (float)fbxTime.GetSecondDouble();
+
+				curveStart = std::min(time, curveStart);
+				curveEnd = std::max(time, curveEnd);
+			}
+		}
+
 		// Read keys directly
 		if(!importOptions.animResample && !forceResample)
 		{
 			bool foundMismatch = false;
 			int keyCount = keyCounts[0];
 			Vector<TKeyframe<T>> keyframes;
+
+			// All curves must match the length of the clip, so add a keyframe if first keyframe doesn't match the start time
+			if(curveStart > clipStart)
+			{
+				keyframes.push_back(TKeyframe<T>());
+				TKeyframe<T>& keyFrame = keyframes.back();
+
+				keyFrame.time = clipStart;
+
+				FbxTime fbxSampleTime;
+				fbxSampleTime.SetSecondDouble(clipStart);
+
+				for (int j = 0; j < C; j++)
+				{
+					setKeyframeValues(keyFrame, j,
+						fbxCurve[j]->Evaluate(fbxSampleTime),
+						fbxCurve[j]->EvaluateLeftDerivative(fbxSampleTime),
+						fbxCurve[j]->EvaluateRightDerivative(fbxSampleTime));
+				}
+			}
+
 			for (int i = 0; i < keyCount; i++)
 			{
 				FbxTime fbxTime = fbxCurve[0]->KeyGetTime(i);
@@ -2054,7 +2100,7 @@ namespace bs
 				if(foundMismatch)
 					break;
 
-				if (time < start || time > end)
+				if (time < clipStart || time > clipEnd)
 					continue;
 
 				keyframes.push_back(TKeyframe<T>());
@@ -2071,50 +2117,47 @@ namespace bs
 				}
 			}
 
+			// All curves must match the length of the clip, so add a keyframe if last keyframe doesn't match the end time
+			if(curveEnd < clipEnd)
+			{
+				keyframes.push_back(TKeyframe<T>());
+				TKeyframe<T>& keyFrame = keyframes.back();
+
+				keyFrame.time = clipEnd;
+
+				FbxTime fbxSampleTime;
+				fbxSampleTime.SetSecondDouble(clipEnd);
+
+				for (int j = 0; j < C; j++)
+				{
+					setKeyframeValues(keyFrame, j,
+						fbxCurve[j]->Evaluate(fbxSampleTime),
+						fbxCurve[j]->EvaluateLeftDerivative(fbxSampleTime),
+						fbxCurve[j]->EvaluateRightDerivative(fbxSampleTime));
+				}
+			}
+
 			if (!foundMismatch)
 				return TAnimationCurve<T>(keyframes);
 			else
 				forceResample = true;
 		}
 
+		// Resample keys
 		if (!importOptions.animResample && forceResample)
 			LOGWRN("Animation has different keyframes for different curve components, forcing resampling.");
 
-		// Resample keys
-		float curveStart = std::numeric_limits<float>::infinity();
-		float curveEnd = -std::numeric_limits<float>::infinity();
-
-		for (INT32 i = 0; i < C; i++)
-		{
-			if(fbxCurve[i] == nullptr)
-			{
-				curveStart = std::min(0.0f, curveStart);
-				curveEnd = std::max(0.0f, curveEnd);
-
-				continue;
-			}
-
-			int keyCount = keyCounts[i];
-			for (INT32 j = 0; j < keyCount; j++)
-			{
-				FbxTime fbxTime = fbxCurve[i]->KeyGetTime(j);
-				float time = (float)fbxTime.GetSecondDouble();
-
-				curveStart = std::min(time, curveStart);
-				curveEnd = std::max(time, curveEnd);
-			}
-		}
-
-		curveStart = Math::clamp(curveStart, start, end);
-		curveEnd = Math::clamp(curveEnd, start, end);
+		// Make sure to resample along the length of the entire clip
+		curveStart = std::min(curveStart, clipStart);
+		curveEnd = std::max(curveEnd, clipEnd);
 
 		float curveLength = curveEnd - curveStart;
-		INT32 numSamples = Math::ceilToInt(curveLength / importOptions.animSampleRate);
+		INT32 numSamples = Math::ceilToInt(curveLength / importOptions.animSampleRate) + 1;
 
 		// We don't use the exact provided sample rate but instead modify it slightly so it
 		// completely covers the curve range including start/end points while maintaining
 		// constant time step between keyframes.
-		float dt = curveLength / (float)numSamples; 
+		float dt = curveLength / (float)(numSamples - 1); 
 
 		INT32 lastKeyframe[] = { 0, 0, 0 };
 		INT32 lastLeftTangent[] = { 0, 0, 0 };

+ 1 - 1
Source/BansheeFBXImporter/BsFBXImporter.h

@@ -105,7 +105,7 @@ namespace bs
 		/**	Converts a single FBX animation curve into an engine curve format, resampling it if necessary. */
 		template<class T, int C>
 		TAnimationCurve<T> importCurve(FbxAnimCurve*(&fbxCurve)[C], float(&defaultValues)[C], 
-			FBXImportOptions& importOptions, float start, float end);
+			FBXImportOptions& importOptions, float clipStart, float clipEnd);
 
 		/** Converts FBX animation clips into engine-ready animation curve format. */
 		void convertAnimations(const Vector<FBXAnimationClip>& clips, const Vector<AnimationSplitInfo>& splits,