mirror of
https://github.com/4sval/FModel.git
synced 2026-03-26 11:45:22 -05:00
rotation only animation
This commit is contained in:
parent
f288791b71
commit
0b7ed2cf7f
|
|
@ -1 +1 @@
|
|||
Subproject commit c6fcae365d91d7d9501e2be79e78dfba136b4670
|
||||
Subproject commit c2143de8bda93c02f5b07657b9110c477ec1d5ff
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<int, int> 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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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<Dictionary<string, Texture>, 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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user