// Cinema Suite
using UnityEngine;
namespace CinemaDirector
{
///
/// A helper class for Unity's AnimationCurve class.
/// Made to account for tangent mode of keys when adding/changing/removing keys.
///
public static class AnimationCurveHelper
{
///
/// Add a new key to an AnimationCurve.
/// Ensures the integrity of other key's tangent modes.
///
/// The existing AnimationCurve.
/// The new keyframe
/// The index of the newly added key.
public static int AddKey(AnimationCurve curve, Keyframe keyframe)
{
if (curve.length == 0)
{
return curve.AddKey(keyframe);
}
else if (curve.length == 1)
{
// Save the existing keyframe data. (Unity changes the tangent info).
Keyframe temp = curve[0];
int newIndex = curve.AddKey(keyframe);
if(newIndex == -1)
{
return 0;
}
else if(newIndex == 0)
{
curve.MoveKey(1, temp);
}
else
{
curve.MoveKey(0, temp);
}
return newIndex;
}
else
{
Keyframe left = new Keyframe();
Keyframe right = new Keyframe();
for (int i = 0; i < curve.length - 1; i++)
{
Keyframe l = curve[i];
Keyframe r = curve[i + 1];
if (l.time < keyframe.time && keyframe.time < r.time)
{
left = l;
right = r;
}
}
int index = curve.AddKey(keyframe);
// Handle left neighbour.
if (index > 0)
{
// Restore the saved data.
curve.MoveKey(index - 1, left);
// Update tangent data based on tangent mode.
int tangentMode = curve[index - 1].tangentMode;
if (IsAuto(tangentMode))
{
curve.SmoothTangents(index - 1, 0);
}
if (IsBroken(tangentMode))
{
if (IsRightLinear(tangentMode))
{
SetKeyRightLinear(curve, index - 1);
}
}
}
// Handle the Right neighbour.
if (index < curve.length - 1)
{
// Restore the saved data.
curve.MoveKey(index + 1, right);
// Update tangent data based on tangent mode.
int tangentMode = curve[index + 1].tangentMode;
if (IsAuto(tangentMode))
{
curve.SmoothTangents(index + 1, 0);
}
if (IsBroken(tangentMode))
{
if (IsLeftLinear(tangentMode))
{
SetKeyLeftLinear(curve, index + 1);
}
}
}
return index;
}
}
///
/// Move/Change an existing key in an AnimationCurve.
/// Maintains TangentMode and updates neighbours.
///
/// The existing AnimationCurve.
/// The index of the current Keyframe.
/// The new Keyframe data.
/// The index of the Keyframe.
public static int MoveKey(AnimationCurve curve, int index, Keyframe keyframe)
{
// Save the tangent mode.
Keyframe old = curve[index];
keyframe.tangentMode = old.tangentMode;
int newIndex = curve.MoveKey(index, keyframe);
// Respect the tangentMode and update as necessary.
if (IsAuto(keyframe.tangentMode))
{
curve.SmoothTangents(newIndex, 0);
}
else if (IsBroken(keyframe.tangentMode))
{
if (IsLeftLinear(keyframe.tangentMode))
{
SetKeyLeftLinear(curve, newIndex);
}
if (IsRightLinear(keyframe.tangentMode))
{
SetKeyRightLinear(curve, newIndex);
}
}
// update the left neighbour
if (newIndex > 0)
{
// Update tangent data based on tangent mode.
int tangentMode = curve[newIndex - 1].tangentMode;
if (IsAuto(tangentMode))
{
curve.SmoothTangents(newIndex - 1, 0);
}
if (IsBroken(tangentMode))
{
if (IsRightLinear(tangentMode))
{
SetKeyRightLinear(curve, newIndex - 1);
}
}
}
// update the right neighbour
if (newIndex < curve.length - 1)
{
// Update tangent data based on tangent mode.
int tangentMode = curve[newIndex + 1].tangentMode;
if (IsAuto(tangentMode))
{
curve.SmoothTangents(newIndex + 1, 0);
}
if (IsBroken(tangentMode))
{
if (IsLeftLinear(tangentMode))
{
SetKeyLeftLinear(curve, newIndex + 1);
}
}
}
return newIndex;
}
///
/// Remove a key from an AnimationCurve.
///
/// The existing AnimationCurve.
/// The index of the Key to be removed.
public static void RemoveKey(AnimationCurve curve, int index)
{
curve.RemoveKey(index);
// Update left neighbour.
if (index > 0)
{
// Update tangent data based on tangent mode.
int tangentMode = curve[index-1].tangentMode;
if (IsAuto(tangentMode))
{
curve.SmoothTangents(index - 1, 0);
}
if (IsBroken(tangentMode))
{
if (IsRightLinear(tangentMode))
{
SetKeyRightLinear(curve, index - 1);
}
}
}
// Update right neighbour.
if (index < curve.length)
{
// Update tangent data based on tangent mode.
int tangentMode = curve[index].tangentMode;
if (IsAuto(tangentMode))
{
curve.SmoothTangents(index, 0);
}
if (IsBroken(tangentMode))
{
if (IsLeftLinear(tangentMode))
{
SetKeyLeftLinear(curve, index);
}
}
}
}
///
/// Set the indexed key of an AnimationCurve to RightLinear.
///
/// The curve to change.
/// The index of the key to set to RightLinear.
public static void SetKeyRightLinear(AnimationCurve curve, int index)
{
Keyframe kf = curve[index];
float tangentValue = kf.outTangent;
if (index < curve.length - 1)
{
Keyframe next = curve[index + 1];
tangentValue = (next.value - kf.value) / (next.time - kf.time);
}
Keyframe newKeyframe = new Keyframe(kf.time, kf.value, kf.inTangent, tangentValue);
// Get current tangent mode.
int leftTangent = (IsAuto(kf.tangentMode) || kf.tangentMode == 0) ? 0 : (kf.tangentMode % 8) - 1;
newKeyframe.tangentMode = leftTangent + 16 + 1;
curve.MoveKey(index, newKeyframe);
}
///
/// Set the indexed key of an AnimationCurve to LeftLinear.
///
/// The curve to change.
/// The index of the key to set to LeftLinear.
public static void SetKeyLeftLinear(AnimationCurve curve, int index)
{
Keyframe kf = curve[index];
float tangentValue = kf.inTangent;
if (index > 0)
{
Keyframe prev = curve[index - 1];
tangentValue = (kf.value - prev.value) / (kf.time - prev.time);
}
Keyframe newKeyframe = new Keyframe(kf.time, kf.value, tangentValue, kf.outTangent);
int rightTangent = kf.tangentMode > 16 ? (kf.tangentMode / 8) * 8 : 0;
newKeyframe.tangentMode = rightTangent + 1 + 4;
curve.MoveKey(index, newKeyframe);
}
///
/// Is the TangentMode Auto.
///
/// The tangentMode value.
/// True if set to auto.
public static bool IsAuto(int tangentMode)
{
return tangentMode == 10;
}
///
/// Is the TangentMode Broken.
///
/// The tangentMode value.
/// True if set to Broken.
public static bool IsBroken(int tangentMode)
{
return (tangentMode % 2) == 1;
}
///
/// Is the TangentMode RightLinear.
///
/// The tangentMode value.
/// True if the right tangent mode is set to linear.
public static bool IsRightLinear(int tangentMode)
{
return (tangentMode / 8) == 2;
}
///
/// Is the TangentMode LeftLinear.
///
/// The tangentMode value.
/// True if the left tangent mode is set to linear.
public static bool IsLeftLinear(int tangentMode)
{
return IsBroken(tangentMode) && (tangentMode % 8) == 5;
}
}
}