diff --git a/FModel/Resources/default.vert b/FModel/Resources/default.vert index 28918dd4..29d22213 100644 --- a/FModel/Resources/default.vert +++ b/FModel/Resources/default.vert @@ -4,6 +4,8 @@ layout (location = 0) in vec3 vPos; layout (location = 1) in vec3 vNormal; layout (location = 2) in vec2 vTexCoords; layout (location = 3) in vec4 vColor; +layout (location = 4) in ivec4 vBoneIds; +layout (location = 5) in vec4 vWeights; uniform mat4 uModel; uniform mat4 uView; diff --git a/FModel/Views/Snooper/Camera.cs b/FModel/Views/Snooper/Camera.cs index 241f39b8..f00d84d6 100644 --- a/FModel/Views/Snooper/Camera.cs +++ b/FModel/Views/Snooper/Camera.cs @@ -12,7 +12,7 @@ public class Camera public float Yaw { get; set; } = -90f; public float Pitch { get; set; } = 0f; public float Zoom { get; set; } = 45f; - public float Speed { get; } = 1f; + public float Speed { get; set; } = 1f; public float Near { get; } = 0.01f; public float Far { get; } = 100f; public float AspectRatio => 16f / 9f; diff --git a/FModel/Views/Snooper/Cube.cs b/FModel/Views/Snooper/Cube.cs new file mode 100644 index 00000000..96af4f37 --- /dev/null +++ b/FModel/Views/Snooper/Cube.cs @@ -0,0 +1,67 @@ +using CUE4Parse.UE4.Assets.Exports.Material; + +namespace FModel.Views.Snooper; + +public class Cube : Model +{ + public Cube(string name, string type, UMaterialInterface unrealMaterial) : base(name, type) + { + Indices = new uint[] + { + 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, + 32, 33, 34, 35 + }; + Vertices = new float[] { + //X Y Z Normals U V + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f + }; + + Sections = new Section[1]; + Sections[0] = new Section(unrealMaterial.Name, 0, (uint) Indices.Length, 0, unrealMaterial); + } +} diff --git a/FModel/Views/Snooper/Model.cs b/FModel/Views/Snooper/Model.cs index 8c432545..fd7e3761 100644 --- a/FModel/Views/Snooper/Model.cs +++ b/FModel/Views/Snooper/Model.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using CUE4Parse_Conversion.Meshes.PSK; using Silk.NET.OpenGL; @@ -21,10 +22,13 @@ public class Model : IDisposable private readonly uint[] _facesIndex = { 1, 0, 2 }; public readonly string Name; + public readonly string Type; public readonly bool HasVertexColors; - public readonly uint[] Indices; - public readonly float[] Vertices; - public readonly Section[] Sections; + public readonly bool HasBones; + public uint[] Indices; + public float[] Vertices; + public Section[] Sections; + public readonly List Skeleton; public readonly Transform Transforms = Transform.Identity; public readonly string[] TransformsLabels = { @@ -33,13 +37,23 @@ public class Model : IDisposable "X Scale", "Y", "Z" }; public bool DisplayVertexColors; + public bool DisplayBones; - public Model(string name, CBaseMeshLod lod, CMeshVertex[] vertices) + protected Model(string name, string type) { Name = name; + Type = type; + } + + public Model(string name, string type, CBaseMeshLod lod, CMeshVertex[] vertices, List skeleton = null) : this(name, type) + { HasVertexColors = lod.VertexColors != null; if (HasVertexColors) _vertexSize += 4; // + Color + Skeleton = skeleton; + HasBones = Skeleton != null; + if (HasBones) _vertexSize += 8; // + BoneIds + BoneWeights + var sections = lod.Sections.Value; Sections = new Section[sections.Length]; Indices = new uint[sections.Sum(section => section.NumFaces * _faceSize)]; @@ -53,27 +67,43 @@ public class Model : IDisposable { foreach (var f in _facesIndex) { + var count = 0; var i = face * _faceSize + f; var index = section.FirstIndex + i; var indice = lod.Indices.Value[index]; var vert = vertices[indice]; - Vertices[index * _vertexSize] = vert.Position.X * Constants.SCALE_DOWN_RATIO; - Vertices[index * _vertexSize + 1] = vert.Position.Z * Constants.SCALE_DOWN_RATIO; - Vertices[index * _vertexSize + 2] = vert.Position.Y * Constants.SCALE_DOWN_RATIO; - Vertices[index * _vertexSize + 3] = vert.Normal.X; - Vertices[index * _vertexSize + 4] = vert.Normal.Z; - Vertices[index * _vertexSize + 5] = vert.Normal.Y; - Vertices[index * _vertexSize + 6] = vert.UV.U; - Vertices[index * _vertexSize + 7] = vert.UV.V; + Vertices[index * _vertexSize + count++] = vert.Position.X * Constants.SCALE_DOWN_RATIO; + Vertices[index * _vertexSize + count++] = vert.Position.Z * Constants.SCALE_DOWN_RATIO; + Vertices[index * _vertexSize + count++] = vert.Position.Y * Constants.SCALE_DOWN_RATIO; + Vertices[index * _vertexSize + count++] = vert.Normal.X; + Vertices[index * _vertexSize + count++] = vert.Normal.Z; + Vertices[index * _vertexSize + count++] = vert.Normal.Y; + Vertices[index * _vertexSize + count++] = vert.UV.U; + Vertices[index * _vertexSize + count++] = vert.UV.V; + if (HasVertexColors) { var color = lod.VertexColors[indice]; - Vertices[index * _vertexSize + 8] = color.R; - Vertices[index * _vertexSize + 9] = color.G; - Vertices[index * _vertexSize + 10] = color.B; - Vertices[index * _vertexSize + 11] = color.A; + Vertices[index * _vertexSize + count++] = color.R; + Vertices[index * _vertexSize + count++] = color.G; + Vertices[index * _vertexSize + count++] = color.B; + Vertices[index * _vertexSize + count++] = color.A; + } + + if (HasBones) + { + var skelVert = (CSkelMeshVertex) vert; + var weightsHash = skelVert.UnpackWeights(); + Vertices[index * _vertexSize + count++] = skelVert.Bone[0]; + Vertices[index * _vertexSize + count++] = skelVert.Bone[1]; + Vertices[index * _vertexSize + count++] = skelVert.Bone[2]; + Vertices[index * _vertexSize + count++] = skelVert.Bone[3]; + Vertices[index * _vertexSize + count++] = weightsHash[0]; + Vertices[index * _vertexSize + count++] = weightsHash[1]; + Vertices[index * _vertexSize + count++] = weightsHash[2]; + Vertices[index * _vertexSize + count++] = weightsHash[3]; } Indices[index] = i; @@ -98,6 +128,8 @@ public class Model : IDisposable _vao.VertexAttributePointer(1, 3, VertexAttribPointerType.Float, _vertexSize, 3); // normal _vao.VertexAttributePointer(2, 2, VertexAttribPointerType.Float, _vertexSize, 6); // uv _vao.VertexAttributePointer(3, 4, VertexAttribPointerType.Float, _vertexSize, 8); // color + _vao.VertexAttributePointer(4, 4, VertexAttribPointerType.Int, _vertexSize, 12); // boneids + _vao.VertexAttributePointer(5, 4, VertexAttribPointerType.Float, _vertexSize, 16); // boneweights for (int section = 0; section < Sections.Length; section++) { diff --git a/FModel/Views/Snooper/Section.cs b/FModel/Views/Snooper/Section.cs index 062c0195..40aa3b6f 100644 --- a/FModel/Views/Snooper/Section.cs +++ b/FModel/Views/Snooper/Section.cs @@ -37,18 +37,13 @@ public class Section : IDisposable public bool HasSpecularMap; public bool HasDiffuseColor; - public Section(string name, int index, uint facesCount, int firstFaceIndex, CMeshSection section) + private Section(string name, int index, uint facesCount, int firstFaceIndex) { Name = name; Index = index; FacesCount = facesCount; FirstFaceIndex = firstFaceIndex; Parameters = new CMaterialParams(); - if (section.Material != null && section.Material.TryLoad(out var material) && material is UMaterialInterface unrealMaterial) - { - Name = unrealMaterial.Name; - unrealMaterial.GetParams(Parameters); - } Show = true; Textures = new Texture[4]; @@ -59,6 +54,20 @@ public class Section : IDisposable _game = ApplicationService.ApplicationView.CUE4Parse.Game; } + public Section(string name, int index, uint facesCount, int firstFaceIndex, CMeshSection section) : this(name, index, facesCount, firstFaceIndex) + { + if (section.Material != null && section.Material.TryLoad(out var material) && material is UMaterialInterface unrealMaterial) + { + Name = unrealMaterial.Name; + unrealMaterial.GetParams(Parameters); + } + } + + public Section(string name, int index, uint facesCount, int firstFaceIndex, UMaterialInterface unrealMaterial) : this(name, index, facesCount, firstFaceIndex) + { + unrealMaterial.GetParams(Parameters); + } + public void Setup(GL gl) { _gl = gl; diff --git a/FModel/Views/Snooper/SnimGui.cs b/FModel/Views/Snooper/SnimGui.cs index 9228447a..952bf904 100644 --- a/FModel/Views/Snooper/SnimGui.cs +++ b/FModel/Views/Snooper/SnimGui.cs @@ -16,6 +16,8 @@ public class SnimGui : IDisposable { private readonly ImGuiController _controller; private readonly GraphicsAPI _api; + private readonly string _renderer; + private readonly string _version; private readonly Vector2 _outlinerSize; private readonly Vector2 _outlinerPosition; @@ -38,6 +40,8 @@ public class SnimGui : IDisposable var fontConfig = new ImGuiFontConfig("C:\\Windows\\Fonts\\segoeui.ttf", 16); _controller = new ImGuiController(gl, window, input, fontConfig); _api = window.API; + _renderer = gl.GetStringS(StringName.Renderer); + _version = gl.GetStringS(StringName.Version); var style = ImGui.GetStyle(); var viewport = ImGui.GetMainViewport(); @@ -121,10 +125,9 @@ public class SnimGui : IDisposable ImGui.SetNextWindowPos(_outlinerPosition, _firstUse); ImGui.Begin("Scene", _noResize | ImGuiWindowFlags.NoCollapse); - ImGui.Text($"{_api.API} {_api.Profile} {_api.Version.MajorVersion}.{_api.Version.MinorVersion}"); - - var vertices = 0; - var indices = 0; + ImGui.Text($"Platform: {_api.API} {_api.Profile} {_api.Version.MajorVersion}.{_api.Version.MinorVersion}"); + ImGui.Text($"Renderer: {_renderer}"); + ImGui.Text($"Version: {_version}"); ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); if (ImGui.TreeNode("Collection")) @@ -132,8 +135,6 @@ public class SnimGui : IDisposable for (var i = 0; i < models.Count; i++) { var model = models[i]; - vertices += model.Vertices.Length; - indices += model.Indices.Length; ImGui.PushID(i); if (ImGui.Selectable(model.Name, _selectedModel == i)) _selectedModel = i; @@ -162,9 +163,6 @@ public class SnimGui : IDisposable ImGui.TreePop(); } - ImGui.Text($"Vertices: {vertices}"); - ImGui.Text($"Indices: {indices}"); - ImGui.End(); } @@ -176,10 +174,14 @@ public class SnimGui : IDisposable if (_selectedModel < 0) return; var model = models[_selectedModel]; + ImGui.Text($"Type: {model.Type}"); ImGui.Text($"Entity: {model.Name}"); ImGui.BeginDisabled(!model.HasVertexColors); ImGui.Checkbox("Vertex Colors", ref model.DisplayVertexColors); ImGui.EndDisabled(); + ImGui.BeginDisabled(!model.HasBones); + ImGui.Checkbox("Bones", ref model.DisplayBones); + ImGui.EndDisabled(); if (ImGui.TreeNode("Transform")) { @@ -291,7 +293,7 @@ public class SnimGui : IDisposable if (ImGui.IsItemHovered()) { ImGui.BeginTooltip(); - ImGui.Text($"Type: ({texture.Format}) {texture.Type} \"{texture.Name}\""); + ImGui.Text($"Type: ({texture.Format}) {texture.Type}:{texture.Name}"); ImGui.Text($"Texture: {texture.Path}"); ImGui.Text($"Imported: {texture.ImportedWidth}x{texture.ImportedHeight}"); ImGui.Text($"Mip Used: {texture.Width}x{texture.Height}"); diff --git a/FModel/Views/Snooper/Snooper.cs b/FModel/Views/Snooper/Snooper.cs index 9ce2ce96..b69cd161 100644 --- a/FModel/Views/Snooper/Snooper.cs +++ b/FModel/Views/Snooper/Snooper.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.InteropServices; using System.Windows; using CUE4Parse.UE4.Assets.Exports; +using CUE4Parse.UE4.Assets.Exports.Material; using CUE4Parse.UE4.Assets.Exports.SkeletalMesh; using CUE4Parse.UE4.Assets.Exports.StaticMesh; using CUE4Parse.UE4.Objects.Core.Math; @@ -84,25 +85,32 @@ public class Snooper public void Run(UObject export) { - _append = false; switch (export) { case UStaticMesh st when st.TryConvert(out var mesh): { - _models.Add(new Model(st.Name, mesh.LODs[0], mesh.LODs[0].Verts)); + _models.Add(new Model(st.Name, st.ExportType, mesh.LODs[0], mesh.LODs[0].Verts)); SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO); break; } case USkeletalMesh sk when sk.TryConvert(out var mesh): { - _models.Add(new Model(sk.Name, mesh.LODs[0], mesh.LODs[0].Verts)); + _models.Add(new Model(sk.Name, sk.ExportType, mesh.LODs[0], mesh.LODs[0].Verts, mesh.RefSkeleton)); SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO); break; } + case UMaterialInstance mi: + { + _models.Add(new Cube(mi.Name, mi.ExportType, mi)); + _camera = new Camera(new Vector3(0f, 0f, 2f), Vector3.Zero, 0.01f, 0.5f * 50f, 0.5f / 2f); + break; + } default: throw new ArgumentOutOfRangeException(nameof(export)); } + // because this calls Reset from an internal class we must recall GL.GetApi(_window) on load + // so basically we can't keep our current scene and we have to setup everything again _window.Run(); } @@ -112,11 +120,11 @@ public class Snooper var center = box.GetCenter(); var position = new Vector3(0f, center.Z, box.Max.Y * 3); var speed = far / 2f; - if (speed > _previousSpeed) - { + + if (_camera == null) _camera = new Camera(position, center, 0.01f, far * 50f, speed); - _previousSpeed = _camera.Speed; - } + else if (speed > _previousSpeed) + _camera.Speed = _previousSpeed = speed; } private void OnLoad() @@ -124,29 +132,22 @@ public class Snooper _window.SetWindowIcon(ref _icon); _window.Center(); - if (_append) + var input = _window.CreateInput(); + _keyboard = input.Keyboards[0]; + _mouse = input.Mice[0]; + + _gl = GL.GetApi(_window); + _gl.Enable(EnableCap.Multisample); + + _imGui = new SnimGui(_gl, _window, input); + + _framebuffer.Setup(_gl); + _skybox.Setup(_gl); + _grid.Setup(_gl); + + foreach (var model in _models) { - _models[^1].Setup(_gl); - } - else - { - var input = _window.CreateInput(); - _keyboard = input.Keyboards[0]; - _mouse = input.Mice[0]; - - _gl = GL.GetApi(_window); - _gl.Enable(EnableCap.Multisample); - - _imGui = new SnimGui(_gl, _window, input); - - _framebuffer.Setup(_gl); - _skybox.Setup(_gl); - _grid.Setup(_gl); - - foreach (var model in _models) - { - model.Setup(_gl); - } + model.Setup(_gl); } } @@ -219,8 +220,6 @@ public class Snooper private void OnClose() { - if (_append) // don't dispose anything, especially _models - return; _framebuffer.Dispose(); _grid.Dispose(); _skybox.Dispose(); @@ -228,8 +227,11 @@ public class Snooper { model.Dispose(); } - _models.Clear(); - _previousSpeed = 0f; + if (!_append) + { + _models.Clear(); + _previousSpeed = 0f; + } _imGui.Dispose(); _window.Dispose(); _gl.Dispose(); diff --git a/FModel/Views/Snooper/VertexArrayObject.cs b/FModel/Views/Snooper/VertexArrayObject.cs index 17068915..e4fc99dd 100644 --- a/FModel/Views/Snooper/VertexArrayObject.cs +++ b/FModel/Views/Snooper/VertexArrayObject.cs @@ -20,7 +20,15 @@ public class VertexArrayObject : IDisposable where TVer public unsafe void VertexAttributePointer(uint index, int count, VertexAttribPointerType type, uint vertexSize, int offSet) { - _gl.VertexAttribPointer(index, count, type, false, vertexSize * (uint) sizeof(TVertexType), (void*) (offSet * sizeof(TVertexType))); + switch (type) + { + case VertexAttribPointerType.Int: + _gl.VertexAttribIPointer(index, count, VertexAttribIType.Int, vertexSize * (uint) sizeof(TVertexType), (void*) (offSet * sizeof(TVertexType))); + break; + default: + _gl.VertexAttribPointer(index, count, type, false, vertexSize * (uint) sizeof(TVertexType), (void*) (offSet * sizeof(TVertexType))); + break; + } _gl.EnableVertexAttribArray(index); }