diff --git a/FModel/Extensions/ImGuiExtensions.cs b/FModel/Extensions/ImGuiExtensions.cs index 88f749d6..0b293077 100644 --- a/FModel/Extensions/ImGuiExtensions.cs +++ b/FModel/Extensions/ImGuiExtensions.cs @@ -8,6 +8,29 @@ public static class ImGuiExtensions public const float PADDING = 5.0f; public static ImGuiStylePtr STYLE = ImGui.GetStyle(); + public static void DrawNavbar() + { + if (ImGui.BeginMainMenuBar()) + { + if (ImGui.BeginMenu("Edit")) + { + if (ImGui.MenuItem("Undo", "CTRL+Z")) {} + if (ImGui.MenuItem("Redo", "CTRL+Y", false, false)) {} // Disabled item + ImGui.Separator(); + if (ImGui.MenuItem("Cut", "CTRL+X")) {} + if (ImGui.MenuItem("Copy", "CTRL+C")) {} + if (ImGui.MenuItem("Paste", "CTRL+V")) {} + ImGui.EndMenu(); + } + + const string text = "Press ESC to Exit..."; + ImGui.SetCursorPosX(ImGui.GetWindowViewport().WorkSize.X - ImGui.CalcTextSize(text).X - 5); + ImGui.TextColored(new Vector4(0.36f, 0.42f, 0.47f, 1.00f), text); // ImGuiCol.TextDisabled + + ImGui.EndMainMenuBar(); + } + } + public static void DrawFPS() { const ImGuiWindowFlags flags = diff --git a/FModel/FModel.csproj b/FModel/FModel.csproj index c11e65eb..fd4883d0 100644 --- a/FModel/FModel.csproj +++ b/FModel/FModel.csproj @@ -137,7 +137,7 @@ - + diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index 563c3b27..cb5099c6 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -79,7 +79,7 @@ public partial class MainWindow #if DEBUG await _threadWorkerView.Begin(_ => _applicationView.CUE4Parse.Extract( - "FortniteGame/Content/Environments/Props/Winter/Meshes/SM_ChristmasTree_Llama.uasset")); + "/Game/Characters/Player/Female/Medium/Bodies/F_MED_Neon_Cat_Tech/Meshes/F_MED_Neon_Cat_Tech.uasset")); #endif } diff --git a/FModel/Resources/shader.frag b/FModel/Resources/shader.frag index be50fdcf..1fa18d57 100644 --- a/FModel/Resources/shader.frag +++ b/FModel/Resources/shader.frag @@ -5,12 +5,16 @@ in vec3 fNormal; in vec2 fTexCoords; struct Material { - sampler2D diffuse; - sampler2D normal; - sampler2D specular; - sampler2D metallic; - sampler2D emission; + sampler2D diffuseMap; + sampler2D normalMap; + sampler2D specularMap; + sampler2D metallicMap; + sampler2D emissionMap; + bool swap; + + vec4 diffuseColor; + vec4 emissionColor; float shininess; }; @@ -29,24 +33,34 @@ out vec4 FragColor; void main() { + if (material.swap) + { + FragColor = material.diffuseColor; + return; + } + // ambient - vec3 ambient = light.ambient * vec3(texture(material.diffuse, fTexCoords)); + vec3 ambient = light.ambient * vec3(texture(material.diffuseMap, fTexCoords)); // diffuse - vec3 norm = texture(material.normal, fTexCoords).rgb; + vec3 norm = texture(material.normalMap, fTexCoords).rgb; norm = normalize(norm * 2.0 - 1.0); vec3 lightDir = normalize(light.position - fPos); float diff = max(dot(norm, lightDir), 0.0f); - vec3 diffuseMap = vec3(texture(material.diffuse, fTexCoords)); + vec3 diffuseMap = vec3(texture(material.diffuseMap, fTexCoords)); vec3 diffuse = light.diffuse * diff * diffuseMap; // specular vec3 viewDir = normalize(viewPos - fPos); vec3 reflectDir = reflect(-lightDir, norm); float spec = pow(max(dot(viewDir, reflectDir), 0.0f), material.shininess); - vec3 specularMap = vec3(texture(material.specular, fTexCoords)); + vec3 specularMap = vec3(texture(material.specularMap, fTexCoords)); vec3 specular = light.specular * spec * specularMap; - vec3 result = ambient + diffuse + specular; + // emission + vec3 emissionMap = vec3(texture(material.emissionMap, fTexCoords)); + vec3 emission = material.emissionColor.rgb * emissionMap; + + vec3 result = ambient + diffuse + specular + emission; FragColor = vec4(result, 1.0); } diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 4b9ac9a6..e413dc4e 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -753,8 +753,8 @@ public class CUE4ParseViewModel : ViewModel export.Owner.Name.EndsWith($"/RenderSwitch_Materials/{export.Name}", StringComparison.OrdinalIgnoreCase) || export.Owner.Name.EndsWith($"/MI_BPTile/{export.Name}", StringComparison.OrdinalIgnoreCase))): { - var snooper = new Snooper(export); - snooper.Run(); + var snooper = new Snooper(); + snooper.Run(export); return true; } case UMaterialInstance m when ModelIsOverwritingMaterial: diff --git a/FModel/Views/Snooper/Model.cs b/FModel/Views/Snooper/Model.cs index ffa32773..8a032683 100644 --- a/FModel/Views/Snooper/Model.cs +++ b/FModel/Views/Snooper/Model.cs @@ -20,8 +20,6 @@ public class Model : IDisposable private const uint _faceSize = 3; // just so we don't have to do .Length private readonly uint[] _facesIndex = { 1, 0, 2 }; - private Shader _shader; - public readonly string Name; public readonly uint[] Indices; public readonly float[] Vertices; @@ -70,8 +68,6 @@ public class Model : IDisposable _handle = _gl.CreateProgram(); - _shader = new Shader(_gl); - _ebo = new BufferObject(_gl, Indices, BufferTargetARB.ElementArrayBuffer); _vbo = new BufferObject(_gl, Vertices, BufferTargetARB.ArrayBuffer); _vao = new VertexArrayObject(_gl, _vbo, _ebo); @@ -92,36 +88,13 @@ public class Model : IDisposable _vao.Bind(); - _shader.Use(); - - _shader.SetUniform("uModel", Matrix4x4.Identity); - _shader.SetUniform("uView", camera.GetViewMatrix()); - _shader.SetUniform("uProjection", camera.GetProjectionMatrix()); - _shader.SetUniform("viewPos", camera.Position); - - _shader.SetUniform("material.diffuse", 0); - _shader.SetUniform("material.normal", 1); - _shader.SetUniform("material.specular", 2); - // _shader.SetUniform("material.metallic", 3); - // _shader.SetUniform("material.emission", 4); - _shader.SetUniform("material.shininess", 32f); - - var lightColor = Vector3.One; - var diffuseColor = lightColor * new Vector3(0.5f); - var ambientColor = diffuseColor * new Vector3(0.2f); - - _shader.SetUniform("light.ambient", ambientColor); - _shader.SetUniform("light.diffuse", diffuseColor); // darkened - _shader.SetUniform("light.specular", Vector3.One); - _shader.SetUniform("light.position", camera.Position); - ImGui.BeginTable("Sections", 2, ImGuiTableFlags.RowBg); ImGui.TableSetupColumn("Index", ImGuiTableColumnFlags.WidthFixed); ImGui.TableSetupColumn("Material", ImGuiTableColumnFlags.WidthStretch); ImGui.TableHeadersRow(); for (int section = 0; section < Sections.Length; section++) { - Sections[section].Bind(Indices.Length); + Sections[section].Bind(camera, Indices.Length); _gl.DrawArrays(PrimitiveType.Triangles, Sections[section].FirstFaceIndex, Sections[section].FacesCount); } ImGui.EndTable(); @@ -134,7 +107,6 @@ public class Model : IDisposable _ebo.Dispose(); _vbo.Dispose(); _vao.Dispose(); - _shader.Dispose(); for (int section = 0; section < Sections.Length; section++) { Sections[section].Dispose(); diff --git a/FModel/Views/Snooper/Section.cs b/FModel/Views/Snooper/Section.cs index 57991d88..a1097bf7 100644 --- a/FModel/Views/Snooper/Section.cs +++ b/FModel/Views/Snooper/Section.cs @@ -1,4 +1,5 @@ using System; +using System.Numerics; using CUE4Parse.UE4.Assets.Exports.Material; using CUE4Parse.UE4.Assets.Exports.Texture; using CUE4Parse_Conversion.Meshes.PSK; @@ -20,6 +21,11 @@ public class Section : IDisposable // private Texture _metallicMap; private Texture _emissionMap; + private Vector4 _diffuseColor; + private Vector4 _emissionColor; + + private Shader _shader; + public readonly string Name; public readonly int Index; public readonly uint FacesCount; @@ -46,8 +52,20 @@ public class Section : IDisposable _handle = _gl.CreateProgram(); + _shader = new Shader(_gl); + + if (Parameters.IsNull) + { + _diffuseColor = new Vector4(1, 0, 0, 1); + return; + } + var platform = UserSettings.Default.OverridedPlatform; - if (Parameters.Diffuse is UTexture2D { IsVirtual: false } diffuse) + if (!Parameters.HasTopDiffuseTexture && Parameters.DiffuseColor is { A: > 0 } diffuseColor) + { + _diffuseColor = new Vector4(diffuseColor.R, diffuseColor.G, diffuseColor.B, diffuseColor.A); + } + else if (Parameters.Diffuse is UTexture2D { IsVirtual: false } diffuse) { var mip = diffuse.GetFirstMip(); TextureDecoder.DecodeTexture(mip, diffuse.Format, diffuse.isNormalMap, platform, out var data, out _); @@ -75,10 +93,11 @@ public class Section : IDisposable var mip = emissive.GetFirstMip(); TextureDecoder.DecodeTexture(mip, emissive.Format, emissive.isNormalMap, platform, out var data, out _); _emissionMap = new Texture(_gl, data, (uint) mip.SizeX, (uint) mip.SizeY); + _emissionColor = new Vector4(emissiveColor.R, emissiveColor.G, emissiveColor.B, emissiveColor.A); } } - public void Bind(float indices) + public void Bind(Camera camera, float indices) { ImGui.TableNextRow(); @@ -97,15 +116,42 @@ public class Section : IDisposable ImGui.EndTooltip(); } - if (Parameters.IsNull) return; + _shader.Use(); + + _shader.SetUniform("uModel", Matrix4x4.Identity); + _shader.SetUniform("uView", camera.GetViewMatrix()); + _shader.SetUniform("uProjection", camera.GetProjectionMatrix()); + _shader.SetUniform("viewPos", camera.Position); + + _shader.SetUniform("material.diffuseMap", 0); + _shader.SetUniform("material.normalMap", 1); + _shader.SetUniform("material.specularMap", 2); + // _shader.SetUniform("material.metallicMap", 3); + _shader.SetUniform("material.emissionMap", 4); + _shader.SetUniform("material.shininess", 32f); + + _shader.SetUniform("material.swap", Convert.ToUInt32(_diffuseColor != Vector4.Zero)); + _shader.SetUniform("material.diffuseColor", _diffuseColor); + _shader.SetUniform("material.emissionColor", _emissionColor); + _diffuseMap?.Bind(TextureUnit.Texture0); _normalMap?.Bind(TextureUnit.Texture1); _specularMap?.Bind(TextureUnit.Texture2); _emissionMap?.Bind(TextureUnit.Texture4); + + var lightColor = Vector3.One; + var diffuseColor = lightColor * new Vector3(0.5f); + var ambientColor = diffuseColor * new Vector3(0.2f); + + _shader.SetUniform("light.ambient", ambientColor); + _shader.SetUniform("light.diffuse", diffuseColor); // darkened + _shader.SetUniform("light.specular", Vector3.One); + _shader.SetUniform("light.position", camera.Position); } public void Dispose() { + _shader.Dispose(); _diffuseMap?.Dispose(); _normalMap?.Dispose(); _specularMap?.Dispose(); diff --git a/FModel/Views/Snooper/Shader.cs b/FModel/Views/Snooper/Shader.cs index 879b9eec..5ac485a2 100644 --- a/FModel/Views/Snooper/Shader.cs +++ b/FModel/Views/Snooper/Shader.cs @@ -61,6 +61,16 @@ public class Shader : IDisposable _gl.UniformMatrix4(location, 1, false, (float*) &value); } + public void SetUniform(string name, uint value) + { + int location = _gl.GetUniformLocation(_handle, name); + if (location == -1) + { + throw new Exception($"{name} uniform not found on shader."); + } + _gl.Uniform1(location, value); + } + public void SetUniform(string name, float value) { int location = _gl.GetUniformLocation(_handle, name); @@ -81,6 +91,16 @@ public class Shader : IDisposable _gl.Uniform3(location, value.X, value.Y, value.Z); } + public void SetUniform(string name, Vector4 value) + { + int location = _gl.GetUniformLocation(_handle, name); + if (location == -1) + { + throw new Exception($"{name} uniform not found on shader."); + } + _gl.Uniform4(location, value.X, value.Y, value.Z, value.W); + } + public void Dispose() { _gl.DeleteProgram(_handle); diff --git a/FModel/Views/Snooper/Snooper.cs b/FModel/Views/Snooper/Snooper.cs index 6c4bd389..f9646acb 100644 --- a/FModel/Views/Snooper/Snooper.cs +++ b/FModel/Views/Snooper/Snooper.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Numerics; using System.Runtime.InteropServices; using System.Windows; @@ -32,23 +33,23 @@ public class Snooper private Vector2 _previousMousePosition; private RawImage _icon; - private Skybox _skybox; - private Grid _grid; - private Model[] _models; + private readonly Skybox _skybox; + private readonly Grid _grid; + private readonly List _models; - public int Width { get; } - public int Height { get; } + private readonly int _width; + private readonly int _height; - public Snooper(UObject export) + public Snooper() { const double ratio = .7; var x = SystemParameters.MaximizedPrimaryScreenWidth; var y = SystemParameters.MaximizedPrimaryScreenHeight; - Width = Convert.ToInt32(x * ratio); - Height = Convert.ToInt32(y * ratio); + _width = Convert.ToInt32(x * ratio); + _height = Convert.ToInt32(y * ratio); var options = WindowOptions.Default; - options.Size = new Vector2D(Width, Height); + options.Size = new Vector2D(_width, _height); options.WindowBorder = WindowBorder.Hidden; options.Title = "Snooper"; options.Samples = 4; @@ -77,28 +78,29 @@ public class Snooper _skybox = new Skybox(); _grid = new Grid(); - _models = new Model[1]; + _models = new List(); + } + + public void Run(UObject export) + { switch (export) { case UStaticMesh st when st.TryConvert(out var mesh): { - _models[0] = new Model(st.Name, mesh.LODs[0], mesh.LODs[0].Verts); + _models.Add(new Model(st.Name, 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[0] = new Model(sk.Name, mesh.LODs[0], mesh.LODs[0].Verts); + _models.Add(new Model(sk.Name, mesh.LODs[0], mesh.LODs[0].Verts)); SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO); break; } default: throw new ArgumentOutOfRangeException(nameof(export)); } - } - public void Run() - { _window.Run(); } @@ -132,6 +134,8 @@ public class Snooper _controller = new ImGuiController(_gl, _window, input); + ImGuiExtensions.Theme(); + _skybox.Setup(_gl); _grid.Setup(_gl); @@ -156,27 +160,7 @@ public class Snooper _skybox.Bind(_camera); _grid.Bind(_camera); - ImGuiExtensions.Theme(); - - if (ImGui.BeginMainMenuBar()) - { - if (ImGui.BeginMenu("Edit")) - { - if (ImGui.MenuItem("Undo", "CTRL+Z")) {} - if (ImGui.MenuItem("Redo", "CTRL+Y", false, false)) {} // Disabled item - ImGui.Separator(); - if (ImGui.MenuItem("Cut", "CTRL+X")) {} - if (ImGui.MenuItem("Copy", "CTRL+C")) {} - if (ImGui.MenuItem("Paste", "CTRL+V")) {} - ImGui.EndMenu(); - } - - const string text = "Press ESC to Exit..."; - ImGui.SetCursorPosX(ImGui.GetWindowViewport().WorkSize.X - ImGui.CalcTextSize(text).X - 5); - ImGui.TextColored(ImGuiExtensions.STYLE.Colors[(int) ImGuiCol.TextDisabled], text); - - ImGui.EndMainMenuBar(); - } + ImGuiExtensions.DrawNavbar(); ImGui.Begin("ImGui.NET", ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoBringToFrontOnFocus | ImGuiWindowFlags.NoSavedSettings); foreach (var model in _models) @@ -263,6 +247,7 @@ public class Snooper { model.Dispose(); } + _models.Clear(); _controller.Dispose(); _window.Dispose(); _gl.Dispose();