cleaned skeleton and bones

This commit is contained in:
4sval 2023-03-14 00:51:23 +01:00
parent 39f5855b12
commit 26e92238ba
11 changed files with 111 additions and 135 deletions

@ -1 +1 @@
Subproject commit 04239c2e1f7fd257c6671a0bb52a7896c0232930
Subproject commit d0a849608247dbdcb14b9b6e6851addd761f3c73

View File

@ -76,19 +76,12 @@ public partial class MainWindow
_discordHandler.Initialize(_applicationView.CUE4Parse.Provider.GameName);
#if DEBUG
// await _threadWorkerView.Begin(cancellationToken =>
// _applicationView.CUE4Parse.Extract(cancellationToken,
// "ShooterGame/Content/Characters/_Core/3P/Models/TP_Core_NewMale_Skelmesh.uasset"));
// await _threadWorkerView.Begin(cancellationToken =>
// _applicationView.CUE4Parse.Extract(cancellationToken,
// "Game/Characters/_Core/3P/Anims/TP_Core_SprintAddN_UB.uasset"));
//
// await _threadWorkerView.Begin(cancellationToken =>
// _applicationView.CUE4Parse.Extract(cancellationToken,
// "ShooterGame/Content/Characters/Guide/S0/1P/Models/FP_Guide_S0_Skelmesh.uasset"));
// await _threadWorkerView.Begin(cancellationToken =>
// _applicationView.CUE4Parse.Extract(cancellationToken,
// "/Game/Equippables/Guns/SniperRifles/Boltsniper/S0/1P/Anims/FP_Core_Boltsniper_S0_Fire.uasset"));
await _threadWorkerView.Begin(cancellationToken =>
_applicationView.CUE4Parse.Extract(cancellationToken,
"ShooterGame/Content/Characters/BountyHunter/S0/Ability_4/1P/Models/AB_BountyHunter_S0_4_TrailCreature_Skelmesh.uasset"));
await _threadWorkerView.Begin(cancellationToken =>
_applicationView.CUE4Parse.Extract(cancellationToken,
"ShooterGame/Content/Characters/BountyHunter/S0/Ability_4/1P/Anims/FP_BountyHunter_S0_4_Aim_S.uasset"));
#endif
}

View File

@ -6,7 +6,6 @@ using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using AdonisUI.Controls;
using CUE4Parse.Encryption.Aes;
using CUE4Parse.FileProvider;
@ -41,7 +40,6 @@ using FModel.Settings;
using FModel.Views;
using FModel.Views.Resources.Controls;
using FModel.Views.Snooper;
using ImGuiNET;
using Newtonsoft.Json;
using Ookii.Dialogs.Wpf;
using OpenTK.Windowing.Common;

View File

@ -1,5 +1,6 @@
using System.Collections;
using System.Linq;
using System.Threading;
using FModel.Framework;
using FModel.Services;
@ -28,6 +29,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
case "Assets_Extract_New_Tab":
foreach (var asset in assetItems)
{
Thread.Sleep(10);
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, true);
}
@ -35,6 +37,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
case "Assets_Export_Data":
foreach (var asset in assetItems)
{
Thread.Sleep(10);
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.ExportData(asset.FullPath);
}
@ -42,6 +45,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
case "Assets_Save_Properties":
foreach (var asset in assetItems)
{
Thread.Sleep(10);
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Properties);
}
@ -49,6 +53,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
case "Assets_Save_Textures":
foreach (var asset in assetItems)
{
Thread.Sleep(10);
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Textures);
}
@ -56,6 +61,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
case "Assets_Save_Models":
foreach (var asset in assetItems)
{
Thread.Sleep(10);
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Meshes | EBulkType.Auto);
}
@ -63,6 +69,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
case "Assets_Save_Animations":
foreach (var asset in assetItems)
{
Thread.Sleep(10);
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Animations | EBulkType.Auto);
}

View File

@ -6,7 +6,6 @@ using FModel.ViewModels.Commands;
using FModel.Views.Resources.Controls;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
using Microsoft.Win32;
using Serilog;
using SkiaSharp;
using System.Collections.Generic;
@ -17,7 +16,6 @@ using System.Windows;
using System.Windows.Media.Imaging;
using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse_Conversion.Textures;
using Ookii.Dialogs.Wpf;
namespace FModel.ViewModels;

View File

@ -0,0 +1,25 @@
namespace FModel.Views.Snooper.Animations;
public class Bone
{
public readonly int Index;
public readonly int ParentIndex;
public readonly Transform Rest;
public string LoweredParentName;
public int SkeletonIndex = -1;
public bool[] IsAnimated;
public Bone(int i, int p, Transform t)
{
Index = i;
ParentIndex = p;
Rest = t;
}
public bool IsRoot => Index == 0 && ParentIndex == -1 && string.IsNullOrEmpty(LoweredParentName);
public bool IsMapped => SkeletonIndex > -1;
public bool IsNative => Index == SkeletonIndex;
public override string ToString() => $"Mesh Ref '{Index}' is Skel Ref '{SkeletonIndex}' ({IsNative})";
}

View File

@ -1,19 +0,0 @@
namespace FModel.Views.Snooper.Animations;
public class BoneIndice
{
public int BoneIndex = -1;
public int ParentBoneIndex = -1;
public string LoweredParentBoneName;
public bool IsRoot => BoneIndex == 0 && ParentBoneIndex == -1 && string.IsNullOrEmpty(LoweredParentBoneName);
public int TrackedBoneIndex = -1;
public int TrackedParentBoneIndex = -1; // bone index of the first tracked parent bone
public bool IsTracked => TrackedBoneIndex > -1;
public bool IsParentTracked => TrackedParentBoneIndex > -1;
public bool IsNative => BoneIndex == TrackedBoneIndex;
public bool IsParentNative => ParentBoneIndex == TrackedParentBoneIndex; // always true?
public override string ToString() => $"Mesh Ref '{BoneIndex}' is Skel Ref '{TrackedBoneIndex}' ({IsNative}, {IsParentNative})";
}

View File

@ -16,8 +16,7 @@ public class Skeleton : IDisposable
private BufferObject<Matrix4x4> _ssbo;
public string Name;
public readonly Dictionary<string, BoneIndice> BonesIndicesByLoweredName;
public readonly Dictionary<int, Transform> BonesTransformByIndex;
public readonly Dictionary<string, Bone> BonesByLoweredName;
private int _previousAnimationSequence;
private int _previousSequenceFrame;
@ -28,48 +27,36 @@ public class Skeleton : IDisposable
public Skeleton()
{
BonesIndicesByLoweredName = new Dictionary<string, BoneIndice>();
BonesTransformByIndex = new Dictionary<int, Transform>();
BonesByLoweredName = new Dictionary<string, Bone>();
_animatedBonesTransform = Array.Empty<Transform[][]>();
_invertedBonesMatrix = Array.Empty<Matrix4x4>();
}
public Skeleton(FReferenceSkeleton referenceSkeleton) : this()
{
for (int boneIndex = 0; boneIndex < referenceSkeleton.FinalRefBoneInfo.Length; boneIndex++)
_invertedBonesMatrix = new Matrix4x4[referenceSkeleton.FinalRefBoneInfo.Length];
for (int boneIndex = 0; boneIndex < _invertedBonesMatrix.Length; boneIndex++)
{
var info = referenceSkeleton.FinalRefBoneInfo[boneIndex];
var boneIndices = new BoneIndice { BoneIndex = boneIndex, ParentBoneIndex = info.ParentIndex };
if (!boneIndices.IsRoot)
boneIndices.LoweredParentBoneName =
referenceSkeleton.FinalRefBoneInfo[boneIndices.ParentBoneIndex].Name.Text.ToLower();
BonesIndicesByLoweredName[info.Name.Text.ToLower()] = boneIndices;
}
_invertedBonesMatrix = new Matrix4x4[BonesIndicesByLoweredName.Count];
foreach (var boneIndices in BonesIndicesByLoweredName.Values)
{
var bone = referenceSkeleton.FinalRefBonePose[boneIndices.BoneIndex];
if (!BonesTransformByIndex.TryGetValue(boneIndices.BoneIndex, out var boneTransform))
var boneTransform = new Transform
{
boneTransform = new Transform
{
Rotation = bone.Rotation,
Position = bone.Translation * Constants.SCALE_DOWN_RATIO,
Scale = bone.Scale3D
};
Rotation = referenceSkeleton.FinalRefBonePose[boneIndex].Rotation,
Position = referenceSkeleton.FinalRefBonePose[boneIndex].Translation * Constants.SCALE_DOWN_RATIO,
Scale = referenceSkeleton.FinalRefBonePose[boneIndex].Scale3D
};
var bone = new Bone(boneIndex, info.ParentIndex, boneTransform);
if (!bone.IsRoot)
{
bone.LoweredParentName =
referenceSkeleton.FinalRefBoneInfo[bone.ParentIndex].Name.Text.ToLower();
bone.Rest.Relation = BonesByLoweredName[bone.LoweredParentName].Rest.Matrix;
}
if (!BonesTransformByIndex.TryGetValue(boneIndices.ParentBoneIndex, out var parentTransform))
parentTransform = new Transform { Relation = Matrix4x4.Identity };
BonesByLoweredName[info.Name.Text.ToLower()] = bone;
boneTransform.Relation = parentTransform.Matrix;
Matrix4x4.Invert(boneTransform.Matrix, out var inverted);
BonesTransformByIndex[boneIndices.BoneIndex] = boneTransform;
_invertedBonesMatrix[boneIndices.BoneIndex] = inverted;
_invertedBonesMatrix[bone.Index] = inverted;
}
}
@ -82,48 +69,47 @@ public class Skeleton : IDisposable
{
var sequence = anim.Sequences[s];
_animatedBonesTransform[s] = new Transform[BoneCount][];
foreach (var boneIndices in BonesIndicesByLoweredName.Values)
foreach (var bone in BonesByLoweredName.Values)
{
var originalTransform = BonesTransformByIndex[boneIndices.BoneIndex];
_animatedBonesTransform[s][boneIndices.BoneIndex] = new Transform[sequence.NumFrames];
_animatedBonesTransform[s][bone.Index] = new Transform[sequence.NumFrames];
var trackedBoneIndex = boneIndices.TrackedBoneIndex;
if (sequence.OriginalSequence.FindTrackForBoneIndex(trackedBoneIndex) < 0)
var skeletonBoneIndex = bone.SkeletonIndex;
bone.IsAnimated[s] = sequence.OriginalSequence.FindTrackForBoneIndex(skeletonBoneIndex) >= 0;
if (!bone.IsAnimated[s])
{
for (int frame = 0; frame < _animatedBonesTransform[s][boneIndices.BoneIndex].Length; frame++)
for (int frame = 0; frame < _animatedBonesTransform[s][bone.Index].Length; frame++)
{
_animatedBonesTransform[s][boneIndices.BoneIndex][frame] = new Transform
_animatedBonesTransform[s][bone.Index][frame] = new Transform
{
Relation = boneIndices.IsParentTracked ?
originalTransform.LocalMatrix * _animatedBonesTransform[s][boneIndices.TrackedParentBoneIndex][frame].Matrix :
originalTransform.Relation
Relation = bone.IsRoot ? bone.Rest.Relation :
bone.Rest.LocalMatrix * _animatedBonesTransform[s][bone.ParentIndex][frame].Matrix
};
}
}
else
{
for (int frame = 0; frame < _animatedBonesTransform[s][boneIndices.BoneIndex].Length; frame++)
for (int frame = 0; frame < _animatedBonesTransform[s][bone.Index].Length; frame++)
{
var boneOrientation = originalTransform.Rotation;
var bonePosition = originalTransform.Position;
var boneScale = originalTransform.Scale;
var boneOrientation = bone.Rest.Rotation;
var bonePosition = bone.Rest.Position;
var boneScale = bone.Rest.Scale;
sequence.Tracks[trackedBoneIndex].GetBoneTransform(frame, sequence.NumFrames, ref boneOrientation, ref bonePosition, ref boneScale);
sequence.Tracks[skeletonBoneIndex].GetBoneTransform(frame, sequence.NumFrames, ref boneOrientation, ref bonePosition, ref boneScale);
switch (anim.Skeleton.BoneTree[trackedBoneIndex])
switch (anim.Skeleton.BoneTree[skeletonBoneIndex])
{
case EBoneTranslationRetargetingMode.Skeleton when !rotationOnly:
{
var targetTransform = sequence.RetargetBasePose?[trackedBoneIndex] ?? anim.Skeleton.ReferenceSkeleton.FinalRefBonePose[trackedBoneIndex];
var targetTransform = sequence.RetargetBasePose?[skeletonBoneIndex] ?? anim.Skeleton.ReferenceSkeleton.FinalRefBonePose[skeletonBoneIndex];
bonePosition = targetTransform.Translation;
break;
}
case EBoneTranslationRetargetingMode.AnimationScaled when !rotationOnly:
{
var sourceTranslationLength = (originalTransform.Position / Constants.SCALE_DOWN_RATIO).Size();
var sourceTranslationLength = (bone.Rest.Position / Constants.SCALE_DOWN_RATIO).Size();
if (sourceTranslationLength > UnrealMath.KindaSmallNumber)
{
var targetTranslationLength = sequence.RetargetBasePose?[trackedBoneIndex].Translation.Size() ?? anim.Skeleton.ReferenceSkeleton.FinalRefBonePose[trackedBoneIndex].Translation.Size();
var targetTranslationLength = sequence.RetargetBasePose?[skeletonBoneIndex].Translation.Size() ?? anim.Skeleton.ReferenceSkeleton.FinalRefBonePose[skeletonBoneIndex].Translation.Size();
bonePosition.Scale(targetTranslationLength / sourceTranslationLength);
}
break;
@ -131,19 +117,19 @@ public class Skeleton : IDisposable
case EBoneTranslationRetargetingMode.AnimationRelative when !rotationOnly:
{
// can't tell if it's working or not
var sourceSkelTrans = originalTransform.Position / Constants.SCALE_DOWN_RATIO;
var refPoseTransform = sequence.RetargetBasePose?[trackedBoneIndex] ?? anim.Skeleton.ReferenceSkeleton.FinalRefBonePose[trackedBoneIndex];
var sourceSkelTrans = bone.Rest.Position / Constants.SCALE_DOWN_RATIO;
var refPoseTransform = sequence.RetargetBasePose?[skeletonBoneIndex] ?? anim.Skeleton.ReferenceSkeleton.FinalRefBonePose[skeletonBoneIndex];
boneOrientation = boneOrientation * FQuat.Conjugate(originalTransform.Rotation) * refPoseTransform.Rotation;
boneOrientation = boneOrientation * FQuat.Conjugate(bone.Rest.Rotation) * refPoseTransform.Rotation;
bonePosition += refPoseTransform.Translation - sourceSkelTrans;
boneScale *= refPoseTransform.Scale3D * originalTransform.Scale;
boneScale *= refPoseTransform.Scale3D * bone.Rest.Scale;
boneOrientation.Normalize();
break;
}
case EBoneTranslationRetargetingMode.OrientAndScale when !rotationOnly:
{
var sourceSkelTrans = originalTransform.Position / Constants.SCALE_DOWN_RATIO;
var targetSkelTrans = sequence.RetargetBasePose?[trackedBoneIndex].Translation ?? anim.Skeleton.ReferenceSkeleton.FinalRefBonePose[trackedBoneIndex].Translation;
var sourceSkelTrans = bone.Rest.Position / Constants.SCALE_DOWN_RATIO;
var targetSkelTrans = sequence.RetargetBasePose?[skeletonBoneIndex].Translation ?? anim.Skeleton.ReferenceSkeleton.FinalRefBonePose[skeletonBoneIndex].Translation;
if (!sourceSkelTrans.Equals(targetSkelTrans))
{
@ -163,11 +149,11 @@ public class Skeleton : IDisposable
}
}
_animatedBonesTransform[s][boneIndices.BoneIndex][frame] = new Transform
_animatedBonesTransform[s][bone.Index][frame] = new Transform
{
Relation = boneIndices.IsParentTracked ? _animatedBonesTransform[s][boneIndices.TrackedParentBoneIndex][frame].Matrix : originalTransform.Relation,
Relation = bone.IsRoot ? bone.Rest.Relation : _animatedBonesTransform[s][bone.ParentIndex][frame].Matrix,
Rotation = boneOrientation,
Position = rotationOnly ? originalTransform.Position : bonePosition * Constants.SCALE_DOWN_RATIO,
Position = rotationOnly ? bone.Rest.Position : bonePosition * Constants.SCALE_DOWN_RATIO,
Scale = boneScale
};
}
@ -180,53 +166,35 @@ public class Skeleton : IDisposable
{
ResetAnimatedData();
// tracked bones
for (int trackIndex = 0; trackIndex < anim.Skeleton.BoneCount; trackIndex++)
// map bones
for (int boneIndex = 0; boneIndex < anim.Skeleton.BoneCount; boneIndex++)
{
var info = anim.Skeleton.ReferenceSkeleton.FinalRefBoneInfo[trackIndex];
if (!BonesIndicesByLoweredName.TryGetValue(info.Name.Text.ToLower(), out var boneIndices))
var info = anim.Skeleton.ReferenceSkeleton.FinalRefBoneInfo[boneIndex];
if (!BonesByLoweredName.TryGetValue(info.Name.Text.ToLower(), out var bone))
continue;
boneIndices.TrackedBoneIndex = trackIndex;
var parentTrackIndex = info.ParentIndex;
do
{
if (parentTrackIndex < 0) break;
info = anim.Skeleton.ReferenceSkeleton.FinalRefBoneInfo[parentTrackIndex];
if (boneIndices.LoweredParentBoneName.Equals(info.Name.Text, StringComparison.OrdinalIgnoreCase) && // same parent (name based)
BonesIndicesByLoweredName.TryGetValue(info.Name.Text.ToLower(), out var parentBoneIndices) && parentBoneIndices.IsTracked)
boneIndices.TrackedParentBoneIndex = parentBoneIndices.BoneIndex;
else parentTrackIndex = info.ParentIndex;
} while (!boneIndices.IsParentTracked);
bone.SkeletonIndex = boneIndex;
}
// fix parent of untracked bones
foreach ((var boneName, var boneIndices) in BonesIndicesByLoweredName)
var seqCount = anim.Sequences.Count;
foreach ((var boneName, var bone) in BonesByLoweredName)
{
if (boneIndices.IsRoot || boneIndices.IsTracked && boneIndices.IsParentTracked) // assuming root bone always has a track
bone.IsAnimated = new bool[seqCount];
#if DEBUG
if (bone.IsRoot || bone.IsMapped) // assuming root bone always is mapped
continue;
#if DEBUG
Log.Warning($"{Name} Bone Mismatch: {boneName} ({boneIndices.BoneIndex}) was not present in the anim's target skeleton");
Log.Warning($"{Name} Bone Mismatch: {boneName} ({bone.Index}) was not present in the anim's target skeleton");
#endif
var loweredParentBoneName = boneIndices.LoweredParentBoneName;
do
{
var parentBoneIndices = BonesIndicesByLoweredName[loweredParentBoneName];
if (parentBoneIndices.IsParentTracked || parentBoneIndices.IsRoot) boneIndices.TrackedParentBoneIndex = parentBoneIndices.BoneIndex;
else loweredParentBoneName = parentBoneIndices.LoweredParentBoneName;
} while (!boneIndices.IsParentTracked);
}
}
public void ResetAnimatedData(bool full = false)
{
foreach (var boneIndices in BonesIndicesByLoweredName.Values)
foreach (var bone in BonesByLoweredName.Values)
{
boneIndices.TrackedBoneIndex = -1;
boneIndices.TrackedParentBoneIndex = -1;
bone.SkeletonIndex = -1;
bone.IsAnimated = null;
}
if (!full) return;
@ -256,11 +224,11 @@ public class Skeleton : IDisposable
_ssbo.Unbind();
}
public Matrix4x4 GetBoneMatrix(BoneIndice boneIndices)
public Matrix4x4 GetBoneMatrix(Bone bone)
{
return IsAnimated
? _animatedBonesTransform[_previousAnimationSequence][boneIndices.BoneIndex][_previousSequenceFrame].Matrix
: BonesTransformByIndex[boneIndices.BoneIndex].Matrix;
? _animatedBonesTransform[_previousAnimationSequence][bone.Index][_previousSequenceFrame].Matrix
: bone.Rest.Matrix;
}
public void Render()
@ -270,8 +238,7 @@ public class Skeleton : IDisposable
public void Dispose()
{
BonesIndicesByLoweredName.Clear();
BonesTransformByIndex.Clear();
BonesByLoweredName.Clear();
_ssbo?.Dispose();
GL.DeleteProgram(_handle);

View File

@ -256,8 +256,8 @@ public class Model : IDisposable
foreach (var socket in Sockets)
{
var boneMatrix = Matrix4x4.Identity;
if (HasSkeleton && Skeleton.BonesIndicesByLoweredName.TryGetValue(socket.BoneName.Text.ToLower(), out var boneIndices))
boneMatrix = Skeleton.GetBoneMatrix(boneIndices);
if (HasSkeleton && Skeleton.BonesByLoweredName.TryGetValue(socket.BoneName.Text.ToLower(), out var bone))
boneMatrix = Skeleton.GetBoneMatrix(bone);
var socketRelation = boneMatrix * worldMatrix;
foreach (var info in socket.AttachedModels)

View File

@ -295,6 +295,7 @@ public class SnimGui
- WASD to move around
- Shift to move faster
- XC to zoom
- Z to animate selected model
- Left Mouse Button pressed to look around
- Right Click to select a model in the world

View File

@ -159,6 +159,12 @@ public class Snooper : GameWindow
var delta = (float) e.Time;
Renderer.CameraOp.Modify(KeyboardState, delta);
if (KeyboardState.IsKeyPressed(Keys.Z))
{
Renderer.Options.RemoveAnimations();
Renderer.Options.AnimateMesh(true);
WindowShouldClose(true, false);
}
if (KeyboardState.IsKeyPressed(Keys.Space))
Renderer.Options.Tracker.IsPaused = !Renderer.Options.Tracker.IsPaused;
if (KeyboardState.IsKeyPressed(Keys.Delete))