You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
427 lines
14 KiB
427 lines
14 KiB
using System.Collections.Generic; |
|
using System.Linq; |
|
using UnityEngine; |
|
using UnityEditor; |
|
using UnityEditorInternal; |
|
|
|
namespace UnityEditor.Timeline |
|
{ |
|
struct CurveBindingPair |
|
{ |
|
public EditorCurveBinding binding; |
|
public AnimationCurve curve; |
|
public ObjectReferenceKeyframe[] objectCurve; |
|
} |
|
|
|
class CurveBindingGroup |
|
{ |
|
public CurveBindingPair[] curveBindingPairs { get; set; } |
|
public Vector2 timeRange { get; set; } |
|
public Vector2 valueRange { get; set; } |
|
|
|
public bool isFloatCurve |
|
{ |
|
get |
|
{ |
|
return curveBindingPairs != null && curveBindingPairs.Length > 0 && |
|
curveBindingPairs[0].curve != null; |
|
} |
|
} |
|
|
|
public bool isObjectCurve |
|
{ |
|
get |
|
{ |
|
return curveBindingPairs != null && curveBindingPairs.Length > 0 && |
|
curveBindingPairs[0].objectCurve != null; |
|
} |
|
} |
|
|
|
public int count |
|
{ |
|
get |
|
{ |
|
if (curveBindingPairs == null) |
|
return 0; |
|
return curveBindingPairs.Length; |
|
} |
|
} |
|
} |
|
|
|
class AnimationClipCurveInfo |
|
{ |
|
bool m_CurveDirty = true; |
|
bool m_KeysDirty = true; |
|
|
|
public bool dirty |
|
{ |
|
get { return m_CurveDirty; } |
|
set |
|
{ |
|
m_CurveDirty = value; |
|
if (m_CurveDirty) |
|
{ |
|
m_KeysDirty = true; |
|
if (m_groupings != null) |
|
m_groupings.Clear(); |
|
} |
|
} |
|
} |
|
|
|
public AnimationCurve[] curves; |
|
public EditorCurveBinding[] bindings; |
|
|
|
public EditorCurveBinding[] objectBindings; |
|
public List<ObjectReferenceKeyframe[]> objectCurves; |
|
|
|
Dictionary<string, CurveBindingGroup> m_groupings; |
|
|
|
// to tell whether the cache has changed |
|
public int version { get; private set; } |
|
|
|
float[] m_KeyTimes; |
|
|
|
Dictionary<EditorCurveBinding, float[]> m_individualBindinsKey; |
|
|
|
public float[] keyTimes |
|
{ |
|
get |
|
{ |
|
if (m_KeysDirty || m_KeyTimes == null) |
|
{ |
|
RebuildKeyCache(); |
|
} |
|
return m_KeyTimes; |
|
} |
|
} |
|
|
|
public float[] GetCurveTimes(EditorCurveBinding curve) |
|
{ |
|
return GetCurveTimes(new[] { curve }); |
|
} |
|
|
|
public float[] GetCurveTimes(EditorCurveBinding[] curves) |
|
{ |
|
if (m_KeysDirty || m_KeyTimes == null) |
|
{ |
|
RebuildKeyCache(); |
|
} |
|
|
|
var keyTimes = new List<float>(); |
|
for (int i = 0; i < curves.Length; i++) |
|
{ |
|
var c = curves[i]; |
|
if (m_individualBindinsKey.ContainsKey(c)) |
|
{ |
|
keyTimes.AddRange(m_individualBindinsKey[c]); |
|
} |
|
} |
|
return keyTimes.ToArray(); |
|
} |
|
|
|
void RebuildKeyCache() |
|
{ |
|
m_individualBindinsKey = new Dictionary<EditorCurveBinding, float[]>(); |
|
|
|
List<float> keys = curves.SelectMany(y => y.keys).Select(z => z.time).ToList(); |
|
for (int i = 0; i < objectCurves.Count; i++) |
|
{ |
|
var kf = objectCurves[i]; |
|
keys.AddRange(kf.Select(x => x.time)); |
|
} |
|
|
|
for (int b = 0; b < bindings.Count(); b++) |
|
{ |
|
m_individualBindinsKey.Add(bindings[b], curves[b].keys.Select(k => k.time).Distinct().ToArray()); |
|
} |
|
|
|
m_KeyTimes = keys.OrderBy(x => x).Distinct().ToArray(); |
|
m_KeysDirty = false; |
|
} |
|
|
|
public void Update(AnimationClip clip) |
|
{ |
|
List<EditorCurveBinding> postfilter = new List<EditorCurveBinding>(); |
|
var clipBindings = AnimationUtility.GetCurveBindings(clip); |
|
for (int i = 0; i < clipBindings.Length; i++) |
|
{ |
|
var bind = clipBindings[i]; |
|
if (!bind.propertyName.Contains("LocalRotation.w")) |
|
postfilter.Add(RotationCurveInterpolation.RemapAnimationBindingForRotationCurves(bind, clip)); |
|
} |
|
bindings = postfilter.ToArray(); |
|
|
|
curves = new AnimationCurve[bindings.Length]; |
|
for (int i = 0; i < bindings.Length; i++) |
|
{ |
|
curves[i] = AnimationUtility.GetEditorCurve(clip, bindings[i]); |
|
} |
|
|
|
objectBindings = AnimationUtility.GetObjectReferenceCurveBindings(clip); |
|
objectCurves = new List<ObjectReferenceKeyframe[]>(objectBindings.Length); |
|
for (int i = 0; i < objectBindings.Length; i++) |
|
{ |
|
objectCurves.Add(AnimationUtility.GetObjectReferenceCurve(clip, objectBindings[i])); |
|
} |
|
|
|
m_CurveDirty = false; |
|
m_KeysDirty = true; |
|
|
|
version = version + 1; |
|
} |
|
|
|
public bool GetBindingForCurve(AnimationCurve curve, ref EditorCurveBinding binding) |
|
{ |
|
for (int i = 0; i < curves.Length; i++) |
|
{ |
|
if (curve == curves[i]) |
|
{ |
|
binding = bindings[i]; |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
public AnimationCurve GetCurveForBinding(EditorCurveBinding binding) |
|
{ |
|
for (int i = 0; i < curves.Length; i++) |
|
{ |
|
if (binding.Equals(bindings[i])) |
|
{ |
|
return curves[i]; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
public ObjectReferenceKeyframe[] GetObjectCurveForBinding(EditorCurveBinding binding) |
|
{ |
|
if (objectCurves == null) |
|
return null; |
|
|
|
for (int i = 0; i < objectCurves.Count; i++) |
|
{ |
|
if (binding.Equals(objectBindings[i])) |
|
{ |
|
return objectCurves[i]; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
// given a groupID, get the list of curve bindings |
|
public CurveBindingGroup GetGroupBinding(string groupID) |
|
{ |
|
if (m_groupings == null) |
|
m_groupings = new Dictionary<string, CurveBindingGroup>(); |
|
|
|
CurveBindingGroup result = null; |
|
if (!m_groupings.TryGetValue(groupID, out result)) |
|
{ |
|
result = new CurveBindingGroup(); |
|
result.timeRange = new Vector2(float.MaxValue, float.MinValue); |
|
result.valueRange = new Vector2(float.MaxValue, float.MinValue); |
|
List<CurveBindingPair> found = new List<CurveBindingPair>(); |
|
for (int i = 0; i < bindings.Length; i++) |
|
{ |
|
if (bindings[i].GetGroupID() == groupID) |
|
{ |
|
CurveBindingPair pair = new CurveBindingPair(); |
|
pair.binding = bindings[i]; |
|
pair.curve = curves[i]; |
|
found.Add(pair); |
|
|
|
for (int k = 0; k < curves[i].keys.Length; k++) |
|
{ |
|
var key = curves[i].keys[k]; |
|
result.timeRange = new Vector2(Mathf.Min(key.time, result.timeRange.x), Mathf.Max(key.time, result.timeRange.y)); |
|
result.valueRange = new Vector2(Mathf.Min(key.value, result.valueRange.x), Mathf.Max(key.value, result.valueRange.y)); |
|
} |
|
} |
|
} |
|
for (int i = 0; i < objectBindings.Length; i++) |
|
{ |
|
if (objectBindings[i].GetGroupID() == groupID) |
|
{ |
|
CurveBindingPair pair = new CurveBindingPair(); |
|
pair.binding = objectBindings[i]; |
|
pair.objectCurve = objectCurves[i]; |
|
found.Add(pair); |
|
|
|
for (int k = 0; k < objectCurves[i].Length; k++) |
|
{ |
|
var key = objectCurves[i][k]; |
|
result.timeRange = new Vector2(Mathf.Min(key.time, result.timeRange.x), Mathf.Max(key.time, result.timeRange.y)); |
|
} |
|
} |
|
} |
|
|
|
result.curveBindingPairs = found.OrderBy(x => AnimationWindowUtility.GetComponentIndex(x.binding.propertyName)).ToArray(); |
|
|
|
m_groupings.Add(groupID, result); |
|
} |
|
return result; |
|
} |
|
} |
|
|
|
// Cache for storing the animation clip data |
|
class AnimationClipCurveCache |
|
{ |
|
static AnimationClipCurveCache s_Instance; |
|
Dictionary<AnimationClip, AnimationClipCurveInfo> m_ClipCache = new Dictionary<AnimationClip, AnimationClipCurveInfo>(); |
|
bool m_IsEnabled; |
|
|
|
|
|
public static AnimationClipCurveCache Instance |
|
{ |
|
get |
|
{ |
|
if (s_Instance == null) |
|
{ |
|
s_Instance = new AnimationClipCurveCache(); |
|
} |
|
|
|
return s_Instance; |
|
} |
|
} |
|
|
|
public void OnEnable() |
|
{ |
|
if (!m_IsEnabled) |
|
{ |
|
AnimationUtility.onCurveWasModified += OnCurveWasModified; |
|
m_IsEnabled = true; |
|
} |
|
} |
|
|
|
public void OnDisable() |
|
{ |
|
if (m_IsEnabled) |
|
{ |
|
AnimationUtility.onCurveWasModified -= OnCurveWasModified; |
|
m_IsEnabled = false; |
|
} |
|
} |
|
|
|
// callback when a curve is edited. Force the cache to update next time it's accessed |
|
void OnCurveWasModified(AnimationClip clip, EditorCurveBinding binding, AnimationUtility.CurveModifiedType modification) |
|
{ |
|
AnimationClipCurveInfo data; |
|
if (m_ClipCache.TryGetValue(clip, out data)) |
|
{ |
|
data.dirty = true; |
|
} |
|
} |
|
|
|
public AnimationClipCurveInfo GetCurveInfo(AnimationClip clip) |
|
{ |
|
AnimationClipCurveInfo data; |
|
if (clip == null) |
|
return null; |
|
if (!m_ClipCache.TryGetValue(clip, out data)) |
|
{ |
|
data = new AnimationClipCurveInfo(); |
|
data.dirty = true; |
|
m_ClipCache[clip] = data; |
|
} |
|
if (data.dirty) |
|
{ |
|
data.Update(clip); |
|
} |
|
return data; |
|
} |
|
|
|
public void ClearCachedProxyClips() |
|
{ |
|
var toRemove = new List<AnimationClip>(); |
|
foreach (var entry in m_ClipCache) |
|
{ |
|
var clip = entry.Key; |
|
if (clip != null && (clip.hideFlags & HideFlags.HideAndDontSave) == HideFlags.HideAndDontSave) |
|
toRemove.Add(clip); |
|
} |
|
|
|
foreach (var clip in toRemove) |
|
{ |
|
m_ClipCache.Remove(clip); |
|
Object.DestroyImmediate(clip, true); |
|
} |
|
} |
|
|
|
public void Clear() |
|
{ |
|
ClearCachedProxyClips(); |
|
m_ClipCache.Clear(); |
|
} |
|
} |
|
|
|
static class EditorCurveBindingExtension |
|
{ |
|
// identifier to generate an id thats the same for all curves in the same group |
|
public static string GetGroupID(this EditorCurveBinding binding) |
|
{ |
|
return binding.type + AnimationWindowUtility.GetPropertyGroupName(binding.propertyName); |
|
} |
|
} |
|
|
|
|
|
static class CurveBindingGroupExtensions |
|
{ |
|
// Extentions to determine curve types |
|
public static bool IsEnableGroup(this CurveBindingGroup curves) |
|
{ |
|
return curves.isFloatCurve && curves.count == 1 && curves.curveBindingPairs[0].binding.propertyName == "m_Enabled"; |
|
} |
|
|
|
public static bool IsVectorGroup(this CurveBindingGroup curves) |
|
{ |
|
if (!curves.isFloatCurve) |
|
return false; |
|
if (curves.count <= 1 || curves.count > 4) |
|
return false; |
|
char l = curves.curveBindingPairs[0].binding.propertyName.Last(); |
|
return l == 'x' || l == 'y' || l == 'z' || l == 'w'; |
|
} |
|
|
|
public static bool IsColorGroup(this CurveBindingGroup curves) |
|
{ |
|
if (!curves.isFloatCurve) |
|
return false; |
|
if (curves.count != 3 && curves.count != 4) |
|
return false; |
|
char l = curves.curveBindingPairs[0].binding.propertyName.Last(); |
|
return l == 'r' || l == 'g' || l == 'b' || l == 'a'; |
|
} |
|
|
|
public static string GetDescription(this CurveBindingGroup group, float t) |
|
{ |
|
string result = string.Empty; |
|
if (group.isFloatCurve) |
|
{ |
|
if (group.count > 1) |
|
{ |
|
result += "(" + group.curveBindingPairs[0].curve.Evaluate(t).ToString("0.##"); |
|
for (int j = 1; j < group.curveBindingPairs.Length; j++) |
|
{ |
|
result += "," + group.curveBindingPairs[j].curve.Evaluate(t).ToString("0.##"); |
|
} |
|
result += ")"; |
|
} |
|
else |
|
{ |
|
result = group.curveBindingPairs[0].curve.Evaluate(t).ToString("0.##"); |
|
} |
|
} |
|
else if (group.isObjectCurve) |
|
{ |
|
Object obj = null; |
|
if (group.curveBindingPairs[0].objectCurve.Length > 0) |
|
obj = CurveEditUtility.Evaluate(group.curveBindingPairs[0].objectCurve, t); |
|
result = (obj == null ? "None" : obj.name); |
|
} |
|
|
|
return result; |
|
} |
|
} |
|
}
|
|
|