diff --git a/FModel/Extensions/ImGuiControllerExtensions.cs b/FModel/Extensions/ImGuiControllerExtensions.cs
new file mode 100644
index 00000000..369ea737
--- /dev/null
+++ b/FModel/Extensions/ImGuiControllerExtensions.cs
@@ -0,0 +1,496 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Numerics;
+using FModel.Framework;
+using ImGuiNET;
+using Silk.NET.Input;
+using Silk.NET.Input.Extensions;
+using Silk.NET.Maths;
+using Silk.NET.OpenGL;
+using Silk.NET.Windowing;
+using Shader = FModel.Views.Snooper.Shader;
+using Texture = FModel.Views.Snooper.Texture;
+
+namespace FModel.Extensions;
+
+///
+/// because we use ImTool for docking purposes we pasted
+/// https://github.com/dotnet/Silk.NET/blob/main/src/OpenGL/Extensions/Silk.NET.OpenGL.Extensions.ImGui/ImGuiController.cs
+///
+public class ImGuiControllerExtensions : IDisposable
+{
+ private GL _gl;
+ private IView _view;
+ private IInputContext _input;
+ private bool _frameBegun;
+ private readonly List _pressedChars = new ();
+ private IKeyboard _keyboard;
+
+ private int _attribLocationTex;
+ private int _attribLocationProjMtx;
+ private int _attribLocationVtxPos;
+ private int _attribLocationVtxUV;
+ private int _attribLocationVtxColor;
+ private uint _vboHandle;
+ private uint _elementsHandle;
+ private uint _vertexArrayObject;
+
+ private Texture _fontTexture;
+ private Shader _shader;
+
+ private int _windowWidth;
+ private int _windowHeight;
+
+ public IntPtr Context;
+
+ public ImGuiControllerExtensions(GL gl, IView view, IInputContext input, ImGuiFontConfig? imGuiFontConfig = null)
+ {
+ Init(gl, view, input);
+
+ var io = ImGui.GetIO();
+ if (imGuiFontConfig is not null)
+ {
+ io.Fonts.AddFontFromFileTTF(imGuiFontConfig.Value.FontPath, imGuiFontConfig.Value.FontSize);
+ }
+
+ io.BackendFlags |= ImGuiBackendFlags.RendererHasVtxOffset;
+
+ CreateDeviceResources();
+ SetKeyMappings();
+
+ SetPerFrameImGuiData(1f / 60f);
+
+ BeginFrame();
+ }
+
+ public void MakeCurrent()
+ {
+ ImGui.SetCurrentContext(Context);
+ }
+
+ private void Init(GL gl, IView view, IInputContext input)
+ {
+ _gl = gl;
+ _view = view;
+ _input = input;
+ _windowWidth = view.Size.X;
+ _windowHeight = view.Size.Y;
+
+ Context = ImGui.CreateContext();
+ ImGui.SetCurrentContext(Context);
+ ImGui.StyleColorsDark();
+ }
+
+ private void BeginFrame()
+ {
+ ImGui.NewFrame();
+ _frameBegun = true;
+ _keyboard = _input.Keyboards[0];
+ _view.Resize += WindowResized;
+ _keyboard.KeyChar += OnKeyChar;
+ }
+
+ private void OnKeyChar(IKeyboard arg1, char arg2)
+ {
+ _pressedChars.Add(arg2);
+ }
+
+ private void WindowResized(Vector2D size)
+ {
+ _windowWidth = size.X;
+ _windowHeight = size.Y;
+ }
+
+ public void Render()
+ {
+ if (!_frameBegun) return;
+
+ var oldCtx = ImGui.GetCurrentContext();
+ if (oldCtx != Context)
+ ImGui.SetCurrentContext(Context);
+
+ _frameBegun = false;
+ ImGui.Render();
+ RenderImDrawData(ImGui.GetDrawData());
+
+ if (oldCtx != Context)
+ ImGui.SetCurrentContext(oldCtx);
+ }
+
+ public void Update(float deltaSeconds)
+ {
+ var oldCtx = ImGui.GetCurrentContext();
+ if (oldCtx != Context)
+ ImGui.SetCurrentContext(Context);
+
+ if (_frameBegun)
+ ImGui.Render();
+
+ SetPerFrameImGuiData(deltaSeconds);
+ UpdateImGuiInput();
+
+ _frameBegun = true;
+ ImGui.NewFrame();
+
+ if (oldCtx != Context)
+ ImGui.SetCurrentContext(oldCtx);
+ }
+
+ private void SetPerFrameImGuiData(float deltaSeconds)
+ {
+ var io = ImGui.GetIO();
+ io.DisplaySize = new Vector2(_windowWidth, _windowHeight);
+
+ if (_windowWidth > 0 && _windowHeight > 0)
+ {
+ io.DisplayFramebufferScale = new Vector2(_view.FramebufferSize.X / _windowWidth,
+ _view.FramebufferSize.Y / _windowHeight);
+ }
+
+ io.DeltaTime = deltaSeconds; // DeltaTime is in seconds.
+ }
+
+ private static Key[] keyEnumArr = (Key[]) Enum.GetValues(typeof(Key));
+ private void UpdateImGuiInput()
+ {
+ var io = ImGui.GetIO();
+
+ var mouseState = _input.Mice[0].CaptureState();
+ var keyboardState = _input.Keyboards[0];
+
+ io.MouseDown[0] = mouseState.IsButtonPressed(MouseButton.Left);
+ io.MouseDown[1] = mouseState.IsButtonPressed(MouseButton.Right);
+ io.MouseDown[2] = mouseState.IsButtonPressed(MouseButton.Middle);
+
+ var point = new Point((int) mouseState.Position.X, (int) mouseState.Position.Y);
+ io.MousePos = new Vector2(point.X, point.Y);
+
+ var wheel = mouseState.GetScrollWheels()[0];
+ io.MouseWheel = wheel.Y;
+ io.MouseWheelH = wheel.X;
+
+ foreach (var key in keyEnumArr)
+ {
+ if (key == Key.Unknown)
+ {
+ continue;
+ }
+ io.KeysDown[(int) key] = keyboardState.IsKeyPressed(key);
+ }
+
+ foreach (var c in _pressedChars)
+ {
+ io.AddInputCharacter(c);
+ }
+
+ _pressedChars.Clear();
+
+ io.KeyCtrl = keyboardState.IsKeyPressed(Key.ControlLeft) || keyboardState.IsKeyPressed(Key.ControlRight);
+ io.KeyAlt = keyboardState.IsKeyPressed(Key.AltLeft) || keyboardState.IsKeyPressed(Key.AltRight);
+ io.KeyShift = keyboardState.IsKeyPressed(Key.ShiftLeft) || keyboardState.IsKeyPressed(Key.ShiftRight);
+ io.KeySuper = keyboardState.IsKeyPressed(Key.SuperLeft) || keyboardState.IsKeyPressed(Key.SuperRight);
+ }
+
+ private void PressChar(char keyChar)
+ {
+ _pressedChars.Add(keyChar);
+ }
+
+ private static void SetKeyMappings()
+ {
+ var io = ImGui.GetIO();
+ io.KeyMap[(int) ImGuiKey.Tab] = (int) Key.Tab;
+ io.KeyMap[(int) ImGuiKey.LeftArrow] = (int) Key.Left;
+ io.KeyMap[(int) ImGuiKey.RightArrow] = (int) Key.Right;
+ io.KeyMap[(int) ImGuiKey.UpArrow] = (int) Key.Up;
+ io.KeyMap[(int) ImGuiKey.DownArrow] = (int) Key.Down;
+ io.KeyMap[(int) ImGuiKey.PageUp] = (int) Key.PageUp;
+ io.KeyMap[(int) ImGuiKey.PageDown] = (int) Key.PageDown;
+ io.KeyMap[(int) ImGuiKey.Home] = (int) Key.Home;
+ io.KeyMap[(int) ImGuiKey.End] = (int) Key.End;
+ io.KeyMap[(int) ImGuiKey.Delete] = (int) Key.Delete;
+ io.KeyMap[(int) ImGuiKey.Backspace] = (int) Key.Backspace;
+ io.KeyMap[(int) ImGuiKey.Enter] = (int) Key.Enter;
+ io.KeyMap[(int) ImGuiKey.Escape] = (int) Key.Escape;
+ io.KeyMap[(int) ImGuiKey.A] = (int) Key.A;
+ io.KeyMap[(int) ImGuiKey.C] = (int) Key.C;
+ io.KeyMap[(int) ImGuiKey.V] = (int) Key.V;
+ io.KeyMap[(int) ImGuiKey.X] = (int) Key.X;
+ io.KeyMap[(int) ImGuiKey.Y] = (int) Key.Y;
+ io.KeyMap[(int) ImGuiKey.Z] = (int) Key.Z;
+ }
+
+ private unsafe void SetupRenderState(ImDrawDataPtr drawDataPtr, int framebufferWidth, int framebufferHeight)
+ {
+ // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
+ _gl.Enable(GLEnum.Blend);
+ _gl.BlendEquation(GLEnum.FuncAdd);
+ _gl.BlendFuncSeparate(GLEnum.SrcAlpha, GLEnum.OneMinusSrcAlpha, GLEnum.One, GLEnum.OneMinusSrcAlpha);
+ _gl.Disable(GLEnum.CullFace);
+ _gl.Disable(GLEnum.DepthTest);
+ _gl.Disable(GLEnum.StencilTest);
+ _gl.Enable(GLEnum.ScissorTest);
+ _gl.Disable(GLEnum.PrimitiveRestart);
+ _gl.PolygonMode(GLEnum.FrontAndBack, GLEnum.Fill);
+
+ float L = drawDataPtr.DisplayPos.X;
+ float R = drawDataPtr.DisplayPos.X + drawDataPtr.DisplaySize.X;
+ float T = drawDataPtr.DisplayPos.Y;
+ float B = drawDataPtr.DisplayPos.Y + drawDataPtr.DisplaySize.Y;
+
+ Span orthoProjection = stackalloc float[] {
+ 2.0f / (R - L), 0.0f, 0.0f, 0.0f,
+ 0.0f, 2.0f / (T - B), 0.0f, 0.0f,
+ 0.0f, 0.0f, -1.0f, 0.0f,
+ (R + L) / (L - R), (T + B) / (B - T), 0.0f, 1.0f,
+ };
+
+ _shader.Use();
+ _gl.Uniform1(_attribLocationTex, 0);
+ _gl.UniformMatrix4(_attribLocationProjMtx, 1, false, orthoProjection);
+
+ _gl.BindSampler(0, 0);
+
+ // Setup desired GL state
+ // Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
+ // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
+ _vertexArrayObject = _gl.GenVertexArray();
+ _gl.BindVertexArray(_vertexArrayObject);
+
+ // Bind vertex/index buffers and setup attributes for ImDrawVert
+ _gl.BindBuffer(GLEnum.ArrayBuffer, _vboHandle);
+ _gl.BindBuffer(GLEnum.ElementArrayBuffer, _elementsHandle);
+ _gl.EnableVertexAttribArray((uint) _attribLocationVtxPos);
+ _gl.EnableVertexAttribArray((uint) _attribLocationVtxUV);
+ _gl.EnableVertexAttribArray((uint) _attribLocationVtxColor);
+ _gl.VertexAttribPointer((uint) _attribLocationVtxPos, 2, GLEnum.Float, false, (uint) sizeof(ImDrawVert), (void*) 0);
+ _gl.VertexAttribPointer((uint) _attribLocationVtxUV, 2, GLEnum.Float, false, (uint) sizeof(ImDrawVert), (void*) 8);
+ _gl.VertexAttribPointer((uint) _attribLocationVtxColor, 4, GLEnum.UnsignedByte, true, (uint) sizeof(ImDrawVert), (void*) 16);
+ }
+
+ private unsafe void RenderImDrawData(ImDrawDataPtr drawDataPtr)
+ {
+ int framebufferWidth = (int) (drawDataPtr.DisplaySize.X * drawDataPtr.FramebufferScale.X);
+ int framebufferHeight = (int) (drawDataPtr.DisplaySize.Y * drawDataPtr.FramebufferScale.Y);
+ if (framebufferWidth <= 0 || framebufferHeight <= 0)
+ return;
+
+ // Backup GL state
+ _gl.GetInteger(GLEnum.ActiveTexture, out int lastActiveTexture);
+ _gl.ActiveTexture(GLEnum.Texture0);
+
+ _gl.GetInteger(GLEnum.CurrentProgram, out int lastProgram);
+ _gl.GetInteger(GLEnum.TextureBinding2D, out int lastTexture);
+
+ _gl.GetInteger(GLEnum.SamplerBinding, out int lastSampler);
+
+ _gl.GetInteger(GLEnum.ArrayBufferBinding, out int lastArrayBuffer);
+ _gl.GetInteger(GLEnum.VertexArrayBinding, out int lastVertexArrayObject);
+
+ Span lastPolygonMode = stackalloc int[2];
+ _gl.GetInteger(GLEnum.PolygonMode, lastPolygonMode);
+
+ Span lastScissorBox = stackalloc int[4];
+ _gl.GetInteger(GLEnum.ScissorBox, lastScissorBox);
+
+ _gl.GetInteger(GLEnum.BlendSrcRgb, out int lastBlendSrcRgb);
+ _gl.GetInteger(GLEnum.BlendDstRgb, out int lastBlendDstRgb);
+
+ _gl.GetInteger(GLEnum.BlendSrcAlpha, out int lastBlendSrcAlpha);
+ _gl.GetInteger(GLEnum.BlendDstAlpha, out int lastBlendDstAlpha);
+
+ _gl.GetInteger(GLEnum.BlendEquationRgb, out int lastBlendEquationRgb);
+ _gl.GetInteger(GLEnum.BlendEquationAlpha, out int lastBlendEquationAlpha);
+
+ bool lastEnableBlend = _gl.IsEnabled(GLEnum.Blend);
+ bool lastEnableCullFace = _gl.IsEnabled(GLEnum.CullFace);
+ bool lastEnableDepthTest = _gl.IsEnabled(GLEnum.DepthTest);
+ bool lastEnableStencilTest = _gl.IsEnabled(GLEnum.StencilTest);
+ bool lastEnableScissorTest = _gl.IsEnabled(GLEnum.ScissorTest);
+ bool lastEnablePrimitiveRestart = _gl.IsEnabled(GLEnum.PrimitiveRestart);
+
+ SetupRenderState(drawDataPtr, framebufferWidth, framebufferHeight);
+
+ // Will project scissor/clipping rectangles into framebuffer space
+ Vector2 clipOff = drawDataPtr.DisplayPos; // (0,0) unless using multi-viewports
+ Vector2 clipScale = drawDataPtr.FramebufferScale; // (1,1) unless using retina display which are often (2,2)
+
+ // Render command lists
+ for (int n = 0; n < drawDataPtr.CmdListsCount; n++)
+ {
+ ImDrawListPtr cmdListPtr = drawDataPtr.CmdListsRange[n];
+
+ // Upload vertex/index buffers
+
+ _gl.BufferData(GLEnum.ArrayBuffer, (nuint) (cmdListPtr.VtxBuffer.Size * sizeof(ImDrawVert)), (void*) cmdListPtr.VtxBuffer.Data, GLEnum.StreamDraw);
+ _gl.BufferData(GLEnum.ElementArrayBuffer, (nuint) (cmdListPtr.IdxBuffer.Size * sizeof(ushort)), (void*) cmdListPtr.IdxBuffer.Data, GLEnum.StreamDraw);
+
+ for (int cmd_i = 0; cmd_i < cmdListPtr.CmdBuffer.Size; cmd_i++)
+ {
+ ImDrawCmdPtr cmdPtr = cmdListPtr.CmdBuffer[cmd_i];
+
+ if (cmdPtr.UserCallback != IntPtr.Zero)
+ throw new NotImplementedException();
+
+ Vector4 clipRect;
+ clipRect.X = (cmdPtr.ClipRect.X - clipOff.X) * clipScale.X;
+ clipRect.Y = (cmdPtr.ClipRect.Y - clipOff.Y) * clipScale.Y;
+ clipRect.Z = (cmdPtr.ClipRect.Z - clipOff.X) * clipScale.X;
+ clipRect.W = (cmdPtr.ClipRect.W - clipOff.Y) * clipScale.Y;
+
+ if (clipRect.X < framebufferWidth && clipRect.Y < framebufferHeight && clipRect.Z >= 0.0f && clipRect.W >= 0.0f)
+ {
+ // Apply scissor/clipping rectangle
+ _gl.Scissor((int) clipRect.X, (int) (framebufferHeight - clipRect.W), (uint) (clipRect.Z - clipRect.X), (uint) (clipRect.W - clipRect.Y));
+
+ // Bind texture, Draw
+ _gl.BindTexture(GLEnum.Texture2D, (uint) cmdPtr.TextureId);
+
+ _gl.DrawElementsBaseVertex(GLEnum.Triangles, cmdPtr.ElemCount, GLEnum.UnsignedShort, (void*) (cmdPtr.IdxOffset * sizeof(ushort)), (int) cmdPtr.VtxOffset);
+ }
+ }
+ }
+
+ // Destroy the temporary VAO
+ _gl.DeleteVertexArray(_vertexArrayObject);
+ _vertexArrayObject = 0;
+
+ // Restore modified GL state
+ _gl.UseProgram((uint) lastProgram);
+ _gl.BindTexture(GLEnum.Texture2D, (uint) lastTexture);
+
+ _gl.BindSampler(0, (uint) lastSampler);
+
+ _gl.ActiveTexture((GLEnum) lastActiveTexture);
+
+ _gl.BindVertexArray((uint) lastVertexArrayObject);
+
+ _gl.BindBuffer(GLEnum.ArrayBuffer, (uint) lastArrayBuffer);
+ _gl.BlendEquationSeparate((GLEnum) lastBlendEquationRgb, (GLEnum) lastBlendEquationAlpha);
+ _gl.BlendFuncSeparate((GLEnum) lastBlendSrcRgb, (GLEnum) lastBlendDstRgb, (GLEnum) lastBlendSrcAlpha, (GLEnum) lastBlendDstAlpha);
+
+ if (lastEnableBlend)
+ {
+ _gl.Enable(GLEnum.Blend);
+ }
+ else
+ {
+ _gl.Disable(GLEnum.Blend);
+ }
+
+ if (lastEnableCullFace)
+ {
+ _gl.Enable(GLEnum.CullFace);
+ }
+ else
+ {
+ _gl.Disable(GLEnum.CullFace);
+ }
+
+ if (lastEnableDepthTest)
+ {
+ _gl.Enable(GLEnum.DepthTest);
+ }
+ else
+ {
+ _gl.Disable(GLEnum.DepthTest);
+ }
+ if (lastEnableStencilTest)
+ {
+ _gl.Enable(GLEnum.StencilTest);
+ }
+ else
+ {
+ _gl.Disable(GLEnum.StencilTest);
+ }
+
+ if (lastEnableScissorTest)
+ {
+ _gl.Enable(GLEnum.ScissorTest);
+ }
+ else
+ {
+ _gl.Disable(GLEnum.ScissorTest);
+ }
+
+ if (lastEnablePrimitiveRestart)
+ {
+ _gl.Enable(GLEnum.PrimitiveRestart);
+ }
+ else
+ {
+ _gl.Disable(GLEnum.PrimitiveRestart);
+ }
+
+ _gl.PolygonMode(GLEnum.FrontAndBack, (GLEnum) lastPolygonMode[0]);
+ _gl.Scissor(lastScissorBox[0], lastScissorBox[1], (uint) lastScissorBox[2], (uint) lastScissorBox[3]);
+ }
+
+ private void CreateDeviceResources()
+ {
+ // Backup GL state
+
+ _gl.GetInteger(GLEnum.TextureBinding2D, out int lastTexture);
+ _gl.GetInteger(GLEnum.ArrayBufferBinding, out int lastArrayBuffer);
+ _gl.GetInteger(GLEnum.VertexArrayBinding, out int lastVertexArray);
+
+ _shader = new Shader(_gl, "imgui");
+
+ _attribLocationTex = _shader.GetUniformLocation("Texture");
+ _attribLocationProjMtx = _shader.GetUniformLocation("ProjMtx");
+ _attribLocationVtxPos = _shader.GetAttribLocation("Position");
+ _attribLocationVtxUV = _shader.GetAttribLocation("UV");
+ _attribLocationVtxColor = _shader.GetAttribLocation("Color");
+
+ _vboHandle = _gl.GenBuffer();
+ _elementsHandle = _gl.GenBuffer();
+
+ RecreateFontDeviceTexture();
+
+ // Restore modified GL state
+ _gl.BindTexture(GLEnum.Texture2D, (uint) lastTexture);
+ _gl.BindBuffer(GLEnum.ArrayBuffer, (uint) lastArrayBuffer);
+
+ _gl.BindVertexArray((uint) lastVertexArray);
+ }
+
+ ///
+ /// Creates the texture used to render text.
+ ///
+ private unsafe void RecreateFontDeviceTexture()
+ {
+ // Build texture atlas
+ var io = ImGui.GetIO();
+ io.Fonts.GetTexDataAsRGBA32(out IntPtr pixels, out int width, out int height, out int bytesPerPixel); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
+
+ // Upload texture to graphics system
+ _gl.GetInteger(GLEnum.TextureBinding2D, out int lastTexture);
+
+ _fontTexture = new Texture(_gl, (uint) width, (uint) height, pixels);
+ _fontTexture.Bind(TextureTarget.Texture2D);
+ _fontTexture.SetMagFilter(TextureMagFilter.Linear);
+ _fontTexture.SetMinFilter(TextureMinFilter.Linear);
+
+ // Store our identifier
+ io.Fonts.SetTexID(_fontTexture.GetPointer());
+
+ // Restore state
+ _gl.BindTexture(GLEnum.Texture2D, (uint) lastTexture);
+ }
+
+ public void Dispose()
+ {
+ _view.Resize -= WindowResized;
+ _keyboard.KeyChar -= OnKeyChar;
+
+ _gl.DeleteBuffer(_vboHandle);
+ _gl.DeleteBuffer(_elementsHandle);
+ _gl.DeleteVertexArray(_vertexArrayObject);
+
+ _fontTexture.Dispose();
+ _shader.Dispose();
+
+ ImGui.DestroyContext(Context);
+ }
+}
diff --git a/FModel/FModel.csproj b/FModel/FModel.csproj
index ce1ddf6b..34b8130f 100644
--- a/FModel/FModel.csproj
+++ b/FModel/FModel.csproj
@@ -101,6 +101,8 @@
+
+
@@ -118,6 +120,8 @@
+
+
@@ -129,6 +133,7 @@
+
@@ -138,8 +143,8 @@
+
-
diff --git a/FModel/Framework/ImGuiFontConfig.cs b/FModel/Framework/ImGuiFontConfig.cs
new file mode 100644
index 00000000..7c862aa3
--- /dev/null
+++ b/FModel/Framework/ImGuiFontConfig.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace FModel.Framework;
+
+public readonly struct ImGuiFontConfig
+{
+ public ImGuiFontConfig(string fontPath, int fontSize)
+ {
+ if (fontSize <= 0) throw new ArgumentOutOfRangeException(nameof(fontSize));
+ FontPath = fontPath ?? throw new ArgumentNullException(nameof(fontPath));
+ FontSize = fontSize;
+ }
+
+ public string FontPath { get; }
+ public int FontSize { get; }
+}
diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs
index 563c3b27..8c311284 100644
--- a/FModel/MainWindow.xaml.cs
+++ b/FModel/MainWindow.xaml.cs
@@ -79,7 +79,7 @@ public partial class MainWindow
#if DEBUG
await _threadWorkerView.Begin(_ =>
_applicationView.CUE4Parse.Extract(
- "FortniteGame/Content/Environments/Props/Winter/Meshes/SM_ChristmasTree_Llama.uasset"));
+ "FortniteGame/Content/Characters/Player/Male/Medium/Bodies/M_MED_StaminaCat/Meshes/M_MED_StaminaCat.uasset"));
#endif
}
diff --git a/FModel/Resources/imgui.frag b/FModel/Resources/imgui.frag
new file mode 100644
index 00000000..5a705b23
--- /dev/null
+++ b/FModel/Resources/imgui.frag
@@ -0,0 +1,13 @@
+#version 330
+
+layout (location = 0) out vec4 Out_Color;
+
+in vec2 Frag_UV;
+in vec4 Frag_Color;
+
+uniform sampler2D Texture;
+
+void main()
+{
+ Out_Color = Frag_Color * texture(Texture, Frag_UV.st);
+}
diff --git a/FModel/Resources/imgui.vert b/FModel/Resources/imgui.vert
new file mode 100644
index 00000000..284ed1a5
--- /dev/null
+++ b/FModel/Resources/imgui.vert
@@ -0,0 +1,17 @@
+#version 330
+
+layout (location = 0) in vec2 Position;
+layout (location = 1) in vec2 UV;
+layout (location = 2) in vec4 Color;
+
+uniform mat4 ProjMtx;
+
+out vec2 Frag_UV;
+out vec4 Frag_Color;
+
+void main()
+{
+ Frag_UV = UV;
+ Frag_Color = Color;
+ gl_Position = ProjMtx * vec4(Position.xy,0,1);
+}
diff --git a/FModel/Views/Snooper/Model.cs b/FModel/Views/Snooper/Model.cs
index 22c939ca..8c432545 100644
--- a/FModel/Views/Snooper/Model.cs
+++ b/FModel/Views/Snooper/Model.cs
@@ -26,7 +26,7 @@ public class Model : IDisposable
public readonly float[] Vertices;
public readonly Section[] Sections;
- public Transform Transforms = Transform.Identity;
+ public readonly Transform Transforms = Transform.Identity;
public readonly string[] TransformsLabels = {
"X Location", "Y", "Z",
"X Rotation", "Y", "Z",
@@ -127,7 +127,7 @@ public class Model : IDisposable
for (int section = 0; section < Sections.Length; section++)
{
- Sections[section].Bind(section, _shader);
+ Sections[section].Bind(_shader);
}
}
diff --git a/FModel/Views/Snooper/Section.cs b/FModel/Views/Snooper/Section.cs
index d1bdd2da..3bc30302 100644
--- a/FModel/Views/Snooper/Section.cs
+++ b/FModel/Views/Snooper/Section.cs
@@ -6,7 +6,6 @@ using CUE4Parse_Conversion.Meshes.PSK;
using CUE4Parse_Conversion.Textures;
using FModel.Services;
using FModel.Settings;
-using ImGuiNET;
using Silk.NET.OpenGL;
namespace FModel.Views.Snooper;
@@ -16,16 +15,6 @@ public class Section : IDisposable
private uint _handle;
private GL _gl;
- private Texture _diffuseMap;
- private Texture _normalMap;
- private Texture _specularMap;
- private Texture _emissionMap;
-
- private bool _hasSpecularMap;
- private bool _hasDiffuseColor;
- private Vector4 _diffuseColor = Vector4.Zero;
- private Vector4 _emissionColor = Vector4.Zero;
-
private Vector3 _ambientLight;
private Vector3 _diffuseLight;
private Vector3 _specularLight;
@@ -38,9 +27,14 @@ public class Section : IDisposable
public readonly int FirstFaceIndex;
public readonly CMaterialParams Parameters;
- private bool _show = true;
- private bool _wireframe;
- private bool _selected;
+ public bool Show;
+ public bool Wireframe;
+ public readonly Texture[] Textures;
+ public readonly string[] TexturesLabels;
+ public Vector4 DiffuseColor;
+ public Vector4 EmissionColor;
+ public bool HasSpecularMap;
+ public bool HasDiffuseColor;
public Section(string name, int index, uint facesCount, int firstFaceIndex, CMeshSection section)
{
@@ -55,6 +49,12 @@ public class Section : IDisposable
unrealMaterial.GetParams(Parameters);
}
+ Show = true;
+ Textures = new Texture[4];
+ TexturesLabels = new[] { "Diffuse", "Normal", "Specular", "Emissive" };
+ DiffuseColor = Vector4.Zero;
+ EmissionColor = Vector4.Zero;
+
_game = ApplicationService.ApplicationView.CUE4Parse.Game;
}
@@ -66,34 +66,34 @@ public class Section : IDisposable
if (Parameters.IsNull)
{
- _diffuseColor = new Vector4(1, 0, 0, 1);
+ DiffuseColor = new Vector4(1, 0, 0, 1);
}
else
{
var platform = UserSettings.Default.OverridedPlatform;
if (!Parameters.HasTopDiffuseTexture && Parameters.DiffuseColor is { A: > 0 } diffuseColor)
{
- _diffuseColor = new Vector4(diffuseColor.R, diffuseColor.G, diffuseColor.B, diffuseColor.A);
+ DiffuseColor = new Vector4(diffuseColor.R, diffuseColor.G, diffuseColor.B, diffuseColor.A);
}
else if (Parameters.Diffuse is UTexture2D { IsVirtual: false } diffuse)
{
var mip = diffuse.GetFirstMip();
TextureDecoder.DecodeTexture(mip, diffuse.Format, diffuse.isNormalMap, platform, out var data, out _);
- _diffuseMap = new Texture(_gl, data, (uint) mip.SizeX, (uint) mip.SizeY);
+ Textures[0] = new Texture(_gl, data, (uint) mip.SizeX, (uint) mip.SizeY);
}
if (Parameters.Normal is UTexture2D { IsVirtual: false } normal)
{
var mip = normal.GetFirstMip();
TextureDecoder.DecodeTexture(mip, normal.Format, normal.isNormalMap, platform, out var data, out _);
- _normalMap = new Texture(_gl, data, (uint) mip.SizeX, (uint) mip.SizeY);
+ Textures[1] = new Texture(_gl, data, (uint) mip.SizeX, (uint) mip.SizeY);
}
if (Parameters.Specular is UTexture2D { IsVirtual: false } specular)
{
var mip = specular.GetFirstMip();
SwapSpecular(specular, mip, platform, out var data);
- _specularMap = new Texture(_gl, data, (uint) mip.SizeX, (uint) mip.SizeY);
+ Textures[2] = new Texture(_gl, data, (uint) mip.SizeX, (uint) mip.SizeY);
}
if (Parameters.HasTopEmissiveTexture &&
@@ -102,18 +102,18 @@ public class Section : IDisposable
{
var mip = emissive.GetFirstMip();
TextureDecoder.DecodeTexture(mip, emissive.Format, emissive.isNormalMap, platform, out var data, out _);
- _emissionMap = new Texture(_gl, data, (uint) mip.SizeX, (uint) mip.SizeY);
- _emissionColor = new Vector4(emissiveColor.R, emissiveColor.G, emissiveColor.B, emissiveColor.A);
+ Textures[3] = new Texture(_gl, data, (uint) mip.SizeX, (uint) mip.SizeY);
+ EmissionColor = new Vector4(emissiveColor.R, emissiveColor.G, emissiveColor.B, emissiveColor.A);
}
}
// diffuse light is based on normal map, so increase ambient if no normal map
- _ambientLight = new Vector3(_normalMap == null ? 1.0f : 0.2f);
+ _ambientLight = new Vector3(Textures[1] == null ? 1.0f : 0.2f);
_diffuseLight = new Vector3(0.75f);
_specularLight = new Vector3(0.5f);
- _hasSpecularMap = _specularMap != null;
- _hasDiffuseColor = _diffuseColor != Vector4.Zero;
- _show = !Parameters.IsNull && !Parameters.IsTransparent;
+ HasSpecularMap = Textures[2] != null;
+ HasDiffuseColor = DiffuseColor != Vector4.Zero;
+ Show = !Parameters.IsNull && !Parameters.IsTransparent;
}
///
@@ -220,49 +220,19 @@ public class Section : IDisposable
}
}
- public void Bind(int index, Shader shader)
+ public void Bind(Shader shader)
{
- // ImGui.TableNextRow();
- //
- // ImGui.TableSetColumnIndex(0);
- // ImGui.Text(Index.ToString());
- // ImGui.TableSetColumnIndex(1);
- // ImGui.Text(Name);
- // if (ImGui.IsItemHovered())
- // {
- // ImGui.BeginTooltip();
- // ImGui.Text($"Faces: {FacesCount} ({Math.Round(FacesCount / indices * 100f, 2)}%%)");
- // ImGui.Text($"First Face: {FirstFaceIndex}");
- // ImGui.Separator();
- // if (_hasDiffuseColor)
- // {
- // ImGui.ColorEdit4("Diffuse Color", ref _diffuseColor, ImGuiColorEditFlags.NoInputs);
- // }
- // else
- // {
- // ImGui.Text($"Diffuse: ({Parameters.Diffuse?.ExportType}) {Parameters.Diffuse?.Name}");
- // ImGui.Text($"Normal: ({Parameters.Normal?.ExportType}) {Parameters.Normal?.Name}");
- // ImGui.Text($"Specular: ({Parameters.Specular?.ExportType}) {Parameters.Specular?.Name}");
- // if (Parameters.HasTopEmissiveTexture)
- // ImGui.Text($"Emissive: ({Parameters.Emissive?.ExportType}) {Parameters.Emissive?.Name}");
- // ImGui.Separator();
- // }
- // ImGui.EndTooltip();
- // }
+ for (var i = 0; i < Textures.Length; i++)
+ {
+ Textures[i]?.Bind(TextureUnit.Texture0 + i);
+ }
- // DrawImGui(index);
+ shader.SetUniform("material.useSpecularMap", HasSpecularMap);
- _diffuseMap?.Bind(TextureUnit.Texture0);
- _normalMap?.Bind(TextureUnit.Texture1);
- _specularMap?.Bind(TextureUnit.Texture2);
- _emissionMap?.Bind(TextureUnit.Texture3);
+ shader.SetUniform("material.hasDiffuseColor", HasDiffuseColor);
+ shader.SetUniform("material.diffuseColor", DiffuseColor);
- shader.SetUniform("material.useSpecularMap", _hasSpecularMap);
-
- shader.SetUniform("material.hasDiffuseColor", _hasDiffuseColor);
- shader.SetUniform("material.diffuseColor", _diffuseColor);
-
- shader.SetUniform("material.emissionColor", _emissionColor);
+ shader.SetUniform("material.emissionColor", EmissionColor);
shader.SetUniform("material.shininess", Parameters.MetallicValue);
@@ -270,27 +240,16 @@ public class Section : IDisposable
shader.SetUniform("light.diffuse", _diffuseLight);
shader.SetUniform("light.specular", _specularLight);
- _gl.PolygonMode(MaterialFace.Front, _wireframe ? PolygonMode.Line : PolygonMode.Fill);
- if (_show) _gl.DrawArrays(PrimitiveType.Triangles, FirstFaceIndex, FacesCount);
+ _gl.PolygonMode(MaterialFace.Front, Wireframe ? PolygonMode.Line : PolygonMode.Fill);
+ if (Show) _gl.DrawArrays(PrimitiveType.Triangles, FirstFaceIndex, FacesCount);
}
public void Dispose()
{
- _diffuseMap?.Dispose();
- _normalMap?.Dispose();
- _specularMap?.Dispose();
- _emissionMap?.Dispose();
+ for (var i = 0; i < Textures.Length; i++)
+ {
+ Textures[i]?.Dispose();
+ }
_gl.DeleteProgram(_handle);
}
-
- private void DrawImGui(int index)
- {
- ImGui.PushID(index);
- ImGui.Selectable(Name, ref _selected);
- if (_selected)
- {
-
- }
- ImGui.PopID();
- }
}
diff --git a/FModel/Views/Snooper/Shader.cs b/FModel/Views/Snooper/Shader.cs
index 89a6623d..e67fb2ef 100644
--- a/FModel/Views/Snooper/Shader.cs
+++ b/FModel/Views/Snooper/Shader.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Numerics;
using System.Reflection;
@@ -8,8 +9,10 @@ namespace FModel.Views.Snooper;
public class Shader : IDisposable
{
- private uint _handle;
- private GL _gl;
+ private readonly uint _handle;
+ private readonly GL _gl;
+ private readonly Dictionary _uniformToLocation = new ();
+ private readonly Dictionary _attribLocation = new ();
public Shader(GL gl) : this(gl, "default") {}
@@ -103,6 +106,34 @@ public class Shader : IDisposable
_gl.Uniform4(location, value.X, value.Y, value.Z, value.W);
}
+ public int GetUniformLocation(string uniform)
+ {
+ if (!_uniformToLocation.TryGetValue(uniform, out int location))
+ {
+ location = _gl.GetUniformLocation(_handle, uniform);
+ _uniformToLocation.Add(uniform, location);
+ if (location == -1)
+ {
+ Serilog.Log.Debug($"The uniform '{uniform}' does not exist in the shader!");
+ }
+ }
+ return location;
+ }
+
+ public int GetAttribLocation(string attrib)
+ {
+ if (!_attribLocation.TryGetValue(attrib, out int location))
+ {
+ location = _gl.GetAttribLocation(_handle, attrib);
+ _attribLocation.Add(attrib, location);
+ if (location == -1)
+ {
+ Serilog.Log.Debug($"The attrib '{attrib}' does not exist in the shader!");
+ }
+ }
+ return location;
+ }
+
public void Dispose()
{
_gl.DeleteProgram(_handle);
diff --git a/FModel/Views/Snooper/SnimGui.cs b/FModel/Views/Snooper/SnimGui.cs
index 21168d02..f8b1a6ec 100644
--- a/FModel/Views/Snooper/SnimGui.cs
+++ b/FModel/Views/Snooper/SnimGui.cs
@@ -1,18 +1,20 @@
using System;
using System.Collections.Generic;
using System.Numerics;
+using FModel.Extensions;
+using FModel.Framework;
using ImGuiNET;
using Silk.NET.Input;
using Silk.NET.Maths;
using Silk.NET.OpenGL;
-using Silk.NET.OpenGL.Extensions.ImGui;
using Silk.NET.Windowing;
namespace FModel.Views.Snooper;
public class SnimGui : IDisposable
{
- private readonly ImGuiController _controller;
+ private readonly ImGuiControllerExtensions _controller;
+ private readonly GraphicsAPI _api;
private readonly Vector2 _outlinerSize;
private readonly Vector2 _outlinerPosition;
@@ -20,29 +22,36 @@ public class SnimGui : IDisposable
private readonly Vector2 _propertiesPosition;
private readonly Vector2 _viewportSize;
private readonly Vector2 _viewportPosition;
+ private readonly Vector2 _textureSize;
+ private readonly Vector2 _texturePosition;
private bool _viewportFocus;
private int _selectedModel;
+ private int _selectedSection;
- private const ImGuiWindowFlags _noResize = ImGuiWindowFlags.NoResize; // delete once we have a proper docking branch
+ private const ImGuiWindowFlags _noResize = ImGuiWindowFlags.NoResize| ImGuiWindowFlags.NoMove; // delete once we have a proper docking branch
private const ImGuiCond _firstUse = ImGuiCond.Appearing; // switch to FirstUseEver once the docking branch will not be useful anymore...
private const uint _dockspaceId = 1337;
public SnimGui(GL gl, IWindow window, IInputContext input)
{
var fontConfig = new ImGuiFontConfig("C:\\Windows\\Fonts\\segoeui.ttf", 16);
- _controller = new ImGuiController(gl, window, input, fontConfig);
+ _controller = new ImGuiControllerExtensions(gl, window, input, fontConfig);
+ _api = window.API;
var style = ImGui.GetStyle();
var viewport = ImGui.GetMainViewport();
var titleBarHeight = ImGui.GetFontSize() + style.FramePadding.Y * 2;
- _outlinerSize = new Vector2(300, 350);
+ _outlinerSize = new Vector2(400, 250);
_outlinerPosition = new Vector2(viewport.WorkSize.X - _outlinerSize.X, titleBarHeight);
_propertiesSize = _outlinerSize with { Y = viewport.WorkSize.Y - _outlinerSize.Y - titleBarHeight };
_propertiesPosition = new Vector2(viewport.WorkSize.X - _propertiesSize.X, _outlinerPosition.Y + _outlinerSize.Y);
- _viewportSize = _outlinerPosition with { Y = viewport.WorkSize.Y - titleBarHeight };
+ _viewportSize = _outlinerPosition with { Y = viewport.WorkSize.Y - titleBarHeight - 150 };
_viewportPosition = new Vector2(0, titleBarHeight);
+ _textureSize = _viewportSize with { Y = viewport.WorkSize.Y - _viewportSize.Y - titleBarHeight };
+ _texturePosition = new Vector2(0, _viewportPosition.Y + _viewportSize.Y);
_selectedModel = 0;
+ _selectedSection = 0;
Theme(style);
}
@@ -54,6 +63,7 @@ public class SnimGui : IDisposable
DrawOuliner(camera, models);
DrawProperties(camera, models);
+ DrawTextures(models);
Draw3DViewport(framebuffer, camera, mouse);
}
@@ -108,12 +118,19 @@ public class SnimGui : IDisposable
ImGui.SetNextWindowPos(_outlinerPosition, _firstUse);
ImGui.Begin("Scene", _noResize | ImGuiWindowFlags.NoCollapse);
+ ImGui.Text($"{_api.API} {_api.Profile} {_api.Version.MajorVersion}.{_api.Version.MinorVersion}");
+
+ var vertices = 0;
+ var indices = 0;
+
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
if (ImGui.TreeNode("Collection"))
{
for (var i = 0; i < models.Count; i++)
{
var model = models[i];
+ vertices += model.Vertices.Length;
+ indices += model.Indices.Length;
if (ImGui.Selectable(model.Name, _selectedModel == i))
_selectedModel = i;
}
@@ -131,8 +148,8 @@ public class SnimGui : IDisposable
ImGui.TreePop();
}
- ImGui.Text($"Position: {_viewportPosition}");
- ImGui.Text($"Size: {_viewportSize}");
+ ImGui.Text($"Vertices: {vertices}");
+ ImGui.Text($"Indices: {indices}");
ImGui.End();
}
@@ -149,7 +166,6 @@ public class SnimGui : IDisposable
ImGui.Checkbox("Vertex Colors", ref model.DisplayVertexColors);
ImGui.EndDisabled();
- ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
if (ImGui.TreeNode("Transform"))
{
const int width = 100;
@@ -199,6 +215,90 @@ public class SnimGui : IDisposable
ImGui.TreePop();
}
+ ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
+ if (ImGui.TreeNode("Materials"))
+ {
+ ImGui.BeginTable("Sections", 2, ImGuiTableFlags.Borders | ImGuiTableFlags.Resizable);
+ ImGui.TableSetupColumn("Index", ImGuiTableColumnFlags.NoHeaderWidth | ImGuiTableColumnFlags.WidthFixed);
+ ImGui.TableSetupColumn("Name");
+ ImGui.TableHeadersRow();
+ for (var i = 0; i < model.Sections.Length; i++)
+ {
+ var section = model.Sections[i];
+
+ ImGui.PushID(i);
+ ImGui.TableNextRow();
+ if (!section.Show)
+ ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, ImGui.GetColorU32(new Vector4(1, 0, 0, .5f)));
+ ImGui.TableSetColumnIndex(0);
+ ImGui.Text(section.Index.ToString("D"));
+ ImGui.TableSetColumnIndex(1);
+ if (ImGui.Selectable(section.Name, _selectedSection == i, ImGuiSelectableFlags.SpanAllColumns))
+ _selectedSection = i;
+ ImGui.PopID();
+ }
+ ImGui.EndTable();
+ ImGui.TreePop();
+ }
+
+ ImGui.End();
+ }
+
+ private void DrawTextures(IList models)
+ {
+ ImGui.SetNextWindowSize(_textureSize, _firstUse);
+ ImGui.SetNextWindowPos(_texturePosition, _firstUse);
+ ImGui.Begin("Textures", _noResize | ImGuiWindowFlags.NoCollapse);
+
+ var section = models[_selectedModel].Sections[_selectedSection];
+ ImGui.BeginGroup();
+ ImGui.Checkbox("Show", ref section.Show);
+ ImGui.Checkbox("Wireframe", ref section.Wireframe);
+ ImGui.Checkbox("3", ref section.Wireframe);
+ ImGui.Checkbox("4", ref section.Wireframe);
+ ImGui.EndGroup();
+ ImGui.SameLine();
+ ImGui.BeginGroup();
+ if (section.HasDiffuseColor)
+ {
+ ImGui.ColorEdit4(section.TexturesLabels[0], ref section.DiffuseColor, ImGuiColorEditFlags.AlphaPreview | ImGuiColorEditFlags.AlphaBar);
+ }
+ else
+ {
+ for (var i = 0; i < section.Textures.Length; i++)
+ {
+ if (section.Textures[i] is not {} texture)
+ continue;
+
+ ImGui.SameLine();
+ ImGui.BeginGroup();
+ ImGui.Image(texture.GetPointer(), new Vector2(88), Vector2.Zero, Vector2.One, Vector4.One, new Vector4(1, 1, 1, .5f));
+ var text = section.TexturesLabels[i];
+ var width = ImGui.GetCursorPos().X;
+ ImGui.SetCursorPosX(width + ImGui.CalcTextSize(text).X * 0.5f);
+ ImGui.Text(text);
+ ImGui.EndGroup();
+ }
+ }
+ ImGui.EndGroup();
+
+ // ImGui.Text($"Faces: {FacesCount} ({Math.Round(FacesCount / indices * 100f, 2)}%%)");
+ // ImGui.Text($"First Face: {FirstFaceIndex}");
+ // ImGui.Separator();
+ // if (_hasDiffuseColor)
+ // {
+ // ImGui.ColorEdit4("Diffuse Color", ref _diffuseColor, ImGuiColorEditFlags.NoInputs);
+ // }
+ // else
+ // {
+ // ImGui.Text($"Diffuse: ({Parameters.Diffuse?.ExportType}) {Parameters.Diffuse?.Name}");
+ // ImGui.Text($"Normal: ({Parameters.Normal?.ExportType}) {Parameters.Normal?.Name}");
+ // ImGui.Text($"Specular: ({Parameters.Specular?.ExportType}) {Parameters.Specular?.Name}");
+ // if (Parameters.HasTopEmissiveTexture)
+ // ImGui.Text($"Emissive: ({Parameters.Emissive?.ExportType}) {Parameters.Emissive?.Name}");
+ // ImGui.Separator();
+ // }
+
ImGui.End();
}
@@ -278,7 +378,7 @@ public class SnimGui : IDisposable
io.ConfigFlags |= ImGuiConfigFlags.DockingEnable;
io.ConfigFlags |= ImGuiConfigFlags.ViewportsEnable;
io.ConfigWindowsMoveFromTitleBarOnly = true;
- io.ConfigDockingWithShift = true;
+ // io.ConfigDockingWithShift = true;
// style.WindowPadding = Vector2.Zero;
// style.Colors[(int) ImGuiCol.Text] = new Vector4(0.95f, 0.96f, 0.98f, 1.00f);
diff --git a/FModel/Views/Snooper/Texture.cs b/FModel/Views/Snooper/Texture.cs
index 1e7840c6..f7915557 100644
--- a/FModel/Views/Snooper/Texture.cs
+++ b/FModel/Views/Snooper/Texture.cs
@@ -8,22 +8,21 @@ namespace FModel.Views.Snooper;
public class Texture : IDisposable
{
- private uint _handle;
- private GL _gl;
-
- private TextureType _type;
+ private readonly uint _handle;
+ private readonly GL _gl;
+ private readonly TextureType _type;
public Texture(GL gl, TextureType type)
{
_gl = gl;
_handle = _gl.GenTexture();
_type = type;
-
- Bind(TextureUnit.Texture0);
}
public Texture(GL gl, uint width, uint height) : this(gl, TextureType.MsaaFramebuffer)
{
+ Bind(TextureUnit.Texture0);
+
_gl.TexImage2DMultisample(TextureTarget.Texture2DMultisample, Constants.SAMPLES_COUNT, InternalFormat.Rgb, width, height, Silk.NET.OpenGL.Boolean.True);
_gl.TexParameter(TextureTarget.Texture2DMultisample, TextureParameterName.TextureMinFilter, (int) GLEnum.Nearest);
@@ -36,6 +35,8 @@ public class Texture : IDisposable
public unsafe Texture(GL gl, int width, int height) : this(gl, TextureType.Framebuffer)
{
+ Bind(TextureUnit.Texture0);
+
_gl.TexImage2D(TextureTarget.Texture2D, 0, (int) InternalFormat.Rgb, (uint) width, (uint) height, 0, PixelFormat.Rgb, PixelType.UnsignedByte, null);
_gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int) GLEnum.Linear);
@@ -48,6 +49,8 @@ public class Texture : IDisposable
public unsafe Texture(GL gl, byte[] data, uint width, uint height) : this(gl, TextureType.Normal)
{
+ Bind(TextureUnit.Texture0);
+
fixed (void* d = &data[0])
{
_gl.TexImage2D(TextureTarget.Texture2D, 0, (int) InternalFormat.Rgba, width, height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, d);
@@ -63,6 +66,8 @@ public class Texture : IDisposable
public unsafe Texture(GL gl, string[] textures) : this(gl, TextureType.Cubemap)
{
+ Bind(TextureUnit.Texture0);
+
for (int t = 0; t < textures.Length; t++)
{
var info = Application.GetResourceStream(new Uri($"/FModel;component/Resources/{textures[t]}.png", UriKind.Relative));
@@ -88,18 +93,45 @@ public class Texture : IDisposable
_gl.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureWrapT, (int) GLEnum.ClampToEdge);
}
+ public unsafe Texture(GL gl, uint width, uint height, IntPtr data) : this(gl, TextureType.Normal)
+ {
+ Bind(TextureTarget.Texture2D);
+
+ _gl.TexStorage2D(GLEnum.Texture2D, 1, SizedInternalFormat.Rgba8, width, height);
+ _gl.TexSubImage2D(GLEnum.Texture2D, 0, 0, 0, width, height, PixelFormat.Bgra, PixelType.UnsignedByte, (void*) data);
+
+ _gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
+ _gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
+
+ _gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureMaxLevel, 1 - 1);
+ }
+
public void Bind(TextureUnit textureSlot)
{
_gl.ActiveTexture(textureSlot);
- var target = _type switch
+ Bind(_type switch
{
TextureType.Cubemap => TextureTarget.TextureCubeMap,
TextureType.MsaaFramebuffer => TextureTarget.Texture2DMultisample,
_ => TextureTarget.Texture2D
- };
+ });
+ }
+
+ public void Bind(TextureTarget target)
+ {
_gl.BindTexture(target, _handle);
}
+ public void SetMinFilter(TextureMinFilter filter)
+ {
+ _gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureMinFilter, (int)filter);
+ }
+
+ public void SetMagFilter(TextureMagFilter filter)
+ {
+ _gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureMagFilter, (int)filter);
+ }
+
public IntPtr GetPointer() => (IntPtr) _handle;
public void Dispose()