From 4eb220168ebe042d5d620ef41349b5d1cb89087c Mon Sep 17 00:00:00 2001 From: 4sval Date: Fri, 17 Feb 2023 04:58:29 +0100 Subject: [PATCH] new timeline part 1 --- FModel/FModel.csproj | 6 + FModel/MainWindow.xaml.cs | 3 +- FModel/Resources/tl_forward.png | Bin 0 -> 210 bytes FModel/Resources/tl_next.png | Bin 0 -> 221 bytes FModel/Resources/tl_pause.png | Bin 0 -> 153 bytes FModel/Resources/tl_play.png | Bin 0 -> 183 bytes FModel/Resources/tl_previous.png | Bin 0 -> 219 bytes FModel/Resources/tl_rewind.png | Bin 0 -> 222 bytes FModel/Views/Snooper/Animations/Animation.cs | 25 +++- FModel/Views/Snooper/Animations/Skeleton.cs | 25 ++-- .../Views/Snooper/Animations/TimeTracker.cs | 122 +++++++++++++----- FModel/Views/Snooper/Buffers/BufferObject.cs | 7 + FModel/Views/Snooper/Models/Model.cs | 5 + FModel/Views/Snooper/Options.cs | 49 ++++++- FModel/Views/Snooper/Renderer.cs | 17 +-- FModel/Views/Snooper/SnimGui.cs | 9 +- 16 files changed, 208 insertions(+), 60 deletions(-) create mode 100644 FModel/Resources/tl_forward.png create mode 100644 FModel/Resources/tl_next.png create mode 100644 FModel/Resources/tl_pause.png create mode 100644 FModel/Resources/tl_play.png create mode 100644 FModel/Resources/tl_previous.png create mode 100644 FModel/Resources/tl_rewind.png diff --git a/FModel/FModel.csproj b/FModel/FModel.csproj index 350dc4ac..6182ae44 100644 --- a/FModel/FModel.csproj +++ b/FModel/FModel.csproj @@ -230,6 +230,12 @@ + + + + + + diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index f5214a29..e78e728c 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -45,6 +45,7 @@ public partial class MainWindow private async void OnLoaded(object sender, RoutedEventArgs e) { + var newOrUpdated = UserSettings.Default.ShowChangelog; #if !DEBUG ApplicationService.ApiEndpointView.FModelApi.CheckForUpdates(UserSettings.Default.UpdateMode); #endif @@ -67,7 +68,7 @@ public partial class MainWindow await _applicationView.CUE4Parse.InitInformation(); #endif await _applicationView.CUE4Parse.InitMappings(); - await _applicationView.InitImGuiSettings(); + await _applicationView.InitImGuiSettings(newOrUpdated); await _applicationView.InitVgmStream(); await _applicationView.InitOodle(); diff --git a/FModel/Resources/tl_forward.png b/FModel/Resources/tl_forward.png new file mode 100644 index 0000000000000000000000000000000000000000..b65b10fde2bd4747b1a4a98882e56917f6abf427 GIT binary patch literal 210 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjGd*1#Lp(a)PTt6S$brXozJRNd zqg9%sZjh0)mX;B-^9Dv`2maR+I2Zip2oT}YiFOx#Iq%84x%=NtKADo%vSh2BjOtpI z#3`@;x?GAAHutnvKJay4q=fal2IhjIe+Ry%>bNc$oQ|+Nb_KX&Vg9{nVC;aeOtG@P3O=Yx0no2p7@%FTp$_EPf|34cO z+Fp76oic|bhtmV@d7k;x^f|r0>$N@5=kIfL(PGOf3(37QH34WBgQu&X%Q~loCIAx@ BH!A=D literal 0 HcmV?d00001 diff --git a/FModel/Resources/tl_play.png b/FModel/Resources/tl_play.png new file mode 100644 index 0000000000000000000000000000000000000000..9784d86ef51415d052b672a9176e2a2281b05952 GIT binary patch literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gjb)GJcAs(G?FD>M4Fc5IKs6BZD zGxtMD=6?5$Y<#WB%q)qM`~`fyf@ZP3i-~Vq^4~(sHIYA}LHGqjWwqVoOtx7OF4_-x zuQlG5tLt?=ARWv&UHF9L5leB~)u$?d^7lH--`pAAd3U`-?$^oDtFx_IgR@!99(4Gw g71HQ35!}geY^&YERf$h{fG%P1boFyt=akR{0BAWtJpcdz literal 0 HcmV?d00001 diff --git a/FModel/Resources/tl_previous.png b/FModel/Resources/tl_previous.png new file mode 100644 index 0000000000000000000000000000000000000000..2e201684009152e5d7f29c51cfcd1b276b1faab6 GIT binary patch literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gji#%N%Lp(a)UNPijbQEd-81Le_ zw1}a%fI(DNynw~ZJ)n?TR{S9gpM!cK*WVMzUYvNh;r83gTe~DWM4f#f@0Z literal 0 HcmV?d00001 diff --git a/FModel/Resources/tl_rewind.png b/FModel/Resources/tl_rewind.png new file mode 100644 index 0000000000000000000000000000000000000000..66e62918ba9cabee560644ee10297d16a4c5e847 GIT binary patch literal 222 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjOFdm2Lp(a)PTt6S$brXozJO}T z83*^W1C4X3m2N*XJ)_l5701 zVtUL)l`qmJ4vaN(KCDRh`@nMTz|obwl`BQB9uTas?p+l3V)4|2{b9S#8qKt4%wVj# WeJVs!V3!xr!3>_RelF{r5}E+|m05NG literal 0 HcmV?d00001 diff --git a/FModel/Views/Snooper/Animations/Animation.cs b/FModel/Views/Snooper/Animations/Animation.cs index 93650eb5..be5bc26b 100644 --- a/FModel/Views/Snooper/Animations/Animation.cs +++ b/FModel/Views/Snooper/Animations/Animation.cs @@ -1,14 +1,18 @@ using System; using System.Collections.Generic; +using System.Numerics; using CUE4Parse_Conversion.Animations; using CUE4Parse.UE4.Objects.Core.Misc; using CUE4Parse.Utils; +using ImGuiNET; namespace FModel.Views.Snooper.Animations; public class Animation : IDisposable { + public readonly string Name; public readonly Sequence[] Sequences; + public readonly float StartTime; // Animation Start Time public readonly float EndTime; // Animation End Time public readonly float TotalElapsedTime; // Animation Max Time @@ -23,8 +27,9 @@ public class Animation : IDisposable AttachedModels = new List(); } - public Animation(CAnimSet animSet) : this() + public Animation(string name, CAnimSet animSet) : this() { + Name = name; Sequences = new Sequence[animSet.Sequences.Count]; for (int i = 0; i < Sequences.Length; i++) { @@ -34,11 +39,11 @@ public class Animation : IDisposable EndTime = Sequences[i].EndTime; } - // if (Sequences.Length > 0) - // Tracker.ElapsedTime = Sequences[0].StartTime; + if (Sequences.Length > 0) + StartTime = Sequences[0].StartTime; } - public Animation(CAnimSet animSet, params FGuid[] animatedModels) : this(animSet) + public Animation(string name, CAnimSet animSet, params FGuid[] animatedModels) : this(name, animSet) { AttachedModels.AddRange(animatedModels); } @@ -72,4 +77,16 @@ public class Animation : IDisposable { AttachedModels.Clear(); } + + public void ImGuiAnimation(ImDrawListPtr drawList, Vector2 timelineP0, Vector2 treeP0, Vector2 treeP1, Vector2 timeStep, Vector2 timeRatio, float y, float t) + { + 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); + + drawList.AddRectFilled(p1, p2, 0xFF175F17, 5.0f, ImDrawFlags.RoundCornersTop); + + drawList.PushClipRect(treeP0, treeP1); + drawList.AddText(treeP0 with { Y = y + timeStep.Y / 4.0f }, 0xFFFFFFFF, Name); + drawList.PopClipRect(); + } } diff --git a/FModel/Views/Snooper/Animations/Skeleton.cs b/FModel/Views/Snooper/Animations/Skeleton.cs index edffb7e1..5d328c4f 100644 --- a/FModel/Views/Snooper/Animations/Skeleton.cs +++ b/FModel/Views/Snooper/Animations/Skeleton.cs @@ -177,12 +177,7 @@ public class Skeleton : IDisposable private void TrackSkeleton(CAnimSet anim) { - // reset - foreach (var boneIndices in BonesIndicesByLoweredName.Values) - { - boneIndices.TrackIndex = -1; - boneIndices.ParentTrackIndex = -1; - } + ResetAnimatedData(); // tracked bones for (int trackIndex = 0; trackIndex < anim.TrackBonesInfo.Length; trackIndex++) @@ -211,7 +206,7 @@ public class Skeleton : IDisposable continue; #if DEBUG - Log.Warning($"Bone Mismatch: {boneName} ({boneIndices.BoneIndex}) was not present in the anim's target skeleton"); + Log.Warning($"{Name} Bone Mismatch: {boneName} ({boneIndices.BoneIndex}) was not present in the anim's target skeleton"); #endif var loweredParentBoneName = boneIndices.LoweredParentBoneName; @@ -224,13 +219,25 @@ public class Skeleton : IDisposable } } + public void ResetAnimatedData(bool full = false) + { + foreach (var boneIndices in BonesIndicesByLoweredName.Values) + { + boneIndices.TrackIndex = -1; + boneIndices.ParentTrackIndex = -1; + } + + if (!full) return; + _animatedBonesTransform = Array.Empty(); + _ssbo.UpdateRange(BoneCount, Matrix4x4.Identity); + } + public void Setup() { _handle = GL.CreateProgram(); _ssbo = new BufferObject(BoneCount, BufferTarget.ShaderStorageBuffer); - for (int boneIndex = 0; boneIndex < BoneCount; boneIndex++) - _ssbo.Update(boneIndex, Matrix4x4.Identity); + _ssbo.UpdateRange(BoneCount, Matrix4x4.Identity); } public void UpdateAnimationMatrices(int currentSequence, int frameInSequence) diff --git a/FModel/Views/Snooper/Animations/TimeTracker.cs b/FModel/Views/Snooper/Animations/TimeTracker.cs index 63dd4e82..c0784ce2 100644 --- a/FModel/Views/Snooper/Animations/TimeTracker.cs +++ b/FModel/Views/Snooper/Animations/TimeTracker.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Numerics; +using FModel.Views.Snooper.Shading; using ImGuiNET; namespace FModel.Views.Snooper.Animations; @@ -16,34 +17,37 @@ public enum ETrackerType public class TimeTracker : IDisposable { public bool IsPaused; + public bool IsActive; public float ElapsedTime; - public float MaxElapsedTime { get; private set; } - - private float _timeHeight = 10.0f; - private float _timeBarHeight => _timeHeight * 2.0f; + public float MaxElapsedTime; public TimeTracker() { Reset(); - SetMaxElapsedTime(0.01f); } public void Update(float deltaSeconds) { - if (IsPaused) return; + if (IsPaused || IsActive) return; ElapsedTime += deltaSeconds; - if (ElapsedTime >= MaxElapsedTime) Reset(); + if (ElapsedTime >= MaxElapsedTime) Reset(false); } - public void SetMaxElapsedTime(float maxElapsedTime) + public void SafeSetElapsedTime(float elapsedTime) + { + ElapsedTime = Math.Clamp(elapsedTime, 0.0f, MaxElapsedTime); + } + + public void SafeSetMaxElapsedTime(float maxElapsedTime) { MaxElapsedTime = MathF.Max(maxElapsedTime, MaxElapsedTime); } - public void Reset() + public void Reset(bool doMet = true) { IsPaused = false; ElapsedTime = 0.0f; + if (doMet) MaxElapsedTime = 0.01f; } public void Dispose() @@ -51,52 +55,100 @@ public class TimeTracker : IDisposable Reset(); } - public void ImGuiTimeline(ImFontPtr fontPtr, List animations) + private const float _margin = 5.0f; + private const float _thickness = 2.0f; + private const float _timeHeight = 10.0f; + private float _timeBarHeight => _timeHeight * 2.0f; + private readonly Vector2 _buttonSize = new (14.0f); + private readonly string[] _icons = { "tl_forward", "tl_pause", "tl_rewind" }; + + public void ImGuiTimeline(Dictionary icons, List animations, Vector2 outliner, int activeAnimation, ImFontPtr fontPtr) { - var io = ImGui.GetIO(); - var canvasP0 = ImGui.GetCursorScreenPos(); + var treeP0 = ImGui.GetCursorScreenPos(); var canvasSize = ImGui.GetContentRegionAvail(); - var canvasP1 = new Vector2(canvasP0.X + canvasSize.X, canvasP0.Y + canvasSize.Y); - var timeRatio = canvasSize / MaxElapsedTime; + var timelineP1 = new Vector2(treeP0.X + canvasSize.X, treeP0.Y + canvasSize.Y); + + var treeP1 = timelineP1 with { X = treeP0.X + outliner.X }; + + var timelineP0 = treeP0 with { X = treeP1.X + _thickness }; + var timelineSize = timelineP1 - timelineP0; + var timeRatio = timelineSize / MaxElapsedTime; + var timeStep = timeRatio * MaxElapsedTime / timelineSize * 50.0f; + timeStep.Y /= 2.0f; var drawList = ImGui.GetWindowDrawList(); + drawList.AddRectFilled(treeP0, treeP1, 0xFF1F1C1C); + drawList.AddRectFilled(timelineP0, timelineP1 with { Y = timelineP0.Y + _timeBarHeight }, 0xFF141414); + drawList.AddRectFilled(timelineP0 with { Y = timelineP0.Y + _timeBarHeight }, timelineP1, 0xFF242424); + drawList.AddLine(new Vector2(treeP1.X, treeP0.Y), treeP1, 0xFF504545, _thickness); + drawList.AddLine(treeP0 with { Y = timelineP0.Y + _timeBarHeight }, timelineP1 with { Y = timelineP0.Y + _timeBarHeight }, 0x50504545, _thickness); - ImGui.InvisibleButton("timeline_canvas", canvasP1 with { Y = _timeBarHeight }, ImGuiButtonFlags.MouseButtonLeft); - IsPaused = ImGui.IsItemActive(); - if (IsPaused && ImGui.IsMouseDragging(ImGuiMouseButton.Left)) + // adding margin + treeP0.X += _margin; + treeP1.X -= _margin; + + // control buttons + for (int i = 0; i < _icons.Length; i++) { - var mousePosCanvas = io.MousePos - canvasP0; - ElapsedTime = Math.Clamp(mousePosCanvas.X / canvasSize.X * MaxElapsedTime, 0.01f, MaxElapsedTime); + var x = _buttonSize.X * 2.0f * i; + ImGui.SetCursorScreenPos(treeP0 with { X = treeP1.X - x - _buttonSize.X * 2.0f + _thickness }); + if (ImGui.ImageButton($"timeline_actions_{_icons[i]}", icons[_icons[i]].GetPointer(), _buttonSize)) + { + switch (i) + { + case 0: + SafeSetElapsedTime(ElapsedTime + timeStep.X / timeRatio.X); + break; + case 1: + IsPaused = !IsPaused; + _icons[1] = IsPaused ? "tl_play" : "tl_pause"; + break; + case 2: + SafeSetElapsedTime(ElapsedTime - timeStep.X / timeRatio.X); + break; + } + } + } + + ImGui.SetCursorScreenPos(timelineP0); + ImGui.InvisibleButton("timeline_timetracker_canvas", timelineSize with { Y = _timeBarHeight }, ImGuiButtonFlags.MouseButtonLeft); + IsActive = ImGui.IsItemActive(); + if (IsActive && ImGui.IsMouseDragging(ImGuiMouseButton.Left)) + { + var mousePosCanvas = ImGui.GetIO().MousePos - timelineP0; + SafeSetElapsedTime(mousePosCanvas.X / timelineSize.X * MaxElapsedTime); foreach (var animation in animations) { animation.TimeCalculation(ElapsedTime); } } - drawList.AddRectFilled(canvasP0, canvasP1 with { Y = canvasP0.Y + _timeBarHeight }, 0xFF181818); - drawList.PushClipRect(canvasP0, canvasP1 with { Y = canvasP0.Y + _timeBarHeight }, true); - { - for (float x = 0; x < canvasSize.X; x += timeRatio.X * MaxElapsedTime / canvasSize.X * 50.0f) + { // draw time + time grid + for (float x = 0; x < timelineSize.X; x += timeStep.X) { - drawList.AddLine(new Vector2(canvasP0.X + x, canvasP0.Y + _timeHeight + 2.5f), canvasP1 with { X = canvasP0.X + x }, 0xA0FFFFFF); - drawList.AddText(fontPtr, 14, new Vector2(canvasP0.X + x + 4, canvasP0.Y + 7.5f), 0x50FFFFFF, $"{x / timeRatio.X:F1}s"); + var cursor = timelineP0.X + x; + drawList.AddLine(new Vector2(cursor, timelineP0.Y + _timeHeight + 2.5f), new Vector2(cursor, timelineP0.Y + _timeBarHeight), 0xA0FFFFFF); + drawList.AddLine(new Vector2(cursor, timelineP0.Y + _timeBarHeight), timelineP1 with { X = cursor }, 0x28C8C8C8); + drawList.AddText(fontPtr, 14, new Vector2(cursor + 4, timelineP0.Y + 7.5f), 0x50FFFFFF, $"{x / timeRatio.X:F1}s"); + } + + for (float y = _timeBarHeight; y < timelineSize.Y; y += timeStep.Y) + { + drawList.AddLine(timelineP0 with { Y = timelineP0.Y + y }, timelineP1 with { Y = timelineP0.Y + y }, 0x28C8C8C8); } } - drawList.PopClipRect(); - foreach (var animation in animations) + for (int i = 0; i < animations.Count; i++) { - for (int i = 0; i < animation.Sequences.Length; i++) - { - animation.Sequences[i].DrawSequence(drawList, canvasP0.X, canvasP0.Y + _timeBarHeight, timeRatio, i, i == animation.CurrentSequence ? 0xFF0000FF : 0xFF175F17); - } - DrawSeparator(drawList, canvasP0, canvasP1, animation.EndTime * timeRatio.X, ETrackerType.End); + var y = timelineP0.Y + _timeBarHeight + timeStep.Y * (i % 2); + animations[i].ImGuiAnimation(drawList, timelineP0, treeP0, treeP1, timeStep, timeRatio, y, _thickness); + DrawSeparator(drawList, timelineP0, y + timeStep.Y, animations[i].EndTime * timeRatio.X, ETrackerType.End); } - DrawSeparator(drawList, canvasP0, canvasP1, ElapsedTime * timeRatio.X, ETrackerType.Frame); + DrawSeparator(drawList, timelineP0, timelineP1.Y, ElapsedTime * timeRatio.X, ETrackerType.Frame); } - private void DrawSeparator(ImDrawListPtr drawList, Vector2 origin, Vector2 destination, float time, ETrackerType separatorType) + private void DrawSeparator(ImDrawListPtr drawList, Vector2 origin, float y, float time, ETrackerType separatorType) { const int size = 5; @@ -106,7 +158,7 @@ public class TimeTracker : IDisposable ETrackerType.End => origin with { X = origin.X + time }, _ => throw new ArgumentOutOfRangeException(nameof(separatorType), separatorType, null) }; - var p2 = new Vector2(p1.X, destination.Y); + var p2 = p1 with { Y = y }; uint color = separatorType switch { diff --git a/FModel/Views/Snooper/Buffers/BufferObject.cs b/FModel/Views/Snooper/Buffers/BufferObject.cs index 743f4cfa..14e86931 100644 --- a/FModel/Views/Snooper/Buffers/BufferObject.cs +++ b/FModel/Views/Snooper/Buffers/BufferObject.cs @@ -26,6 +26,13 @@ public class BufferObject : IDisposable where TDataType : unmanaged GL.BufferData(bufferTarget, length * sizeof(TDataType), IntPtr.Zero, BufferUsageHint.DynamicDraw); } + public void UpdateRange(int count, TDataType data) + { + Bind(); + for (int i = 0; i < count; i++) Update(i, data); + Unbind(); + } + public unsafe void Update(int offset, TDataType data) { GL.BufferSubData(_bufferTarget, (IntPtr) (offset * sizeof(TDataType)), sizeof(TDataType), ref data); diff --git a/FModel/Views/Snooper/Models/Model.cs b/FModel/Views/Snooper/Models/Model.cs index e6831d61..5df5b811 100644 --- a/FModel/Views/Snooper/Models/Model.cs +++ b/FModel/Views/Snooper/Models/Model.cs @@ -81,6 +81,7 @@ public class Model : IDisposable public Section[] Sections; public Material[] Materials; public bool TwoSided; + public bool IsAnimatedProp; public bool HasSkeleton => Skeleton != null; public readonly Skeleton Skeleton; @@ -306,7 +307,11 @@ public class Model : IDisposable public void DetachModel(Model attachedTo, Socket socket) { socket.AttachedModels.Remove(Guid); + SafeDetachModel(attachedTo); + } + public void SafeDetachModel(Model attachedTo) + { _attachedTo = string.Empty; attachedTo._attachedFor.Remove($"'{Name}'"); Transforms[SelectedInstance].Relation = _previousMatrix; diff --git a/FModel/Views/Snooper/Options.cs b/FModel/Views/Snooper/Options.cs index 3eb67cac..e0d41b0d 100644 --- a/FModel/Views/Snooper/Options.cs +++ b/FModel/Views/Snooper/Options.cs @@ -47,6 +47,12 @@ public class Options ["link_on"] = new ("link_on"), ["link_off"] = new ("link_off"), ["link_has"] = new ("link_has"), + ["tl_play"] = new ("tl_play"), + ["tl_pause"] = new ("tl_pause"), + ["tl_rewind"] = new ("tl_rewind"), + ["tl_forward"] = new ("tl_forward"), + ["tl_previous"] = new ("tl_previous"), + ["tl_next"] = new ("tl_next"), }; _platform = UserSettings.Default.OverridedPlatform; @@ -88,9 +94,50 @@ public class Options SelectedMorph = 0; } - public void SelectAnimation() + public void RemoveModel(FGuid guid) { + if (!TryGetModel(guid, out var model)) return; + DetachAndRemoveModels(model); + model.Dispose(); + Models.Remove(guid); + } + + private void DetachAndRemoveModels(Model model) + { + foreach (var socket in model.Sockets) + { + foreach (var guid in socket.AttachedModels) + { + if (!TryGetModel(guid, out var attachedModel)) continue; + + attachedModel.SafeDetachModel(model); + RemoveModel(guid); + } + socket.Dispose(); + } + } + + public void AddAnimation(Animation animation) + { + Animations.Add(animation); + } + + public void RemoveAnimations() + { + Tracker.Reset(); + foreach (var animation in Animations) + { + foreach (var guid in animation.AttachedModels) + { + if (!TryGetModel(guid, out var animatedModel)) continue; + + animatedModel.Skeleton.ResetAnimatedData(true); + DetachAndRemoveModels(animatedModel); + } + animation.Dispose(); + } + Animations.Clear(); } public void SelectSection(int index) diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs index 7f6f1584..c9cee5ec 100644 --- a/FModel/Views/Snooper/Renderer.cs +++ b/FModel/Views/Snooper/Renderer.cs @@ -108,19 +108,19 @@ public class Renderer : IDisposable case UAnimSequence animSequence when animSequence.Skeleton.TryLoad(out USkeleton skeleton): { var animSet = skeleton.ConvertAnims(animSequence); - var animation = new Animation(animSet, model.Guid); + var animation = new Animation(animSequence.Name, animSet, model.Guid); maxElapsedTime = animation.TotalElapsedTime; model.Skeleton.Animate(animSet, AnimateWithRotationOnly); - Options.Animations.Add(animation); + Options.AddAnimation(animation); break; } case UAnimMontage animMontage when animMontage.Skeleton.TryLoad(out USkeleton skeleton): { var animSet = skeleton.ConvertAnims(animMontage); - var animation = new Animation(animSet, model.Guid); + var animation = new Animation(animMontage.Name, animSet, model.Guid); maxElapsedTime = animation.TotalElapsedTime; model.Skeleton.Animate(animSet, AnimateWithRotationOnly); - Options.Animations.Add(animation); + Options.AddAnimation(animation); foreach (var notifyEvent in animMontage.Notifies) { @@ -138,6 +138,7 @@ public class Renderer : IDisposable if (!Options.TryGetModel(guid, out var addedModel)) continue; + addedModel.IsAnimatedProp = true; if (notifyClass.TryGetValue(out UObject skeletalMeshPropAnimation, "SkeletalMeshPropAnimation")) Animate(skeletalMeshPropAnimation, addedModel); if (notifyClass.TryGetValue(out FName socketName, "SocketName")) @@ -150,7 +151,7 @@ public class Renderer : IDisposable if (notifyClass.TryGetValue(out FVector scale, "Scale")) t.Scale = scale; - var s = new Socket("hello", socketName, t); + var s = new Socket($"TL_{addedModel.Name}", socketName, t); model.Sockets.Add(s); addedModel.AttachModel(model, s); } @@ -160,17 +161,17 @@ public class Renderer : IDisposable case UAnimComposite animComposite when animComposite.Skeleton.TryLoad(out USkeleton skeleton): { var animSet = skeleton.ConvertAnims(animComposite); - var animation = new Animation(animSet, model.Guid); + var animation = new Animation(animComposite.Name, animSet, model.Guid); maxElapsedTime = animation.TotalElapsedTime; model.Skeleton.Animate(animSet, AnimateWithRotationOnly); - Options.Animations.Add(animation); + Options.AddAnimation(animation); break; } default: throw new ArgumentException(); } - Options.Tracker.SetMaxElapsedTime(maxElapsedTime); + Options.Tracker.SafeSetMaxElapsedTime(maxElapsedTime); Options.AnimateMesh(false); } diff --git a/FModel/Views/Snooper/SnimGui.cs b/FModel/Views/Snooper/SnimGui.cs index d9d0b31e..1d052628 100644 --- a/FModel/Views/Snooper/SnimGui.cs +++ b/FModel/Views/Snooper/SnimGui.cs @@ -47,6 +47,8 @@ public class SnimGui private readonly Save _saver = new (); private readonly string _renderer; private readonly string _version; + + private Vector2 _outlinerSize; private bool _ti_open; private bool _viewportFocus; @@ -71,7 +73,8 @@ public class SnimGui DrawDockSpace(s.Size); SectionWindow("Material Inspector", s.Renderer, DrawMaterialInspector, false); - AnimationWindow("Timeline", s.Renderer, (icons, tracker, animations) => tracker.ImGuiTimeline(Controller.FontSemiBold, animations)); + AnimationWindow("Timeline", s.Renderer, (icons, tracker, animations) => + tracker.ImGuiTimeline(icons, animations, _outlinerSize, s.Renderer.Options.SelectedAnimation, Controller.FontSemiBold)); Window("World", () => DrawWorld(s), false); @@ -305,6 +308,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero); Window("Outliner", () => { + _outlinerSize = ImGui.GetWindowSize(); if (ImGui.BeginTable("Items", 4, ImGuiTableFlags.Resizable | ImGuiTableFlags.BordersOuterV | ImGuiTableFlags.NoSavedSettings, ImGui.GetContentRegionAvail())) { ImGui.TableSetupColumn("Instance", ImGuiTableColumnFlags.NoHeaderWidth | ImGuiTableColumnFlags.WidthFixed, _tableWidth); @@ -349,6 +353,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio ImGui.BeginDisabled(!model.HasSkeleton); if (ImGui.Selectable("Animate")) { + s.Renderer.Options.RemoveAnimations(); s.Renderer.Options.AnimateMesh(true); s.WindowShouldClose(true, false); } @@ -359,7 +364,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio s.Renderer.CameraOp.Teleport(instancePos, model.Box); } - if (ImGui.Selectable("Delete")) s.Renderer.Options.Models.Remove(guid); + if (ImGui.Selectable("Delete")) s.Renderer.Options.RemoveModel(guid); if (ImGui.Selectable("Deselect")) s.Renderer.Options.SelectModel(Guid.Empty); ImGui.Separator(); if (ImGui.Selectable("Copy Name to Clipboard")) ImGui.SetClipboardText(model.Name);