428 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			428 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| 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;
 | |
|         }
 | |
|     }
 | |
| }
 |