mirror of
https://github.com/4sval/FModel.git
synced 2026-08-28 04:14:30 -05:00
multiple anims on same model
This commit is contained in:
Submodule CUE4Parse updated: 4f5d66ac7c...b18da13023
@@ -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()
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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}'";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user