Ingen beskrivning
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AnimationPreviewUtilities.cs 13KB

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