mirror of
https://github.com/4sval/FModel.git
synced 2026-04-26 00:04:53 -05:00
timeline
This commit is contained in:
parent
591a2d2336
commit
4247dec633
|
|
@ -78,10 +78,10 @@ public partial class MainWindow
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
await _threadWorkerView.Begin(cancellationToken =>
|
await _threadWorkerView.Begin(cancellationToken =>
|
||||||
_applicationView.CUE4Parse.Extract(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 =>
|
await _threadWorkerView.Begin(cancellationToken =>
|
||||||
_applicationView.CUE4Parse.Extract(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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,9 @@ namespace FModel.Views.Snooper.Animations;
|
||||||
|
|
||||||
public class Animation : IDisposable
|
public class Animation : IDisposable
|
||||||
{
|
{
|
||||||
public readonly UObject Export;
|
private readonly UObject _export;
|
||||||
public readonly CAnimSet AnimSet;
|
private readonly CAnimSet _animSet;
|
||||||
|
|
||||||
public readonly string Path;
|
public readonly string Path;
|
||||||
public readonly string Name;
|
public readonly string Name;
|
||||||
public readonly Sequence[] Sequences;
|
public readonly Sequence[] Sequences;
|
||||||
|
|
@ -33,25 +34,25 @@ public class Animation : IDisposable
|
||||||
|
|
||||||
public Animation(UObject export)
|
public Animation(UObject export)
|
||||||
{
|
{
|
||||||
Export = export;
|
_export = export;
|
||||||
Path = Export.GetPathName();
|
Path = _export.GetPathName();
|
||||||
Name = Export.Name;
|
Name = _export.Name;
|
||||||
Sequences = Array.Empty<Sequence>();
|
Sequences = Array.Empty<Sequence>();
|
||||||
AttachedModels = new List<FGuid>();
|
AttachedModels = new List<FGuid>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Animation(UObject export, CAnimSet animSet) : this(export)
|
public Animation(UObject export, CAnimSet animSet) : this(export)
|
||||||
{
|
{
|
||||||
AnimSet = animSet;
|
_animSet = animSet;
|
||||||
TargetSkeleton = AnimSet.OriginalAnim.Name;
|
TargetSkeleton = _animSet.OriginalAnim.Name;
|
||||||
|
|
||||||
Sequences = new Sequence[AnimSet.Sequences.Count];
|
Sequences = new Sequence[_animSet.Sequences.Count];
|
||||||
for (int i = 0; i < Sequences.Length; i++)
|
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;
|
EndTime = Sequences[i].EndTime;
|
||||||
TotalElapsedTime += AnimSet.Sequences[i].NumFrames * Sequences[i].TimePerFrame;
|
TotalElapsedTime += _animSet.Sequences[i].NumFrames * Sequences[i].TimePerFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Sequences.Length > 0)
|
if (Sequences.Length > 0)
|
||||||
|
|
@ -95,29 +96,31 @@ public class Animation : IDisposable
|
||||||
|
|
||||||
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)
|
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 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);
|
var p2 = new Vector2(timelineP0.X + EndTime * timeRatio.X - t, y + timeStep.Y - t);
|
||||||
|
|
||||||
ImGui.SetCursorScreenPos(p1);
|
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();
|
IsActive = ImGui.IsItemActive();
|
||||||
IsSelected = s.Renderer.Options.SelectedAnimation == i;
|
IsSelected = s.Renderer.Options.SelectedAnimation == i;
|
||||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
||||||
{
|
{
|
||||||
s.Renderer.Options.SelectedAnimation = i;
|
s.Renderer.Options.SelectAnimation(i);
|
||||||
}
|
}
|
||||||
SnimGui.Popup(() =>
|
SnimGui.Popup(() =>
|
||||||
{
|
{
|
||||||
s.Renderer.Options.SelectedAnimation = i;
|
s.Renderer.Options.SelectAnimation(i);
|
||||||
if (ImGui.BeginMenu("Animate"))
|
if (ImGui.BeginMenu("Animate"))
|
||||||
{
|
{
|
||||||
foreach ((var guid, var model) in s.Renderer.Options.Models)
|
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.ResetAnimatedData(true);
|
||||||
model.Skeleton.Animate(AnimSet, s.Renderer.AnimateWithRotationOnly);
|
if (!selected) model.Skeleton.Animate(_animSet, s.Renderer.AnimateWithRotationOnly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui.EndMenu();
|
ImGui.EndMenu();
|
||||||
|
|
@ -125,7 +128,7 @@ public class Animation : IDisposable
|
||||||
if (ImGui.Selectable("Save"))
|
if (ImGui.Selectable("Save"))
|
||||||
{
|
{
|
||||||
s.WindowShouldFreeze(true);
|
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);
|
s.WindowShouldFreeze(false);
|
||||||
}
|
}
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
@ -138,8 +141,10 @@ public class Animation : IDisposable
|
||||||
Sequences[j].DrawSequence(drawList, timelineP0.X, p2, timeStep, timeRatio, t);
|
Sequences[j].DrawSequence(drawList, timelineP0.X, p2, timeStep, timeRatio, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawList.PushClipRect(treeP0 with { Y = p1.Y }, treeP1 with { Y = p2.Y }, true);
|
ImGui.SetCursorScreenPos(treeP0 with { Y = p1.Y });
|
||||||
drawList.AddText(treeP0 with { Y = y + timeStep.Y / 4.0f }, 0xFFFFFFFF, Name);
|
if (ImGui.Selectable(name, s.Renderer.Options.SelectedAnimation == i, ImGuiSelectableFlags.SpanAllColumns, new Vector2(p1.X - treeP0.X, timeStep.Y - t - t)))
|
||||||
drawList.PopClipRect();
|
{
|
||||||
|
s.Renderer.Options.SelectAnimation(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,12 +141,14 @@ public class TimeTracker : IDisposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui.PushStyleVar(ImGuiStyleVar.SelectableTextAlign, new Vector2(0.0f, 0.5f));
|
||||||
for (int i = 0; i < animations.Count; i++)
|
for (int i = 0; i < animations.Count; i++)
|
||||||
{
|
{
|
||||||
var y = timelineP0.Y + _timeBarHeight + _timeStep.Y * i;
|
var y = timelineP0.Y + _timeBarHeight + _timeStep.Y * i;
|
||||||
animations[i].ImGuiAnimation(s, saver, drawList, timelineP0, treeP0, treeP1, _timeStep, timeRatio, y, _thickness, 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);
|
DrawSeparator(drawList, timelineP0, y + _timeStep.Y, animations[i].EndTime * timeRatio.X, ETrackerType.End);
|
||||||
}
|
}
|
||||||
|
ImGui.PopStyleVar();
|
||||||
|
|
||||||
for (int i = 0; i < animations.Count; i++)
|
for (int i = 0; i < animations.Count; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -165,7 +167,13 @@ public class TimeTracker : IDisposable
|
||||||
|
|
||||||
private void DrawSeparator(ImDrawListPtr drawList, Vector2 origin, float y, float time, ETrackerType separatorType)
|
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
|
Vector2 p1 = separatorType switch
|
||||||
{
|
{
|
||||||
|
|
@ -180,11 +188,10 @@ public class TimeTracker : IDisposable
|
||||||
{
|
{
|
||||||
ETrackerType.Frame => 0xFF6F6F6F,
|
ETrackerType.Frame => 0xFF6F6F6F,
|
||||||
ETrackerType.End => 0xFF2E3E82,
|
ETrackerType.End => 0xFF2E3E82,
|
||||||
ETrackerType.InBetween => 0x50FFFFFF,
|
ETrackerType.InBetween => 0xA0FFFFFF,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(separatorType), separatorType, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(separatorType), separatorType, null)
|
||||||
};
|
};
|
||||||
|
|
||||||
drawList.AddLine(p1, p2, color, 1f);
|
|
||||||
switch (separatorType)
|
switch (separatorType)
|
||||||
{
|
{
|
||||||
case ETrackerType.Frame:
|
case ETrackerType.Frame:
|
||||||
|
|
@ -193,13 +200,19 @@ public class TimeTracker : IDisposable
|
||||||
var xr = p1.X + size;
|
var xr = p1.X + size;
|
||||||
var yb = origin.Y + _timeBarHeight - _timeHeight / 2.0f;
|
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.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);
|
drawList.AddTriangleFilled(new Vector2(xl, yb), new Vector2(xr, yb), p1, color);
|
||||||
break;
|
break;
|
||||||
case ETrackerType.End:
|
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);
|
drawList.AddTriangleFilled(p1, p1 with { X = p1.X - size }, p1 with { Y = p1.Y + size }, color);
|
||||||
break;
|
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:
|
default:
|
||||||
throw new ArgumentOutOfRangeException(nameof(separatorType), separatorType, null);
|
throw new ArgumentOutOfRangeException(nameof(separatorType), separatorType, null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ public class Options
|
||||||
public FGuid SelectedModel { get; private set; }
|
public FGuid SelectedModel { get; private set; }
|
||||||
public int SelectedSection { get; private set; }
|
public int SelectedSection { get; private set; }
|
||||||
public int SelectedMorph { get; private set; }
|
public int SelectedMorph { get; private set; }
|
||||||
public int SelectedAnimation{ get; set; }
|
public int SelectedAnimation{ get; private set; }
|
||||||
|
|
||||||
public readonly Dictionary<FGuid, Model> Models;
|
public readonly Dictionary<FGuid, Model> Models;
|
||||||
public readonly Dictionary<FGuid, Texture> Textures;
|
public readonly Dictionary<FGuid, Texture> Textures;
|
||||||
|
|
@ -98,6 +98,11 @@ public class Options
|
||||||
SelectedMorph = 0;
|
SelectedMorph = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SelectAnimation(int animation)
|
||||||
|
{
|
||||||
|
SelectedAnimation = animation;
|
||||||
|
}
|
||||||
|
|
||||||
public void RemoveModel(FGuid guid)
|
public void RemoveModel(FGuid guid)
|
||||||
{
|
{
|
||||||
if (!TryGetModel(guid, out var model)) return;
|
if (!TryGetModel(guid, out var model)) return;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user