From b5c1fdccc6a178a34d89403235b2147c5a9c9624 Mon Sep 17 00:00:00 2001 From: Asval Date: Fri, 14 Feb 2025 19:23:10 +0100 Subject: [PATCH] SoftObjectPath notify animation support Marvel Rivals emotes will display notified models --- CUE4Parse | 2 +- FModel/Views/Snooper/Animations/Animation.cs | 4 +- .../Views/Snooper/Animations/TimeTracker.cs | 4 +- FModel/Views/Snooper/Renderer.cs | 156 ++++++++---------- FModel/Views/Snooper/SnimGui.cs | 2 +- 5 files changed, 73 insertions(+), 95 deletions(-) diff --git a/CUE4Parse b/CUE4Parse index 2aed4da1..7c76fdc0 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit 2aed4da1ee440cd421222526bdc501031f685be6 +Subproject commit 7c76fdc0d3053170121bd5b6f0bb3b5fa28afbf6 diff --git a/FModel/Views/Snooper/Animations/Animation.cs b/FModel/Views/Snooper/Animations/Animation.cs index 87936494..2b1e4921 100644 --- a/FModel/Views/Snooper/Animations/Animation.cs +++ b/FModel/Views/Snooper/Animations/Animation.cs @@ -35,9 +35,9 @@ public class Animation : IDisposable _export = export; Path = _export.GetPathName(); Name = _export.Name; - Sequences = Array.Empty(); + Sequences = []; Framing = new Dictionary(); - AttachedModels = new List(); + AttachedModels = []; } public Animation(UObject export, CAnimSet animSet) : this(export) diff --git a/FModel/Views/Snooper/Animations/TimeTracker.cs b/FModel/Views/Snooper/Animations/TimeTracker.cs index 5417f39c..28cc9538 100644 --- a/FModel/Views/Snooper/Animations/TimeTracker.cs +++ b/FModel/Views/Snooper/Animations/TimeTracker.cs @@ -20,7 +20,7 @@ public class TimeTracker : IDisposable public bool IsActive; public float ElapsedTime; public float MaxElapsedTime; - public int TimeMultiplier; + public float TimeMultiplier; public TimeTracker() { @@ -51,7 +51,7 @@ public class TimeTracker : IDisposable if (doMet) { MaxElapsedTime = 0.01f; - TimeMultiplier = 1; + TimeMultiplier = 1f; } } diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs index b3ea7dd6..0dfceee2 100644 --- a/FModel/Views/Snooper/Renderer.cs +++ b/FModel/Views/Snooper/Renderer.cs @@ -122,111 +122,89 @@ public class Renderer : IDisposable public void Animate(Lazy anim) => Animate(anim.Value, Options.SelectedModel); private void Animate(UObject anim, FGuid guid) { - if (!Options.TryGetModel(guid, out var m) || m is not SkeletalModel model) + if (anim is not UAnimSequenceBase animBase || !Options.TryGetModel(guid, out var m) || m is not SkeletalModel model) return; - float maxElapsedTime; - switch (anim) + var animSet = animBase switch { - case UAnimSequence animSequence when animSequence.Skeleton.TryLoad(out USkeleton skeleton): - { - var animSet = skeleton.ConvertAnims(animSequence); - var animation = new Animation(animSequence, animSet, guid); - maxElapsedTime = animation.TotalElapsedTime; - model.Skeleton.Animate(animSet); - Options.AddAnimation(animation); - break; - } - case UAnimMontage animMontage when animMontage.Skeleton.TryLoad(out USkeleton skeleton): - { - var animSet = skeleton.ConvertAnims(animMontage); - var animation = new Animation(animMontage, animSet, guid); - maxElapsedTime = animation.TotalElapsedTime; - model.Skeleton.Animate(animSet); - Options.AddAnimation(animation); + UAnimSequence animSequence when animSequence.Skeleton.TryLoad(out USkeleton skeleton) => skeleton.ConvertAnims(animSequence), + UAnimMontage animMontage when animMontage.Skeleton.TryLoad(out USkeleton skeleton) => skeleton.ConvertAnims(animMontage), + UAnimComposite animComposite when animComposite.Skeleton.TryLoad(out USkeleton skeleton) => skeleton.ConvertAnims(animComposite), + _ => throw new ArgumentException("Unknown animation type") + }; - foreach (var notifyEvent in animMontage.Notifies) + var animation = new Animation(anim, animSet, guid); + model.Skeleton.Animate(animSet); + Options.AddAnimation(animation); + + foreach (var notifyEvent in animBase.Notifies) + { + if (!notifyEvent.NotifyStateClass.TryLoad(out UObject notifyClass) || + !notifyClass.TryGetValue(out UObject export, "SkeletalMeshProp", "StaticMeshProp", "Mesh", "SkeletalMeshTemplate")) + continue; + + var t = Transform.Identity; + if (notifyClass.TryGetValue(out FTransform offset, "Offset")) + { + t.Rotation = offset.Rotation; + t.Position = offset.Translation * Constants.SCALE_DOWN_RATIO; + t.Scale = offset.Scale3D; + } + + UModel addedModel = null; + switch (export) + { + case UStaticMesh st: { - if (!notifyEvent.NotifyStateClass.TryLoad(out UObject notifyClass) || - !notifyClass.TryGetValue(out FPackageIndex meshProp, "SkeletalMeshProp", "StaticMeshProp", "Mesh") || - !meshProp.TryLoad(out UObject export)) continue; - - var t = Transform.Identity; - if (notifyClass.TryGetValue(out FTransform offset, "Offset")) + guid = st.LightingGuid; + if (Options.TryGetModel(guid, out addedModel)) { - t.Rotation = offset.Rotation; - t.Position = offset.Translation * Constants.SCALE_DOWN_RATIO; - t.Scale = offset.Scale3D; + addedModel.AddInstance(t); } - - UModel addedModel = null; - switch (export) + else if (st.TryConvert(out var mesh)) { - case UStaticMesh st: - { - guid = st.LightingGuid; - if (Options.TryGetModel(guid, out addedModel)) - { - addedModel.AddInstance(t); - } - else if (st.TryConvert(out var mesh)) - { - addedModel = new StaticModel(st, mesh, t); - Options.Models[guid] = addedModel; - } - break; - } - case USkeletalMesh sk: - { - guid = Guid.NewGuid(); - if (!Options.Models.ContainsKey(guid) && sk.TryConvert(out var mesh)) - { - addedModel = new SkeletalModel(sk, mesh, t); - Options.Models[guid] = addedModel; - } - break; - } - } - - if (addedModel == null) - throw new ArgumentException("Unknown model type"); - - addedModel.IsProp = true; - if (notifyClass.TryGetValue(out UObject skeletalMeshPropAnimation, "SkeletalMeshPropAnimation", "Animation")) - Animate(skeletalMeshPropAnimation, guid); - if (notifyClass.TryGetValue(out FName socketName, "SocketName")) - { - t = Transform.Identity; - if (notifyClass.TryGetValue(out FVector location, "LocationOffset", "Location")) - t.Position = location * Constants.SCALE_DOWN_RATIO; - if (notifyClass.TryGetValue(out FRotator rotation, "RotationOffset", "Rotation")) - t.Rotation = rotation.Quaternion(); - if (notifyClass.TryGetValue(out FVector scale, "Scale")) - t.Scale = scale; - - var s = new Socket($"ANIM_{addedModel.Name}", socketName, t, true); - model.Sockets.Add(s); - addedModel.Attachments.Attach(model, addedModel.GetTransform(), s, - new SocketAttachementInfo { Guid = guid, Instance = addedModel.SelectedInstance }); + addedModel = new StaticModel(st, mesh, t); + Options.Models[guid] = addedModel; } + break; + } + case USkeletalMesh sk: + { + guid = Guid.NewGuid(); + if (!Options.Models.ContainsKey(guid) && sk.TryConvert(out var mesh)) + { + addedModel = new SkeletalModel(sk, mesh, t); + Options.Models[guid] = addedModel; + } + break; } - break; } - case UAnimComposite animComposite when animComposite.Skeleton.TryLoad(out USkeleton skeleton): + + if (addedModel == null) + throw new ArgumentException("Unknown model type"); + + addedModel.IsProp = true; + if (notifyClass.TryGetValue(out UObject skeletalMeshPropAnimation, "SkeletalMeshPropAnimation", "Animation", "AnimToPlay")) + Animate(skeletalMeshPropAnimation, guid); + if (notifyClass.TryGetValue(out FName socketName, "SocketName")) { - var animSet = skeleton.ConvertAnims(animComposite); - var animation = new Animation(animComposite, animSet, guid); - maxElapsedTime = animation.TotalElapsedTime; - model.Skeleton.Animate(animSet); - Options.AddAnimation(animation); - break; + t = Transform.Identity; + if (notifyClass.TryGetValue(out FVector location, "LocationOffset", "Location")) + t.Position = location * Constants.SCALE_DOWN_RATIO; + if (notifyClass.TryGetValue(out FRotator rotation, "RotationOffset", "Rotation")) + t.Rotation = rotation.Quaternion(); + if (notifyClass.TryGetValue(out FVector scale, "Scale")) + t.Scale = scale; + + var s = new Socket($"ANIM_{addedModel.Name}", socketName, t, true); + model.Sockets.Add(s); + addedModel.Attachments.Attach(model, addedModel.GetTransform(), s, + new SocketAttachementInfo { Guid = guid, Instance = addedModel.SelectedInstance }); } - default: - throw new ArgumentException(); } Options.Tracker.IsPaused = false; - Options.Tracker.SafeSetMaxElapsedTime(maxElapsedTime); + Options.Tracker.SafeSetMaxElapsedTime(animation.TotalElapsedTime); } public void Setup() diff --git a/FModel/Views/Snooper/SnimGui.cs b/FModel/Views/Snooper/SnimGui.cs index 4b182f79..b07cfb13 100644 --- a/FModel/Views/Snooper/SnimGui.cs +++ b/FModel/Views/Snooper/SnimGui.cs @@ -218,7 +218,7 @@ public class SnimGui Layout("Animate With Rotation Only");ImGui.PushID(1); ImGui.Checkbox("", ref s.Renderer.AnimateWithRotationOnly); ImGui.PopID();Layout("Time Multiplier");ImGui.PushID(2); - ImGui.DragInt("", ref s.Renderer.Options.Tracker.TimeMultiplier, 0.1f, 1, 8, "x%i", ImGuiSliderFlags.NoInput); + ImGui.DragFloat("", ref s.Renderer.Options.Tracker.TimeMultiplier, 0.01f, 0.25f, 8f, "x%.2f", ImGuiSliderFlags.NoInput); ImGui.PopID();Layout("Vertex Colors");ImGui.PushID(3); var c = (int) s.Renderer.Color; ImGui.Combo("vertex_colors", ref c,