AnimancerPreviewObject.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. #if UNITY_EDITOR && UNITY_IMGUI
  3. using System;
  4. using UnityEditor;
  5. using UnityEditor.SceneManagement;
  6. using UnityEngine;
  7. using Object = UnityEngine.Object;
  8. namespace Animancer.Editor.Previews
  9. {
  10. /// <summary>[Editor-Only] Manages the selection and instantiation of models for previewing animations.</summary>
  11. /// https://kybernetik.com.au/animancer/api/Animancer.Editor.Previews/AnimancerPreviewObject
  12. [Serializable]
  13. public class AnimancerPreviewObject : IDisposable
  14. {
  15. /************************************************************************************************************************/
  16. /// <summary>[Editor-Only] Handles events from an <see cref="AnimancerPreviewObject"/>.</summary>
  17. /// https://kybernetik.com.au/animancer/api/Animancer.Editor.Previews/IEventHandler
  18. public interface IEventHandler
  19. {
  20. /// <summary>Called after the <see cref="InstanceObject"/> is instantiated.</summary>
  21. void OnInstantiateObject();
  22. /// <summary>Called after the <see cref="SelectedInstanceAnimator"/> is changed.</summary>
  23. void OnSetSelectedAnimator();
  24. /// <summary>Called after the <see cref="Graph"/> is initialized.</summary>
  25. void OnCreateGraph();
  26. }
  27. /// <summary>An optional listener for this object's events.</summary>
  28. [field: NonSerialized] public IEventHandler EventHandler { get; set; }
  29. /************************************************************************************************************************/
  30. [SerializeField]
  31. private Transform _OriginalObject;
  32. /// <summary>[<see cref="SerializeField"/>]
  33. /// The original model which was instantiated to create the <see cref="InstanceObject"/>.
  34. /// </summary>
  35. public Transform OriginalObject
  36. {
  37. get => _OriginalObject;
  38. set
  39. {
  40. _OriginalObject = value;
  41. InstantiateObject();
  42. if (value != null)
  43. TransitionPreviewSettings.AddModel(value.gameObject);
  44. }
  45. }
  46. /************************************************************************************************************************/
  47. /// <summary>The object to instantiate the <see cref="InstanceObject"/> under.</summary>
  48. [field: NonSerialized]
  49. public Transform InstanceRoot { get; private set; }
  50. /// <summary>The preview copy of the <see cref="OriginalObject"/>.</summary>
  51. [field: NonSerialized]
  52. public Transform InstanceObject { get; private set; }
  53. /// <summary>The bounds of the <see cref="InstanceObject"/>.</summary>
  54. [field: NonSerialized]
  55. public Bounds InstanceBounds { get; private set; }
  56. /************************************************************************************************************************/
  57. /// <summary>The <see cref="Animator"/>s on the <see cref="InstanceObject"/> and its children.</summary>
  58. [field: NonSerialized]
  59. public Animator[] InstanceAnimators { get; private set; }
  60. /// <summary>The type of the <see cref="SelectedInstanceAnimator"/>.</summary>
  61. [field: NonSerialized]
  62. public AnimationType SelectedInstanceType { get; private set; }
  63. [SerializeField]
  64. private int _SelectedInstanceAnimator;
  65. /// <summary>The <see cref="Animator"/> component currently being used for the preview.</summary>
  66. public Animator SelectedInstanceAnimator
  67. {
  68. get
  69. {
  70. if (InstanceAnimators.IsNullOrEmpty())
  71. return null;
  72. if (_SelectedInstanceAnimator >= InstanceAnimators.Length)
  73. _SelectedInstanceAnimator = InstanceAnimators.Length - 1;
  74. return InstanceAnimators[_SelectedInstanceAnimator];
  75. }
  76. }
  77. /************************************************************************************************************************/
  78. [NonSerialized]
  79. private AnimancerGraph _Graph;
  80. /// <summary>The <see cref="AnimancerGraph"/> being used for the preview.</summary>
  81. public AnimancerGraph Graph
  82. {
  83. get
  84. {
  85. if ((_Graph == null || !_Graph.IsValidOrDispose()) &&
  86. InstanceObject != null)
  87. {
  88. _Graph = null;
  89. var animator = SelectedInstanceAnimator;
  90. if (animator != null)
  91. {
  92. AnimancerGraph.SetNextGraphName($"{animator.name} (Animancer Preview)");
  93. _Graph = new AnimancerGraph();
  94. _Graph.CreateOutput(
  95. new DummyAnimancerComponent(animator, _Graph));
  96. EventHandler?.OnCreateGraph();
  97. }
  98. }
  99. return _Graph;
  100. }
  101. }
  102. /************************************************************************************************************************/
  103. /// <summary>
  104. /// Creates a new <see cref="AnimancerPreviewObject"/>
  105. /// and calls <see cref="Initialize(Transform)"/>.
  106. /// </summary>
  107. public static AnimancerPreviewObject Initialize(
  108. ref AnimancerPreviewObject preview,
  109. IEventHandler eventHandler,
  110. Transform instanceRoot)
  111. {
  112. preview ??= new();
  113. preview.EventHandler = eventHandler;
  114. preview.Initialize(instanceRoot);
  115. return preview;
  116. }
  117. /************************************************************************************************************************/
  118. [NonSerialized] private bool _HasInitialized;
  119. /// <summary>Sets the <see cref="InstanceRoot"/> for this preview to work under.</summary>
  120. public void Initialize(Transform instanceRoot)
  121. {
  122. if (InstanceRoot != instanceRoot)
  123. {
  124. DestroyInstanceObject();
  125. InstanceRoot = instanceRoot;
  126. }
  127. if (InstanceObject == null)
  128. InstantiateObject();
  129. if (_HasInitialized)
  130. return;
  131. _HasInitialized = true;
  132. EditorSceneManager.sceneOpening += OnSceneOpening;
  133. EditorApplication.playModeStateChanged += OnPlayModeChanged;
  134. }
  135. /************************************************************************************************************************/
  136. /// <summary>Cleans up this preview.</summary>
  137. public void Dispose()
  138. {
  139. EditorSceneManager.sceneOpening -= OnSceneOpening;
  140. EditorApplication.playModeStateChanged -= OnPlayModeChanged;
  141. }
  142. /************************************************************************************************************************/
  143. /// <summary>Called when entering or exiting Play Mode to destroy the <see cref="InstanceObject"/>.</summary>
  144. private void OnPlayModeChanged(PlayModeStateChange change)
  145. {
  146. switch (change)
  147. {
  148. case PlayModeStateChange.ExitingEditMode:
  149. case PlayModeStateChange.ExitingPlayMode:
  150. DestroyInstanceObject();
  151. break;
  152. }
  153. }
  154. /************************************************************************************************************************/
  155. /// <summary>Called when opening a scene to destroy the <see cref="InstanceObject"/>.</summary>
  156. private void OnSceneOpening(string path, OpenSceneMode mode)
  157. {
  158. if (mode == OpenSceneMode.Single)
  159. DestroyInstanceObject();
  160. }
  161. /************************************************************************************************************************/
  162. /// <summary>Destroys and re-instantiates the <see cref="InstanceObject"/>.</summary>
  163. private void InstantiateObject()
  164. {
  165. if (AnimancerEditorUtilities.IsChangingPlayMode)
  166. return;
  167. DestroyInstanceObject();
  168. if (_OriginalObject == null ||
  169. InstanceRoot == null)
  170. return;
  171. InstanceRoot.gameObject.SetActive(false);
  172. InstanceObject = Object.Instantiate(_OriginalObject, InstanceRoot);
  173. InstanceObject.localPosition = default;
  174. InstanceObject.name = _OriginalObject.name;
  175. InstanceBounds = AnimancerEditorUtilities.CalculateBounds(InstanceObject);
  176. DisableUnnecessaryComponents(InstanceObject.gameObject);
  177. InstanceAnimators = InstanceObject.GetComponentsInChildren<Animator>();
  178. for (int i = 0; i < InstanceAnimators.Length; i++)
  179. {
  180. var animator = InstanceAnimators[i];
  181. animator.enabled = false;
  182. animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
  183. animator.fireEvents = false;
  184. animator.updateMode = AnimatorUpdateMode.Normal;
  185. animator.applyRootMotion = true;
  186. }
  187. InstanceRoot.gameObject.SetActive(true);
  188. SetSelectedAnimator(_SelectedInstanceAnimator);
  189. EventHandler?.OnInstantiateObject();
  190. }
  191. /************************************************************************************************************************/
  192. /// <summary>Disables all unnecessary components on the `root` or its children.</summary>
  193. private static void DisableUnnecessaryComponents(GameObject root)
  194. {
  195. var behaviours = root.GetComponentsInChildren<Behaviour>();
  196. for (int i = 0; i < behaviours.Length; i++)
  197. {
  198. var behaviour = behaviours[i];
  199. // Other undesirable components aren't Behaviours anyway: Transform, MeshFilter, Renderer.
  200. if (behaviour is Animator)
  201. continue;
  202. var type = behaviour.GetType();
  203. if (type.IsDefined(typeof(ExecuteAlways), true) ||
  204. type.IsDefined(typeof(ExecuteInEditMode), true))
  205. continue;
  206. behaviour.enabled = false;
  207. behaviour.hideFlags |= HideFlags.NotEditable;
  208. }
  209. }
  210. /************************************************************************************************************************/
  211. /// <summary>Sets the <see cref="SelectedInstanceAnimator"/>.</summary>
  212. public void SetSelectedAnimator(int index)
  213. {
  214. DestroyGraph();
  215. var animator = SelectedInstanceAnimator;
  216. if (animator != null && animator.enabled)
  217. {
  218. animator.Rebind();
  219. animator.enabled = false;
  220. return;
  221. }
  222. _SelectedInstanceAnimator = index;
  223. animator = SelectedInstanceAnimator;
  224. if (animator != null)
  225. {
  226. animator.enabled = true;
  227. SelectedInstanceType = AnimationBindings.GetAnimationType(animator);
  228. }
  229. else
  230. {
  231. SelectedInstanceType = default;
  232. }
  233. EventHandler?.OnSetSelectedAnimator();
  234. }
  235. /************************************************************************************************************************/
  236. /// <summary>Destroys the <see cref="InstanceObject"/>.</summary>
  237. public void DestroyInstanceObject()
  238. {
  239. DestroyGraph();
  240. if (InstanceObject == null)
  241. return;
  242. Object.DestroyImmediate(InstanceObject.gameObject);
  243. InstanceObject = null;
  244. InstanceAnimators = null;
  245. }
  246. /************************************************************************************************************************/
  247. /// <summary>Destroys the <see cref="Graph"/>.</summary>
  248. private void DestroyGraph()
  249. {
  250. if (_Graph == null)
  251. return;
  252. _Graph.Destroy();
  253. _Graph = null;
  254. }
  255. /************************************************************************************************************************/
  256. /// <summary>
  257. /// Calls <see cref="TransitionPreviewSettings.TrySelectBestModel(object, Transform)"/>
  258. /// if there is no <see cref="OriginalObject"/> yet.
  259. /// </summary>
  260. public void TrySelectBestModel(object animationClipSource)
  261. {
  262. if (OriginalObject == null)
  263. OriginalObject = TransitionPreviewSettings.TrySelectBestModel(animationClipSource, InstanceRoot);
  264. }
  265. /************************************************************************************************************************/
  266. /// <summary>
  267. /// Creates an object using <see cref="HideFlags.HideAndDontSave"/>
  268. /// without <see cref="HideFlags.NotEditable"/>.
  269. /// </summary>
  270. public static GameObject CreateEmpty(string name)
  271. => EditorUtility.CreateGameObjectWithHideFlags(
  272. name,
  273. HideFlags.HideInHierarchy | HideFlags.DontSave);
  274. /************************************************************************************************************************/
  275. }
  276. }
  277. #endif