diff --git a/FModel/Constants.cs b/FModel/Constants.cs index 613a6ceb..e2667514 100644 --- a/FModel/Constants.cs +++ b/FModel/Constants.cs @@ -11,7 +11,6 @@ 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 49331454..92f3a62f 100644 --- a/FModel/Resources/default.vert +++ b/FModel/Resources/default.vert @@ -15,7 +15,10 @@ layout (location = 14) in vec3 vMorphTargetTangent; uniform mat4 uView; uniform mat4 uProjection; uniform float uMorphTime; -uniform mat4 uFinalBonesMatrix[250]; +layout(std430, binding = 1) buffer layoutName +{ + mat4 uFinalBonesMatrix[]; +}; out vec3 fPos; out vec3 fNormal; diff --git a/FModel/Resources/outline.vert b/FModel/Resources/outline.vert index 1784f75e..12cab0be 100644 --- a/FModel/Resources/outline.vert +++ b/FModel/Resources/outline.vert @@ -11,7 +11,10 @@ uniform mat4 uView; uniform vec3 uViewPos; uniform mat4 uProjection; uniform float uMorphTime; -uniform mat4 uFinalBonesMatrix[250]; +layout(std430, binding = 1) buffer layoutName +{ + mat4 uFinalBonesMatrix[]; +}; void main() { diff --git a/FModel/Resources/picking.vert b/FModel/Resources/picking.vert index a1d21292..f4c40d9a 100644 --- a/FModel/Resources/picking.vert +++ b/FModel/Resources/picking.vert @@ -9,7 +9,10 @@ layout (location = 13) in vec3 vMorphTargetPos; uniform mat4 uView; uniform mat4 uProjection; uniform float uMorphTime; -uniform mat4 uFinalBonesMatrix[250]; +layout(std430, binding = 1) buffer layoutName +{ + mat4 uFinalBonesMatrix[]; +}; void main() { diff --git a/FModel/Views/Snooper/Buffers/BufferObject.cs b/FModel/Views/Snooper/Buffers/BufferObject.cs index 6559ccf5..59fc6e03 100644 --- a/FModel/Views/Snooper/Buffers/BufferObject.cs +++ b/FModel/Views/Snooper/Buffers/BufferObject.cs @@ -36,6 +36,13 @@ public class BufferObject : IDisposable where TDataType : unmanaged GL.BindBuffer(_bufferTarget, _handle); } + public void BindBufferBase(int index) + { + if (_bufferTarget != BufferTarget.ShaderStorageBuffer) + throw new ArgumentException("BindBufferBase is not allowed for anything but Shader Storage Buffers"); + GL.BindBufferBase(BufferRangeTarget.ShaderStorageBuffer, index, _handle); + } + public void Unbind() { GL.BindBuffer(_bufferTarget, 0); diff --git a/FModel/Views/Snooper/Models/Animations/Skeleton.cs b/FModel/Views/Snooper/Models/Animations/Skeleton.cs index 754c549f..b72a24af 100644 --- a/FModel/Views/Snooper/Models/Animations/Skeleton.cs +++ b/FModel/Views/Snooper/Models/Animations/Skeleton.cs @@ -1,10 +1,13 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Numerics; using CUE4Parse_Conversion.Animations; using CUE4Parse.UE4.Assets.Exports.Animation; using CUE4Parse.UE4.Objects.UObject; +using FModel.Views.Snooper.Buffers; using FModel.Views.Snooper.Shading; +using OpenTK.Graphics.OpenGL4; using Serilog; namespace FModel.Views.Snooper.Models.Animations; @@ -17,12 +20,15 @@ public struct BoneIndice public class Skeleton : IDisposable { + private int _handle; + private BufferObject _ssbo; + public readonly USkeleton UnrealSkeleton; public readonly bool IsLoaded; public readonly Dictionary BonesIndicesByLoweredName; public readonly Dictionary BonesTransformByIndex; - public readonly Dictionary InvertedBonesMatrixByIndex; + public readonly Matrix4x4[] InvertedBonesMatrixByIndex; public Animation Anim; public bool HasAnim => Anim != null; @@ -31,7 +37,7 @@ public class Skeleton : IDisposable { BonesIndicesByLoweredName = new Dictionary(); BonesTransformByIndex = new Dictionary(); - InvertedBonesMatrixByIndex = new Dictionary(); + InvertedBonesMatrixByIndex = Array.Empty(); } public Skeleton(FPackageIndex package, FReferenceSkeleton referenceSkeleton) : this() @@ -55,6 +61,7 @@ public class Skeleton : IDisposable } #endif + InvertedBonesMatrixByIndex = new Matrix4x4[BonesIndicesByLoweredName.Count]; foreach (var boneIndices in BonesIndicesByLoweredName.Values) { var bone = referenceSkeleton.FinalRefBonePose[boneIndices.Index]; @@ -84,16 +91,23 @@ public class Skeleton : IDisposable Anim = new Animation(this, anim, rotationOnly); } - public void SetUniform(Shader shader, float deltaSeconds = 0f, bool update = false) + public void Setup() + { + _handle = GL.CreateProgram(); + _ssbo = new BufferObject(InvertedBonesMatrixByIndex, BufferTarget.ShaderStorageBuffer); + } + + public void Render(float deltaSeconds = 0f, bool update = false) { if (!IsLoaded) return; + + _ssbo.BindBufferBase(1); + if (!HasAnim) { foreach (var boneIndex in BonesTransformByIndex.Keys) { - if (boneIndex >= Constants.MAX_BONE_UNIFORM) - break; - shader.SetUniform($"uFinalBonesMatrix[{boneIndex}]", Matrix4x4.Identity); + _ssbo.Update(boneIndex, Matrix4x4.Identity); } } else @@ -101,22 +115,20 @@ public class Skeleton : IDisposable if (update) Anim.Update(deltaSeconds); foreach (var boneIndex in BonesTransformByIndex.Keys) { - if (boneIndex >= Constants.MAX_BONE_UNIFORM) - break; - if (!InvertedBonesMatrixByIndex.TryGetValue(boneIndex, out var invertMatrix)) - throw new ArgumentNullException($"no inverse matrix for bone '{boneIndex}'"); - - shader.SetUniform($"uFinalBonesMatrix[{boneIndex}]", invertMatrix * Anim.InterpolateBoneTransform(boneIndex)); + _ssbo.Update(boneIndex, InvertedBonesMatrixByIndex[boneIndex] * Anim.InterpolateBoneTransform(boneIndex)); } if (update) Anim.CheckForNextSequence(); } + + _ssbo.Unbind(); } public void Dispose() { BonesIndicesByLoweredName.Clear(); BonesTransformByIndex.Clear(); - InvertedBonesMatrixByIndex.Clear(); Anim?.Dispose(); + + GL.DeleteProgram(_handle); } } diff --git a/FModel/Views/Snooper/Models/Model.cs b/FModel/Views/Snooper/Models/Model.cs index 3ee5caa6..44bef470 100644 --- a/FModel/Views/Snooper/Models/Model.cs +++ b/FModel/Views/Snooper/Models/Model.cs @@ -348,6 +348,7 @@ public class Model : IDisposable Materials[i].Setup(options, broken ? 1 : UvCount); } + if (HasSkeleton) Skeleton.Setup(); if (HasMorphTargets) { for (uint morph = 0; morph < Morphs.Length; morph++) @@ -382,7 +383,7 @@ public class Model : IDisposable _vao.Bind(); shader.SetUniform("uMorphTime", MorphTime); - if (HasSkeleton) Skeleton.SetUniform(shader, deltaSeconds, !outline); + if (HasSkeleton) Skeleton.Render(deltaSeconds, !outline); if (!outline) { shader.SetUniform("uUvCount", UvCount); @@ -413,7 +414,7 @@ public class Model : IDisposable _vao.Bind(); shader.SetUniform("uMorphTime", MorphTime); - if (HasSkeleton) Skeleton.SetUniform(shader); + if (HasSkeleton) Skeleton.Render(); foreach (var section in Sections) { diff --git a/FModel/Views/Snooper/SnimGui.cs b/FModel/Views/Snooper/SnimGui.cs index 6b471a46..5be9e080 100644 --- a/FModel/Views/Snooper/SnimGui.cs +++ b/FModel/Views/Snooper/SnimGui.cs @@ -452,7 +452,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio if (model.HasSkeleton) { Layout("Skeleton");ImGui.Text($" : {model.Skeleton.UnrealSkeleton.Name}"); - Layout("Bones");ImGui.Text($" : x{model.Skeleton.InvertedBonesMatrixByIndex.Count}"); + Layout("Bones");ImGui.Text($" : x{model.Skeleton.InvertedBonesMatrixByIndex.Length}"); } else {