diff --git a/.gitmodules b/.gitmodules index 2221fc41..667a20e3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "CUE4Parse"] path = CUE4Parse url = https://github.com/FabianFG/CUE4Parse +[submodule "Snooper"] + path = Snooper + url = https://github.com/FModel/Snooper diff --git a/CUE4Parse b/CUE4Parse index c99a1d6d..746a6d77 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit c99a1d6dfc269281c2a7bf4dc36f8664dd8791e6 +Subproject commit 746a6d779a9607df1aa32ee2858f3ce0fc8557ae diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 00000000..aa6773f9 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,5 @@ + + + $(MSBuildThisFileDirectory)CUE4Parse\ + + \ No newline at end of file diff --git a/FModel/App.xaml.cs b/FModel/App.xaml.cs index 5cd426a3..ecfeed1e 100644 --- a/FModel/App.xaml.cs +++ b/FModel/App.xaml.cs @@ -7,11 +7,10 @@ using System.IO; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Threading; -using CUE4Parse; using FModel.Framework; using FModel.Services; using FModel.Settings; -using FModel.Views.Snooper; +using FModel.ViewModels; using Newtonsoft.Json; using Serilog.Events; using Serilog.Sinks.SystemConsole.Themes; @@ -134,7 +133,7 @@ public partial class App .Filter.ByIncludingOnly(IsConversionLibrary) .WriteTo.Console(outputTemplate: template2, theme: AnsiConsoleTheme.Literate) .WriteTo.File(outputTemplate: template2, path: filePath, shared: true)) - .MinimumLevel.Override("CUE4Parse_Conversion", LogEventLevel.Verbose).WriteTo.Sink(ImGuiSink.Instance) + // .MinimumLevel.Override("CUE4Parse_Conversion", LogEventLevel.Verbose).WriteTo.Sink(ImGuiSink.Instance) .CreateLogger(); CacheManager.MigrateLegacyFiles(); @@ -150,6 +149,7 @@ public partial class App private void AppExit(object sender, ExitEventArgs e) { + SnooperViewModel.Instance.Dispose(); Log.Information("––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––"); Log.CloseAndFlush(); UserSettings.Save(); diff --git a/FModel/FModel.csproj b/FModel/FModel.csproj index 1bfc1c33..979b0361 100644 --- a/FModel/FModel.csproj +++ b/FModel/FModel.csproj @@ -169,19 +169,18 @@ - - + diff --git a/FModel/FModel.slnx b/FModel/FModel.slnx index ca74a91b..30826f89 100644 --- a/FModel/FModel.slnx +++ b/FModel/FModel.slnx @@ -1,5 +1,7 @@ + + diff --git a/FModel/Framework/ImGuiController.cs b/FModel/Framework/ImGuiController.cs deleted file mode 100644 index 0768de97..00000000 --- a/FModel/Framework/ImGuiController.cs +++ /dev/null @@ -1,672 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Numerics; -using System.Runtime.InteropServices; -using System.Windows; -using System.Windows.Forms; -using FModel.Settings; -using ImGuiNET; -using ImGuizmoNET; -using OpenTK.Graphics.OpenGL4; -using OpenTK.Windowing.Desktop; -using OpenTK.Windowing.GraphicsLibraryFramework; -using ErrorCode = OpenTK.Graphics.OpenGL4.ErrorCode; -using Keys = OpenTK.Windowing.GraphicsLibraryFramework.Keys; - -namespace FModel.Framework; - -public class ImGuiController : IDisposable -{ - private bool _frameBegun; - - private int _vertexArray; - private int _vertexBuffer; - private int _vertexBufferSize; - private int _indexBuffer; - private int _indexBufferSize; - - //private Texture _fontTexture; - - private int _fontTexture; - - private int _shader; - private int _shaderFontTextureLocation; - private int _shaderProjectionMatrixLocation; - - private int _windowWidth; - private int _windowHeight; - - public ImFontPtr FontNormal; - public ImFontPtr FontBold; - public ImFontPtr FontSemiBold; - - private readonly Vector2 _scaleFactor = Vector2.One; - public readonly float DpiScale = GetDpiScale(); - - private static bool KHRDebugAvailable = false; - - public ImGuiController(int width, int height) - { - _windowWidth = width; - _windowHeight = height; - - int major = GL.GetInteger(GetPName.MajorVersion); - int minor = GL.GetInteger(GetPName.MinorVersion); - KHRDebugAvailable = (major == 4 && minor >= 3) || IsExtensionSupported("KHR_debug"); - - IntPtr context = ImGui.CreateContext(); - ImGui.SetCurrentContext(context); - ImGuizmo.SetImGuiContext(context); - - var io = ImGui.GetIO(); - unsafe - { - var iniFileNamePtr = Marshal.StringToCoTaskMemUTF8(Path.Combine(UserSettings.Default.OutputDirectory, ".data", "imgui.ini")); - io.NativePtr->IniFilename = (byte*)iniFileNamePtr; - } - - var assembly = System.Reflection.Assembly.GetExecutingAssembly(); - var assemblyName = assembly.GetName().Name; - byte[] LoadFont(string name) - { - using var stream = assembly.GetManifestResourceStream($"{assemblyName}.Resources.{name}") - ?? throw new FileNotFoundException($"Embedded font '{name}' not found."); - using var ms = new MemoryStream(); - stream.CopyTo(ms); - return ms.ToArray(); - } - - var faSolid = LoadFont("fa-solid-900.otf"); - var faRegular = LoadFont("fa-regular-400.otf"); - var faBrands = LoadFont("fa-brands-400.otf"); - - unsafe - { - // FA5 icons live in E000–F8FF (brands/regular/solid) - var iconRanges = stackalloc ushort[] { 0xe000, 0xf8ff, 0 }; - - var cfg = ImGuiNative.ImFontConfig_ImFontConfig(); - cfg->MergeMode = 1; - cfg->PixelSnapH = 1; - cfg->GlyphMinAdvanceX = 16f; - // FontDataOwnedByAtlas = 0 because we manage the GCHandle lifetime ourselves - cfg->FontDataOwnedByAtlas = 0; - - void MergeFontAwesome(byte[] solid, byte[] regular, byte[] brands) - { - fixed (byte* pSolid = solid) - fixed (byte* pRegular = regular) - fixed (byte* pBrands = brands) - { - io.Fonts.AddFontFromMemoryTTF((IntPtr)pSolid, solid.Length, 14, (IntPtr)cfg, (IntPtr)iconRanges); - io.Fonts.AddFontFromMemoryTTF((IntPtr)pRegular, regular.Length, 14, (IntPtr)cfg, (IntPtr)iconRanges); - io.Fonts.AddFontFromMemoryTTF((IntPtr)pBrands, brands.Length, 14, (IntPtr)cfg, (IntPtr)iconRanges); - } - } - - // If not found, Fallback to default ImGui Font - var normalPath = @"C:\Windows\Fonts\segoeui.ttf"; - var boldPath = @"C:\Windows\Fonts\segoeuib.ttf"; - var semiBoldPath = @"C:\Windows\Fonts\seguisb.ttf"; - - if (File.Exists(normalPath)) - { - FontNormal = io.Fonts.AddFontFromFileTTF(normalPath, 16 * DpiScale); - MergeFontAwesome(faSolid, faRegular, faBrands); - } - if (File.Exists(boldPath)) - { - FontBold = io.Fonts.AddFontFromFileTTF(boldPath, 16 * DpiScale); - MergeFontAwesome(faSolid, faRegular, faBrands); - } - if (File.Exists(semiBoldPath)) - { - FontSemiBold = io.Fonts.AddFontFromFileTTF(semiBoldPath, 16 * DpiScale); - MergeFontAwesome(faSolid, faRegular, faBrands); - } - - ImGuiNative.ImFontConfig_destroy(cfg); - } - - io.Fonts.AddFontDefault(); - io.Fonts.Build(); // Build font atlas - - io.BackendFlags |= ImGuiBackendFlags.RendererHasVtxOffset; - io.ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard; - io.ConfigFlags |= ImGuiConfigFlags.DockingEnable; - io.Fonts.Flags |= ImFontAtlasFlags.NoBakedLines; - io.ConfigDockingWithShift = true; - io.ConfigWindowsMoveFromTitleBarOnly = true; - io.BackendRendererUserData = 0; - - CreateDeviceResources(); - } - - public void Normal() => PushFont(FontNormal); - public void Bold() => PushFont(FontBold); - public void SemiBold() => PushFont(FontSemiBold); - - private void PushFont(ImFontPtr ptr) => ImGui.PushFont(ptr); - - public void WindowResized(int width, int height) - { - _windowWidth = width; - _windowHeight = height; - } - - public void DestroyDeviceObjects() - { - Dispose(); - } - - public void CreateDeviceResources() - { - _vertexBufferSize = 10000; - _indexBufferSize = 2000; - - int prevVAO = GL.GetInteger(GetPName.VertexArrayBinding); - int prevArrayBuffer = GL.GetInteger(GetPName.ArrayBufferBinding); - - _vertexArray = GL.GenVertexArray(); - GL.BindVertexArray(_vertexArray); - LabelObject(ObjectLabelIdentifier.VertexArray, _vertexArray, "ImGui"); - - _vertexBuffer = GL.GenBuffer(); - GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBuffer); - LabelObject(ObjectLabelIdentifier.Buffer, _vertexBuffer, "VBO: ImGui"); - GL.BufferData(BufferTarget.ArrayBuffer, _vertexBufferSize, IntPtr.Zero, BufferUsageHint.DynamicDraw); - - _indexBuffer = GL.GenBuffer(); - GL.BindBuffer(BufferTarget.ElementArrayBuffer, _indexBuffer); - LabelObject(ObjectLabelIdentifier.Buffer, _indexBuffer, "EBO: ImGui"); - GL.BufferData(BufferTarget.ElementArrayBuffer, _indexBufferSize, IntPtr.Zero, BufferUsageHint.DynamicDraw); - - RecreateFontDeviceTexture(); - - string VertexSource = @"#version 330 core - -uniform mat4 projection_matrix; - -layout(location = 0) in vec2 in_position; -layout(location = 1) in vec2 in_texCoord; -layout(location = 2) in vec4 in_color; - -out vec4 color; -out vec2 texCoord; - -void main() -{ - gl_Position = projection_matrix * vec4(in_position, 0, 1); - color = in_color; - texCoord = in_texCoord; -}"; - string FragmentSource = @"#version 330 core - -uniform sampler2D in_fontTexture; - -in vec4 color; -in vec2 texCoord; - -out vec4 outputColor; - -void main() -{ - outputColor = color * texture(in_fontTexture, texCoord); -}"; - - _shader = CreateProgram("ImGui", VertexSource, FragmentSource); - _shaderProjectionMatrixLocation = GL.GetUniformLocation(_shader, "projection_matrix"); - _shaderFontTextureLocation = GL.GetUniformLocation(_shader, "in_fontTexture"); - - int stride = Marshal.SizeOf(); - GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, stride, 0); - GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, stride, 8); - GL.VertexAttribPointer(2, 4, VertexAttribPointerType.UnsignedByte, true, stride, 16); - - GL.EnableVertexAttribArray(0); - GL.EnableVertexAttribArray(1); - GL.EnableVertexAttribArray(2); - - GL.BindVertexArray(prevVAO); - GL.BindBuffer(BufferTarget.ArrayBuffer, prevArrayBuffer); - - CheckGLError("End of ImGui setup"); - } - - /// - /// Recreates the device texture used to render text. - /// - public void RecreateFontDeviceTexture() - { - ImGuiIOPtr io = ImGui.GetIO(); - io.Fonts.GetTexDataAsRGBA32(out IntPtr pixels, out int width, out int height, out int bytesPerPixel); - - int mips = (int)Math.Floor(Math.Log(Math.Max(width, height), 2)); - - int prevActiveTexture = GL.GetInteger(GetPName.ActiveTexture); - GL.ActiveTexture(TextureUnit.Texture0); - int prevTexture2D = GL.GetInteger(GetPName.TextureBinding2D); - - _fontTexture = GL.GenTexture(); - GL.BindTexture(TextureTarget.Texture2D, _fontTexture); - GL.TexStorage2D(TextureTarget2d.Texture2D, mips, SizedInternalFormat.Rgba8, width, height); - LabelObject(ObjectLabelIdentifier.Texture, _fontTexture, "ImGui Text Atlas"); - - GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, width, height, PixelFormat.Bgra, PixelType.UnsignedByte, pixels); - - GL.GenerateMipmap(GenerateMipmapTarget.Texture2D); - - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat); - - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMaxLevel, mips - 1); - - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); - - // Restore state - GL.BindTexture(TextureTarget.Texture2D, prevTexture2D); - GL.ActiveTexture((TextureUnit)prevActiveTexture); - - io.Fonts.SetTexID((IntPtr)_fontTexture); - - io.Fonts.ClearTexData(); - } - - /// - /// Renders the ImGui draw list data. - /// - public void Render() - { - if (_frameBegun) - { - _frameBegun = false; - ImGui.Render(); - RenderImDrawData(ImGui.GetDrawData()); - } - } - - /// - /// Updates ImGui input and IO configuration state. - /// - public void Update(GameWindow wnd, float deltaSeconds) - { - if (_frameBegun) - { - ImGui.Render(); - } - - SetPerFrameImGuiData(deltaSeconds); - UpdateImGuiInput(wnd); - - _frameBegun = true; - ImGui.NewFrame(); - ImGuizmo.BeginFrame(); - } - - /// - /// Sets per-frame data based on the associated window. - /// This is called by Update(float). - /// - private void SetPerFrameImGuiData(float deltaSeconds) - { - ImGuiIOPtr io = ImGui.GetIO(); - io.DisplaySize = new Vector2( - _windowWidth / _scaleFactor.X, - _windowHeight / _scaleFactor.Y); - io.DisplayFramebufferScale = _scaleFactor; - io.DeltaTime = deltaSeconds; // DeltaTime is in seconds. - } - - readonly List PressedChars = new List(); - - private void UpdateImGuiInput(GameWindow wnd) - { - ImGuiIOPtr io = ImGui.GetIO(); - - var mState = wnd.MouseState; - var kState = wnd.KeyboardState; - - io.AddMousePosEvent(mState.X, mState.Y); - io.AddMouseButtonEvent(0, mState[MouseButton.Left]); - io.AddMouseButtonEvent(1, mState[MouseButton.Right]); - io.AddMouseButtonEvent(2, mState[MouseButton.Middle]); - io.AddMouseButtonEvent(3, mState[MouseButton.Button1]); - io.AddMouseButtonEvent(4, mState[MouseButton.Button2]); - io.AddMouseWheelEvent(mState.ScrollDelta.X, mState.ScrollDelta.Y); - - foreach (Keys key in Enum.GetValues()) - { - if (key == Keys.Unknown) continue; - io.AddKeyEvent(TranslateKey(key), kState.IsKeyDown(key)); - } - - foreach (var c in PressedChars) - { - io.AddInputCharacter(c); - } - PressedChars.Clear(); - - io.KeyShift = kState.IsKeyDown(Keys.LeftShift) || kState.IsKeyDown(Keys.RightShift); - io.KeyCtrl = kState.IsKeyDown(Keys.LeftControl) || kState.IsKeyDown(Keys.RightControl); - io.KeyAlt = kState.IsKeyDown(Keys.LeftAlt) || kState.IsKeyDown(Keys.RightAlt); - io.KeySuper = kState.IsKeyDown(Keys.LeftSuper) || kState.IsKeyDown(Keys.RightSuper); - } - - internal void PressChar(char keyChar) - { - PressedChars.Add(keyChar); - } - - private void RenderImDrawData(ImDrawDataPtr draw_data) - { - if (draw_data.CmdListsCount == 0) - { - return; - } - - // Get intial state. - int prevVAO = GL.GetInteger(GetPName.VertexArrayBinding); - int prevArrayBuffer = GL.GetInteger(GetPName.ArrayBufferBinding); - int prevProgram = GL.GetInteger(GetPName.CurrentProgram); - bool prevBlendEnabled = GL.GetBoolean(GetPName.Blend); - bool prevScissorTestEnabled = GL.GetBoolean(GetPName.ScissorTest); - int prevBlendEquationRgb = GL.GetInteger(GetPName.BlendEquationRgb); - int prevBlendEquationAlpha = GL.GetInteger(GetPName.BlendEquationAlpha); - int prevBlendFuncSrcRgb = GL.GetInteger(GetPName.BlendSrcRgb); - int prevBlendFuncSrcAlpha = GL.GetInteger(GetPName.BlendSrcAlpha); - int prevBlendFuncDstRgb = GL.GetInteger(GetPName.BlendDstRgb); - int prevBlendFuncDstAlpha = GL.GetInteger(GetPName.BlendDstAlpha); - bool prevCullFaceEnabled = GL.GetBoolean(GetPName.CullFace); - bool prevDepthTestEnabled = GL.GetBoolean(GetPName.DepthTest); - int prevActiveTexture = GL.GetInteger(GetPName.ActiveTexture); - GL.ActiveTexture(TextureUnit.Texture0); - int prevTexture2D = GL.GetInteger(GetPName.TextureBinding2D); - Span prevScissorBox = stackalloc int[4]; - unsafe - { - fixed (int* iptr = &prevScissorBox[0]) - { - GL.GetInteger(GetPName.ScissorBox, iptr); - } - } - - // Bind the element buffer (thru the VAO) so that we can resize it. - GL.BindVertexArray(_vertexArray); - // Bind the vertex buffer so that we can resize it. - GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBuffer); - for (int i = 0; i < draw_data.CmdListsCount; i++) - { - ImDrawListPtr cmd_list = draw_data.CmdLists[i]; - - int vertexSize = cmd_list.VtxBuffer.Size * Marshal.SizeOf(); - if (vertexSize > _vertexBufferSize) - { - int newSize = (int)Math.Max(_vertexBufferSize * 1.5f, vertexSize); - - GL.BufferData(BufferTarget.ArrayBuffer, newSize, IntPtr.Zero, BufferUsageHint.DynamicDraw); - _vertexBufferSize = newSize; - } - - int indexSize = cmd_list.IdxBuffer.Size * sizeof(ushort); - if (indexSize > _indexBufferSize) - { - int newSize = (int)Math.Max(_indexBufferSize * 1.5f, indexSize); - GL.BufferData(BufferTarget.ElementArrayBuffer, newSize, IntPtr.Zero, BufferUsageHint.DynamicDraw); - _indexBufferSize = newSize; - } - } - - // Setup orthographic projection matrix into our constant buffer - ImGuiIOPtr io = ImGui.GetIO(); - var mvp = OpenTK.Mathematics.Matrix4.CreateOrthographicOffCenter( - 0.0f, - io.DisplaySize.X, - io.DisplaySize.Y, - 0.0f, - -1.0f, - 1.0f); - - GL.UseProgram(_shader); - GL.UniformMatrix4(_shaderProjectionMatrixLocation, false, ref mvp); - GL.Uniform1(_shaderFontTextureLocation, 0); - CheckGLError("Projection"); - - GL.BindVertexArray(_vertexArray); - CheckGLError("VAO"); - - draw_data.ScaleClipRects(io.DisplayFramebufferScale); - - GL.Enable(EnableCap.Blend); - GL.Enable(EnableCap.ScissorTest); - GL.BlendEquation(BlendEquationMode.FuncAdd); - GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); - GL.Disable(EnableCap.CullFace); - GL.Disable(EnableCap.DepthTest); - - // Render command lists - for (int n = 0; n < draw_data.CmdListsCount; n++) - { - ImDrawListPtr cmd_list = draw_data.CmdLists[n]; - - GL.BufferSubData(BufferTarget.ArrayBuffer, IntPtr.Zero, cmd_list.VtxBuffer.Size * Marshal.SizeOf(), cmd_list.VtxBuffer.Data); - CheckGLError($"Data Vert {n}"); - - GL.BufferSubData(BufferTarget.ElementArrayBuffer, IntPtr.Zero, cmd_list.IdxBuffer.Size * sizeof(ushort), cmd_list.IdxBuffer.Data); - CheckGLError($"Data Idx {n}"); - - for (int cmd_i = 0; cmd_i < cmd_list.CmdBuffer.Size; cmd_i++) - { - ImDrawCmdPtr pcmd = cmd_list.CmdBuffer[cmd_i]; - if (pcmd.UserCallback != IntPtr.Zero) - { - throw new NotImplementedException(); - } - else - { - GL.ActiveTexture(TextureUnit.Texture0); - GL.BindTexture(TextureTarget.Texture2D, (int)pcmd.TextureId); - CheckGLError("Texture"); - - // We do _windowHeight - (int)clip.W instead of (int)clip.Y because gl has flipped Y when it comes to these coordinates - var clip = pcmd.ClipRect; - GL.Scissor((int)clip.X, _windowHeight - (int)clip.W, (int)(clip.Z - clip.X), (int)(clip.W - clip.Y)); - CheckGLError("Scissor"); - - if ((io.BackendFlags & ImGuiBackendFlags.RendererHasVtxOffset) != 0) - { - GL.DrawElementsBaseVertex(PrimitiveType.Triangles, (int)pcmd.ElemCount, DrawElementsType.UnsignedShort, (IntPtr)(pcmd.IdxOffset * sizeof(ushort)), unchecked((int)pcmd.VtxOffset)); - } - else - { - GL.DrawElements(BeginMode.Triangles, (int)pcmd.ElemCount, DrawElementsType.UnsignedShort, (int)pcmd.IdxOffset * sizeof(ushort)); - } - CheckGLError("Draw"); - } - } - } - - GL.Disable(EnableCap.Blend); - GL.Disable(EnableCap.ScissorTest); - - // Reset state - GL.BindTexture(TextureTarget.Texture2D, prevTexture2D); - GL.ActiveTexture((TextureUnit)prevActiveTexture); - GL.UseProgram(prevProgram); - GL.BindVertexArray(prevVAO); - GL.Scissor(prevScissorBox[0], prevScissorBox[1], prevScissorBox[2], prevScissorBox[3]); - GL.BindBuffer(BufferTarget.ArrayBuffer, prevArrayBuffer); - GL.BlendEquationSeparate((BlendEquationMode)prevBlendEquationRgb, (BlendEquationMode)prevBlendEquationAlpha); - GL.BlendFuncSeparate( - (BlendingFactorSrc)prevBlendFuncSrcRgb, - (BlendingFactorDest)prevBlendFuncDstRgb, - (BlendingFactorSrc)prevBlendFuncSrcAlpha, - (BlendingFactorDest)prevBlendFuncDstAlpha); - if (prevBlendEnabled) GL.Enable(EnableCap.Blend); else GL.Disable(EnableCap.Blend); - if (prevDepthTestEnabled) GL.Enable(EnableCap.DepthTest); else GL.Disable(EnableCap.DepthTest); - if (prevCullFaceEnabled) GL.Enable(EnableCap.CullFace); else GL.Disable(EnableCap.CullFace); - if (prevScissorTestEnabled) GL.Enable(EnableCap.ScissorTest); else GL.Disable(EnableCap.ScissorTest); - } - - /// - /// Frees all graphics resources used by the renderer. - /// - public void Dispose() - { - GL.DeleteVertexArray(_vertexArray); - GL.DeleteBuffer(_vertexBuffer); - GL.DeleteBuffer(_indexBuffer); - - GL.DeleteTexture(_fontTexture); - GL.DeleteProgram(_shader); - } - - public static void LabelObject(ObjectLabelIdentifier objLabelIdent, int glObject, string name) - { - if (KHRDebugAvailable) - GL.ObjectLabel(objLabelIdent, glObject, name.Length, name); - } - - static bool IsExtensionSupported(string name) - { - int n = GL.GetInteger(GetPName.NumExtensions); - for (int i = 0; i < n; i++) - { - string extension = GL.GetString(StringNameIndexed.Extensions, i); - if (extension == name) return true; - } - - return false; - } - - public static int CreateProgram(string name, string vertexSource, string fragmentSoruce) - { - int program = GL.CreateProgram(); - LabelObject(ObjectLabelIdentifier.Program, program, $"Program: {name}"); - - int vertex = CompileShader(name, ShaderType.VertexShader, vertexSource); - int fragment = CompileShader(name, ShaderType.FragmentShader, fragmentSoruce); - - GL.AttachShader(program, vertex); - GL.AttachShader(program, fragment); - - GL.LinkProgram(program); - - GL.GetProgram(program, GetProgramParameterName.LinkStatus, out int success); - if (success == 0) - { - string info = GL.GetProgramInfoLog(program); - Debug.WriteLine($"GL.LinkProgram had info log [{name}]:\n{info}"); - } - - GL.DetachShader(program, vertex); - GL.DetachShader(program, fragment); - - GL.DeleteShader(vertex); - GL.DeleteShader(fragment); - - return program; - } - - private static int CompileShader(string name, ShaderType type, string source) - { - int shader = GL.CreateShader(type); - LabelObject(ObjectLabelIdentifier.Shader, shader, $"Shader: {name}"); - - GL.ShaderSource(shader, source); - GL.CompileShader(shader); - - GL.GetShader(shader, ShaderParameter.CompileStatus, out int success); - if (success == 0) - { - string info = GL.GetShaderInfoLog(shader); - Debug.WriteLine($"GL.CompileShader for shader '{name}' [{type}] had info log:\n{info}"); - } - - return shader; - } - - public static void CheckGLError(string title) - { - ErrorCode error; - int i = 1; - while ((error = GL.GetError()) != ErrorCode.NoError) - { - Debug.Print($"{title} ({i++}): {error}"); - } - } - - public static float GetDpiScale() - { - return Math.Max((float)(Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth), (float)(Screen.PrimaryScreen.Bounds.Height / SystemParameters.PrimaryScreenHeight)); - } - - public static ImGuiKey TranslateKey(Keys key) - { - if (key >= Keys.D0 && key <= Keys.D9) - return key - Keys.D0 + ImGuiKey._0; - - if (key >= Keys.A && key <= Keys.Z) - return key - Keys.A + ImGuiKey.A; - - if (key >= Keys.KeyPad0 && key <= Keys.KeyPad9) - return key - Keys.KeyPad0 + ImGuiKey.Keypad0; - - if (key >= Keys.F1 && key <= Keys.F24) - return key - Keys.F1 + ImGuiKey.F24; - - return key switch - { - Keys.Tab => ImGuiKey.Tab, - Keys.Left => ImGuiKey.LeftArrow, - Keys.Right => ImGuiKey.RightArrow, - Keys.Up => ImGuiKey.UpArrow, - Keys.Down => ImGuiKey.DownArrow, - Keys.PageUp => ImGuiKey.PageUp, - Keys.PageDown => ImGuiKey.PageDown, - Keys.Home => ImGuiKey.Home, - Keys.End => ImGuiKey.End, - Keys.Insert => ImGuiKey.Insert, - Keys.Delete => ImGuiKey.Delete, - Keys.Backspace => ImGuiKey.Backspace, - Keys.Space => ImGuiKey.Space, - Keys.Enter => ImGuiKey.Enter, - Keys.Escape => ImGuiKey.Escape, - Keys.Apostrophe => ImGuiKey.Apostrophe, - Keys.Comma => ImGuiKey.Comma, - Keys.Minus => ImGuiKey.Minus, - Keys.Period => ImGuiKey.Period, - Keys.Slash => ImGuiKey.Slash, - Keys.Semicolon => ImGuiKey.Semicolon, - Keys.Equal => ImGuiKey.Equal, - Keys.LeftBracket => ImGuiKey.LeftBracket, - Keys.Backslash => ImGuiKey.Backslash, - Keys.RightBracket => ImGuiKey.RightBracket, - Keys.GraveAccent => ImGuiKey.GraveAccent, - Keys.CapsLock => ImGuiKey.CapsLock, - Keys.ScrollLock => ImGuiKey.ScrollLock, - Keys.NumLock => ImGuiKey.NumLock, - Keys.PrintScreen => ImGuiKey.PrintScreen, - Keys.Pause => ImGuiKey.Pause, - Keys.KeyPadDecimal => ImGuiKey.KeypadDecimal, - Keys.KeyPadDivide => ImGuiKey.KeypadDivide, - Keys.KeyPadMultiply => ImGuiKey.KeypadMultiply, - Keys.KeyPadSubtract => ImGuiKey.KeypadSubtract, - Keys.KeyPadAdd => ImGuiKey.KeypadAdd, - Keys.KeyPadEnter => ImGuiKey.KeypadEnter, - Keys.KeyPadEqual => ImGuiKey.KeypadEqual, - Keys.LeftShift => ImGuiKey.ModShift, - Keys.LeftControl => ImGuiKey.LeftCtrl, - Keys.LeftAlt => ImGuiKey.LeftAlt, - Keys.LeftSuper => ImGuiKey.LeftSuper, - Keys.RightShift => ImGuiKey.RightShift, - Keys.RightControl => ImGuiKey.RightCtrl, - Keys.RightAlt => ImGuiKey.RightAlt, - Keys.RightSuper => ImGuiKey.RightSuper, - Keys.Menu => ImGuiKey.Menu, - _ => ImGuiKey.None - }; - } -} diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index 2609d0a5..b860ef8a 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -123,7 +123,7 @@ public partial class MainWindow _applicationView.CUE4Parse.InitMappings(), ApplicationViewModel.InitDetex(), ApplicationViewModel.InitVgmStream(), - ApplicationViewModel.InitImGuiSettings(newOrUpdated), + // ApplicationViewModel.InitImGuiSettings(newOrUpdated), Task.Run(() => { if (UserSettings.Default.DiscordRpc == EDiscordRpc.Always) diff --git a/FModel/Settings/UserSettings.cs b/FModel/Settings/UserSettings.cs index aff57f4c..332b36da 100644 --- a/FModel/Settings/UserSettings.cs +++ b/FModel/Settings/UserSettings.cs @@ -12,7 +12,6 @@ using FModel.Extensions.Themes; using FModel.Framework; using FModel.ViewModels; using FModel.ViewModels.ApiEndpoints.Models; -using FModel.Views.Snooper; using Newtonsoft.Json; namespace FModel.Settings; @@ -564,13 +563,6 @@ public sealed class UserSettings : ViewModel set => SetProperty(ref _animateWithRotationOnly, value); } - private Camera.WorldMode _cameraMode = Camera.WorldMode.Arcball; - public Camera.WorldMode CameraMode - { - get => _cameraMode; - set => SetProperty(ref _cameraMode, value); - } - private int _previewMaxTextureSize = 1024; public int PreviewMaxTextureSize { diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 528e01fd..eeddae64 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -9,7 +9,6 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using System.Windows; using AdonisUI.Controls; using CUE4Parse; using CUE4Parse.Compression; @@ -78,11 +77,8 @@ using FModel.Services; using FModel.Settings; using FModel.Views; using FModel.Views.Resources.Controls; -using FModel.Views.Snooper; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -using OpenTK.Windowing.Common; -using OpenTK.Windowing.Desktop; using Serilog; using SkiaSharp; using Svg.Skia; @@ -116,39 +112,6 @@ public class CUE4ParseViewModel : ViewModel set => SetProperty(ref _modelIsWaitingAnimation, value); } - public bool IsSnooperOpen => _snooper is { Exists: true, IsVisible: true }; - private Snooper _snooper; - public Snooper SnooperViewer - { - get - { - if (_snooper != null) return _snooper; - - return Application.Current.Dispatcher.Invoke(delegate - { - var scale = ImGuiController.GetDpiScale(); - var htz = Snooper.GetMaxRefreshFrequency(); - return _snooper = new Snooper( - new GameWindowSettings { UpdateFrequency = htz }, - new NativeWindowSettings - { - ClientSize = new OpenTK.Mathematics.Vector2i( - Convert.ToInt32(SystemParameters.MaximizedPrimaryScreenWidth * .75 * scale), - Convert.ToInt32(SystemParameters.MaximizedPrimaryScreenHeight * .85 * scale)), - NumberOfSamples = Constants.SAMPLES_COUNT, - WindowBorder = WindowBorder.Resizable, - Flags = ContextFlags.ForwardCompatible, - Profile = ContextProfile.Core, - Vsync = VSyncMode.Adaptive, - APIVersion = new Version(4, 6), - StartVisible = false, - StartFocused = false, - Title = "3D Viewer" - }); - }); - } - } - public AbstractVfsFileProvider Provider { get; } public GameDirectoryViewModel GameDirectory { get; } public AssetsFolderViewModel AssetsFolder { get; } @@ -1569,21 +1532,22 @@ public class CUE4ParseViewModel : ViewModel pkg.Name.Contains("/RenderSwitch_Materials/", StringComparison.OrdinalIgnoreCase) || pkg.Name.Contains("/MI_BPTile/", StringComparison.OrdinalIgnoreCase))): { - if (SnooperViewer.TryLoadExport(cancellationToken, dummy, pointer.Object)) - SnooperViewer.Run(); + SnooperViewModel.Instance.Load(pointer.Object.Value); + // if (SnooperViewer.TryLoadExport(cancellationToken, dummy, pointer.Object)) + // SnooperViewer.Run(); return true; } case UMaterialInstance when isNone && ModelIsOverwritingMaterial && pointer.Object.Value is UMaterialInstance m: { - SnooperViewer.Renderer.Swap(m); - SnooperViewer.Run(); + // SnooperViewer.Renderer.Swap(m); + // SnooperViewer.Run(); return true; } case UAnimSequenceBase when isNone && UserSettings.Default.PreviewAnimations || ModelIsWaitingAnimation: { // animate all animations using their specified skeleton or when we explicitly asked for a loaded model to be animated (ignoring whether we wanted to preview animations) - SnooperViewer.Renderer.Animate(pointer.Object.Value); - SnooperViewer.Run(); + // SnooperViewer.Renderer.Animate(pointer.Object.Value); + // SnooperViewer.Run(); return true; } case UStaticMesh when HasFlag(bulk, EBulkType.Meshes): diff --git a/FModel/ViewModels/Commands/MenuCommand.cs b/FModel/ViewModels/Commands/MenuCommand.cs index 06e17a1a..24a9a478 100644 --- a/FModel/ViewModels/Commands/MenuCommand.cs +++ b/FModel/ViewModels/Commands/MenuCommand.cs @@ -38,7 +38,8 @@ public class MenuCommand : ViewModelCommand contextViewModel.CUE4Parse.TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(contextViewModel.CUE4Parse.GameDirectory.DirectoryFiles, Formatting.Indented), false, false); break; case "Views_3dViewer": - contextViewModel.CUE4Parse.SnooperViewer.Run(); + SnooperViewModel.Instance.Load(null); + SnooperViewModel.Instance.Run(); break; case "Views_ExportSession": Helper.OpenWindow("Export Session", () => new ExportSessionWindow().Show()); diff --git a/FModel/ViewModels/ExportSessionViewModel.cs b/FModel/ViewModels/ExportSessionViewModel.cs index d877fd40..f77b5a7b 100644 --- a/FModel/ViewModels/ExportSessionViewModel.cs +++ b/FModel/ViewModels/ExportSessionViewModel.cs @@ -18,7 +18,6 @@ using FModel.Framework; using FModel.Settings; using FModel.Views; using FModel.Views.Resources.Controls; -using FModel.Views.Snooper; using Serilog.Events; namespace FModel.ViewModels; @@ -145,7 +144,7 @@ public class ExportSessionViewModel : ViewModel private ExportSessionViewModel() { - ImGuiSink.Instance.OnExporterLogEvent += OnLogEvent; + // ImGuiSink.Instance.OnExporterLogEvent += OnLogEvent; } private void OnLogEvent(LogEvent log) diff --git a/FModel/ViewModels/SnooperViewModel.cs b/FModel/ViewModels/SnooperViewModel.cs new file mode 100644 index 00000000..94579486 --- /dev/null +++ b/FModel/ViewModels/SnooperViewModel.cs @@ -0,0 +1,197 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Windows; +using System.Windows.Forms; +using CUE4Parse.UE4.Assets.Exports; +using CUE4Parse.UE4.Assets.Exports.SkeletalMesh; +using CUE4Parse.UE4.Assets.Exports.StaticMesh; +using CUE4Parse.UE4.Objects.Engine; +using Editor; +using FModel.Framework; +using FModel.Services; +using OpenTK.Windowing.Desktop; +using Serilog; +using Snooper.Rendering.Actors; +using Snooper.Rendering.Components; +using Snooper.Rendering.Components.Light; +using Snooper.Rendering.Components.Primitive; +using Snooper.Rendering.Components.Transforms; + +namespace FModel.ViewModels; + +public class SnooperViewModel : ViewModel, IDisposable +{ + public static SnooperViewModel Instance { get; } = new(); + + private readonly Lazy _editor; + + private SnooperViewModel() + { + var scale = GetDpiScale(); + var htz = GetMaxRefreshFrequency(); + var width = Convert.ToInt32(SystemParameters.MaximizedPrimaryScreenWidth * .75 * scale); + var height = Convert.ToInt32(SystemParameters.MaximizedPrimaryScreenHeight * .85 * scale); + + _editor = new Lazy(() => StartEditor(htz, width, height)); + } + + private static EditorWindow StartEditor(int htz, int width, int height) + { + GLFWProvider.CheckForMainThread = false; + + var ready = new ManualResetEventSlim(); + EditorWindow? editor = null; + Exception? failure = null; + + new Thread(() => + { + try + { + editor = new EditorWindow(htz, width, height, ApplicationService.ApplicationView.CUE4Parse.Provider, false, true); + } + catch (Exception e) + { + failure = e; + return; + } + finally + { + ready.Set(); + } + + try + { + editor.Run(); + } + catch (Exception e) + { + Log.Error(e, "Snooper crashed"); + } + }) { IsBackground = true, Name = "Snooper" }.Start(); + + ready.Wait(); + return editor ?? throw new InvalidOperationException("Snooper failed to start", failure); + } + + public void Load(UObject? obj) + { + var scene = new Actor("Example Scene"); + scene.Components.Add(new BoxComponent(Vector3.Zero, Vector3.One)); + + var camera = new CameraActor("Camera"); + camera.CameraComponent.LocalTransform.Position = new Vector3(1, 2, -0.5f); + camera.CameraComponent.LocalTransform.Rotation = new Quaternion(0, -1, 0, 1); + scene.Children.Add(camera); + + var sun = new Actor("Sun Light"); + sun.Components.Add(new DirectionalLightComponent(MathF.PI, new Vector3(1.0f, 0.87f, 0.72f), new Transform(new Quaternion(new Vector3(0.5f, -0.5f, 0.0f), 1.0f)), "Directional Light")); + scene.Children.Add(sun); + + GridComponent gridComponent = new OpaqueGridComponent(); + switch (obj) + { + case UStaticMesh sm: + scene.Children.Add(new MeshActor(sm)); + break; + case USkeletalMesh sk: + scene.Children.Add(new MeshActor(sk)); + break; + case UWorld w: + gridComponent = new GridComponent(); + scene.Children.Add(new WorldActor(w)); + break; + } + var grid = new Actor("Grid"); + grid.Components.Add(gridComponent); + scene.Children.Insert(0, grid); + + var editor = _editor.Value; + editor.Invoke(() => + { + editor.Manager.LoadScene(scene); + editor.Show(); + }); + } + + public void Run() + { + var editor = _editor.Value; + editor.Invoke(editor.Show); + } + + [DllImport("user32.dll")] + private static extern bool EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE devMode); + + [StructLayout(LayoutKind.Sequential)] + private struct DEVMODE + { + private const int CCHDEVICENAME = 0x20; + private const int CCHFORMNAME = 0x20; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)] + public string dmDeviceName; + public short dmSpecVersion; + public short dmDriverVersion; + public short dmSize; + public short dmDriverExtra; + public int dmFields; + public int dmPositionX; + public int dmPositionY; + public ScreenOrientation dmDisplayOrientation; + public int dmDisplayFixedOutput; + public short dmColor; + public short dmDuplex; + public short dmYResolution; + public short dmTTOption; + public short dmCollate; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)] + public string dmFormName; + public short dmLogPixels; + public int dmBitsPerPel; + public int dmPelsWidth; + public int dmPelsHeight; + public int dmDisplayFlags; + public int dmDisplayFrequency; + public int dmICMMethod; + public int dmICMIntent; + public int dmMediaType; + public int dmDitherType; + public int dmReserved1; + public int dmReserved2; + public int dmPanningWidth; + public int dmPanningHeight; + + } + + private static float GetDpiScale() + { + if (Screen.PrimaryScreen is not { } primaryScreen) + return 1.0f; + + return (float)Math.Max( + primaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth, + primaryScreen.Bounds.Height / SystemParameters.PrimaryScreenHeight + ); + } + + private static int GetMaxRefreshFrequency() + { + var rf = 60; + var vDevMode = new DEVMODE(); + var i = 0; + while (EnumDisplaySettings(null, i, ref vDevMode)) + { + i++; + rf = Math.Max(rf, vDevMode.dmDisplayFrequency); + } + + return rf; + } + + public void Dispose() + { + if (!_editor.IsValueCreated) return; + _editor.Value.Dispose(); + } +} diff --git a/FModel/ViewModels/ThreadWorkerViewModel.cs b/FModel/ViewModels/ThreadWorkerViewModel.cs index 59c49f19..a310a3c4 100644 --- a/FModel/ViewModels/ThreadWorkerViewModel.cs +++ b/FModel/ViewModels/ThreadWorkerViewModel.cs @@ -49,9 +49,9 @@ public class ThreadWorkerViewModel : ViewModel public async Task Begin(Action action) { - if (_applicationView.CUE4Parse.IsSnooperOpen) + /*if (_applicationView.CUE4Parse.IsSnooperOpen) _applicationView.CUE4Parse.SnooperViewer.Close(); - else if (!_applicationView.Status.IsReady) + else */if (!_applicationView.Status.IsReady) { SignalOperationInProgress(); return; @@ -88,8 +88,8 @@ public class ThreadWorkerViewModel : ViewModel catch (OperationCanceledException) { _applicationView.Status.SetStatus(EStatusKind.Stopped); - if (_applicationView.CUE4Parse.IsSnooperOpen) - _applicationView.CUE4Parse.SnooperViewer.Close(); + // if (_applicationView.CUE4Parse.IsSnooperOpen) + // _applicationView.CUE4Parse.SnooperViewer.Close(); CurrentCancellationTokenSource = null; // kill token OperationCancelled = true; OperationCancelled = false; diff --git a/FModel/Views/Snooper/Animations/Animation.cs b/FModel/Views/Snooper/Animations/Animation.cs deleted file mode 100644 index 561a9121..00000000 --- a/FModel/Views/Snooper/Animations/Animation.cs +++ /dev/null @@ -1,148 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Numerics; -using CUE4Parse_Conversion; -using CUE4Parse_Conversion.Writers.ActorX.Structs.Animations; -using CUE4Parse.UE4.Assets.Exports; -using CUE4Parse.UE4.Objects.Core.Misc; -using FModel.Settings; -using FModel.Views.Snooper.Models; -using ImGuiNET; - -namespace FModel.Views.Snooper.Animations; - -public class Animation : IExportableThing, IDisposable -{ - private readonly UObject _export; - - public readonly CAnimSet UnrealAnim; - public readonly string Path; - public readonly string Name; - public readonly Sequence[] Sequences; - public readonly float StartTime; // Animation Start Time - public readonly float EndTime; // Animation End Time - public readonly float TotalElapsedTime; // Animation Max Time - public readonly Dictionary Framing; - - public bool IsActive; - public bool IsSelected; - - public readonly List AttachedModels; - - public Animation(UObject export) - { - _export = export; - Path = _export.GetPathName(); - Name = _export.Name; - Sequences = []; - Framing = new Dictionary(); - AttachedModels = []; - } - - public Animation(UObject export, CAnimSet animSet) : this(export) - { - UnrealAnim = animSet; - - Sequences = new Sequence[UnrealAnim.Sequences.Count]; - for (int i = 0; i < Sequences.Length; i++) - { - Sequences[i] = new Sequence(UnrealAnim.Sequences[i]); - EndTime = Sequences[i].EndTime; - } - - TotalElapsedTime = EndTime; - if (Sequences.Length > 0) - StartTime = Sequences[0].StartTime; - } - - public Animation(UObject export, CAnimSet animSet, params FGuid[] animatedModels) : this(export, animSet) - { - AttachedModels.AddRange(animatedModels); - } - - public void TimeCalculation(float elapsedTime) - { - for (int i = 0; i < Sequences.Length; i++) - { - var sequence = Sequences[i]; - if (elapsedTime <= sequence.EndTime && elapsedTime >= sequence.StartTime) - { - Framing[i] = (elapsedTime - sequence.StartTime) * sequence.RateScale / sequence.SecondsPerFrame; - } - else Framing.Remove(i); - } - - if (elapsedTime >= TotalElapsedTime) - Framing.Clear(); - } - - public void Dispose() - { - AttachedModels.Clear(); - } - - public void ImGuiAnimation(Snooper s, ImDrawListPtr drawList, ImFontPtr fontPtr, Vector2 timelineP0, Vector2 treeP0, Vector2 timeStep, Vector2 timeRatio, float y, float t, int i) - { - var name = $"{Name}##{i}"; - var p1 = new Vector2(timelineP0.X + StartTime * timeRatio.X + t, y + t); - var p2 = new Vector2(timelineP0.X + EndTime * timeRatio.X - t, y + timeStep.Y - t); - - ImGui.SetCursorScreenPos(p1); - ImGui.InvisibleButton($"timeline_sequencetracker_{name}", new Vector2(EndTime * timeRatio.X - t, timeStep.Y - t), ImGuiButtonFlags.MouseButtonLeft); - IsActive = ImGui.IsItemActive(); - IsSelected = s.Renderer.Options.SelectedAnimation == i; - if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) - { - s.Renderer.Options.SelectAnimation(i); - } - Popup(s, i); - - drawList.AddRectFilled(p1, p2, IsSelected ? 0xFF48B048 : 0xFF175F17, 5.0f, ImDrawFlags.RoundCornersTop); - for (int j = 0; j < Sequences.Length; j++) - { - Sequences[j].DrawSequence(drawList, fontPtr, timelineP0.X, p2, timeStep, timeRatio, t, IsSelected); - } - - ImGui.SetCursorScreenPos(treeP0 with { Y = p1.Y }); - if (ImGui.Selectable(name, s.Renderer.Options.SelectedAnimation == i, ImGuiSelectableFlags.SpanAllColumns, new Vector2(p1.X - treeP0.X, timeStep.Y - t - t))) - { - s.Renderer.Options.SelectAnimation(i); - } - Popup(s, i); - } - - private void Popup(Snooper s, int i) - { - SnimGui.Popup(() => - { - s.Renderer.Options.SelectAnimation(i); - if (ImGui.BeginMenu("Animate")) - { - foreach ((var guid, var model) in s.Renderer.Options.Models) - { - var selected = AttachedModels.Contains(guid); - if (model is SkeletalModel skeletalModel && ImGui.MenuItem(model.Name, null, selected, !skeletalModel.Skeleton.IsAnimated || selected)) - { - if (selected) AttachedModels.Remove(guid); else AttachedModels.Add(guid); - skeletalModel.Skeleton.ResetAnimatedData(true); - if (!selected) skeletalModel.Skeleton.Animate(UnrealAnim); - } - } - ImGui.EndMenu(); - } - if (ImGui.MenuItem("Save")) - { - s.WindowShouldFreeze(true); - ExportModal.Instance.Export([this], UserSettings.Default.ModelDirectory, UserSettings.GetExportOptions()); - s.WindowShouldFreeze(false); - } - ImGui.Separator(); - if (ImGui.MenuItem("Copy Path to Clipboard")) ImGui.SetClipboardText(Path); - }); - } - - public void AddToExportSession(ExportSession session) - { - session.Add(_export); - } -} diff --git a/FModel/Views/Snooper/Animations/Bone.cs b/FModel/Views/Snooper/Animations/Bone.cs deleted file mode 100644 index cb0d4dec..00000000 --- a/FModel/Views/Snooper/Animations/Bone.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; - -namespace FModel.Views.Snooper.Animations; - -public class Bone -{ - public readonly int Index; - public int ParentIndex; - public readonly Transform Rest; - public readonly bool IsVirtual; - - public string LoweredParentName; - public List LoweredChildNames; - - public int SkeletonIndex = -1; - public readonly List AnimatedBySequences; - - public Bone(int i, int p, Transform t, bool isVirtual = false) - { - Index = i; - ParentIndex = p; - Rest = t; - IsVirtual = isVirtual; - - LoweredChildNames = new List(); - AnimatedBySequences = new List(); - } - - public bool IsRoot => Index == 0 && ParentIndex == -1 && string.IsNullOrEmpty(LoweredParentName); - public bool IsDaron => LoweredChildNames.Count > 0; - public bool IsMapped => SkeletonIndex > -1; - public bool IsAnimated => AnimatedBySequences.Count > 0; - public bool IsNative => Index == SkeletonIndex; - public uint Color => !IsMapped || !IsAnimated ? 0xFFA0A0A0 : 0xFF48B048; - - public override string ToString() => $"Mesh Ref '{Index}' is Skel Ref '{SkeletonIndex}'"; -} diff --git a/FModel/Views/Snooper/Animations/Sequence.cs b/FModel/Views/Snooper/Animations/Sequence.cs deleted file mode 100644 index 04bbf09c..00000000 --- a/FModel/Views/Snooper/Animations/Sequence.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Numerics; -using CUE4Parse_Conversion.Writers.ActorX.Structs.Animations; -using ImGuiNET; - -namespace FModel.Views.Snooper.Animations; - -public class Sequence -{ - public readonly string Name; - public readonly float RateScale; - public readonly float StartTime; - public readonly float Duration; - public readonly float EndTime; - public readonly int EndFrame; - public readonly int LoopingCount; - public readonly bool IsAdditive; - public readonly float SecondsPerFrame; - - public Sequence(CAnimSequence sequence) - { - Name = sequence.Name; - RateScale = sequence.OriginalSequence.RateScale; - StartTime = sequence.StartPos; - Duration = sequence.AnimEndTime; - EndTime = StartTime + Duration / RateScale; - EndFrame = sequence.NumFrames; - LoopingCount = sequence.LoopingCount; - IsAdditive = sequence.IsAdditive; - SecondsPerFrame = Duration / EndFrame; - } - - public void DrawSequence(ImDrawListPtr drawList, ImFontPtr fontPtr, float x, Vector2 p2, Vector2 timeStep, Vector2 timeRatio, float t, bool animSelected) - { - var halfThickness = t / 2.0f; - var q1 = new Vector2(x + StartTime * timeRatio.X + t + halfThickness, p2.Y - timeStep.Y / 2.0f); - var q2 = p2 with { X = x + EndTime * timeRatio.X - t * 2.0f }; - - drawList.PushClipRect(q1, q2 with { X = q2.X + t }, true); - - var lineColor = animSelected ? 0xA0FFFFFF : 0x50FFFFFF; - drawList.AddLine(new Vector2(q1.X, q2.Y), q1, lineColor, 1.0f); - drawList.AddLine(q1, new Vector2(q2.X, q1.Y), lineColor, 1.0f); - drawList.AddLine(new Vector2(q2.X, q1.Y), q2, lineColor, 1.0f); - - if (IsAdditive) - drawList.AddText(fontPtr, 12 * ImGui.GetWindowDpiScale(), new Vector2(q1.X + t, q1.Y + halfThickness), animSelected ? 0xFFFFFFFF : 0x50FFFFFF, "Is Additive"); - - drawList.PopClipRect(); - } -} diff --git a/FModel/Views/Snooper/Animations/Skeleton.cs b/FModel/Views/Snooper/Animations/Skeleton.cs deleted file mode 100644 index e35d0530..00000000 --- a/FModel/Views/Snooper/Animations/Skeleton.cs +++ /dev/null @@ -1,393 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using CUE4Parse_Conversion.Writers.ActorX.Structs.Animations; -using CUE4Parse.UE4.Assets.Exports.Animation; -using CUE4Parse.UE4.Objects.Core.Math; -using CUE4Parse.UE4.Objects.Core.Misc; -using FModel.Views.Snooper.Buffers; -using FModel.Views.Snooper.Shading; -using ImGuiNET; -using OpenTK.Graphics.OpenGL4; -using Serilog; - -namespace FModel.Views.Snooper.Animations; - -public class Skeleton : IDisposable -{ - private int _handle; - private BufferObject _rest; - private BufferObject _ssbo; - private Matrix4x4[] _boneMatriceAtFrame; - private readonly List _breadcrumb; - - public string Name; - public FGuid Guid; - public readonly Dictionary BonesByLoweredName; - - public readonly int BoneCount; - private int _additionalBoneCount; - private int TotalBoneCount => BoneCount + _additionalBoneCount; - - public bool IsAnimated { get; private set; } - public string SelectedBone; - - private const int _vertexSize = 12; - private BufferObject _vbo; - private int _vaoHandle; - - public Skeleton() - { - BonesByLoweredName = new Dictionary(); - _breadcrumb = new List(); - } - - public Skeleton(FReferenceSkeleton referenceSkeleton) : this() - { - BoneCount = referenceSkeleton.FinalRefBoneInfo.Length; - for (int boneIndex = 0; boneIndex < BoneCount; boneIndex++) - { - var info = referenceSkeleton.FinalRefBoneInfo[boneIndex]; - var boneName = info.Name.Text.ToLower(); - var bone = new Bone(boneIndex, info.ParentIndex, new Transform - { - Rotation = referenceSkeleton.FinalRefBonePose[boneIndex].Rotation, - Position = referenceSkeleton.FinalRefBonePose[boneIndex].Translation * Constants.SCALE_DOWN_RATIO, - Scale = referenceSkeleton.FinalRefBonePose[boneIndex].Scale3D - }); - - if (!bone.IsRoot) - { - bone.LoweredParentName = referenceSkeleton.FinalRefBoneInfo[bone.ParentIndex].Name.Text.ToLower(); - var parentBone = BonesByLoweredName[bone.LoweredParentName]; - - bone.Rest.Relation = parentBone.Rest.Matrix; - parentBone.LoweredChildNames.Add(boneName); - } - else bone.Rest.Scale = FVector.OneVector; - - if (boneIndex == 0) SelectedBone = boneName; - BonesByLoweredName[boneName] = bone; - } - _breadcrumb.Add(SelectedBone); - _boneMatriceAtFrame = new Matrix4x4[BoneCount]; - } - - public void Merge(FReferenceSkeleton referenceSkeleton) - { - for (int boneIndex = 0; boneIndex < referenceSkeleton.FinalRefBoneInfo.Length; boneIndex++) - { - var info = referenceSkeleton.FinalRefBoneInfo[boneIndex]; - var boneName = info.Name.Text.ToLower(); - - if (!BonesByLoweredName.TryGetValue(boneName, out var bone)) - { - bone = new Bone(BoneCount + _additionalBoneCount, -1, new Transform - { - Rotation = referenceSkeleton.FinalRefBonePose[boneIndex].Rotation, - Position = referenceSkeleton.FinalRefBonePose[boneIndex].Translation * Constants.SCALE_DOWN_RATIO, - Scale = referenceSkeleton.FinalRefBonePose[boneIndex].Scale3D - }, true); - - if (!bone.IsRoot) - { - bone.LoweredParentName = referenceSkeleton.FinalRefBoneInfo[info.ParentIndex].Name.Text.ToLower(); - var parentBone = BonesByLoweredName[bone.LoweredParentName]; - - bone.ParentIndex = parentBone.Index; - bone.Rest.Relation = parentBone.Rest.Matrix; - parentBone.LoweredChildNames.Add(boneName); - } - - BonesByLoweredName[boneName] = bone; - _additionalBoneCount++; - } - } - _boneMatriceAtFrame = new Matrix4x4[TotalBoneCount]; - } - - public void Setup() - { - _handle = GL.CreateProgram(); - - _vaoHandle = GL.GenVertexArray(); - GL.BindVertexArray(_vaoHandle); - - _vbo = new BufferObject(_vertexSize * BoneCount, BufferTarget.ArrayBuffer); - - var sf = sizeof(float); - var half = _vertexSize / 2; - GL.EnableVertexAttribArray(0); - GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, sf * half, sf * 0); - GL.EnableVertexAttribArray(1); - GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, sf * half, sf * 3); - - GL.BindVertexArray(0); - - _rest = new BufferObject(BoneCount, BufferTarget.ShaderStorageBuffer); - foreach (var bone in BonesByLoweredName.Values) - { - if (bone.IsVirtual) break; - _rest.Update(bone.Index, bone.Rest.Matrix); - } - _rest.Unbind(); - - _ssbo = new BufferObject(TotalBoneCount, BufferTarget.ShaderStorageBuffer); - _ssbo.UpdateRange(Matrix4x4.Identity); - } - - public void Animate(CAnimSet animation) - { - IsAnimated = true; - ResetAnimatedData(); - - // map bones - for (int boneIndex = 0; boneIndex < animation.Skeleton.BoneCount; boneIndex++) - { - var info = animation.Skeleton.ReferenceSkeleton.FinalRefBoneInfo[boneIndex]; - if (!BonesByLoweredName.TryGetValue(info.Name.Text.ToLower(), out var bone)) - continue; - - bone.SkeletonIndex = boneIndex; - } - - // find playable sequences - for (int s = 0; s < animation.Sequences.Count; s++) - { - var sequence = animation.Sequences[s]; - foreach (var bone in BonesByLoweredName.Values.Where(bone => sequence.OriginalSequence.FindTrackForBoneIndex(bone.SkeletonIndex) >= 0)) - { - bone.AnimatedBySequences.Add(s); - } - sequence.RetargetTracks(animation.Skeleton); - } - -#if DEBUG - foreach ((var boneName, var bone) in BonesByLoweredName) - { - if (bone.IsRoot || bone.IsMapped) // assuming root bone always is mapped - continue; - - Log.Warning($"{Name} Bone Mismatch: {boneName} ({bone.Index}) was not present in the anim's target skeleton"); - } -#endif - } - - public void ResetAnimatedData(bool full = false) - { - foreach (var bone in BonesByLoweredName.Values) - { - bone.SkeletonIndex = -1; - bone.AnimatedBySequences.Clear(); - } - - if (!full) return; - IsAnimated = false; - _ssbo.UpdateRange(Matrix4x4.Identity); - } - - public void UpdateAnimationMatrices(Animation animation, bool rotationOnly) - { - if (!IsAnimated) return; - - _ssbo.Bind(); - foreach (var bone in BonesByLoweredName.Values) - { - var boneMatrix = bone.IsRoot ? bone.Rest.Relation : bone.Rest.LocalMatrix * _boneMatriceAtFrame[bone.ParentIndex]; - if (bone.IsAnimated) - { - var (s, f) = GetBoneFrameData(bone, animation); - var sequence = animation.UnrealAnim.Sequences[s]; - var boneOrientation = bone.Rest.Rotation; - var bonePosition = bone.Rest.Position; - var boneScale = bone.Rest.Scale; - - sequence.Tracks[bone.SkeletonIndex].GetBoneTransform(f, sequence.NumFrames, ref boneOrientation, ref bonePosition, ref boneScale); - if (!bone.IsRoot) boneMatrix = _boneMatriceAtFrame[bone.ParentIndex]; - else boneScale = bone.Rest.Scale; - bonePosition = rotationOnly ? bone.Rest.Position : bonePosition * Constants.SCALE_DOWN_RATIO; - - boneMatrix = new Transform - { - Relation = boneMatrix, - Rotation = boneOrientation, - Position = bonePosition, - Scale = boneScale - }.Matrix; - } - - _ssbo.Update(bone.Index, boneMatrix); - _boneMatriceAtFrame[bone.Index] = boneMatrix; - } - _ssbo.Unbind(); - } - - public void UpdateVertices() - { - _vbo.Bind(); - foreach (var (boneName, bone) in BonesByLoweredName) - { - var boneMatrix = IsAnimated ? _boneMatriceAtFrame[bone.Index] : bone.Rest.Matrix; - var parentBoneMatrix = bone.IsRoot ? boneMatrix : - IsAnimated ? _boneMatriceAtFrame[bone.ParentIndex] : - BonesByLoweredName[bone.LoweredParentName].Rest.Matrix; - - var count = 0; - var baseIndex = bone.Index * _vertexSize; - _vbo.Update(baseIndex + count++, boneMatrix.Translation.X); - _vbo.Update(baseIndex + count++, boneMatrix.Translation.Y); - _vbo.Update(baseIndex + count++, boneMatrix.Translation.Z); - _vbo.Update(baseIndex + count++, 1.0f); - _vbo.Update(baseIndex + count++, boneName == SelectedBone ? 0.0f : 1.0f); - _vbo.Update(baseIndex + count++, boneName == SelectedBone ? 0.0f : 1.0f); - _vbo.Update(baseIndex + count++, parentBoneMatrix.Translation.X); - _vbo.Update(baseIndex + count++, parentBoneMatrix.Translation.Y); - _vbo.Update(baseIndex + count++, parentBoneMatrix.Translation.Z); - _vbo.Update(baseIndex + count++, 1.0f); - _vbo.Update(baseIndex + count++, bone.LoweredParentName == SelectedBone ? 0.0f : 1.0f); - _vbo.Update(baseIndex + count++, bone.LoweredParentName == SelectedBone ? 0.0f : 1.0f); - } - _vbo.Unbind(); - } - - private (int, float) GetBoneFrameData(Bone bone, Animation animation) - { - int s = -1; - float f = 0.0f; - - void Get(Bone b) - { - foreach (var i in b.AnimatedBySequences) - { - s = i; - if (animation.Framing.TryGetValue(s, out f)) - break; - } - } - - Get(bone); - if (s == -1) - { - var parent = BonesByLoweredName[bone.LoweredParentName]; - while (!parent.IsAnimated) - { - parent = BonesByLoweredName[parent.LoweredParentName]; - } - Get(parent); - } - - return (s, f); - } - - public Matrix4x4 GetBoneMatrix(Bone bone) => IsAnimated ? _boneMatriceAtFrame[bone.Index] : bone.Rest.Matrix; - - public void Render(Shader shader) - { - shader.SetUniform("uIsAnimated", IsAnimated); - - _ssbo.BindBufferBase(1); - _rest.BindBufferBase(2); - } - - public void RenderBones() - { - GL.Disable(EnableCap.DepthTest); - - GL.BindVertexArray(_vaoHandle); - GL.DrawArrays(PrimitiveType.Lines, 0, _vbo.Size); - GL.DrawArrays(PrimitiveType.Points, 0, _vbo.Size); - GL.BindVertexArray(0); - - GL.Enable(EnableCap.DepthTest); - } - - public void ImGuiBoneBreadcrumb() - { - var p1 = ImGui.GetCursorScreenPos(); - var canvasSize = ImGui.GetContentRegionAvail() with { Y = 20 }; - var p2 = p1 + canvasSize; - ImGui.BeginChild("skeleton_breadcrumb", canvasSize); - - var drawList = ImGui.GetWindowDrawList(); - drawList.AddRectFilled(p1, p2, 0xFF242424); - - var x = p1.X; - var y = p1.Y + (p2.Y - p1.Y) / 2; - for (int i = Math.Min(_breadcrumb.Count - 1, 5); i >= 0; i--) - { - var boneName = _breadcrumb[i]; - var size = ImGui.CalcTextSize(boneName); - var position = new Vector2(x + 5, y - size.Y / 2f); - - ImGui.SetCursorScreenPos(position); - if (ImGui.InvisibleButton($"breakfast_{boneName}", size, ImGuiButtonFlags.MouseButtonLeft)) - { - SelectedBone = boneName; - _breadcrumb.RemoveRange(0, i); - break; - } - - drawList.AddText(position, i == 0 || ImGui.IsItemHovered() ? 0xFFFFFFFF : 0xA0FFFFFF, boneName); - x += size.X + 7.5f; - drawList.AddText(position with { X = x }, 0xA0FFFFFF, ">"); - x += 7.5f; - } - - ImGui.EndChild(); - } - - public void ImGuiBoneHierarchy() - { - foreach (var name in BonesByLoweredName[SelectedBone].LoweredChildNames) - { - DrawBoneTree(name, BonesByLoweredName[name]); - } - } - - private void DrawBoneTree(string boneName, Bone bone) - { - ImGui.PushID(bone.Index); - ImGui.TableNextRow(); - ImGui.TableNextColumn(); - - var flags = ImGuiTreeNodeFlags.OpenOnArrow | ImGuiTreeNodeFlags.SpanFullWidth; - if (bone.IsVirtual) flags |= ImGuiTreeNodeFlags.Leaf; - else if (!bone.IsDaron) flags |= ImGuiTreeNodeFlags.Bullet; - - ImGui.SetNextItemOpen(bone.LoweredChildNames.Count <= 1, ImGuiCond.Appearing); - var open = ImGui.TreeNodeEx(boneName, flags); - if (ImGui.IsItemClicked() && !ImGui.IsItemToggledOpen() && bone.IsDaron) - { - SelectedBone = boneName; - _breadcrumb.Clear(); - do - { - _breadcrumb.Add(boneName); - boneName = BonesByLoweredName[boneName].LoweredParentName; - } while (boneName != null); - } - - ImGui.TableNextColumn(); - ImGui.TextColored(ImGui.ColorConvertU32ToFloat4(bone.Color), bone.SkeletonIndex.ToString()); - - if (open) - { - foreach (var name in bone.LoweredChildNames) - { - DrawBoneTree(name, BonesByLoweredName[name]); - } - ImGui.TreePop(); - } - ImGui.PopID(); - } - - public void Dispose() - { - BonesByLoweredName.Clear(); - - _rest?.Dispose(); - _ssbo?.Dispose(); - GL.DeleteProgram(_handle); - } -} diff --git a/FModel/Views/Snooper/Animations/TimeTracker.cs b/FModel/Views/Snooper/Animations/TimeTracker.cs deleted file mode 100644 index f482599d..00000000 --- a/FModel/Views/Snooper/Animations/TimeTracker.cs +++ /dev/null @@ -1,226 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Numerics; -using FModel.Views.Snooper.Shading; -using ImGuiNET; - -namespace FModel.Views.Snooper.Animations; - -public enum ETrackerType -{ - Start, - Frame, - InBetween, - End -} - -public class TimeTracker : IDisposable -{ - public bool IsPaused; - public bool IsActive; - public float ElapsedTime; - public float MaxElapsedTime; - public float TimeMultiplier; - - public TimeTracker() - { - Reset(); - } - - public void Update(float deltaSeconds) - { - if (IsPaused || IsActive) return; - ElapsedTime += deltaSeconds * TimeMultiplier; - if (ElapsedTime >= MaxElapsedTime) Reset(false); - } - - public void SafeSetElapsedTime(float elapsedTime) - { - ElapsedTime = Math.Clamp(elapsedTime, 0.0f, MaxElapsedTime); - } - - public void SafeSetMaxElapsedTime(float maxElapsedTime) - { - MaxElapsedTime = MathF.Max(maxElapsedTime, MaxElapsedTime); - } - - public void Reset(bool doMet = true) - { - IsPaused = false; - ElapsedTime = 0.0f; - if (doMet) - { - MaxElapsedTime = 0.01f; - TimeMultiplier = 1f; - } - } - - public void Dispose() - { - Reset(); - } - - private readonly string[] _icons = { "tl_forward", "tl_pause", "tl_rewind" }; - public void ImGuiTimeline(Snooper s, Dictionary icons, List animations, Vector2 outliner, ImFontPtr fontPtr) - { - var dpiScale = ImGui.GetWindowDpiScale(); - var thickness = 2.0f * dpiScale; - var buttonWidth = 14.0f * dpiScale; - var timeHeight = 10.0f * dpiScale; - var timeBarHeight = timeHeight * 2.0f; - var timeStep = new Vector2(50 * dpiScale, 25 * dpiScale); - - var treeP0 = ImGui.GetCursorScreenPos(); - var canvasSize = ImGui.GetContentRegionAvail(); - var canvasMaxY = MathF.Max(canvasSize.Y, timeBarHeight + timeStep.Y * animations.Count); - ImGui.BeginChild("timeline_child", canvasSize with { Y = canvasMaxY }); - - var timelineP1 = new Vector2(treeP0.X + canvasSize.X, treeP0.Y + canvasMaxY); - var treeP1 = timelineP1 with { X = treeP0.X + outliner.X }; - - var timelineP0 = treeP0 with { X = treeP1.X + thickness }; - var timelineSize = timelineP1 - timelineP0; - var timeRatio = timelineSize / MaxElapsedTime; - - var drawList = ImGui.GetWindowDrawList(); - drawList.PushClipRect(treeP0, timelineP1, true); - drawList.AddRectFilled(treeP0, treeP1, 0xFF1F1C1C); - drawList.AddRectFilled(timelineP0, timelineP1 with { Y = timelineP0.Y + timeBarHeight }, 0xFF141414); - drawList.AddRectFilled(timelineP0 with { Y = timelineP0.Y + timeBarHeight }, timelineP1, 0xFF242424); - drawList.AddLine(new Vector2(treeP1.X, treeP0.Y), treeP1, 0xFF504545, thickness); - drawList.AddLine(treeP0 with { Y = timelineP0.Y + timeBarHeight }, timelineP1 with { Y = timelineP0.Y + timeBarHeight }, 0x50504545, thickness); - - // adding margin - var margin = 5.0f * dpiScale; - treeP0.X += margin; - treeP1.X -= margin; - - // control buttons - for (int i = 0; i < _icons.Length; i++) - { - var x = buttonWidth * 2.0f * i; - ImGui.SetCursorScreenPos(treeP0 with { X = treeP1.X - x - buttonWidth * 2.0f + thickness }); - if (ImGui.ImageButton($"timeline_actions_{_icons[i]}", icons[i == 1 ? IsPaused ? "tl_play" : "tl_pause" : _icons[i]].GetPointer(), new Vector2(buttonWidth))) - { - switch (i) - { - case 0: - SafeSetElapsedTime(ElapsedTime + timeStep.X / timeRatio.X); - break; - case 1: - IsPaused = !IsPaused; - break; - case 2: - SafeSetElapsedTime(ElapsedTime - timeStep.X / timeRatio.X); - break; - } - } - } - - drawList.AddText(treeP0 with { Y = treeP0.Y + thickness }, 0xA0FFFFFF, $"{ElapsedTime:F1}/{MaxElapsedTime:F1} seconds"); - - ImGui.SetCursorScreenPos(timelineP0); - ImGui.InvisibleButton("timeline_timetracker_canvas", timelineSize with { Y = timeBarHeight }, ImGuiButtonFlags.MouseButtonLeft); - IsActive = ImGui.IsItemActive(); - if (IsActive && ImGui.IsMouseDragging(ImGuiMouseButton.Left)) - { - var mousePosCanvas = ImGui.GetIO().MousePos - timelineP0; - SafeSetElapsedTime(mousePosCanvas.X / timelineSize.X * MaxElapsedTime); - foreach (var animation in animations) - { - animation.TimeCalculation(ElapsedTime); - } - } - - { // draw time + time grid - for (float x = 0; x < timelineSize.X; x += timeStep.X) - { - var cursor = timelineP0.X + x; - drawList.AddLine(new Vector2(cursor, timelineP0.Y + timeHeight + 2.5f), new Vector2(cursor, timelineP0.Y + timeBarHeight), 0xA0FFFFFF); - drawList.AddLine(new Vector2(cursor, timelineP0.Y + timeBarHeight), timelineP1 with { X = cursor }, 0x28C8C8C8); - drawList.AddText(fontPtr, 14 * dpiScale, new Vector2(cursor + 4, timelineP0.Y + 7.5f), 0x50FFFFFF, $"{x / timeRatio.X:F1}s"); - } - - for (float y = timeBarHeight; y < timelineSize.Y; y += timeStep.Y) - { - drawList.AddLine(timelineP0 with { Y = timelineP0.Y + y }, timelineP1 with { Y = timelineP0.Y + y }, 0x28C8C8C8); - } - } - - ImGui.PushStyleVar(ImGuiStyleVar.SelectableTextAlign, new Vector2(0.0f, 0.5f)); - for (int i = 0; i < animations.Count; i++) - { - var y = timelineP0.Y + timeBarHeight + timeStep.Y * i; - animations[i].ImGuiAnimation(s, drawList, fontPtr, timelineP0, treeP0, timeStep, timeRatio, y, thickness, i); - DrawSeparator(drawList, timelineP0, y + timeStep.Y, animations[i].EndTime * timeRatio.X, timeHeight, timeBarHeight, ETrackerType.End); - } - ImGui.PopStyleVar(); - - for (int i = 0; i < animations.Count; i++) - { - var y = timelineP0.Y + timeBarHeight + timeStep.Y * i; - for (int j = 0; j < animations[i].Sequences.Length - 1; j++) - { - DrawSeparator(drawList, timelineP0, y + timeStep.Y - thickness, animations[i].Sequences[j].EndTime * timeRatio.X - 0.5f, timeHeight, timeBarHeight, ETrackerType.InBetween); - } - } - - DrawSeparator(drawList, timelineP0, timelineP1.Y, ElapsedTime * timeRatio.X, timeHeight, timeBarHeight, ETrackerType.Frame); - - drawList.PopClipRect(); - ImGui.EndChild(); - } - - private void DrawSeparator(ImDrawListPtr drawList, Vector2 origin, float y, float time, float timeHeight, float timeBarHeight, ETrackerType separatorType) - { - float size = separatorType switch - { - ETrackerType.Frame => 5, - ETrackerType.End => 5, - ETrackerType.InBetween => 7.5f, - _ => throw new ArgumentOutOfRangeException(nameof(separatorType), separatorType, null) - }; - - Vector2 p1 = separatorType switch - { - ETrackerType.Frame => new Vector2(origin.X + time, origin.Y + timeBarHeight), - ETrackerType.End => origin with { X = origin.X + time }, - ETrackerType.InBetween => origin with { X = origin.X + time }, - _ => throw new ArgumentOutOfRangeException(nameof(separatorType), separatorType, null) - }; - var p2 = p1 with { Y = y }; - - uint color = separatorType switch - { - ETrackerType.Frame => 0xFF6F6F6F, - ETrackerType.End => 0xFF2E3E82, - ETrackerType.InBetween => 0xA0FFFFFF, - _ => throw new ArgumentOutOfRangeException(nameof(separatorType), separatorType, null) - }; - - switch (separatorType) - { - case ETrackerType.Frame: - color = 0xFF30478C; - var xl = p1.X - size; - var xr = p1.X + size; - var yb = origin.Y + timeBarHeight - timeHeight / 2.0f; - - drawList.AddLine(p1, p2, color, 1f); - drawList.AddQuadFilled(origin with { X = xl }, origin with { X = xr }, new Vector2(xr, yb), new Vector2(xl, yb), color); - drawList.AddTriangleFilled(new Vector2(xl, yb), new Vector2(xr, yb), p1, color); - break; - case ETrackerType.End: - drawList.AddLine(p1, p2, color, 1f); - drawList.AddTriangleFilled(p1, p1 with { X = p1.X - size }, p1 with { Y = p1.Y + size }, color); - break; - case ETrackerType.InBetween: - p1.Y += timeBarHeight; - drawList.AddLine(p1, p2, color, 1f); - drawList.AddTriangleFilled(p1, new Vector2(p1.X - size / 2.0f, p1.Y - size), new Vector2(p1.X + size / 2.0f, p1.Y - size), color); - break; - default: - throw new ArgumentOutOfRangeException(nameof(separatorType), separatorType, null); - } - } -} diff --git a/FModel/Views/Snooper/Buffers/BufferObject.cs b/FModel/Views/Snooper/Buffers/BufferObject.cs deleted file mode 100644 index f92fa167..00000000 --- a/FModel/Views/Snooper/Buffers/BufferObject.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper.Buffers; - -public class BufferObject : IDisposable where TDataType : unmanaged -{ - private readonly int _handle; - private readonly int _sizeOf; - private readonly BufferTarget _bufferTarget; - - public readonly int Size; - - private unsafe BufferObject(BufferTarget bufferTarget) - { - _bufferTarget = bufferTarget; - _handle = GL.GenBuffer(); - _sizeOf = sizeof(TDataType); - - Bind(); - } - - public BufferObject(TDataType[] data, BufferTarget bufferTarget) : this(bufferTarget) - { - Size = data.Length; - GL.BufferData(bufferTarget, Size * _sizeOf, data, BufferUsageHint.StaticDraw); - } - - public BufferObject(int length, BufferTarget bufferTarget) : this(bufferTarget) - { - Size = length; - GL.BufferData(bufferTarget, Size * _sizeOf, IntPtr.Zero, BufferUsageHint.DynamicDraw); - } - - public void UpdateRange(TDataType data) => UpdateRange(Size, data); - public void UpdateRange(int count, TDataType data) - { - Bind(); - for (int i = 0; i < count; i++) Update(i, data); - Unbind(); - } - - public void Update(int offset, TDataType data) - { - GL.BufferSubData(_bufferTarget, (IntPtr) (offset * _sizeOf), _sizeOf, ref data); - } - - public void Update(TDataType[] data) - { - Bind(); - GL.BufferSubData(_bufferTarget, IntPtr.Zero, data.Length * _sizeOf, data); - Unbind(); - } - - public TDataType Get(int offset) - { - TDataType data = default; - GL.GetBufferSubData(_bufferTarget, (IntPtr) (offset * _sizeOf), _sizeOf, ref data); - return data; - } - - public void Bind() - { - 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); - } - - public void Dispose() - { - GL.DeleteBuffer(_handle); - } -} diff --git a/FModel/Views/Snooper/Buffers/FramebufferObject.cs b/FModel/Views/Snooper/Buffers/FramebufferObject.cs deleted file mode 100644 index d509d2c0..00000000 --- a/FModel/Views/Snooper/Buffers/FramebufferObject.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using FModel.Views.Snooper.Shading; -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper.Buffers; - -public class FramebufferObject : IDisposable -{ - private int _framebufferHandle; - private int _postProcessingHandle; - - private int _width; - private int _height; - private readonly RenderbufferObject _renderbuffer; - - private BufferObject _ebo; - private BufferObject _vbo; - private VertexArrayObject _vao; - - private Shader _shader; - private Texture _framebufferTexture; - private Texture _postProcessingTexture; - - public readonly uint[] Indices = { 0, 1, 2, 3, 4, 5 }; - public readonly float[] Vertices = { - // Coords // texCoords - 1.0f, -1.0f, 1.0f, 0.0f, - -1.0f, -1.0f, 0.0f, 0.0f, - -1.0f, 1.0f, 0.0f, 1.0f, - - 1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, -1.0f, 1.0f, 0.0f, - -1.0f, 1.0f, 0.0f, 1.0f - }; - - public FramebufferObject(OpenTK.Mathematics.Vector2i size) - { - _width = size.X; - _height = size.Y; - _renderbuffer = new RenderbufferObject(_width, _height); - } - - public void Setup() - { - _framebufferHandle = GL.GenFramebuffer(); - Bind(); - - _framebufferTexture = new Texture((uint) _width, (uint) _height); - - _renderbuffer.Setup(); - - _shader = new Shader("framebuffer"); - _shader.Use(); - _shader.SetUniform("screenTexture", 0); - - _ebo = new BufferObject(Indices, BufferTarget.ElementArrayBuffer); - _vbo = new BufferObject(Vertices, BufferTarget.ArrayBuffer); - _vao = new VertexArrayObject(_vbo, _ebo); - - _vao.VertexAttributePointer(0, 2, VertexAttribPointerType.Float, 4, 0); // position - _vao.VertexAttributePointer(1, 2, VertexAttribPointerType.Float, 4, 2); // uv - - var status = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer); - if (status != FramebufferErrorCode.FramebufferComplete) - { - throw new Exception($"Framebuffer failed to bind with error: {GL.GetProgramInfoLog(_framebufferHandle)}"); - } - - _postProcessingHandle = GL.GenFramebuffer(); - Bind(_postProcessingHandle); - - _postProcessingTexture = new Texture(_width, _height); - - status = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer); - if (status != FramebufferErrorCode.FramebufferComplete) - { - throw new Exception($"Post-Processing framebuffer failed to bind with error: {GL.GetProgramInfoLog(_postProcessingHandle)}"); - } - } - - public void Bind() => Bind(_framebufferHandle); - public void Bind(int handle) - { - GL.BindFramebuffer(FramebufferTarget.Framebuffer, handle); - } - - public void BindMsaa() - { - GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, _framebufferHandle); - GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, _postProcessingHandle); - GL.BlitFramebuffer(0, 0, _width, _height, 0, 0, _width, _height, ClearBufferMask.ColorBufferBit, BlitFramebufferFilter.Nearest); - GL.Disable(EnableCap.DepthTest); - - _shader.Use(); - _vao.Bind(); - - _postProcessingTexture.Bind(TextureUnit.Texture0); - - GL.DrawArrays(PrimitiveType.Triangles, 0, Indices.Length); - GL.Enable(EnableCap.DepthTest); - } - - public IntPtr GetPointer() => _postProcessingTexture.GetPointer(); - - public void WindowResized(int width, int height) - { - _width = width; - _height = height; - - _renderbuffer.WindowResized(width, height); - - _framebufferTexture.WindowResized(width, height); - _postProcessingTexture.WindowResized(width, height); - } - - public void Dispose() - { - _vao?.Dispose(); - _shader?.Dispose(); - _framebufferTexture?.Dispose(); - _postProcessingTexture?.Dispose(); - _renderbuffer?.Dispose(); - GL.DeleteFramebuffer(_framebufferHandle); - GL.DeleteFramebuffer(_postProcessingHandle); - } -} diff --git a/FModel/Views/Snooper/Buffers/PickingTexture.cs b/FModel/Views/Snooper/Buffers/PickingTexture.cs deleted file mode 100644 index 9ee999cb..00000000 --- a/FModel/Views/Snooper/Buffers/PickingTexture.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; -using System.Collections.Generic; -using CUE4Parse.UE4.Objects.Core.Misc; -using OpenTK.Graphics.OpenGL4; -using System.Numerics; -using FModel.Views.Snooper.Models; -using FModel.Views.Snooper.Shading; - -namespace FModel.Views.Snooper.Buffers; - -public class PickingTexture : IDisposable -{ - private int _width; - private int _height; - - private int _framebufferHandle; - - private Shader _shader; - private int _pickingTexture; - private int _depthTexture; - - public PickingTexture(int width, int height) - { - _width = width; - _height = height; - } - - public void Setup() - { - _framebufferHandle = GL.GenFramebuffer(); - Bind(); - - _shader = new Shader("picking"); - - _pickingTexture = GL.GenTexture(); - GL.BindTexture(TextureTarget.Texture2D, _pickingTexture); - GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba32ui, _width, _height, 0, PixelFormat.RgbaInteger, PixelType.UnsignedInt, IntPtr.Zero); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Nearest); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int) TextureMinFilter.Nearest); - GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, _pickingTexture, 0); - - _depthTexture = GL.GenTexture(); - GL.BindTexture(TextureTarget.Texture2D, _depthTexture); - GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.DepthComponent, _width, _height, 0, PixelFormat.DepthComponent, PixelType.Float, IntPtr.Zero); - GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthAttachment, TextureTarget.Texture2D, _depthTexture, 0); - - var status = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer); - if (status != FramebufferErrorCode.FramebufferComplete) - { - throw new Exception($"Framebuffer failed to bind with error: {GL.GetProgramInfoLog(_framebufferHandle)}"); - } - - GL.BindTexture(TextureTarget.Texture2D, 0); - Bind(0); - } - - public void Render(Matrix4x4 viewMatrix, Matrix4x4 projMatrix, IDictionary models) - { - Bind(); - GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); - - _shader.Render(viewMatrix, projMatrix); - foreach ((var guid, var model) in models) - { - _shader.SetUniform("uA", guid.A); - _shader.SetUniform("uB", guid.B); - _shader.SetUniform("uC", guid.C); - _shader.SetUniform("uD", guid.D); - - if (!model.IsVisible) continue; - model.PickingRender(_shader); - } - - Bind(0); - } - - public void Bind() => Bind(_framebufferHandle); - public void Bind(int handle) - { - GL.BindFramebuffer(FramebufferTarget.Framebuffer, handle); - } - - public FGuid ReadPixel(Vector2 mousePos, Vector2 windowPos, Vector2 windowSize) - { - Bind(); - FGuid pixel = default; - - var scaleX = windowSize.X / _width; - var scaleY = windowSize.Y / _height; - var x = Convert.ToInt32((mousePos.X - windowPos.X) / scaleX); - var y = -Convert.ToInt32((mousePos.Y - windowPos.Y) / scaleY); - - GL.ReadBuffer(ReadBufferMode.ColorAttachment0); - GL.ReadPixels(x, y, 1, 1, PixelFormat.RgbaInteger, PixelType.UnsignedInt, ref pixel); - GL.ReadBuffer(ReadBufferMode.None); - - Bind(0); - return pixel; - } - - public IntPtr GetPointer() => (IntPtr) _pickingTexture; - - public void WindowResized(int width, int height) - { - _width = width; - _height = height; - - GL.BindTexture(TextureTarget.Texture2D, _pickingTexture); - GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba32ui, _width, _height, 0, PixelFormat.RgbaInteger, PixelType.UnsignedInt, IntPtr.Zero); - - GL.BindTexture(TextureTarget.Texture2D, _depthTexture); - GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.DepthComponent, _width, _height, 0, PixelFormat.DepthComponent, PixelType.Float, IntPtr.Zero); - } - - public void Dispose() - { - _shader?.Dispose(); - GL.DeleteTexture(_pickingTexture); - GL.DeleteTexture(_depthTexture); - GL.DeleteFramebuffer(_framebufferHandle); - } -} diff --git a/FModel/Views/Snooper/Buffers/RenderbufferObject.cs b/FModel/Views/Snooper/Buffers/RenderbufferObject.cs deleted file mode 100644 index 2338341f..00000000 --- a/FModel/Views/Snooper/Buffers/RenderbufferObject.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper.Buffers; - -public class RenderbufferObject : IDisposable -{ - private int _handle; - - private int _width; - private int _height; - - public RenderbufferObject(int width, int height) - { - _width = width; - _height = height; - } - - public void Setup() - { - _handle = GL.GenRenderbuffer(); - - GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, _handle); - GL.RenderbufferStorageMultisample(RenderbufferTarget.Renderbuffer, Constants.SAMPLES_COUNT, RenderbufferStorage.Depth24Stencil8, _width, _height); - GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthStencilAttachment, RenderbufferTarget.Renderbuffer, _handle); - } - - public void WindowResized(int width, int height) - { - _width = width; - _height = height; - - GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, _handle); - GL.RenderbufferStorageMultisample(RenderbufferTarget.Renderbuffer, Constants.SAMPLES_COUNT, RenderbufferStorage.Depth24Stencil8, _width, _height); - } - - public void Dispose() - { - GL.DeleteRenderbuffer(_handle); - } -} diff --git a/FModel/Views/Snooper/Buffers/VertexArrayObject.cs b/FModel/Views/Snooper/Buffers/VertexArrayObject.cs deleted file mode 100644 index 59a0aa9e..00000000 --- a/FModel/Views/Snooper/Buffers/VertexArrayObject.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Numerics; -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper.Buffers; - -public class VertexArrayObject : IDisposable where TVertexType : unmanaged where TIndexType : unmanaged -{ - private readonly int _handle; - private readonly int _sizeOfVertex; - private readonly int _sizeOfIndex; - - public unsafe VertexArrayObject(BufferObject vbo, BufferObject ebo) - { - _handle = GL.GenVertexArray(); - _sizeOfVertex = sizeof(TVertexType); - _sizeOfIndex = sizeof(TIndexType); - - Bind(); - vbo.Bind(); - ebo.Bind(); - } - - public void VertexAttributePointer(uint index, int count, VertexAttribPointerType type, int vertexSize, int offset) - { - switch (type) - { - case VertexAttribPointerType.Int: - case VertexAttribPointerType.UnsignedInt: - GL.VertexAttribIPointer(index, count, (VertexAttribIntegerType) type, vertexSize * _sizeOfVertex, offset * _sizeOfVertex); - break; - default: - GL.VertexAttribPointer(index, count, type, false, vertexSize * _sizeOfVertex, offset * _sizeOfVertex); - break; - } - GL.EnableVertexAttribArray(index); - } - - public void Bind() - { - GL.BindVertexArray(_handle); - } - - public void Unbind() - { - GL.BindVertexArray(0); - } - - public unsafe void BindInstancing() - { - Bind(); - - var size = sizeof(Vector4); - GL.EnableVertexAttribArray(9); - GL.VertexAttribPointer(9, 4, VertexAttribPointerType.Float, false, 4 * size, 0); - GL.EnableVertexAttribArray(10); - GL.VertexAttribPointer(10, 4, VertexAttribPointerType.Float, false, 4 * size, 1 * size); - GL.EnableVertexAttribArray(11); - GL.VertexAttribPointer(11, 4, VertexAttribPointerType.Float, false, 4 * size, 2 * size); - GL.EnableVertexAttribArray(12); - GL.VertexAttribPointer(12, 4, VertexAttribPointerType.Float, false, 4 * size, 3 * size); - - GL.VertexAttribDivisor(9, 1); - GL.VertexAttribDivisor(10, 1); - GL.VertexAttribDivisor(11, 1); - GL.VertexAttribDivisor(12, 1); - - Unbind(); - } - - public void Dispose() - { - GL.DeleteVertexArray(_handle); - } -} diff --git a/FModel/Views/Snooper/Camera.cs b/FModel/Views/Snooper/Camera.cs deleted file mode 100644 index 426fe357..00000000 --- a/FModel/Views/Snooper/Camera.cs +++ /dev/null @@ -1,207 +0,0 @@ -using System; -using System.Numerics; -using CUE4Parse.UE4.Objects.Core.Math; -using FModel.Settings; -using ImGuiNET; -using OpenTK.Windowing.GraphicsLibraryFramework; - -namespace FModel.Views.Snooper; - -public class Camera -{ - public enum WorldMode - { - FlyCam, - Arcball - } - - public Vector3 Position; - public Vector3 Direction; - public WorldMode Mode; - public Vector3 PositionArc => Position - Direction; - public Vector3 DirectionArc => Direction - Position; - public Vector3 Up => Vector3.UnitY; - - public float Zoom = 60f; - public float Speed = 1f; - public float Far = 100f; - public float Near => 0.01f; - public float AspectRatio = 16f / 9f; - - public Camera() - { - Position = new Vector3(0, 1, 1); - Direction = Vector3.Zero; - Mode = UserSettings.Default.CameraMode; - } - - public void Setup(FBox box) => Teleport(FVector.ZeroVector, box, true); - public void Teleport(Vector3 instancePos, FBox box, bool updateAll = false) - { - box.GetCenterAndExtents(out var center, out var extents); - center += new FVector(instancePos.X, instancePos.Z, instancePos.Y); - var distance = extents.AbsMax(); - - Position = new Vector3(instancePos.X, center.Z, instancePos.Z + distance * 2); - Direction = new Vector3(center.X, center.Z, center.Y); - if (updateAll) - { - Far = Math.Max(Far, box.Max.AbsMax() * 50f); - Speed = Math.Max(Speed, distance); - } - } - - public void Modify(Vector2 mouseDelta) - { - var lookSensitivity = Mode switch - { - WorldMode.FlyCam => 0.002f, - WorldMode.Arcball => 0.003f, - _ => throw new ArgumentOutOfRangeException() - }; - mouseDelta *= lookSensitivity; - - const float tolerance = 0.001f; - var rotationX = Matrix4x4.CreateFromAxisAngle(-Up, mouseDelta.X); - switch (Mode) - { - case WorldMode.FlyCam: - { - Direction = Vector3.Transform(DirectionArc, rotationX) + Position; - - var right = Vector3.Normalize(Vector3.Cross(Up, DirectionArc)); - - var currentPitch = MathF.Acos(Vector3.Dot(DirectionArc, Up) / (DirectionArc.Length() * Up.Length())); - var newPitch = currentPitch + mouseDelta.Y; - var clampedPitch = Math.Clamp(newPitch, tolerance, MathF.PI - tolerance); - var pitchDelta = clampedPitch - currentPitch; - - var rotationY = Matrix4x4.CreateFromAxisAngle(right, pitchDelta); - Direction = Vector3.Transform(DirectionArc, rotationY) + Position; - break; - } - case WorldMode.Arcball: - { - Position = Vector3.Transform(PositionArc, rotationX) + Direction; - - var right = Vector3.Normalize(Vector3.Cross(-Up, PositionArc)); - - var currentPitch = MathF.Acos(Vector3.Dot(PositionArc, -Up) / (PositionArc.Length() * Up.Length())); - var newPitch = currentPitch + mouseDelta.Y; - var clampedPitch = Math.Clamp(newPitch, tolerance, MathF.PI - tolerance); - var pitchDelta = clampedPitch - currentPitch; - - var rotationY = Matrix4x4.CreateFromAxisAngle(right, pitchDelta); - Position = Vector3.Transform(PositionArc, rotationY) + Direction; - break; - } - default: - throw new ArgumentOutOfRangeException(); - } - } - - public void Modify(KeyboardState keyboard, float time) - { - if (!keyboard.IsAnyKeyDown) return; - var multiplier = keyboard.IsKeyDown(Keys.LeftShift) ? 2f : 1f; - var moveSpeed = Speed * multiplier * time; - var moveAxis = Vector3.Normalize(-PositionArc); - var panAxis = Vector3.Normalize(Vector3.Cross(moveAxis, Up)); - - switch (Mode) - { - case WorldMode.FlyCam: - { - if (keyboard.IsKeyDown(Keys.W)) // forward - { - var d = moveSpeed * moveAxis; - Position += d; - Direction += d; - } - if (keyboard.IsKeyDown(Keys.S)) // backward - { - var d = moveSpeed * moveAxis; - Position -= d; - Direction -= d; - } - break; - } - case WorldMode.Arcball: - { - if (keyboard.IsKeyDown(Keys.W)) // forward - Position += moveSpeed * moveAxis; - if (keyboard.IsKeyDown(Keys.S)) // backward - Position -= moveSpeed * moveAxis; - break; - } - default: - throw new ArgumentOutOfRangeException(); - } - - if (keyboard.IsKeyDown(Keys.A)) // left - { - var d = panAxis * moveSpeed; - Position -= d; - Direction -= d; - } - if (keyboard.IsKeyDown(Keys.D)) // right - { - var d = panAxis * moveSpeed; - Position += d; - Direction += d; - } - if (keyboard.IsKeyDown(Keys.E)) // up - { - var d = moveSpeed * Up; - Position += d; - Direction += d; - } - if (keyboard.IsKeyDown(Keys.Q)) // down - { - var d = moveSpeed * Up; - Position -= d; - Direction -= d; - } - - if (keyboard.IsKeyDown(Keys.C)) // zoom in - ModifyZoom(+.5f); - if (keyboard.IsKeyDown(Keys.X)) // zoom out - ModifyZoom(-.5f); - } - - private void ModifyZoom(float zoomAmount) - { - //We don't want to be able to zoom in too close or too far away so clamp to these values - Zoom = Math.Clamp(Zoom - zoomAmount, 1.0f, 89f); - } - - public Matrix4x4 GetViewMatrix() => Matrix4x4.CreateLookAt(Position, Direction, Up); - public Matrix4x4 GetProjectionMatrix() - => Matrix4x4.CreatePerspectiveFieldOfView(Helper.DegreesToRadians(Zoom), AspectRatio, Near, Far); - - private const float _step = 0.01f; - private const float _zero = 0.000001f; // doesn't actually work if _infinite is used as max value /shrug - private const float _infinite = 0.0f; - private const ImGuiSliderFlags _clamp = ImGuiSliderFlags.AlwaysClamp; - public void ImGuiCamera() - { - ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(8, 3)); - ImGui.PushStyleVar(ImGuiStyleVar.CellPadding, new Vector2(0, 1)); - if (ImGui.BeginTable("camera_editor", 2)) - { - SnimGui.Layout("Mode"); - ImGui.PushID(1);var m = (int) Mode; - ImGui.Combo("world_mode", ref m, "Fly Cam\0Arcball\0"); - Mode = (WorldMode) m;ImGui.PopID(); - - SnimGui.Layout("Speed");ImGui.PushID(2); - ImGui.DragFloat("", ref Speed, _step, _zero, _infinite, "%.2f m/s", _clamp); - ImGui.PopID();SnimGui.Layout("Far Plane");ImGui.PushID(3); - ImGui.DragFloat("", ref Far, 0.1f, 0.1f, Far * 2f, "%.2f m", _clamp); - ImGui.PopID(); - - ImGui.EndTable(); - } - ImGui.PopStyleVar(2); - } -} diff --git a/FModel/Views/Snooper/ExportModal.cs b/FModel/Views/Snooper/ExportModal.cs deleted file mode 100644 index 4f4d8bd6..00000000 --- a/FModel/Views/Snooper/ExportModal.cs +++ /dev/null @@ -1,664 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Numerics; -using System.Threading; -using System.Threading.Tasks; -using CUE4Parse_Conversion; -using CUE4Parse_Conversion.Options; -using CUE4Parse.Utils; -using FModel.Extensions; -using FModel.Views.Snooper.Models; -using ImGuiNET; -using Serilog; -using Serilog.Core; -using Serilog.Events; - -namespace FModel.Views.Snooper; - -public sealed class ExportModal -{ - public static ExportModal Instance { get; } = new(); - - private const string Title = "Export Progress"; - private const string IconXMark = "\uf057"; - private const string IconFolder = "\uf08e"; - - private readonly Vector4[] _pieColors = - [ - new(0.22f, 0.52f, 0.90f, 1f), - new(0.28f, 0.78f, 0.44f, 1f), - new(0.90f, 0.62f, 0.22f, 1f), - new(0.75f, 0.32f, 0.75f, 1f), - new(0.32f, 0.75f, 0.85f, 1f), - new(0.85f, 0.32f, 0.45f, 1f), - new(0.90f, 0.90f, 0.22f, 1f), - new(0.55f, 0.75f, 0.32f, 1f), - ]; - - private static readonly Vector4 _redColor = new(1f, 0.4f, 0.4f, 1f); - private static readonly Vector4 _orangeColor = new(1f, 0.5f, 0f, 1f); - private static readonly Vector4 _yellowColor = new(1f, 1f, 0.4f, 1f); - private static readonly Vector4 _greenColor = new(0.4f, 1f, 0.4f, 1f); - - private bool _openPopup; - private bool _modalOpen; - private bool _inProgress; - private CancellationTokenSource? _cts; - private IReadOnlyList? _exportResults; - - private ExportProgress _currentProgress; - private readonly IProgress _progress; - private readonly Stopwatch _stopwatch = new(); - private readonly ConcurrentQueue _pendingLogs = new(); - private readonly List _classGroups = []; - - private const int MaxGraphSamples = 4096; - private const float GraphSampleIntervalSec = 0.25f; - private readonly List _graphSamples = []; - private float _graphNextSampleAt; - private int _graphLastCompleted; - - private ExportModal() - { - ImGuiSink.Instance.OnExporterLogEvent += _pendingLogs.Enqueue; - _progress = new Progress(p => _currentProgress = p); - } - - public void Export(IEnumerable nodes, string exportDirectory, ExportOptions options) - { - Reset(); - _openPopup = true; - _inProgress = true; - _cts = new CancellationTokenSource(); - _stopwatch.Restart(); - - var token = _cts.Token; - _ = Task.Run(async () => - { - try - { - var session = new ExportSession(); - foreach (var node in nodes) node.AddToExportSession(session); - _exportResults = await session.RunAsync(exportDirectory, options, _progress, token); - } - catch (OperationCanceledException) - { - Log.Error("Export cancelled by user"); - } - catch (Exception ex) - { - Log.Error(ex, "Export failed"); - } - finally - { - _stopwatch.Stop(); - _inProgress = false; - } - }, token); - } - - public void Draw() - { - if (_openPopup) - { - ImGui.OpenPopup(Title); - _modalOpen = true; - _openPopup = false; - } - - if (!_modalOpen) return; - - var viewport = ImGui.GetMainViewport(); - ImGui.SetNextWindowSize(viewport.WorkSize * 0.75f, ImGuiCond.Always); - ImGui.SetNextWindowPos(viewport.GetCenter(), ImGuiCond.Always, new Vector2(0.5f, 0.5f)); - - var open = true; - if (ImGui.BeginPopupModal(Title, ref open, ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize)) - { - if (ImGui.BeginChild("##ModalInfoBody", Vector2.Zero, ImGuiChildFlags.FrameStyle)) - { - DrawProgressBar(); - - ImGui.Spacing(); - ImGui.SeparatorText("Throughput"); - DrawThroughputGraph(); - - ImGui.Spacing(); - ImGui.SeparatorText("Export Log"); - DrawExportLog(); - } - ImGui.EndChild(); - ImGui.EndPopup(); - } - - if (!open) - { - _modalOpen = false; - Reset(); - } - } - - private void Reset() - { - _pendingLogs.Clear(); - _classGroups.Clear(); - _exportResults = null; - _currentProgress = new ExportProgress(0, 0); - _cts?.Cancel(); - _cts?.Dispose(); - _cts = null; - - _graphSamples.Clear(); - _graphNextSampleAt = 0f; - _graphLastCompleted = 0; - } - - private void DrawProgressBar() - { - var e = _stopwatch.Elapsed; - ImGui.TextDisabled("\uf2f2"); - ImGui.SameLine(); - ImGui.TextUnformatted($"{e.Minutes:D2}:{e.Seconds:D2}.{e.Milliseconds / 10:D2}"); - - if (_inProgress && _currentProgress is { Total: > 0, Completed: > 1 }) - { - var rate = _currentProgress.Completed / e.TotalSeconds; - if (rate > 0) - { - var remaining = (_currentProgress.Total - _currentProgress.Completed) / rate; - var eta = TimeSpan.FromSeconds(remaining); - ImGui.SameLine(); - ImGui.TextDisabled("\uf017"); - ImGui.SameLine(); - ImGui.TextUnformatted($"ETA {eta.Minutes:D2}:{eta.Seconds:D2}"); - } - } - else if (!_inProgress && _exportResults is { Count: > 0 }) - { - ImGui.SameLine(); - ImGui.TextColored(_greenColor, "\uf058"); - ImGui.SameLine(); - ImGui.TextUnformatted($"{_exportResults?.Count(r => r.Success) ?? 0} succeeded"); - - ImGui.SameLine(); - ImGui.TextColored(_redColor, IconXMark); - ImGui.SameLine(); - ImGui.TextUnformatted($"{_exportResults?.Count(r => !r.Success) ?? 0} failed"); - } - - ImGui.Spacing(); - - var barColor = _classGroups.Any(cg => cg.ErrorCount > 0) ? new Vector4(0.75f, 0.32f, 0.32f, 1f) : _inProgress ? new Vector4(0.22f, 0.52f, 0.90f, 1f) : new Vector4(0.28f, 0.78f, 0.44f, 1f); - var label = _inProgress && _currentProgress.Total > 0 ? _currentProgress.DisplayText : _inProgress ? "Preparing..." : "Done"; - - var barPos = ImGui.GetCursorScreenPos(); - var barSize = new Vector2(ImGui.GetContentRegionAvail().X, ImGui.GetFrameHeight()); - var hovered = ImGui.IsMouseHoveringRect(barPos, barPos + barSize); - if (hovered && _inProgress) - { - barColor = new Vector4(0.75f, 0.32f, 0.32f, 1f); - label = "\uf05e Cancel"; - } - - ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 4f); - ImGui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1f); - ImGui.PushStyleColor(ImGuiCol.PlotHistogram, barColor); - ImGui.ProgressBar(_currentProgress.Percentage, barSize, label); - ImGui.PopStyleColor(); - ImGui.PopStyleVar(2); - - if (_inProgress) - { - ImGui.SetCursorScreenPos(barPos); - if (ImGui.InvisibleButton("##BarAction", barSize)) - { - _cts?.Cancel(); - } - } - } - - private void DrawThroughputGraph() - { - var elapsedSec = (float)_stopwatch.Elapsed.TotalSeconds; - if (_inProgress && _currentProgress.Completed > 0 && elapsedSec >= _graphNextSampleAt && _graphSamples.Count < MaxGraphSamples) - { - var completed = _currentProgress.Completed; - var rate = (completed - _graphLastCompleted) / GraphSampleIntervalSec; - _graphSamples.Add(MathF.Max(rate, 0f)); - _graphLastCompleted = completed; - _graphNextSampleAt = elapsedSec + GraphSampleIntervalSec; - } - - var graphPos = ImGui.GetCursorScreenPos(); - var graphW = ImGui.GetContentRegionAvail().X; - var graphH = ImGui.GetFrameHeight() * 3f; - var graphSize = new Vector2(graphW, graphH); - ImGui.InvisibleButton("##ThroughputGraph", graphSize); - var dl = ImGui.GetWindowDrawList(); - - dl.AddRectFilled(graphPos, graphPos + graphSize, 0xFF_14_14_14); - - var count = _graphSamples.Count; - switch (count) - { - case 0 when !_inProgress: - { - dl.AddRect(graphPos, graphPos + graphSize, 0xFF_2A_2A_2A); - return; - } - case < 2: - { - if (_inProgress) - { - const string msg = "Collecting data..."; - var msgSz = ImGui.CalcTextSize(msg); - dl.AddText(graphPos + (graphSize - msgSz) * 0.5f, 0x44_FF_FF_FF, msg); - } - dl.AddRect(graphPos, graphPos + graphSize, 0xFF_2A_2A_2A); - return; - } - } - - // Y scale - var maxVal = 0f; - for (var i = 0; i < count; i++) maxVal = MathF.Max(maxVal, _graphSamples[i]); - if (maxVal < 0.01f) maxVal = 1f; - maxVal *= 1.15f; // top headroom - - dl.PushClipRect(graphPos, graphPos + graphSize, true); - - // Horizontal grid lines at 25 / 50 / 75 % - for (var g = 1; g < 4; g++) - { - var gy = graphPos.Y + graphH * g / 4f; - dl.AddLine(new Vector2(graphPos.X, gy), new Vector2(graphPos.X + graphW, gy), 0x18_FF_FF_FF, 0.5f); - } - - var lineColor = ImGui.GetColorU32(new Vector4(0.22f, 0.52f, 0.90f, 1f)); - var fillColor = ImGui.GetColorU32(new Vector4(0.22f, 0.52f, 0.90f, 0.15f)); - - // xStep shrinks as samples accumulate → graph zooms out naturally. - // Oldest sample is always at x=0, newest at x=graphW. - var xStep = graphW / (count - 1f); - - // Filled area (series of convex quads) - for (var i = 0; i < count - 1; i++) - { - var t0 = Math.Clamp(_graphSamples[i] / maxVal, 0f, 1f); - var t1 = Math.Clamp(_graphSamples[i + 1] / maxVal, 0f, 1f); - var x0 = graphPos.X + xStep * i; - var x1 = graphPos.X + xStep * (i + 1); - var y0 = graphPos.Y + graphH - graphH * t0; - var y1 = graphPos.Y + graphH - graphH * t1; - var bot = graphPos.Y + graphH; - dl.AddQuadFilled(new Vector2(x0, y0), new Vector2(x1, y1), new Vector2(x1, bot), new Vector2(x0, bot), fillColor); - } - - // Line - for (var i = 0; i < count - 1; i++) - { - var t0 = Math.Clamp(_graphSamples[i] / maxVal, 0f, 1f); - var t1 = Math.Clamp(_graphSamples[i + 1] / maxVal, 0f, 1f); - var x0 = graphPos.X + xStep * i; - var x1 = graphPos.X + xStep * (i + 1); - var y0 = graphPos.Y + graphH - graphH * t0; - var y1 = graphPos.Y + graphH - graphH * t1; - dl.AddLine(new Vector2(x0, y0), new Vector2(x1, y1), lineColor, 1.5f); - } - - // Dot on the newest sample (always at the right edge) - var newestT = Math.Clamp(_graphSamples[count - 1] / maxVal, 0f, 1f); - var newestY = graphPos.Y + graphH - graphH * newestT; - dl.AddCircleFilled(new Vector2(graphPos.X + graphW, newestY), 3f, lineColor); - - var rateStr = $"{_graphSamples[count - 1]:F1} items/s"; - dl.AddText(new Vector2(graphPos.X + 4, graphPos.Y + 3), 0xCC_FF_FF_FF, rateStr); - - dl.PopClipRect(); - - dl.AddRect(graphPos, graphPos + graphSize, 0xFF_2A_2A_2A); - } - - private void DrawExportLog() - { - var avail = ImGui.GetContentRegionAvail(); - var rowH = ImGui.GetTextLineHeightWithSpacing(); - var canvasSize = 15 * rowH + ImGui.GetFrameHeightWithSpacing(); - var treeW = avail.X - canvasSize - ImGui.GetStyle().ItemSpacing.X; - - DrainPendingLogs(); - - if (ImGui.BeginChild("##ExportLogTree", avail with { X = treeW }, ImGuiChildFlags.FrameStyle)) - { - if (_classGroups.Count == 0) - { - ImGui.TextDisabled(_inProgress ? "Waiting for export data..." : "No export log."); - } - else for (var i = 0; i < _classGroups.Count; i++) - { - DrawClassGroup(i, _classGroups[i]); - } - } - ImGui.EndChild(); - - ImGui.SameLine(); - if (ImGui.BeginChild("##RightPanel", new Vector2(canvasSize, -1), ImGuiChildFlags.FrameStyle)) - { - DrawPieCanvas(); - - if (_classGroups.Count == 0) - { - ImGui.TextDisabled(_inProgress ? "Waiting for data..." : "No export data."); - } - else - { - var total = _classGroups.Sum(cg => cg.Objects.Count); - for (var i = 0; i < _classGroups.Count; i++) - { - var cg = _classGroups[i]; - ImGui.PushStyleColor(ImGuiCol.Text, _pieColors[i % _pieColors.Length]); - ImGui.TextUnformatted("\uf111"); - ImGui.PopStyleColor(); - ImGui.SameLine(); - ImGui.TextUnformatted(cg.Name); - ImGui.SameLine(); - ImGui.PushStyleColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled)); - ImGui.TextUnformatted($"({(float) cg.Objects.Count / total * 100f:F1}%)"); - ImGui.PopStyleColor(); - } - } - } - - ImGui.EndChild(); - } - - private void DrawPieCanvas() - { - const int segments = 64; - - var canvasPos = ImGui.GetCursorScreenPos(); - var size = ImGui.GetContentRegionAvail().X; - var canvasVec = new Vector2(size); - ImGui.InvisibleButton("##PieCanvas", canvasVec); - var isHovered = ImGui.IsItemHovered(); - var mousePos = ImGui.GetMousePos(); - var dl = ImGui.GetWindowDrawList(); - - dl.AddRectFilled(canvasPos, canvasPos + canvasVec, 0xFF_14_14_14); - dl.AddRect(canvasPos, canvasPos + canvasVec, 0xFF_32_32_32); - - var total = _classGroups.Sum(cg => cg.Objects.Count); - var padding = ImGui.GetFrameHeight() * 0.5f; - var radius = size * 0.5f - padding; - var center = canvasPos + new Vector2(size * 0.5f); - - if (total == 0 || radius <= 0) - { - dl.AddCircleFilled(center, MathF.Max(radius, 1f), 0xFF_1F_1F_1F, segments); - return; - } - - // Determine hovered slice by angle - var hoveredSlice = -1; - if (isHovered) - { - var dx = mousePos.X - center.X; - var dy = mousePos.Y - center.Y; - if (dx * dx + dy * dy <= radius * radius) - { - var angle = MathF.Atan2(dy, dx); - while (angle < -MathF.PI / 2f) angle += MathF.PI * 2f; - var cur = -MathF.PI / 2f; - for (var i = 0; i < _classGroups.Count; i++) - { - var sweep = (float)_classGroups[i].Objects.Count / total * MathF.PI * 2f; - if (angle >= cur && angle < cur + sweep) { hoveredSlice = i; break; } - cur += sweep; - } - } - } - - float startAngle = -MathF.PI / 2f; - for (var i = 0; i < _classGroups.Count; i++) - { - var cg = _classGroups[i]; - var ratio = (float) cg.Objects.Count / total; - var sliceAngle = ratio * MathF.PI * 2f; - var col = ImGui.GetColorU32(_pieColors[i % _pieColors.Length]); - var r = i == hoveredSlice ? radius + padding * 0.25f : radius; - dl.PathLineTo(center); - dl.PathArcTo(center, r, startAngle, startAngle + sliceAngle); - dl.PathFillConvex(col); - - var midAngle = startAngle + sliceAngle * 0.5f; - var labelPos = center + new Vector2(MathF.Cos(midAngle), MathF.Sin(midAngle)) * (radius * 0.62f); - var pctStr = $"{ratio * 100f:F0}%"; - dl.AddText(labelPos - ImGui.CalcTextSize(pctStr) * 0.5f, 0xFF_FF_FF_FF, pctStr); - - startAngle += sliceAngle; - } - - dl.AddCircle(center, radius, 0xAA_00_00_00, segments, 1.5f); - - if (hoveredSlice >= 0) - { - ImGui.BeginTooltip(); - - var cg = _classGroups[hoveredSlice]; - ImGui.PushStyleColor(ImGuiCol.Text, _pieColors[hoveredSlice % _pieColors.Length]); - ImGui.TextUnformatted("\uf111"); - ImGui.PopStyleColor(); - ImGui.SameLine(); - ImGui.TextUnformatted(cg.Name); - - ImGui.EndTooltip(); - } - } - - private void DrawClassGroup(int index, ClassGroup cg) - { - ImGui.PushStyleColor(ImGuiCol.Header, new Vector4(0.20f, 0.20f, 0.20f, 1.00f)); - ImGui.PushStyleColor(ImGuiCol.HeaderHovered, new Vector4(0.69f, 0.69f, 1.00f, 0.20f)); - ImGui.PushStyleColor(ImGuiCol.HeaderActive, new Vector4(0.69f, 0.69f, 1.00f, 0.20f)); - var open = ImGui.CollapsingHeader($"{cg.Name} ({cg.Objects.Count})##class_{cg.Name}"); - ImGui.PopStyleColor(3); - var headerMin = ImGui.GetItemRectMin(); - var headerMax = ImGui.GetItemRectMax(); - - var labelW = MathF.Floor(ImGui.GetStyle().ItemSpacing.X * 0.5f); - var col = ImGui.GetColorU32(_pieColors[index % _pieColors.Length]); - ImGui.GetWindowDrawList().AddRectFilled(headerMin, headerMax with { X = headerMin.X + labelW }, col); - - if (cg.ErrorCount > 0 && ImGui.IsItemHovered()) - { - ImGui.SetTooltip($"{cg.ErrorCount} error{(cg.ErrorCount > 1 ? "s" : "")} in this class"); - } - - if (!open) return; - foreach (var og in cg.Objects) - { - DrawObjectGroup(cg.Name, og); - } - } - - private void DrawObjectGroup(string className, ObjectGroup og) - { - var rightEdge = ImGui.GetCursorPosX() + ImGui.GetContentRegionAvail().X; - - var hasErr = og.ErrorCount > 0; - if (hasErr) ImGui.PushStyleColor(ImGuiCol.Text, _redColor); - var flags = ImGuiTreeNodeFlags.AllowOverlap | ImGuiTreeNodeFlags.SpanFullWidth | ImGuiTreeNodeFlags.FramePadding; - var open = ImGui.TreeNodeEx($"{og.Name}##obj_{className}_{og.Name}", flags); - if (hasErr) ImGui.PopStyleColor(); - - if (og.Entries.FirstOrDefault(e => !string.IsNullOrEmpty(e.FilePath)) is { } first) - { - var style = ImGui.GetStyle(); - var btnW = ImGui.CalcTextSize(IconFolder).X + style.FramePadding.X * 2; - ImGui.SameLine(rightEdge - btnW); - ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, style.ItemSpacing with { X = 0 }); - ImGui.PushStyleColor(ImGuiCol.Button, Vector4.Zero); - if (ImGui.Button($"{IconFolder}##obj_{className}_{og.Name}")) - { - OpenInExplorer(first.FilePath!); - } - if (ImGui.IsItemHovered()) ImGui.SetTooltip("Open In Explorer"); - ImGui.PopStyleColor(); - ImGui.PopStyleVar(); - } - - if (!open) return; - foreach (var entry in og.Entries) - { - DrawLogEntry(entry); - } - ImGui.TreePop(); - } - - private void DrawLogEntry(LogEntry entry) - { - ImGui.PushStyleColor(ImGuiCol.Text, entry.Color); - ImGui.TextUnformatted(entry.Icon); - ImGui.PopStyleColor(); - ImGui.SameLine(); - ImGui.TextUnformatted(entry.Message); - if (ImGui.IsItemHovered() && entry.Exception != null) - { - DrawExceptionTooltip(entry.Exception); - } - } - - private static void DrawExceptionTooltip(Exception ex) - { - ImGui.BeginTooltip(); - ImGui.PushStyleColor(ImGuiCol.Text, _redColor); - ImGui.TextUnformatted(ex.GetType().ToString()); - ImGui.PopStyleColor(); - ImGui.SameLine(0, 0); - ImGui.TextDisabled(":"); - ImGui.SameLine(); - ImGui.TextUnformatted(ex.Message); - if (!string.IsNullOrEmpty(ex.StackTrace)) - { - ImGui.SetWindowFontScale(0.85f); - ImGui.PushStyleColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled)); - ImGui.TextUnformatted(ex.StackTrace); - ImGui.PopStyleColor(); - ImGui.SetWindowFontScale(1.0f); - } - ImGui.EndTooltip(); - } - - private void OpenInExplorer(string path) - { - try - { - if (File.Exists(path) || Directory.Exists(path)) Process.Start("explorer.exe", $"/select, \"{path}\""); - else Log.Warning("File or directory does not exist: {Path}", path); - } - catch (Exception ex) - { - Log.Error(ex, "Failed to open in explorer: {Path}", path); - } - } - - private void DrainPendingLogs() - { - while (_pendingLogs.TryDequeue(out var log)) - { - var className = log.GetContext("ClassName"); - var objectPath = log.GetContext("ObjectPath"); - var filePath = log.GetContext("FilePath"); - - var cg = FindOrCreateClass(className); - var og = FindOrCreateObject(cg, objectPath); - var entry = new LogEntry(log, filePath); - if (entry.Icon == IconXMark) - { - og.ErrorCount++; - cg.ErrorCount++; - } - og.Entries.Add(entry); - } - } - - private ClassGroup FindOrCreateClass(string name) - { - foreach (var cg in _classGroups) - if (cg.Name == name) return cg; - - var n = new ClassGroup(name); - _classGroups.Add(n); - return n; - } - - private static ObjectGroup FindOrCreateObject(ClassGroup cg, string path) - { - foreach (var og in cg.Objects) - if (og.Path == path) return og; - - var n = new ObjectGroup(path); - cg.Objects.Add(n); - return n; - } - - private sealed class LogEntry(LogEvent log, string? filePath) - { - public string Icon { get; } = log.Level switch - { - LogEventLevel.Error or LogEventLevel.Fatal => IconXMark, - LogEventLevel.Warning => "\uf071", - LogEventLevel.Information => "\uf05a", - LogEventLevel.Debug => "\uf188", - _ => "\uf5dc" - }; - public Vector4 Color { get; } = log.Level switch - { - LogEventLevel.Error or LogEventLevel.Fatal => _redColor, - LogEventLevel.Warning => _yellowColor, - _ => new Vector4(0.5f, 0.5f, 0.5f, 1f) - }; - public string Message { get; } = $"[{log.Timestamp:HH:mm:ss.fff}] {log.RenderMessage()}"; - public string? FilePath { get; } = filePath; - public Exception? Exception { get; } = log.Exception; - } - - private sealed class ObjectGroup(string path) - { - public string Path { get; } = path; - public string Name { get; } = path.SubstringAfterLast('.'); - public List Entries { get; } = []; - public int ErrorCount { get; set; } - } - - private sealed class ClassGroup(string name) - { - public string Name { get; } = name; - public List Objects { get; } = []; - public int ErrorCount { get; set; } - } -} - -public class ImGuiSink : ILogEventSink -{ - public static ImGuiSink Instance { get; } = new(); - - private ImGuiSink() - { - - } - - public event Action? OnExporterLogEvent; - - public void Emit(LogEvent logEvent) - { - if (logEvent.Properties.TryGetValue("ExporterV2", out var state) && state is ScalarValue { Value: true }) - { - OnExporterLogEvent?.Invoke(logEvent); - } - } -} diff --git a/FModel/Views/Snooper/Lights/Light.cs b/FModel/Views/Snooper/Lights/Light.cs deleted file mode 100644 index 8159f3b5..00000000 --- a/FModel/Views/Snooper/Lights/Light.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System; -using System.Numerics; -using CUE4Parse.UE4.Assets.Exports; -using CUE4Parse.UE4.Objects.Core.Math; -using CUE4Parse.UE4.Objects.Core.Misc; -using FModel.Views.Snooper.Buffers; -using FModel.Views.Snooper.Shading; -using ImGuiNET; -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper.Lights; - -public abstract class Light : IDisposable -{ - private int _handle; - - private BufferObject _ebo; - private BufferObject _vbo; - private BufferObject _matrixVbo; - private VertexArrayObject _vao; - - public readonly uint[] Indices = { 0, 1, 2, 3, 4, 5 }; - public readonly float[] Vertices = { - 1f, 1f, 0f, - -1f, -1f, 0f, - -1f, 1f, 0f, - -1f, -1f, 0f, - 1f, 1f, 0f, - 1f, -1f, 0 - }; - public readonly FGuid Model; - public readonly Texture Icon; - public Transform Transform; - - public Vector4 Color; - public float Intensity; - public bool IsSetup; - - public Light(Texture icon, UObject light) - { - Transform = new Transform - { - Position = light.GetOrDefault("RelativeLocation", FVector.ZeroVector) * Constants.SCALE_DOWN_RATIO, - Rotation = light.GetOrDefault("RelativeRotation", FRotator.ZeroRotator).Quaternion(), - Scale = light.GetOrDefault("RelativeScale3D", FVector.OneVector) - }; - - Model = new FGuid((uint) light.GetFullName().GetHashCode()); - Icon = icon; - - Color = light.GetOrDefault("LightColor", new FColor(0xFF, 0xFF, 0xFF, 0xFF)); - Intensity = light.GetOrDefault("Intensity", 1.0f); - } - - public Light(FGuid model, Texture icon, UObject parent, UObject light, Transform transform) - { - Transform = new Transform - { - Relation = transform.Matrix, - Position = light.GetOrDefault("RelativeLocation", parent.GetOrDefault("RelativeLocation", FVector.ZeroVector)) * Constants.SCALE_DOWN_RATIO, - Rotation = light.GetOrDefault("RelativeRotation", parent.GetOrDefault("RelativeRotation", FRotator.ZeroRotator)).Quaternion(), - Scale = light.GetOrDefault("RelativeScale3D", parent.GetOrDefault("RelativeScale3D", FVector.OneVector)) - }; - - Model = model; - Icon = icon; - - Color = light.GetOrDefault("LightColor", parent.GetOrDefault("LightColor", new FColor(0xFF, 0xFF, 0xFF, 0xFF))); - Intensity = light.GetOrDefault("Intensity", parent.GetOrDefault("Intensity", 1.0f)); - } - - public void SetupInstances() - { - var instanceMatrix = new [] {Transform.Matrix}; - _matrixVbo = new BufferObject(instanceMatrix, BufferTarget.ArrayBuffer); - _vao.BindInstancing(); // VertexAttributePointer - } - - public void UpdateMatrices() - { - _matrixVbo.Bind(); - _matrixVbo.Update(0, Transform.Matrix); - _matrixVbo.Unbind(); - } - - public void Setup() - { - _handle = GL.CreateProgram(); - - _ebo = new BufferObject(Indices, BufferTarget.ElementArrayBuffer); - _vbo = new BufferObject(Vertices, BufferTarget.ArrayBuffer); - _vao = new VertexArrayObject(_vbo, _ebo); - - _vao.VertexAttributePointer(0, 3, VertexAttribPointerType.Float, 3, 0); // position - SetupInstances(); - - IsSetup = true; - } - - public void Render(Shader shader) - { - GL.Disable(EnableCap.CullFace); - - _vao.Bind(); - - Icon?.Bind(TextureUnit.Texture0); - shader.SetUniform("uIcon", 0); - shader.SetUniform("uColor", Color); - - GL.DrawArrays(PrimitiveType.Triangles, 0, Indices.Length); - - GL.Enable(EnableCap.CullFace); - } - - public virtual void Render(int i, Shader shader) - { - shader.SetUniform($"uLights[{i}].Base.Color", Color); - shader.SetUniform($"uLights[{i}].Base.Position", Transform.Matrix.Translation); - shader.SetUniform($"uLights[{i}].Base.Intensity", Intensity); - } - - public virtual void ImGuiLight() - { - SnimGui.Layout("Color");ImGui.PushID(1); - ImGui.ColorEdit4("", ref Color, ImGuiColorEditFlags.NoAlpha); - ImGui.PopID();SnimGui.Layout("Intensity");ImGui.PushID(2); - ImGui.DragFloat("", ref Intensity, 0.1f);ImGui.PopID(); - } - - public void Dispose() - { - _ebo?.Dispose(); - _vbo?.Dispose(); - _vao?.Dispose(); - GL.DeleteProgram(_handle); - } -} diff --git a/FModel/Views/Snooper/Lights/PointLight.cs b/FModel/Views/Snooper/Lights/PointLight.cs deleted file mode 100644 index c1d71d88..00000000 --- a/FModel/Views/Snooper/Lights/PointLight.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using CUE4Parse.UE4.Assets.Exports; -using CUE4Parse.UE4.Objects.Core.Misc; -using FModel.Views.Snooper.Shading; -using ImGuiNET; - -namespace FModel.Views.Snooper.Lights; - -public class PointLight : Light -{ - public float Linear; - public float Quadratic; - - public PointLight(Texture icon, UObject point) : base(icon, point) - { - if (!point.TryGetValue(out float radius, "SourceRadius", "AttenuationRadius")) - radius = 1.0f; - - radius *= Constants.SCALE_DOWN_RATIO; - Linear = 4.5f / radius; - Quadratic = 75.0f / MathF.Pow(radius, 2.0f); - } - - public PointLight(FGuid model, Texture icon, UObject parent, UObject point, Transform transform) : base(model, icon, parent, point, transform) - { - if (!point.TryGetValue(out float radius, "AttenuationRadius", "SourceRadius")) - radius = 1.0f; - - radius *= Constants.SCALE_DOWN_RATIO; - Linear = 4.5f / radius; - Quadratic = 75.0f / MathF.Pow(radius, 2.0f); - } - - public override void Render(int i, Shader shader) - { - base.Render(i, shader); - shader.SetUniform($"uLights[{i}].Linear", Linear); - shader.SetUniform($"uLights[{i}].Quadratic", Quadratic); - - shader.SetUniform($"uLights[{i}].Type", 0); - } - - public override void ImGuiLight() - { - base.ImGuiLight(); - SnimGui.Layout("Linear");ImGui.PushID(3); - ImGui.DragFloat("", ref Linear, 0.1f); - ImGui.PopID();SnimGui.Layout("Quadratic");ImGui.PushID(4); - ImGui.DragFloat("", ref Quadratic, 0.1f);ImGui.PopID(); - } -} diff --git a/FModel/Views/Snooper/Lights/SpotLight.cs b/FModel/Views/Snooper/Lights/SpotLight.cs deleted file mode 100644 index 267dde9c..00000000 --- a/FModel/Views/Snooper/Lights/SpotLight.cs +++ /dev/null @@ -1,58 +0,0 @@ -using CUE4Parse.UE4.Assets.Exports; -using CUE4Parse.UE4.Objects.Core.Misc; -using FModel.Views.Snooper.Shading; -using ImGuiNET; - -namespace FModel.Views.Snooper.Lights; - -public class SpotLight : Light -{ - public float Attenuation; - public float InnerConeAngle; - public float OuterConeAngle; - - public SpotLight(Texture icon, UObject spot) : base(icon, spot) - { - if (!spot.TryGetValue(out Attenuation, "SourceRadius", "AttenuationRadius")) - Attenuation = 1.0f; - - Attenuation *= Constants.SCALE_DOWN_RATIO; - InnerConeAngle = spot.GetOrDefault("InnerConeAngle", 50.0f); - OuterConeAngle = spot.GetOrDefault("OuterConeAngle", InnerConeAngle + 10); - if (OuterConeAngle < InnerConeAngle) - InnerConeAngle = OuterConeAngle - 10; - } - - public SpotLight(FGuid model, Texture icon, UObject parent, UObject spot, Transform transform) : base(model, icon, parent, spot, transform) - { - if (!spot.TryGetValue(out Attenuation, "AttenuationRadius", "SourceRadius")) - Attenuation = 1.0f; - - Attenuation *= Constants.SCALE_DOWN_RATIO; - InnerConeAngle = spot.GetOrDefault("InnerConeAngle", 50.0f); - OuterConeAngle = spot.GetOrDefault("OuterConeAngle", InnerConeAngle + 10); - if (OuterConeAngle < InnerConeAngle) - InnerConeAngle = OuterConeAngle - 10; - } - - public override void Render(int i, Shader shader) - { - base.Render(i, shader); - shader.SetUniform($"uLights[{i}].Attenuation", Attenuation); - shader.SetUniform($"uLights[{i}].InnerConeAngle", InnerConeAngle); - shader.SetUniform($"uLights[{i}].OuterConeAngle", OuterConeAngle); - - shader.SetUniform($"uLights[{i}].Type", 1); - } - - public override void ImGuiLight() - { - base.ImGuiLight(); - SnimGui.Layout("Attenuation");ImGui.PushID(3); - ImGui.DragFloat("", ref Attenuation, 0.1f);ImGui.PopID(); - SnimGui.Layout("Inner Cone Angle");ImGui.PushID(4); - ImGui.DragFloat("", ref InnerConeAngle, 0.1f, 0.0f, 90.0f, "%.1f°");ImGui.PopID(); - SnimGui.Layout("Outer Cone Angle");ImGui.PushID(5); - ImGui.DragFloat("", ref OuterConeAngle, 0.1f, 0.0f, 90.0f, "%.1f°");ImGui.PopID(); - } -} diff --git a/FModel/Views/Snooper/Models/Attachment.cs b/FModel/Views/Snooper/Models/Attachment.cs deleted file mode 100644 index 1031a35b..00000000 --- a/FModel/Views/Snooper/Models/Attachment.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Collections.Generic; -using System.Numerics; -using CUE4Parse.UE4.Objects.Core.Math; - -namespace FModel.Views.Snooper.Models; - -public class Attachment -{ - private string _modelName; - private string _attachedTo; - private readonly List _attachedFor; - private Matrix4x4 _oldRelation; - - public bool IsAttached => _attachedTo.Length > 0; - public bool IsAttachment => _attachedFor.Count > 0; - - public string Icon => IsAttachment ? "link_has" : IsAttached ? "link_on" : "link_off"; - public string Tooltip => IsAttachment ? $"Is Attachment For:\n{string.Join("\n", _attachedFor)}" : IsAttached ? $"Is Attached To {_attachedTo}" : "Not Attached To Any Socket Nor Attachment For Any Model"; - - public Attachment(string modelName) - { - _modelName = modelName; - _attachedTo = string.Empty; - _attachedFor = new List(); - } - - public void Attach(IRenderableModel attachedTo, Transform transform, Socket socket, SocketAttachementInfo info) - { - socket.AttachedModels.Add(info); - - _attachedTo = $"'{socket.Name}' from '{attachedTo.Name}'{(!socket.BoneName.IsNone ? $" at '{socket.BoneName}'" : "")}"; - attachedTo.Attachments.AddAttachment(_modelName); - - // reset PRS to 0 so it's attached to the actual position (can be transformed relative to the socket later by the user) - _oldRelation = transform.Relation; - transform.Position = FVector.ZeroVector; - transform.Rotation = FQuat.Identity; - transform.Scale = FVector.OneVector; - } - - public void Detach(IRenderableModel attachedTo, Transform transform, Socket socket, SocketAttachementInfo info) - { - socket.AttachedModels.Remove(info); - SafeDetach(attachedTo, transform); - } - - public void SafeDetach(IRenderableModel attachedTo, Transform transform) - { - _attachedTo = string.Empty; - attachedTo.Attachments.RemoveAttachment(_modelName); - - transform.Relation = _oldRelation; - } - - public void AddAttachment(string modelName) => _attachedFor.Add($"'{modelName}'"); - public void RemoveAttachment(string modelName) => _attachedFor.Remove($"'{modelName}'"); -} diff --git a/FModel/Views/Snooper/Models/Collision.cs b/FModel/Views/Snooper/Models/Collision.cs deleted file mode 100644 index 05e2ce44..00000000 --- a/FModel/Views/Snooper/Models/Collision.cs +++ /dev/null @@ -1,228 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Numerics; -using CUE4Parse.UE4.Objects.Core.Math; -using CUE4Parse.UE4.Objects.PhysicsEngine; -using CUE4Parse.UE4.Objects.UObject; -using FModel.Views.Snooper.Buffers; -using FModel.Views.Snooper.Shading; -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper.Models; - -public class Collision : IDisposable -{ - private const int Slices = 16; - private const int Stacks = 8; - private const float SectorStep = 2 * MathF.PI / Slices; - private const float StackStep = MathF.PI / Stacks; - - private readonly int[] _indexData; - private readonly FVector[] _vertexData; - private readonly Transform _transform; - public readonly string LoweredBoneName; - - private int _handle; - private BufferObject _ebo { get; set; } - private BufferObject _vbo { get; set; } - private VertexArrayObject _vao { get; set; } - - private Collision() - { - _indexData = []; - _vertexData = []; - _transform = Transform.Identity; - } - - private Collision(FName boneName) : this() - { - LoweredBoneName = boneName.Text.ToLower(); - } - - public Collision(FKConvexElem convexElems, FName boneName = default) : this(boneName) - { - _indexData = convexElems.IndexData; - _vertexData = convexElems.VertexData; - _transform = new Transform - { - Position = convexElems.Transform.Translation * Constants.SCALE_DOWN_RATIO, - Rotation = convexElems.Transform.Rotation, - Scale = convexElems.Transform.Scale3D - }; - } - - public Collision(FKSphereElem sphereElem, FName boneName = default) : this(boneName) - { - _vertexData = new FVector[(Slices + 1) * (Stacks + 1)]; - for (var i = 0; i <= Stacks; i++) - { - var stackAngle = MathF.PI / 2 - i * StackStep; - var xy = MathF.Cos(stackAngle); - var z = MathF.Sin(stackAngle); - - for (var j = 0; j <= Slices; j++) - { - var sectorAngle = j * SectorStep; - var x = xy * MathF.Cos(sectorAngle); - var y = xy * MathF.Sin(sectorAngle); - _vertexData[i * (Slices + 1) + j] = new FVector(x, y, z); - } - } - - _indexData = new int[Stacks * Slices * 6]; - for (var i = 0; i < Stacks; i++) - { - for (var j = 0; j < Slices; j++) - { - var a = i * (Slices + 1) + j; - var b = a + Slices + 1; - _indexData[(i * Slices + j) * 6 + 0] = a; - _indexData[(i * Slices + j) * 6 + 1] = b; - _indexData[(i * Slices + j) * 6 + 2] = a + 1; - _indexData[(i * Slices + j) * 6 + 3] = b; - _indexData[(i * Slices + j) * 6 + 4] = b + 1; - _indexData[(i * Slices + j) * 6 + 5] = a + 1; - } - } - - _transform = new Transform - { - Position = sphereElem.Center * Constants.SCALE_DOWN_RATIO, - Scale = new FVector(sphereElem.Radius) - }; - } - - public Collision(FKBoxElem boxElem, FName boneName = default) : this(boneName) - { - _vertexData = - [ - new FVector(-boxElem.X, -boxElem.Y, -boxElem.Z), - new FVector(boxElem.X, -boxElem.Y, -boxElem.Z), - new FVector(boxElem.X, boxElem.Y, -boxElem.Z), - new FVector(-boxElem.X, boxElem.Y, -boxElem.Z), - new FVector(-boxElem.X, -boxElem.Y, boxElem.Z), - new FVector(boxElem.X, -boxElem.Y, boxElem.Z), - new FVector(boxElem.X, boxElem.Y, boxElem.Z), - new FVector(-boxElem.X, boxElem.Y, boxElem.Z) - ]; - - _indexData = - [ - 0, 1, 2, 2, 3, 0, - 1, 5, 6, 6, 2, 1, - 5, 4, 7, 7, 6, 5, - 4, 0, 3, 3, 7, 4, - 3, 2, 6, 6, 7, 3, - 4, 5, 1, 1, 0, 4 - ]; - - _transform = new Transform - { - Position = boxElem.Center * Constants.SCALE_DOWN_RATIO, - Rotation = boxElem.Rotation.Quaternion(), - Scale = new FVector(.5f) - }; - } - - public Collision(FKSphylElem sphylElem, FName boneName = default) - : this(sphylElem.Length, [sphylElem.Radius, sphylElem.Radius], sphylElem.Center, sphylElem.Rotation, boneName) {} - public Collision(FKTaperedCapsuleElem taperedCapsuleElem, FName boneName = default) - : this(taperedCapsuleElem.Length, [taperedCapsuleElem.Radius1, taperedCapsuleElem.Radius0], taperedCapsuleElem.Center, taperedCapsuleElem.Rotation, boneName) {} - - private Collision(float length, float[] radius, FVector center = default, FRotator rotator = default, FName boneName = default) : this(boneName) - { - int vLength = 0; - int half = Slices / 2; - int k2 = (Slices + 1) * (Stacks + 1); - - _vertexData = new FVector[k2 + Slices + 1]; - for(int i = 0; i < 2; ++i) - { - float h = -length / 2.0f + i * length; - int start = i == 0 ? Stacks / 2 : 0; - int end = i == 0 ? Stacks : Stacks / 2; - - for(int j = start; j <= end; ++j) - { - var stackAngle = MathF.PI / 2 - j * StackStep; - var xy = radius[i] * MathF.Cos(stackAngle); - var z = radius[i] * MathF.Sin(stackAngle) + h; - - for(int k = 0; k <= Slices; ++k) - { - var sectorAngle = k * SectorStep; - var x = xy * MathF.Cos(sectorAngle); - var y = xy * MathF.Sin(sectorAngle); - _vertexData[vLength++] = new FVector(x, y, z); - } - } - } - - var indices = new List(); - AddIndicesForSlices(indices, ref k2); - indices.AddRange(new[] {0, k2, k2, k2 - half, half, half}); - AddIndicesForStacks(indices); - half /= 2; - indices.AddRange(new[] {half, k2 - half * 3, k2 - half * 3, half * 3, k2 - half, k2 - half}); - AddIndicesForStacks(indices, Stacks / 2); - _indexData = indices.ToArray(); - - _transform = new Transform - { - Position = center * Constants.SCALE_DOWN_RATIO, - Rotation = rotator.Quaternion() - }; - } - - private void AddIndicesForSlices(List indices, ref int k2) - { - for(int k1 = 0; k1 < Slices; ++k1, ++k2) - { - indices.AddRange(new[] {k1, k1 + 1, k1 + 1, k2, k2 + 1, k2 + 1}); - } - } - - private void AddIndicesForStacks(List indices, int start = 0) - { - for (int k1 = start; k1 < Stacks * Slices + Slices; k1 += Slices + 1) - { - if (k1 == Stacks / 2 * (Slices + 1) + start) continue; - indices.AddRange(new[] {k1, k1 + Slices + 1, k1 + Slices + 1, k1 + Slices / 2, k1 + Slices / 2 + Slices + 1, k1 + Slices / 2 + Slices + 1}); - } - } - - public void Setup() - { - _handle = GL.CreateProgram(); - _ebo = new BufferObject(_indexData, BufferTarget.ElementArrayBuffer); - _vbo = new BufferObject(_vertexData, BufferTarget.ArrayBuffer); - _vao = new VertexArrayObject(_vbo, _ebo); - - _vao.VertexAttributePointer(0, 3, VertexAttribPointerType.Float, 1, 0); - _vao.Unbind(); - } - - public void Render(Shader shader, Matrix4x4 boneMatrix) - { - shader.SetUniform("uCollisionMatrix", _transform.Matrix * boneMatrix); - - _vao.Bind(); - if (_indexData.Length > 0) - { - GL.DrawElements(PrimitiveType.Triangles, _ebo.Size, DrawElementsType.UnsignedInt, 0); - } - else - { - GL.DrawArrays(PrimitiveType.Points, 0, _vbo.Size); - } - _vao.Unbind(); - } - - public void Dispose() - { - _ebo?.Dispose(); - _vbo?.Dispose(); - _vao?.Dispose(); - GL.DeleteProgram(_handle); - } -} diff --git a/FModel/Views/Snooper/Models/EAttribute.cs b/FModel/Views/Snooper/Models/EAttribute.cs deleted file mode 100644 index 06badbb2..00000000 --- a/FModel/Views/Snooper/Models/EAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace FModel.Views.Snooper.Models; - -public enum EAttribute -{ - Index, - Position, - Normals, - Tangent, - UVs, - Layer, - Colors, - BonesId, - BonesWeight -} diff --git a/FModel/Views/Snooper/Models/Grid.cs b/FModel/Views/Snooper/Models/Grid.cs deleted file mode 100644 index d6ee539a..00000000 --- a/FModel/Views/Snooper/Models/Grid.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Numerics; -using FModel.Views.Snooper.Buffers; -using FModel.Views.Snooper.Shading; -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper.Models; - -public class Grid : IDisposable -{ - private int _handle; - - private BufferObject _ebo; - private BufferObject _vbo; - private VertexArrayObject _vao; - - private Shader _shader; - - public readonly uint[] Indices = { 0, 1, 2, 3, 4, 5 }; - public readonly float[] Vertices = { - 1f, 1f, 0f, - -1f, -1f, 0f, - -1f, 1f, 0f, - -1f, -1f, 0f, - 1f, 1f, 0f, - 1f, -1f, 0 - }; - - public Grid() {} - - public void Setup() - { - _handle = GL.CreateProgram(); - - _ebo = new BufferObject(Indices, BufferTarget.ElementArrayBuffer); - _vbo = new BufferObject(Vertices, BufferTarget.ArrayBuffer); - _vao = new VertexArrayObject(_vbo, _ebo); - - _shader = new Shader("grid"); - - _vao.VertexAttributePointer(0, 3, VertexAttribPointerType.Float, 3, 0); // position - } - - public void Render(Matrix4x4 viewMatrix, Matrix4x4 projMatrix, float near, float far) - { - GL.Disable(EnableCap.CullFace); - GL.Disable(EnableCap.DepthTest); - - _vao.Bind(); - - _shader.Use(); - _shader.SetUniform("view", viewMatrix); - _shader.SetUniform("proj", projMatrix); - _shader.SetUniform("uNear", near); - _shader.SetUniform("uFar", far); - - GL.DrawArrays(PrimitiveType.Triangles, 0, Indices.Length); - - GL.Enable(EnableCap.DepthTest); - GL.Enable(EnableCap.CullFace); - } - - public void Dispose() - { - _ebo?.Dispose(); - _vbo?.Dispose(); - _vao?.Dispose(); - _shader?.Dispose(); - GL.DeleteProgram(_handle); - } -} diff --git a/FModel/Views/Snooper/Models/IRenderableModel.cs b/FModel/Views/Snooper/Models/IRenderableModel.cs deleted file mode 100644 index aff17989..00000000 --- a/FModel/Views/Snooper/Models/IRenderableModel.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Numerics; -using CUE4Parse_Conversion; -using CUE4Parse.UE4.Objects.Core.Math; -using FModel.Views.Snooper.Buffers; -using FModel.Views.Snooper.Shading; - -namespace FModel.Views.Snooper.Models; - -public interface IRenderableModel : IExportableThing, IDisposable -{ - protected int Handle { get; set; } - protected BufferObject Ebo { get; set; } - protected BufferObject Vbo { get; set; } - protected BufferObject MatrixVbo { get; set; } - protected VertexArrayObject Vao { get; set; } - - public string Path { get; } - public string Name { get; } - public string Type { get; } - public int UvCount { get; } - public uint[] Indices { get; protected set; } - public float[] Vertices { get; protected set; } - public Section[] Sections { get; protected set; } - public List Transforms { get; } - public Attachment Attachments { get; } - - public FBox Box { get; protected init; } - public List Sockets { get; } - public List Collisions { get; } - public Material[] Materials { get; protected init; } - public bool IsTwoSided { get; internal set; } - public bool IsProp { get; internal set; } - - public bool HasSockets { get; } - public bool HasCollisions { get; } - public int TransformsCount { get; } - - public bool IsSetup { get; set; } - public bool IsVisible { get; set; } - public bool IsSelected { get; set; } - public bool ShowWireframe { get; set; } - public bool ShowCollisions { get; set; } - public int SelectedInstance { get; set; } - - public void Setup(Options options); - public void SetupInstances(); - public void Render(Shader shader, Texture checker = null, bool outline = false); - public void RenderCollision(Shader shader); - public void PickingRender(Shader shader); - public void Update(Options options); - public void AddInstance(Transform transform); - - public Transform GetTransform(); -} - -public interface IExportableThing -{ - public void AddToExportSession(ExportSession session); -} diff --git a/FModel/Views/Snooper/Models/Morph.cs b/FModel/Views/Snooper/Models/Morph.cs deleted file mode 100644 index c769b359..00000000 --- a/FModel/Views/Snooper/Models/Morph.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Collections.Generic; -using CUE4Parse.UE4.Assets.Exports.Animation; -using CUE4Parse.UE4.Objects.Core.Math; -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper.Models; - -public class Morph : IDisposable -{ - private int _handle; - - public static readonly int VertexSize = 6; // Position + Tangent - - public readonly string Name; - public readonly float[] Vertices; - - public Morph(float[] vertices, int vertexSize, UMorphTarget morphTarget) - { - Name = morphTarget.Name; - Vertices = new float[vertices.Length / vertexSize * VertexSize]; - - bool TryFindVertex(uint index, out FVector positionDelta, out FVector tangentDelta) - { - foreach (var vertex in morphTarget.MorphLODModels[0].Vertices) - { - if (vertex.SourceIdx == index) - { - positionDelta = vertex.PositionDelta; - tangentDelta = vertex.TangentZDelta; - return true; - } - } - positionDelta = FVector.ZeroVector; - tangentDelta = FVector.ZeroVector; - return false; - } - - for (int i = 0; i < vertices.Length; i += vertexSize) - { - var count = 0; - var baseIndex = i / vertexSize * VertexSize; - if (TryFindVertex((uint) vertices[i + 0], out var positionDelta, out var tangentDelta)) - { - Vertices[baseIndex + count++] = vertices[i + 1] + positionDelta.X * Constants.SCALE_DOWN_RATIO; - Vertices[baseIndex + count++] = vertices[i + 2] + positionDelta.Z * Constants.SCALE_DOWN_RATIO; - Vertices[baseIndex + count++] = vertices[i + 3] + positionDelta.Y * Constants.SCALE_DOWN_RATIO; - Vertices[baseIndex + count++] = vertices[i + 7] + tangentDelta.X; - Vertices[baseIndex + count++] = vertices[i + 8] + tangentDelta.Z; - Vertices[baseIndex + count++] = vertices[i + 9] + tangentDelta.Y; - } - else - { - Vertices[baseIndex + count++] = vertices[i + 1]; - Vertices[baseIndex + count++] = vertices[i + 2]; - Vertices[baseIndex + count++] = vertices[i + 3]; - Vertices[baseIndex + count++] = vertices[i + 7]; - Vertices[baseIndex + count++] = vertices[i + 8]; - Vertices[baseIndex + count++] = vertices[i + 9]; - } - } - } - - public Morph(float[] vertices, Dictionary dict, UMorphTarget morphTarget, uint index = 0) - { - Name = morphTarget.Name; - Vertices = new float[vertices.Length]; - Array.Copy(vertices, Vertices, vertices.Length); - - foreach (var vert in morphTarget.MorphLODModels[index].Vertices) - { - var count = 0; - if (dict.TryGetValue(vert.SourceIdx, out var baseIndex)) - { - Vertices[baseIndex + count++] += vert.PositionDelta.X * Constants.SCALE_DOWN_RATIO; - Vertices[baseIndex + count++] += vert.PositionDelta.Z * Constants.SCALE_DOWN_RATIO; - Vertices[baseIndex + count++] += vert.PositionDelta.Y * Constants.SCALE_DOWN_RATIO; - Vertices[baseIndex + count++] += vert.TangentZDelta.X; - Vertices[baseIndex + count++] += vert.TangentZDelta.Z; - Vertices[baseIndex + count++] += vert.TangentZDelta.Y; - } - } - } - - public void Setup() - { - _handle = GL.CreateProgram(); - } - - public void Dispose() - { - GL.DeleteProgram(_handle); - } -} diff --git a/FModel/Views/Snooper/Models/Section.cs b/FModel/Views/Snooper/Models/Section.cs deleted file mode 100644 index 6720e167..00000000 --- a/FModel/Views/Snooper/Models/Section.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Numerics; -using FModel.Views.Snooper.Shading; - -namespace FModel.Views.Snooper.Models; - -public class Section -{ - public readonly int MaterialIndex; - public readonly int FacesCount; - public readonly int FirstFaceIndex; - public readonly IntPtr FirstFaceIndexPtr; - public readonly Vector3 Color; - - public bool Show; - - public Section(int index, int facesCount, int firstFaceIndex) - { - MaterialIndex = Math.Max(0, index); - FacesCount = facesCount; - FirstFaceIndex = firstFaceIndex; - FirstFaceIndexPtr = new IntPtr(FirstFaceIndex * sizeof(uint)); - Color = Constants.COLOR_PALETTE[MaterialIndex % Constants.PALETTE_LENGTH]; - } - - public void SetupMaterial(Material material) - { - material.IsUsed = true; - Show = !material.Parameters.IsNull && !material.Parameters.IsTranslucent; - } -} diff --git a/FModel/Views/Snooper/Models/SkeletalModel.cs b/FModel/Views/Snooper/Models/SkeletalModel.cs deleted file mode 100644 index 4f9fcd2d..00000000 --- a/FModel/Views/Snooper/Models/SkeletalModel.cs +++ /dev/null @@ -1,203 +0,0 @@ -using System.Collections.Generic; -using System.Numerics; -using CUE4Parse_Conversion.Dto; -using CUE4Parse.UE4.Assets.Exports.Animation; -using CUE4Parse.UE4.Assets.Exports.SkeletalMesh; -using CUE4Parse.UE4.Objects.Core.Math; -using CUE4Parse.UE4.Objects.PhysicsEngine; -using CUE4Parse.UE4.Objects.UObject; -using FModel.Views.Snooper.Animations; -using FModel.Views.Snooper.Buffers; -using FModel.Views.Snooper.Shading; -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper.Models; - -public class SkeletalModel : UModel -{ - private BufferObject _morphVbo; - - public readonly Skeleton Skeleton; - public readonly List Morphs; - - public bool HasMorphTargets => Morphs.Count > 0; - - public float MorphTime; - - public SkeletalModel(USkeletalMesh export, SkeletalMeshDto skeletalMesh, Transform transform = null) - : base(export, skeletalMesh.LODs[LodLevel], export.Materials, skeletalMesh.LODs[LodLevel].Vertices, skeletalMesh.LODs.Count, transform) - { - Box = skeletalMesh.Bounds * Constants.SCALE_DOWN_RATIO; - Skeleton = new Skeleton(export.ReferenceSkeleton); - - var sockets = new List(); - sockets.AddRange(export.Sockets); - if (export.Skeleton.TryLoad(out USkeleton skeleton)) - { - Skeleton.Name = skeleton.Name; - Skeleton.Guid = skeleton.Guid; - // Skeleton.Merge(skeleton.ReferenceSkeleton); - sockets.AddRange(skeleton.Sockets); - } - - for (int i = 0; i < sockets.Count; i++) - { - if (sockets[i].Load() is not { } socket) continue; - Sockets.Add(new Socket(socket)); - } - - if (export.PhysicsAsset.TryLoad(out UPhysicsAsset physicsAsset)) - { - foreach (var skeletalBodySetup in physicsAsset.SkeletalBodySetups) - { - if (!skeletalBodySetup.TryLoad(out USkeletalBodySetup bodySetup) || bodySetup.AggGeom == null) continue; - foreach (var convexElem in bodySetup.AggGeom.ConvexElems) - { - Collisions.Add(new Collision(convexElem, bodySetup.BoneName)); - } - foreach (var sphereElem in bodySetup.AggGeom.SphereElems) - { - Collisions.Add(new Collision(sphereElem, bodySetup.BoneName)); - } - foreach (var boxElem in bodySetup.AggGeom.BoxElems) - { - Collisions.Add(new Collision(boxElem, bodySetup.BoneName)); - } - foreach (var sphylElem in bodySetup.AggGeom.SphylElems) - { - Collisions.Add(new Collision(sphylElem, bodySetup.BoneName)); - } - foreach (var taperedCapsuleElem in bodySetup.AggGeom.TaperedCapsuleElems) - { - Collisions.Add(new Collision(taperedCapsuleElem, bodySetup.BoneName)); - } - } - } - - Morphs = []; - if (export.MorphTargets.Length == 0) return; - - export.PopulateMorphTargetVerticesData(); - - var verticesCount = Vertices.Length / VertexSize; - var cachedVertices = new float[verticesCount * Morph.VertexSize]; - var vertexLookup = new Dictionary(verticesCount); - for (int i = 0; i < Vertices.Length; i += VertexSize) - { - var count = 0; - var baseIndex = i / VertexSize * Morph.VertexSize; - vertexLookup[(uint) Vertices[i]] = baseIndex; - { - cachedVertices[baseIndex + count++] = Vertices[i + 1]; - cachedVertices[baseIndex + count++] = Vertices[i + 2]; - cachedVertices[baseIndex + count++] = Vertices[i + 3]; - cachedVertices[baseIndex + count++] = Vertices[i + 7]; - cachedVertices[baseIndex + count++] = Vertices[i + 8]; - cachedVertices[baseIndex + count++] = Vertices[i + 9]; - } - } - - foreach (var morph in export.MorphTargets) - { - if (!morph.TryLoad(out UMorphTarget morphTarget) || morphTarget.MorphLODModels.Length <= skeletalMesh.LODs[LodLevel].SourceLodIndex || - morphTarget.MorphLODModels[skeletalMesh.LODs[LodLevel].SourceLodIndex].Vertices.Length < 1) - continue; - - Morphs.Add(new Morph(cachedVertices, vertexLookup, morphTarget, skeletalMesh.LODs[LodLevel].SourceLodIndex)); - } - } - - public SkeletalModel(USkeleton export, FBox box) : base(export) - { - Indices = []; - Materials = []; - Vertices = []; - Sections = []; - AddInstance(Transform.Identity); - - Box = box * Constants.SCALE_DOWN_RATIO; - Morphs = []; - Skeleton = new Skeleton(export.ReferenceSkeleton); - Skeleton.Name = export.Name; - Skeleton.Guid = export.Guid; - - for (int i = 0; i < export.Sockets.Length; i++) - { - if (export.Sockets[i].Load() is not { } socket) continue; - Sockets.Add(new Socket(socket)); - } - - IsVisible = true; - } - - public override void Setup(Options options) - { - base.Setup(options); - - Skeleton.Setup(); - if (!HasMorphTargets) return; - - for (int morph = 0; morph < Morphs.Count; morph++) - { - Morphs[morph].Setup(); - if (morph == 0) - _morphVbo = new BufferObject(Morphs[morph].Vertices, BufferTarget.ArrayBuffer); - } - - Vao.Bind(); - Vao.VertexAttributePointer(13, 3, VertexAttribPointerType.Float, Morph.VertexSize, 0); // morph position - Vao.VertexAttributePointer(14, 3, VertexAttribPointerType.Float, Morph.VertexSize, 0); // morph tangent - Vao.Unbind(); - } - - public override void RenderCollision(Shader shader) - { - base.RenderCollision(shader); - - GL.Disable(EnableCap.DepthTest); - GL.Disable(EnableCap.CullFace); - GL.PolygonMode(TriangleFace.FrontAndBack, PolygonMode.Line); - foreach (var collision in Collisions) - { - var boneMatrix = Matrix4x4.Identity; - if (Skeleton.BonesByLoweredName.TryGetValue(collision.LoweredBoneName, out var bone)) - boneMatrix = Skeleton.GetBoneMatrix(bone); - - collision.Render(shader, boneMatrix); - } - GL.PolygonMode(TriangleFace.FrontAndBack, PolygonMode.Fill); - GL.Enable(EnableCap.CullFace); - GL.Enable(EnableCap.DepthTest); - } - - public void Render(Shader shader) - { - shader.SetUniform("uMorphTime", MorphTime); - shader.SetUniform("uIsSpline", false); - Skeleton.Render(shader); - } - - public void RenderBones(Shader shader) - { - shader.SetUniform("uInstanceMatrix", GetTransform().Matrix); - Skeleton.RenderBones(); - } - - public void UpdateMorph(int index) - { - _morphVbo.Update(Morphs[index].Vertices); - } - - public override void Dispose() - { - Skeleton?.Dispose(); - if (HasMorphTargets) _morphVbo.Dispose(); - foreach (var morph in Morphs) - { - morph?.Dispose(); - } - Morphs.Clear(); - - base.Dispose(); - } -} diff --git a/FModel/Views/Snooper/Models/Skybox.cs b/FModel/Views/Snooper/Models/Skybox.cs deleted file mode 100644 index d025bbc8..00000000 --- a/FModel/Views/Snooper/Models/Skybox.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.Numerics; -using FModel.Views.Snooper.Buffers; -using FModel.Views.Snooper.Shading; -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper.Models; - -public class Skybox : IDisposable -{ - private int _handle; - - private BufferObject _ebo; - private BufferObject _vbo; - private VertexArrayObject _vao; - - private string[] _textures = { "px", "nx", "py", "ny", "pz", "nz" }; - - private Texture _cubeMap; - private Shader _shader; - - public readonly uint[] Indices = { 0, 1, 3, 1, 2, 3 }; - public readonly float[] Vertices = { - //X Y Z - -0.5f, -0.5f, -0.5f, - 0.5f, -0.5f, -0.5f, - 0.5f, 0.5f, -0.5f, - 0.5f, 0.5f, -0.5f, - -0.5f, 0.5f, -0.5f, - -0.5f, -0.5f, -0.5f, - - -0.5f, -0.5f, 0.5f, - 0.5f, -0.5f, 0.5f, - 0.5f, 0.5f, 0.5f, - 0.5f, 0.5f, 0.5f, - -0.5f, 0.5f, 0.5f, - -0.5f, -0.5f, 0.5f, - - -0.5f, 0.5f, 0.5f, - -0.5f, 0.5f, -0.5f, - -0.5f, -0.5f, -0.5f, - -0.5f, -0.5f, -0.5f, - -0.5f, -0.5f, 0.5f, - -0.5f, 0.5f, 0.5f, - - 0.5f, 0.5f, 0.5f, - 0.5f, 0.5f, -0.5f, - 0.5f, -0.5f, -0.5f, - 0.5f, -0.5f, -0.5f, - 0.5f, -0.5f, 0.5f, - 0.5f, 0.5f, 0.5f, - - -0.5f, -0.5f, -0.5f, - 0.5f, -0.5f, -0.5f, - 0.5f, -0.5f, 0.5f, - 0.5f, -0.5f, 0.5f, - -0.5f, -0.5f, 0.5f, - -0.5f, -0.5f, -0.5f, - - -0.5f, 0.5f, -0.5f, - 0.5f, 0.5f, -0.5f, - 0.5f, 0.5f, 0.5f, - 0.5f, 0.5f, 0.5f, - -0.5f, 0.5f, 0.5f, - -0.5f, 0.5f, -0.5f - }; - - public Skybox() {} - - public void Setup() - { - _handle = GL.CreateProgram(); - - _ebo = new BufferObject(Indices, BufferTarget.ElementArrayBuffer); - _vbo = new BufferObject(Vertices, BufferTarget.ArrayBuffer); - _vao = new VertexArrayObject(_vbo, _ebo); - - _cubeMap = new Texture(_textures); - _shader = new Shader("skybox"); - - _vao.VertexAttributePointer(0, 3, VertexAttribPointerType.Float, 3, 0); // position - } - - public void Render(Matrix4x4 viewMatrix, Matrix4x4 projMatrix) - { - GL.Disable(EnableCap.CullFace); - GL.DepthFunc(DepthFunction.Lequal); - - _vao.Bind(); - _shader.Use(); - - viewMatrix.M41 = 0; - viewMatrix.M42 = 0; - viewMatrix.M43 = 0; - _shader.SetUniform("uView", viewMatrix); - _shader.SetUniform("uProjection", projMatrix); - - _cubeMap.Bind(TextureUnit.Texture0); - _shader.SetUniform("cubemap", 0); - - GL.DrawArrays(PrimitiveType.Triangles, 0, 36); - - GL.DepthFunc(DepthFunction.Less); - GL.Enable(EnableCap.CullFace); - } - - public void Dispose() - { - _ebo?.Dispose(); - _vbo?.Dispose(); - _vao?.Dispose(); - _shader?.Dispose(); - GL.DeleteProgram(_handle); - } -} diff --git a/FModel/Views/Snooper/Models/Socket.cs b/FModel/Views/Snooper/Models/Socket.cs deleted file mode 100644 index 8199b16b..00000000 --- a/FModel/Views/Snooper/Models/Socket.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using CUE4Parse.UE4.Assets.Exports.SkeletalMesh; -using CUE4Parse.UE4.Assets.Exports.StaticMesh; -using CUE4Parse.UE4.Objects.Core.Misc; -using CUE4Parse.UE4.Objects.UObject; - -namespace FModel.Views.Snooper.Models; - -public struct SocketAttachementInfo -{ - public FGuid Guid; - public int Instance; -} - -public class Socket : IDisposable -{ - public readonly string Name; - public readonly FName BoneName; - public readonly Transform Transform; - public readonly bool IsVirtual; - - public readonly List AttachedModels; - public bool IsDaron => AttachedModels.Count > 0; - - private Socket() - { - Transform = Transform.Identity; - AttachedModels = new List(); - } - - public Socket(string name, FName boneName, Transform transform, bool isVirtual) : this() - { - Name = name; - BoneName = boneName; - Transform = transform; - IsVirtual = isVirtual; - } - - public Socket(UStaticMeshSocket socket) : this() - { - Name = socket.SocketName.Text; - Transform.Rotation = socket.RelativeRotation.Quaternion(); - Transform.Position = socket.RelativeLocation * Constants.SCALE_DOWN_RATIO; - Transform.Scale = socket.RelativeScale; - } - - public Socket(USkeletalMeshSocket socket) : this() - { - Name = socket.SocketName.Text; - BoneName = socket.BoneName; - Transform.Rotation = socket.RelativeRotation.Quaternion(); - Transform.Position = socket.RelativeLocation * Constants.SCALE_DOWN_RATIO; - Transform.Scale = socket.RelativeScale; - } - - public void Dispose() - { - AttachedModels.Clear(); - } -} diff --git a/FModel/Views/Snooper/Models/SplineModel.cs b/FModel/Views/Snooper/Models/SplineModel.cs deleted file mode 100644 index 2a11919e..00000000 --- a/FModel/Views/Snooper/Models/SplineModel.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System.Collections.Generic; -using System.Runtime.InteropServices; -using CUE4Parse_Conversion.Dto; -using CUE4Parse.UE4.Assets.Exports.Component.SplineMesh; -using CUE4Parse.UE4.Assets.Exports.StaticMesh; -using CUE4Parse.UE4.Objects.Core.Math; -using FModel.Views.Snooper.Buffers; -using FModel.Views.Snooper.Shading; -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper.Models; - -public class SplineModel : StaticModel -{ - [StructLayout(LayoutKind.Sequential)] - public struct GpuParams - { - public int ForwardAxis; - public float SplineBoundaryMin; - public float SplineBoundaryMax; - public bool bSmoothInterpRollScale; - - public FVector MeshOrigin; - public int _padding0; - public FVector MeshBoxExtent; - public int _padding1; - - public FVector StartPos; - public float StartRoll; - public FVector StartTangent; - public int _padding2; - public FVector2D StartScale; - public FVector2D StartOffset; - public FVector EndPos; - public float EndRoll; - public FVector EndTangent; - public int _padding3; - public FVector2D EndScale; - public FVector2D EndOffset; - - public FVector SplineUpDir; - public int _padding4; - - public GpuParams(USplineMeshComponent splineMesh) - { - ForwardAxis = (int)splineMesh.ForwardAxis; - SplineBoundaryMin = splineMesh.SplineBoundaryMin; - SplineBoundaryMax = splineMesh.SplineBoundaryMax; - bSmoothInterpRollScale = splineMesh.bSmoothInterpRollScale; - - var b = splineMesh.GetLoadedStaticMesh()?.RenderData?.Bounds ?? new FBoxSphereBounds(); - MeshOrigin = b.Origin * Constants.SCALE_DOWN_RATIO; - MeshBoxExtent = b.BoxExtent * Constants.SCALE_DOWN_RATIO; - - var p = splineMesh.SplineParams; - StartPos = p.StartPos * Constants.SCALE_DOWN_RATIO; - StartRoll = p.StartRoll; - StartTangent = p.StartTangent * Constants.SCALE_DOWN_RATIO; - StartScale = p.StartScale; - StartOffset = p.StartOffset; - EndPos = p.EndPos * Constants.SCALE_DOWN_RATIO; - EndRoll = p.EndRoll; - EndTangent = p.EndTangent * Constants.SCALE_DOWN_RATIO; - EndScale = p.EndScale; - EndOffset = p.EndOffset; - - SplineUpDir = splineMesh.SplineUpDir; - } - } - - private readonly List _splineParams; - private BufferObject _ssbo; - - public SplineModel(UStaticMesh export, StaticMeshDto staticMesh, USplineMeshComponent splineMesh, Transform transform = null) : base(export, staticMesh, transform) - { - _splineParams = [new GpuParams(splineMesh)]; - - Type = "SplineMesh"; - IsVisible = true; - IsTwoSided = true; - } - - public void AddComponent(USplineMeshComponent splineMesh) - { - _splineParams.Add(new GpuParams(splineMesh)); - } - - public override void Setup(Options options) - { - base.Setup(options); - - _ssbo = new BufferObject(_splineParams.ToArray(), BufferTarget.ShaderStorageBuffer); - } - - public void Render(Shader shader) - { - shader.SetUniform("uIsSpline", true); - _ssbo.BindBufferBase(3); - } - - public override void Dispose() - { - base.Dispose(); - _ssbo?.Dispose(); - } -} diff --git a/FModel/Views/Snooper/Models/StaticModel.cs b/FModel/Views/Snooper/Models/StaticModel.cs deleted file mode 100644 index e32e655c..00000000 --- a/FModel/Views/Snooper/Models/StaticModel.cs +++ /dev/null @@ -1,159 +0,0 @@ -using System; -using System.Numerics; -using CUE4Parse_Conversion.Dto; -using CUE4Parse.UE4.Assets.Exports.Material; -using CUE4Parse.UE4.Assets.Exports.StaticMesh; -using CUE4Parse.UE4.Assets.Exports.Texture; -using CUE4Parse.UE4.Objects.Core.Math; -using CUE4Parse.UE4.Objects.PhysicsEngine; -using FModel.Views.Snooper.Shading; -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper.Models; - -public class StaticModel : UModel -{ - public StaticModel(UMaterialInterface unrealMaterial, StaticMeshDto staticMesh) : base(unrealMaterial) - { - var lod = staticMesh.LODs[LodLevel]; - - Indices = new uint[lod.Indices.Length]; - for (int i = 0; i < Indices.Length; i++) - { - Indices[i] = lod.Indices[i]; - } - - Vertices = new float[lod.Vertices.Length * VertexSize]; - for (int i = 0; i < lod.Vertices.Length; i++) - { - var count = 0; - var baseIndex = i * VertexSize; - var vert = lod.Vertices[i]; - Vertices[baseIndex + count++] = i; - Vertices[baseIndex + count++] = vert.Position.X * Constants.SCALE_DOWN_RATIO; - Vertices[baseIndex + count++] = vert.Position.Z * Constants.SCALE_DOWN_RATIO; - Vertices[baseIndex + count++] = vert.Position.Y * Constants.SCALE_DOWN_RATIO; - Vertices[baseIndex + count++] = vert.Normal.X; - Vertices[baseIndex + count++] = vert.Normal.Z; - Vertices[baseIndex + count++] = vert.Normal.Y; - Vertices[baseIndex + count++] = vert.Tangent.X; - Vertices[baseIndex + count++] = vert.Tangent.Z; - Vertices[baseIndex + count++] = vert.Tangent.Y; - Vertices[baseIndex + count++] = vert.Uv.U; - Vertices[baseIndex + count++] = vert.Uv.V; - Vertices[baseIndex + count++] = .5f; - } - - Materials = new Material[1]; - Materials[0] = new Material(unrealMaterial) { IsUsed = true }; - - Sections = new Section[1]; - Sections[0] = new Section(0, Indices.Length, 0); - - AddInstance(Transform.Identity); - - Box = staticMesh.Bounds * 1.5f * Constants.SCALE_DOWN_RATIO; - } - - public StaticModel(UPaperSprite paperSprite, UTexture2D texture) : base(paperSprite) - { - Indices = new uint[paperSprite.BakedRenderData.Length]; - for (int i = 0; i < Indices.Length; i++) - { - Indices[i] = (uint) i; - } - - Vertices = new float[paperSprite.BakedRenderData.Length * VertexSize]; - for (int i = 0; i < paperSprite.BakedRenderData.Length; i++) - { - var count = 0; - var baseIndex = i * VertexSize; - var vert = paperSprite.BakedRenderData[i]; - var u = vert.Z; - var v = vert.W; - - Vertices[baseIndex + count++] = i; - Vertices[baseIndex + count++] = vert.X * paperSprite.PixelsPerUnrealUnit * Constants.SCALE_DOWN_RATIO; - Vertices[baseIndex + count++] = vert.Y * paperSprite.PixelsPerUnrealUnit * Constants.SCALE_DOWN_RATIO; - Vertices[baseIndex + count++] = 0; - Vertices[baseIndex + count++] = 0; - Vertices[baseIndex + count++] = 0; - Vertices[baseIndex + count++] = 0; - Vertices[baseIndex + count++] = 0; - Vertices[baseIndex + count++] = 0; - Vertices[baseIndex + count++] = 0; - Vertices[baseIndex + count++] = u; - Vertices[baseIndex + count++] = v; - Vertices[baseIndex + count++] = .5f; - } - - Materials = new Material[1]; - if (paperSprite.DefaultMaterial?.TryLoad(out UMaterialInstance unrealMaterial) ?? false) - { - Materials[0] = new Material(unrealMaterial); - } - else - { - Materials[0] = new Material(); - } - Materials[0].Parameters.Textures[CMaterialParams2.FallbackDiffuse] = texture; - Materials[0].IsUsed = true; - - Sections = new Section[1]; - Sections[0] = new Section(0, Indices.Length, 0); - - AddInstance(Transform.Identity); - - var backward = new FVector(0, Math.Max(paperSprite.BakedSourceDimension.X, paperSprite.BakedSourceDimension.Y) / 2, 0); - Box = new FBox(-backward, backward) * Constants.SCALE_DOWN_RATIO; - } - - public StaticModel(UStaticMesh export, StaticMeshDto staticMesh, Transform transform = null) - : base(export, staticMesh.LODs[LodLevel], export.Materials, staticMesh.LODs[LodLevel].Vertices, staticMesh.LODs.Count, transform) - { - if (export.BodySetup.TryLoad(out UBodySetup bodySetup) && bodySetup.AggGeom != null) - { - foreach (var convexElem in bodySetup.AggGeom.ConvexElems) - { - Collisions.Add(new Collision(convexElem)); - } - foreach (var sphereElem in bodySetup.AggGeom.SphereElems) - { - Collisions.Add(new Collision(sphereElem)); - } - foreach (var boxElem in bodySetup.AggGeom.BoxElems) - { - Collisions.Add(new Collision(boxElem)); - } - foreach (var sphylElem in bodySetup.AggGeom.SphylElems) - { - Collisions.Add(new Collision(sphylElem)); - } - foreach (var taperedCapsuleElem in bodySetup.AggGeom.TaperedCapsuleElems) - { - Collisions.Add(new Collision(taperedCapsuleElem)); - } - } - - Box = staticMesh.Bounds * Constants.SCALE_DOWN_RATIO; - foreach (var s in export.Sockets ?? []) - { - if (s.Load() is not { } socket) continue; - Sockets.Add(new Socket(socket)); - } - } - - public override void RenderCollision(Shader shader) - { - base.RenderCollision(shader); - - GL.Disable(EnableCap.CullFace); - GL.PolygonMode(TriangleFace.FrontAndBack, PolygonMode.Line); - foreach (var collision in Collisions) - { - collision.Render(shader, Matrix4x4.Identity); - } - GL.PolygonMode(TriangleFace.FrontAndBack, PolygonMode.Fill); - GL.Enable(EnableCap.CullFace); - } -} diff --git a/FModel/Views/Snooper/Models/UModel.cs b/FModel/Views/Snooper/Models/UModel.cs deleted file mode 100644 index 9fa2a11d..00000000 --- a/FModel/Views/Snooper/Models/UModel.cs +++ /dev/null @@ -1,416 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using CUE4Parse_Conversion; -using CUE4Parse_Conversion.Dto; -using CUE4Parse.UE4.Assets.Exports; -using CUE4Parse.UE4.Assets.Exports.Material; -using CUE4Parse.UE4.Objects.Core.Math; -using CUE4Parse.UE4.Objects.UObject; -using FModel.Views.Snooper.Buffers; -using FModel.Views.Snooper.Shading; -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper.Models; - -public class VertexAttribute -{ - public int Size; - public VertexAttribPointerType Type; - public bool Enabled; -} - -public abstract class UModel : IRenderableModel where TVertex : struct, IMeshVertex -{ - protected const int LodLevel = 0; - - private readonly UObject _export; - private readonly List _vertexAttributes = - [ - new VertexAttribute { Size = 1, Type = VertexAttribPointerType.Int, Enabled = false }, // VertexIndex - new VertexAttribute { Size = 3, Type = VertexAttribPointerType.Float, Enabled = true }, // Position - new VertexAttribute { Size = 3, Type = VertexAttribPointerType.Float, Enabled = false }, // Normal - new VertexAttribute { Size = 3, Type = VertexAttribPointerType.Float, Enabled = false }, // Tangent - new VertexAttribute { Size = 2, Type = VertexAttribPointerType.Float, Enabled = false }, // UV - new VertexAttribute { Size = 1, Type = VertexAttribPointerType.Float, Enabled = false }, // TextureLayer - new VertexAttribute { Size = 1, Type = VertexAttribPointerType.Float, Enabled = false }, // Colors - new VertexAttribute { Size = 4, Type = VertexAttribPointerType.Float, Enabled = false }, // BoneIds - new VertexAttribute { Size = 4, Type = VertexAttribPointerType.Float, Enabled = false } // BoneWeights - ]; - - public int Handle { get; set; } - public BufferObject Ebo { get; set; } - public BufferObject Vbo { get; set; } - public BufferObject MatrixVbo { get; set; } - public VertexArrayObject Vao { get; set; } - - public string Path { get; } - public string Name { get; } - public string Type { get; protected set; } - public int UvCount { get; } - public uint[] Indices { get; set; } - public float[] Vertices { get; set; } - public Section[] Sections { get; set; } - public List Transforms { get; } - public Attachment Attachments { get; } - - public FBox Box { get; init; } - public List Sockets { get; } - public List Collisions { get; } - public Material[] Materials { get; init; } - public bool IsTwoSided { get; set; } - public bool IsProp { get; set; } - - public int VertexSize => _vertexAttributes.Where(x => x.Enabled).Sum(x => x.Size); - public bool HasVertexColors => _vertexAttributes[(int) EAttribute.Colors].Enabled; - public bool HasSockets => Sockets.Count > 0; - public bool HasCollisions => Collisions.Count > 0; - public int TransformsCount => Transforms.Count; - - public bool IsSetup { get; set; } - public bool IsVisible { get; set; } - public bool IsSelected { get; set; } - public bool ShowWireframe { get; set; } - public bool ShowCollisions { get; set; } - public int SelectedInstance { get; set; } - - protected UModel() - { - _export = null; - UvCount = 1; - - Box = new FBox(new FVector(-2f), new FVector(2f)); - Sockets = new List(); - Collisions = new List(); - Transforms = new List(); - } - - protected UModel(UObject export) - { - _export = export; - Path = _export.GetPathName(); - Name = export.Name; - Type = export.ExportType; - UvCount = 1; - - Box = new FBox(new FVector(-2f), new FVector(2f)); - Sockets = new List(); - Collisions = new List(); - Transforms = new List(); - Attachments = new Attachment(Name); - - _vertexAttributes[(int) EAttribute.Index].Enabled = - _vertexAttributes[(int) EAttribute.Normals].Enabled = - _vertexAttributes[(int) EAttribute.Tangent].Enabled = - _vertexAttributes[(int) EAttribute.UVs].Enabled = - _vertexAttributes[(int) EAttribute.Layer].Enabled = true; - } - - protected UModel(UObject export, MeshLodDto lod, IReadOnlyList materials, IReadOnlyList vertices, int numLods, Transform transform = null) : this(export) - { - var hasCustomUvs = lod.ExtraUvs.Length > 0; - UvCount = hasCustomUvs ? Math.Max(lod.ExtraUvs.Length, numLods) : lod.ExtraUvs.Length + 1; - IsTwoSided = lod.IsTwoSided; - - Indices = new uint[lod.Indices.Length]; - for (int i = 0; i < Indices.Length; i++) - { - Indices[i] = lod.Indices[i]; - } - - Materials = new Material[materials.Count]; - for (int m = 0; m < Materials.Length; m++) - { - if ((materials[m]?.TryLoad(out var material) ?? false) && material is UMaterialInterface unrealMaterial) - Materials[m] = new Material(unrealMaterial); else Materials[m] = new Material(); - } - - _vertexAttributes[(int) EAttribute.Colors].Enabled = lod.VertexColors is { Length: > 0 }; - _vertexAttributes[(int) EAttribute.BonesId].Enabled = - _vertexAttributes[(int) EAttribute.BonesWeight].Enabled = vertices is SkinnedMeshVertex[]; - - Vertices = new float[vertices.Count * VertexSize]; - for (int i = 0; i < vertices.Count; i++) - { - var count = 0; - var baseIndex = i * VertexSize; - var vert = vertices[i]; - Vertices[baseIndex + count++] = i; - Vertices[baseIndex + count++] = vert.Position.X * Constants.SCALE_DOWN_RATIO; - Vertices[baseIndex + count++] = vert.Position.Z * Constants.SCALE_DOWN_RATIO; - Vertices[baseIndex + count++] = vert.Position.Y * Constants.SCALE_DOWN_RATIO; - Vertices[baseIndex + count++] = vert.Normal.X; - Vertices[baseIndex + count++] = vert.Normal.Z; - Vertices[baseIndex + count++] = vert.Normal.Y; - Vertices[baseIndex + count++] = vert.Tangent.X; - Vertices[baseIndex + count++] = vert.Tangent.Z; - Vertices[baseIndex + count++] = vert.Tangent.Y; - Vertices[baseIndex + count++] = vert.Uv.U; - Vertices[baseIndex + count++] = vert.Uv.V; - Vertices[baseIndex + count++] = hasCustomUvs ? lod.ExtraUvs[0][i].U - 1 : .5f; - - if (HasVertexColors) - { - Vertices[baseIndex + count++] = lod.VertexColors![0].Colors[i].ToPackedARGB(); - } - - if (vert is SkinnedMeshVertex skelVert) - { - int max = skelVert.Influences.Length; - for (int j = 0; j < 8; j++) - { - var boneID = j < max ? skelVert.Influences[j].Bone : (ushort) 0; - var weight = j < max ? skelVert.Influences[j].RawWeight : (ushort) 0; - - // Pack bone ID and weight - Vertices[baseIndex + count++] = (boneID << 16) | weight; - } - } - } - - Sections = new Section[lod.Sections.Length]; - for (var s = 0; s < Sections.Length; s++) - { - var section = lod.Sections[s]; - Sections[s] = new Section(section.MaterialIndex, section.NumFaces * 3, section.FirstIndex); - if (section.IsValid) Sections[s].SetupMaterial(Materials[section.MaterialIndex]); - } - - AddInstance(transform ?? Transform.Identity); - } - - public virtual void Setup(Options options) - { - Handle = GL.CreateProgram(); - Ebo = new BufferObject(Indices, BufferTarget.ElementArrayBuffer); - Vbo = new BufferObject(Vertices, BufferTarget.ArrayBuffer); - Vao = new VertexArrayObject(Vbo, Ebo); - - var offset = 0; - var broken = GL.GetInteger(GetPName.MaxTextureCoords) == 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, attribute.Type, VertexSize, offset); - } - - offset += attribute.Size; - } - - SetupInstances(); // instanced models transform - - // setup all used materials for use in different UV channels - for (var i = 0; i < Materials.Length; i++) - { - if (!Materials[i].IsUsed) continue; - Materials[i].Setup(options, broken ? 1 : UvCount); - } - - foreach (var collision in Collisions) - { - collision.Setup(); - } - - if (options.Models.Count == 1 && Sections.All(x => !x.Show)) // visible if alone and invisible - { - IsVisible = true; - foreach (var section in Sections) - { - section.Show = true; - } - } - else if (!IsVisible) // default: visible if one section is visible - { - foreach (var section in Sections) - { - if (section.Show) - { - IsVisible = true; - break; - } - } - } - else foreach (var section in Sections) // force visibility - { - section.Show = true; - } - - IsSetup = true; - } - - public virtual void Render(Shader shader, Texture checker = null, bool outline = false) - { - if (outline) GL.Disable(EnableCap.DepthTest); - if (IsTwoSided) GL.Disable(EnableCap.CullFace); - if (IsSelected) - { - GL.Enable(EnableCap.StencilTest); - GL.StencilFunc(outline ? StencilFunction.Notequal : StencilFunction.Always, 1, 0xFF); - } - - if (this is SkeletalModel skeletalModel) skeletalModel.Render(shader); - else if (this is SplineModel splineModel) splineModel.Render(shader); - else - { - shader.SetUniform("uIsAnimated", false); - shader.SetUniform("uIsSpline", false); - } - - if (!outline) - { - shader.SetUniform("uUvCount", UvCount); - shader.SetUniform("uOpacity", ShowCollisions && IsSelected ? 0.75f : 1f); - shader.SetUniform("uHasVertexColors", HasVertexColors); - } - - Vao.Bind(); - GL.PolygonMode(TriangleFace.FrontAndBack, ShowWireframe ? PolygonMode.Line : PolygonMode.Fill); - foreach (var section in Sections) - { - if (!section.Show) continue; - if (!outline) - { - if (checker != null) - { - shader.SetUniform("uParameters.Diffuse[0].Sampler", 0); - checker.Bind(TextureUnit.Texture0); - } - else - { - shader.SetUniform("uSectionColor", section.Color); - Materials[section.MaterialIndex].Render(shader); - } - } - - GL.DrawElementsInstanced(PrimitiveType.Triangles, section.FacesCount, DrawElementsType.UnsignedInt, section.FirstFaceIndexPtr, TransformsCount); - } - GL.PolygonMode(TriangleFace.FrontAndBack, PolygonMode.Fill); - Vao.Unbind(); - - if (IsSelected) - { - GL.StencilFunc(StencilFunction.Always, 0, 0xFF); - GL.Disable(EnableCap.StencilTest); - } - if (IsTwoSided) GL.Enable(EnableCap.CullFace); - if (outline) GL.Enable(EnableCap.DepthTest); - } - - public void PickingRender(Shader shader) - { - if (IsTwoSided) GL.Disable(EnableCap.CullFace); - if (this is SkeletalModel skeletalModel) skeletalModel.Render(shader); - if (this is SplineModel splineModel) splineModel.Render(shader); - else - { - shader.SetUniform("uIsAnimated", false); - shader.SetUniform("uIsSpline", false); - } - - Vao.Bind(); - foreach (var section in Sections) - { - if (!section.Show) continue; - GL.DrawElementsInstanced(PrimitiveType.Triangles, section.FacesCount, DrawElementsType.UnsignedInt, section.FirstFaceIndexPtr, TransformsCount); - } - Vao.Unbind(); - - if (IsTwoSided) GL.Enable(EnableCap.CullFace); - } - - public virtual void RenderCollision(Shader shader) - { - shader.SetUniform("uInstanceMatrix", GetTransform().Matrix); - shader.SetUniform("uScaleDown", Constants.SCALE_DOWN_RATIO); - } - - public void Update(Options options) - { - MatrixVbo.Bind(); - for (int instance = 0; instance < TransformsCount; instance++) - { - MatrixVbo.Update(instance, Transforms[instance].Matrix); - } - MatrixVbo.Unbind(); - - var worldMatrix = GetTransform().Matrix; - foreach (var socket in Sockets) - { - if (!socket.IsDaron) continue; - - var boneMatrix = Matrix4x4.Identity; - if (this is SkeletalModel skeletalModel && skeletalModel.Skeleton.BonesByLoweredName.TryGetValue(socket.BoneName.Text.ToLower(), out var bone)) - boneMatrix = skeletalModel.Skeleton.GetBoneMatrix(bone); - - var socketRelation = boneMatrix * worldMatrix; - foreach (var info in socket.AttachedModels) - { - if (!options.TryGetModel(info.Guid, out var attachedModel)) - continue; - - attachedModel.Transforms[info.Instance].Relation = socket.Transform.LocalMatrix * socketRelation; - attachedModel.Update(options); - } - } - } - - public void AddInstance(Transform transform) - { - SelectedInstance = TransformsCount; - Transforms.Add(transform); - } - - public void SetupInstances() - { - MatrixVbo = new BufferObject(TransformsCount, BufferTarget.ArrayBuffer); - for (int instance = 0; instance < TransformsCount; instance++) - { - Transforms[instance].Save(); - MatrixVbo.Update(instance, Transforms[instance].Matrix); - } - Vao.BindInstancing(); // VertexAttributePointer - } - - public Transform GetTransform() => Transforms[SelectedInstance]; - public Matrix4x4 GetSocketTransform(int index) - { - var socket = Sockets[index]; - var worldMatrix = GetTransform().Matrix; - var boneMatrix = Matrix4x4.Identity; - if (this is SkeletalModel skeletalModel && skeletalModel.Skeleton.BonesByLoweredName.TryGetValue(socket.BoneName.Text.ToLower(), out var bone)) - boneMatrix = skeletalModel.Skeleton.GetBoneMatrix(bone); - - var socketRelation = boneMatrix * worldMatrix; - return socket.Transform.LocalMatrix * socketRelation; - } - - public void AddToExportSession(ExportSession session) - { - session.Add(_export); - } - - public virtual void Dispose() - { - Ebo?.Dispose(); - Vbo?.Dispose(); - MatrixVbo?.Dispose(); - Vao?.Dispose(); - foreach (var socket in Sockets) - { - socket?.Dispose(); - } - Sockets.Clear(); - foreach (var collision in Collisions) - { - collision?.Dispose(); - } - Collisions.Clear(); - - GL.DeleteProgram(Handle); - } -} diff --git a/FModel/Views/Snooper/Options.cs b/FModel/Views/Snooper/Options.cs deleted file mode 100644 index fc33b2cd..00000000 --- a/FModel/Views/Snooper/Options.cs +++ /dev/null @@ -1,285 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using CUE4Parse_Conversion.Textures; -using CUE4Parse.UE4.Assets.Exports.Texture; -using CUE4Parse.UE4.Objects.Core.Misc; -using FModel.Settings; -using FModel.Views.Snooper.Animations; -using FModel.Views.Snooper.Lights; -using FModel.Views.Snooper.Models; -using FModel.Views.Snooper.Shading; -using SkiaSharp; - -namespace FModel.Views.Snooper; - -public class Options -{ - public FGuid SelectedModel { get; private set; } - public int SelectedSection { get; private set; } - public int SelectedMorph { get; private set; } - public int SelectedAnimation{ get; private set; } - - public readonly Dictionary Models; - public readonly Dictionary Textures; - public readonly List Lights; - - public readonly TimeTracker Tracker; - public readonly List Animations; - - public readonly Dictionary Icons; - - private readonly string _game; - - public Options() - { - Models = new Dictionary(); - Textures = new Dictionary(); - Lights = new List(); - - Tracker = new TimeTracker(); - Animations = new List(); - - Icons = new Dictionary - { - ["material"] = new ("materialicon"), - ["square"] = new ("square"), - ["square_off"] = new ("square_off"), - ["cube"] = new ("cube"), - ["cube_off"] = new ("cube_off"), - ["light"] = new ("light"), - ["light_off"] = new ("light_off"), - ["noimage"] = new ("T_Placeholder_Item_Image"), - ["checker"] = new ("checker"), - ["pointlight"] = new ("pointlight"), - ["spotlight"] = new ("spotlight"), - ["link_on"] = new ("link_on"), - ["link_off"] = new ("link_off"), - ["link_has"] = new ("link_has"), - ["tl_play"] = new ("tl_play"), - ["tl_pause"] = new ("tl_pause"), - ["tl_rewind"] = new ("tl_rewind"), - ["tl_forward"] = new ("tl_forward"), - ["tl_previous"] = new ("tl_previous"), - ["tl_next"] = new ("tl_next"), - }; - - _game = Services.ApplicationService.ApplicationView.CUE4Parse.Provider.ProjectName.ToUpper(); - - SelectModel(Guid.Empty); - } - - public void SetupModelsAndLights() - { - foreach (var model in Models.Values) - { - if (model.IsSetup) continue; - model.Setup(this); - } - - foreach (var light in Lights) - { - if (light.IsSetup) continue; - light.Setup(); - } - } - - public void SelectModel(FGuid guid) - { - // unselect old - if (TryGetModel(out var model)) - model.IsSelected = false; - - // select new - if (!TryGetModel(guid, out model)) - SelectedModel = Guid.Empty; - else - { - model.IsSelected = true; - SelectedModel = guid; - } - - SelectedSection = 0; - SelectedMorph = 0; - } - - public void SelectAnimation(int animation) - { - SelectedAnimation = animation; - } - - public void RemoveModel(FGuid guid) - { - if (!TryGetModel(guid, out var m) || m is null) - return; - - DetachAndRemoveModels(m, true); - m.Dispose(); - Models.Remove(guid); - } - - private void DetachAndRemoveModels(IRenderableModel model, bool detach) - { - foreach (var socket in model.Sockets.ToList()) - { - foreach (var info in socket.AttachedModels) - { - if (!TryGetModel(info.Guid, out var m) || m is null) - continue; - - var t = m.GetTransform(); - if (m.IsProp) - { - m.Attachments.SafeDetach(model, t); - RemoveModel(info.Guid); - } - else if (detach) m.Attachments.SafeDetach(model, t); - } - - if (socket.IsVirtual) - { - socket.Dispose(); - model.Sockets.Remove(socket); - } - } - } - - public void AddAnimation(Animation animation) - { - Animations.Add(animation); - } - - public void RemoveAnimations() - { - Tracker.Reset(); - SelectedAnimation = 0; - - foreach (var animation in Animations) - { - foreach (var guid in animation.AttachedModels) - { - if (!TryGetModel(guid, out var model) || model is not SkeletalModel animatedModel) - continue; - - animatedModel.Skeleton.ResetAnimatedData(true); - DetachAndRemoveModels(animatedModel, false); - } - - animation.Dispose(); - } - - foreach (var kvp in Models) - if (kvp.Value.IsProp) - RemoveModel(kvp.Key); - - Animations.Clear(); - } - - public void SelectSection(int index) - { - SelectedSection = index; - } - - public void SelectMorph(int index, SkeletalModel model) - { - SelectedMorph = index; - model.UpdateMorph(SelectedMorph); - } - - public bool TryGetTexture(UTexture o, bool fix, out Texture texture) - { - var guid = o.LightingGuid; - if (Textures.TryGetValue(guid, out texture)) return texture != null; - - var bitmap = o switch - { - UTexture2D texture2D => texture2D.Decode(UserSettings.Default.PreviewMaxTextureSize, UserSettings.Default.CurrentDir.TexturePlatform), - UTexture2DArray texture2DArray => texture2DArray.DecodeTextureArray(UserSettings.Default.CurrentDir.TexturePlatform)?.FirstOrDefault(), - _ => o.Decode(UserSettings.Default.CurrentDir.TexturePlatform) - }; - - if (bitmap is not null) - { - texture = new Texture(bitmap.ToSkBitmap(), o); - if (fix) TextureHelper.FixChannels(_game, texture); - Textures[guid] = texture; - } - - return texture != null; - } - - public bool TryGetModel([MaybeNullWhen(false)] out IRenderableModel model) => Models.TryGetValue(SelectedModel, out model); - public bool TryGetModel(FGuid guid, [MaybeNullWhen(false)] out IRenderableModel model) => Models.TryGetValue(guid, out model); - - public bool TryGetSection(out Section section) => TryGetSection(SelectedModel, out section); - public bool TryGetSection(FGuid guid, out Section section) - { - if (TryGetModel(guid, out var model)) - { - return TryGetSection(model, out section); - } - - section = null; - return false; - } - public bool TryGetSection(IRenderableModel model, out Section section) - { - if (SelectedSection >= 0 && SelectedSection < model.Sections.Length) - section = model.Sections[SelectedSection]; else section = null; - return section != null; - } - - public void SwapMaterial(bool value) - { - Services.ApplicationService.ApplicationView.CUE4Parse.ModelIsOverwritingMaterial = value; - } - - public void AnimateMesh(bool value) - { - Services.ApplicationService.ApplicationView.CUE4Parse.ModelIsWaitingAnimation = value; - } - - /// - /// Skip emissive for specific games, cause of excessive use in their materials - /// - public bool SkipEmissive() - { - return _game switch - { - "LIESOFP" or "CODEVEIN2" or "HIGHONLIFE2" or "MORTALSHELL2" => true, - _ => false, - }; - } - - public void ResetModelsLightsAnimations() - { - foreach (var model in Models.Values) - { - model.Dispose(); - } - Models.Clear(); - Lights.Clear(); - Tracker.Reset(); - foreach (var animation in Animations) - { - animation.Dispose(); - } - Animations.Clear(); - } - - public void Dispose() - { - ResetModelsLightsAnimations(); - foreach (var texture in Textures.Values) - { - texture.Dispose(); - } - Textures.Clear(); - foreach (var texture in Icons.Values) - { - texture.Dispose(); - } - Icons.Clear(); - } -} diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs deleted file mode 100644 index 9bd26581..00000000 --- a/FModel/Views/Snooper/Renderer.cs +++ /dev/null @@ -1,816 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Threading; -using System.Windows; -using CUE4Parse_Conversion.Animations; -using CUE4Parse_Conversion.Meshes; -using CUE4Parse_Conversion.Options; -using CUE4Parse.UE4.Assets.Exports; -using CUE4Parse.UE4.Assets.Exports.Animation; -using CUE4Parse.UE4.Assets.Exports.Component.SplineMesh; -using CUE4Parse.UE4.Assets.Exports.Component.StaticMesh; -using CUE4Parse.UE4.Assets.Exports.GeometryCollection; -using CUE4Parse.UE4.Assets.Exports.Material; -using CUE4Parse.UE4.Assets.Exports.SkeletalMesh; -using CUE4Parse.UE4.Assets.Exports.StaticMesh; -using CUE4Parse.UE4.Assets.Exports.Texture; -using CUE4Parse.UE4.Objects.Core.Math; -using CUE4Parse.UE4.Objects.Core.Misc; -using CUE4Parse.UE4.Objects.Engine; -using CUE4Parse.UE4.Objects.UObject; -using CUE4Parse.Utils; -using FModel.Creator; -using FModel.Settings; -using FModel.Views.Snooper.Animations; -using FModel.Views.Snooper.Buffers; -using FModel.Views.Snooper.Lights; -using FModel.Views.Snooper.Models; -using FModel.Views.Snooper.Shading; -using OpenTK.Windowing.GraphicsLibraryFramework; - -namespace FModel.Views.Snooper; - -public enum VertexColor -{ - Default, - Sections, - Colors, - Normals, - TextureCoordinates -} - -public class Renderer : IDisposable -{ - private readonly Skybox _skybox; - private readonly Grid _grid; - private Shader _shader; - private Shader _outline; - private Shader _light; - private Shader _bone; - private Shader _collision; - private bool _saveCameraMode; - - public bool ShowSkybox; - public bool ShowGrid; - public bool ShowLights; - public bool AnimateWithRotationOnly; - public bool IsSkeletonTreeOpen; - public VertexColor Color; - - public Camera CameraOp { get; } - public PickingTexture Picking { get; } - public Options Options { get; } - - public Renderer(int width, int height) - { - _skybox = new Skybox(); - _grid = new Grid(); - - CameraOp = new Camera(); - Picking = new PickingTexture(width, height); - Options = new Options(); - - ShowSkybox = UserSettings.Default.ShowSkybox; - ShowGrid = UserSettings.Default.ShowGrid; - AnimateWithRotationOnly = UserSettings.Default.AnimateWithRotationOnly; - Color = VertexColor.Default; - } - - public void Load(CancellationToken cancellationToken, UObject dummy, Lazy export) - { - ShowLights = false; - Color = VertexColor.Default; - _saveCameraMode = dummy is not UWorld and not UBlueprintGeneratedClass; - switch (dummy) - { - case UStaticMesh when export.Value is UStaticMesh st: - LoadStaticMesh(st, UserSettings.Default.NaniteMeshExportFormat); - break; - case USkeletalMesh when export.Value is USkeletalMesh sk: - LoadSkeletalMesh(sk); - break; - case USkeleton when export.Value is USkeleton skel: - LoadSkeleton(skel); - break; - case UMaterialInstance when export.Value is UMaterialInstance mi: - LoadMaterialInstance(mi); - break; - case UWorld when export.Value is UWorld wd: - LoadWorld(cancellationToken, wd, Transform.Identity); - break; - case UBlueprintGeneratedClass when export.Value is UBlueprintGeneratedClass bp: - LoadJunoWorld(cancellationToken, bp, Transform.Identity); - Color = VertexColor.Colors; - break; - case UPaperSprite when export.Value is UPaperSprite ps: - LoadPaperSprite(ps); - break; - } - CameraOp.Mode = _saveCameraMode ? UserSettings.Default.CameraMode : Camera.WorldMode.FlyCam; - SetupCamera(); - } - - public void Swap(UMaterialInstance unrealMaterial) - { - if (!Options.TryGetModel(out var model) || !Options.TryGetSection(model, out var section)) return; - - model.Materials[section.MaterialIndex].SwapMaterial(unrealMaterial); - Application.Current.Dispatcher.Invoke(() => model.Materials[section.MaterialIndex].Setup(Options, model.UvCount)); - } - - public void Animate(UObject anim) - { - if (!Services.ApplicationService.ApplicationView.CUE4Parse.ModelIsWaitingAnimation) - { - if (anim is UAnimSequenceBase animBase) - { - /*if (Options.TryGetModel(out var selected) && - selected is SkeletalModel { IsVisible: true } skeletalModel && - skeletalModel.Skeleton.Guid == animBase.SkeletonGuid) - { - // do nothing, selected model has the correct skeleton for this animation - } - else */if (animBase.Skeleton.TryLoad(out USkeleton skeleton)) - { - LoadSkeleton(skeleton); - } - } - else return; // should never end here - } - - Animate(anim, Options.SelectedModel); - } - private void Animate(UObject anim, FGuid guid) - { - if (anim is not UAnimSequenceBase animBase || !animBase.Skeleton.TryLoad(out USkeleton skeleton) || - !Options.TryGetModel(guid, out var m) || m is not SkeletalModel model) - return; - - var animSet = animBase switch - { - UAnimSequence animSequence => skeleton.ConvertAnims(animSequence), - UAnimMontage animMontage => skeleton.ConvertAnims(animMontage), - UAnimComposite animComposite => skeleton.ConvertAnims(animComposite), - _ => throw new ArgumentException("Unknown animation type") - }; - - var animation = new Animation(anim, animSet, guid); - model.Skeleton.Animate(animSet); - Options.AddAnimation(animation); - - foreach (var notifyEvent in animBase.Notifies ?? []) - { - if (notifyEvent.NotifyStateClass is null || - !notifyEvent.NotifyStateClass.TryLoad(out UObject notifyClass) || - !notifyClass.TryGetValue(out UObject export, "SkeletalMeshProp", "StaticMeshProp", "Mesh", "SkeletalMeshTemplate")) - continue; - - var t = Transform.Identity; - if (notifyClass.TryGetValue(out FTransform offset, "Offset")) - { - t.Rotation = offset.Rotation; - t.Position = offset.Translation * Constants.SCALE_DOWN_RATIO; - t.Scale = offset.Scale3D; - } - - IRenderableModel addedModel = null; - switch (export) - { - case UStaticMesh st: - { - guid = st.LightingGuid; - if (Options.TryGetModel(guid, out addedModel)) - { - addedModel.AddInstance(t); - } - else if (st.TryConvert(out var mesh, EMeshQuality.Highest)) - { - addedModel = new StaticModel(st, mesh, t); - Options.Models[guid] = addedModel; - } - break; - } - case USkeletalMesh sk: - { - guid = Guid.NewGuid(); - if (!Options.Models.ContainsKey(guid) && sk.TryConvert(out var mesh, EMeshQuality.Highest)) - { - addedModel = new SkeletalModel(sk, mesh, t); - Options.Models[guid] = addedModel; - } - break; - } - } - - if (addedModel == null) - throw new ArgumentException("Unknown model type"); - - addedModel.IsProp = true; - if (notifyClass.TryGetValue(out UObject skeletalMeshPropAnimation, "SkeletalMeshPropAnimation", "Animation", "AnimToPlay")) - Animate(skeletalMeshPropAnimation, guid); - if (notifyClass.TryGetValue(out FName socketName, "SocketName")) - { - t = Transform.Identity; - if (notifyClass.TryGetValue(out FVector location, "LocationOffset", "Location")) - t.Position = location * Constants.SCALE_DOWN_RATIO; - if (notifyClass.TryGetValue(out FRotator rotation, "RotationOffset", "Rotation")) - t.Rotation = rotation.Quaternion(); - if (notifyClass.TryGetValue(out FVector scale, "Scale")) - t.Scale = scale; - - var s = new Socket($"ANIM_{addedModel.Name}", socketName, t, true); - model.Sockets.Add(s); - addedModel.Attachments.Attach(model, addedModel.GetTransform(), s, - new SocketAttachementInfo { Guid = guid, Instance = addedModel.SelectedInstance }); - } - } - - Options.Tracker.IsPaused = false; - Options.Tracker.SafeSetMaxElapsedTime(animation.TotalElapsedTime); - } - - public void Setup() - { - _skybox.Setup(); - _grid.Setup(); - - _shader = new Shader(); - _outline = new Shader("outline"); - _light = new Shader("light"); - _bone = new Shader("bone"); - _collision = new Shader("collision", "bone"); - - Picking.Setup(); - Options.SetupModelsAndLights(); - } - - public void Render() - { - var viewMatrix = CameraOp.GetViewMatrix(); - var projMatrix = CameraOp.GetProjectionMatrix(); - - if (ShowSkybox) _skybox.Render(viewMatrix, projMatrix); - if (ShowGrid) _grid.Render(viewMatrix, projMatrix, CameraOp.Near, CameraOp.Far); - - _shader.Render(viewMatrix, CameraOp.Position, projMatrix); - for (int i = 0; i < 5; i++) - _shader.SetUniform($"bVertexColors[{i}]", i == (int) Color); - - // render model pass - foreach (var model in Options.Models.Values) - { - if (!model.IsVisible) continue; - model.Render(_shader, Color == VertexColor.TextureCoordinates ? Options.Icons["checker"] : null); - } - - { // light pass - var uNumLights = Math.Min(Options.Lights.Count, 100); - _shader.SetUniform("uNumLights", ShowLights ? uNumLights : 0); - - if (ShowLights) - for (int i = 0; i < uNumLights; i++) - Options.Lights[i].Render(i, _shader); - - _light.Render(viewMatrix, projMatrix); - for (int i = 0; i < uNumLights; i++) - Options.Lights[i].Render(_light); - } - - // debug + outline pass - if (Options.TryGetModel(out var selected) && selected.IsVisible) - { - if (IsSkeletonTreeOpen && selected is SkeletalModel skeletalModel) - { - _bone.Render(viewMatrix, projMatrix); - skeletalModel.RenderBones(_bone); - } - else if (selected.ShowCollisions) - { - _collision.Render(viewMatrix, projMatrix); - selected.RenderCollision(_collision); - } - - _outline.Render(viewMatrix, CameraOp.Position, projMatrix); - selected.Render(_outline, Color == VertexColor.TextureCoordinates ? Options.Icons["checker"] : null, true); - } - - // picking pass (dedicated FBO, binding to 0 afterward) - Picking.Render(viewMatrix, projMatrix, Options.Models); - } - - public void Update(Snooper wnd, float deltaSeconds) - { - if (Options.Animations.Count > 0) Options.Tracker.Update(deltaSeconds); - foreach (var animation in Options.Animations) - { - animation.TimeCalculation(Options.Tracker.ElapsedTime); - foreach (var guid in animation.AttachedModels) - { - if (!Options.TryGetModel(guid, out var m) || m is not SkeletalModel skeletalModel) continue; - skeletalModel.Skeleton.UpdateAnimationMatrices(animation, AnimateWithRotationOnly); - } - } - - { - foreach (var model in Options.Models.Values) - { - model.Update(Options); - } - if (IsSkeletonTreeOpen && Options.TryGetModel(out var selected) && selected is SkeletalModel { IsVisible: true } skeletalModel) - { - skeletalModel.Skeleton.UpdateVertices(); - } - } - - CameraOp.Modify(wnd.KeyboardState, deltaSeconds); - - if (wnd.KeyboardState.IsKeyPressed(Keys.Z) && - Options.TryGetModel(out var selectedModel) && - selectedModel is SkeletalModel) - { - Options.RemoveAnimations(); - Options.AnimateMesh(true); - wnd.WindowShouldClose(true, false); - } - if (wnd.KeyboardState.IsKeyPressed(Keys.Space)) - Options.Tracker.IsPaused = !Options.Tracker.IsPaused; - if (wnd.KeyboardState.IsKeyPressed(Keys.Delete)) - Options.RemoveModel(Options.SelectedModel); - if (wnd.KeyboardState.IsKeyPressed(Keys.H)) - wnd.WindowShouldClose(true, false); - if (wnd.KeyboardState.IsKeyPressed(Keys.Escape)) - wnd.WindowShouldClose(true, true); - } - - private void LoadStaticMesh(UStaticMesh original, ENaniteMeshFormat naniteFormat = ENaniteMeshFormat.NoNanite) - { - var guid = original.LightingGuid; - if (Options.TryGetModel(guid, out var model)) - { - model.AddInstance(Transform.Identity); - Application.Current.Dispatcher.Invoke(() => model.SetupInstances()); - return; - } - - if (!original.TryConvert(out var mesh, EMeshQuality.Highest, naniteFormat)) - return; - - Options.Models[guid] = new StaticModel(original, mesh); - Options.SelectModel(guid); - } - - private void LoadSkeletalMesh(USkeletalMesh original) - { - var guid = new FGuid((uint) original.GetFullName().GetHashCode()); - if (Options.Models.ContainsKey(guid) || !original.TryConvert(out var mesh, EMeshQuality.Highest)) return; - - var skeletalModel = new SkeletalModel(original, mesh); - Options.Models[guid] = skeletalModel; - Options.SelectModel(guid); - } - - private void LoadSkeleton(USkeleton original) - { - var guid = original.Guid; - if (Options.Models.ContainsKey(guid) || !original.TryConvert(out _, out var box)) return; - - var fakeSkeletalModel = new SkeletalModel(original, box); - Options.Models[guid] = fakeSkeletalModel; - Options.SelectModel(guid); - IsSkeletonTreeOpen = true; - } - - private void LoadMaterialInstance(UMaterialInstance original) - { - if (!Utils.TryLoadObject("Engine/Content/BasicShapes/Cube.Cube", out UStaticMesh editorCube)) - return; - - var guid = editorCube.LightingGuid; - if (Options.TryGetModel(guid, out var model)) - { - model.Materials[0].SwapMaterial(original); - Application.Current.Dispatcher.Invoke(() => model.Materials[0].Setup(Options, model.UvCount)); - return; - } - - if (!editorCube.TryConvert(out var mesh, EMeshQuality.Highest)) - return; - - Options.Models[guid] = new StaticModel(original, mesh); - Options.SelectModel(guid); - } - - private void LoadPaperSprite(UPaperSprite original) - { - if (!(original.BakedSourceTexture?.TryLoad(out UTexture2D texture) ?? false)) - return; - - var guid = texture.LightingGuid; - if (Options.TryGetModel(guid, out var model)) - { - model.AddInstance(Transform.Identity); - Application.Current.Dispatcher.Invoke(() => model.SetupInstances()); - return; - } - - Options.Models[guid] = new StaticModel(original, texture); - Options.SelectModel(guid); - } - - private void SetupCamera() - { - if (Options.TryGetModel(out var model)) - CameraOp.Setup(model.Box); - } - - private void LoadWorld(CancellationToken cancellationToken, UWorld original, Transform transform) - { - CameraOp.Setup(new FBox(FVector.ZeroVector, new FVector(0, 10, 10))); - if (original.PersistentLevel.Load() is not { } persistentLevel) - return; - - if (persistentLevel.TryGetValue(out FSoftObjectPath runtimeCell, "WorldPartitionRuntimeCell") && - runtimeCell.TryLoad(out UObject worldPartition)) - { - var position = worldPartition.GetOrDefault("Position", FVector.ZeroVector) * Constants.SCALE_DOWN_RATIO; - var box = worldPartition.GetOrDefault("ContentBounds", new FBox(FVector.ZeroVector, FVector.OneVector)); - box *= MathF.Pow(Constants.SCALE_DOWN_RATIO, 2); - CameraOp.Teleport(new Vector3(position.X, position.Z, position.Y), box, true); - } - - var length = persistentLevel.Actors.Length; - for (var i = 0; i < length; i++) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (persistentLevel.Actors[i].Load() is not { } actor || actor.ExportType is "LODActor") - continue; - - Services.ApplicationService.ApplicationView.Status.UpdateStatusLabel($"{original.Name} ... {i}/{length}"); - WorldCamera(actor); - WorldLight(actor); - WorldMesh(actor, transform); - AdditionalWorlds(actor, transform.Matrix, cancellationToken); - } - Services.ApplicationService.ApplicationView.Status.UpdateStatusLabel($"{original.Name} ... {length}/{length}"); - } - - private void LoadJunoWorld(CancellationToken cancellationToken, UBlueprintGeneratedClass original, Transform transform) - { - CameraOp.Setup(new FBox(FVector.ZeroVector, new FVector(0, 10, 10))); - - var length = 0; - FPackageIndex[] allNodes = []; - IPropertyHolder[] records = []; - if (original.TryGetValue(out FPackageIndex simpleConstructionScript, "SimpleConstructionScript") && - simpleConstructionScript.TryLoad(out var scs) && scs.TryGetValue(out allNodes, "AllNodes")) - length = allNodes.Length; - else if (original.TryGetValue(out FPackageIndex inheritableComponentHandler, "InheritableComponentHandler") && - inheritableComponentHandler.TryLoad(out var ich) && ich.TryGetValue(out records, "Records")) - length = records.Length; - - for (var i = 0; i < length; i++) - { - cancellationToken.ThrowIfCancellationRequested(); - - IPropertyHolder actor; - if (allNodes is {Length: > 0} && allNodes[i].TryLoad(out UObject node)) - { - actor = node; - } - else if (records is {Length: > 0}) - { - actor = records[i]; - } - else continue; - - Services.ApplicationService.ApplicationView.Status.UpdateStatusLabel($"{original.Name} ... {i}/{length}"); - WorldMesh(actor, transform, true); - } - Services.ApplicationService.ApplicationView.Status.UpdateStatusLabel($"{original.Name} ... {length}/{length}"); - - if (Options.Models.Count == 1) - { - var (guid, model) = Options.Models.First(); - Options.SelectModel(guid); - CameraOp.Setup(model.Box); - _saveCameraMode = true; - } - } - - private void WorldCamera(UObject actor) - { - if (actor.ExportType != "LevelBounds" || !actor.TryGetValue(out FPackageIndex boxComponent, "BoxComponent") || - boxComponent.Load() is not { } boxObject) return; - - var direction = boxObject.GetOrDefault("RelativeLocation", FVector.ZeroVector) * Constants.SCALE_DOWN_RATIO; - var position = boxObject.GetOrDefault("RelativeScale3D", FVector.OneVector) / 2f * Constants.SCALE_DOWN_RATIO; - CameraOp.Setup(new FBox(direction, position)); - } - - private void WorldLight(UObject actor) - { - if (!actor.TryGetValue(out FPackageIndex lightComponent, "LightComponent") || - lightComponent.Load() is not { } lightObject) return; - - switch (actor.ExportType) - { - case "PointLight": - Options.Lights.Add(new PointLight(Options.Icons["pointlight"], lightObject)); - break; - case "SpotLight": - Options.Lights.Add(new SpotLight(Options.Icons["spotlight"], lightObject)); - break; - case "RectLight": - case "SkyLight": - case "DirectionalLight": - break; - } - } - - private void WorldMesh(IPropertyHolder actor, Transform transform, bool forceShow = false) - { - if (actor.TryGetValue(out FPackageIndex[] instanceComponents, "InstanceComponents")) - { - foreach (var component in instanceComponents) - { - if (!component.TryLoad(out UStaticMeshComponent staticMeshComp) || - !staticMeshComp.GetStaticMesh().TryLoad(out UStaticMesh m) || m.Materials.Length < 1) - continue; - - var relation = CalculateTransform(staticMeshComp, transform); - if (staticMeshComp is UInstancedStaticMeshComponent { PerInstanceSMData.Length: > 0 } instancedStaticMeshComp) - { - - foreach (var perInstanceData in instancedStaticMeshComp.PerInstanceSMData) - { - ProcessMesh(actor, instancedStaticMeshComp, m, new Transform - { - Relation = relation.Matrix, - Position = perInstanceData.TransformData.Translation * Constants.SCALE_DOWN_RATIO, - Rotation = perInstanceData.TransformData.Rotation, - Scale = perInstanceData.TransformData.Scale3D - }); - } - } - else ProcessMesh(actor, staticMeshComp, m, relation); - } - } - else if (actor.TryGetValue(out FPackageIndex componentTemplate, "ComponentTemplate") && - componentTemplate.TryLoad(out UObject compTemplate)) - { - UGeometryCollection geometryCollection = null; - if (!compTemplate.TryGetValue(out UStaticMesh m, "StaticMesh") && - compTemplate.TryGetValue(out FPackageIndex restCollection, "RestCollection") && - restCollection.TryLoad(out geometryCollection) && geometryCollection.RootProxyData is { ProxyMeshes.Length: > 0 } rootProxyData) - { - rootProxyData.ProxyMeshes[0].TryLoad(out m); - } - - if (m is { Materials.Length: > 0 }) - { - OverrideJunoVertexColors(m, geometryCollection); - ProcessMesh(actor, compTemplate, m, CalculateTransform(compTemplate, transform), forceShow); - } - } - else if (actor.TryGetValue(out FPackageIndex staticMeshComponent, "StaticMeshComponent", "ComponentTemplate", "StaticMesh", "Mesh", "LightMesh", "SplineMesh") && - staticMeshComponent.TryLoad(out UStaticMeshComponent staticMeshComp) && - staticMeshComp.GetStaticMesh().TryLoad(out UStaticMesh m) && m.Materials.Length > 0) - { - ProcessMesh(actor, staticMeshComp, m, CalculateTransform(staticMeshComp, transform)); - } - } - - private void ProcessMesh(IPropertyHolder actor, UStaticMeshComponent staticMeshComp, UStaticMesh m, Transform transform) - { - OverrideVertexColors(staticMeshComp, m); - ProcessMesh(actor, staticMeshComp, m, transform, false); - } - private void ProcessMesh(IPropertyHolder actor, UObject staticMeshComp, UStaticMesh m, Transform transform, bool forceShow) - { - var bSpline = staticMeshComp is USplineMeshComponent; - var guid = m.LightingGuid; - if (Options.TryGetModel(guid, out var model)) - { - model.AddInstance(transform); - if (bSpline && model is SplineModel splineModel) - splineModel.AddComponent((USplineMeshComponent)staticMeshComp); - } - else if (m.TryConvert(out var mesh, EMeshQuality.Highest, UserSettings.Default.NaniteMeshExportFormat)) - { - model = bSpline ? new SplineModel(m, mesh, (USplineMeshComponent)staticMeshComp, transform) : new StaticModel(m, mesh, transform); - model.IsTwoSided = actor.GetOrDefault("bMirrored", staticMeshComp.GetOrDefault("bDisallowMeshPaintPerInstance", model.IsTwoSided)); - - if (actor.TryGetAllValues(out FPackageIndex[] textureData, "TextureData")) - { - var material = model.Materials.FirstOrDefault(); - if (material is { IsUsed: true }) - { - for (int j = 0; j < textureData.Length; j++) - { - if (textureData[j]?.Load() is not { } textureDataIdx) - continue; - - if (textureDataIdx.TryGetValue(out FPackageIndex overrideMaterial, "OverrideMaterial") && - overrideMaterial.TryLoad(out var oMaterial) && oMaterial is UMaterialInterface oUnrealMaterial) - material.SwapMaterial(oUnrealMaterial); - - WorldTextureData(material, textureDataIdx, "Diffuse", j switch - { - 0 => "Diffuse", - > 0 => $"Diffuse_Texture_{j + 1}", - _ => CMaterialParams2.FallbackDiffuse - }); - WorldTextureData(material, textureDataIdx, "Normal", j switch - { - 0 => "Normals", - > 0 => $"Normals_Texture_{j + 1}", - _ => CMaterialParams2.FallbackNormals - }); - WorldTextureData(material, textureDataIdx, "Specular", j switch - { - 0 => "SpecularMasks", - > 0 => $"SpecularMasks_{j + 1}", - _ => CMaterialParams2.FallbackNormals - }); - } - } - } - - if (staticMeshComp.TryGetValue(out FPackageIndex[] overrideMaterials, "OverrideMaterials")) - { - for (var j = 0; j < overrideMaterials.Length && j < model.Sections.Length; j++) - { - var matIndex = model.Sections[j].MaterialIndex; - if (matIndex < 0 || matIndex >= model.Materials.Length || matIndex >= overrideMaterials.Length || - overrideMaterials[matIndex].Load() is not UMaterialInterface unrealMaterial) continue; - - model.Materials[matIndex].SwapMaterial(unrealMaterial); - } - } - - if (forceShow) - { - foreach (var section in model.Sections) - { - section.Show = true; - } - } - Options.Models[guid] = model; - } - - if (actor.TryGetValue(out FPackageIndex treasureLight, "PointLight", "TreasureLight") && - treasureLight.TryLoad(out var pl1) && pl1.Template.TryLoad(out var pl2)) - { - Options.Lights.Add(new PointLight(guid, Options.Icons["pointlight"], pl1, pl2, transform)); - } - if (actor.TryGetValue(out FPackageIndex spotLight, "SpotLight") && - spotLight.TryLoad(out var sl1) && sl1.Template.TryLoad(out var sl2)) - { - Options.Lights.Add(new SpotLight(guid, Options.Icons["spotlight"], sl1, sl2, transform)); - } - } - - private Transform CalculateTransform(IPropertyHolder staticMeshComp, Transform relation) - { - if (staticMeshComp.TryGetValue(out FPackageIndex ap, "AttachParent") && ap.TryLoad(out UObject component)) - { - relation = CalculateTransform(component, relation); - } - - return new Transform - { - Relation = relation.Matrix, - Position = staticMeshComp.GetOrDefault("RelativeLocation", FVector.ZeroVector) * Constants.SCALE_DOWN_RATIO, - Rotation = staticMeshComp.GetOrDefault("RelativeRotation", FRotator.ZeroRotator).Quaternion(), - Scale = staticMeshComp.GetOrDefault("RelativeScale3D", FVector.OneVector) - }; - } - - private void OverrideJunoVertexColors(UStaticMesh staticMesh, UGeometryCollection geometryCollection = null) - { - if (staticMesh.RenderData is not { LODs.Length: > 0 } || staticMesh.RenderData.LODs[0].ColorVertexBuffer == null) - return; - - var dico = new Dictionary(); - if (geometryCollection?.Materials is not { Length: > 0 }) - { - var distinctReds = new HashSet(); - for (int i = 0; i < staticMesh.RenderData.LODs[0].ColorVertexBuffer.Data.Length; i++) - { - ref var vertexColor = ref staticMesh.RenderData.LODs[0].ColorVertexBuffer.Data[i]; - var indexAsByte = vertexColor.R; - if (indexAsByte == 255) indexAsByte = vertexColor.A; - distinctReds.Add(indexAsByte); - } - - foreach (var indexAsByte in distinctReds) - { - var path = string.Concat("/JunoAtomAssets/Materials/MI_LegoStandard_", indexAsByte, ".MI_LegoStandard_", indexAsByte); - if (!Utils.TryLoadObject(path, out UMaterialInterface unrealMaterial)) - continue; - - var parameters = new CMaterialParams2(); - unrealMaterial.GetParams(parameters, EMaterialDepth.TopLayerOnly); - - if (!parameters.TryGetLinearColor(out var color, "Color")) - color = FLinearColor.Gray; - - dico[indexAsByte] = color.ToFColor(true); - } - } - else foreach (var material in geometryCollection.Materials) - { - if (!material.TryLoad(out UMaterialInterface unrealMaterial)) continue; - - var parameters = new CMaterialParams2(); - unrealMaterial.GetParams(parameters, EMaterialDepth.TopLayerOnly); - - if (!byte.TryParse(material.Name.SubstringAfterLast("_"), out var indexAsByte)) - indexAsByte = byte.MaxValue; - if (!parameters.TryGetLinearColor(out var color, "Color")) - color = FLinearColor.Gray; - - dico[indexAsByte] = color.ToFColor(true); - } - - for (int i = 0; i < staticMesh.RenderData.LODs[0].ColorVertexBuffer.Data.Length; i++) - { - ref var vertexColor = ref staticMesh.RenderData.LODs[0].ColorVertexBuffer.Data[i]; - vertexColor = dico.TryGetValue(vertexColor.R, out var color) ? color : FColor.Gray; - } - } - - private void OverrideVertexColors(UStaticMeshComponent staticMeshComp, UStaticMesh staticMesh) - { - if (staticMeshComp.LODData is not { Length: > 0 } || staticMesh.RenderData is not { LODs.Length: > 0 }) - return; - - for (var lod = 0; lod < staticMeshComp.LODData.Length; lod++) - { - var vertexColors = staticMeshComp.LODData[lod].OverrideVertexColors; - if (vertexColors == null) continue; - - staticMesh.RenderData.LODs[lod].ColorVertexBuffer = vertexColors; - } - } - - private void WorldTextureData(Material material, UObject textureData, string name, string key) - { - if (textureData.TryGetValue(out FPackageIndex package, name) && package.Load() is UTexture2D texture) - material.Parameters.Textures[key] = texture; - } - - private void AdditionalWorlds(UObject actor, Matrix4x4 relation, CancellationToken cancellationToken) - { - if (!actor.TryGetValue(out FSoftObjectPath[] additionalWorlds, "AdditionalWorlds") || - !actor.TryGetValue(out FPackageIndex staticMeshComponent, "StaticMeshComponent", "Mesh") || - staticMeshComponent.Load() is not { } staticMeshComp) - return; - - var transform = new Transform - { - Relation = relation, - Position = staticMeshComp.GetOrDefault("RelativeLocation", FVector.ZeroVector) * Constants.SCALE_DOWN_RATIO, - Rotation = staticMeshComp.GetOrDefault("RelativeRotation", FRotator.ZeroRotator).Quaternion() - }; - - for (int j = 0; j < additionalWorlds.Length; j++) - if (Utils.TryLoadObject(additionalWorlds[j].AssetPathName.Text, out UWorld w)) - LoadWorld(cancellationToken, w, transform); - } - - public void WindowResized(int width, int height) - { - CameraOp.AspectRatio = width / (float) height; - Picking.WindowResized(width, height); - } - - public void Save() - { - Options.ResetModelsLightsAnimations(); - Options.SelectModel(Guid.Empty); - Options.SwapMaterial(false); - Options.AnimateMesh(false); - - if (_saveCameraMode) UserSettings.Default.CameraMode = CameraOp.Mode; - UserSettings.Default.ShowSkybox = ShowSkybox; - UserSettings.Default.ShowGrid = ShowGrid; - UserSettings.Default.AnimateWithRotationOnly = AnimateWithRotationOnly; - } - - public void Dispose() - { - _skybox?.Dispose(); - _grid?.Dispose(); - _shader?.Dispose(); - _outline?.Dispose(); - _light?.Dispose(); - _bone?.Dispose(); - _collision?.Dispose(); - Picking?.Dispose(); - Options?.Dispose(); - } -} diff --git a/FModel/Views/Snooper/Shading/Material.cs b/FModel/Views/Snooper/Shading/Material.cs deleted file mode 100644 index d250510a..00000000 --- a/FModel/Views/Snooper/Shading/Material.cs +++ /dev/null @@ -1,413 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using CUE4Parse.UE4.Assets.Exports.Material; -using CUE4Parse.UE4.Assets.Exports.Texture; -using CUE4Parse.UE4.Objects.Core.Math; -using CUE4Parse.UE4.Objects.Core.Misc; -using FModel.Extensions; -using FModel.Settings; -using FModel.Views.Snooper.Models; -using ImGuiNET; -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper.Shading; - -public class Material : IDisposable -{ - private int _handle; - - public readonly CMaterialParams2 Parameters; - public string Name; - public string Path; - public int SelectedChannel; - public int SelectedTexture; - public bool IsUsed; - - public Texture[] Diffuse; - public Texture[] Normals; - public Texture[] SpecularMasks; - public Texture[] Emissive; - - public Vector4[] DiffuseColor; - public Vector4[] EmissiveColor; - public Vector4 EmissiveRegion; - - public AoParams Ao; - public bool HasAo; - - public float RoughnessMin = 0f; - public float RoughnessMax = 1f; - public float EmissiveMult = 1f; - - public Material() - { - Parameters = new CMaterialParams2(); - Name = ""; - Path = "None"; - IsUsed = false; - - Diffuse = Array.Empty(); - Normals = Array.Empty(); - SpecularMasks = Array.Empty(); - Emissive = Array.Empty(); - - DiffuseColor = Array.Empty(); - EmissiveColor = Array.Empty(); - EmissiveRegion = new Vector4(0, 0, 1, 1); - } - - public Material(UMaterialInterface unrealMaterial) : this() - { - SwapMaterial(unrealMaterial); - } - - public void SwapMaterial(UMaterialInterface unrealMaterial) - { - Name = unrealMaterial.Name; - Path = unrealMaterial.GetPathName(); - unrealMaterial.GetParams(Parameters, UserSettings.Default.MaterialExportFormat); - } - - public void Setup(Options options, int uvCount) - { - _handle = GL.CreateProgram(); - - if (uvCount < 1 || Parameters.IsNull) - { - Diffuse = [new Texture(FLinearColor.Gray)]; - Normals = [new Texture(new FLinearColor(0.5f, 0.5f, 1f, 1f))]; - SpecularMasks = [new Texture(new FLinearColor(1f, 0.5f, 0.5f, 1f))]; - Emissive = new Texture[1]; - DiffuseColor = FillColors(1, Diffuse, CMaterialParams2.DiffuseColors, Vector4.One); - EmissiveColor = [Vector4.One]; - } - else - { - { // textures - Diffuse = FillTextures(options, uvCount, Parameters.HasTopDiffuse, CMaterialParams2.Diffuse, CMaterialParams2.FallbackDiffuse, true); - Normals = FillTextures(options, uvCount, Parameters.HasTopNormals, CMaterialParams2.Normals, CMaterialParams2.FallbackNormals); - SpecularMasks = FillTextures(options, uvCount, Parameters.HasTopSpecularMasks, CMaterialParams2.SpecularMasks, CMaterialParams2.FallbackSpecularMasks); - Emissive = FillTextures(options, uvCount, true, CMaterialParams2.Emissive, CMaterialParams2.FallbackEmissive); - } - - { // colors - DiffuseColor = FillColors(uvCount, Diffuse, CMaterialParams2.DiffuseColors, Vector4.One); - EmissiveColor = FillColors(uvCount, Emissive, CMaterialParams2.EmissiveColors, Vector4.One); - } - - { // ambient occlusion + color boost - if (Parameters.TryGetTexture2d(out var original, "M", "AEM", "AO") && - !original.Name.Equals("T_BlackMask") && options.TryGetTexture(original, false, out var transformed)) - { - HasAo = true; - Ao = new AoParams { Texture = transformed }; - if (Parameters.TryGetLinearColor(out var l, "Skin Boost Color And Exponent")) - { - Ao.HasColorBoost = true; - Ao.ColorBoost = new Boost { Color = new Vector3(l.R, l.G, l.B), Exponent = l.A }; - } - } - - if (Parameters.TryGetScalar(out var roughnessMin, "RoughnessMin", "SpecRoughnessMin")) - RoughnessMin = roughnessMin; - if (Parameters.TryGetScalar(out var roughnessMax, "RoughnessMax", "SpecRoughnessMax")) - RoughnessMax = roughnessMax; - if (Parameters.TryGetScalar(out var roughness, "Rough", "Roughness", "Ro Multiplier", "RO_mul", "Roughness_Mult")) - { - var d = roughness / 2; - RoughnessMin = roughness - d; - RoughnessMax = roughness + d; - } - - if (!options.SkipEmissive()) - { - if (Parameters.TryGetScalar(out var emissiveMultScalar, "emissive mult", "Emissive_Mult", "EmissiveIntensity", "EmissionIntensity")) - EmissiveMult = emissiveMultScalar; - else if (Parameters.TryGetLinearColor(out var emissiveMultColor, "Emissive Multiplier", "EmissiveMultiplier")) - EmissiveMult = emissiveMultColor.R; - } - else - EmissiveMult = 0f; - - if (Parameters.TryGetLinearColor(out var EmissiveUVs, - "EmissiveUVs_RG_UpperLeftCorner_BA_LowerRightCorner", - "Emissive Texture UVs RG_TopLeft BA_BottomRight", - "Emissive 2 UV Positioning (RG)UpperLeft (BA)LowerRight", - "EmissiveUVPositioning (RG)UpperLeft (BA)LowerRight", - "Emissive_CH", "EmissiveColor4LM", "Emissive Sphere Center")) - EmissiveRegion = new Vector4(EmissiveUVs.R, EmissiveUVs.G, EmissiveUVs.B, EmissiveUVs.A); - - if ((Parameters.TryGetSwitch(out var swizzleRoughnessToGreen, "SwizzleRoughnessToGreen") && swizzleRoughnessToGreen) || - Parameters.Textures.ContainsKey("SRM")) - { - foreach (var specMask in SpecularMasks) - { - specMask.SwizzleMask = new [] - { - (int) PixelFormat.Red, - (int) PixelFormat.Blue, - (int) PixelFormat.Green, - (int) PixelFormat.Alpha - }; - specMask.Swizzle(); - } - } - } - } - } - - /// just the cache object - /// number of item in the array - /// has at least 1 clearly defined texture, else will go straight to fallback - /// list of texture parameter names by uv channel - /// fallback texture name to use if no top texture found - /// if no top texture, no fallback texture, then use the first texture found - private Texture[] FillTextures(Options options, int uvCount, bool top, string[][] triggers, string fallback, bool first = false) - { - UTexture original; - Texture transformed; - var fix = fallback == CMaterialParams2.FallbackSpecularMasks; - var textures = new Texture[uvCount]; - - if (top) - { - for (int i = 0; i < textures.Length; i++) - { - if (Parameters.TryGetTexture2d(out original, triggers[i]) && options.TryGetTexture(original, fix, out transformed)) - textures[i] = transformed; - else if (i > 0 && textures[i - 1] != null) - textures[i] = textures[i - 1]; - } - } - else if (Parameters.TryGetTexture2d(out original, fallback) && options.TryGetTexture(original, fix, out transformed)) - { - for (int i = 0; i < textures.Length; i++) - textures[i] = transformed; - } - else if (first && Parameters.TryGetFirstTexture2d(out original) && options.TryGetTexture(original, fix, out transformed)) - { - for (int i = 0; i < textures.Length; i++) - textures[i] = transformed; - } - return textures; - } - - /// number of item in the array - /// reference array - /// list of color parameter names by uv channel - /// fallback color to use if no trigger was found - private Vector4[] FillColors(int uvCount, Texture[] textures, string[][] triggers, Vector4 fallback) - { - var colors = new Vector4[uvCount]; - for (int i = 0; i < colors.Length; i++) - { - if (textures[i] == null) continue; - - if (Parameters.TryGetLinearColor(out var color, triggers[i]) && color is { A: > 0 }) - { - colors[i] = new Vector4(color.R, color.G, color.B, color.A); - } - else colors[i] = fallback; - } - return colors; - } - - public void Render(Shader shader) - { - var unit = 0; - for (var i = 0; i < Diffuse.Length; i++) - { - shader.SetUniform($"uParameters.Diffuse[{i}].Sampler", unit); - shader.SetUniform($"uParameters.Diffuse[{i}].Color", DiffuseColor[i]); - Diffuse[i]?.Bind(TextureUnit.Texture0 + unit++); - } - - for (var i = 0; i < Normals.Length; i++) - { - shader.SetUniform($"uParameters.Normals[{i}].Sampler", unit); - Normals[i]?.Bind(TextureUnit.Texture0 + unit++); - } - - for (var i = 0; i < SpecularMasks.Length; i++) - { - shader.SetUniform($"uParameters.SpecularMasks[{i}].Sampler", unit); - SpecularMasks[i]?.Bind(TextureUnit.Texture0 + unit++); - } - - for (var i = 0; i < Emissive.Length; i++) - { - shader.SetUniform($"uParameters.Emissive[{i}].Sampler", unit); - shader.SetUniform($"uParameters.Emissive[{i}].Color", EmissiveColor[i]); - Emissive[i]?.Bind(TextureUnit.Texture0 + unit++); - } - - Ao.Texture?.Bind(TextureUnit.Texture31); - shader.SetUniform("uParameters.Ao.Sampler", 31); - shader.SetUniform("uParameters.Ao.HasColorBoost", Ao.HasColorBoost); - shader.SetUniform("uParameters.Ao.ColorBoost.Color", Ao.ColorBoost.Color); - shader.SetUniform("uParameters.Ao.ColorBoost.Exponent", Ao.ColorBoost.Exponent); - shader.SetUniform("uParameters.HasAo", HasAo); - - shader.SetUniform("uParameters.EmissiveRegion", EmissiveRegion); - shader.SetUniform("uParameters.RoughnessMin", RoughnessMin); - shader.SetUniform("uParameters.RoughnessMax", RoughnessMax); - shader.SetUniform("uParameters.EmissiveMult", EmissiveMult); - } - - private const string _mult = "x %.2f"; - private const float _step = 0.01f; - private const float _zero = 0.000001f; // doesn't actually work if _infinite is used as max value /shrug - private const float _infinite = 0.0f; - private const ImGuiSliderFlags _clamp = ImGuiSliderFlags.AlwaysClamp; - public void ImGuiParameters() - { - if (ImGui.BeginTable("parameters", 2)) - { - var id = 1; - SnimGui.Layout("Roughness Min");ImGui.PushID(id++); - ImGui.DragFloat("", ref RoughnessMin, _step, _zero, 1.0f, _mult, _clamp); - ImGui.PopID();SnimGui.Layout("Roughness Max");ImGui.PushID(id++); - ImGui.DragFloat("", ref RoughnessMax, _step, _zero, 1.0f, _mult, _clamp); - ImGui.PopID();SnimGui.Layout("Emissive Multiplier");ImGui.PushID(id++); - ImGui.DragFloat("", ref EmissiveMult, _step, _zero, _infinite, _mult, _clamp); - ImGui.PopID(); - - if (HasAo && Ao.HasColorBoost) - { - SnimGui.Layout("Color Boost");ImGui.PushID(id++); - ImGui.ColorEdit3("", ref Ao.ColorBoost.Color);ImGui.PopID(); - SnimGui.Layout("Color Boost Exponent");ImGui.PushID(id++); - ImGui.DragFloat("", ref Ao.ColorBoost.Exponent, _step, _zero, _infinite, _mult, _clamp); - ImGui.PopID(); - } - ImGui.EndTable(); - } - } - - public void ImGuiBaseProperties(string id) - { - if (ImGui.BeginTable(id, 2, ImGuiTableFlags.SizingStretchProp)) - { - Layout("Blend", Parameters.BlendMode.GetDescription(), true, true); - Layout("Shading", Parameters.ShadingModel.GetDescription(), true, true); - ImGui.EndTable(); - } - } - - public void ImGuiDictionaries(string id, Dictionary dictionary, bool center = false, bool wrap = false) - { - if (ImGui.BeginTable(id, 2)) - { - foreach ((string key, T value) in dictionary.Reverse()) - { - Layout(key, value, center, wrap); - } - ImGui.EndTable(); - } - } - - public void ImGuiColors(Dictionary colors) - { - foreach ((string key, FLinearColor value) in colors.Reverse()) - { - ImGui.ColorButton(key, new Vector4(value.R, value.G, value.B, value.A), ImGuiColorEditFlags.None, new Vector2(16)); - ImGui.SameLine();ImGui.Text(key);SnimGui.TooltipCopy(key); - } - } - - public bool ImGuiTextures(Dictionary icons, IRenderableModel model) - { - if (ImGui.BeginTable("material_textures", 2)) - { - SnimGui.Layout("Channel");ImGui.PushID(1); ImGui.BeginDisabled(model.UvCount < 2); - ImGui.DragInt("", ref SelectedChannel, _step, 0, model.UvCount - 1, "UV %i", ImGuiSliderFlags.AlwaysClamp); - ImGui.EndDisabled();ImGui.PopID();SnimGui.Layout("Type");ImGui.PushID(2); - ImGui.Combo("texture_type", ref SelectedTexture, "Diffuse\0Normals\0Specular\0Ambient Occlusion\0Emissive\0"); - ImGui.PopID(); - - switch (SelectedTexture) - { - case 0 when DiffuseColor.Length > 0: - SnimGui.Layout("Color");ImGui.PushID(3); - ImGui.ColorEdit4("", ref DiffuseColor[SelectedChannel], ImGuiColorEditFlags.NoAlpha); - ImGui.PopID(); - break; - case 4 when EmissiveColor.Length > 0: - SnimGui.Layout("Color");ImGui.PushID(3); - ImGui.ColorEdit4("", ref EmissiveColor[SelectedChannel], ImGuiColorEditFlags.NoAlpha); - ImGui.PopID();SnimGui.Layout("Region");ImGui.PushID(4); - ImGui.DragFloat4("", ref EmissiveRegion, _step, _zero, 1.0f, "%.2f", _clamp); - ImGui.PopID(); - break; - } - - ImGui.EndTable(); - } - - var texture = GetSelectedTexture() ?? icons["noimage"]; - ImGui.Image(texture.GetPointer(), - new Vector2(ImGui.GetContentRegionAvail().X - ImGui.GetScrollX()), - Vector2.Zero, Vector2.One); - return ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(ImGuiMouseButton.Left); - } - - public Texture GetSelectedTexture() - { - return SelectedTexture switch - { - 0 when Diffuse.Length > 0 => Diffuse[SelectedChannel], - 1 when Normals.Length > 0 => Normals[SelectedChannel], - 2 when SpecularMasks.Length > 0 => SpecularMasks[SelectedChannel], - 3 => Ao.Texture, - 4 when Emissive.Length > 0 => Emissive[SelectedChannel], - _ => null - }; - } - - private void Layout(string key, T value, bool center = false, bool wrap = false) - { - SnimGui.Layout(key, true); - var text = $"{value:N}"; - if (center) ImGui.SetCursorPosX(ImGui.GetCursorPosX() + (ImGui.GetColumnWidth() - ImGui.CalcTextSize(text).X) / 2); - if (wrap) ImGui.TextWrapped(text); else ImGui.Text(text); - SnimGui.TooltipCopy(text); - } - - public void Dispose() - { - for (int i = 0; i < Diffuse.Length; i++) - { - Diffuse[i]?.Dispose(); - } - for (int i = 0; i < Normals.Length; i++) - { - Normals[i]?.Dispose(); - } - for (int i = 0; i < SpecularMasks.Length; i++) - { - SpecularMasks[i]?.Dispose(); - } - for (int i = 0; i < Emissive.Length; i++) - { - Emissive[i]?.Dispose(); - } - Ao.Texture?.Dispose(); - GL.DeleteProgram(_handle); - } -} - -public struct AoParams -{ - public Texture Texture; - - public Boost ColorBoost; - public bool HasColorBoost; -} - -public struct Boost -{ - public Vector3 Color; - public float Exponent; -} diff --git a/FModel/Views/Snooper/Shading/Shader.cs b/FModel/Views/Snooper/Shading/Shader.cs deleted file mode 100644 index a1c0f962..00000000 --- a/FModel/Views/Snooper/Shading/Shader.cs +++ /dev/null @@ -1,153 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Numerics; -using System.Reflection; -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper.Shading; - -public class Shader : IDisposable -{ - private readonly int _handle; - private readonly int _vHandle; - private readonly int _fHandle; - private readonly Dictionary _uniformsLocation = new (); - - public Shader() : this("default") {} - - public Shader(string name1, string name2 = null) - { - _handle = GL.CreateProgram(); - _vHandle = LoadShader(ShaderType.VertexShader, $"{name1}.vert"); - _fHandle = LoadShader(ShaderType.FragmentShader, $"{name2 ?? name1}.frag"); - Attach(); - } - - private void Attach() - { - GL.AttachShader(_handle, _vHandle); - GL.AttachShader(_handle, _fHandle); - GL.LinkProgram(_handle); - GL.GetProgram(_handle, GetProgramParameterName.LinkStatus, out var status); - if (status == 0) - { - throw new Exception($"Program failed to link with error: {GL.GetProgramInfoLog(_handle)}"); - } - } - - private void Detach() - { - GL.DetachShader(_handle, _vHandle); - GL.DetachShader(_handle, _fHandle); - GL.DeleteShader(_vHandle); - GL.DeleteShader(_fHandle); - } - - private int LoadShader(ShaderType type, string file) - { - var executingAssembly = Assembly.GetExecutingAssembly(); - var executingAssemblyName = executingAssembly.GetName().Name; - using var stream = executingAssembly.GetManifestResourceStream($"{executingAssemblyName}.Resources.{file}"); - using var reader = new StreamReader(stream); - var handle = GL.CreateShader(type); - - var content = reader.ReadToEnd(); - if (file.Equals("default.frag") && GL.GetInteger(GetPName.MaxTextureCoords) == 0) - content = content.Replace("#define MAX_UV_COUNT 8", "#define MAX_UV_COUNT 1"); - if (type == ShaderType.VertexShader && Array.IndexOf(["default.vert", "outline.vert", "picking.vert"], file) > -1) - { - using var splineStream = executingAssembly.GetManifestResourceStream($"{executingAssemblyName}.Resources.spline.vert"); - using var splineReader = new StreamReader(splineStream); - content = splineReader.ReadToEnd() + Environment.NewLine + content.Replace("#version 460 core", ""); - } - - GL.ShaderSource(handle, content); - GL.CompileShader(handle); - string infoLog = GL.GetShaderInfoLog(handle); - if (!string.IsNullOrWhiteSpace(infoLog)) - { - throw new Exception($"Error compiling shader of type {type}, failed with error {infoLog}"); - } - - return handle; - } - - public void Use() - { - GL.UseProgram(_handle); - } - - public void Render(Matrix4x4 viewMatrix, Vector3 viewPos, Matrix4x4 projMatrix) - { - Render(viewMatrix, projMatrix); - SetUniform("uViewPos", viewPos); - } - public void Render(Matrix4x4 viewMatrix, Matrix4x4 projMatrix) - { - Use(); - SetUniform("uView", viewMatrix); - SetUniform("uProjection", projMatrix); - } - - public void SetUniform(string name, int value) - { - GL.Uniform1(GetUniformLocation(name), value); - } - - public unsafe void SetUniform(string name, Matrix4x4 value) => UniformMatrix4(name, (float*) &value); - public unsafe void UniformMatrix4(string name, float* value) - { - GL.UniformMatrix4(GetUniformLocation(name), 1, false, value); - } - - public void SetUniform(string name, bool value) => SetUniform(name, Convert.ToUInt32(value)); - - public void SetUniform(string name, uint value) - { - GL.Uniform1(GetUniformLocation(name), value); - } - - public void SetUniform(string name, float value) - { - GL.Uniform1(GetUniformLocation(name), value); - } - - public void SetUniform(string name, Vector2 value) => SetUniform3(name, value.X, value.Y); - public void SetUniform3(string name, float x, float y) - { - GL.Uniform2(GetUniformLocation(name), x, y); - } - - public void SetUniform(string name, Vector3 value) => SetUniform3(name, value.X, value.Y, value.Z); - public void SetUniform3(string name, float x, float y, float z) - { - GL.Uniform3(GetUniformLocation(name), x, y, z); - } - - public void SetUniform(string name, Vector4 value) => SetUniform4(name, value.X, value.Y, value.Z, value.W); - public void SetUniform4(string name, float x, float y, float z, float w) - { - GL.Uniform4(GetUniformLocation(name), x, y, z, w); - } - - private int GetUniformLocation(string name) - { - if (!_uniformsLocation.TryGetValue(name, out int location)) - { - location = GL.GetUniformLocation(_handle, name); - _uniformsLocation.Add(name, location); - if (location == -1) - { - throw new Exception($"{name} uniform not found on shader."); - } - } - return location; - } - - public void Dispose() - { - Detach(); - GL.DeleteProgram(_handle); - } -} diff --git a/FModel/Views/Snooper/Shading/Texture.cs b/FModel/Views/Snooper/Shading/Texture.cs deleted file mode 100644 index c425c5ee..00000000 --- a/FModel/Views/Snooper/Shading/Texture.cs +++ /dev/null @@ -1,316 +0,0 @@ -using System; -using System.Numerics; -using System.Windows; -using CUE4Parse_Conversion.Textures; -using CUE4Parse.UE4.Assets.Exports.Texture; -using CUE4Parse.UE4.Objects.Core.Math; -using CUE4Parse.UE4.Objects.Core.Misc; -using ImGuiNET; -using OpenTK.Graphics.OpenGL4; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using SkiaSharp; - -namespace FModel.Views.Snooper.Shading; - -public class Texture : IDisposable -{ - private readonly int _handle; - private readonly TextureType _type; - private readonly TextureTarget _target; - - public readonly string Type; - public readonly FGuid Guid; - public readonly string Name; - public readonly string Path; - public readonly EPixelFormat Format; - public readonly uint ImportedWidth; - public readonly uint ImportedHeight; - public int Width; - public int Height; - - private const int DisabledChannel = (int)BlendingFactor.Zero; - private readonly bool[] _values = [true, true, true, true]; - private readonly string[] _labels = ["R", "G", "B", "A"]; - public int[] SwizzleMask = - [ - (int) PixelFormat.Red, - (int) PixelFormat.Green, - (int) PixelFormat.Blue, - (int) PixelFormat.Alpha - ]; - - private Texture(TextureType type) - { - _handle = GL.GenTexture(); - _type = type; - _target = _type switch - { - TextureType.Cubemap => TextureTarget.TextureCubeMap, - TextureType.MsaaFramebuffer => TextureTarget.Texture2DMultisample, - _ => TextureTarget.Texture2D - }; - - Guid = new FGuid(); - } - - public Texture(uint width, uint height) : this(TextureType.MsaaFramebuffer) - { - Width = (int) width; - Height = (int) height; - Bind(TextureUnit.Texture0); - - GL.TexImage2DMultisample(TextureTargetMultisample.Texture2DMultisample, Constants.SAMPLES_COUNT, PixelInternalFormat.Rgb, Width, Height, true); - GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, _target, _handle, 0); - } - - public Texture(int width, int height) : this(TextureType.Framebuffer) - { - Width = width; - Height = height; - Bind(TextureUnit.Texture0); - - GL.TexImage2D(_target, 0, PixelInternalFormat.Rgb, Width, Height, 0, PixelFormat.Rgb, PixelType.UnsignedByte, IntPtr.Zero); - - GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Linear); - GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear); - GL.TexParameter(_target, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge); - GL.TexParameter(_target, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge); - - GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, _target, _handle, 0); - } - - public Texture(SKBitmap bitmap, UTexture texture2D) : this(TextureType.Normal) - { - Type = texture2D.ExportType; - Guid = texture2D.LightingGuid; - Name = texture2D.Name; - Path = texture2D.GetPathName(); - Format = texture2D.Format; - Width = bitmap.Width; - Height = bitmap.Height; - Bind(TextureUnit.Texture0); - - var internalFormat = bitmap.ColorType switch - { - SKColorType.Gray8 => PixelInternalFormat.R8, - _ => texture2D.SRGB ? PixelInternalFormat.Srgb : PixelInternalFormat.Rgb - }; - - var pixelFormat = bitmap.ColorType switch - { - SKColorType.Gray8 => PixelFormat.Red, - SKColorType.Bgra8888 => PixelFormat.Bgra, - _ => PixelFormat.Rgba - }; - - GL.TexImage2D(_target, 0, internalFormat, Width, Height, 0, pixelFormat, PixelType.UnsignedByte, bitmap.Bytes); - GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear); - GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear); - GL.TexParameter(_target, TextureParameterName.TextureBaseLevel, 0); - GL.TexParameter(_target, TextureParameterName.TextureMaxLevel, 8); - - GL.GenerateMipmap(GenerateMipmapTarget.Texture2D); - bitmap.Dispose(); - } - - public Texture(FLinearColor color) : this(TextureType.Normal) - { - Type = "LinearColor"; - Name = color.Hex; - Width = 1; - Height = 1; - Bind(TextureUnit.Texture0); - - GL.TexImage2D(_target, 0, PixelInternalFormat.Rgba, Width, Height, 0, PixelFormat.Rgba, PixelType.Float, ref color); - GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear); - GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear); - GL.TexParameter(_target, TextureParameterName.TextureBaseLevel, 0); - GL.TexParameter(_target, TextureParameterName.TextureMaxLevel, 8); - - GL.GenerateMipmap(GenerateMipmapTarget.Texture2D); - } - - public Texture(string[] textures) : this(TextureType.Cubemap) - { - Bind(TextureUnit.Texture0); - - for (int t = 0; t < textures.Length; t++) - { - ProcessPixels(textures[t], TextureTarget.TextureCubeMapPositiveX + t); - } - - GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear); - GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear); - GL.TexParameter(_target, TextureParameterName.TextureWrapR, (int) TextureWrapMode.ClampToEdge); - GL.TexParameter(_target, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge); - GL.TexParameter(_target, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge); - - GL.GenerateMipmap(GenerateMipmapTarget.TextureCubeMap); - } - - public Texture(string texture) : this(TextureType.Normal) - { - Bind(TextureUnit.Texture0); - - ProcessPixels(texture, _target); - - GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Linear); - GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear); - GL.TexParameter(_target, TextureParameterName.TextureWrapR, (int) TextureWrapMode.ClampToEdge); - GL.TexParameter(_target, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge); - GL.TexParameter(_target, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge); - } - - private void ProcessPixels(string texture, TextureTarget target) - { - var info = Application.GetResourceStream(new Uri($"/FModel;component/Resources/{texture}.png", UriKind.Relative)); - using var img = Image.Load(info.Stream); - Width = img.Width; - Height = img.Height; - GL.TexImage2D(target, 0, PixelInternalFormat.Rgba8, Width, Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero); - img.ProcessPixelRows(accessor => - { - for (int y = 0; y < accessor.Height; y++) - { - GL.TexSubImage2D(target, 0, 0, y, accessor.Width, 1, PixelFormat.Rgba, PixelType.UnsignedByte, accessor.GetRowSpan(y).ToArray()); - } - }); - } - - public void Bind(TextureUnit textureSlot) - { - GL.ActiveTexture(textureSlot); - Bind(_target); - } - - public void Bind(TextureTarget target) - { - GL.BindTexture(target, _handle); - } - - public void Bind() - { - GL.BindTexture(_target, _handle); - } - - public void Swizzle() - { - Bind(); - GL.TexParameter(_target, TextureParameterName.TextureSwizzleRgba, SwizzleMask); - } - - public IntPtr GetPointer() => (IntPtr) _handle; - - public void WindowResized(int width, int height) - { - Width = width; - Height = height; - - Bind(); - switch (_type) - { - case TextureType.MsaaFramebuffer: - GL.TexImage2DMultisample(TextureTargetMultisample.Texture2DMultisample, Constants.SAMPLES_COUNT, PixelInternalFormat.Rgb, Width, Height, true); - GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, _target, _handle, 0); - break; - case TextureType.Framebuffer: - GL.TexImage2D(_target, 0, PixelInternalFormat.Rgb, Width, Height, 0, PixelFormat.Rgb, PixelType.UnsignedByte, IntPtr.Zero); - GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, _target, _handle, 0); - break; - default: - throw new NotSupportedException(); - } - } - - public void Dispose() - { - GL.DeleteTexture(_handle); - } - - private Vector3 _scrolling = new (0.0f, 0.0f, 1.0f); - public void ImGuiTextureInspector() - { - if (ImGui.BeginTable("texture_inspector", 2, ImGuiTableFlags.SizingStretchProp)) - { - SnimGui.NoFramePaddingOnY(() => - { - SnimGui.Layout("Type");ImGui.Text($" : ({Format}) {Name}"); - SnimGui.TooltipCopy("(?) Click to Copy Path", Path); - SnimGui.Layout("Guid");ImGui.Text($" : {Guid.ToString(EGuidFormats.UniqueObjectGuid)}"); - SnimGui.Layout("Size"); - ImGui.Text($" : {Width}x{Height}"); - - SnimGui.Layout("Swizzle"); - for (int c = 0; c < SwizzleMask.Length; c++) - { - if (ImGui.Checkbox(_labels[c], ref _values[c])) - { - Bind(); - GL.TexParameter(_target, TextureParameterName.TextureSwizzleR + c, _values[c] ? SwizzleMask[c] : DisabledChannel); - } - ImGui.SameLine(); - } - - ImGui.EndTable(); - }); - } - - var io = ImGui.GetIO(); - var canvasP0 = ImGui.GetCursorScreenPos(); - var canvasSize = ImGui.GetContentRegionAvail(); - if (canvasSize.X < 50.0f) canvasSize.X = 50.0f; - if (canvasSize.Y < 50.0f) canvasSize.Y = 50.0f; - var canvasP1 = canvasP0 + canvasSize; - var origin = new Vector2(canvasP0.X + _scrolling.X, canvasP0.Y + _scrolling.Y); - var absoluteMiddle = canvasSize / 2.0f; - - ImGui.InvisibleButton("texture_inspector_canvas", canvasSize, ImGuiButtonFlags.MouseButtonLeft); - if (ImGui.IsItemActive() && ImGui.IsMouseDragging(ImGuiMouseButton.Left)) - { - _scrolling.X += io.MouseDelta.X; - _scrolling.Y += io.MouseDelta.Y; - } - else if (ImGui.IsItemHovered() && io.MouseWheel != 0.0f) - { - var zoomFactor = 1.0f + io.MouseWheel * 0.1f; - var mousePosCanvas = io.MousePos - origin; - - _scrolling.X -= (mousePosCanvas.X - absoluteMiddle.X) * (zoomFactor - 1); - _scrolling.Y -= (mousePosCanvas.Y - absoluteMiddle.Y) * (zoomFactor - 1); - _scrolling.Z *= zoomFactor; - origin = new Vector2(canvasP0.X + _scrolling.X, canvasP0.Y + _scrolling.Y); - } - - var drawList = ImGui.GetWindowDrawList(); - drawList.AddRectFilled(canvasP0, canvasP1, 0xFF242424); - drawList.PushClipRect(canvasP0, canvasP1, true); - { - var sensitivity = _scrolling.Z * 25.0f; - for (float x = _scrolling.X % sensitivity; x < canvasSize.X; x += sensitivity) - drawList.AddLine(canvasP0 with { X = canvasP0.X + x }, canvasP1 with { X = canvasP0.X + x }, 0x28C8C8C8); - for (float y = _scrolling.Y % sensitivity; y < canvasSize.Y; y += sensitivity) - drawList.AddLine(canvasP0 with { Y = canvasP0.Y + y }, canvasP1 with { Y = canvasP0.Y + y }, 0x28C8C8C8); - } - drawList.PopClipRect(); - - drawList.PushClipRect(canvasP0, canvasP1, true); - { - var relativeMiddle = origin + absoluteMiddle; - var ratio = Math.Min(canvasSize.X / Width, canvasSize.Y / Height) * 0.95f * _scrolling.Z; - var size = new Vector2(Width, Height) * ratio / 2f; - - drawList.AddImage(GetPointer(), relativeMiddle - size, relativeMiddle + size); - drawList.AddRect(relativeMiddle - size, relativeMiddle + size, 0xFFFFFFFF); - } - drawList.PopClipRect(); - } -} - -public enum TextureType -{ - Normal, - Cubemap, - Framebuffer, - MsaaFramebuffer -} diff --git a/FModel/Views/Snooper/Shading/TextureHelper.cs b/FModel/Views/Snooper/Shading/TextureHelper.cs deleted file mode 100644 index 01831229..00000000 --- a/FModel/Views/Snooper/Shading/TextureHelper.cs +++ /dev/null @@ -1,73 +0,0 @@ -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper.Shading; - -public static class TextureHelper -{ - /// - /// Red : Specular (not used anymore) - /// Green : Metallic - /// Blue : Roughness - /// - public static void FixChannels(string game, Texture texture) - { - switch (game) - { - // R: Whatever (AO / S / E / ...) - // G: Roughness - // B: Metallic - case "GAMEFACE": - case "HK_PROJECT": - case "COSMICSHAKE": - case "PHOENIX": - case "ATOMICHEART": - case "MULTIVERSUS": - case "BODYCAM": - case "TSLGAME": - { - texture.SwizzleMask = - [ - (int) PixelFormat.Red, - (int) PixelFormat.Blue, - (int) PixelFormat.Green, - (int) PixelFormat.Alpha - ]; - break; - } - // R: Metallic - // G: Roughness - // B: Whatever (AO / S / E / ...) - case "SHOOTERGAME": - case "DIVINEKNOCKOUT": - case "MOONMAN": - case "SHF": - { - texture.SwizzleMask = - [ - (int) PixelFormat.Blue, - (int) PixelFormat.Red, - (int) PixelFormat.Green, - (int) PixelFormat.Alpha - ]; - break; - } - // R: Roughness - // G: Metallic - // B: Whatever (AO / S / E / ...) - case "CCFF7R": - case "PJ033": - case "ABINFINITE": - { - texture.SwizzleMask = - [ - (int) PixelFormat.Blue, - (int) PixelFormat.Green, - (int) PixelFormat.Red, - (int) PixelFormat.Alpha - ]; - break; - } - } - texture.Swizzle(); - } -} diff --git a/FModel/Views/Snooper/SnimGui.cs b/FModel/Views/Snooper/SnimGui.cs deleted file mode 100644 index d923945b..00000000 --- a/FModel/Views/Snooper/SnimGui.cs +++ /dev/null @@ -1,1019 +0,0 @@ -using System; -using System.Collections.Generic; -using CUE4Parse.UE4.Objects.Core.Misc; -using FModel.Framework; -using ImGuiNET; -using OpenTK.Windowing.Common; -using System.Numerics; -using System.Text; -using FModel.Settings; -using FModel.Views.Snooper.Animations; -using FModel.Views.Snooper.Models; -using FModel.Views.Snooper.Shading; -using ImGuizmoNET; -using OpenTK.Graphics.OpenGL4; - -namespace FModel.Views.Snooper; - -public class Swap -{ - public string Title; - public string Description; - public bool Value; - public bool IsAware; - public Action Content; - - public Swap() - { - Reset(); - } - - public void Reset() - { - Title = string.Empty; - Description = string.Empty; - Value = false; - Content = null; - } -} - -public class SnimGui -{ - public readonly ImGuiController Controller; - private readonly Swap _swapper = new (); - private readonly string _renderer; - private readonly string _version; - private readonly float _tableWidth; - - private Vector2 _outlinerSize; - private bool _tiOpen; - private bool _transformOpen; - private bool _viewportFocus; - private OPERATION _guizmoOperation; - - private readonly Vector4 _accentColor = new (0.125f, 0.42f, 0.831f, 1.0f); - private readonly Vector4 _alertColor = new (0.831f, 0.573f, 0.125f, 1.0f); - private readonly Vector4 _errorColor = new (0.761f, 0.169f, 0.169f, 1.0f); - - private const uint _dockspaceId = 1337; - - public SnimGui(int width, int height) - { - Controller = new ImGuiController(width, height); - - _renderer = GL.GetString(StringName.Renderer); - _version = "OpenGL " + GL.GetString(StringName.Version); - _tableWidth = 17 * Controller.DpiScale; - _guizmoOperation = OPERATION.TRANSLATE; - - Theme(); - } - - public void Render(Snooper s) - { - ImGui.DockSpaceOverViewport(_dockspaceId, ImGui.GetMainViewport(), ImGuiDockNodeFlags.PassthruCentralNode); - - SectionWindow("Material Inspector", s.Renderer, DrawMaterialInspector, false); - AnimationWindow("Timeline", s.Renderer, (icons, tracker, animations) => - tracker.ImGuiTimeline(s, icons, animations, _outlinerSize, Controller.FontSemiBold)); - - Window("World", () => DrawWorld(s), false); - - DrawSockets(s); - DrawOuliner(s); - DrawDetails(s); - Draw3DViewport(s); - DrawNavbar(); - - DrawTextureInspector(s); - DrawSkeletonTree(s); - - DrawModals(s); - - Controller.Render(); - } - - private void DrawModals(Snooper s) - { - Modal(_swapper.Title, _swapper.Value, () => - { - ImGui.TextWrapped(_swapper.Description); - ImGui.Separator(); - - ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, Vector2.Zero); - ImGui.Checkbox("Got it! Don't show me again", ref _swapper.IsAware); - ImGui.PopStyleVar(); - - var size = new Vector2(120, 0); - if (ImGui.Button("OK", size)) - { - _swapper.Content(); - _swapper.Reset(); - ImGui.CloseCurrentPopup(); - s.WindowShouldClose(true, false); - } - - ImGui.SetItemDefaultFocus(); - ImGui.SameLine(); - - if (ImGui.Button("Cancel", size)) - { - _swapper.Reset(); - ImGui.CloseCurrentPopup(); - } - }); - - ExportModal.Instance.Draw(); - } - - private void DrawWorld(Snooper s) - { - if (ImGui.BeginTable("world_details", 2, ImGuiTableFlags.SizingStretchProp)) - { - var length = s.Renderer.Options.Models.Count; - - NoFramePaddingOnY(() => - { - Layout("Renderer");ImGui.Text($" : {_renderer}"); - Layout("Version");ImGui.Text($" : {_version}"); - Layout("Loaded Models");ImGui.Text($" : x{length}");ImGui.SameLine(); - - if (ImGui.SmallButton("Save All")) - { - ExportModal.Instance.Export(s.Renderer.Options.Models.Values, UserSettings.Default.ModelDirectory, UserSettings.GetExportOptions()); - } - }); - - ImGui.EndTable(); - } - - ImGui.SeparatorText("Editor"); - if (ImGui.BeginTable("world_editor", 2)) - { - Layout("Animate With Rotation Only");ImGui.PushID(1); - ImGui.Checkbox("", ref s.Renderer.AnimateWithRotationOnly); - ImGui.PopID();Layout("Time Multiplier");ImGui.PushID(2); - ImGui.DragFloat("", ref s.Renderer.Options.Tracker.TimeMultiplier, 0.01f, 0.25f, 8f, "x%.2f", ImGuiSliderFlags.NoInput); - ImGui.PopID();Layout("Vertex Colors");ImGui.PushID(3); - var c = (int) s.Renderer.Color; - ImGui.Combo("vertex_colors", ref c, - "Default\0Sections\0Colors\0Normals\0Texture Coordinates\0"); - s.Renderer.Color = (VertexColor) c; - ImGui.PopID(); - - ImGui.EndTable(); - } - - ImGui.SeparatorText("Camera"); - s.Renderer.CameraOp.ImGuiCamera(); - - ImGui.SeparatorText("Lights"); - for (int i = 0; i < s.Renderer.Options.Lights.Count; i++) - { - var light = s.Renderer.Options.Lights[i]; - var id = s.Renderer.Options.TryGetModel(light.Model, out var lightModel) ? lightModel.Name : "None"; - - id += $"##{i}"; - if (ImGui.TreeNode(id) && ImGui.BeginTable(id, 2)) - { - s.Renderer.Options.SelectModel(light.Model); - light.ImGuiLight(); - ImGui.EndTable(); - ImGui.TreePop(); - } - } - } - - private void DrawNavbar() - { - if (!ImGui.BeginMainMenuBar()) return; - - const int cursorX = 360; - Modal("Commands", ImGui.MenuItem("Commands"), () => - { - ImGui.TextWrapped( - @"Most commands should be pretty straightforward but just in case here is a non-exhaustive list of things you can do in this 3D viewer: - -1. UI / UX - - Press Shift while moving a window to dock it - - Double Click in a box to input a new value - - Mouse Click + Drag in a box to modify the value without having to type - - Press H to hide the window and append the next mesh you extract - -2. Viewport - - WASD to move around - - Shift to move faster - - XC to zoom - - Z to animate the selected model - - Left Mouse Button pressed to look around - - Right Click to select a model in the world - -3. Outliner - 3.1. Right Click Model - - Show / Hide the model - - Show a skeletal representation of the model - - Save to save the model as .psk / .pskx - - Animate to load an animation on the model - - Teleport to quickly move the camera to the position of the model - - Delete - - Deselect - - Copy Path to Clipboard - -4. World - - Save All to save all loaded models at once - (no it's not dying it's just freezing while saving them all) - -5. Details - 5.1. Right Click Section - - Show / Hide the section - - Swap to change the material used by this section - - Copy Path to Clipboard - 5.2. Transform - - Move / Rotate / Scale the model in the world - 5.3. Morph Targets - - Modify the vertices position by a given amount to change the shape of the model - -6. Timeline - - Press Space to play/pause - - Control the time with your mouse - 6.1 Right Click Section - - Animate another loaded model - - Save - - Copy Path to Clipboard -"); - ImGui.Separator(); - - ImGui.SetCursorPosX(cursorX); - ImGui.SetItemDefaultFocus(); - if (ImGui.Button("OK", new Vector2(120, 0))) - { - ImGui.CloseCurrentPopup(); - } - }); - - Modal("GPU OpenGL Info", ImGui.MenuItem("GPU Info"), () => - { - var s = new StringBuilder(); - s.AppendLine($"MaxTextureImageUnits: {GL.GetInteger(GetPName.MaxTextureImageUnits)}"); - s.AppendLine($"MaxTextureUnits: {GL.GetInteger(GetPName.MaxTextureUnits)}"); - s.AppendLine($"MaxVertexTextureImageUnits: {GL.GetInteger(GetPName.MaxVertexTextureImageUnits)}"); - s.AppendLine($"MaxCombinedTextureImageUnits: {GL.GetInteger(GetPName.MaxCombinedTextureImageUnits)}"); - s.AppendLine($"MaxGeometryTextureImageUnits: {GL.GetInteger(GetPName.MaxGeometryTextureImageUnits)}"); - s.AppendLine($"MaxTextureCoords: {GL.GetInteger(GetPName.MaxTextureCoords)}"); - s.AppendLine($"Renderer: {_renderer}"); - s.AppendLine($"Version: {_version}"); - ImGui.TextWrapped(s.ToString()); - ImGui.Separator(); - - ImGui.SetCursorPosX(cursorX); - ImGui.SetItemDefaultFocus(); - if (ImGui.Button("OK", new Vector2(120, 0))) - { - ImGui.CloseCurrentPopup(); - } - }); - - Modal("About Snooper", ImGui.MenuItem("About"), () => - { - ImGui.TextWrapped( - @"Snooper, an ""OpenGL x ImGui"" based 3D viewer, is the result of months of work in order to improve our last one and open up the capabilities data-mining offers. For too long, softwares including FModel were only focused on a bare minimum level of detail showed to the end-user. This is the first step of a long and painful transition to make FModel a viable open-source tool to deep dive into Unreal Engine, its structure, and show how things work internally. - -Snooper aims to give an accurate preview of models, materials, skeletal animations, particles, levels, and level animations (oof) while keeping it compatible with most UE games. This is not an easy task AT ALL, in fact, I don't really know if everything will make out, but what I can say is that we have ideas and a vision for the future of FModel. -"); - ImGui.Separator(); - - ImGui.SetCursorPosX(cursorX); - ImGui.SetItemDefaultFocus(); - if (ImGui.Button("OK", new Vector2(120, 0))) - { - ImGui.CloseCurrentPopup(); - } - }); - - const string text = "Press H to Hide or 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); - - ImGui.EndMainMenuBar(); - } - - private void DrawOuliner(Snooper s) - { - ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero); - Window("Outliner", () => - { - _outlinerSize = ImGui.GetWindowSize(); - if (ImGui.BeginTable("Items", 4, ImGuiTableFlags.Resizable | ImGuiTableFlags.BordersOuterV | ImGuiTableFlags.NoSavedSettings, ImGui.GetContentRegionAvail())) - { - ImGui.TableSetupColumn("Instance", ImGuiTableColumnFlags.NoHeaderWidth | ImGuiTableColumnFlags.WidthFixed, _tableWidth); - ImGui.TableSetupColumn("Channels", ImGuiTableColumnFlags.NoHeaderWidth | ImGuiTableColumnFlags.WidthFixed, _tableWidth); - ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch); - ImGui.TableSetupColumn("", ImGuiTableColumnFlags.NoHeaderWidth | ImGuiTableColumnFlags.WidthFixed, _tableWidth); - ImGui.TableHeadersRow(); - - var i = 0; - foreach ((var guid, var model) in s.Renderer.Options.Models) - { - ImGui.PushID(i); - ImGui.TableNextRow(); - ImGui.TableNextColumn(); - if (!model.IsVisible) - ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, ImGui.GetColorU32(new Vector4(1, 0, 0, .5f))); - else if (model.Attachments.IsAttachment) - ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, ImGui.GetColorU32(new Vector4(0, .75f, 0, .5f))); - else if (model.Attachments.IsAttached) - ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, ImGui.GetColorU32(new Vector4(1, 1, 0, .5f))); - - ImGui.Text(model.TransformsCount.ToString("D")); - ImGui.TableNextColumn(); - ImGui.Text(model.UvCount.ToString("D")); - ImGui.TableNextColumn(); - var doubleClick = false; - if (ImGui.Selectable(model.Name, s.Renderer.Options.SelectedModel == guid, ImGuiSelectableFlags.SpanAllColumns | ImGuiSelectableFlags.AllowDoubleClick)) - { - s.Renderer.Options.SelectModel(guid); - doubleClick = ImGui.IsMouseDoubleClicked(ImGuiMouseButton.Left); - } - Popup(() => - { - s.Renderer.Options.SelectModel(guid); - if (ImGui.MenuItem("Show", null, model.IsVisible)) model.IsVisible = !model.IsVisible; - if (ImGui.MenuItem("Wireframe", null, model.ShowWireframe)) model.ShowWireframe = !model.ShowWireframe; - if (ImGui.MenuItem("Collisions", null, model.ShowCollisions, model.HasCollisions)) model.ShowCollisions = !model.ShowCollisions; - ImGui.Separator(); - if (ImGui.MenuItem("Save")) - { - s.WindowShouldFreeze(true); - ExportModal.Instance.Export([model], UserSettings.Default.ModelDirectory, UserSettings.GetExportOptions()); - s.WindowShouldFreeze(false); - } - if (ImGui.MenuItem("Animate", model is SkeletalModel)) - { - if (_swapper.IsAware) - { - s.Renderer.Options.RemoveAnimations(); - s.Renderer.Options.AnimateMesh(true); - s.WindowShouldClose(true, false); - } - else - { - _swapper.Title = "Skeletal Animation"; - _swapper.Description = "You're about to animate a model.\nThe window will close for you to extract an animation!\n\n"; - _swapper.Content = () => - { - s.Renderer.Options.RemoveAnimations(); - s.Renderer.Options.AnimateMesh(true); - }; - _swapper.Value = true; - } - } - if (ImGui.MenuItem("Skeleton Tree", model is SkeletalModel)) - { - s.Renderer.IsSkeletonTreeOpen = true; - ImGui.SetWindowFocus("Skeleton Tree"); - } - doubleClick = ImGui.MenuItem("Teleport To"); - - if (ImGui.MenuItem("Delete")) s.Renderer.Options.RemoveModel(guid); - if (ImGui.MenuItem("Deselect")) s.Renderer.Options.SelectModel(Guid.Empty); - ImGui.Separator(); - if (ImGui.MenuItem("Copy Path to Clipboard")) ImGui.SetClipboardText(model.Path); - }); - if (doubleClick) - { - s.Renderer.CameraOp.Teleport(model.GetTransform().Matrix.Translation, model.Box); - } - - ImGui.TableNextColumn(); - ImGui.Image(s.Renderer.Options.Icons[model.Attachments.Icon].GetPointer(), new Vector2(_tableWidth)); - TooltipCopy(model.Attachments.Tooltip); - - ImGui.PopID(); - i++; - } - - ImGui.EndTable(); - } - }); - ImGui.PopStyleVar(); - } - - private void DrawSockets(Snooper s) - { - MeshWindow("Sockets", s.Renderer, (icons, selectedModel) => - { - var info = new SocketAttachementInfo { Guid = s.Renderer.Options.SelectedModel, Instance = selectedModel.SelectedInstance }; - foreach (var model in s.Renderer.Options.Models.Values) - { - if (!model.HasSockets || model.IsSelected) continue; - if (ImGui.TreeNode($"{model.Name} [{model.Sockets.Count}]")) - { - var i = 0; - foreach (var socket in model.Sockets) - { - var isAttached = socket.AttachedModels.Contains(info); - ImGui.PushID(i); - ImGui.BeginDisabled(selectedModel.Attachments.IsAttached && !isAttached); - switch (isAttached) - { - case false when ImGui.Button($"Attach to '{socket.Name}'"): - selectedModel.Attachments.Attach(model, selectedModel.GetTransform(), socket, info); - break; - case true when ImGui.Button($"Detach from '{socket.Name}'"): - selectedModel.Attachments.Detach(model, selectedModel.GetTransform(), socket, info); - break; - } - ImGui.EndDisabled(); - ImGui.PopID(); - i++; - } - ImGui.TreePop(); - } - } - }); - } - - private void DrawDetails(Snooper s) - { - ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero); - MeshWindow("Details", s.Renderer, (icons, model) => - { - if (ImGui.BeginTable("model_details", 2, ImGuiTableFlags.SizingStretchProp)) - { - NoFramePaddingOnY(() => - { - Layout("Entity");ImGui.Text($" : ({model.Type}) {model.Name}"); - Layout("Guid");ImGui.Text($" : {s.Renderer.Options.SelectedModel.ToString(EGuidFormats.UniqueObjectGuid)}"); - if (model is SkeletalModel skeletalModel) - { - Layout("Skeleton");ImGui.Text($" : {skeletalModel.Skeleton.Name}"); - Layout("Bones");ImGui.Text($" : x{skeletalModel.Skeleton.BoneCount}"); - } - else - { - Layout("Two Sided");ImGui.Text($" : {model.IsTwoSided}"); - } - Layout("Sockets");ImGui.Text($" : x{model.Sockets.Count}"); - - ImGui.EndTable(); - }); - } - if (ImGui.BeginTabBar("tabbar_details", ImGuiTabBarFlags.None)) - { - if (ImGui.BeginTabItem("Sections") && ImGui.BeginTable("table_sections", 2, ImGuiTableFlags.Resizable | ImGuiTableFlags.BordersOuterV | ImGuiTableFlags.NoSavedSettings, ImGui.GetContentRegionAvail())) - { - ImGui.TableSetupColumn("Index", ImGuiTableColumnFlags.NoHeaderWidth | ImGuiTableColumnFlags.WidthFixed, _tableWidth); - ImGui.TableSetupColumn("Material"); - ImGui.TableHeadersRow(); - - for (var i = 0; i < model.Sections.Length; i++) - { - var section = model.Sections[i]; - var material = model.Materials[section.MaterialIndex]; - - ImGui.PushID(i); - ImGui.TableNextRow(); - ImGui.TableNextColumn(); - if (!section.Show) - { - ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, ImGui.GetColorU32(new Vector4(1, 0, 0, .5f))); - } - else if (s.Renderer.Color == VertexColor.Sections) - { - ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, ImGui.GetColorU32(new Vector4(section.Color, 0.5f))); - } - - ImGui.Text(section.MaterialIndex.ToString("D")); - ImGui.TableNextColumn(); - if (ImGui.Selectable(material.Name, s.Renderer.Options.SelectedSection == i, ImGuiSelectableFlags.SpanAllColumns)) - { - s.Renderer.Options.SelectSection(i); - } - Popup(() => - { - s.Renderer.Options.SelectSection(i); - if (ImGui.MenuItem("Show", null, section.Show)) section.Show = !section.Show; - if (ImGui.MenuItem("Swap")) - { - if (_swapper.IsAware) - { - s.Renderer.Options.SwapMaterial(true); - s.WindowShouldClose(true, false); - } - else - { - _swapper.Title = "Material Swap"; - _swapper.Description = "You're about to swap a material.\nThe window will close for you to extract a material!\n\n"; - _swapper.Content = () => s.Renderer.Options.SwapMaterial(true); - _swapper.Value = true; - } - } - ImGui.Separator(); - if (ImGui.MenuItem("Copy Path to Clipboard")) ImGui.SetClipboardText(material.Path); - }); - ImGui.PopID(); - } - ImGui.EndTable(); - - ImGui.EndTabItem(); - } - - _transformOpen = ImGui.BeginTabItem("Transform"); - if (_transformOpen) - { - ImGui.PushID(0); ImGui.BeginDisabled(model.TransformsCount < 2); - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); - var instance = model.SelectedInstance; - if (ImGui.SliderInt("", ref instance, 0, model.TransformsCount - 1, "Instance %i", ImGuiSliderFlags.AlwaysClamp)) - { - model.SelectedInstance = instance; - } - ImGui.EndDisabled(); ImGui.PopID(); - - if (ImGui.BeginTable("guizmo_controls", 2, ImGuiTableFlags.SizingStretchProp)) - { - var t = model.Transforms[model.SelectedInstance]; - var c = _guizmoOperation switch - { - OPERATION.TRANSLATE => 0, - OPERATION.ROTATE => 1, - OPERATION.SCALE => 2, - _ => 3 - }; - - Layout("Operation "); - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X * 0.6f); - ImGui.PushID(1);ImGui.Combo("", ref c, "Translate\0Rotate\0Scale\0"); - ImGui.PopID();ImGui.SameLine();if (ImGui.Button("Reset All")) t.Reset(); - Layout("Position");ImGui.Text(t.Position.ToString()); - Layout("Rotation");ImGui.Text(t.Rotation.ToString()); - Layout("Scale");ImGui.Text(t.Scale.ToString()); - - _guizmoOperation = c switch - { - 0 => OPERATION.TRANSLATE, - 1 => OPERATION.ROTATE, - 2 => OPERATION.SCALE, - _ => OPERATION.UNIVERSAL - }; - - ImGui.EndTable(); - } - - ImGui.SeparatorText("Manual Inputs"); - model.Transforms[model.SelectedInstance].ImGuiTransform(s.Renderer.CameraOp.Speed / 100f); - - ImGui.EndTabItem(); - } - - if (ImGui.BeginTabItem("Morph Targets")) - { - if (model is SkeletalModel { HasMorphTargets: true } skeletalModel) - { - const float width = 10; - var region = ImGui.GetContentRegionAvail(); - var box = new Vector2(region.X - width, region.Y / 1.5f); - - if (ImGui.BeginListBox("", box)) - { - for (int i = 0; i < skeletalModel.Morphs.Count; i++) - { - ImGui.PushID(i); - if (ImGui.Selectable(skeletalModel.Morphs[i].Name, s.Renderer.Options.SelectedMorph == i)) - { - s.Renderer.Options.SelectMorph(i, skeletalModel); - } - ImGui.PopID(); - } - ImGui.EndListBox(); - - ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(2f, 0f)); - ImGui.SameLine(); ImGui.PushID(99); - ImGui.VSliderFloat("", box with { X = width }, ref skeletalModel.MorphTime, 0.0f, 1.0f, "", ImGuiSliderFlags.AlwaysClamp); - ImGui.PopID(); ImGui.PopStyleVar(); - ImGui.Spacing(); - ImGui.Text($"Time: {skeletalModel.MorphTime:P}%"); - } - } - else CenteredTextColored(_errorColor, "Selected Mesh Has No Morph Targets"); - ImGui.EndTabItem(); - } - - ImGui.EndTabBar(); - } - }); - ImGui.PopStyleVar(); - } - - private void DrawMaterialInspector(Dictionary icons, IRenderableModel model, Section section) - { - var material = model.Materials[section.MaterialIndex]; - - ImGui.Spacing(); - ImGui.Image(icons["material"].GetPointer(), new Vector2(24)); - ImGui.SameLine(); ImGui.AlignTextToFramePadding(); ImGui.Text(material.Name); - ImGui.Spacing(); - - ImGui.SeparatorText("Parameters"); - material.ImGuiParameters(); - - ImGui.SeparatorText("Textures"); - if (material.ImGuiTextures(icons, model)) - { - _tiOpen = true; - ImGui.SetWindowFocus("Texture Inspector"); - } - - ImGui.SeparatorText("Properties"); - NoFramePaddingOnY(() => - { - ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); - if (ImGui.TreeNode("Base")) - { - material.ImGuiBaseProperties("base"); - ImGui.TreePop(); - } - - ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); - if (ImGui.TreeNode("Scalars")) - { - material.ImGuiDictionaries("scalars", material.Parameters.Scalars, true); - ImGui.TreePop(); - } - ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); - if (ImGui.TreeNode("Switches")) - { - material.ImGuiDictionaries("switches", material.Parameters.Switches, true); - ImGui.TreePop(); - } - ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); - if (ImGui.TreeNode("Colors")) - { - material.ImGuiColors(material.Parameters.Colors); - ImGui.TreePop(); - } - if (ImGui.TreeNode("All Textures")) - { - material.ImGuiDictionaries("textures", material.Parameters.Textures); - ImGui.TreePop(); - } - }); - } - - private void DrawTextureInspector(Snooper s) - { - if (!_tiOpen) return; - if (ImGui.Begin("Texture Inspector", ref _tiOpen, ImGuiWindowFlags.NoScrollbar)) - { - if (s.Renderer.Options.TryGetModel(out var model) && s.Renderer.Options.TryGetSection(model, out var section)) - { - (model.Materials[section.MaterialIndex].GetSelectedTexture() ?? s.Renderer.Options.Icons["noimage"]).ImGuiTextureInspector(); - } - } - ImGui.End(); - } - - private void DrawSkeletonTree(Snooper s) - { - if (!s.Renderer.IsSkeletonTreeOpen) return; - - ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero); - if (ImGui.Begin("Skeleton Tree", ref s.Renderer.IsSkeletonTreeOpen, ImGuiWindowFlags.NoScrollbar)) - { - if (s.Renderer.Options.TryGetModel(out var model) && model is SkeletalModel skeletalModel) - { - skeletalModel.Skeleton.ImGuiBoneBreadcrumb(); - if (ImGui.BeginTable("skeleton_tree", 2, ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.RowBg, ImGui.GetContentRegionAvail(), ImGui.GetWindowWidth())) - { - ImGui.TableSetupColumn("Bone", ImGuiTableColumnFlags.WidthStretch); - ImGui.TableSetupColumn("", ImGuiTableColumnFlags.NoHeaderWidth | ImGuiTableColumnFlags.WidthFixed, _tableWidth); - skeletalModel.Skeleton.ImGuiBoneHierarchy(); - ImGui.EndTable(); - } - } - } - ImGui.End(); - ImGui.PopStyleVar(); - } - - private void Draw3DViewport(Snooper s) - { - ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero); - Window("3D Viewport", () => - { - var largest = ImGui.GetContentRegionAvail(); - largest.X -= ImGui.GetScrollX(); - largest.Y -= ImGui.GetScrollY(); - - var size = new Vector2(largest.X, largest.Y); - var pos = ImGui.GetWindowPos(); - var fHeight = ImGui.GetFrameHeight(); - - s.Renderer.CameraOp.AspectRatio = size.X / size.Y; - ImGui.Image(s.Framebuffer.GetPointer(), size, new Vector2(0, 1), new Vector2(1, 0)); - - if (_transformOpen) - { - ImGuizmo.SetDrawlist(ImGui.GetWindowDrawList()); - ImGuizmo.SetRect(pos.X, pos.Y + fHeight, size.X, size.Y); - DrawGuizmo(s); - } - - if (!ImGuizmo.IsUsing()) - { - if (ImGui.IsItemHovered()) - { - // if left button down while mouse is hover viewport - if (ImGui.IsMouseDown(ImGuiMouseButton.Left) && !_viewportFocus) - { - _viewportFocus = true; - s.CursorState = CursorState.Grabbed; - } - if (ImGui.IsMouseClicked(ImGuiMouseButton.Right)) - { - var guid = s.Renderer.Picking.ReadPixel(ImGui.GetMousePos(), ImGui.GetCursorScreenPos(), size); - s.Renderer.Options.SelectModel(guid); - ImGui.SetWindowFocus("Outliner"); - ImGui.SetWindowFocus("Details"); - } - } - - if (_viewportFocus && ImGui.IsMouseDragging(ImGuiMouseButton.Left)) - { - s.Renderer.CameraOp.Modify(ImGui.GetIO().MouseDelta); - } - - // if left button up and mouse was in viewport - if (_viewportFocus && ImGui.IsMouseReleased(ImGuiMouseButton.Left)) - { - _viewportFocus = false; - s.CursorState = CursorState.Normal; - } - } - - const float margin = 7.5f; - var buttonWidth = 14.0f * ImGui.GetWindowDpiScale(); - var basePos = new Vector2( size.X - buttonWidth - margin * 2, fHeight + margin); - ImGui.SetCursorPos(basePos); - ImGui.PushStyleColor(ImGuiCol.Button, Vector4.Zero); - ImGui.PushStyleColor(ImGuiCol.ButtonHovered, new Vector4(0.2f)); - ImGui.ImageButton("skybox_btn", s.Renderer.Options.Icons[s.Renderer.ShowSkybox ? "cube" : "cube_off"].GetPointer(), new Vector2(buttonWidth)); - TooltipCheckbox("Skybox", ref s.Renderer.ShowSkybox); - - basePos.X -= buttonWidth + margin; - ImGui.SetCursorPos(basePos); - ImGui.ImageButton("grid_btn", s.Renderer.Options.Icons[s.Renderer.ShowGrid ? "square" : "square_off"].GetPointer(), new Vector2(buttonWidth)); - TooltipCheckbox("Grid", ref s.Renderer.ShowGrid); - - basePos.X -= buttonWidth + margin; - ImGui.SetCursorPos(basePos); - ImGui.ImageButton("lights_btn", s.Renderer.Options.Icons[s.Renderer.ShowLights ? "light" : "light_off"].GetPointer(), new Vector2(buttonWidth)); - TooltipCheckbox("Lights", ref s.Renderer.ShowLights); - - ImGui.PopStyleColor(2); - - float framerate = ImGui.GetIO().Framerate; - ImGui.SetCursorPos(size with { X = margin }); - ImGui.Text($"FPS: {framerate:0} ({1000.0f / framerate:0.##} ms)"); - - const string label = "Previewed content may differ from final version saved or used in-game."; - ImGui.SetCursorPos(size with { X = size.X - ImGui.CalcTextSize(label).X - margin }); - ImGui.TextColored(new Vector4(0.50f, 0.50f, 0.50f, 1.00f), label); - - }, false); - ImGui.PopStyleVar(); - } - - private void DrawGuizmo(Snooper s) - { - var enableGuizmo = s.Renderer.Options.TryGetModel(out var selected) && selected.IsVisible; - if (enableGuizmo) - { - var view = s.Renderer.CameraOp.GetViewMatrix(); - var proj = s.Renderer.CameraOp.GetProjectionMatrix(); - var transform = selected.Transforms[selected.SelectedInstance]; - var matrix = transform.Matrix; - - if (ImGuizmo.Manipulate(ref view.M11, ref proj.M11, _guizmoOperation, MODE.LOCAL, ref matrix.M11) && - Matrix4x4.Invert(transform.Relation, out var invRelation)) - { - // ^ long story short: there was issues with other transformation methods - // that's one way of modifying root elements without breaking the world matrix - transform.ModifyLocal(matrix * invRelation); - } - } - } - - public static void Popup(Action content) - { - ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(4f)); - if (ImGui.BeginPopupContextItem()) - { - content(); - ImGui.EndPopup(); - } - ImGui.PopStyleVar(); - } - - private void Modal(string title, bool condition, Action content) - { - ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(4f)); - var pOpen = true; - if (condition) ImGui.OpenPopup(title); - ImGui.SetNextWindowPos(ImGui.GetMainViewport().GetCenter(), ImGuiCond.Appearing, new Vector2(.5f)); - if (ImGui.BeginPopupModal(title, ref pOpen, ImGuiWindowFlags.AlwaysAutoResize)) - { - content(); - ImGui.EndPopup(); - } - ImGui.PopStyleVar(); - } - - private void Window(string name, Action content, bool styled = true) - { - if (ImGui.Begin(name, ImGuiWindowFlags.NoScrollbar)) - { - Controller.Normal(); - if (styled) PushStyleCompact(); - content(); - if (styled) PopStyleCompact(); - ImGui.PopFont(); - } - ImGui.End(); - } - - private void MeshWindow(string name, Renderer renderer, Action, IRenderableModel> content, bool styled = true) - { - Window(name, () => - { - if (renderer.Options.TryGetModel(out var model)) content(renderer.Options.Icons, model); - else NoMeshSelected(); - }, styled); - } - - private void SectionWindow(string name, Renderer renderer, Action, IRenderableModel, Section> content, bool styled = true) - { - MeshWindow(name, renderer, (icons, model) => - { - if (renderer.Options.TryGetSection(model, out var section)) content(icons, model, section); - else NoSectionSelected(); - }, styled); - } - - private void AnimationWindow(string name, Renderer renderer, Action, TimeTracker, List> content, bool styled = true) - { - ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero); - Window(name, () => content(renderer.Options.Icons, renderer.Options.Tracker, renderer.Options.Animations), styled); - ImGui.PopStyleVar(); - } - - private void PopStyleCompact() => ImGui.PopStyleVar(2); - private void PushStyleCompact() - { - ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(8, 3)); - ImGui.PushStyleVar(ImGuiStyleVar.CellPadding, new Vector2(0, 1)); - } - - public static void NoFramePaddingOnY(Action content) - { - ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(8, 0)); - content(); - ImGui.PopStyleVar(); - } - - private void NoMeshSelected() => CenteredTextColored(_errorColor, "No Mesh Selected"); - private void NoSectionSelected() => CenteredTextColored(_errorColor, "No Section Selected"); - private void CenteredTextColored(Vector4 color, string text) - { - var region = ImGui.GetContentRegionAvail(); - var size = ImGui.CalcTextSize(text); - ImGui.SetCursorPos(new Vector2( - ImGui.GetCursorPosX() + (region.X - size.X) / 2, - ImGui.GetCursorPosY() + (region.Y - size.Y) / 2)); - Controller.Bold(); - ImGui.TextColored(color, text); - ImGui.PopFont(); - } - - public static void Layout(string name, bool tooltip = false) - { - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); - ImGui.AlignTextToFramePadding(); - ImGui.Spacing();ImGui.SameLine();ImGui.Text(name); - if (tooltip) TooltipCopy(name); - ImGui.TableSetColumnIndex(1); - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); - } - - public static void TooltipCopy(string label, string text = null) - { - if (ImGui.IsItemHovered()) - { - ImGui.BeginTooltip(); - ImGui.Text(label); - ImGui.EndTooltip(); - } - if (ImGui.IsItemClicked()) ImGui.SetClipboardText(text ?? label); - } - - private static void TooltipCheckbox(string tooltip, ref bool value) - { - if (ImGui.IsItemHovered()) - { - ImGui.BeginTooltip(); - ImGui.Text($"{tooltip}: {value}"); - ImGui.EndTooltip(); - } - if (ImGui.IsItemClicked()) value = !value; - } - - private void Theme() - { - var style = ImGui.GetStyle(); - style.WindowPadding = new Vector2(4f); - style.FramePadding = new Vector2(3f); - style.CellPadding = new Vector2(3f, 2f); - style.ItemSpacing = new Vector2(6f, 3f); - style.ItemInnerSpacing = new Vector2(3f); - style.TouchExtraPadding = new Vector2(0f); - style.IndentSpacing = 20f; - style.ScrollbarSize = 10f; - style.GrabMinSize = 8f; - style.WindowBorderSize = 0f; - style.ChildBorderSize = 0f; - style.PopupBorderSize = 0f; - style.FrameBorderSize = 0f; - style.TabBorderSize = 0f; - style.WindowRounding = 0f; - style.ChildRounding = 0f; - style.FrameRounding = 0f; - style.PopupRounding = 0f; - style.ScrollbarRounding = 0f; - style.GrabRounding = 0f; - style.LogSliderDeadzone = 0f; - style.TabRounding = 0f; - style.WindowTitleAlign = new Vector2(0.5f); - style.WindowMenuButtonPosition = ImGuiDir.Right; - style.ColorButtonPosition = ImGuiDir.Right; - style.ButtonTextAlign = new Vector2(0.5f); - style.SelectableTextAlign = new Vector2(0f); - style.DisplaySafeAreaPadding = new Vector2(3f); - - style.Colors[(int) ImGuiCol.Text] = new Vector4(1.00f, 1.00f, 1.00f, 1.00f); - style.Colors[(int) ImGuiCol.TextDisabled] = new Vector4(0.50f, 0.50f, 0.50f, 1.00f); - style.Colors[(int) ImGuiCol.WindowBg] = new Vector4(0.11f, 0.11f, 0.12f, 1.00f); - style.Colors[(int) ImGuiCol.ChildBg] = new Vector4(0.15f, 0.15f, 0.19f, 1.00f); - style.Colors[(int) ImGuiCol.PopupBg] = new Vector4(0.08f, 0.08f, 0.08f, 0.94f); - style.Colors[(int) ImGuiCol.Border] = new Vector4(0.25f, 0.26f, 0.33f, 1.00f); - style.Colors[(int) ImGuiCol.BorderShadow] = new Vector4(0.00f, 0.00f, 0.00f, 0.00f); - style.Colors[(int) ImGuiCol.FrameBg] = new Vector4(0.05f, 0.05f, 0.05f, 0.54f); - style.Colors[(int) ImGuiCol.FrameBgHovered] = new Vector4(0.69f, 0.69f, 1.00f, 0.20f); - style.Colors[(int) ImGuiCol.FrameBgActive] = new Vector4(0.69f, 0.69f, 1.00f, 0.39f); - style.Colors[(int) ImGuiCol.TitleBg] = new Vector4(0.09f, 0.09f, 0.09f, 1.00f); - style.Colors[(int) ImGuiCol.TitleBgActive] = new Vector4(0.09f, 0.09f, 0.09f, 1.00f); - style.Colors[(int) ImGuiCol.TitleBgCollapsed] = new Vector4(0.05f, 0.05f, 0.05f, 0.51f); - style.Colors[(int) ImGuiCol.MenuBarBg] = new Vector4(0.14f, 0.14f, 0.14f, 1.00f); - style.Colors[(int) ImGuiCol.ScrollbarBg] = new Vector4(0.02f, 0.02f, 0.02f, 0.53f); - style.Colors[(int) ImGuiCol.ScrollbarGrab] = new Vector4(0.31f, 0.31f, 0.31f, 1.00f); - style.Colors[(int) ImGuiCol.ScrollbarGrabHovered] = new Vector4(0.41f, 0.41f, 0.41f, 1.00f); - style.Colors[(int) ImGuiCol.ScrollbarGrabActive] = new Vector4(0.51f, 0.51f, 0.51f, 1.00f); - style.Colors[(int) ImGuiCol.CheckMark] = new Vector4(0.13f, 0.42f, 0.83f, 1.00f); - style.Colors[(int) ImGuiCol.SliderGrab] = new Vector4(0.13f, 0.42f, 0.83f, 0.78f); - style.Colors[(int) ImGuiCol.SliderGrabActive] = new Vector4(0.13f, 0.42f, 0.83f, 1.00f); - style.Colors[(int) ImGuiCol.Button] = new Vector4(0.05f, 0.05f, 0.05f, 0.54f); - style.Colors[(int) ImGuiCol.ButtonHovered] = new Vector4(0.69f, 0.69f, 1.00f, 0.20f); - style.Colors[(int) ImGuiCol.ButtonActive] = new Vector4(0.69f, 0.69f, 1.00f, 0.39f); - style.Colors[(int) ImGuiCol.Header] = new Vector4(0.05f, 0.26f, 0.56f, 1.00f); - style.Colors[(int) ImGuiCol.HeaderHovered] = new Vector4(0.05f, 0.26f, 0.56f, 0.39f); - style.Colors[(int) ImGuiCol.HeaderActive] = new Vector4(0.04f, 0.23f, 0.52f, 1.00f); - style.Colors[(int) ImGuiCol.Separator] = new Vector4(0.43f, 0.43f, 0.50f, 0.50f); - style.Colors[(int) ImGuiCol.SeparatorHovered] = new Vector4(0.10f, 0.40f, 0.75f, 0.78f); - style.Colors[(int) ImGuiCol.SeparatorActive] = new Vector4(0.10f, 0.40f, 0.75f, 1.00f); - style.Colors[(int) ImGuiCol.ResizeGrip] = new Vector4(0.13f, 0.42f, 0.83f, 0.39f); - style.Colors[(int) ImGuiCol.ResizeGripHovered] = new Vector4(0.12f, 0.41f, 0.81f, 0.78f); - style.Colors[(int) ImGuiCol.ResizeGripActive] = new Vector4(0.12f, 0.41f, 0.81f, 1.00f); - style.Colors[(int) ImGuiCol.Tab] = new Vector4(0.15f, 0.15f, 0.19f, 1.00f); - style.Colors[(int) ImGuiCol.TabHovered] = new Vector4(0.35f, 0.35f, 0.41f, 0.80f); - style.Colors[(int) ImGuiCol.TabSelected] = new Vector4(0.23f, 0.24f, 0.29f, 1.00f); - style.Colors[(int) ImGuiCol.TabDimmed] = new Vector4(0.15f, 0.15f, 0.15f, 1.00f); - style.Colors[(int) ImGuiCol.TabDimmedSelected] = new Vector4(0.23f, 0.24f, 0.29f, 1.00f); - style.Colors[(int) ImGuiCol.DockingPreview] = new Vector4(0.26f, 0.59f, 0.98f, 0.70f); - style.Colors[(int) ImGuiCol.DockingEmptyBg] = new Vector4(0.20f, 0.20f, 0.20f, 1.00f); - style.Colors[(int) ImGuiCol.PlotLines] = new Vector4(0.61f, 0.61f, 0.61f, 1.00f); - style.Colors[(int) ImGuiCol.PlotLinesHovered] = new Vector4(1.00f, 0.43f, 0.35f, 1.00f); - style.Colors[(int) ImGuiCol.PlotHistogram] = new Vector4(0.90f, 0.70f, 0.00f, 1.00f); - style.Colors[(int) ImGuiCol.PlotHistogramHovered] = new Vector4(1.00f, 0.60f, 0.00f, 1.00f); - style.Colors[(int) ImGuiCol.TableHeaderBg] = new Vector4(0.09f, 0.09f, 0.09f, 1.00f); - style.Colors[(int) ImGuiCol.TableBorderStrong] = new Vector4(0.69f, 0.69f, 1.00f, 0.20f); - style.Colors[(int) ImGuiCol.TableBorderLight] = new Vector4(0.69f, 0.69f, 1.00f, 0.20f); - style.Colors[(int) ImGuiCol.TableRowBg] = new Vector4(0.00f, 0.00f, 0.00f, 0.00f); - style.Colors[(int) ImGuiCol.TableRowBgAlt] = new Vector4(1.00f, 1.00f, 1.00f, 0.06f); - style.Colors[(int) ImGuiCol.TextSelectedBg] = new Vector4(0.26f, 0.59f, 0.98f, 0.35f); - style.Colors[(int) ImGuiCol.DragDropTarget] = new Vector4(1.00f, 1.00f, 0.00f, 0.90f); - style.Colors[(int) ImGuiCol.NavCursor] = new Vector4(0.26f, 0.59f, 0.98f, 1.00f); - style.Colors[(int) ImGuiCol.NavWindowingHighlight] = new Vector4(1.00f, 1.00f, 1.00f, 0.70f); - 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); - } -} diff --git a/FModel/Views/Snooper/Snooper.cs b/FModel/Views/Snooper/Snooper.cs deleted file mode 100644 index 5784e51c..00000000 --- a/FModel/Views/Snooper/Snooper.cs +++ /dev/null @@ -1,234 +0,0 @@ -using System; -using System.ComponentModel; -using System.Runtime.InteropServices; -using System.Threading; -using System.Windows.Forms; -using CUE4Parse.UE4.Assets.Exports; -using FModel.Views.Snooper.Buffers; -using OpenTK.Graphics.OpenGL4; -using OpenTK.Windowing.Common; -using OpenTK.Windowing.Common.Input; -using OpenTK.Windowing.Desktop; -using OpenTK.Windowing.GraphicsLibraryFramework; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.PixelFormats; -using Application = System.Windows.Application; - -namespace FModel.Views.Snooper; - -public class Snooper : GameWindow -{ - public readonly FramebufferObject Framebuffer; - public readonly Renderer Renderer; - - private readonly SnimGui _gui; - - private bool _init; - - public Snooper(GameWindowSettings gwSettings, NativeWindowSettings nwSettings) : base(gwSettings, nwSettings) - { - Framebuffer = new FramebufferObject(ClientSize); - Renderer = new Renderer(ClientSize.X, ClientSize.Y); - - _gui = new SnimGui(ClientSize.X, ClientSize.Y); - _init = false; - } - - public bool TryLoadExport(CancellationToken cancellationToken, UObject dummy, Lazy export) - { - Renderer.Load(cancellationToken, dummy, export); - return Renderer.Options.Models.Count > 0; - } - - public unsafe void WindowShouldClose(bool value, bool clear) - { - if (clear) - { - Renderer.CameraOp.Speed = 1f; - Renderer.Save(); - } - - GLFW.SetWindowShouldClose(WindowPtr, value); // start / stop game loop - IsVisible = !value; - } - - public unsafe void WindowShouldFreeze(bool value) - { - GLFW.SetWindowShouldClose(WindowPtr, value); // start / stop game loop - IsVisible = true; - } - - public override void Run() - { - Renderer.Options.SwapMaterial(false); - Renderer.Options.AnimateMesh(false); - Application.Current.Dispatcher.Invoke(delegate - { - WindowShouldClose(false, false); - base.Run(); - }); - } - - private unsafe void LoadWindowIcon() - { - var info = Application.GetResourceStream(new Uri("/FModel;component/Resources/engine.png", UriKind.Relative)); - using var img = SixLabors.ImageSharp.Image.Load(info.Stream); - var memoryGroup = img.GetPixelMemoryGroup(); - Memory array = new byte[memoryGroup.TotalLength * sizeof(Rgba32)]; - var block = MemoryMarshal.Cast(array.Span); - foreach (var memory in memoryGroup) - { - memory.Span.CopyTo(block); - block = block[memory.Length..]; - } - - Icon = new WindowIcon(new OpenTK.Windowing.Common.Input.Image(img.Width, img.Height, array.ToArray())); - } - - protected override void OnLoad() - { - if (_init) - { - Renderer.Options.SetupModelsAndLights(); - return; - } - - base.OnLoad(); - CenterWindow(); - LoadWindowIcon(); - - GL.ClearColor(OpenTK.Mathematics.Color4.Black); - GL.Enable(EnableCap.Blend); - GL.Enable(EnableCap.CullFace); - GL.Enable(EnableCap.DepthTest); - GL.Enable(EnableCap.Multisample); - GL.Enable(EnableCap.VertexProgramPointSize); - GL.StencilOp(StencilOp.Keep, StencilOp.Replace, StencilOp.Replace); - GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); - - Framebuffer.Setup(); - Renderer.Setup(); - _init = true; - } - - private void ClearWhatHasBeenDrawn() - { - GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit); - } - - protected override void OnRenderFrame(FrameEventArgs args) - { - base.OnRenderFrame(args); - if (!IsVisible) - return; - - ClearWhatHasBeenDrawn(); // clear window background - Framebuffer.Bind(); // switch to viewport background - ClearWhatHasBeenDrawn(); // clear viewport background - - Renderer.Render(); // render everything - - Framebuffer.BindMsaa(); - Framebuffer.Bind(0); // switch to window background - - _gui.Render(this); // render UI - SwapBuffers(); - } - - protected override void OnUpdateFrame(FrameEventArgs e) - { - base.OnUpdateFrame(e); - if (!IsVisible) - return; - - var delta = (float) e.Time; - - _gui.Controller.Update(this, delta); - Renderer.Update(this, delta); - } - - protected override void OnTextInput(TextInputEventArgs e) - { - base.OnTextInput(e); - if (!IsVisible) - return; - - _gui.Controller.PressChar((char) e.Unicode); - } - - protected override void OnResize(ResizeEventArgs e) - { - base.OnResize(e); - - GL.Viewport(0, 0, e.Width, e.Height); - - Framebuffer.WindowResized(e.Width, e.Height); - Renderer.WindowResized(e.Width, e.Height); - - _gui.Controller.WindowResized(e.Width, e.Height); - } - - protected override void OnClosing(CancelEventArgs e) - { - base.OnClosing(e); - WindowShouldClose(true, true); - } - - [DllImport("user32.dll")] - private static extern bool EnumDisplaySettings( - string deviceName, int modeNum, ref DEVMODE devMode); - - [StructLayout(LayoutKind.Sequential)] - private struct DEVMODE - { - private const int CCHDEVICENAME = 0x20; - private const int CCHFORMNAME = 0x20; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)] - public string dmDeviceName; - public short dmSpecVersion; - public short dmDriverVersion; - public short dmSize; - public short dmDriverExtra; - public int dmFields; - public int dmPositionX; - public int dmPositionY; - public ScreenOrientation dmDisplayOrientation; - public int dmDisplayFixedOutput; - public short dmColor; - public short dmDuplex; - public short dmYResolution; - public short dmTTOption; - public short dmCollate; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)] - public string dmFormName; - public short dmLogPixels; - public int dmBitsPerPel; - public int dmPelsWidth; - public int dmPelsHeight; - public int dmDisplayFlags; - public int dmDisplayFrequency; - public int dmICMMethod; - public int dmICMIntent; - public int dmMediaType; - public int dmDitherType; - public int dmReserved1; - public int dmReserved2; - public int dmPanningWidth; - public int dmPanningHeight; - - } - - public static int GetMaxRefreshFrequency() - { - var rf = 60; - var vDevMode = new DEVMODE(); - var i = 0; - while (EnumDisplaySettings(null, i, ref vDevMode)) - { - i++; - rf = Math.Max(rf, vDevMode.dmDisplayFrequency); - } - - return rf; - } -} diff --git a/FModel/Views/Snooper/Transform.cs b/FModel/Views/Snooper/Transform.cs deleted file mode 100644 index 5f81874f..00000000 --- a/FModel/Views/Snooper/Transform.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System.Numerics; -using CUE4Parse.UE4.Objects.Core.Math; -using ImGuiNET; - -namespace FModel.Views.Snooper; - -public class Transform -{ - public static Transform Identity - { - get => new (); - } - - public Matrix4x4 Relation = Matrix4x4.Identity; - public FVector Position = FVector.ZeroVector; - public FQuat Rotation = FQuat.Identity; - public FVector Scale = FVector.OneVector; - - private Matrix4x4? _saved; - public Matrix4x4 LocalMatrix => Matrix4x4.CreateScale(Scale.X, Scale.Z, Scale.Y) * - Matrix4x4.CreateFromQuaternion(Quaternion.Normalize(new Quaternion(Rotation.X, Rotation.Z, Rotation.Y, -Rotation.W))) * - Matrix4x4.CreateTranslation(Position.X, Position.Z, Position.Y); - public Matrix4x4 Matrix => LocalMatrix * Relation; - - public void Save() - { - _saved = LocalMatrix; - } - - public void ModifyLocal(Matrix4x4 matrix) - { - Matrix4x4.Decompose(matrix, out var scale, out var rotation, out var position); - - Scale.X = scale.X; - Scale.Y = scale.Z; - Scale.Z = scale.Y; - Rotation.X = rotation.X; - Rotation.Y = rotation.Z; - Rotation.Z = rotation.Y; - Rotation.W = -rotation.W; - Position.X = position.X; - Position.Z = position.Y; - Position.Y = position.Z; - } - - public void Reset() - { - if (!_saved.HasValue) return; - ModifyLocal(_saved.Value); - } - - public void ImGuiTransform(float speed) - { - const float width = 100f; - - if (ImGui.TreeNode("Position")) - { - ImGui.SetNextItemWidth(width); - ImGui.DragFloat("X", ref Position.X, speed, 0f, 0f, "%.2f m"); - - ImGui.SetNextItemWidth(width); - ImGui.DragFloat("Y", ref Position.Y, speed, 0f, 0f, "%.2f m"); - - ImGui.SetNextItemWidth(width); - ImGui.DragFloat("Z", ref Position.Z, speed, 0f, 0f, "%.2f m"); - - ImGui.TreePop(); - } - - if (ImGui.TreeNode("Rotation")) - { - ImGui.SetNextItemWidth(width); - ImGui.DragFloat("W", ref Rotation.W, .005f, 0f, 0f, "%.3f rad"); - - ImGui.SetNextItemWidth(width); - ImGui.DragFloat("X", ref Rotation.X, .005f, 0f, 0f, "%.3f rad"); - - ImGui.SetNextItemWidth(width); - ImGui.DragFloat("Y", ref Rotation.Y, .005f, 0f, 0f, "%.3f rad"); - - ImGui.SetNextItemWidth(width); - ImGui.DragFloat("Z", ref Rotation.Z, .005f, 0f, 0f, "%.3f rad"); - - ImGui.TreePop(); - } - - if (ImGui.TreeNode("Scale")) - { - ImGui.SetNextItemWidth(width); - ImGui.DragFloat("X", ref Scale.X, speed, 0f, 0f, "%.3f"); - - ImGui.SetNextItemWidth(width); - ImGui.DragFloat("Y", ref Scale.Y, speed, 0f, 0f, "%.3f"); - - ImGui.SetNextItemWidth(width); - ImGui.DragFloat("Z", ref Scale.Z, speed, 0f, 0f, "%.3f"); - - ImGui.TreePop(); - } - } - - public override string ToString() => Matrix.Translation.ToString(); -} diff --git a/Snooper b/Snooper new file mode 160000 index 00000000..b6ce5045 --- /dev/null +++ b/Snooper @@ -0,0 +1 @@ +Subproject commit b6ce5045131c972a9ab6bd7fd658beb46e306114