Maze / Library /PackageCache /com.unity.timeline@1.6.5 /Editor /Animation /AnimationClipCurveCache.cs
| 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; | |
| } | |
| } | |
| } | |