From 0b7ed2cf7fd6e884fe9f5de8e2c0cf66cc48e9d1 Mon Sep 17 00:00:00 2001 From: 4sval Date: Mon, 6 Feb 2023 00:12:46 +0100 Subject: [PATCH] rotation only animation --- CUE4Parse | 2 +- FModel/Settings/UserSettings.cs | 7 ++ .../Snooper/Models/Animations/Animation.cs | 30 +++-- .../Snooper/Models/Animations/Skeleton.cs | 12 +- FModel/Views/Snooper/Models/Model.cs | 2 +- FModel/Views/Snooper/Renderer.cs | 5 +- FModel/Views/Snooper/Shading/Material.cs | 15 ++- FModel/Views/Snooper/SnimGui.cs | 111 +++++++++++------- 8 files changed, 120 insertions(+), 64 deletions(-) diff --git a/CUE4Parse b/CUE4Parse index c6fcae36..c2143de8 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit c6fcae365d91d7d9501e2be79e78dfba136b4670 +Subproject commit c2143de8bda93c02f5b07657b9110c477ec1d5ff diff --git a/FModel/Settings/UserSettings.cs b/FModel/Settings/UserSettings.cs index 1ca12fca..ba1d0b86 100644 --- a/FModel/Settings/UserSettings.cs +++ b/FModel/Settings/UserSettings.cs @@ -647,6 +647,13 @@ namespace FModel.Settings set => SetProperty(ref _showGrid, value); } + private bool _animateWithRotationOnly; + public bool AnimateWithRotationOnly + { + get => _animateWithRotationOnly; + set => SetProperty(ref _animateWithRotationOnly, value); + } + private Camera.WorldMode _cameraMode = Camera.WorldMode.Arcball; public Camera.WorldMode CameraMode { diff --git a/FModel/Views/Snooper/Models/Animations/Animation.cs b/FModel/Views/Snooper/Models/Animations/Animation.cs index 67227251..f0da035f 100644 --- a/FModel/Views/Snooper/Models/Animations/Animation.cs +++ b/FModel/Views/Snooper/Models/Animations/Animation.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Collections.Generic; using System.Numerics; using CUE4Parse_Conversion.Animations; using CUE4Parse.Utils; +using ImGuiNET; namespace FModel.Views.Snooper.Models.Animations; @@ -15,9 +16,9 @@ public class Animation : IDisposable public readonly Dictionary TrackIndexByBoneIndex; public readonly Transform[][] BoneTransforms; - public float TimePerFrame => 1.0f / FramesPerSecond; + private float TimePerFrame => 1.0f / FramesPerSecond; - public Animation(Skeleton skeleton, CAnimSet anim) + public Animation(Skeleton skeleton, CAnimSet anim, bool rotationOnly) { Frame = 0; ElapsedTime = 0; @@ -39,12 +40,13 @@ public class Animation : IDisposable throw new ArgumentNullException($"no transform for bone '{boneIndex}'"); TrackIndexByBoneIndex[boneIndex] = trackIndex; - var boneOrientation = originalTransform.Rotation; - var bonePosition = originalTransform.Position; - var boneScale = originalTransform.Scale; for (var frame = 0; frame < BoneTransforms[trackIndex].Length; frame++) { + var boneOrientation = originalTransform.Rotation; + var bonePosition = originalTransform.Position; + var boneScale = originalTransform.Scale; + sequence.Tracks[trackIndex].GetBonePosition(frame, MaxFrame, false, ref bonePosition, ref boneOrientation); if (frame < sequence.Tracks[trackIndex].KeyScale.Length) boneScale = sequence.Tracks[trackIndex].KeyScale[frame]; @@ -70,19 +72,31 @@ public class Animation : IDisposable { Relation = bone.ParentIndex >= 0 ? BoneTransforms[bone.ParentIndex][frame].Matrix : originalTransform.Relation, Rotation = boneOrientation, - Position = bonePosition, + Position = rotationOnly ? originalTransform.Position : bonePosition, Scale = boneScale }; } } } + public void Update(float deltaSeconds) + { + ElapsedTime += deltaSeconds / TimePerFrame; + Frame = ElapsedTime.FloorToInt() % MaxFrame; + } + public Matrix4x4 InterpolateBoneTransform(int trackIndex) { - Frame = ElapsedTime.FloorToInt() % MaxFrame; // interpolate here + // interpolate here return BoneTransforms[trackIndex][Frame].Matrix; } + public void ImGuiTimeline() + { + ImGui.Text($"Frame: {Frame}/{MaxFrame}"); + ImGui.Text($"FPS: {FramesPerSecond}"); + } + public void Dispose() { TrackIndexByBoneIndex.Clear(); diff --git a/FModel/Views/Snooper/Models/Animations/Skeleton.cs b/FModel/Views/Snooper/Models/Animations/Skeleton.cs index 95f12317..92f23acf 100644 --- a/FModel/Views/Snooper/Models/Animations/Skeleton.cs +++ b/FModel/Views/Snooper/Models/Animations/Skeleton.cs @@ -18,6 +18,7 @@ public class Skeleton : IDisposable public readonly bool IsLoaded; public Animation Anim; + public bool HasAnim => Anim != null; public Skeleton() { @@ -62,9 +63,9 @@ public class Skeleton : IDisposable } } - public void SetAnimation(CAnimSet anim) + public void SetAnimation(CAnimSet anim, bool rotationOnly) { - Anim = new Animation(this, anim); + Anim = new Animation(this, anim, rotationOnly); } public void SetPoseUniform(Shader shader) @@ -77,13 +78,14 @@ public class Skeleton : IDisposable shader.SetUniform($"uFinalBonesMatrix[{boneIndex}]", Matrix4x4.Identity); } } - public void SetUniform(Shader shader, float deltaSeconds = 0f, bool updateElapsedTime = false) + + public void SetUniform(Shader shader, float deltaSeconds = 0f, bool update = false) { if (!IsLoaded) return; - if (Anim == null) SetPoseUniform(shader); + if (!HasAnim) SetPoseUniform(shader); else { - if (!updateElapsedTime) Anim.ElapsedTime += deltaSeconds / Anim.TimePerFrame; + if (update) Anim.Update(deltaSeconds); foreach (var boneIndex in BonesTransformByIndex.Keys) { if (boneIndex >= Constants.MAX_BONE_UNIFORM) diff --git a/FModel/Views/Snooper/Models/Model.cs b/FModel/Views/Snooper/Models/Model.cs index 14b71559..a9a54c7f 100644 --- a/FModel/Views/Snooper/Models/Model.cs +++ b/FModel/Views/Snooper/Models/Model.cs @@ -382,7 +382,7 @@ public class Model : IDisposable _vao.Bind(); shader.SetUniform("uMorphTime", MorphTime); - if (HasSkeleton) Skeleton.SetUniform(shader, deltaSeconds, outline); + if (HasSkeleton) Skeleton.SetUniform(shader, deltaSeconds, !outline); if (!outline) { shader.SetUniform("uUvCount", UvCount); diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs index 9e5b4aaa..b6c128af 100644 --- a/FModel/Views/Snooper/Renderer.cs +++ b/FModel/Views/Snooper/Renderer.cs @@ -36,6 +36,7 @@ public class Renderer : IDisposable public bool ShowSkybox; public bool ShowGrid; public bool ShowLights; + public bool AnimateWithRotationOnly; public int VertexColor; public Camera CameraOp { get; } @@ -53,6 +54,7 @@ public class Renderer : IDisposable ShowSkybox = UserSettings.Default.ShowSkybox; ShowGrid = UserSettings.Default.ShowGrid; + AnimateWithRotationOnly = UserSettings.Default.AnimateWithRotationOnly; VertexColor = 0; // default } @@ -93,7 +95,7 @@ public class Renderer : IDisposable model.Skeleton?.UnrealSkeleton.ConvertAnims(animSequence) is not { } anim || anim.Sequences.Count == 0) return; - model.Skeleton.SetAnimation(anim); + model.Skeleton.SetAnimation(anim, AnimateWithRotationOnly); Options.AnimateMesh(false); } @@ -408,6 +410,7 @@ public class Renderer : IDisposable if (_saveCameraMode) UserSettings.Default.CameraMode = CameraOp.Mode; UserSettings.Default.ShowSkybox = ShowSkybox; UserSettings.Default.ShowGrid = ShowGrid; + UserSettings.Default.AnimateWithRotationOnly = AnimateWithRotationOnly; } public void Dispose() diff --git a/FModel/Views/Snooper/Shading/Material.cs b/FModel/Views/Snooper/Shading/Material.cs index c4114d89..e83b3d83 100644 --- a/FModel/Views/Snooper/Shading/Material.cs +++ b/FModel/Views/Snooper/Shading/Material.cs @@ -341,12 +341,15 @@ public class Material : IDisposable var texture = GetSelectedTexture() ?? fallback; if (ImGui.BeginTable("texture_inspector", 2, ImGuiTableFlags.SizingStretchProp)) { - SnimGui.Layout("Type");ImGui.Text($" : ({texture.Format}) {texture.Name}"); - SnimGui.TooltipCopy("(?) Click to Copy Path", texture.Path); - SnimGui.Layout("Guid");ImGui.Text($" : {texture.Guid.ToString(EGuidFormats.UniqueObjectGuid)}"); - SnimGui.Layout("Import");ImGui.Text($" : {texture.ImportedWidth}x{texture.ImportedHeight}"); - SnimGui.Layout("Export");ImGui.Text($" : {texture.Width}x{texture.Height}"); - ImGui.EndTable(); + SnimGui.NoFramePaddingOnY(() => + { + SnimGui.Layout("Type");ImGui.Text($" : ({texture.Format}) {texture.Name}"); + SnimGui.TooltipCopy("(?) Click to Copy Path", texture.Path); + SnimGui.Layout("Guid");ImGui.Text($" : {texture.Guid.ToString(EGuidFormats.UniqueObjectGuid)}"); + SnimGui.Layout("Import");ImGui.Text($" : {texture.ImportedWidth}x{texture.ImportedHeight}"); + SnimGui.Layout("Export");ImGui.Text($" : {texture.Width}x{texture.Height}"); + ImGui.EndTable(); + }); } var largest = ImGui.GetContentRegionAvail(); diff --git a/FModel/Views/Snooper/SnimGui.cs b/FModel/Views/Snooper/SnimGui.cs index 7bca4ab6..7de4dbef 100644 --- a/FModel/Views/Snooper/SnimGui.cs +++ b/FModel/Views/Snooper/SnimGui.cs @@ -9,6 +9,7 @@ using System.Numerics; using System.Text; using FModel.Settings; using FModel.Views.Snooper.Models; +using FModel.Views.Snooper.Models.Animations; using FModel.Views.Snooper.Shading; using OpenTK.Graphics.OpenGL4; @@ -71,8 +72,8 @@ public class SnimGui DrawDockSpace(s.Size); SectionWindow("Material Inspector", s.Renderer, DrawMaterialInspector, false); + AnimationWindow("Timeline", s.Renderer, (icons, skeleton) => skeleton.Anim.ImGuiTimeline()); - // Window("Timeline", () => {}); Window("World", () => DrawWorld(s), false); DrawSockets(s); @@ -138,7 +139,9 @@ public class SnimGui ImGui.Checkbox("", ref s.Renderer.ShowGrid); ImGui.PopID();Layout("Lights");ImGui.PushID(3); ImGui.Checkbox("", ref s.Renderer.ShowLights); - ImGui.PopID();Layout("Vertex Colors");ImGui.PushID(4); + ImGui.PopID();Layout("Animate With Rotation Only");ImGui.PushID(4); + ImGui.Checkbox("", ref s.Renderer.AnimateWithRotationOnly); + ImGui.PopID();Layout("Vertex Colors");ImGui.PushID(5); ImGui.Combo("vertex_colors", ref s.Renderer.VertexColor, "Default\0Diffuse Only\0Colors\0Normals\0Texture Coordinates\0"); ImGui.PopID(); @@ -442,20 +445,23 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio { if (ImGui.BeginTable("model_details", 2, ImGuiTableFlags.SizingStretchProp)) { - Layout("Entity");ImGui.Text($" : ({model.Type}) {model.Name}"); - Layout("Guid");ImGui.Text($" : {s.Renderer.Options.SelectedModel.ToString(EGuidFormats.UniqueObjectGuid)}"); - if (model.HasSkeleton) + NoFramePaddingOnY(() => { - Layout("Skeleton");ImGui.Text($" : {model.Skeleton.UnrealSkeleton.Name}"); - Layout("Bones");ImGui.Text($" : x{model.Skeleton.UnrealSkeleton.BoneTree.Length}"); - } - else - { - Layout("Two Sided");ImGui.Text($" : {model.TwoSided}"); - } - Layout("Sockets");ImGui.Text($" : x{model.Sockets.Length}"); + Layout("Entity");ImGui.Text($" : ({model.Type}) {model.Name}"); + Layout("Guid");ImGui.Text($" : {s.Renderer.Options.SelectedModel.ToString(EGuidFormats.UniqueObjectGuid)}"); + if (model.HasSkeleton) + { + Layout("Skeleton");ImGui.Text($" : {model.Skeleton.UnrealSkeleton.Name}"); + Layout("Bones");ImGui.Text($" : x{model.Skeleton.UnrealSkeleton.BoneTree.Length}"); + } + else + { + Layout("Two Sided");ImGui.Text($" : {model.TwoSided}"); + } + Layout("Sockets");ImGui.Text($" : x{model.Sockets.Length}"); - ImGui.EndTable(); + ImGui.EndTable(); + }); } if (ImGui.BeginTabBar("tabbar_details", ImGuiTabBarFlags.None)) { @@ -609,36 +615,39 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); if (ImGui.CollapsingHeader("Properties")) { - ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); - if (ImGui.TreeNode("Base")) + NoFramePaddingOnY(() => { - material.ImGuiBaseProperties("base"); - ImGui.TreePop(); - } + ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); + if (ImGui.TreeNode("Base")) + { + material.ImGuiBaseProperties("base"); + ImGui.TreePop(); + } - ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); - if (ImGui.TreeNode("Scalars")) - { - material.ImGuiDictionaries("scalars", material.Parameters.Scalars, true); - ImGui.TreePop(); - } - ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); - if (ImGui.TreeNode("Switchs")) - { - material.ImGuiDictionaries("switchs", material.Parameters.Switchs, true); - ImGui.TreePop(); - } - ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); - if (ImGui.TreeNode("Colors")) - { - material.ImGuiColors(material.Parameters.Colors); - ImGui.TreePop(); - } - if (ImGui.TreeNode("All Textures")) - { - material.ImGuiDictionaries("textures", material.Parameters.Textures); - ImGui.TreePop(); - } + ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); + if (ImGui.TreeNode("Scalars")) + { + material.ImGuiDictionaries("scalars", material.Parameters.Scalars, true); + ImGui.TreePop(); + } + ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); + if (ImGui.TreeNode("Switchs")) + { + material.ImGuiDictionaries("switchs", material.Parameters.Switchs, true); + ImGui.TreePop(); + } + ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); + if (ImGui.TreeNode("Colors")) + { + material.ImGuiColors(material.Parameters.Colors); + ImGui.TreePop(); + } + if (ImGui.TreeNode("All Textures")) + { + material.ImGuiDictionaries("textures", material.Parameters.Textures); + ImGui.TreePop(); + } + }); } } @@ -772,6 +781,16 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio }, styled); } + private void AnimationWindow(string name, Renderer renderer, Action, Skeleton> content, bool styled = true) + { + 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); + } + private void PopStyleCompact() => ImGui.PopStyleVar(2); private void PushStyleCompact() { @@ -779,6 +798,13 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio ImGui.PushStyleVar(ImGuiStyleVar.CellPadding, new Vector2(0, 1)); } + public static void NoFramePaddingOnY(Action content) + { + ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(8, 0)); + content(); + ImGui.PopStyleVar(); + } + private void NoMeshSelected() => CenteredTextColored(_errorColor, "No Mesh Selected"); private void NoSectionSelected() => CenteredTextColored(_errorColor, "No Section Selected"); private void CenteredTextColored(Vector4 color, string text) @@ -797,6 +823,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio { ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); ImGui.Spacing();ImGui.SameLine();ImGui.Text(name); if (tooltip) TooltipCopy(name); ImGui.TableSetColumnIndex(1);