#if UNITY_EDITOR using System.Collections.Generic; using UnityEngine; using UnityEngine.Playables; using UnityEngine.Timeline; using UnityEditor; namespace UnityEngine.Timeline { /// /// Animator to Editor Curve Binding cache. Used to prevent frequent calls to GetAnimatorBindings which can be costly /// class AnimatorBindingCache { public const string TRPlaceHolder = "TransformTR"; public const string ScalePlaceholder = "TransformScale"; struct AnimatorEntry { public int animatorID; public bool applyRootMotion; public bool humanoid; } class AnimatorEntryComparer : IEqualityComparer { public bool Equals(AnimatorEntry x, AnimatorEntry y) { return x.animatorID == y.animatorID && x.applyRootMotion == y.applyRootMotion && x.humanoid == y.humanoid; } public int GetHashCode(AnimatorEntry obj) { return HashUtility.CombineHash(obj.animatorID, obj.applyRootMotion.GetHashCode(), obj.humanoid.GetHashCode()); } public static readonly AnimatorEntryComparer Instance = new AnimatorEntryComparer(); } readonly Dictionary m_AnimatorCache = new Dictionary(AnimatorEntryComparer.Instance); readonly Dictionary m_ClipCache = new Dictionary(); private static readonly EditorCurveBinding[] kEmptyArray = new EditorCurveBinding[0]; private static readonly List s_BindingScratchPad = new List(1000); public AnimatorBindingCache() { AnimationUtility.onCurveWasModified += OnCurveWasModified; } public EditorCurveBinding[] GetAnimatorBindings(GameObject gameObject) { if (gameObject == null) return kEmptyArray; Animator animator = gameObject.GetComponent(); if (animator == null) return kEmptyArray; AnimatorEntry entry = new AnimatorEntry() { animatorID = animator.GetInstanceID(), applyRootMotion = animator.applyRootMotion, humanoid = animator.isHuman }; EditorCurveBinding[] result = null; if (m_AnimatorCache.TryGetValue(entry, out result)) return result; s_BindingScratchPad.Clear(); // Replacement for AnimationMode.GetAnimatorBinding - this is faster and allocates kB instead of MB var transforms = animator.GetComponentsInChildren(); foreach (var t in transforms) { if (animator.IsBoneTransform(t)) s_BindingScratchPad.Add(EditorCurveBinding.FloatCurve(AnimationUtility.CalculateTransformPath(t, animator.transform), typeof(Transform), TRPlaceHolder)); } var streamBindings = AnimationUtility.GetAnimationStreamBindings(animator.gameObject); UpdateTransformBindings(streamBindings); s_BindingScratchPad.AddRange(streamBindings); result = new EditorCurveBinding[s_BindingScratchPad.Count]; s_BindingScratchPad.CopyTo(result); m_AnimatorCache[entry] = result; return result; } public EditorCurveBinding[] GetCurveBindings(AnimationClip clip) { if (clip == null) return kEmptyArray; EditorCurveBinding[] result; if (!m_ClipCache.TryGetValue(clip, out result)) { result = AnimationMode.GetCurveBindings(clip); UpdateTransformBindings(result); m_ClipCache[clip] = result; } return result; } private static void UpdateTransformBindings(EditorCurveBinding[] bindings) { for (int i = 0; i < bindings.Length; i++) { var binding = bindings[i]; if (AnimationPreviewUtilities.IsRootMotion(binding)) { binding.type = typeof(Transform); binding.propertyName = TRPlaceHolder; } else if (typeof(Transform).IsAssignableFrom(binding.type) && (binding.propertyName.StartsWith("m_LocalRotation.") || binding.propertyName.StartsWith("m_LocalPosition."))) { binding.propertyName = TRPlaceHolder; } else if (typeof(Transform).IsAssignableFrom(binding.type) && binding.propertyName.StartsWith("m_LocalScale.")) { binding.propertyName = ScalePlaceholder; } bindings[i] = binding; } } public void Clear() { m_AnimatorCache.Clear(); m_ClipCache.Clear(); } void OnCurveWasModified(AnimationClip clip, EditorCurveBinding binding, AnimationUtility.CurveModifiedType modification) { m_ClipCache.Remove(clip); } } } #endif