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; | ||
|  |         } | ||
|  |     } | ||
|  | } |