multiple anims on same model

This commit is contained in:
Asval
2023-08-09 23:56:20 +02:00
parent 946d38c87b
commit 223dd8fc3d
7 changed files with 74 additions and 54 deletions

View File

@@ -85,10 +85,10 @@ public partial class MainWindow
#if DEBUG #if DEBUG
await _threadWorkerView.Begin(cancellationToken => await _threadWorkerView.Begin(cancellationToken =>
_applicationView.CUE4Parse.Extract(cancellationToken, _applicationView.CUE4Parse.Extract(cancellationToken,
"Hk_project/Content/Character/Cat/Mesh/SKM_Cat.uasset")); "ShooterGame/Content/Characters/Guide/S0/3P/Models/TP_Guide_S0_Skelmesh.uasset"));
await _threadWorkerView.Begin(cancellationToken => await _threadWorkerView.Begin(cancellationToken =>
_applicationView.CUE4Parse.Extract(cancellationToken, _applicationView.CUE4Parse.Extract(cancellationToken,
"Hk_project/Content/Animation/CAT/Idle/CAT_SitBalledStrechingToStand_Idle.uasset")); "ShooterGame/Content/Characters/Guide/S0/Ability_Q/3P/Anims/TP_Guide_S0_Q_WolfTotem_Cast_Outro_Montage.uasset"));
#endif #endif
} }
@@ -142,7 +142,7 @@ public partial class MainWindow
private async void OnMappingsReload(object sender, ExecutedRoutedEventArgs e) private async void OnMappingsReload(object sender, ExecutedRoutedEventArgs e)
{ {
await _applicationView.CUE4Parse.InitMappings(); await _applicationView.CUE4Parse.InitMappings(true);
} }
private void OnOpenAvalonFinder() private void OnOpenAvalonFinder()

View File

@@ -351,7 +351,7 @@ public class CUE4ParseViewModel : ViewModel
}); });
} }
public Task InitMappings() public Task InitMappings(bool force = false)
{ {
if (!UserSettings.IsEndpointValid(EEndpointType.Mapping, out var endpoint)) if (!UserSettings.IsEndpointValid(EEndpointType.Mapping, out var endpoint))
{ {
@@ -377,7 +377,7 @@ public class CUE4ParseViewModel : ViewModel
if (!mapping.IsValid) continue; if (!mapping.IsValid) continue;
var mappingPath = Path.Combine(mappingsFolder, mapping.FileName); var mappingPath = Path.Combine(mappingsFolder, mapping.FileName);
if (!File.Exists(mappingPath)) if (force || !File.Exists(mappingPath))
{ {
_apiEndpointView.DownloadFile(mapping.Url, mappingPath); _apiEndpointView.DownloadFile(mapping.Url, mappingPath);
} }
@@ -883,8 +883,12 @@ public class CUE4ParseViewModel : ViewModel
{ {
var shouldDecompress = UserSettings.Default.CompressedAudioMode == ECompressedAudio.PlayDecompressed; var shouldDecompress = UserSettings.Default.CompressedAudioMode == ECompressedAudio.PlayDecompressed;
export.Decode(shouldDecompress, out var audioFormat, out var data); export.Decode(shouldDecompress, out var audioFormat, out var data);
if (data == null || string.IsNullOrEmpty(audioFormat) || export.Owner == null) var hasAf = !string.IsNullOrEmpty(audioFormat);
if (data == null || !hasAf || export.Owner == null)
{
if (hasAf) FLogger.Append(ELog.Warning, () => FLogger.Text($"Unsupported audio format '{audioFormat}'", Constants.WHITE, true));
return false; return false;
}
SaveAndPlaySound(Path.Combine(TabControl.SelectedTab.Directory, TabControl.SelectedTab.Header.SubstringBeforeLast('.')).Replace('\\', '/'), audioFormat, data); SaveAndPlaySound(Path.Combine(TabControl.SelectedTab.Directory, TabControl.SelectedTab.Header.SubstringBeforeLast('.')).Replace('\\', '/'), audioFormat, data);
return false; return false;

View File

@@ -4,7 +4,6 @@ using System.Numerics;
using CUE4Parse_Conversion.Animations.PSA; using CUE4Parse_Conversion.Animations.PSA;
using CUE4Parse.UE4.Assets.Exports; using CUE4Parse.UE4.Assets.Exports;
using CUE4Parse.UE4.Objects.Core.Misc; using CUE4Parse.UE4.Objects.Core.Misc;
using CUE4Parse.Utils;
using ImGuiNET; using ImGuiNET;
namespace FModel.Views.Snooper.Animations; namespace FModel.Views.Snooper.Animations;
@@ -20,15 +19,8 @@ public class Animation : IDisposable
public readonly float StartTime; // Animation Start Time public readonly float StartTime; // Animation Start Time
public readonly float EndTime; // Animation End Time public readonly float EndTime; // Animation End Time
public readonly float TotalElapsedTime; // Animation Max Time public readonly float TotalElapsedTime; // Animation Max Time
public readonly string TargetSkeleton; public readonly Dictionary<int, float> Framing;
public int CurrentSequence;
public int FrameInSequence; // Current Sequence's Frame to Display
public int NextFrameInSequence;
public float LerpAmount;
public string Label =>
$"Retarget: {TargetSkeleton}\nSequences: {CurrentSequence + 1}/{Sequences.Length}\nFrames: {FrameInSequence}/{Sequences[CurrentSequence].EndFrame}";
public bool IsActive; public bool IsActive;
public bool IsSelected; public bool IsSelected;
@@ -40,23 +32,22 @@ public class Animation : IDisposable
Path = _export.GetPathName(); Path = _export.GetPathName();
Name = _export.Name; Name = _export.Name;
Sequences = Array.Empty<Sequence>(); Sequences = Array.Empty<Sequence>();
Framing = new Dictionary<int, float>();
AttachedModels = new List<FGuid>(); AttachedModels = new List<FGuid>();
} }
public Animation(UObject export, CAnimSet animSet) : this(export) public Animation(UObject export, CAnimSet animSet) : this(export)
{ {
_animSet = animSet; _animSet = animSet;
TargetSkeleton = _animSet.Skeleton.Name;
Sequences = new Sequence[_animSet.Sequences.Count]; Sequences = new Sequence[_animSet.Sequences.Count];
for (int i = 0; i < Sequences.Length; i++) for (int i = 0; i < Sequences.Length; i++)
{ {
Sequences[i] = new Sequence(_animSet.Sequences[i]); Sequences[i] = new Sequence(_animSet.Sequences[i]);
EndTime = Sequences[i].EndTime; EndTime = Sequences[i].EndTime;
TotalElapsedTime += _animSet.Sequences[i].NumFrames * Sequences[i].TimePerFrame;
} }
TotalElapsedTime = animSet.TotalAnimTime;
if (Sequences.Length > 0) if (Sequences.Length > 0)
StartTime = Sequences[0].StartTime; StartTime = Sequences[0].StartTime;
} }
@@ -70,30 +61,16 @@ public class Animation : IDisposable
{ {
for (int i = 0; i < Sequences.Length; i++) for (int i = 0; i < Sequences.Length; i++)
{ {
if (elapsedTime < Sequences[i].EndTime && elapsedTime >= Sequences[i].StartTime) var sequence = Sequences[i];
if (elapsedTime < sequence.EndTime && elapsedTime >= sequence.StartTime)
{ {
CurrentSequence = i; Framing[i] = (elapsedTime - sequence.StartTime) / sequence.TimePerFrame;
break;
} }
else Framing.Remove(i);
} }
if (elapsedTime >= TotalElapsedTime) Reset();
var lastEndTime = 0.0f; if (elapsedTime >= TotalElapsedTime)
for (int s = 0; s < CurrentSequence; s++) Framing.Clear();
lastEndTime = Sequences[s].EndTime;
var exactFrameAtThisTime = (elapsedTime - lastEndTime) / Sequences[CurrentSequence].TimePerFrame;
FrameInSequence = Math.Min(exactFrameAtThisTime.FloorToInt(), Sequences[CurrentSequence].EndFrame);
NextFrameInSequence = Math.Min(FrameInSequence + 1, Sequences[CurrentSequence].EndFrame);
LerpAmount = Math.Clamp(exactFrameAtThisTime - FrameInSequence, 0, 1);
}
private void Reset()
{
FrameInSequence = 0;
NextFrameInSequence = 0;
LerpAmount = 0.0f;
CurrentSequence = 0;
} }
public void Dispose() public void Dispose()

View File

@@ -1,4 +1,6 @@
namespace FModel.Views.Snooper.Animations; using System.Collections.Generic;
namespace FModel.Views.Snooper.Animations;
public class Bone public class Bone
{ {
@@ -8,18 +10,21 @@ public class Bone
public string LoweredParentName; public string LoweredParentName;
public int SkeletonIndex = -1; public int SkeletonIndex = -1;
public bool IsAnimated; public readonly List<int> AnimatedBySequences;
public Bone(int i, int p, Transform t) public Bone(int i, int p, Transform t)
{ {
Index = i; Index = i;
ParentIndex = p; ParentIndex = p;
Rest = t; Rest = t;
AnimatedBySequences = new List<int>();
} }
public bool IsRoot => Index == 0 && ParentIndex == -1 && string.IsNullOrEmpty(LoweredParentName); public bool IsRoot => Index == 0 && ParentIndex == -1 && string.IsNullOrEmpty(LoweredParentName);
public bool IsMapped => SkeletonIndex > -1; public bool IsMapped => SkeletonIndex > -1;
public bool IsAnimated => AnimatedBySequences.Count > 0;
public bool IsNative => Index == SkeletonIndex; public bool IsNative => Index == SkeletonIndex;
public override string ToString() => $"Mesh Ref '{Index}' is Skel Ref '{SkeletonIndex}' ({IsAnimated})"; public override string ToString() => $"Mesh Ref '{Index}' is Skel Ref '{SkeletonIndex}'";
} }

View File

@@ -4,6 +4,7 @@ using System.Numerics;
using CUE4Parse_Conversion.Animations.PSA; using CUE4Parse_Conversion.Animations.PSA;
using CUE4Parse.UE4.Assets.Exports.Animation; using CUE4Parse.UE4.Assets.Exports.Animation;
using CUE4Parse.UE4.Objects.Core.Math; using CUE4Parse.UE4.Objects.Core.Math;
using CUE4Parse.Utils;
using FModel.Views.Snooper.Buffers; using FModel.Views.Snooper.Buffers;
using OpenTK.Graphics.OpenGL4; using OpenTK.Graphics.OpenGL4;
using Serilog; using Serilog;
@@ -74,7 +75,6 @@ public class Skeleton : IDisposable
var skeletonBoneIndex = bone.SkeletonIndex; var skeletonBoneIndex = bone.SkeletonIndex;
if (sequence.OriginalSequence.FindTrackForBoneIndex(skeletonBoneIndex) < 0) if (sequence.OriginalSequence.FindTrackForBoneIndex(skeletonBoneIndex) < 0)
{ {
bone.IsAnimated |= false;
for (int frame = 0; frame < _animatedBonesTransform[s][bone.Index].Length; frame++) for (int frame = 0; frame < _animatedBonesTransform[s][bone.Index].Length; frame++)
{ {
_animatedBonesTransform[s][bone.Index][frame] = new Transform _animatedBonesTransform[s][bone.Index][frame] = new Transform
@@ -86,7 +86,7 @@ public class Skeleton : IDisposable
} }
else else
{ {
bone.IsAnimated |= true; bone.AnimatedBySequences.Add(s);
for (int frame = 0; frame < _animatedBonesTransform[s][bone.Index].Length; frame++) for (int frame = 0; frame < _animatedBonesTransform[s][bone.Index].Length; frame++)
{ {
var boneOrientation = bone.Rest.Rotation; var boneOrientation = bone.Rest.Rotation;
@@ -173,7 +173,6 @@ public class Skeleton : IDisposable
continue; continue;
bone.SkeletonIndex = boneIndex; bone.SkeletonIndex = boneIndex;
bone.IsAnimated = false;
} }
#if DEBUG #if DEBUG
@@ -192,7 +191,7 @@ public class Skeleton : IDisposable
foreach (var bone in BonesByLoweredName.Values) foreach (var bone in BonesByLoweredName.Values)
{ {
bone.SkeletonIndex = -1; bone.SkeletonIndex = -1;
bone.IsAnimated = false; bone.AnimatedBySequences.Clear();
} }
if (!full) return; if (!full) return;
@@ -208,22 +207,59 @@ public class Skeleton : IDisposable
_ssbo.UpdateRange(BoneCount, Matrix4x4.Identity); _ssbo.UpdateRange(BoneCount, Matrix4x4.Identity);
} }
public void UpdateAnimationMatrices(int currentSequence, int frameInSequence, int nextFrameInSequence, float lerp) public void UpdateAnimationMatrices(Animation animation)
{ {
if (!IsAnimated) return; if (!IsAnimated) return;
_ssbo.Bind(); _ssbo.Bind();
for (int boneIndex = 0; boneIndex < BoneCount; boneIndex++)
foreach (var bone in BonesByLoweredName.Values)
{ {
var (s, f) = GetBoneFrameData(bone, animation);
var frameInSequence = Math.Min(f.FloorToInt(), animation.Sequences[s].EndFrame);
var nextFrameInSequence = Math.Min(frameInSequence + 1, animation.Sequences[s].EndFrame);
var lerpAmount = Math.Clamp(f - frameInSequence, 0, 1);
var boneIndex = bone.Index;
var matrix = Matrix4x4.Lerp( var matrix = Matrix4x4.Lerp(
_animatedBonesTransform[currentSequence][boneIndex][frameInSequence].Matrix, _animatedBonesTransform[s][boneIndex][frameInSequence].Matrix,
_animatedBonesTransform[currentSequence][boneIndex][nextFrameInSequence].Matrix, _animatedBonesTransform[s][boneIndex][nextFrameInSequence].Matrix,
lerp); lerpAmount);
_ssbo.Update(boneIndex, _invertedBonesMatrix[boneIndex] * matrix); _ssbo.Update(boneIndex, _invertedBonesMatrix[boneIndex] * matrix);
} }
_ssbo.Unbind(); _ssbo.Unbind();
} }
private (int, float) GetBoneFrameData(Bone bone, Animation animation)
{
int s = -1;
float f = 0.0f;
void Get(Bone b)
{
foreach (var i in b.AnimatedBySequences)
{
s = i;
if (animation.Framing.TryGetValue(s, out f))
break;
}
}
Get(bone);
if (s == -1)
{
var parent = BonesByLoweredName[bone.LoweredParentName];
while (!parent.IsAnimated)
{
parent = BonesByLoweredName[parent.LoweredParentName];
}
Get(parent);
}
return (s, f);
}
public Matrix4x4 GetBoneMatrix(Bone bone) => IsAnimated ? bone.Rest.Matrix * _ssbo.Get(bone.Index) : bone.Rest.Matrix; public Matrix4x4 GetBoneMatrix(Bone bone) => IsAnimated ? bone.Rest.Matrix * _ssbo.Get(bone.Index) : bone.Rest.Matrix;
public void Render() public void Render()

View File

@@ -267,9 +267,7 @@ public class Renderer : IDisposable
animation.TimeCalculation(Options.Tracker.ElapsedTime); animation.TimeCalculation(Options.Tracker.ElapsedTime);
foreach (var guid in animation.AttachedModels.Where(guid => Options.Models[guid].HasSkeleton)) foreach (var guid in animation.AttachedModels.Where(guid => Options.Models[guid].HasSkeleton))
{ {
Options.Models[guid].Skeleton.UpdateAnimationMatrices( Options.Models[guid].Skeleton.UpdateAnimationMatrices(animation);
animation.CurrentSequence, animation.FrameInSequence,
animation.NextFrameInSequence, animation.LerpAmount);
} }
} }