TransitionPreviewWindow.Scene.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. #if UNITY_EDITOR && UNITY_IMGUI
  3. #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value.
  4. using System;
  5. using System.Collections.Generic;
  6. using UnityEditor;
  7. using UnityEditor.SceneManagement;
  8. using UnityEngine;
  9. using UnityEngine.SceneManagement;
  10. using Object = UnityEngine.Object;
  11. namespace Animancer.Editor.Previews
  12. {
  13. /// https://kybernetik.com.au/animancer/api/Animancer.Editor.Previews/TransitionPreviewWindow
  14. partial class TransitionPreviewWindow
  15. {
  16. /************************************************************************************************************************/
  17. /// <summary>The <see cref="Scene"/> of the current <see cref="TransitionPreviewWindow"/> instance.</summary>
  18. public static Scene InstanceScene
  19. => _Instance != null
  20. ? _Instance._Scene
  21. : null;
  22. /************************************************************************************************************************/
  23. /// <summary>Temporary scene management for the <see cref="TransitionPreviewWindow"/>.</summary>
  24. /// <remarks>
  25. /// <strong>Documentation:</strong>
  26. /// <see href="https://kybernetik.com.au/animancer/docs/manual/transitions#previews">
  27. /// Previews</see>
  28. /// </remarks>
  29. [Serializable]
  30. public class Scene :
  31. AnimancerPreviewObject.IEventHandler
  32. {
  33. /************************************************************************************************************************/
  34. #region Fields and Properties
  35. /************************************************************************************************************************/
  36. /// <summary>The scene displayed by the <see cref="TransitionPreviewWindow"/>.</summary>
  37. [SerializeField]
  38. private UnityEngine.SceneManagement.Scene _Scene;
  39. /// <summary>The root object in the preview scene.</summary>
  40. public Transform PreviewSceneRoot { get; private set; }
  41. /// <summary>The root of the model in the preview scene. A child of the <see cref="PreviewSceneRoot"/>.</summary>
  42. public Transform InstanceRoot { get; private set; }
  43. /// <summary>
  44. /// An instance of the <see cref="TransitionPreviewSettings.SceneEnvironment"/>.
  45. /// A child of the <see cref="PreviewSceneRoot"/>.
  46. /// </summary>
  47. public GameObject EnvironmentInstance { get; private set; }
  48. /************************************************************************************************************************/
  49. [SerializeField]
  50. private AnimancerPreviewObject _PreviewObject;
  51. /// <summary>[<see cref="SerializeField"/>] The object being previewed.</summary>
  52. public AnimancerPreviewObject PreviewObject
  53. => AnimancerPreviewObject.Initialize(ref _PreviewObject, this, PreviewSceneRoot);
  54. /************************************************************************************************************************/
  55. private Vector3 _PreviousPreviewObjectPosition;
  56. private Vector3 CurrentPreviewObjectPosition
  57. => PreviewObject.SelectedInstanceAnimator.transform.position;
  58. /************************************************************************************************************************/
  59. #endregion
  60. /************************************************************************************************************************/
  61. #region Initialization
  62. /************************************************************************************************************************/
  63. /// <summary>Initializes this <see cref="Scene"/>.</summary>
  64. public void OnEnable()
  65. {
  66. duringSceneGui += DoCustomGUI;
  67. CreateScene();
  68. PreviewObject.TrySelectBestModel(Transition);
  69. }
  70. /************************************************************************************************************************/
  71. private void CreateScene()
  72. {
  73. _Scene = EditorSceneManager.NewPreviewScene();
  74. _Scene.name = "Transition Preview";
  75. _Instance.customScene = _Scene;
  76. var root = AnimancerPreviewObject.CreateEmpty(nameof(TransitionPreviewWindow));
  77. PreviewSceneRoot = root.transform;
  78. SceneManager.MoveGameObjectToScene(root, _Scene);
  79. _Instance.customParentForDraggedObjects = PreviewSceneRoot;
  80. OnEnvironmentPrefabChanged();
  81. }
  82. /************************************************************************************************************************/
  83. internal void OnEnvironmentPrefabChanged()
  84. {
  85. DestroyImmediate(EnvironmentInstance);
  86. var prefab = TransitionPreviewSettings.SceneEnvironment;
  87. if (prefab != null)
  88. EnvironmentInstance = Instantiate(prefab, PreviewSceneRoot);
  89. }
  90. /************************************************************************************************************************/
  91. /// <inheritdoc/>
  92. void AnimancerPreviewObject.IEventHandler.OnInstantiateObject()
  93. {
  94. FocusCamera();
  95. _Instance._Animations.GatherAnimations();
  96. }
  97. /************************************************************************************************************************/
  98. /// <inheritdoc/>
  99. void AnimancerPreviewObject.IEventHandler.OnSetSelectedAnimator()
  100. {
  101. _Instance.in2DMode = PreviewObject.SelectedInstanceType == AnimationType.Sprite;
  102. }
  103. /// <inheritdoc/>
  104. void AnimancerPreviewObject.IEventHandler.OnCreateGraph()
  105. {
  106. PreviewObject.Graph.RequirePostUpdate(new Animations.WindowMatchStateTime());
  107. _Instance._Animations.NormalizedTime = _Instance._Animations.NormalizedTime;
  108. }
  109. /************************************************************************************************************************/
  110. /// <summary>Called when the target transition property is changed.</summary>
  111. public void OnTargetPropertyChanged()
  112. {
  113. _ExpandedHierarchy?.Clear();
  114. var previewObject = PreviewObject;
  115. previewObject.OriginalObject = AnimancerUtilities.FindRoot(_Instance._TransitionProperty.TargetObject);
  116. previewObject.TrySelectBestModel(Transition);
  117. _Instance._Animations.NormalizedTime = 0;
  118. _Instance.in2DMode = previewObject.SelectedInstanceType == AnimationType.Sprite;
  119. }
  120. /************************************************************************************************************************/
  121. private void FocusCamera()
  122. {
  123. if (InstanceRoot == null)
  124. return;
  125. var bounds = CalculateBounds(InstanceRoot);
  126. var rotation = _Instance.in2DMode ?
  127. Quaternion.identity :
  128. Quaternion.Euler(35, 135, 0);
  129. var size = bounds.extents.magnitude * 1.5f;
  130. if (size == float.PositiveInfinity)
  131. return;
  132. else if (size == 0)
  133. size = 10;
  134. _Instance.LookAt(bounds.center, rotation, size, _Instance.in2DMode, true);
  135. }
  136. /************************************************************************************************************************/
  137. private static Bounds CalculateBounds(Transform transform)
  138. {
  139. if (transform == null)
  140. return default;
  141. var renderers = transform.GetComponentsInChildren<Renderer>();
  142. if (renderers.Length == 0)
  143. return default;
  144. var bounds = renderers[0].bounds;
  145. for (int i = 1; i < renderers.Length; i++)
  146. {
  147. bounds.Encapsulate(renderers[i].bounds);
  148. }
  149. return bounds;
  150. }
  151. /************************************************************************************************************************/
  152. #endregion
  153. /************************************************************************************************************************/
  154. #region Execution
  155. /************************************************************************************************************************/
  156. /// <summary>Called when the window GUI is drawn.</summary>
  157. public void OnGUI()
  158. {
  159. if (_PreviewObject != null &&
  160. _PreviewObject.Graph != null &&
  161. _PreviewObject.Graph.IsGraphPlaying)
  162. AnimancerGUI.RepaintEverything();
  163. if (Selection.activeObject == _Instance &&
  164. Event.current.type == EventType.KeyUp &&
  165. Event.current.keyCode == KeyCode.F)
  166. FocusCamera();
  167. }
  168. /************************************************************************************************************************/
  169. private void DoCustomGUI(SceneView sceneView)
  170. {
  171. FollowPreviewObject(sceneView);
  172. var animancer = PreviewObject.Graph;
  173. if (animancer == null ||
  174. sceneView is not TransitionPreviewWindow instance ||
  175. !AnimancerUtilities.TryGetWrappedObject(Transition, out ITransitionGUI gui) ||
  176. instance._TransitionProperty == null)
  177. return;
  178. EditorGUI.BeginChangeCheck();
  179. using (new TransitionDrawer.DrawerContext(instance._TransitionProperty))
  180. {
  181. try
  182. {
  183. gui.OnPreviewSceneGUI(new(animancer));
  184. }
  185. catch (Exception exception)
  186. {
  187. Debug.LogException(exception);
  188. }
  189. }
  190. if (EditorGUI.EndChangeCheck())
  191. AnimancerGUI.RepaintEverything();
  192. }
  193. /************************************************************************************************************************/
  194. private void FollowPreviewObject(SceneView sceneView)
  195. {
  196. var currentPreviewObjectPosition = CurrentPreviewObjectPosition;
  197. if (_PreviousPreviewObjectPosition == currentPreviewObjectPosition)
  198. return;
  199. sceneView.pivot += currentPreviewObjectPosition - _PreviousPreviewObjectPosition;
  200. _PreviousPreviewObjectPosition = currentPreviewObjectPosition;
  201. }
  202. /************************************************************************************************************************/
  203. /// <summary>Is the `obj` a <see cref="GameObject"/> in the preview scene?</summary>
  204. public bool IsSceneObject(Object obj)
  205. => obj is GameObject gameObject
  206. && gameObject.transform.IsChildOf(PreviewSceneRoot);
  207. /************************************************************************************************************************/
  208. [SerializeField]
  209. private List<Transform> _ExpandedHierarchy;
  210. /// <summary>A list of all objects with their child hierarchy expanded.</summary>
  211. public List<Transform> ExpandedHierarchy
  212. => _ExpandedHierarchy ??= new();
  213. /************************************************************************************************************************/
  214. #endregion
  215. /************************************************************************************************************************/
  216. #region Cleanup
  217. /************************************************************************************************************************/
  218. /// <summary>Called by <see cref="TransitionPreviewWindow.OnDisable"/>.</summary>
  219. public void OnDisable()
  220. {
  221. duringSceneGui -= DoCustomGUI;
  222. _PreviewObject?.Dispose();
  223. EditorSceneManager.ClosePreviewScene(_Scene);
  224. }
  225. /************************************************************************************************************************/
  226. /// <summary>Called by <see cref="TransitionPreviewWindow.OnDestroy"/>.</summary>
  227. public void OnDestroy()
  228. {
  229. if (PreviewSceneRoot != null)
  230. {
  231. DestroyImmediate(PreviewSceneRoot.gameObject);
  232. PreviewSceneRoot = null;
  233. }
  234. }
  235. /************************************************************************************************************************/
  236. #endregion
  237. /************************************************************************************************************************/
  238. }
  239. }
  240. }
  241. #endif