123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523 |
- // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
- #if UNITY_EDITOR && UNITY_IMGUI
- using Animancer.Editor.Previews;
- using Animancer.TransitionLibraries;
- using System;
- using UnityEditor;
- using UnityEngine;
- using static Animancer.Editor.AnimancerGUI;
- using Object = UnityEngine.Object;
- namespace Animancer.Editor.TransitionLibraries
- {
- /// <summary>[Editor-Only] Custom preview for <see cref="TransitionLibrarySelection"/>.</summary>
- /// <remarks>Parts of this class are based on Unity's <see cref="MeshPreview"/>.</remarks>
- /// https://kybernetik.com.au/animancer/api/Animancer.Editor.TransitionLibraries/TransitionLibrarySelectionPreview
- [CustomPreview(typeof(TransitionLibrarySelection))]
- public class TransitionLibrarySelectionPreview : ObjectPreview
- {
- /************************************************************************************************************************/
- [SerializeField] private AnimancerPreviewRenderer _PreviewRenderer;
- [SerializeField] private TransitionPreviewPlayer _PreviewPlayer;
- [NonSerialized] private TransitionLibrarySelection _Target;
- [NonSerialized] private int _TargetVersion = -1;
- [NonSerialized] private readonly TransitionLibrarySelectionPreviewSpeed Speed = new();
- /************************************************************************************************************************/
- /// <inheritdoc/>
- public override void Initialize(Object[] targets)
- {
- _PreviewRenderer ??= new();
- _PreviewPlayer ??= new();
- if (targets.Length == 1)
- {
- _Target = targets[0] as TransitionLibrarySelection;
- if (_Target != null)
- {
- _TargetVersion = _Target.Version - 1;
- if (_Target.Window != null)
- _PreviewRenderer.PreviewObject.TrySelectBestModel(_Target.Window.Data);
- CheckTarget();
- }
- }
- base.Initialize(targets);
- }
- /************************************************************************************************************************/
- /// <inheritdoc/>
- public override void Cleanup()
- {
- base.Cleanup();
- _PreviewPlayer?.Dispose();
- _PreviewPlayer = null;
- _PreviewRenderer?.Dispose();
- _PreviewRenderer = null;
- }
- /************************************************************************************************************************/
- /// <summary>Handles changes to the target object.</summary>
- private void CheckTarget()
- {
- if (_TargetVersion == _Target.Version)
- return;
- _TargetVersion = _Target.Version;
- _PreviewPlayer.IsPlaying = false;
- switch (_Target.Type)
- {
- case TransitionLibrarySelection.SelectionType.FromTransition:
- _PreviewPlayer.FromTransition = _Target.FromTransition;
- _PreviewPlayer.ToTransition = null;
- break;
- case TransitionLibrarySelection.SelectionType.ToTransition:
- _PreviewPlayer.FromTransition = null;
- _PreviewPlayer.ToTransition = _Target.ToTransition;
- break;
- case TransitionLibrarySelection.SelectionType.Modifier:
- _PreviewPlayer.FromTransition = _Target.FromTransition;
- _PreviewPlayer.ToTransition = _Target.ToTransition;
- break;
- }
- }
- /************************************************************************************************************************/
- /// <summary>Updates the settings of the <see cref="TransitionPreviewPlayer"/>.</summary>
- private void UpdatePlayerSettings()
- {
- _PreviewPlayer.Graph = _PreviewRenderer.PreviewObject.Graph;
- _PreviewPlayer.FadeDuration = _Target.FadeDuration;
- _PreviewPlayer.Speed = Speed.Speed;
- _PreviewPlayer.RecalculateTimeBounds();
- }
- /************************************************************************************************************************/
- private static readonly GUIContent
- Title = new("Preview");
- /// <inheritdoc/>
- public override GUIContent GetPreviewTitle()
- => Title;
- /************************************************************************************************************************/
- /// <inheritdoc/>
- public override bool HasPreviewGUI()
- => _Target != null
- && _Target.Type switch
- {
- TransitionLibrarySelection.SelectionType.FromTransition or
- TransitionLibrarySelection.SelectionType.ToTransition or
- TransitionLibrarySelection.SelectionType.Modifier
- => true,
- _ => false,
- };
- /************************************************************************************************************************/
- #region Header Settings
- /************************************************************************************************************************/
- private static GUIStyle _ToolbarButtonStyle;
- /// <inheritdoc/>
- public override void OnPreviewSettings()
- {
- CheckTarget();
- _ToolbarButtonStyle ??= new(EditorStyles.toolbarButton)
- {
- padding = new(),
- };
- var area = GUILayoutUtility.GetRect(LineHeight * 1.5f, LineHeight);
- DoPlayPauseToggle(area, _ToolbarButtonStyle);
- area = GUILayoutUtility.GetRect(LineHeight * 2f, LineHeight);
- Speed.DoToggleGUI(area, _ToolbarButtonStyle);
- }
- /************************************************************************************************************************/
- /// <summary>Draws a toggle to play and pause the preview.</summary>
- private void DoPlayPauseToggle(Rect area, GUIStyle style)
- {
- if (TryUseClickEvent(area, 1) || TryUseClickEvent(area, 2))
- _PreviewPlayer.CurrentTime = _PreviewPlayer.MinTime;
- _PreviewPlayer.IsPlaying = AnimancerGUI.DoPlayPauseToggle(
- area,
- _PreviewPlayer.IsPlaying,
- style,
- "Left Click = Play/Pause\nRight Click = Reset Time");
- }
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- /// <inheritdoc/>
- public override void OnInteractivePreviewGUI(Rect area, GUIStyle background)
- {
- if (_Target == null)
- return;
- CheckTarget();
- UpdatePlayerSettings();
- DoSettingsGUI(ref area);
- DoTimelineGUI(ref area);
- _PreviewRenderer.DoGUI(area, background);
- AnimancerPreviewObjectGUI.HandleDragAndDrop(area, _PreviewRenderer.PreviewObject);
- }
- /************************************************************************************************************************/
- /// <summary>Draws settings for modifying the preview.</summary>
- private void DoSettingsGUI(ref Rect area)
- {
- if (!Speed.IsOn)
- return;
- area.yMin += StandardSpacing;
- Speed.DoSpeedSlider(ref area, EditorStyles.toolbar);
- var preview = _PreviewRenderer.PreviewObject;
- var height = AnimancerPreviewObjectGUI.CalculateHeight(preview);
- var settingsArea = StealFromTop(ref area, height, StandardSpacing);
- settingsArea = settingsArea.Expand(-StandardSpacing, 0);
- GUI.Label(settingsArea, GUIContent.none, EditorStyles.toolbar);
- AnimancerPreviewObjectGUI.DoModelGUI(settingsArea, preview);
- }
- /************************************************************************************************************************/
- #region Timeline
- /************************************************************************************************************************/
- /// <summary>Draws the preview timeline.</summary>
- private void DoTimelineGUI(ref Rect area)
- {
- var timelineArea = StealFromTop(ref area, EditorStyles.toolbar.fixedHeight, StandardSpacing);
- EditorGUI.DrawRect(timelineArea, Grey(0.25f, 0.3f));
- EditorGUI.DrawRect(new(timelineArea.x, timelineArea.yMax - 1, timelineArea.width, 1), Grey(0, 0.5f));
- DoFadeDurationSliderGUI(timelineArea);
- DoTimeSliderGUI(timelineArea);
- }
- /************************************************************************************************************************/
- private static readonly int SliderHash = "Slider".GetHashCode();
- /************************************************************************************************************************/
- /// <summary>Draws the fade duration slider.</summary>
- private void DoFadeDurationSliderGUI(Rect area)
- {
- if (!CalculateFadeBounds(area, out var startFadeX, out var endFadeX))
- return;
- switch (_Target.Type)
- {
- default:
- return;
- case TransitionLibrarySelection.SelectionType.FromTransition:
- case TransitionLibrarySelection.SelectionType.ToTransition:
- case TransitionLibrarySelection.SelectionType.Modifier:
- break;
- }
- var sliderArea = area;
- sliderArea.width = LineHeight * 0.5f;
- sliderArea.x = endFadeX - sliderArea.width * 0.5f;
- var control = new GUIControl(sliderArea, SliderHash);
- switch (control.EventType)
- {
- case EventType.MouseDown:
- if (control.TryUseMouseDown())
- _PreviewPlayer.IsPlaying = false;
- break;
- case EventType.MouseUp:
- control.TryUseMouseUp();
- break;
- case EventType.MouseDrag:
- if (control.TryUseHotControl())
- {
- var x = Math.Max(startFadeX, control.Event.mousePosition.x);
- var normalizedTime = area.InverseLerpUnclampedX(x);
- var normalizedStartFade = area.InverseLerpUnclampedX(startFadeX);
- _PreviewPlayer.NormalizedTime = normalizedTime;
- var fadeDuration =
- _PreviewPlayer.LerpTimeUnclamped(normalizedTime) -
- _PreviewPlayer.LerpTimeUnclamped(normalizedStartFade);
- var selected = _Target.Selected;
- if (selected is TransitionModifierDefinition modifier)
- {
- _Target.Window.RecordUndo()
- .SetModifier(modifier.WithFadeDuration(fadeDuration));
- }
- else if (selected is TransitionAssetBase transitionAsset)
- {
- if (fadeDuration < 0)
- fadeDuration = 0;
- using var serializedObject = new SerializedObject(transitionAsset);
- var property = serializedObject.FindProperty(TransitionAssetBase.TransitionField);
- property = property.FindPropertyRelative("_" + nameof(ITransition.FadeDuration));
- property.floatValue = fadeDuration;
- serializedObject.ApplyModifiedProperties();
- }
- _Target.Window.Repaint();
- }
- break;
- case EventType.Repaint:
- var color = AnimancerStateDrawerColors.FadeLineColor;
- var showCursor = GUIUtility.hotControl == 0 || GUIUtility.hotControl == control.ID;
- if (showCursor)
- EditorGUIUtility.AddCursorRect(sliderArea, MouseCursor.ResizeHorizontal);
- if (!showCursor || !sliderArea.Contains(control.Event.mousePosition))
- color.a *= 0.5f;
- EditorGUI.DrawRect(
- new(endFadeX, sliderArea.y, 1, sliderArea.height - 1),
- color);
- break;
- }
- }
- /************************************************************************************************************************/
- /// <summary>Draws the preview time slider.</summary>
- private void DoTimeSliderGUI(Rect area)
- {
- var control = new GUIControl(area, SliderHash);
- switch (control.EventType)
- {
- case EventType.MouseDown:
- if (control.TryUseMouseDown())
- {
- _ForceClampTime = true;
- _DidWrapTime = false;
- HandleDragTime(area, control.Event);
- _ForceClampTime = control.Event.control;
- if (!_ForceClampTime)
- EditorGUIUtility.SetWantsMouseJumping(1);
- _PreviewPlayer.IsPlaying = control.Event.clickCount > 1;
- }
- break;
- case EventType.MouseUp:
- if (control.TryUseMouseUp())
- EditorGUIUtility.SetWantsMouseJumping(0);
- break;
- case EventType.MouseDrag:
- if (control.TryUseHotControl())
- HandleDragTime(area, control.Event);
- break;
- case EventType.Repaint:
- BeginTriangles(AnimancerStateDrawerColors.FadeLineColor);
- if (CalculateFadeBounds(area, out var startFadeX, out var endFadeX))
- {
- // Fade.
- DrawLineBatched(
- new(startFadeX, area.yMin + 1),
- new(endFadeX, area.yMax - 1),
- 1);
- // To.
- if (endFadeX < area.xMax)
- DrawLineBatched(
- new(endFadeX, area.yMax - 1),
- new(area.xMax, area.yMax - 1),
- 1);
- }
- // From.
- if (area.xMin < startFadeX)
- DrawLineBatched(
- new(area.xMin, area.yMin + 1),
- new(startFadeX, area.yMin + 1),
- 1);
- var color = _PreviewPlayer.IsPlaying
- ? AnimancerStateDrawerColors.PlayingBarColor
- : AnimancerStateDrawerColors.PausedBarColor;
- color.a = 1;
- var timeX = area.LerpUnclampedX(_PreviewPlayer.NormalizedTime);
- GL.Color(color);
- DrawLineBatched(new(timeX, area.yMin), new(timeX, area.yMax), 2);
- EndTriangles();
- DoTransitionLabels(area);
- break;
- }
- }
- /************************************************************************************************************************/
- private bool _ForceClampTime;
- private bool _DidWrapTime;
- /// <summary>Draws handles drag events to control the preview time.</summary>
- private void HandleDragTime(Rect area, Event currentEvent)
- {
- if (_ForceClampTime)
- {
- _PreviewPlayer.NormalizedTime = area.InverseLerpUnclampedX(currentEvent.mousePosition.x);
- return;
- }
- var delta = currentEvent.delta.x;
- var normalizedTime = _PreviewPlayer.NormalizedTime;
- if (normalizedTime == 0 && !_DidWrapTime && delta > 0)
- {
- var x = currentEvent.mousePosition.x;
- if (area.xMin > x || area.xMax < x)
- return;
- }
- normalizedTime += delta / area.width;
- if (normalizedTime >= 0 || _DidWrapTime)
- {
- if (normalizedTime > 1)
- _DidWrapTime = true;
- normalizedTime = AnimancerUtilities.Wrap01(normalizedTime);
- }
- else
- {
- normalizedTime = 0;
- }
- _PreviewPlayer.NormalizedTime = normalizedTime;
- }
- /************************************************************************************************************************/
- /// <summary>Calculates the start and end pixels of the fade.</summary>
- private bool CalculateFadeBounds(
- Rect area,
- out float startFadeX,
- out float endFadeX)
- {
- var fadeDuration = _Target.FadeDuration;
- if (!float.IsNaN(fadeDuration))
- {
- startFadeX = area.LerpUnclampedX(_PreviewPlayer.InverseLerpTimeUnclamped(0));
- endFadeX = area.LerpUnclampedX(_PreviewPlayer.InverseLerpTimeUnclamped(fadeDuration));
- if (_Target.FromTransition.IsValid())
- {
- if (!_Target.ToTransition.IsValid())
- {
- endFadeX -= startFadeX;
- startFadeX = area.xMin;
- }
- return true;
- }
- else
- {
- if (_Target.ToTransition.IsValid())
- {
- return true;
- }
- }
- }
- startFadeX = area.LerpUnclampedX(_PreviewPlayer.InverseLerpTimeUnclamped(0));
- endFadeX = startFadeX;
- return false;
- }
- /************************************************************************************************************************/
- /// <summary>Draws labels for the selected transitions.</summary>
- private void DoTransitionLabels(Rect area)
- {
- area.xMin += 1;
- area.xMax -= 2;
- var mid = area.width * 0.5f;
- var leftArea = area;
- var rightArea = area;
- var fromTransition = _Target.FromTransition;
- var toTransition = _Target.ToTransition;
- var hasFrom = fromTransition.IsValid();
- var hasTo = toTransition.IsValid();
- if (hasFrom && hasTo)
- {
- leftArea.width = mid - StandardSpacing * 0.5f;
- rightArea.x = area.xMax - leftArea.width;
- rightArea.width = leftArea.width;
- }
- if (hasFrom)
- GUI.Label(leftArea, _Target.FromTransition.GetCachedName());
- if (hasTo)
- GUI.Label(rightArea, _Target.ToTransition.GetCachedName(), RightLabelStyle);
- }
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- }
- }
- #endif
|