From 4247dec63327871fced3e047eb8386e6c117d4c2 Mon Sep 17 00:00:00 2001 From: 4sval Date: Tue, 21 Feb 2023 23:21:14 +0100 Subject: [PATCH] timeline --- FModel/MainWindow.xaml.cs | 4 +- FModel/Views/Snooper/Animations/Animation.cs | 45 ++++++++++--------- .../Views/Snooper/Animations/TimeTracker.cs | 21 +++++++-- FModel/Views/Snooper/Options.cs | 7 ++- 4 files changed, 50 insertions(+), 27 deletions(-) diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index e78e728c..b24b2d7c 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -78,10 +78,10 @@ private async void OnLoaded(object sender, RoutedEventArgs e) #if DEBUG await _threadWorkerView.Begin(cancellationToken => _applicationView.CUE4Parse.Extract(cancellationToken, - "fortnitegame/Content/Characters/Player/Male/Medium/Bodies/M_Med_Soldier_04/Meshes/SK_M_Med_Soldier_04.uasset")); + "fortnitegame/Content/Characters/Player/Male/Medium/Bodies/M_MED_BlueGlaze/Meshes/M_MED_BlueGlaze.uasset")); await _threadWorkerView.Begin(cancellationToken => _applicationView.CUE4Parse.Extract(cancellationToken, - "fortnitegame/Content/Animation/Game/MainPlayer/Emotes/Basketball_Tricks/Basketball_Tricks_Loop_CMM_M.uasset")); + "fortnitegame/Content/Animation/Game/MainPlayer/Emotes/Troops/Emote_Troops_CMM_M.uasset")); #endif } diff --git a/FModel/Views/Snooper/Animations/Animation.cs b/FModel/Views/Snooper/Animations/Animation.cs index ae566716..004cf248 100644 --- a/FModel/Views/Snooper/Animations/Animation.cs +++ b/FModel/Views/Snooper/Animations/Animation.cs @@ -11,8 +11,9 @@ namespace FModel.Views.Snooper.Animations; public class Animation : IDisposable { - public readonly UObject Export; - public readonly CAnimSet AnimSet; + private readonly UObject _export; + private readonly CAnimSet _animSet; + public readonly string Path; public readonly string Name; public readonly Sequence[] Sequences; @@ -33,25 +34,25 @@ public class Animation : IDisposable public Animation(UObject export) { - Export = export; - Path = Export.GetPathName(); - Name = Export.Name; + _export = export; + Path = _export.GetPathName(); + Name = _export.Name; Sequences = Array.Empty(); AttachedModels = new List(); } public Animation(UObject export, CAnimSet animSet) : this(export) { - AnimSet = animSet; - TargetSkeleton = AnimSet.OriginalAnim.Name; + _animSet = animSet; + TargetSkeleton = _animSet.OriginalAnim.Name; - Sequences = new Sequence[AnimSet.Sequences.Count]; + Sequences = new Sequence[_animSet.Sequences.Count]; for (int i = 0; i < Sequences.Length; i++) { - Sequences[i] = new Sequence(AnimSet.Sequences[i]); + Sequences[i] = new Sequence(_animSet.Sequences[i]); EndTime = Sequences[i].EndTime; - TotalElapsedTime += AnimSet.Sequences[i].NumFrames * Sequences[i].TimePerFrame; + TotalElapsedTime += _animSet.Sequences[i].NumFrames * Sequences[i].TimePerFrame; } if (Sequences.Length > 0) @@ -95,29 +96,31 @@ public void Dispose() public void ImGuiAnimation(Snooper s, Save saver, ImDrawListPtr drawList, Vector2 timelineP0, Vector2 treeP0, Vector2 treeP1, Vector2 timeStep, Vector2 timeRatio, float y, float t, int i) { + var name = $"{Name}##{i}"; var p1 = new Vector2(timelineP0.X + StartTime * timeRatio.X + t, y + t); var p2 = new Vector2(timelineP0.X + EndTime * timeRatio.X - t, y + timeStep.Y - t); ImGui.SetCursorScreenPos(p1); - ImGui.InvisibleButton($"timeline_sequencetracker_{Name}##{i}", new Vector2(EndTime * timeRatio.X - t, timeStep.Y - t), ImGuiButtonFlags.MouseButtonLeft); + ImGui.InvisibleButton($"timeline_sequencetracker_{name}", new Vector2(EndTime * timeRatio.X - t, timeStep.Y - t), ImGuiButtonFlags.MouseButtonLeft); IsActive = ImGui.IsItemActive(); IsSelected = s.Renderer.Options.SelectedAnimation == i; if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) { - s.Renderer.Options.SelectedAnimation = i; + s.Renderer.Options.SelectAnimation(i); } SnimGui.Popup(() => { - s.Renderer.Options.SelectedAnimation = i; + s.Renderer.Options.SelectAnimation(i); if (ImGui.BeginMenu("Animate")) { foreach ((var guid, var model) in s.Renderer.Options.Models) { - if (ImGui.MenuItem(model.Name, model.HasSkeleton && !AttachedModels.Contains(guid))) + var selected = AttachedModels.Contains(guid); + if (ImGui.MenuItem(model.Name, null, selected, (model.HasSkeleton && !model.Skeleton.IsAnimated) || selected)) { - AttachedModels.Add(guid); + if (selected) AttachedModels.Remove(guid); else AttachedModels.Add(guid); model.Skeleton.ResetAnimatedData(true); - model.Skeleton.Animate(AnimSet, s.Renderer.AnimateWithRotationOnly); + if (!selected) model.Skeleton.Animate(_animSet, s.Renderer.AnimateWithRotationOnly); } } ImGui.EndMenu(); @@ -125,7 +128,7 @@ public void ImGuiAnimation(Snooper s, Save saver, ImDrawListPtr drawList, Vector if (ImGui.Selectable("Save")) { s.WindowShouldFreeze(true); - saver.Value = s.Renderer.Options.TrySave(Export, out saver.Label, out saver.Path); + saver.Value = s.Renderer.Options.TrySave(_export, out saver.Label, out saver.Path); s.WindowShouldFreeze(false); } ImGui.Separator(); @@ -138,8 +141,10 @@ public void ImGuiAnimation(Snooper s, Save saver, ImDrawListPtr drawList, Vector Sequences[j].DrawSequence(drawList, timelineP0.X, p2, timeStep, timeRatio, t); } - drawList.PushClipRect(treeP0 with { Y = p1.Y }, treeP1 with { Y = p2.Y }, true); - drawList.AddText(treeP0 with { Y = y + timeStep.Y / 4.0f }, 0xFFFFFFFF, Name); - drawList.PopClipRect(); + ImGui.SetCursorScreenPos(treeP0 with { Y = p1.Y }); + if (ImGui.Selectable(name, s.Renderer.Options.SelectedAnimation == i, ImGuiSelectableFlags.SpanAllColumns, new Vector2(p1.X - treeP0.X, timeStep.Y - t - t))) + { + s.Renderer.Options.SelectAnimation(i); + } } } diff --git a/FModel/Views/Snooper/Animations/TimeTracker.cs b/FModel/Views/Snooper/Animations/TimeTracker.cs index fcdba813..dd8de3f3 100644 --- a/FModel/Views/Snooper/Animations/TimeTracker.cs +++ b/FModel/Views/Snooper/Animations/TimeTracker.cs @@ -141,12 +141,14 @@ public void ImGuiTimeline(Snooper s, Save saver, Dictionary ico } } + ImGui.PushStyleVar(ImGuiStyleVar.SelectableTextAlign, new Vector2(0.0f, 0.5f)); for (int i = 0; i < animations.Count; i++) { var y = timelineP0.Y + _timeBarHeight + _timeStep.Y * i; animations[i].ImGuiAnimation(s, saver, drawList, timelineP0, treeP0, treeP1, _timeStep, timeRatio, y, _thickness, i); DrawSeparator(drawList, timelineP0, y + _timeStep.Y, animations[i].EndTime * timeRatio.X, ETrackerType.End); } + ImGui.PopStyleVar(); for (int i = 0; i < animations.Count; i++) { @@ -165,7 +167,13 @@ public void ImGuiTimeline(Snooper s, Save saver, Dictionary ico private void DrawSeparator(ImDrawListPtr drawList, Vector2 origin, float y, float time, ETrackerType separatorType) { - const int size = 5; + float size = separatorType switch + { + ETrackerType.Frame => 5, + ETrackerType.End => 5, + ETrackerType.InBetween => 7.5f, + _ => throw new ArgumentOutOfRangeException(nameof(separatorType), separatorType, null) + }; Vector2 p1 = separatorType switch { @@ -180,11 +188,10 @@ private void DrawSeparator(ImDrawListPtr drawList, Vector2 origin, float y, floa { ETrackerType.Frame => 0xFF6F6F6F, ETrackerType.End => 0xFF2E3E82, - ETrackerType.InBetween => 0x50FFFFFF, + ETrackerType.InBetween => 0xA0FFFFFF, _ => throw new ArgumentOutOfRangeException(nameof(separatorType), separatorType, null) }; - drawList.AddLine(p1, p2, color, 1f); switch (separatorType) { case ETrackerType.Frame: @@ -193,13 +200,19 @@ private void DrawSeparator(ImDrawListPtr drawList, Vector2 origin, float y, floa var xr = p1.X + size; var yb = origin.Y + _timeBarHeight - _timeHeight / 2.0f; + drawList.AddLine(p1, p2, color, 1f); drawList.AddQuadFilled(origin with { X = xl }, origin with { X = xr }, new Vector2(xr, yb), new Vector2(xl, yb), color); drawList.AddTriangleFilled(new Vector2(xl, yb), new Vector2(xr, yb), p1, color); break; case ETrackerType.End: - case ETrackerType.InBetween: + drawList.AddLine(p1, p2, color, 1f); drawList.AddTriangleFilled(p1, p1 with { X = p1.X - size }, p1 with { Y = p1.Y + size }, color); break; + case ETrackerType.InBetween: + p1.Y += _timeBarHeight; + drawList.AddLine(p1, p2, color, 1f); + drawList.AddTriangleFilled(p1, new Vector2(p1.X - size / 2.0f, p1.Y - size), new Vector2(p1.X + size / 2.0f, p1.Y - size), color); + break; default: throw new ArgumentOutOfRangeException(nameof(separatorType), separatorType, null); } diff --git a/FModel/Views/Snooper/Options.cs b/FModel/Views/Snooper/Options.cs index 441790c5..361ae4d1 100644 --- a/FModel/Views/Snooper/Options.cs +++ b/FModel/Views/Snooper/Options.cs @@ -20,7 +20,7 @@ public class Options public FGuid SelectedModel { get; private set; } public int SelectedSection { get; private set; } public int SelectedMorph { get; private set; } - public int SelectedAnimation{ get; set; } + public int SelectedAnimation{ get; private set; } public readonly Dictionary Models; public readonly Dictionary Textures; @@ -98,6 +98,11 @@ public void SelectModel(FGuid guid) SelectedMorph = 0; } + public void SelectAnimation(int animation) + { + SelectedAnimation = animation; + } + public void RemoveModel(FGuid guid) { if (!TryGetModel(guid, out var model)) return;