Add **Nested Animations** for compositing animation clips
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2012-2022 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;
|
||||
@@ -24,6 +26,7 @@ namespace FlaxEditor.GUI.Timeline
|
||||
}
|
||||
|
||||
private AnimationPreview _preview;
|
||||
internal Guid _id;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the animated preview used for the animation playback.
|
||||
@@ -62,6 +65,7 @@ namespace FlaxEditor.GUI.Timeline
|
||||
TrackArchetypes.Add(AnimationChannelTrack.GetArchetype());
|
||||
TrackArchetypes.Add(AnimationChannelDataTrack.GetArchetype());
|
||||
TrackArchetypes.Add(AnimationEventTrack.GetArchetype());
|
||||
TrackArchetypes.Add(NestedAnimationTrack.GetArchetype());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -169,5 +173,48 @@ namespace FlaxEditor.GUI.Timeline
|
||||
|
||||
base.OnSeek(frame);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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<Animation>(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<Animation>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
183
Source/Editor/GUI/Timeline/Tracks/NestedAnimationTrack.cs
Normal file
183
Source/Editor/GUI/Timeline/Tracks/NestedAnimationTrack.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FlaxEditor.GUI.Timeline.Undo;
|
||||
using FlaxEditor.Utilities;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
{
|
||||
/// <summary>
|
||||
/// The timeline media that represents a nested animation media event.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.GUI.Timeline.Media" />
|
||||
public class NestedAnimationMedia : Media
|
||||
{
|
||||
private sealed class Proxy : ProxyBase<NestedAnimationTrack, NestedAnimationMedia>
|
||||
{
|
||||
/// <summary>
|
||||
/// The nested animation to play.
|
||||
/// </summary>
|
||||
[EditorDisplay("General"), EditorOrder(10)]
|
||||
public Animation NestedAnimation
|
||||
{
|
||||
get => Track.Asset;
|
||||
set => Track.Asset = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If checked, the nested animation will loop when playback exceeds its duration. Otherwise it will stop play.
|
||||
/// </summary>
|
||||
[EditorDisplay("General"), EditorOrder(20)]
|
||||
public bool Loop
|
||||
{
|
||||
get => Track.Loop;
|
||||
set => Track.Loop = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Animation playback speed.
|
||||
/// </summary>
|
||||
[EditorDisplay("General"), EditorOrder(30), Limit(0.0f, 100.0f, 0.001f)]
|
||||
public float Speed
|
||||
{
|
||||
get => Track.Speed;
|
||||
set => Track.Speed = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Animation playback start time position (in seconds). Can be used to offset the nested animation start.
|
||||
/// </summary>
|
||||
[EditorDisplay("General"), EditorOrder(40)]
|
||||
public float StartTime
|
||||
{
|
||||
get => Track.StartTime;
|
||||
set => Track.StartTime = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Proxy(NestedAnimationTrack track, NestedAnimationMedia media)
|
||||
: base(track, media)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnTimelineChanged(Track track)
|
||||
{
|
||||
base.OnTimelineChanged(track);
|
||||
|
||||
PropertiesEditObject = track != null ? new Proxy((NestedAnimationTrack)track, this) : null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The timeline track that represents a nested animation playback.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.GUI.Timeline.Track" />
|
||||
public class NestedAnimationTrack : SingleMediaAssetTrack<Animation, NestedAnimationMedia>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the archetype.
|
||||
/// </summary>
|
||||
/// <returns>The archetype.</returns>
|
||||
public static TrackArchetype GetArchetype()
|
||||
{
|
||||
return new TrackArchetype
|
||||
{
|
||||
TypeId = 20,
|
||||
Name = "Nested Animation",
|
||||
Create = options => new NestedAnimationTrack(ref options),
|
||||
Load = LoadTrack,
|
||||
Save = SaveTrack,
|
||||
};
|
||||
}
|
||||
|
||||
private static void LoadTrack(int version, Track track, BinaryReader stream)
|
||||
{
|
||||
var e = (NestedAnimationTrack)track;
|
||||
Guid id = stream.ReadGuid();
|
||||
e.Asset = FlaxEngine.Content.LoadAsync<Animation>(id);
|
||||
var m = e.TrackMedia;
|
||||
m.StartFrame = (int)stream.ReadSingle();
|
||||
m.DurationFrames = (int)stream.ReadSingle();
|
||||
e.Speed = stream.ReadSingle();
|
||||
e.StartTime = stream.ReadSingle();
|
||||
}
|
||||
|
||||
private static void SaveTrack(Track track, BinaryWriter stream)
|
||||
{
|
||||
var e = (NestedAnimationTrack)track;
|
||||
stream.WriteGuid(ref e.AssetID);
|
||||
if (e.Media.Count != 0)
|
||||
{
|
||||
var m = e.TrackMedia;
|
||||
stream.Write((float)m.StartFrame);
|
||||
stream.Write((float)m.DurationFrames);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.Write((float)0);
|
||||
stream.Write((float)track.Timeline.DurationFrames);
|
||||
}
|
||||
stream.Write(e.Speed);
|
||||
stream.Write(e.StartTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Nested animation playback speed.
|
||||
/// </summary>
|
||||
public float Speed = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Nested animation playback start time position (in seconds). Can be used to offset the nested animation start.
|
||||
/// </summary>
|
||||
public float StartTime = 0.0f;
|
||||
|
||||
/// <inheritdoc />
|
||||
public NestedAnimationTrack(ref TrackCreateOptions options)
|
||||
: base(ref options)
|
||||
{
|
||||
MinMediaCount = 1;
|
||||
MaxMediaCount = 1;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAssetChanged()
|
||||
{
|
||||
base.OnAssetChanged();
|
||||
|
||||
CheckCyclicReferences();
|
||||
|
||||
if (Timeline != null && Asset && !Asset.WaitForLoaded())
|
||||
{
|
||||
using (new TrackUndoBlock(this))
|
||||
TrackMedia.Duration = Asset.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnTimelineChanged(Timeline timeline)
|
||||
{
|
||||
base.OnTimelineChanged(timeline);
|
||||
|
||||
CheckCyclicReferences();
|
||||
}
|
||||
|
||||
private void CheckCyclicReferences()
|
||||
{
|
||||
if (Asset && Timeline is AnimationTimeline timeline)
|
||||
{
|
||||
var refs = Asset.GetReferences();
|
||||
var id = timeline._id;
|
||||
if (Asset.ID == id || refs.Contains(id))
|
||||
{
|
||||
Asset = null;
|
||||
throw new Exception("Cannot use nested scene animation (recursion).");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,8 +42,11 @@ namespace FlaxEditor.GUI.Timeline.Undo
|
||||
track.Flags = (TrackFlags)stream.ReadByte();
|
||||
track.Archetype.Load(Timeline.FormatVersion, track, stream);
|
||||
}
|
||||
_timeline.ArrangeTracks();
|
||||
_timeline.MarkAsEdited();
|
||||
if (_timeline != null)
|
||||
{
|
||||
_timeline.ArrangeTracks();
|
||||
_timeline.MarkAsEdited();
|
||||
}
|
||||
track.OnUndo();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user