From b4df69fbeb3b34d739f39a4a1ca626ef3a3c9a86 Mon Sep 17 00:00:00 2001 From: 4sval Date: Mon, 31 Oct 2022 01:56:29 +0100 Subject: [PATCH] junky cam movements --- FModel/Framework/ImGuiController.cs | 31 ++- FModel/Resources/default.frag | 43 ++--- FModel/Resources/default.vert | 6 +- FModel/ViewModels/CUE4ParseViewModel.cs | 6 +- FModel/Views/Snooper/Cache.cs | 64 ++++-- FModel/Views/Snooper/Material.cs | 29 +-- FModel/Views/Snooper/Model.cs | 11 +- FModel/Views/Snooper/Options.cs | 4 - FModel/Views/Snooper/Renderer.cs | 37 ++-- FModel/Views/Snooper/SnimGui.cs | 246 +++++++++++++++++++----- FModel/Views/Snooper/Snooper.cs | 97 +++++----- 11 files changed, 361 insertions(+), 213 deletions(-) diff --git a/FModel/Framework/ImGuiController.cs b/FModel/Framework/ImGuiController.cs index 3eab99c9..48dc9beb 100644 --- a/FModel/Framework/ImGuiController.cs +++ b/FModel/Framework/ImGuiController.cs @@ -232,25 +232,19 @@ outputColor = color * texture(in_fontTexture, texCoord); private void UpdateImGuiInput(GameWindow wnd) { ImGuiIOPtr io = ImGui.GetIO(); + var mState = wnd.MouseState; + var kState = wnd.KeyboardState; - MouseState MouseState = wnd.MouseState; - KeyboardState KeyboardState = wnd.KeyboardState; + io.AddMouseButtonEvent(0, mState[MouseButton.Left]); + io.AddMouseButtonEvent(1, mState[MouseButton.Right]); + io.AddMouseButtonEvent(2, mState[MouseButton.Middle]); - io.MouseDown[0] = MouseState[MouseButton.Left]; - io.MouseDown[1] = MouseState[MouseButton.Right]; - io.MouseDown[2] = MouseState[MouseButton.Middle]; - - var screenPoint = new Vector2i((int)MouseState.X, (int)MouseState.Y); - var point = screenPoint;//wnd.PointToClient(screenPoint); - io.MousePos = new System.Numerics.Vector2(point.X, point.Y); + io.AddMousePosEvent(mState.PreviousX, mState.PreviousY); foreach (Keys key in Enum.GetValues(typeof(Keys))) { - if (key == Keys.Unknown) - { - continue; - } - io.KeysDown[(int)key] = KeyboardState.IsKeyDown(key); + if (key == Keys.Unknown) continue; + io.AddKeyEvent((ImGuiKey) (int)key, kState.IsKeyDown(key)); } foreach (var c in PressedChars) @@ -259,10 +253,10 @@ outputColor = color * texture(in_fontTexture, texCoord); } PressedChars.Clear(); - io.KeyCtrl = KeyboardState.IsKeyDown(Keys.LeftControl) || KeyboardState.IsKeyDown(Keys.RightControl); - io.KeyAlt = KeyboardState.IsKeyDown(Keys.LeftAlt) || KeyboardState.IsKeyDown(Keys.RightAlt); - io.KeyShift = KeyboardState.IsKeyDown(Keys.LeftShift) || KeyboardState.IsKeyDown(Keys.RightShift); - io.KeySuper = KeyboardState.IsKeyDown(Keys.LeftSuper) || KeyboardState.IsKeyDown(Keys.RightSuper); + io.KeyCtrl = kState.IsKeyDown(Keys.LeftControl) || kState.IsKeyDown(Keys.RightControl); + io.KeyAlt = kState.IsKeyDown(Keys.LeftAlt) || kState.IsKeyDown(Keys.RightAlt); + io.KeyShift = kState.IsKeyDown(Keys.LeftShift) || kState.IsKeyDown(Keys.RightShift); + io.KeySuper = kState.IsKeyDown(Keys.LeftSuper) || kState.IsKeyDown(Keys.RightSuper); } internal void PressChar(char keyChar) @@ -273,7 +267,6 @@ outputColor = color * texture(in_fontTexture, texCoord); internal void MouseScroll(Vector2 offset) { ImGuiIOPtr io = ImGui.GetIO(); - io.MouseWheel = offset.Y; io.MouseWheelH = offset.X; } diff --git a/FModel/Resources/default.frag b/FModel/Resources/default.frag index 6314c7d3..8222cc51 100644 --- a/FModel/Resources/default.frag +++ b/FModel/Resources/default.frag @@ -3,7 +3,7 @@ in vec3 fPos; in vec3 fNormal; in vec2 fTexCoords; -in float fTexIndex; +in float fTexLayer; in vec4 fColor; struct Material { @@ -37,33 +37,24 @@ uniform bool display_vertex_colors; out vec4 FragColor; -vec4 getValueFromSamplerArray(sampler2D array[8]) { - if (fTexIndex < 1.0) { - return texture(array[0], fTexCoords); - } if (fTexIndex < 2.0) { - return texture(array[1], fTexCoords); - } if (fTexIndex < 3.0) { - return texture(array[2], fTexCoords); - } else { - return texture(array[3], fTexCoords); - } +int LayerToIndex(int max) +{ + return min(int(fTexLayer), max); } -vec3 getValueFromVec4Array(vec4 array[8]) { - if (fTexIndex < 1.0) { - return array[0].rgb; - } if (fTexIndex < 2.0) { - return array[1].rgb; - } if (fTexIndex < 3.0) { - return array[2].rgb; - } else { - return array[3].rgb; - } +vec4 SamplerSelector(sampler2D array[8]) +{ + return texture(array[LayerToIndex(7)], fTexCoords); +} + +vec4 VectorSelector(vec4 array[8]) +{ + return array[LayerToIndex(7)]; } vec3 getNormalFromMap() { - vec3 tangentNormal = getValueFromSamplerArray(material.normalMap).xyz * 2.0 - 1.0; + vec3 tangentNormal = SamplerSelector(material.normalMap).xyz * 2.0 - 1.0; vec3 q1 = dFdx(fPos); vec3 q2 = dFdy(fPos); @@ -98,7 +89,7 @@ void main() } else { - vec4 diffuse_map = getValueFromSamplerArray(material.diffuseMap); + vec4 diffuse_map = SamplerSelector(material.diffuseMap); vec3 diffuse_no_alpha = vec3(diffuse_map); vec3 result = light.ambient * diffuse_no_alpha; @@ -111,14 +102,14 @@ void main() vec3 n_view_direction = normalize(viewPos - fPos); vec3 reflect_direction = reflect(-n_light_direction, n_normal_map); float metallic = pow(max(dot(n_view_direction, reflect_direction), 0.0f), material.metallic_value); - vec3 specular_map = vec3(getValueFromSamplerArray(material.specularMap)); + vec3 specular_map = vec3(SamplerSelector(material.specularMap)); result += specular_map.r * light.specular * (metallic * specular_map.g); result += material.roughness_value * specular_map.b; } // emission - vec3 emission_map = vec3(getValueFromSamplerArray(material.emissionMap)); - result += getValueFromVec4Array(material.emissionColor) * emission_map; + vec3 emission_map = vec3(SamplerSelector(material.emissionMap)); + result += VectorSelector(material.emissionColor).rgb * emission_map; FragColor = vec4(result, 1.0); } diff --git a/FModel/Resources/default.vert b/FModel/Resources/default.vert index ff6dd064..f8288001 100644 --- a/FModel/Resources/default.vert +++ b/FModel/Resources/default.vert @@ -3,7 +3,7 @@ layout (location = 1) in vec3 vPos; layout (location = 2) in vec3 vNormal; layout (location = 3) in vec2 vTexCoords; -layout (location = 4) in float vTexIndex; +layout (location = 4) in float vTexLayer; layout (location = 5) in vec4 vColor; layout (location = 6) in ivec4 vBoneIds; layout (location = 7) in vec4 vWeights; @@ -17,7 +17,7 @@ uniform float uMorphTime; out vec3 fPos; out vec3 fNormal; out vec2 fTexCoords; -out float fTexIndex; +out float fTexLayer; out vec4 fColor; void main() @@ -28,6 +28,6 @@ void main() fPos = vec3(vInstanceMatrix * vec4(pos, 1.0)); fNormal = mat3(transpose(inverse(vInstanceMatrix))) * vNormal; fTexCoords = vTexCoords; - fTexIndex = vTexIndex; + fTexLayer = vTexLayer; fColor = vColor; } diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 0c4e02d6..7ef450e2 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -83,8 +83,8 @@ public class CUE4ParseViewModel : ViewModel new NativeWindowSettings { Size = new Vector2i( - Convert.ToInt32(SystemParameters.MaximizedPrimaryScreenWidth * .7), - Convert.ToInt32(SystemParameters.MaximizedPrimaryScreenHeight * .7)), + Convert.ToInt32(SystemParameters.MaximizedPrimaryScreenWidth * .75), + Convert.ToInt32(SystemParameters.MaximizedPrimaryScreenHeight * .85)), NumberOfSamples = Constants.SAMPLES_COUNT, WindowBorder = WindowBorder.Resizable, StartVisible = false, @@ -786,7 +786,7 @@ public class CUE4ParseViewModel : ViewModel } case UMaterialInstance m when ModelIsOverwritingMaterial: { - SnooperViewer.SwapMaterial(m); + SnooperViewer.Renderer.Swap(m); SnooperViewer.Run(); return true; } diff --git a/FModel/Views/Snooper/Cache.cs b/FModel/Views/Snooper/Cache.cs index b48a220e..ce35a67f 100644 --- a/FModel/Views/Snooper/Cache.cs +++ b/FModel/Views/Snooper/Cache.cs @@ -1,67 +1,89 @@ using System; using System.Collections.Generic; +using CUE4Parse_Conversion.Meshes; +using CUE4Parse_Conversion.Textures; +using CUE4Parse.UE4.Assets.Exports.StaticMesh; +using CUE4Parse.UE4.Assets.Exports.Texture; using CUE4Parse.UE4.Objects.Core.Misc; +using FModel.Settings; namespace FModel.Views.Snooper; public class Cache : IDisposable { - private readonly Dictionary _models; - private readonly Dictionary _textures; + public readonly Dictionary Models; + public readonly Dictionary Textures; + + private ETexturePlatform _platform; public Cache() { - _models = new Dictionary(); - _textures = new Dictionary(); + Models = new Dictionary(); + Textures = new Dictionary(); + _platform = UserSettings.Default.OverridedPlatform; } - public void AddModel(FGuid guid, Model model) => _models.Add(guid, model); - public void AddTexture(FGuid guid, Texture texture) => _textures.Add(guid, texture); + public bool TryGetCachedModel(UStaticMesh o, out Model model) + { + var guid = o.LightingGuid; + if (!Models.TryGetValue(guid, out model) && o.TryConvert(out var mesh)) + { + model = new Model(o.Name, o.ExportType, o.Materials, mesh); + Models[guid] = model; + } + return model != null; + } - public bool HasModel(FGuid guid) => _models.ContainsKey(guid); - public bool HasTexture(FGuid guid) => _textures.ContainsKey(guid); + public bool TryGetCachedTexture(UTexture2D o, out Texture texture) + { + var guid = o.LightingGuid; + if (!Textures.TryGetValue(guid, out texture) && o.GetFirstMip() is { } mip) + { + TextureDecoder.DecodeTexture(mip, o.Format, o.isNormalMap, _platform, out var data, out _); - public bool TryGetModel(FGuid guid, out Model model) => _models.TryGetValue(guid, out model); - public bool TryGetTexture(FGuid guid, out Texture texture) => _textures.TryGetValue(guid, out texture); + texture = new Texture(data, mip.SizeX, mip.SizeY, o); + Textures[guid] = texture; + } + return texture != null; + } public void Setup() { - foreach (var model in _models.Values) + foreach (var model in Models.Values) { if (model.IsSetup) continue; model.Setup(this); } } + public void Render(Shader shader) { - foreach (var model in _models.Values) + foreach (var model in Models.Values) { if (!model.Show) continue; model.Render(shader); } } + public void Outline(Shader shader) { - foreach (var model in _models.Values) + foreach (var model in Models.Values) { - if (!model.IsSelected) continue; + if (!model.IsSelected || !model.Show) continue; model.Outline(shader); } } - public void ClearModels() => _models.Clear(); - public void ClearTextures() => _textures.Clear(); - public void DisposeModels() { - foreach (var model in _models.Values) + foreach (var model in Models.Values) { model.Dispose(); } } public void DisposeTextures() { - foreach (var texture in _textures.Values) + foreach (var texture in Textures.Values) { texture.Dispose(); } @@ -70,9 +92,9 @@ public class Cache : IDisposable public void Dispose() { DisposeModels(); - ClearModels(); + Models.Clear(); DisposeTextures(); - ClearTextures(); + Textures.Clear(); } } diff --git a/FModel/Views/Snooper/Material.cs b/FModel/Views/Snooper/Material.cs index ab2b309e..773a88b6 100644 --- a/FModel/Views/Snooper/Material.cs +++ b/FModel/Views/Snooper/Material.cs @@ -1,8 +1,5 @@ using System; -using CUE4Parse_Conversion.Textures; using CUE4Parse.UE4.Assets.Exports.Material; -using CUE4Parse.UE4.Assets.Exports.Texture; -using FModel.Settings; using OpenTK.Graphics.OpenGL4; using OpenTK.Mathematics; @@ -67,43 +64,25 @@ public class Material : IDisposable } else { - var platform = UserSettings.Default.OverridedPlatform; - bool TryGetCached(UTexture2D o, out Texture t) - { - var guid = o.LightingGuid; - if (!cache.TryGetTexture(guid, out t)) - { - if (o.GetFirstMip() is { } mip) - { - TextureDecoder.DecodeTexture(mip, o.Format, o.isNormalMap, platform, out var data, out _); - - t = new Texture(data, mip.SizeX, mip.SizeY, o); - cache.AddTexture(guid, t); - } - else t = null; - } - return t != null; - } - Diffuse = new Texture[UvNumber]; for (int i = 0; i < Diffuse.Length; i++) - if (Parameters.TryGetTexture2d(out var o, CMaterialParams2.Diffuse[i]) && TryGetCached(o, out var t)) + if (Parameters.TryGetTexture2d(out var o, CMaterialParams2.Diffuse[i]) && cache.TryGetCachedTexture(o, out var t)) Diffuse[i] = t; Normals = new Texture[UvNumber]; for (int i = 0; i < Normals.Length; i++) - if (Parameters.TryGetTexture2d(out var o, CMaterialParams2.Normals[i]) && TryGetCached(o, out var t)) + if (Parameters.TryGetTexture2d(out var o, CMaterialParams2.Normals[i]) && cache.TryGetCachedTexture(o, out var t)) Normals[i] = t; SpecularMasks = new Texture[UvNumber]; for (int i = 0; i < SpecularMasks.Length; i++) - if (Parameters.TryGetTexture2d(out var o, CMaterialParams2.SpecularMasks[i]) && TryGetCached(o, out var t)) + if (Parameters.TryGetTexture2d(out var o, CMaterialParams2.SpecularMasks[i]) && cache.TryGetCachedTexture(o, out var t)) SpecularMasks[i] = t; Emissive = new Texture[UvNumber]; EmissionColor = new Vector4[UvNumber]; for (int i = 0; i < Emissive.Length; i++) - if (Parameters.TryGetTexture2d(out var o, CMaterialParams2.Emissive[i]) && TryGetCached(o, out var t)) + if (Parameters.TryGetTexture2d(out var o, CMaterialParams2.Emissive[i]) && cache.TryGetCachedTexture(o, out var t)) { Emissive[i] = t; diff --git a/FModel/Views/Snooper/Model.cs b/FModel/Views/Snooper/Model.cs index e956d774..485e0a90 100644 --- a/FModel/Views/Snooper/Model.cs +++ b/FModel/Views/Snooper/Model.cs @@ -46,6 +46,7 @@ public class Model : IDisposable public bool IsSelected; public bool DisplayVertexColors; public bool DisplayBones; + public int SelectedInstance; public float MorphTime; protected Model(string name, string type) @@ -53,7 +54,6 @@ public class Model : IDisposable Name = name; Type = type; Transforms = new List(); - Show = true; } public Model(string name, string type, ResolvedObject[] materials, CStaticMesh staticMesh) : this(name, type, materials, staticMesh, Transform.Identity) {} @@ -79,7 +79,7 @@ public class Model : IDisposable for (int m = 0; m < Materials.Length; m++) { if ((materials[m]?.TryLoad(out var material) ?? false) && material is UMaterialInterface unrealMaterial) - Materials[m] = new Material(lod.NumTexCoords, unrealMaterial); // lod.NumTexCoords + Materials[m] = new Material(lod.NumTexCoords, unrealMaterial); else Materials[m] = new Material(1); } @@ -160,10 +160,10 @@ public class Model : IDisposable public void AddInstance(Transform transform) => Transforms.Add(transform); - public void UpdateMatrix(int index) + public void UpdateMatrix(int instance) { _matrixVbo.Bind(); - _matrixVbo.Update(index, Transforms[index].Matrix); + _matrixVbo.Update(instance, Transforms[instance].Matrix); _matrixVbo.Unbind(); } @@ -223,6 +223,7 @@ public class Model : IDisposable for (int section = 0; section < Sections.Length; section++) { + if (Sections[section].Show) Show = true; Sections[section].Setup(); } @@ -266,7 +267,7 @@ public class Model : IDisposable for (int section = 0; section < Sections.Length; section++) { if (!Sections[section].Show) continue; - GL.DrawArraysInstanced(PrimitiveType.Triangles, Sections[section].FirstFaceIndex, Sections[section].FacesCount, TransformsCount); + GL.DrawArraysInstancedBaseInstance(PrimitiveType.Triangles, Sections[section].FirstFaceIndex, Sections[section].FacesCount, TransformsCount, SelectedInstance); } _vao.Unbind(); diff --git a/FModel/Views/Snooper/Options.cs b/FModel/Views/Snooper/Options.cs index fc024765..daeb6bfe 100644 --- a/FModel/Views/Snooper/Options.cs +++ b/FModel/Views/Snooper/Options.cs @@ -6,8 +6,6 @@ namespace FModel.Views.Snooper; public class Options { public FGuid SelectedModel { get; private set; } - public int SelectedModelInstance; - public int SelectedSection { get; private set; } public int SelectedMorph { get; private set; } @@ -19,7 +17,6 @@ public class Options public void Reset() { SelectedModel = Guid.Empty; - SelectedModelInstance = 0; SelectedSection = 0; SelectedMorph = 0; } @@ -27,7 +24,6 @@ public class Options public void SelectModel(FGuid guid) { SelectedModel = guid; - SelectedModelInstance = 0; SelectedSection = 0; SelectedMorph = 0; } diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs index 4a9d7aa8..cde75e90 100644 --- a/FModel/Views/Snooper/Renderer.cs +++ b/FModel/Views/Snooper/Renderer.cs @@ -9,6 +9,7 @@ using CUE4Parse.UE4.Assets.Exports.Texture; using CUE4Parse.UE4.Objects.Core.Math; using CUE4Parse.UE4.Objects.Engine; using CUE4Parse.UE4.Objects.UObject; +using OpenTK.Graphics.OpenGL4; using OpenTK.Mathematics; namespace FModel.Views.Snooper; @@ -16,7 +17,7 @@ namespace FModel.Views.Snooper; public class Renderer : IDisposable { private Shader _shader; - // private Shader _outline; // fix stutter + private Shader _outline; private Vector3 _diffuseLight; private Vector3 _specularLight; @@ -43,7 +44,7 @@ public class Renderer : IDisposable public void Swap(UMaterialInstance unrealMaterial) { - if (!Cache.TryGetModel(Settings.SelectedModel, out var model) || + if (!Cache.Models.TryGetValue(Settings.SelectedModel, out var model) || !Settings.TryGetSection(model, out var section)) return; model.Materials[section.MaterialIndex].SwapMaterial(unrealMaterial); @@ -53,7 +54,7 @@ public class Renderer : IDisposable public void Setup() { _shader = new Shader(); - // _outline = new Shader("outline"); + _outline = new Shader("outline"); _diffuseLight = new Vector3(0.75f); _specularLight = new Vector3(0.5f); @@ -65,10 +66,10 @@ public class Renderer : IDisposable var viewMatrix = cam.GetViewMatrix(); var projMatrix = cam.GetProjectionMatrix(); - // _outline.Use(); - // _outline.SetUniform("uView", viewMatrix); - // _outline.SetUniform("uProjection", projMatrix); - // _outline.SetUniform("viewPos", cam.Position); + _outline.Use(); + _outline.SetUniform("uView", viewMatrix); + _outline.SetUniform("uProjection", projMatrix); + _outline.SetUniform("viewPos", cam.Position); _shader.Use(); _shader.SetUniform("uView", viewMatrix); @@ -80,8 +81,8 @@ public class Renderer : IDisposable _shader.SetUniform("light.specular", _specularLight); Cache.Render(_shader); - // GL.Enable(EnableCap.StencilTest); // I don't get why this must be here but it works now so... - // Cache.Outline(_outline); + GL.Enable(EnableCap.StencilTest); + Cache.Outline(_outline); } private Camera SetupCamera(FBox box) @@ -97,7 +98,7 @@ public class Renderer : IDisposable private Camera LoadStaticMesh(UStaticMesh original) { var guid = original.LightingGuid; - if (Cache.TryGetModel(guid, out var model)) + if (Cache.Models.TryGetValue(guid, out var model)) { model.AddInstance(Transform.Identity); return null; @@ -106,7 +107,7 @@ public class Renderer : IDisposable if (!original.TryConvert(out var mesh)) return null; - Cache.AddModel(guid, new Model(original.Name, original.ExportType, original.Materials, mesh)); + Cache.Models[guid] = new Model(original.Name, original.ExportType, original.Materials, mesh); Settings.SelectModel(guid); return SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO); } @@ -114,9 +115,9 @@ public class Renderer : IDisposable private Camera LoadSkeletalMesh(USkeletalMesh original) { var guid = Guid.NewGuid(); - if (Cache.HasModel(guid) || !original.TryConvert(out var mesh)) return null; + if (Cache.Models.ContainsKey(guid) || !original.TryConvert(out var mesh)) return null; - Cache.AddModel(guid, new Model(original.Name, original.ExportType, original.Materials, original.MorphTargets, mesh)); + Cache.Models[guid] = new Model(original.Name, original.ExportType, original.Materials, original.MorphTargets, mesh); Settings.SelectModel(guid); return SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO); } @@ -124,9 +125,9 @@ public class Renderer : IDisposable private Camera LoadMaterialInstance(UMaterialInstance original) { var guid = Guid.NewGuid(); - if (Cache.HasModel(guid)) return null; + if (Cache.Models.ContainsKey(guid)) return null; - Cache.AddModel(guid, new Cube(original)); + Cache.Models[guid] = new Cube(original); Settings.SelectModel(guid); return SetupCamera(new FBox(new FVector(-.65f), new FVector(.65f))); } @@ -164,7 +165,7 @@ public class Renderer : IDisposable }; transform.Rotation.Yaw = -transform.Rotation.Yaw; - if (Cache.TryGetModel(guid, out var model)) + if (Cache.Models.TryGetValue(guid, out var model)) { model.AddInstance(transform); } @@ -207,7 +208,7 @@ public class Renderer : IDisposable } } - Cache.AddModel(guid, model); + Cache.Models[guid] = model; } } Services.ApplicationService.ApplicationView.Status.UpdateStatusLabel($"Actor {length}/{length}"); @@ -217,7 +218,7 @@ public class Renderer : IDisposable public void Dispose() { _shader.Dispose(); - // _outline.Dispose(); + _outline.Dispose(); Cache.Dispose(); } } diff --git a/FModel/Views/Snooper/SnimGui.cs b/FModel/Views/Snooper/SnimGui.cs index f317572b..2bfaa151 100644 --- a/FModel/Views/Snooper/SnimGui.cs +++ b/FModel/Views/Snooper/SnimGui.cs @@ -5,15 +5,15 @@ using CUE4Parse.UE4.Objects.Core.Misc; using FModel.Framework; using ImGuiNET; using OpenTK.Mathematics; -using OpenTK.Windowing.Desktop; +using OpenTK.Windowing.Common; using Vector2 = System.Numerics.Vector2; using Vector4 = System.Numerics.Vector4; namespace FModel.Views.Snooper; -public class SnimGui : IDisposable +public class SnimGui { - private readonly ImGuiController _controller; + public readonly ImGuiController Controller; private readonly Vector2 _outlinerSize; private readonly Vector2 _outlinerPosition; @@ -31,7 +31,7 @@ public class SnimGui : IDisposable public SnimGui(int width, int height) { - _controller = new ImGuiController(width, height); + Controller = new ImGuiController(width, height); var style = ImGui.GetStyle(); var viewport = ImGui.GetMainViewport(); @@ -49,22 +49,32 @@ public class SnimGui : IDisposable Theme(style); } - public void Render(Vector2i size, FramebufferObject framebuffer, Camera camera) + public void Render(Snooper s) { - DrawDockSpace(size); - + DrawDockSpace(s.Size); DrawNavbar(); - ImGui.Begin("Outliner"); + + ImGui.Begin("Camera"); ImGui.End(); - ImGui.Begin("Properties"); + ImGui.Begin("World"); ImGui.End(); - ImGui.Begin("UV Editor"); + ImGui.Begin("UV Channels"); ImGui.End(); ImGui.Begin("Timeline"); ImGui.End(); - Draw3DViewport(framebuffer, camera); + ImGui.Begin("Materials"); + ImGui.End(); + ImGui.Begin("Textures"); + ImGui.End(); - _controller.Render(); + DrawTransform(s); + DrawDetails(s); + DrawOuliner(s); + Draw3DViewport(s); + // last render will always be on top + // order by decreasing importance + + Controller.Render(); ImGuiController.CheckGLError("End of frame"); } @@ -113,7 +123,157 @@ public class SnimGui : IDisposable ImGui.EndMainMenuBar(); } - private void Draw3DViewport(FramebufferObject framebuffer, Camera camera) + private void DrawOuliner(Snooper s) + { + ImGui.Begin("Outliner"); + + PushStyleCompact(); + if (ImGui.BeginTable("Items", 2, ImGuiTableFlags.Borders | ImGuiTableFlags.Resizable)) + { + ImGui.TableSetupColumn("Count", ImGuiTableColumnFlags.NoHeaderWidth | ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("Name"); + ImGui.TableHeadersRow(); + + var i = 0; + foreach ((FGuid guid, Model model) in s.Renderer.Cache.Models) + { + ImGui.PushID(i); + ImGui.TableNextRow(); + ImGui.TableNextColumn(); + if (!model.Show) + ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, ImGui.GetColorU32(new Vector4(1, 0, 0, .5f))); + ImGui.Text(model.TransformsCount.ToString("D")); + ImGui.TableNextColumn(); + model.IsSelected = s.Renderer.Settings.SelectedModel == guid; + if (ImGui.Selectable(model.Name, model.IsSelected, ImGuiSelectableFlags.SpanAllColumns)) + { + s.Renderer.Settings.SelectModel(guid); + } + if (ImGui.BeginPopupContextItem()) + { + s.Renderer.Settings.SelectModel(guid); + if (ImGui.Selectable("Deselect")) + s.Renderer.Settings.SelectModel(Guid.Empty); + if (ImGui.Selectable("Delete")) + s.Renderer.Cache.Models.Remove(guid); + if (ImGui.Selectable("Copy Name to Clipboard")) + Application.Current.Dispatcher.Invoke(delegate + { + Clipboard.SetText(model.Name); + }); + ImGui.EndPopup(); + } + ImGui.PopID(); + i++; + } + + ImGui.EndTable(); + } + PopStyleCompact(); + + ImGui.End(); + } + + private void DrawDetails(Snooper s) + { + if (ImGui.Begin("Details")) + { + var guid = s.Renderer.Settings.SelectedModel; + if (s.Renderer.Cache.Models.TryGetValue(guid, out var model)) + { + ImGui.Text($"Entity: ({model.Type}) {model.Name}"); + ImGui.Text($"Guid: {guid.ToString(EGuidFormats.UniqueObjectGuid)}"); + + PushStyleCompact(); + ImGui.Columns(4, "Actions", false); + // if (ImGui.Button("Go To")) s.Camera.Position = model.Transforms[s.Renderer.Settings.SelectedModelInstance].Position; + ImGui.NextColumn(); ImGui.Checkbox("Show", ref model.Show); + ImGui.NextColumn(); ImGui.BeginDisabled(!model.HasVertexColors); ImGui.Checkbox("Colors", ref model.DisplayVertexColors); ImGui.EndDisabled(); + ImGui.NextColumn(); ImGui.BeginDisabled(!model.HasBones); ImGui.Checkbox("Bones", ref model.DisplayBones); ImGui.EndDisabled(); + ImGui.Columns(1); + PopStyleCompact(); + + ImGui.Separator(); + } + + ImGui.End(); + } + } + + private void DrawTransform(Snooper s) + { + if (ImGui.Begin("Transform")) + { + if (s.Renderer.Cache.Models.TryGetValue(s.Renderer.Settings.SelectedModel, out var model)) + { + const int width = 100; + var speed = s.Camera.Speed / 100; + + PushStyleCompact(); + ImGui.PushID(0); ImGui.BeginDisabled(model.TransformsCount < 2); + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); + ImGui.SliderInt("", ref model.SelectedInstance, 0, model.TransformsCount - 1, "Instance %i", ImGuiSliderFlags.AlwaysClamp); + ImGui.EndDisabled(); ImGui.PopID(); + + ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); + if (ImGui.TreeNode("Location")) + { + ImGui.PushID(1); + ImGui.SetNextItemWidth(width); + ImGui.DragFloat("X", ref model.Transforms[model.SelectedInstance].Position.X, speed, 0f, 0f, "%.2f m"); + + ImGui.SetNextItemWidth(width); + ImGui.DragFloat("Y", ref model.Transforms[model.SelectedInstance].Position.Y, speed, 0f, 0f, "%.2f m"); + + ImGui.SetNextItemWidth(width); + ImGui.DragFloat("Z", ref model.Transforms[model.SelectedInstance].Position.Z, speed, 0f, 0f, "%.2f m"); + + ImGui.PopID(); + ImGui.TreePop(); + } + + ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); + if (ImGui.TreeNode("Rotation")) + { + ImGui.PushID(2); + ImGui.SetNextItemWidth(width); + ImGui.DragFloat("X", ref model.Transforms[model.SelectedInstance].Rotation.Pitch, .5f, 0f, 0f, "%.1f°"); + + ImGui.SetNextItemWidth(width); + ImGui.DragFloat("Y", ref model.Transforms[model.SelectedInstance].Rotation.Roll, .5f, 0f, 0f, "%.1f°"); + + ImGui.SetNextItemWidth(width); + ImGui.DragFloat("Z", ref model.Transforms[model.SelectedInstance].Rotation.Yaw, .5f, 0f, 0f, "%.1f°"); + + ImGui.PopID(); + ImGui.TreePop(); + } + + if (ImGui.TreeNode("Scale")) + { + ImGui.PushID(3); + ImGui.SetNextItemWidth(width); + ImGui.DragFloat("X", ref model.Transforms[model.SelectedInstance].Scale.X, speed, 0f, 0f, "%.3f"); + + ImGui.SetNextItemWidth(width); + ImGui.DragFloat("Y", ref model.Transforms[model.SelectedInstance].Scale.Y, speed, 0f, 0f, "%.3f"); + + ImGui.SetNextItemWidth(width); + ImGui.DragFloat("Z", ref model.Transforms[model.SelectedInstance].Scale.Z, speed, 0f, 0f, "%.3f"); + + ImGui.PopID(); + ImGui.TreePop(); + } + + model.UpdateMatrix(model.SelectedInstance); + PopStyleCompact(); + } + + ImGui.End(); + } + } + + private void Draw3DViewport(Snooper s) { const float lookSensitivity = 0.1f; const ImGuiWindowFlags flags = @@ -131,43 +291,45 @@ public class SnimGui : IDisposable largest.Y -= ImGui.GetScrollY(); var size = new Vector2(largest.X, largest.Y); - camera.AspectRatio = size.X / size.Y; - ImGui.ImageButton(framebuffer.GetPointer(), size, new Vector2(0, 1), new Vector2(1, 0), 0); + s.Camera.AspectRatio = size.X / size.Y; + ImGui.ImageButton(s.Framebuffer.GetPointer(), size, new Vector2(0, 1), new Vector2(1, 0), 0); // it took me 5 hours to make it work, don't change any of the following code // basically the Raw cursor doesn't actually freeze the mouse position // so for ImGui, the IsItemHovered will be false if mouse leave, even in Raw mode - // var io = ImGui.GetIO(); - // if (ImGui.IsItemHovered()) - // { - // // if right button down while mouse is hover viewport - // if (ImGui.IsMouseDown(ImGuiMouseButton.Right) && !_viewportFocus) - // _viewportFocus = true; - // } + if (ImGui.IsItemHovered()) + { + // if left button down while mouse is hover viewport + if (ImGui.IsMouseDown(ImGuiMouseButton.Left) && !_viewportFocus) + { + _viewportFocus = true; + s.CursorState = CursorState.Grabbed; + } + } // this can't be inside IsItemHovered! read it as - // if right mouse button was pressed while hovering the viewport - // move camera until right mouse button is released + // if left mouse button was pressed while hovering the viewport + // move camera until left mouse button is released // no matter where mouse position end up - // if (ImGui.IsMouseDragging(ImGuiMouseButton.Right, lookSensitivity) && _viewportFocus) - // { - // var delta = io.MouseDelta * lookSensitivity; - // camera.ModifyDirection(delta.X, delta.Y); - // mouse.Cursor.CursorMode = CursorMode.Raw; - // } + var io = ImGui.GetIO(); + if (ImGui.IsMouseDragging(ImGuiMouseButton.Left, lookSensitivity) && _viewportFocus) + { + var delta = io.MouseDelta * lookSensitivity; + s.Camera.ModifyDirection(delta.X, delta.Y); + } // if left button up and mouse was in viewport - // if (ImGui.IsMouseReleased(ImGuiMouseButton.Right) && _viewportFocus) - // { - // _viewportFocus = false; - // mouse.Cursor.CursorMode = CursorMode.Normal; - // } + if (ImGui.IsMouseReleased(ImGuiMouseButton.Left) && _viewportFocus) + { + _viewportFocus = false; + s.CursorState = CursorState.Normal; + } - // const float padding = 5f; - // float framerate = ImGui.GetIO().Framerate; - // var text = $"FPS: {framerate:0} ({1000.0f / framerate:0.##} ms)"; - // ImGui.SetCursorPos(new Vector2(pos.X + padding, pos.Y + size.Y - ImGui.CalcTextSize(text).Y - padding)); - // ImGui.Text(text); + const float padding = 7.5f; + float framerate = ImGui.GetIO().Framerate; + var text = $"FPS: {framerate:0} ({1000.0f / framerate:0.##} ms)"; + ImGui.SetCursorPos(size with { X = padding }); + ImGui.Text(text); ImGui.End(); } @@ -249,8 +411,4 @@ public class SnimGui : IDisposable style.Colors[(int) ImGuiCol.NavWindowingDimBg] = new Vector4(0.80f, 0.80f, 0.80f, 0.20f); style.Colors[(int) ImGuiCol.ModalWindowDimBg] = new Vector4(0.80f, 0.80f, 0.80f, 0.35f); } - - public void Update(GameWindow wnd, float deltaSeconds) => _controller.Update(wnd, deltaSeconds); - public void WindowResized(int width, int height) => _controller.WindowResized(width, height); - public void Dispose() => _controller.Dispose(); } diff --git a/FModel/Views/Snooper/Snooper.cs b/FModel/Views/Snooper/Snooper.cs index fc4a92ba..5c9141cb 100644 --- a/FModel/Views/Snooper/Snooper.cs +++ b/FModel/Views/Snooper/Snooper.cs @@ -1,7 +1,6 @@ using System.Threading; using System.Windows; using CUE4Parse.UE4.Assets.Exports; -using CUE4Parse.UE4.Assets.Exports.Material; using OpenTK.Graphics.OpenGL4; using OpenTK.Mathematics; using OpenTK.Windowing.Common; @@ -12,49 +11,50 @@ namespace FModel.Views.Snooper; public class Snooper : GameWindow { - private FramebufferObject _framebuffer; + public Camera Camera; + public FramebufferObject Framebuffer; + public readonly Renderer Renderer; + private readonly Skybox _skybox; private readonly Grid _grid; - private readonly Renderer _renderer; private readonly SnimGui _gui; - private Camera _camera; private float _previousSpeed; private bool _init; public Snooper(GameWindowSettings gwSettings, NativeWindowSettings nwSettings) : base(gwSettings, nwSettings) { - _framebuffer = new FramebufferObject(Size); + Framebuffer = new FramebufferObject(Size); + Renderer = new Renderer(); + _skybox = new Skybox(); _grid = new Grid(); - _renderer = new Renderer(); _gui = new SnimGui(ClientSize.X, ClientSize.Y); + _init = false; } - public void SwapMaterial(UMaterialInstance mi) => _renderer.Swap(mi); public void LoadExport(CancellationToken cancellationToken, UObject export) { - var newCamera = _renderer.Load(cancellationToken, export); + var newCamera = Renderer.Load(cancellationToken, export); if (newCamera == null || !(newCamera.Speed > _previousSpeed)) return; - _camera = newCamera; - _previousSpeed = _camera.Speed; + Camera = newCamera; + _previousSpeed = Camera.Speed; } private unsafe void WindowShouldClose(bool value, bool clear) { if (clear) { - _renderer.Cache.DisposeModels(); - _renderer.Cache.ClearModels(); - _renderer.Settings.Reset(); + Renderer.Cache.DisposeModels(); + Renderer.Cache.Models.Clear(); + Renderer.Settings.Reset(); _previousSpeed = 0f; } GLFW.SetWindowShouldClose(WindowPtr, value); // start / stop game loop - // CursorState = value ? CursorState.Normal : CursorState.Grabbed; IsVisible = !value; } @@ -71,7 +71,7 @@ public class Snooper : GameWindow { if (_init) { - _renderer.Cache.Setup(); + Renderer.Cache.Setup(); return; } @@ -85,10 +85,10 @@ public class Snooper : GameWindow GL.StencilOp(StencilOp.Keep, StencilOp.Replace, StencilOp.Replace); GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); - _framebuffer.Setup(); + Framebuffer.Setup(); _skybox.Setup(); _grid.Setup(); - _renderer.Setup(); + Renderer.Setup(); _init = true; } @@ -98,20 +98,20 @@ public class Snooper : GameWindow if (!IsVisible) return; - _gui.Update(this, (float)args.Time); + _gui.Controller.Update(this, (float)args.Time); ClearWhatHasBeenDrawn(); - _framebuffer.Bind(); + Framebuffer.Bind(); ClearWhatHasBeenDrawn(); - _skybox.Render(_camera); - _grid.Render(_camera); - _renderer.Render(_camera); + _skybox.Render(Camera); + _grid.Render(Camera); + Renderer.Render(Camera); - _framebuffer.BindMsaa(); - _framebuffer.Bind(0); + Framebuffer.BindMsaa(); + Framebuffer.Bind(0); - _gui.Render(Size, _framebuffer, _camera); + _gui.Render(this); SwapBuffers(); } @@ -122,15 +122,22 @@ public class Snooper : GameWindow GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill); } - protected override void OnMouseMove(MouseMoveEventArgs e) + protected override void OnMouseWheel(MouseWheelEventArgs e) { - base.OnMouseMove(e); + base.OnMouseWheel(e); if (!IsVisible) return; - // const float lookSensitivity = 0.1f; - // var delta = e.Delta * lookSensitivity; - // _camera.ModifyDirection(delta.X, delta.Y); + _gui.Controller.MouseScroll(e.Offset); + } + + protected override void OnTextInput(TextInputEventArgs e) + { + base.OnTextInput(e); + if (!IsVisible) + return; + + _gui.Controller.PressChar((char) e.Unicode); } protected override void OnUpdateFrame(FrameEventArgs e) @@ -140,23 +147,23 @@ public class Snooper : GameWindow return; var multiplier = KeyboardState.IsKeyDown(Keys.LeftShift) ? 2f : 1f; - var moveSpeed = _camera.Speed * multiplier * (float) e.Time; + var moveSpeed = Camera.Speed * multiplier * (float) e.Time; if (KeyboardState.IsKeyDown(Keys.W)) - _camera.Position += moveSpeed * _camera.Direction; + Camera.Position += moveSpeed * Camera.Direction; if (KeyboardState.IsKeyDown(Keys.S)) - _camera.Position -= moveSpeed * _camera.Direction; + Camera.Position -= moveSpeed * Camera.Direction; if (KeyboardState.IsKeyDown(Keys.A)) - _camera.Position -= Vector3.Normalize(Vector3.Cross(_camera.Direction, _camera.Up)) * moveSpeed; + Camera.Position -= Vector3.Normalize(Vector3.Cross(Camera.Direction, Camera.Up)) * moveSpeed; if (KeyboardState.IsKeyDown(Keys.D)) - _camera.Position += Vector3.Normalize(Vector3.Cross(_camera.Direction, _camera.Up)) * moveSpeed; + Camera.Position += Vector3.Normalize(Vector3.Cross(Camera.Direction, Camera.Up)) * moveSpeed; if (KeyboardState.IsKeyDown(Keys.E)) - _camera.Position += moveSpeed * _camera.Up; + Camera.Position += moveSpeed * Camera.Up; if (KeyboardState.IsKeyDown(Keys.Q)) - _camera.Position -= moveSpeed * _camera.Up; + Camera.Position -= moveSpeed * Camera.Up; if (KeyboardState.IsKeyDown(Keys.X)) - _camera.ModifyZoom(-.5f); + Camera.ModifyZoom(-.5f); if (KeyboardState.IsKeyDown(Keys.C)) - _camera.ModifyZoom(+.5f); + Camera.ModifyZoom(+.5f); if (KeyboardState.IsKeyPressed(Keys.R)) WindowShouldClose(true, false); @@ -170,10 +177,10 @@ public class Snooper : GameWindow GL.Viewport(0, 0, Size.X, Size.Y); - _framebuffer = new FramebufferObject(Size); - _framebuffer.Setup(); - _camera.AspectRatio = Size.X / (float)Size.Y; - _gui.WindowResized(ClientSize.X, ClientSize.Y); + Framebuffer = new FramebufferObject(Size); + Framebuffer.Setup(); + Camera.AspectRatio = Size.X / (float)Size.Y; + _gui.Controller.WindowResized(ClientSize.X, ClientSize.Y); } protected override void Dispose(bool disposing) @@ -182,7 +189,7 @@ public class Snooper : GameWindow _skybox?.Dispose(); _grid?.Dispose(); - _renderer?.Dispose(); - _gui?.Dispose(); + Renderer?.Dispose(); + _gui?.Controller.Dispose(); } }