// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using FlaxEditor.Content;
using FlaxEditor.GUI.Drag;
using FlaxEditor.GUI.Timeline.Tracks;
using FlaxEditor.Viewport.Previews;
using FlaxEngine;
using Object = FlaxEngine.Object;
namespace FlaxEditor.GUI.Timeline
{
///
/// The timeline editor for animation asset.
///
///
public sealed class AnimationTimeline : Timeline
{
private sealed class Proxy : ProxyBase
{
///
public Proxy(AnimationTimeline timeline)
: base(timeline)
{
}
}
private AnimationPreview _preview;
internal Guid _id;
///
/// Gets or sets the animated preview used for the animation playback.
///
public AnimationPreview Preview
{
get => _preview;
set
{
if (_preview == value)
return;
_preview = value;
value?.PreviewActor.UpdateAnimation();
UpdatePlaybackState();
PreviewChanged?.Invoke();
}
}
///
/// Occurs when the selected animated model preview gets changed.
///
public event Action PreviewChanged;
///
/// Initializes a new instance of the class.
///
/// The undo/redo to use for the history actions recording. Optional, can be null to disable undo support.
public AnimationTimeline(FlaxEditor.Undo undo)
: base(PlaybackButtons.Play | PlaybackButtons.Stop, undo, false, true)
{
PlaybackState = PlaybackStates.Seeking;
ShowPreviewValues = false;
PropertiesEditObject = new Proxy(this);
// Setup track types
TrackArchetypes.Add(AnimationChannelTrack.GetArchetype());
TrackArchetypes.Add(AnimationChannelDataTrack.GetArchetype());
TrackArchetypes.Add(AnimationEventTrack.GetArchetype());
TrackArchetypes.Add(NestedAnimationTrack.GetArchetype());
}
///
/// Loads the timeline from the specified asset.
///
/// The asset.
public void Load(Animation asset)
{
Profiler.BeginEvent("Asset.LoadTimeline");
asset.LoadTimeline(out var data);
Profiler.EndEvent();
Profiler.BeginEvent("Timeline.Load");
Load(data);
Profiler.EndEvent();
}
///
/// Saves the timeline data to the asset.
///
/// The asset.
public void Save(Animation asset)
{
var data = Save();
asset.SaveTimeline(data);
asset.Reload();
}
private void UpdatePlaybackState()
{
PlaybackStates state;
ShowPlaybackButtonsArea = _preview != null;
if (_preview != null)
{
if (_preview.PlayAnimation)
{
state = PlaybackStates.Playing;
}
else
{
state = PlaybackStates.Paused;
}
var time = Editor.Internal_GetAnimationTime(Object.GetUnmanagedPtr(_preview.PreviewActor));
CurrentFrame = (int)(time * FramesPerSecond);
}
else
{
state = PlaybackStates.Seeking;
}
PlaybackState = state;
}
///
public override void Update(float deltaTime)
{
base.Update(deltaTime);
UpdatePlaybackState();
}
///
public override void OnPlay()
{
var time = CurrentTime;
_preview.Play();
if (_preview != null)
{
Editor.Internal_SetAnimationTime(Object.GetUnmanagedPtr(_preview.PreviewActor), time);
}
base.OnPlay();
}
///
public override void OnPause()
{
_preview.Pause();
base.OnPause();
}
///
public override void OnStop()
{
_preview.Stop();
base.OnStop();
}
///
public override void OnSeek(int frame)
{
if (_preview != null)
{
frame = Mathf.Clamp(frame, 0, DurationFrames - 1);
var time = frame / FramesPerSecond;
Editor.Internal_SetAnimationTime(Object.GetUnmanagedPtr(_preview.PreviewActor), time);
if (!_preview.PlayAnimation)
_preview.PreviewActor.UpdateAnimation();
}
else
{
CurrentFrame = frame;
}
base.OnSeek(frame);
}
///
protected override void SetupDragDrop()
{
base.SetupDragDrop();
DragHandlers.Add(new DragHandler(new DragAssets(IsValidAsset), OnDragAsset));
}
private static bool IsValidAsset(AssetItem assetItem)
{
if (assetItem is BinaryAssetItem binaryAssetItem)
{
if (typeof(Animation).IsAssignableFrom(binaryAssetItem.Type))
{
var sceneAnimation = FlaxEngine.Content.LoadAsync(binaryAssetItem.ID);
if (sceneAnimation)
return true;
}
}
return false;
}
private static void OnDragAsset(Timeline timeline, DragHelper drag)
{
foreach (var assetItem in ((DragAssets)drag).Objects)
{
if (assetItem is BinaryAssetItem binaryAssetItem)
{
if (typeof(Animation).IsAssignableFrom(binaryAssetItem.Type))
{
var animation = FlaxEngine.Content.LoadAsync(binaryAssetItem.ID);
if (!animation || animation.WaitForLoaded())
continue;
var track = (NestedAnimationTrack)timeline.NewTrack(NestedAnimationTrack.GetArchetype());
track.Asset = animation;
track.TrackMedia.DurationFrames = (int)(animation.Length * timeline.FramesPerSecond);
track.Rename(assetItem.ShortName);
timeline.AddTrack(track);
}
}
}
}
}
}