diff --git a/CUE4Parse b/CUE4Parse index 4f5d66ac..b18da130 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit 4f5d66ac7cacb0aad373a9f19c203569f1c38a93 +Subproject commit b18da13023101f3d050420cf4878f4a1175dc344 diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index 2c486824..71508d8e 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -85,10 +85,10 @@ public partial class MainWindow #if DEBUG await _threadWorkerView.Begin(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 => _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 } @@ -142,7 +142,7 @@ public partial class MainWindow private async void OnMappingsReload(object sender, ExecutedRoutedEventArgs e) { - await _applicationView.CUE4Parse.InitMappings(); + await _applicationView.CUE4Parse.InitMappings(true); } private void OnOpenAvalonFinder() diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 52ec612f..c9b00d73 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -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)) { @@ -377,7 +377,7 @@ public class CUE4ParseViewModel : ViewModel if (!mapping.IsValid) continue; var mappingPath = Path.Combine(mappingsFolder, mapping.FileName); - if (!File.Exists(mappingPath)) + if (force || !File.Exists(mappingPath)) { _apiEndpointView.DownloadFile(mapping.Url, mappingPath); } @@ -883,8 +883,12 @@ public class CUE4ParseViewModel : ViewModel { var shouldDecompress = UserSettings.Default.CompressedAudioMode == ECompressedAudio.PlayDecompressed; 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; + } SaveAndPlaySound(Path.Combine(TabControl.SelectedTab.Directory, TabControl.SelectedTab.Header.SubstringBeforeLast('.')).Replace('\\', '/'), audioFormat, data); return false; diff --git a/FModel/Views/Snooper/Animations/Animation.cs b/FModel/Views/Snooper/Animations/Animation.cs index dc9204de..028676c5 100644 --- a/FModel/Views/Snooper/Animations/Animation.cs +++ b/FModel/Views/Snooper/Animations/Animation.cs @@ -4,7 +4,6 @@ using System.Numerics; using CUE4Parse_Conversion.Animations.PSA; using CUE4Parse.UE4.Assets.Exports; using CUE4Parse.UE4.Objects.Core.Misc; -using CUE4Parse.Utils; using ImGuiNET; namespace FModel.Views.Snooper.Animations; @@ -20,15 +19,8 @@ public class Animation : IDisposable public readonly float StartTime; // Animation Start Time public readonly float EndTime; // Animation End Time public readonly float TotalElapsedTime; // Animation Max Time - public readonly string TargetSkeleton; + public readonly Dictionary 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 IsSelected; @@ -40,23 +32,22 @@ public class Animation : IDisposable Path = _export.GetPathName(); Name = _export.Name; Sequences = Array.Empty(); + Framing = new Dictionary(); AttachedModels = new List(); } public Animation(UObject export, CAnimSet animSet) : this(export) { _animSet = animSet; - TargetSkeleton = _animSet.Skeleton.Name; Sequences = new Sequence[_animSet.Sequences.Count]; for (int i = 0; i < Sequences.Length; i++) { Sequences[i] = new Sequence(_animSet.Sequences[i]); - EndTime = Sequences[i].EndTime; - TotalElapsedTime += _animSet.Sequences[i].NumFrames * Sequences[i].TimePerFrame; } + TotalElapsedTime = animSet.TotalAnimTime; if (Sequences.Length > 0) StartTime = Sequences[0].StartTime; } @@ -70,30 +61,16 @@ public class Animation : IDisposable { 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; - break; + Framing[i] = (elapsedTime - sequence.StartTime) / sequence.TimePerFrame; } + else Framing.Remove(i); } - if (elapsedTime >= TotalElapsedTime) Reset(); - var lastEndTime = 0.0f; - for (int s = 0; s < CurrentSequence; s++) - 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; + if (elapsedTime >= TotalElapsedTime) + Framing.Clear(); } public void Dispose() diff --git a/FModel/Views/Snooper/Animations/Bone.cs b/FModel/Views/Snooper/Animations/Bone.cs index 460618f3..5fd846df 100644 --- a/FModel/Views/Snooper/Animations/Bone.cs +++ b/FModel/Views/Snooper/Animations/Bone.cs @@ -1,4 +1,6 @@ -namespace FModel.Views.Snooper.Animations; +using System.Collections.Generic; + +namespace FModel.Views.Snooper.Animations; public class Bone { @@ -8,18 +10,21 @@ public class Bone public string LoweredParentName; public int SkeletonIndex = -1; - public bool IsAnimated; + public readonly List AnimatedBySequences; public Bone(int i, int p, Transform t) { Index = i; ParentIndex = p; Rest = t; + + AnimatedBySequences = new List(); } public bool IsRoot => Index == 0 && ParentIndex == -1 && string.IsNullOrEmpty(LoweredParentName); public bool IsMapped => SkeletonIndex > -1; + public bool IsAnimated => AnimatedBySequences.Count > 0; 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}'"; } diff --git a/FModel/Views/Snooper/Animations/Skeleton.cs b/FModel/Views/Snooper/Animations/Skeleton.cs index a1914d62..94f33938 100644 --- a/FModel/Views/Snooper/Animations/Skeleton.cs +++ b/FModel/Views/Snooper/Animations/Skeleton.cs @@ -4,6 +4,7 @@ using System.Numerics; using CUE4Parse_Conversion.Animations.PSA; using CUE4Parse.UE4.Assets.Exports.Animation; using CUE4Parse.UE4.Objects.Core.Math; +using CUE4Parse.Utils; using FModel.Views.Snooper.Buffers; using OpenTK.Graphics.OpenGL4; using Serilog; @@ -74,7 +75,6 @@ public class Skeleton : IDisposable var skeletonBoneIndex = bone.SkeletonIndex; if (sequence.OriginalSequence.FindTrackForBoneIndex(skeletonBoneIndex) < 0) { - bone.IsAnimated |= false; for (int frame = 0; frame < _animatedBonesTransform[s][bone.Index].Length; frame++) { _animatedBonesTransform[s][bone.Index][frame] = new Transform @@ -86,7 +86,7 @@ public class Skeleton : IDisposable } else { - bone.IsAnimated |= true; + bone.AnimatedBySequences.Add(s); for (int frame = 0; frame < _animatedBonesTransform[s][bone.Index].Length; frame++) { var boneOrientation = bone.Rest.Rotation; @@ -173,7 +173,6 @@ public class Skeleton : IDisposable continue; bone.SkeletonIndex = boneIndex; - bone.IsAnimated = false; } #if DEBUG @@ -192,7 +191,7 @@ public class Skeleton : IDisposable foreach (var bone in BonesByLoweredName.Values) { bone.SkeletonIndex = -1; - bone.IsAnimated = false; + bone.AnimatedBySequences.Clear(); } if (!full) return; @@ -208,22 +207,59 @@ public class Skeleton : IDisposable _ssbo.UpdateRange(BoneCount, Matrix4x4.Identity); } - public void UpdateAnimationMatrices(int currentSequence, int frameInSequence, int nextFrameInSequence, float lerp) + public void UpdateAnimationMatrices(Animation animation) { if (!IsAnimated) return; _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( - _animatedBonesTransform[currentSequence][boneIndex][frameInSequence].Matrix, - _animatedBonesTransform[currentSequence][boneIndex][nextFrameInSequence].Matrix, - lerp); + _animatedBonesTransform[s][boneIndex][frameInSequence].Matrix, + _animatedBonesTransform[s][boneIndex][nextFrameInSequence].Matrix, + lerpAmount); _ssbo.Update(boneIndex, _invertedBonesMatrix[boneIndex] * matrix); } + _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 void Render() diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs index e856e4c5..b25391f2 100644 --- a/FModel/Views/Snooper/Renderer.cs +++ b/FModel/Views/Snooper/Renderer.cs @@ -267,9 +267,7 @@ public class Renderer : IDisposable animation.TimeCalculation(Options.Tracker.ElapsedTime); foreach (var guid in animation.AttachedModels.Where(guid => Options.Models[guid].HasSkeleton)) { - Options.Models[guid].Skeleton.UpdateAnimationMatrices( - animation.CurrentSequence, animation.FrameInSequence, - animation.NextFrameInSequence, animation.LerpAmount); + Options.Models[guid].Skeleton.UpdateAnimationMatrices(animation); } }