A game about forced loneliness, made by TACStudios
at master 301 lines 13 kB view raw
1using System; 2#if UNITY_EDITOR 3using System.Collections.Generic; 4using UnityEditor; 5 6 7namespace UnityEngine.Timeline 8{ 9 static class AnimationPreviewUtilities 10 { 11 private const string k_PosX = "m_LocalPosition.x"; 12 private const string k_PosY = "m_LocalPosition.y"; 13 private const string k_PosZ = "m_LocalPosition.z"; 14 private const string k_RotX = "m_LocalRotation.x"; 15 private const string k_RotY = "m_LocalRotation.y"; 16 private const string k_RotZ = "m_LocalRotation.z"; 17 private const string k_RotW = "m_LocalRotation.w"; 18 private const string k_ScaleX = "m_LocalScale.x"; 19 private const string k_ScaleY = "m_LocalScale.y"; 20 private const string k_ScaleZ = "m_LocalScale.z"; 21 private const string k_EulerAnglesRaw = "localEulerAnglesRaw"; 22 private const string k_EulerHint = "m_LocalEulerAnglesHint"; 23 private const string k_Pos = "m_LocalPosition"; 24 private const string k_Rot = "m_LocalRotation"; 25 private const string k_MotionT = "MotionT"; 26 private const string k_MotionQ = "MotionQ"; 27 private const string k_RootT = "RootT"; 28 private const string k_RootQ = "RootQ"; 29 30 31 internal static Object s_PreviewDriver; 32 33 34 internal class EditorCurveBindingComparer : IEqualityComparer<EditorCurveBinding> 35 { 36 public bool Equals(EditorCurveBinding x, EditorCurveBinding y) { return x.path.Equals(y.path) && x.type == y.type && x.propertyName == y.propertyName; } 37 public int GetHashCode(EditorCurveBinding obj) 38 { 39 return obj.propertyName.GetHashCode() ^ obj.path.GetHashCode(); 40 } 41 42 public static readonly EditorCurveBindingComparer Instance = new EditorCurveBindingComparer(); 43 } 44 45 // a dictionary is faster than a hashset, because the capacity can be pre-set 46 private static readonly Dictionary<EditorCurveBinding, int> s_CurveSet = new Dictionary<EditorCurveBinding, int>(10000, EditorCurveBindingComparer.Instance); 47 private static readonly AnimatorBindingCache s_BindingCache = new AnimatorBindingCache(); 48 49 // string.StartsWith is slow (https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity5.html) 50 // hand rolled version has best performance. 51 private static bool FastStartsWith(string a, string toCompare) 52 { 53 int aLen = a.Length; 54 int bLen = toCompare.Length; 55 56 int ap = 0; 57 int bp = 0; 58 59 while (ap < aLen && bp < bLen && a[ap] == toCompare[bp]) 60 { 61 ap++; 62 bp++; 63 } 64 65 return (bp == bLen); 66 } 67 68 public static void ClearCaches() 69 { 70 s_BindingCache.Clear(); 71 s_CurveSet.Clear(); 72 } 73 74 public static EditorCurveBinding[] GetBindings(GameObject animatorRoot, IEnumerable<AnimationClip> clips) 75 { 76 s_CurveSet.Clear(); 77 foreach (var clip in clips) 78 { 79 AddBindings(s_BindingCache.GetCurveBindings(clip)); 80 } 81 82 // if we have a transform binding, bind the entire skeleton 83 if (NeedsSkeletonBindings(s_CurveSet.Keys)) 84 AddBindings(s_BindingCache.GetAnimatorBindings(animatorRoot)); 85 86 var bindings = new EditorCurveBinding[s_CurveSet.Keys.Count]; 87 s_CurveSet.Keys.CopyTo(bindings, 0); 88 return bindings; 89 } 90 91 public static int GetClipHash(List<AnimationClip> clips) 92 { 93 int hash = 0; 94 95 foreach (var clip in clips) 96 { 97 var stats = AnimationUtility.GetAnimationClipStats(clip); 98 hash = HashUtility.CombineHash(hash, clip.GetHashCode(), stats.clips, stats.size, stats.totalCurves); 99 } 100 return hash; 101 } 102 103 public static void PreviewFromCurves(GameObject animatorRoot, IEnumerable<EditorCurveBinding> keys) 104 { 105 if (!AnimationMode.InAnimationMode()) 106 return; 107 108 var avatarRoot = GetAvatarRoot(animatorRoot); 109 foreach (var binding in keys) 110 { 111 if (IsAvatarBinding(binding) || IsEuler(binding)) 112 continue; 113 114 bool isTransform = typeof(Transform).IsAssignableFrom(binding.type); 115 if (isTransform && binding.propertyName.Equals(AnimatorBindingCache.TRPlaceHolder)) 116 AddTRBinding(animatorRoot, binding); 117 else if (isTransform && binding.propertyName.Equals(AnimatorBindingCache.ScalePlaceholder)) 118 AddScaleBinding(animatorRoot, binding); 119 else 120 AnimationMode.AddEditorCurveBinding(avatarRoot, binding); 121 } 122 } 123 124 public static AnimationClip CreateDefaultClip(GameObject animatorRoot, IEnumerable<EditorCurveBinding> keys) 125 { 126 AnimationClip animClip = new AnimationClip() { name = "DefaultPose" }; 127 var keyFrames = new[] {new Keyframe(0, 0)}; 128 var curve = new AnimationCurve(keyFrames); 129 bool rootMotion = false; 130 var avatarRoot = GetAvatarRoot(animatorRoot); 131 132 foreach (var binding in keys) 133 { 134 if (IsRootMotion(binding)) 135 { 136 rootMotion = true; 137 continue; 138 } 139 140 if (typeof(Transform).IsAssignableFrom(binding.type) && binding.propertyName.Equals(AnimatorBindingCache.TRPlaceHolder)) 141 { 142 if (string.IsNullOrEmpty(binding.path)) 143 rootMotion = true; 144 else 145 { 146 var transform = animatorRoot.transform.Find(binding.path); 147 if (transform != null) 148 { 149 var pos = transform.localPosition; 150 var rot = transform.localRotation; 151 animClip.SetCurve(binding.path, typeof(Transform), k_PosX, SetZeroKey(curve, keyFrames, pos.x)); 152 animClip.SetCurve(binding.path, typeof(Transform), k_PosY, SetZeroKey(curve, keyFrames, pos.y)); 153 animClip.SetCurve(binding.path, typeof(Transform), k_PosZ, SetZeroKey(curve, keyFrames, pos.z)); 154 animClip.SetCurve(binding.path, typeof(Transform), k_RotX, SetZeroKey(curve, keyFrames, rot.x)); 155 animClip.SetCurve(binding.path, typeof(Transform), k_RotY, SetZeroKey(curve, keyFrames, rot.y)); 156 animClip.SetCurve(binding.path, typeof(Transform), k_RotZ, SetZeroKey(curve, keyFrames, rot.z)); 157 animClip.SetCurve(binding.path, typeof(Transform), k_RotW, SetZeroKey(curve, keyFrames, rot.w)); 158 } 159 } 160 161 continue; 162 } 163 164 if (typeof(Transform).IsAssignableFrom(binding.type) && binding.propertyName == AnimatorBindingCache.ScalePlaceholder) 165 { 166 var transform = animatorRoot.transform.Find(binding.path); 167 if (transform != null) 168 { 169 var scale = transform.localScale; 170 animClip.SetCurve(binding.path, typeof(Transform), k_ScaleX, SetZeroKey(curve, keyFrames, scale.x)); 171 animClip.SetCurve(binding.path, typeof(Transform), k_ScaleY, SetZeroKey(curve, keyFrames, scale.y)); 172 animClip.SetCurve(binding.path, typeof(Transform), k_ScaleZ, SetZeroKey(curve, keyFrames, scale.z)); 173 } 174 175 continue; 176 } 177 178 // Not setting curves through AnimationUtility.SetEditorCurve to avoid reentrant 179 // onCurveWasModified calls in timeline. This means we don't get sprite curves 180 // in the default clip right now. 181 if (IsAvatarBinding(binding) || IsEulerHint(binding) || binding.isPPtrCurve) 182 continue; 183 184 float floatValue; 185 AnimationUtility.GetFloatValue(avatarRoot, binding, out floatValue); 186 animClip.SetCurve(binding.path, binding.type, binding.propertyName, SetZeroKey(curve, keyFrames, floatValue)); 187 } 188 189 // add root motion explicitly. 190 if (rootMotion) 191 { 192 var pos = Vector3.zero; // the appropriate root motion offsets are applied by timeline 193 var rot = Quaternion.identity; 194 animClip.SetCurve(string.Empty, typeof(Transform), k_PosX, SetZeroKey(curve, keyFrames, pos.x)); 195 animClip.SetCurve(string.Empty, typeof(Transform), k_PosY, SetZeroKey(curve, keyFrames, pos.y)); 196 animClip.SetCurve(string.Empty, typeof(Transform), k_PosZ, SetZeroKey(curve, keyFrames, pos.z)); 197 animClip.SetCurve(string.Empty, typeof(Transform), k_RotX, SetZeroKey(curve, keyFrames, rot.x)); 198 animClip.SetCurve(string.Empty, typeof(Transform), k_RotY, SetZeroKey(curve, keyFrames, rot.y)); 199 animClip.SetCurve(string.Empty, typeof(Transform), k_RotZ, SetZeroKey(curve, keyFrames, rot.z)); 200 animClip.SetCurve(string.Empty, typeof(Transform), k_RotW, SetZeroKey(curve, keyFrames, rot.w)); 201 } 202 203 return animClip; 204 } 205 206 public static bool IsRootMotion(EditorCurveBinding binding) 207 { 208 // Root Transform TR. 209 if (typeof(Transform).IsAssignableFrom(binding.type) && string.IsNullOrEmpty(binding.path)) 210 { 211 return FastStartsWith(binding.propertyName, k_Pos) || FastStartsWith(binding.propertyName, k_Rot); 212 } 213 214 // MotionCurves/RootCurves. 215 if (binding.type == typeof(Animator)) 216 { 217 return FastStartsWith(binding.propertyName, k_MotionT) || 218 FastStartsWith(binding.propertyName, k_MotionQ) || 219 FastStartsWith(binding.propertyName, k_RootT) || 220 FastStartsWith(binding.propertyName, k_RootQ); 221 } 222 223 return false; 224 } 225 226 private static bool NeedsSkeletonBindings(IEnumerable<EditorCurveBinding> bindings) 227 { 228 foreach (var b in bindings) 229 { 230 if (IsSkeletalBinding(b)) 231 return true; 232 } 233 234 return false; 235 } 236 237 private static void AddBindings(IEnumerable<EditorCurveBinding> bindings) 238 { 239 foreach (var b in bindings) 240 { 241 if (!s_CurveSet.ContainsKey(b)) 242 s_CurveSet[b] = 1; 243 } 244 } 245 246 private static void AddTRBinding(GameObject root, EditorCurveBinding binding) 247 { 248 var t = root.transform.Find(binding.path); 249 if (t != null) 250 { 251 DrivenPropertyManager.RegisterProperty(s_PreviewDriver, t, "m_LocalPosition"); 252 DrivenPropertyManager.RegisterProperty(s_PreviewDriver, t, "m_LocalRotation"); 253 } 254 } 255 256 private static void AddScaleBinding(GameObject root, EditorCurveBinding binding) 257 { 258 var t = root.transform.Find(binding.path); 259 if (t != null) 260 DrivenPropertyManager.RegisterProperty(s_PreviewDriver, t, "m_LocalScale"); 261 } 262 263 private static bool IsEuler(EditorCurveBinding binding) 264 { 265 return FastStartsWith(binding.propertyName, k_EulerAnglesRaw) && 266 typeof(Transform).IsAssignableFrom(binding.type); 267 } 268 269 private static bool IsAvatarBinding(EditorCurveBinding binding) 270 { 271 return string.IsNullOrEmpty(binding.path) && typeof(Animator) == binding.type; 272 } 273 274 private static bool IsSkeletalBinding(EditorCurveBinding binding) 275 { 276 // skin mesh incorporates blend shapes 277 return typeof(Transform).IsAssignableFrom(binding.type) || typeof(SkinnedMeshRenderer).IsAssignableFrom(binding.type); 278 } 279 280 private static AnimationCurve SetZeroKey(AnimationCurve curve, Keyframe[] keys, float val) 281 { 282 keys[0].value = val; 283 curve.keys = keys; 284 return curve; 285 } 286 287 private static bool IsEulerHint(EditorCurveBinding binding) 288 { 289 return typeof(Transform).IsAssignableFrom(binding.type) && binding.propertyName.StartsWith(k_EulerHint); 290 } 291 292 private static GameObject GetAvatarRoot(GameObject animatorRoot) 293 { 294 var animator = animatorRoot.GetComponent<Animator>(); 295 if (animator != null && animator.avatarRoot != animatorRoot.transform) 296 return animator.avatarRoot.gameObject; 297 return animatorRoot; 298 } 299 } 300} 301#endif