|
|
@@ -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);
|