// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik // #if UNITY_EDITOR && UNITY_IMGUI #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value. using System; using System.Collections.Generic; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; using Object = UnityEngine.Object; namespace Animancer.Editor.Previews { /// https://kybernetik.com.au/animancer/api/Animancer.Editor.Previews/TransitionPreviewWindow partial class TransitionPreviewWindow { /************************************************************************************************************************/ /// The of the current instance. public static Scene InstanceScene => _Instance != null ? _Instance._Scene : null; /************************************************************************************************************************/ /// Temporary scene management for the . /// /// Documentation: /// /// Previews /// [Serializable] public class Scene : AnimancerPreviewObject.IEventHandler { /************************************************************************************************************************/ #region Fields and Properties /************************************************************************************************************************/ /// The scene displayed by the . [SerializeField] private UnityEngine.SceneManagement.Scene _Scene; /// The root object in the preview scene. public Transform PreviewSceneRoot { get; private set; } /// The root of the model in the preview scene. A child of the . public Transform InstanceRoot { get; private set; } /// /// An instance of the . /// A child of the . /// public GameObject EnvironmentInstance { get; private set; } /************************************************************************************************************************/ [SerializeField] private AnimancerPreviewObject _PreviewObject; /// [] The object being previewed. public AnimancerPreviewObject PreviewObject => AnimancerPreviewObject.Initialize(ref _PreviewObject, this, PreviewSceneRoot); /************************************************************************************************************************/ private Vector3 _PreviousPreviewObjectPosition; private Vector3 CurrentPreviewObjectPosition => PreviewObject.SelectedInstanceAnimator.transform.position; /************************************************************************************************************************/ #endregion /************************************************************************************************************************/ #region Initialization /************************************************************************************************************************/ /// Initializes this . public void OnEnable() { duringSceneGui += DoCustomGUI; CreateScene(); PreviewObject.TrySelectBestModel(Transition); } /************************************************************************************************************************/ private void CreateScene() { _Scene = EditorSceneManager.NewPreviewScene(); _Scene.name = "Transition Preview"; _Instance.customScene = _Scene; var root = AnimancerPreviewObject.CreateEmpty(nameof(TransitionPreviewWindow)); PreviewSceneRoot = root.transform; SceneManager.MoveGameObjectToScene(root, _Scene); _Instance.customParentForDraggedObjects = PreviewSceneRoot; OnEnvironmentPrefabChanged(); } /************************************************************************************************************************/ internal void OnEnvironmentPrefabChanged() { DestroyImmediate(EnvironmentInstance); var prefab = TransitionPreviewSettings.SceneEnvironment; if (prefab != null) EnvironmentInstance = Instantiate(prefab, PreviewSceneRoot); } /************************************************************************************************************************/ /// void AnimancerPreviewObject.IEventHandler.OnInstantiateObject() { FocusCamera(); _Instance._Animations.GatherAnimations(); } /************************************************************************************************************************/ /// void AnimancerPreviewObject.IEventHandler.OnSetSelectedAnimator() { _Instance.in2DMode = PreviewObject.SelectedInstanceType == AnimationType.Sprite; } /// void AnimancerPreviewObject.IEventHandler.OnCreateGraph() { PreviewObject.Graph.RequirePostUpdate(new Animations.WindowMatchStateTime()); _Instance._Animations.NormalizedTime = _Instance._Animations.NormalizedTime; } /************************************************************************************************************************/ /// Called when the target transition property is changed. public void OnTargetPropertyChanged() { _ExpandedHierarchy?.Clear(); var previewObject = PreviewObject; previewObject.OriginalObject = AnimancerUtilities.FindRoot(_Instance._TransitionProperty.TargetObject); previewObject.TrySelectBestModel(Transition); _Instance._Animations.NormalizedTime = 0; _Instance.in2DMode = previewObject.SelectedInstanceType == AnimationType.Sprite; } /************************************************************************************************************************/ private void FocusCamera() { if (InstanceRoot == null) return; var bounds = CalculateBounds(InstanceRoot); var rotation = _Instance.in2DMode ? Quaternion.identity : Quaternion.Euler(35, 135, 0); var size = bounds.extents.magnitude * 1.5f; if (size == float.PositiveInfinity) return; else if (size == 0) size = 10; _Instance.LookAt(bounds.center, rotation, size, _Instance.in2DMode, true); } /************************************************************************************************************************/ private static Bounds CalculateBounds(Transform transform) { if (transform == null) return default; var renderers = transform.GetComponentsInChildren(); if (renderers.Length == 0) return default; var bounds = renderers[0].bounds; for (int i = 1; i < renderers.Length; i++) { bounds.Encapsulate(renderers[i].bounds); } return bounds; } /************************************************************************************************************************/ #endregion /************************************************************************************************************************/ #region Execution /************************************************************************************************************************/ /// Called when the window GUI is drawn. public void OnGUI() { if (_PreviewObject != null && _PreviewObject.Graph != null && _PreviewObject.Graph.IsGraphPlaying) AnimancerGUI.RepaintEverything(); if (Selection.activeObject == _Instance && Event.current.type == EventType.KeyUp && Event.current.keyCode == KeyCode.F) FocusCamera(); } /************************************************************************************************************************/ private void DoCustomGUI(SceneView sceneView) { FollowPreviewObject(sceneView); var animancer = PreviewObject.Graph; if (animancer == null || sceneView is not TransitionPreviewWindow instance || !AnimancerUtilities.TryGetWrappedObject(Transition, out ITransitionGUI gui) || instance._TransitionProperty == null) return; EditorGUI.BeginChangeCheck(); using (new TransitionDrawer.DrawerContext(instance._TransitionProperty)) { try { gui.OnPreviewSceneGUI(new(animancer)); } catch (Exception exception) { Debug.LogException(exception); } } if (EditorGUI.EndChangeCheck()) AnimancerGUI.RepaintEverything(); } /************************************************************************************************************************/ private void FollowPreviewObject(SceneView sceneView) { var currentPreviewObjectPosition = CurrentPreviewObjectPosition; if (_PreviousPreviewObjectPosition == currentPreviewObjectPosition) return; sceneView.pivot += currentPreviewObjectPosition - _PreviousPreviewObjectPosition; _PreviousPreviewObjectPosition = currentPreviewObjectPosition; } /************************************************************************************************************************/ /// Is the `obj` a in the preview scene? public bool IsSceneObject(Object obj) => obj is GameObject gameObject && gameObject.transform.IsChildOf(PreviewSceneRoot); /************************************************************************************************************************/ [SerializeField] private List _ExpandedHierarchy; /// A list of all objects with their child hierarchy expanded. public List ExpandedHierarchy => _ExpandedHierarchy ??= new(); /************************************************************************************************************************/ #endregion /************************************************************************************************************************/ #region Cleanup /************************************************************************************************************************/ /// Called by . public void OnDisable() { duringSceneGui -= DoCustomGUI; _PreviewObject?.Dispose(); EditorSceneManager.ClosePreviewScene(_Scene); } /************************************************************************************************************************/ /// Called by . public void OnDestroy() { if (PreviewSceneRoot != null) { DestroyImmediate(PreviewSceneRoot.gameObject); PreviewSceneRoot = null; } } /************************************************************************************************************************/ #endregion /************************************************************************************************************************/ } } } #endif