From f36a7b79cd89b55a51c7b7b8f29b4b32a4d88967 Mon Sep 17 00:00:00 2001 From: 4sval Date: Sat, 4 Feb 2023 01:31:37 +0100 Subject: [PATCH] some animations work, others do not, awaiting improvements --- CUE4Parse | 2 +- FModel/Constants.cs | 1 + FModel/Resources/default.vert | 44 +++++------ FModel/Resources/outline.vert | 26 ++++++- .../Snooper/Models/Animations/Animation.cs | 33 +++++--- .../Snooper/Models/Animations/Skeleton.cs | 49 ++++++++---- FModel/Views/Snooper/Models/Model.cs | 77 +++++++++++++------ FModel/Views/Snooper/Shading/TextureHelper.cs | 4 + FModel/Views/Snooper/SnimGui.cs | 5 +- 9 files changed, 162 insertions(+), 79 deletions(-) diff --git a/CUE4Parse b/CUE4Parse index eef05377..3a7ddc13 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit eef05377f72ec488588b9c2b7dcc26bd9dea7c71 +Subproject commit 3a7ddc13bb370e5988bc122a7cf67ad8036ccaf1 diff --git a/FModel/Constants.cs b/FModel/Constants.cs index e2667514..613a6ceb 100644 --- a/FModel/Constants.cs +++ b/FModel/Constants.cs @@ -11,6 +11,7 @@ public static class Constants public const float SCALE_DOWN_RATIO = 0.01F; public const int SAMPLES_COUNT = 4; + public const int MAX_BONE_UNIFORM = 250; public const string WHITE = "#DAE5F2"; public const string GRAY = "#BBBBBB"; diff --git a/FModel/Resources/default.vert b/FModel/Resources/default.vert index 2a7fb360..03da234c 100644 --- a/FModel/Resources/default.vert +++ b/FModel/Resources/default.vert @@ -12,12 +12,10 @@ layout (location = 9) in mat4 vInstanceMatrix; layout (location = 13) in vec3 vMorphTargetPos; layout (location = 14) in vec3 vMorphTargetTangent; -//const int MAX_BONES = 140; - uniform mat4 uView; uniform mat4 uProjection; uniform float uMorphTime; -//uniform mat4 uFinalBonesMatrix[MAX_BONES]; +uniform mat4 uFinalBonesMatrix[250]; out vec3 fPos; out vec3 fNormal; @@ -32,25 +30,27 @@ void main() vec4 bindNormal = vec4(vNormal, 1.0); vec4 bindTangent = vec4(mix(vTangent, vMorphTargetTangent, uMorphTime), 1.0); -// vec4 finalPos = vec4(0.0); -// vec4 finalNormal = vec4(0.0); -// vec4 finalTangent = vec4(0.0); -// vec4 weights = normalize(vBoneWeights); -// for(int i = 0 ; i < 4; i++) -// { -// int boneIndex = int(vBoneIds[i]); -// if(boneIndex < 0) break; -// -// finalPos += uFinalBonesMatrix[boneIndex] * bindPos * weights[i]; -// finalNormal += uFinalBonesMatrix[boneIndex] * bindNormal * weights[i]; -// finalTangent += uFinalBonesMatrix[boneIndex] * bindTangent * weights[i]; -// } - - gl_Position = uProjection * uView * vInstanceMatrix * bindPos; - - fPos = vec3(vInstanceMatrix * bindPos); - fNormal = vec3(transpose(inverse(vInstanceMatrix)) * bindNormal); - fTangent = vec3(transpose(inverse(vInstanceMatrix)) * bindTangent); + vec4 finalPos = vec4(0.0); + vec4 finalNormal = vec4(0.0); + vec4 finalTangent = vec4(0.0); + for(int i = 0 ; i < 4; i++) + { + int boneIndex = int(vBoneIds[i]); + if(boneIndex < 0) break; + + mat4 boneMatrix = uFinalBonesMatrix[boneIndex]; + float weight = vBoneWeights[i]; + + finalPos += boneMatrix * bindPos * weight; + finalNormal += boneMatrix * bindNormal * weight; + finalTangent += boneMatrix * bindTangent * weight; + } + + gl_Position = uProjection * uView * vInstanceMatrix * finalPos; + + fPos = vec3(vInstanceMatrix * finalPos); + fNormal = vec3(transpose(inverse(vInstanceMatrix)) * finalNormal); + fTangent = vec3(transpose(inverse(vInstanceMatrix)) * finalTangent); fTexCoords = vTexCoords; fTexLayer = vTexLayer; fColor = vColor; diff --git a/FModel/Resources/outline.vert b/FModel/Resources/outline.vert index 104b6329..645441b7 100644 --- a/FModel/Resources/outline.vert +++ b/FModel/Resources/outline.vert @@ -2,18 +2,38 @@ layout (location = 1) in vec3 vPos; layout (location = 2) in vec3 vNormal; +layout (location = 7) in vec4 vBoneIds; +layout (location = 8) in vec4 vBoneWeights; layout (location = 9) in mat4 vInstanceMatrix; -layout (location = 13) in vec3 vMorphTarget; +layout (location = 13) in vec3 vMorphTargetPos; uniform mat4 uView; uniform vec3 uViewPos; uniform mat4 uProjection; uniform float uMorphTime; +uniform mat4 uFinalBonesMatrix[250]; void main() { - vec3 pos = vec3(vInstanceMatrix * vec4(mix(vPos, vMorphTarget, uMorphTime), 1.0)); - vec3 nor = mat3(transpose(inverse(vInstanceMatrix))) * vNormal; + vec4 bindPos = vec4(mix(vPos, vMorphTargetPos, uMorphTime), 1.0); + vec4 bindNormal = vec4(vNormal, 1.0); + + vec4 finalPos = vec4(0.0); + vec4 finalNormal = vec4(0.0); + for(int i = 0 ; i < 4; i++) + { + int boneIndex = int(vBoneIds[i]); + if(boneIndex < 0) break; + + mat4 boneMatrix = uFinalBonesMatrix[boneIndex]; + float weight = vBoneWeights[i]; + + finalPos += boneMatrix * bindPos * weight; + finalNormal += boneMatrix * bindNormal * weight; + } + + vec3 pos = vec3(vInstanceMatrix * finalPos); + vec3 nor = vec3(transpose(inverse(vInstanceMatrix)) * finalNormal); float scaleFactor = distance(pos, uViewPos) * 0.0025; vec3 scaleVertex = pos + nor * scaleFactor; diff --git a/FModel/Views/Snooper/Models/Animations/Animation.cs b/FModel/Views/Snooper/Models/Animations/Animation.cs index ed3dc90c..3afb06dc 100644 --- a/FModel/Views/Snooper/Models/Animations/Animation.cs +++ b/FModel/Views/Snooper/Models/Animations/Animation.cs @@ -1,5 +1,7 @@ using System; +using System.Numerics; using CUE4Parse_Conversion.Animations; +using CUE4Parse.UE4.Objects.Core.Math; namespace FModel.Views.Snooper.Models.Animations; @@ -15,30 +17,37 @@ public Animation(Skeleton skeleton, CAnimSet anim) var sequence = anim.Sequences[0]; MaxTime = sequence.NumFrames - 1; - BoneTransforms = new Transform[skeleton.BonesTransformByIndex.Count][]; - for (var boneIndex = 0; boneIndex < BoneTransforms.Length; boneIndex++) + BoneTransforms = new Transform[skeleton.UnrealSkeleton.ReferenceSkeleton.FinalRefBoneInfo.Length][]; + for (var trackIndex = 0; trackIndex < BoneTransforms.Length; trackIndex++) { - var parentIndex = skeleton.ReferenceSkeleton.FinalRefBoneInfo[boneIndex].ParentIndex; + var bone = skeleton.UnrealSkeleton.ReferenceSkeleton.FinalRefBoneInfo[trackIndex]; + if (!skeleton.BonesIndexByLoweredName.TryGetValue(bone.Name.Text.ToLower(), out var boneIndex)) + { + BoneTransforms[trackIndex] = new Transform[sequence.NumFrames]; + continue; + } if (!skeleton.BonesTransformByIndex.TryGetValue(boneIndex, out var originalTransform)) - throw new ArgumentNullException("no transform for bone " + boneIndex); + throw new ArgumentNullException($"no transform for bone '{boneIndex}'"); var boneOrientation = originalTransform.Rotation; var bonePosition = originalTransform.Position; var boneScale = originalTransform.Scale; - BoneTransforms[boneIndex] = new Transform[sequence.NumFrames]; - for (var frame = 0; frame < BoneTransforms[boneIndex].Length; frame++) + BoneTransforms[trackIndex] = new Transform[sequence.NumFrames]; + for (var frame = 0; frame < BoneTransforms[trackIndex].Length; frame++) { - sequence.Tracks[boneIndex].GetBonePosition(frame, sequence.NumFrames, false, ref bonePosition, ref boneOrientation); - if (CurrentTime < sequence.Tracks[boneIndex].KeyScale.Length) - boneScale = sequence.Tracks[boneIndex].KeyScale[CurrentTime]; + sequence.Tracks[trackIndex].GetBonePosition(frame, sequence.NumFrames, false, ref bonePosition, ref boneOrientation); + if (CurrentTime < sequence.Tracks[trackIndex].KeyScale.Length) + boneScale = sequence.Tracks[trackIndex].KeyScale[CurrentTime]; + + // revert FixRotationKeys + if (trackIndex > 0) boneOrientation.Conjugate(); - boneOrientation.W *= -1; bonePosition *= Constants.SCALE_DOWN_RATIO; - BoneTransforms[boneIndex][frame] = new Transform + BoneTransforms[trackIndex][frame] = new Transform { - Relation = parentIndex >= 0 ? BoneTransforms[parentIndex][frame].Matrix : originalTransform.Relation, + Relation = bone.ParentIndex >= 0 ? BoneTransforms[bone.ParentIndex][frame].Matrix : originalTransform.Relation, Rotation = boneOrientation, Position = bonePosition, Scale = boneScale diff --git a/FModel/Views/Snooper/Models/Animations/Skeleton.cs b/FModel/Views/Snooper/Models/Animations/Skeleton.cs index 9ff0d689..689a2bcf 100644 --- a/FModel/Views/Snooper/Models/Animations/Skeleton.cs +++ b/FModel/Views/Snooper/Models/Animations/Skeleton.cs @@ -12,16 +12,18 @@ public class Skeleton : IDisposable { public readonly USkeleton UnrealSkeleton; public readonly FReferenceSkeleton ReferenceSkeleton; - public readonly Dictionary BonesIndexByName; + public readonly Dictionary BonesIndexByLoweredName; public readonly Dictionary BonesTransformByIndex; + public readonly Dictionary InvertedBonesMatrixByIndex; public readonly bool IsLoaded; public Animation Anim; public Skeleton() { - BonesIndexByName = new Dictionary(); + BonesIndexByLoweredName = new Dictionary(); BonesTransformByIndex = new Dictionary(); + InvertedBonesMatrixByIndex = new Dictionary(); } public Skeleton(FPackageIndex package, FReferenceSkeleton referenceSkeleton, Transform transform) : this() @@ -30,15 +32,10 @@ public Skeleton(FPackageIndex package, FReferenceSkeleton referenceSkeleton, Tra IsLoaded = UnrealSkeleton != null; if (!IsLoaded) return; - ReferenceSkeleton = UnrealSkeleton.ReferenceSkeleton; - foreach ((var name, var boneIndex) in ReferenceSkeleton.FinalNameToIndexMap) - { - if (!referenceSkeleton.FinalNameToIndexMap.TryGetValue(name, out var newBoneIndex)) - continue; + ReferenceSkeleton = referenceSkeleton; + foreach ((var name, var boneIndex) in referenceSkeleton.FinalNameToIndexMap) + BonesIndexByLoweredName[name.ToLower()] = boneIndex; - ReferenceSkeleton.FinalRefBonePose[boneIndex] = referenceSkeleton.FinalRefBonePose[newBoneIndex]; - } - BonesIndexByName = ReferenceSkeleton.FinalNameToIndexMap; UpdateBoneMatrices(transform.Matrix); } @@ -50,7 +47,7 @@ public void SetAnimation(CAnimSet anim) public void UpdateBoneMatrices(Matrix4x4 matrix) { if (!IsLoaded) return; - foreach (var boneIndex in BonesIndexByName.Values) + foreach (var boneIndex in BonesIndexByLoweredName.Values) { var bone = ReferenceSkeleton.FinalRefBonePose[boneIndex]; var parentIndex = ReferenceSkeleton.FinalRefBoneInfo[boneIndex].ParentIndex; @@ -69,22 +66,44 @@ public void UpdateBoneMatrices(Matrix4x4 matrix) parentTransform = new Transform { Relation = matrix }; boneTransform.Relation = parentTransform.Matrix; + Matrix4x4.Invert(boneTransform.Matrix, out var inverted); + BonesTransformByIndex[boneIndex] = boneTransform; + InvertedBonesMatrixByIndex[boneIndex] = inverted; + } + } + + public void SetPoseUniform(Shader shader) + { + if (!IsLoaded) return; + foreach ((var boneIndex, var transform) in BonesTransformByIndex) + { + if (boneIndex >= Constants.MAX_BONE_UNIFORM) + break; + shader.SetUniform($"uFinalBonesMatrix[{boneIndex}]", InvertedBonesMatrixByIndex[boneIndex] * transform.Matrix); } } public void SetUniform(Shader shader) { - if (!IsLoaded || Anim == null) return; - for (int boneIndex = 0; boneIndex < Anim.BoneTransforms.Length; boneIndex++) + if (!IsLoaded) return; + if (Anim == null) SetPoseUniform(shader); + else foreach ((var boneName, var trackIndex) in UnrealSkeleton.ReferenceSkeleton.FinalNameToIndexMap) { - shader.SetUniform($"uFinalBonesMatrix[{boneIndex}]", Anim.BoneTransforms[boneIndex][Anim.CurrentTime].Matrix); + if (!BonesIndexByLoweredName.TryGetValue(boneName.ToLower(), out var boneIndex)) + continue; + if (!InvertedBonesMatrixByIndex.TryGetValue(boneIndex, out var invertMatrix)) + throw new ArgumentNullException($"no inverse matrix for bone '{boneIndex}'"); + if (boneIndex >= Constants.MAX_BONE_UNIFORM) + break; + + shader.SetUniform($"uFinalBonesMatrix[{boneIndex}]", invertMatrix * Anim.BoneTransforms[trackIndex][Anim.CurrentTime].Matrix); } } public void Dispose() { - BonesIndexByName.Clear(); + BonesIndexByLoweredName.Clear(); BonesTransformByIndex.Clear(); Anim?.Dispose(); } diff --git a/FModel/Views/Snooper/Models/Model.cs b/FModel/Views/Snooper/Models/Model.cs index 2de4a53e..94f760eb 100644 --- a/FModel/Views/Snooper/Models/Model.cs +++ b/FModel/Views/Snooper/Models/Model.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Numerics; using CUE4Parse_Conversion; using CUE4Parse.UE4.Assets.Exports.Animation; @@ -21,6 +22,25 @@ namespace FModel.Views.Snooper.Models; +public class VertexAttribute +{ + public int Size; + public bool Enabled; +} + +public enum EAttribute +{ + Index, + Position, + Normals, + Tangent, + UVs, + Layer, + Colors, + BonesId, + BonesWeight +} + public class Model : IDisposable { private int _handle; @@ -32,14 +52,26 @@ public class Model : IDisposable private BufferObject _matrixVbo; private VertexArrayObject _vao; - private readonly UObject _export; - protected readonly int VertexSize = 13; // VertexIndex + Position + Normal + Tangent + UV + TextureLayer + protected int VertexSize => _vertexAttributes.Where(x => x.Enabled).Sum(x => x.Size); + protected bool HasVertexColors => _vertexAttributes[(int) EAttribute.Colors].Enabled; + private readonly List _vertexAttributes = new() + { + new VertexAttribute { Size = 1, Enabled = true }, // VertexIndex + new VertexAttribute { Size = 3, Enabled = true }, // Position + new VertexAttribute { Size = 3, Enabled = true }, // Normal + new VertexAttribute { Size = 3, Enabled = true }, // Tangent + new VertexAttribute { Size = 2, Enabled = true }, // UV + new VertexAttribute { Size = 1, Enabled = true }, // TextureLayer + new VertexAttribute { Size = 4, Enabled = false }, // Colors + new VertexAttribute { Size = 4, Enabled = false }, // BoneIds + new VertexAttribute { Size = 4, Enabled = false } // BoneWeights + }; private const int _faceSize = 3; + private readonly UObject _export; public readonly string Path; public readonly string Name; public readonly string Type; - public readonly bool HasVertexColors; public readonly int UvCount; public readonly FBox Box; public uint[] Indices; @@ -117,7 +149,7 @@ private Model(USkeletalMesh export, CSkeletalMesh skeletalMesh, Transform transf { if (sockets[i].Load() is not { } socket) continue; - if (!Skeleton.BonesIndexByName.TryGetValue(socket.BoneName.Text, out var boneIndex) || + if (!Skeleton.BonesIndexByLoweredName.TryGetValue(socket.BoneName.Text, out var boneIndex) || !Skeleton.BonesTransformByIndex.TryGetValue(boneIndex, out var boneTransform)) boneTransform = t; @@ -148,16 +180,9 @@ private Model(UObject export, IReadOnlyList materials, CBaseMesh Materials[m] = new Material(unrealMaterial); else Materials[m] = new Material(); } - if (lod.VertexColors is { Length: > 0}) - { - HasVertexColors = true; - VertexSize += 4; // + Color - } - - if (vertices is CSkelMeshVertex[]) - { - VertexSize += 8; // + BoneIds + BoneWeights - } + _vertexAttributes[(int) EAttribute.Colors].Enabled = lod.VertexColors is { Length: > 0}; + _vertexAttributes[(int) EAttribute.BonesId].Enabled = + _vertexAttributes[(int) EAttribute.BonesWeight].Enabled = vertices is CSkelMeshVertex[]; Indices = new uint[lod.Indices.Value.Length]; for (int i = 0; i < Indices.Length; i++) @@ -255,7 +280,7 @@ private void UpdateMatrices() foreach (var socket in Sockets) { if (!HasSkeleton || - !Skeleton.BonesIndexByName.TryGetValue(socket.BoneName.Text, out var boneIndex) || + !Skeleton.BonesIndexByLoweredName.TryGetValue(socket.BoneName.Text, out var boneIndex) || !Skeleton.BonesTransformByIndex.TryGetValue(boneIndex, out var boneTransform)) boneTransform = Transforms[SelectedInstance]; @@ -307,15 +332,18 @@ public void Setup(Options options) _vbo = new BufferObject(Vertices, BufferTarget.ArrayBuffer); _vao = new VertexArrayObject(_vbo, _ebo); - _vao.VertexAttributePointer(0, 1, VertexAttribPointerType.Int, VertexSize, 0); // vertex index - _vao.VertexAttributePointer(1, 3, VertexAttribPointerType.Float, VertexSize, 1); // position - _vao.VertexAttributePointer(2, 3, VertexAttribPointerType.Float, VertexSize, 4); // normal - _vao.VertexAttributePointer(3, 3, VertexAttribPointerType.Float, VertexSize, 7); // tangent - _vao.VertexAttributePointer(4, 2, VertexAttribPointerType.Float, VertexSize, 10); // uv - if (!broken) _vao.VertexAttributePointer(5, 1, VertexAttribPointerType.Float, VertexSize, 12); // texture index - _vao.VertexAttributePointer(6, 4, VertexAttribPointerType.Float, VertexSize, 13); // color - _vao.VertexAttributePointer(7, 4, VertexAttribPointerType.Float, VertexSize, 17); // boneids - _vao.VertexAttributePointer(8, 4, VertexAttribPointerType.Float, VertexSize, 21); // boneweights + var offset = 0; + for (int i = 0; i < _vertexAttributes.Count; i++) + { + var attribute = _vertexAttributes[i]; + if (!attribute.Enabled) continue; + + if (i != 5 || !broken) + { + _vao.VertexAttributePointer((uint) i, attribute.Size, i == 0 ? VertexAttribPointerType.Int : VertexAttribPointerType.Float, VertexSize, offset); + } + offset += attribute.Size; + } SetupInstances(); // instanced models transform @@ -360,6 +388,7 @@ public void Render(Shader shader, bool outline = false) _vao.Bind(); shader.SetUniform("uMorphTime", MorphTime); + if (HasSkeleton) Skeleton.SetUniform(shader); if (!outline) { shader.SetUniform("uUvCount", UvCount); diff --git a/FModel/Views/Snooper/Shading/TextureHelper.cs b/FModel/Views/Snooper/Shading/TextureHelper.cs index a3118384..e7538d9c 100644 --- a/FModel/Views/Snooper/Shading/TextureHelper.cs +++ b/FModel/Views/Snooper/Shading/TextureHelper.cs @@ -16,7 +16,11 @@ public static void FixChannels(UTexture2D o, FTexture2DMipMap mip, ref byte[] da // only if it makes a big difference pls switch (_game) { + // R: Whatever (AO / S / E / ...) + // G: Roughness + // B: Metallic case "hk_project": + case "cosmicshake": { unsafe { diff --git a/FModel/Views/Snooper/SnimGui.cs b/FModel/Views/Snooper/SnimGui.cs index cb4b90d3..fa4b2960 100644 --- a/FModel/Views/Snooper/SnimGui.cs +++ b/FModel/Views/Snooper/SnimGui.cs @@ -344,8 +344,7 @@ private void DrawOuliner(Snooper s) _saver.Value = model.TrySave(out _saver.Label, out _saver.Path); s.WindowShouldFreeze(false); } - ImGui.BeginDisabled(true); - // ImGui.BeginDisabled(!model.HasSkeleton); + ImGui.BeginDisabled(!model.HasSkeleton); if (ImGui.Selectable("Animate")) { s.Renderer.Options.AnimateMesh(true); @@ -449,7 +448,9 @@ private void DrawDetails(Snooper s) { if (model.Skeleton.Anim != null) { + ImGui.BeginDisabled(model.Skeleton.Anim.MaxTime == 0); ImGui.DragInt("Time", ref model.Skeleton.Anim.CurrentTime, 1, 0, model.Skeleton.Anim.MaxTime); + ImGui.EndDisabled(); } Layout("Skeleton");ImGui.Text($" : {model.Skeleton.UnrealSkeleton.Name}"); Layout("Bones");ImGui.Text($" : x{model.Skeleton.UnrealSkeleton.BoneTree.Length}");