SoftObjectPath notify animation support

Marvel Rivals emotes will display notified models
This commit is contained in:
Asval 2025-02-14 19:23:10 +01:00
parent a97d773c64
commit b5c1fdccc6
5 changed files with 73 additions and 95 deletions

@ -1 +1 @@
Subproject commit 2aed4da1ee440cd421222526bdc501031f685be6
Subproject commit 7c76fdc0d3053170121bd5b6f0bb3b5fa28afbf6

View File

@ -35,9 +35,9 @@ public class Animation : IDisposable
_export = export;
Path = _export.GetPathName();
Name = _export.Name;
Sequences = Array.Empty<Sequence>();
Sequences = [];
Framing = new Dictionary<int, float>();
AttachedModels = new List<FGuid>();
AttachedModels = [];
}
public Animation(UObject export, CAnimSet animSet) : this(export)

View File

@ -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;
}
}

View File

@ -122,111 +122,89 @@ public class Renderer : IDisposable
public void Animate(Lazy<UObject> 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()

View File

@ -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,