AnimationPreviewUtilities.cs 12 KB

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