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 00000000..b65b10fd
Binary files /dev/null and b/FModel/Resources/tl_forward.png differ
diff --git a/FModel/Resources/tl_next.png b/FModel/Resources/tl_next.png
new file mode 100644
index 00000000..0a50b4fb
Binary files /dev/null and b/FModel/Resources/tl_next.png differ
diff --git a/FModel/Resources/tl_pause.png b/FModel/Resources/tl_pause.png
new file mode 100644
index 00000000..24eed9f9
Binary files /dev/null and b/FModel/Resources/tl_pause.png differ
diff --git a/FModel/Resources/tl_play.png b/FModel/Resources/tl_play.png
new file mode 100644
index 00000000..9784d86e
Binary files /dev/null and b/FModel/Resources/tl_play.png differ
diff --git a/FModel/Resources/tl_previous.png b/FModel/Resources/tl_previous.png
new file mode 100644
index 00000000..2e201684
Binary files /dev/null and b/FModel/Resources/tl_previous.png differ
diff --git a/FModel/Resources/tl_rewind.png b/FModel/Resources/tl_rewind.png
new file mode 100644
index 00000000..66e62918
Binary files /dev/null and b/FModel/Resources/tl_rewind.png differ
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);