// 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