This commit is contained in:
4sval 2023-02-13 01:05:54 +01:00
parent c7d532fff9
commit 9b4c83931b
10 changed files with 170 additions and 66 deletions

@ -1 +1 @@
Subproject commit f91288c261ecf9cd5a276683a9812fe920265eb7
Subproject commit c7fed92ddb2dc2aaec428504396945deefb1ff22

View File

@ -33,9 +33,9 @@ public class ImGuiController : IDisposable
private int _windowHeight;
// private string _iniPath;
private ImFontPtr _normal;
private ImFontPtr _bold;
private ImFontPtr _semiBold;
public ImFontPtr FontNormal;
public ImFontPtr FontBold;
public ImFontPtr FontSemiBold;
private readonly Vector2 _scaleFactor = Vector2.One;
@ -57,9 +57,9 @@ public class ImGuiController : IDisposable
// ImGui.LoadIniSettingsFromDisk(_iniPath);
var io = ImGui.GetIO();
_normal = io.Fonts.AddFontFromFileTTF("C:\\Windows\\Fonts\\segoeui.ttf", 16);
_bold = io.Fonts.AddFontFromFileTTF("C:\\Windows\\Fonts\\segoeuib.ttf", 16);
_semiBold = io.Fonts.AddFontFromFileTTF("C:\\Windows\\Fonts\\seguisb.ttf", 16);
FontNormal = io.Fonts.AddFontFromFileTTF("C:\\Windows\\Fonts\\segoeui.ttf", 16);
FontBold = io.Fonts.AddFontFromFileTTF("C:\\Windows\\Fonts\\segoeuib.ttf", 16);
FontSemiBold = io.Fonts.AddFontFromFileTTF("C:\\Windows\\Fonts\\seguisb.ttf", 16);
io.BackendFlags |= ImGuiBackendFlags.RendererHasVtxOffset;
io.ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard;
@ -75,13 +75,13 @@ public class ImGuiController : IDisposable
_frameBegun = true;
}
public void Bold() => PushFont(_bold);
public void SemiBold() => PushFont(_semiBold);
public void Bold() => PushFont(FontBold);
public void SemiBold() => PushFont(FontSemiBold);
public void PopFont()
{
ImGui.PopFont();
PushFont(_normal);
PushFont(FontNormal);
}
private void PushFont(ImFontPtr ptr) => ImGui.PushFont(ptr);

View File

@ -80,7 +80,7 @@ public partial class MainWindow
"MoonMan/Content/DeliverUsTheMoon/Characters/Astronaut/SK_Astronaut.uasset"));
await _threadWorkerView.Begin(cancellationToken =>
_applicationView.CUE4Parse.Extract(cancellationToken,
"MoonMan/Content/DeliverUsTheMoon/Characters/Astronaut/AM_ControllingASE.uasset"));
"MoonMan/Content/DeliverUsTheMoon/Characters/Astronaut/cinematic/A_Astro_Space_Breach_Grab2_Success.uasset"));
await _threadWorkerView.Begin(cancellationToken =>
_applicationView.CUE4Parse.Extract(cancellationToken,
"MoonMan/Content/DeliverUsTheMoon/Characters/Astronaut/AM_OxygenHub_Enter.uasset"));

View File

@ -1,18 +1,35 @@
using System;
using System.Numerics;
using CUE4Parse_Conversion.Animations;
using CUE4Parse.Utils;
using ImGuiNET;
namespace FModel.Views.Snooper.Models.Animations;
public enum AnimSeparatorType
{
InBetween,
End
}
public class Animation : IDisposable
{
public float ElapsedTime;
public bool IsPaused;
public readonly int TotalFrames;
public readonly float TotalDuration;
public int CurrentSequence;
public readonly Sequence[] Sequences;
public int SequencesCount => Sequences.Length;
public Animation()
{
Reset();
IsPaused = false;
TotalFrames = 0;
TotalDuration = 0.0f;
Sequences = Array.Empty<Sequence>();
}
@ -22,52 +39,124 @@ public class Animation : IDisposable
for (int i = 0; i < Sequences.Length; i++)
{
Sequences[i] = new Sequence(anim.Sequences[i], skeleton, rotationOnly);
TotalFrames += Sequences[i].MaxFrame;
TotalDuration += Sequences[i].Duration;
}
if (Sequences.Length > 0)
ElapsedTime = Sequences[0].StartTime;
}
public void Update(float deltaSeconds)
{
Sequences[CurrentSequence].Update(deltaSeconds);
if (IsPaused) return;
if (Sequences[CurrentSequence].IsComplete)
{
Sequences[CurrentSequence].Reset();
CurrentSequence++;
}
if (CurrentSequence >= SequencesCount)
Reset();
ElapsedTime += Sequences[CurrentSequence].Update(deltaSeconds);
}
public Matrix4x4 InterpolateBoneTransform(int boneIndex)
{
// interpolate here
return Sequences[CurrentSequence].BonesTransform[boneIndex][Sequences[CurrentSequence].Frame].Matrix;
}
public void CheckForNextSequence()
private void Reset()
{
if (Sequences[CurrentSequence].ElapsedTime > Sequences[CurrentSequence].EndPos)
{
Sequences[CurrentSequence].ElapsedTime = 0;
Sequences[CurrentSequence].Frame = 0;
CurrentSequence++;
}
if (CurrentSequence >= SequencesCount)
{
CurrentSequence = 0;
}
ElapsedTime = 0.0f;
CurrentSequence = 0;
}
public void ImGuiTimeline()
private float _timeHeight = 10.0f;
private float _timeBarHeight => _timeHeight * 2.0f;
public void ImGuiTimeline(ImFontPtr fontPtr)
{
var io = ImGui.GetIO();
var canvasP0 = ImGui.GetCursorScreenPos();
var canvasSize = ImGui.GetContentRegionAvail();
var canvasP1 = new Vector2(canvasP0.X + canvasSize.X, canvasP0.Y + canvasSize.Y);
var ratio = canvasSize / Sequences[CurrentSequence].MaxFrame;
var ratio = canvasSize / TotalFrames;
var drawList = ImGui.GetWindowDrawList();
drawList.AddRectFilled(canvasP0, canvasP1, 0xFF242424);
var l1 = new Vector2(canvasP0.X + Sequences[CurrentSequence].Frame * ratio.X, canvasP0.Y);
var l2 = new Vector2(l1.X, canvasP1.Y);
drawList.AddLine(l1, l2, 0xFF0000FF, 2f);
ImGui.InvisibleButton("timeline_canvas", canvasP1 with { Y = _timeBarHeight }, ImGuiButtonFlags.MouseButtonLeft);
IsPaused = ImGui.IsItemActive();
if (IsPaused && ImGui.IsMouseDragging(ImGuiMouseButton.Left))
{
var mousePosCanvas = io.MousePos - canvasP0;
ElapsedTime = Math.Clamp(mousePosCanvas.X / canvasSize.X * TotalDuration, 0, TotalDuration);
}
ImGui.Text($"{Sequences[CurrentSequence].Name} > {(CurrentSequence < SequencesCount - 1 ? Sequences[CurrentSequence + 1].Name : Sequences[0].Name)}");
ImGui.Text($"Frame: {Sequences[CurrentSequence].Frame}/{Sequences[CurrentSequence].MaxFrame}");
ImGui.Text($"FPS: {Sequences[CurrentSequence].FramesPerSecond}");
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 += ratio.X * 10f)
{
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 / ratio.X).FloorToInt().ToString());
}
}
drawList.PopClipRect();
for (int i = 0; i < Sequences.Length; i++)
{
Sequences[i].DrawSequence(drawList, canvasP0, ratio, i);
}
DrawSeparator(drawList, canvasP0, canvasP1, ElapsedTime * ratio.X, AnimSeparatorType.InBetween);
DrawSeparator(drawList, canvasP0, canvasP1, TotalDuration * ratio.X, AnimSeparatorType.End);
// ImGui.Text($"{Sequences[CurrentSequence].Name} > {(CurrentSequence < SequencesCount - 1 ? Sequences[CurrentSequence + 1].Name : Sequences[0].Name)}");
// ImGui.Text($"Frame: {Sequences[CurrentSequence].Frame}/{Sequences[CurrentSequence].MaxFrame}");
// ImGui.Text($"Frame: {Frame}/{TotalFrames}");
// ImGui.Text($"FPS: {Sequences[CurrentSequence].FramesPerSecond}");
}
private void DrawSeparator(ImDrawListPtr drawList, Vector2 origin, Vector2 destination, float time, AnimSeparatorType separatorType)
{
const int size = 5;
Vector2 p1 = separatorType switch
{
AnimSeparatorType.InBetween => new Vector2(origin.X + time, origin.Y + _timeBarHeight),
AnimSeparatorType.End => origin with { X = origin.X + time },
_ => throw new ArgumentOutOfRangeException(nameof(separatorType), separatorType, null)
};
var p2 = new Vector2(p1.X, destination.Y);
uint color = separatorType switch
{
AnimSeparatorType.InBetween => 0xFF6F6F6F,
AnimSeparatorType.End => 0xFF2E3E82,
_ => throw new ArgumentOutOfRangeException(nameof(separatorType), separatorType, null)
};
drawList.AddLine(p1, p2, color, 1f);
switch (separatorType)
{
case AnimSeparatorType.InBetween:
color = 0xFF30478C;
var xl = p1.X - size;
var xr = p1.X + size;
var yb = origin.Y + _timeBarHeight - _timeHeight / 2.0f;
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 AnimSeparatorType.End:
drawList.AddTriangleFilled(p1, p1 with { X = p1.X - size }, p1 with { Y = p1.Y + size }, color);
break;
default:
throw new ArgumentOutOfRangeException(nameof(separatorType), separatorType, null);
}
}
public void Dispose()

View File

@ -1,6 +1,8 @@
using System;
using System.Numerics;
using CUE4Parse_Conversion.Animations;
using CUE4Parse.Utils;
using ImGuiNET;
namespace FModel.Views.Snooper.Models.Animations;
@ -10,27 +12,27 @@ public class Sequence : IDisposable
public float ElapsedTime;
public readonly string Name;
public readonly int MaxFrame;
public readonly float FramesPerSecond;
public readonly float StartPos;
public readonly float AnimStartTime;
public readonly float AnimEndTime;
public readonly float TimePerFrame;
public readonly float StartTime;
public readonly float Duration;
public readonly float EndTime;
public readonly int LoopingCount;
public float TimePerFrame => 1.0f / FramesPerSecond;
public float EndPos => AnimEndTime / TimePerFrame;
public readonly Transform[][] BonesTransform;
public bool IsComplete => ElapsedTime > Duration;
// public bool IsComplete => Frame >= MaxFrame;
public Sequence(CAnimSequence sequence, Skeleton skeleton, bool rotationOnly)
{
Frame = 0;
ElapsedTime = 0.0f;
Reset();
Name = sequence.Name;
MaxFrame = sequence.NumFrames - 1;
FramesPerSecond = sequence.Rate;
StartPos = sequence.StartPos;
AnimStartTime = sequence.AnimStartTime;
AnimEndTime = sequence.AnimEndTime;
TimePerFrame = 1.0f / sequence.Rate;
StartTime = sequence.StartPos / TimePerFrame;
Duration = sequence.AnimEndTime / TimePerFrame;
EndTime = StartTime + Duration;
LoopingCount = sequence.LoopingCount;
BonesTransform = new Transform[skeleton.BonesTransformByIndex.Count][];
@ -81,10 +83,29 @@ public class Sequence : IDisposable
}
}
public void Update(float deltaSeconds)
public float Update(float deltaSeconds)
{
ElapsedTime += deltaSeconds / TimePerFrame;
var delta = deltaSeconds / TimePerFrame;
ElapsedTime += delta;
Frame = Math.Min(ElapsedTime.FloorToInt(), MaxFrame);
return delta;
}
public void Reset()
{
ElapsedTime = 0.0f;
Frame = 0;
}
private readonly float _height = 20.0f;
public void DrawSequence(ImDrawListPtr drawList, Vector2 origin, Vector2 ratio, int index)
{
var height = _height * (index % 2) + _height;
var p1 = new Vector2(origin.X + StartTime * ratio.X, origin.Y + height);
var p2 = new Vector2(origin.X + EndTime * ratio.X, origin.Y + height + _height);
drawList.AddRectFilled(p1, p2, 0xFF175F17);
drawList.AddText(p1 with { X = p1.X + 2.5f }, 0xFF000000, Name);
}
public void Dispose()

View File

@ -95,29 +95,22 @@ public class Skeleton : IDisposable
_ssbo = new BufferObject<Matrix4x4>(InvertedBonesMatrixByIndex.Length, BufferTarget.ShaderStorageBuffer);
}
public void Render(float deltaSeconds = 0f, bool update = false)
public void UpdateMatrices(float deltaSeconds)
{
if (!IsLoaded) return;
_ssbo.BindBufferBase(1);
if (!HasAnim)
{
for (int boneIndex = 0; boneIndex < InvertedBonesMatrixByIndex.Length; boneIndex++)
{
_ssbo.Update(boneIndex, Matrix4x4.Identity);
}
}
else
{
if (update) Anim.Update(deltaSeconds);
Anim.Update(deltaSeconds);
for (int boneIndex = 0; boneIndex < InvertedBonesMatrixByIndex.Length; boneIndex++)
{
_ssbo.Update(boneIndex, InvertedBonesMatrixByIndex[boneIndex] * Anim.InterpolateBoneTransform(boneIndex));
}
if (update) Anim.CheckForNextSequence();
}
_ssbo.Unbind();
}

View File

@ -246,9 +246,10 @@ public class Model : IDisposable
Transforms.Add(transform);
}
public void UpdateMatrices(Options options)
public void UpdateMatrices(Options options, float deltaSeconds = 0f, bool update = false)
{
var worldMatrix = UpdateMatrices();
if (update && HasSkeleton) Skeleton.UpdateMatrices(deltaSeconds);
foreach (var socket in Sockets)
{
var boneMatrix = Matrix4x4.Identity;
@ -370,7 +371,7 @@ public class Model : IDisposable
IsSetup = true;
}
public void Render(float deltaSeconds, Shader shader, bool outline = false)
public void Render(Shader shader, bool outline = false)
{
if (outline) GL.Disable(EnableCap.DepthTest);
if (TwoSided) GL.Disable(EnableCap.CullFace);
@ -382,7 +383,6 @@ public class Model : IDisposable
_vao.Bind();
shader.SetUniform("uMorphTime", MorphTime);
if (HasSkeleton) Skeleton.Render(deltaSeconds, !outline);
if (!outline)
{
shader.SetUniform("uUvCount", UvCount);
@ -413,7 +413,6 @@ public class Model : IDisposable
_vao.Bind();
shader.SetUniform("uMorphTime", MorphTime);
if (HasSkeleton) Skeleton.Render();
foreach (var section in Sections)
{

View File

@ -135,9 +135,9 @@ public class Renderer : IDisposable
// render model pass
foreach (var model in Options.Models.Values)
{
model.UpdateMatrices(Options);
model.UpdateMatrices(Options, deltaSeconds, model.Show);
if (!model.Show) continue;
model.Render(deltaSeconds, _shader);
model.Render(_shader);
}
{ // light pass
@ -157,7 +157,7 @@ public class Renderer : IDisposable
if (Options.TryGetModel(out var selected) && selected.Show)
{
_outline.Render(viewMatrix, CameraOp.Position, projMatrix);
selected.Render(deltaSeconds, _outline, true);
selected.Render(_outline, true);
}
// picking pass (dedicated FBO, binding to 0 afterward)

View File

@ -362,7 +362,7 @@ public class Material : IDisposable
var origin = new Vector2(canvasP0.X + _scrolling.X, canvasP0.Y + _scrolling.Y);
var absoluteMiddle = canvasSize / 2.0f;
ImGui.InvisibleButton("texture_inspector_canvas", canvasSize, ImGuiButtonFlags.MouseButtonLeft | ImGuiButtonFlags.MouseButtonRight);
ImGui.InvisibleButton("texture_inspector_canvas", canvasSize, ImGuiButtonFlags.MouseButtonLeft);
if (ImGui.IsItemActive() && ImGui.IsMouseDragging(ImGuiMouseButton.Left))
{
_scrolling.X += io.MouseDelta.X;

View File

@ -71,7 +71,7 @@ public class SnimGui
DrawDockSpace(s.Size);
SectionWindow("Material Inspector", s.Renderer, DrawMaterialInspector, false);
AnimationWindow("Timeline", s.Renderer, (icons, skeleton) => skeleton.Anim.ImGuiTimeline());
AnimationWindow("Timeline", s.Renderer, (icons, skeleton) => skeleton.Anim.ImGuiTimeline(Controller.FontSemiBold));
Window("World", () => DrawWorld(s), false);
@ -770,12 +770,14 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
private void AnimationWindow(string name, Renderer renderer, Action<Dictionary<string, Texture>, Skeleton> content, bool styled = true)
{
ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero);
MeshWindow(name, renderer, (icons, model) =>
{
if (!model.HasSkeleton) CenteredTextColored(_errorColor, "No Skeleton To Animate");
else if (!model.Skeleton.HasAnim) CenteredTextColored(_errorColor, "Mesh Not Animated");
else content(icons, model.Skeleton);
}, styled);
ImGui.PopStyleVar();
}
private void PopStyleCompact() => ImGui.PopStyleVar(2);