Compare commits
12 Commits
16b04ea22a
...
7a81026ca5
Author | SHA1 | Date | |
---|---|---|---|
7a81026ca5 | |||
60b85960ff | |||
b003ffbaec | |||
777059489c | |||
4b824f3205 | |||
9890026656 | |||
d8b41b0827 | |||
42b978e8c9 | |||
2c839d8fad | |||
1eb899b240 | |||
7cbb745721 | |||
1e1ed303ad |
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -13,3 +13,6 @@
|
||||
[submodule "Nerfed.Runtime/Libraries/dav1dfile"]
|
||||
path = Nerfed.Runtime/Libraries/dav1dfile
|
||||
url = https://github.com/MoonsideGames/dav1dfile.git
|
||||
[submodule "Nerfed.Runtime/Libraries/ImGui.NET"]
|
||||
path = Nerfed.Runtime/Libraries/ImGui.NET
|
||||
url = https://github.com/ImGuiNET/ImGui.NET.git
|
||||
|
9
LICENSE
Normal file
9
LICENSE
Normal file
@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Nerfed Engine
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
BIN
Native/x64/cimgui.dll
(Stored with Git LFS)
Normal file
BIN
Native/x64/cimgui.dll
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -1,30 +1,71 @@
|
||||
namespace Nerfed.Editor.Editor
|
||||
using ImGuiNET;
|
||||
using Nerfed.Runtime;
|
||||
using Nerfed.Runtime.Graphics;
|
||||
using Nerfed.Runtime.Gui;
|
||||
|
||||
namespace Nerfed.Editor
|
||||
{
|
||||
internal static class EditorGui
|
||||
{
|
||||
private static GuiController guiController;
|
||||
|
||||
internal static void Initialize()
|
||||
{
|
||||
// Create GuiController.
|
||||
|
||||
guiController = new GuiController(Engine.GraphicsDevice, Engine.MainWindow, Color.DimGray);
|
||||
// Subscribe to GUI update.
|
||||
// GuiController.OnGui call => UpdateDock;
|
||||
// GuiController.OnGui call => UpdateEditorWindows;
|
||||
// GuiController.OnGui call => ...;
|
||||
guiController.OnGui += HandleOnGui;
|
||||
}
|
||||
|
||||
internal static void Update()
|
||||
{
|
||||
// Update GuiController.
|
||||
guiController.Update(Engine.Timestep.TotalSeconds);
|
||||
}
|
||||
|
||||
internal static void Render()
|
||||
{
|
||||
// Reneder GuiController.
|
||||
guiController.Render();
|
||||
}
|
||||
|
||||
internal static void Quit()
|
||||
{
|
||||
guiController.Dispose();
|
||||
}
|
||||
|
||||
private static void UpdateDock()
|
||||
{
|
||||
// Setup default dockspace for the main window.
|
||||
uint id = ImGui.GetID("MainDockSpace");
|
||||
ImGui.DockSpaceOverViewport(id, ImGui.GetMainViewport(), ImGuiDockNodeFlags.None);
|
||||
}
|
||||
|
||||
private static void UpdateMainMenu()
|
||||
{
|
||||
if (ImGui.BeginMainMenuBar())
|
||||
{
|
||||
if (ImGui.BeginMenu("File"))
|
||||
{
|
||||
if (ImGui.MenuItem("Exit"))
|
||||
{
|
||||
Engine.Quit();
|
||||
}
|
||||
ImGui.EndMenu();
|
||||
}
|
||||
ImGui.EndMainMenuBar();
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleOnGui()
|
||||
{
|
||||
UpdateMainMenu();
|
||||
UpdateDock();
|
||||
|
||||
ImGui.ShowDemoWindow();
|
||||
}
|
||||
}
|
||||
}
|
@ -18,21 +18,25 @@ private static void HandleOnInitialize()
|
||||
{
|
||||
// Open project.
|
||||
// Setip EditorGui.
|
||||
EditorGui.Initialize();
|
||||
}
|
||||
|
||||
private static void HandleOnUpdate()
|
||||
{
|
||||
// Editor Update.
|
||||
EditorGui.Update();
|
||||
|
||||
|
||||
// Try Catch UserCode Update.
|
||||
}
|
||||
|
||||
private static void HandleOnRender()
|
||||
{
|
||||
// Editor GUI Render.
|
||||
EditorGui.Render();
|
||||
}
|
||||
|
||||
private static void HandleOnQuit()
|
||||
{
|
||||
EditorGui.Quit();
|
||||
}
|
||||
}
|
2
Nerfed.Runtime/Content/Shaders/generate-spirv.bat
Normal file
2
Nerfed.Runtime/Content/Shaders/generate-spirv.bat
Normal file
@ -0,0 +1,2 @@
|
||||
glslangvalidator -V imgui-vertex.glsl -o imgui-vertex.spv -S vert
|
||||
glslangvalidator -V imgui-frag.glsl -o imgui-frag.spv -S frag
|
13
Nerfed.Runtime/Content/Shaders/imgui-frag.glsl
Normal file
13
Nerfed.Runtime/Content/Shaders/imgui-frag.glsl
Normal file
@ -0,0 +1,13 @@
|
||||
#version 450
|
||||
|
||||
layout (location = 0) in vec4 color;
|
||||
layout (location = 1) in vec2 texCoord;
|
||||
|
||||
layout(set = 2, binding = 0) uniform sampler2D Sampler;
|
||||
|
||||
layout (location = 0) out vec4 outputColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
outputColor = color * texture(Sampler, texCoord);
|
||||
}
|
BIN
Nerfed.Runtime/Content/Shaders/imgui-frag.spv
Normal file
BIN
Nerfed.Runtime/Content/Shaders/imgui-frag.spv
Normal file
Binary file not shown.
20
Nerfed.Runtime/Content/Shaders/imgui-vertex.glsl
Normal file
20
Nerfed.Runtime/Content/Shaders/imgui-vertex.glsl
Normal file
@ -0,0 +1,20 @@
|
||||
#version 450
|
||||
|
||||
layout (location = 0) in vec2 in_position;
|
||||
layout (location = 1) in vec2 in_texCoord;
|
||||
layout (location = 2) in vec4 in_color;
|
||||
|
||||
layout (set = 1, binding = 0) uniform ProjectionMatrixBuffer
|
||||
{
|
||||
mat4 projection_matrix;
|
||||
};
|
||||
|
||||
layout (location = 0) out vec4 color;
|
||||
layout (location = 1) out vec2 texCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = projection_matrix * vec4(in_position, 0, 1);
|
||||
color = in_color;
|
||||
texCoord = in_texCoord;
|
||||
}
|
BIN
Nerfed.Runtime/Content/Shaders/imgui-vertex.spv
Normal file
BIN
Nerfed.Runtime/Content/Shaders/imgui-vertex.spv
Normal file
Binary file not shown.
50
Nerfed.Runtime/Gui/GuiClipboard.cs
Normal file
50
Nerfed.Runtime/Gui/GuiClipboard.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Nerfed.Runtime.Gui;
|
||||
|
||||
public static unsafe class GuiClipboard
|
||||
{
|
||||
private static IntPtr clipboard;
|
||||
private static readonly Dictionary<Delegate, IntPtr> pinned = new Dictionary<Delegate, IntPtr>();
|
||||
|
||||
public static readonly IntPtr GetFnPtr = GetPointerTo(Get);
|
||||
public static readonly IntPtr SetFnPtr = GetPointerTo(Set);
|
||||
|
||||
private static unsafe void Set(void* userdata, byte* text)
|
||||
{
|
||||
int len = 0; while (text[len] != 0) len++;
|
||||
string str = Encoding.UTF8.GetString(text, len);
|
||||
SDL2.SDL.SDL_SetClipboardText(str);
|
||||
}
|
||||
|
||||
private static unsafe byte* Get(void* userdata)
|
||||
{
|
||||
if (clipboard != IntPtr.Zero)
|
||||
{
|
||||
NativeMemory.Free((void*) clipboard);
|
||||
clipboard = IntPtr.Zero;
|
||||
}
|
||||
|
||||
string str = SDL2.SDL.SDL_GetClipboardText();
|
||||
int length = Encoding.UTF8.GetByteCount(str);
|
||||
byte* bytes = (byte*)(clipboard = (nint)NativeMemory.Alloc((nuint)(length + 1)));
|
||||
|
||||
Encoding.UTF8.GetBytes(str, new Span<byte>(bytes, length));
|
||||
bytes[length] = 0;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// Stops the delegate pointer from being collected
|
||||
private static IntPtr GetPointerTo<T>(T fn) where T : Delegate
|
||||
{
|
||||
if (pinned.TryGetValue(fn, out nint ptr))
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
|
||||
ptr = Marshal.GetFunctionPointerForDelegate(fn);
|
||||
pinned.Add(fn, ptr);
|
||||
return ptr;
|
||||
}
|
||||
}
|
651
Nerfed.Runtime/Gui/GuiController.cs
Normal file
651
Nerfed.Runtime/Gui/GuiController.cs
Normal file
@ -0,0 +1,651 @@
|
||||
// ImGuiController with docking and viewport support for MoonWorks/Refresh.
|
||||
// Based on the example im ImGui.NET and MoonWorksDearImGuiScaffold.
|
||||
|
||||
using ImGuiNET;
|
||||
using Nerfed.Runtime.Graphics;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Nerfed.Runtime.Gui;
|
||||
|
||||
public class GuiController : IDisposable
|
||||
{
|
||||
public event Action OnGui;
|
||||
|
||||
private readonly string shaderContentPath = Path.Combine(System.AppContext.BaseDirectory, "Content", "Shaders");
|
||||
|
||||
private readonly GraphicsDevice graphicsDevice;
|
||||
private readonly Window mainWindow;
|
||||
private readonly Color clearColor;
|
||||
|
||||
private readonly Platform_CreateWindow createWindow;
|
||||
private readonly Platform_DestroyWindow destroyWindow;
|
||||
private readonly Platform_GetWindowPos getWindowPos;
|
||||
private readonly Platform_ShowWindow showWindow;
|
||||
private readonly Platform_SetWindowPos setWindowPos;
|
||||
private readonly Platform_SetWindowSize setWindowSize;
|
||||
private readonly Platform_GetWindowSize getWindowSize;
|
||||
private readonly Platform_SetWindowFocus setWindowFocus;
|
||||
private readonly Platform_GetWindowFocus getWindowFocus;
|
||||
private readonly Platform_GetWindowMinimized getWindowMinimized;
|
||||
private readonly Platform_SetWindowTitle setWindowTitle;
|
||||
|
||||
private readonly ResourceUploader resourceUploader;
|
||||
private readonly GraphicsPipeline imGuiPipeline;
|
||||
private readonly Shader imGuiVertexShader;
|
||||
private readonly Shader imGuiFragmentShader;
|
||||
private readonly Sampler imGuiSampler;
|
||||
private readonly GuiTextureStorage textureStorage = new GuiTextureStorage();
|
||||
private readonly GuiViewportWindow mainViewportWindow;
|
||||
|
||||
private Texture fontTexture = null;
|
||||
private uint vertexCount = 0;
|
||||
private uint indexCount = 0;
|
||||
private Graphics.Buffer imGuiVertexBuffer = null;
|
||||
private Graphics.Buffer imGuiIndexBuffer = null;
|
||||
private bool frameBegun = false;
|
||||
|
||||
public GuiController(GraphicsDevice graphicsDevice, Window mainWindow, Color clearColor, ImGuiConfigFlags configFlags = ImGuiConfigFlags.NavEnableKeyboard | ImGuiConfigFlags.DockingEnable | ImGuiConfigFlags.ViewportsEnable)
|
||||
{
|
||||
this.graphicsDevice = graphicsDevice;
|
||||
this.mainWindow = mainWindow;
|
||||
this.clearColor = clearColor;
|
||||
|
||||
resourceUploader = new ResourceUploader(graphicsDevice);
|
||||
|
||||
ImGui.CreateContext();
|
||||
|
||||
ImGuiIOPtr io = ImGui.GetIO();
|
||||
io.DisplaySize = new Vector2(mainWindow.Width, mainWindow.Height);
|
||||
io.DisplayFramebufferScale = Vector2.One;
|
||||
|
||||
ShaderCreateInfo vertexCreateInfo = new ShaderCreateInfo {
|
||||
ShaderStage = ShaderStage.Vertex,
|
||||
ShaderFormat = ShaderFormat.SPIRV,
|
||||
UniformBufferCount = 1,
|
||||
};
|
||||
imGuiVertexShader = new Shader(graphicsDevice, Path.Combine(shaderContentPath, "imgui-vertex.spv"), "main", in vertexCreateInfo);
|
||||
|
||||
ShaderCreateInfo fragCreateInfo = new ShaderCreateInfo {
|
||||
ShaderStage = ShaderStage.Fragment,
|
||||
ShaderFormat = ShaderFormat.SPIRV,
|
||||
SamplerCount = 1,
|
||||
|
||||
};
|
||||
imGuiFragmentShader = new Shader(graphicsDevice, Path.Combine(shaderContentPath, "imgui-frag.spv"), "main", in fragCreateInfo);
|
||||
|
||||
imGuiSampler = new Sampler(graphicsDevice, SamplerCreateInfo.LinearClamp);
|
||||
|
||||
imGuiPipeline = new GraphicsPipeline(
|
||||
graphicsDevice,
|
||||
new GraphicsPipelineCreateInfo
|
||||
{
|
||||
AttachmentInfo = new GraphicsPipelineAttachmentInfo(
|
||||
new ColorAttachmentDescription(
|
||||
mainWindow.SwapchainFormat,
|
||||
ColorAttachmentBlendState.NonPremultiplied
|
||||
)
|
||||
),
|
||||
DepthStencilState = DepthStencilState.Disable,
|
||||
MultisampleState = MultisampleState.None,
|
||||
PrimitiveType = PrimitiveType.TriangleList,
|
||||
RasterizerState = RasterizerState.CW_CullNone,
|
||||
VertexInputState = VertexInputState.CreateSingleBinding<Position2DTextureColorVertex>(),
|
||||
VertexShader = imGuiVertexShader,
|
||||
FragmentShader = imGuiFragmentShader,
|
||||
}
|
||||
);
|
||||
|
||||
BuildFontAtlas();
|
||||
|
||||
io.ConfigFlags = configFlags;
|
||||
|
||||
if (!OperatingSystem.IsWindows())
|
||||
{
|
||||
io.SetClipboardTextFn = GuiClipboard.SetFnPtr;
|
||||
io.GetClipboardTextFn = GuiClipboard.GetFnPtr;
|
||||
}
|
||||
|
||||
ImGuiPlatformIOPtr platformIO = ImGui.GetPlatformIO();
|
||||
ImGuiViewportPtr mainViewport = platformIO.Viewports[0];
|
||||
mainViewport.PlatformHandle = mainWindow.Handle;
|
||||
mainViewportWindow = new GuiViewportWindow(graphicsDevice, mainViewport, mainWindow);
|
||||
|
||||
unsafe
|
||||
{
|
||||
createWindow = CreateWindow;
|
||||
destroyWindow = DestroyWindow;
|
||||
getWindowPos = GetWindowPos;
|
||||
showWindow = ShowWindow;
|
||||
setWindowPos = SetWindowPos;
|
||||
setWindowSize = SetWindowSize;
|
||||
getWindowSize = GetWindowSize;
|
||||
setWindowFocus = SetWindowFocus;
|
||||
getWindowFocus = GetWindowFocus;
|
||||
getWindowMinimized = GetWindowMinimized;
|
||||
setWindowTitle = SetWindowTitle;
|
||||
|
||||
platformIO.Platform_CreateWindow = Marshal.GetFunctionPointerForDelegate(createWindow);
|
||||
platformIO.Platform_DestroyWindow = Marshal.GetFunctionPointerForDelegate(destroyWindow);
|
||||
platformIO.Platform_ShowWindow = Marshal.GetFunctionPointerForDelegate(showWindow);
|
||||
platformIO.Platform_SetWindowPos = Marshal.GetFunctionPointerForDelegate(setWindowPos);
|
||||
platformIO.Platform_SetWindowSize = Marshal.GetFunctionPointerForDelegate(setWindowSize);
|
||||
platformIO.Platform_SetWindowFocus = Marshal.GetFunctionPointerForDelegate(setWindowFocus);
|
||||
platformIO.Platform_GetWindowFocus = Marshal.GetFunctionPointerForDelegate(getWindowFocus);
|
||||
platformIO.Platform_GetWindowMinimized = Marshal.GetFunctionPointerForDelegate(getWindowMinimized);
|
||||
platformIO.Platform_SetWindowTitle = Marshal.GetFunctionPointerForDelegate(setWindowTitle);
|
||||
|
||||
ImGuiNative.ImGuiPlatformIO_Set_Platform_GetWindowPos(platformIO.NativePtr, Marshal.GetFunctionPointerForDelegate(getWindowPos));
|
||||
ImGuiNative.ImGuiPlatformIO_Set_Platform_GetWindowSize(platformIO.NativePtr, Marshal.GetFunctionPointerForDelegate(getWindowSize));
|
||||
}
|
||||
|
||||
io.BackendFlags |= ImGuiBackendFlags.HasMouseCursors;
|
||||
io.BackendFlags |= ImGuiBackendFlags.HasSetMousePos;
|
||||
io.BackendFlags |= ImGuiBackendFlags.PlatformHasViewports;
|
||||
io.BackendFlags |= ImGuiBackendFlags.RendererHasViewports;
|
||||
|
||||
UpdatePerFrameImGuiData(1.0 / 60.0);
|
||||
ImGui.NewFrame();
|
||||
frameBegun = true;
|
||||
}
|
||||
|
||||
public void Update(double deltaTime)
|
||||
{
|
||||
if (frameBegun)
|
||||
{
|
||||
ImGui.Render();
|
||||
ImGui.UpdatePlatformWindows();
|
||||
}
|
||||
|
||||
UpdatePerFrameImGuiData(deltaTime);
|
||||
UpdateInput();
|
||||
UpdateCursor();
|
||||
UpdateMonitors();
|
||||
|
||||
frameBegun = true;
|
||||
ImGui.NewFrame();
|
||||
|
||||
OnGui?.Invoke();
|
||||
|
||||
ImGui.EndFrame();
|
||||
}
|
||||
|
||||
private void UpdatePerFrameImGuiData(double deltaSeconds)
|
||||
{
|
||||
ImGuiIOPtr io = ImGui.GetIO();
|
||||
io.DisplaySize = new Vector2(mainWindow.Width, mainWindow.Height);
|
||||
io.DisplayFramebufferScale = new Vector2(1, 1);
|
||||
io.DeltaTime = (float)deltaSeconds; // DeltaTime is in seconds.
|
||||
}
|
||||
|
||||
private void UpdateInput()
|
||||
{
|
||||
ImGuiIOPtr io = ImGui.GetIO();
|
||||
|
||||
if ((ImGui.GetIO().ConfigFlags & ImGuiConfigFlags.ViewportsEnable) != 0)
|
||||
{
|
||||
// For viewports we use the global mouse position.
|
||||
_ = SDL2.SDL.SDL_GetGlobalMouseState(out int x, out int y);
|
||||
io.MousePos = new Vector2(x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Without viewports we need to use the relative position.
|
||||
//_ = SDL2.SDL.SDL_GetMouseState(out int x, out int y);
|
||||
io.MousePos = Mouse.Position;
|
||||
}
|
||||
|
||||
io.MouseDown[0] = Mouse.IsButtonDown(MouseButton.Left);
|
||||
io.MouseDown[1] = Mouse.IsButtonDown(MouseButton.Right);
|
||||
io.MouseDown[2] = Mouse.IsButtonDown(MouseButton.Middle);
|
||||
|
||||
io.MouseWheel = Mouse.GetWheel();
|
||||
|
||||
io.AddKeyEvent(ImGuiKey.A, Keyboard.IsKeyDown(Key.A));
|
||||
io.AddKeyEvent(ImGuiKey.Z, Keyboard.IsKeyDown(Key.Z));
|
||||
io.AddKeyEvent(ImGuiKey.Y, Keyboard.IsKeyDown(Key.Y));
|
||||
io.AddKeyEvent(ImGuiKey.X, Keyboard.IsKeyDown(Key.X));
|
||||
io.AddKeyEvent(ImGuiKey.C, Keyboard.IsKeyDown(Key.C));
|
||||
io.AddKeyEvent(ImGuiKey.V, Keyboard.IsKeyDown(Key.V));
|
||||
|
||||
io.AddKeyEvent(ImGuiKey.Tab, Keyboard.IsKeyDown(Key.Tab));
|
||||
io.AddKeyEvent(ImGuiKey.LeftArrow, Keyboard.IsKeyDown(Key.Left));
|
||||
io.AddKeyEvent(ImGuiKey.RightArrow, Keyboard.IsKeyDown(Key.Right));
|
||||
io.AddKeyEvent(ImGuiKey.UpArrow, Keyboard.IsKeyDown(Key.Up));
|
||||
io.AddKeyEvent(ImGuiKey.DownArrow, Keyboard.IsKeyDown(Key.Down));
|
||||
io.AddKeyEvent(ImGuiKey.Enter, Keyboard.IsKeyDown(Key.Enter));
|
||||
io.AddKeyEvent(ImGuiKey.Escape, Keyboard.IsKeyDown(Key.Escape));
|
||||
io.AddKeyEvent(ImGuiKey.Delete, Keyboard.IsKeyDown(Key.Delete));
|
||||
io.AddKeyEvent(ImGuiKey.Backspace, Keyboard.IsKeyDown(Key.Backspace));
|
||||
io.AddKeyEvent(ImGuiKey.Home, Keyboard.IsKeyDown(Key.Home));
|
||||
io.AddKeyEvent(ImGuiKey.End, Keyboard.IsKeyDown(Key.End));
|
||||
io.AddKeyEvent(ImGuiKey.PageDown, Keyboard.IsKeyDown(Key.PageDown));
|
||||
io.AddKeyEvent(ImGuiKey.PageUp, Keyboard.IsKeyDown(Key.PageUp));
|
||||
io.AddKeyEvent(ImGuiKey.Insert, Keyboard.IsKeyDown(Key.Insert));
|
||||
|
||||
io.AddKeyEvent(ImGuiKey.ModCtrl, Keyboard.IsKeyDown(Key.LeftControl) || Keyboard.IsKeyDown(Key.RightControl));
|
||||
io.AddKeyEvent(ImGuiKey.ModShift, Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift));
|
||||
io.AddKeyEvent(ImGuiKey.ModAlt, Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt));
|
||||
io.AddKeyEvent(ImGuiKey.ModSuper, Keyboard.IsKeyDown(Key.LeftSuper) || Keyboard.IsKeyDown(Key.RightSuper));
|
||||
|
||||
ReadOnlySpan<char> input = Keyboard.GetTextInput();
|
||||
if (!input.IsEmpty)
|
||||
{
|
||||
foreach (char c in input)
|
||||
{
|
||||
if (c == '\t')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
io.AddInputCharacter(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCursor()
|
||||
{
|
||||
ImGuiIOPtr io = ImGui.GetIO();
|
||||
|
||||
if ((io.ConfigFlags & ImGuiConfigFlags.NoMouseCursorChange) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ImGuiMouseCursor imGuiCursor = ImGui.GetMouseCursor();
|
||||
|
||||
if (imGuiCursor == ImGuiMouseCursor.None || io.MouseDrawCursor)
|
||||
{
|
||||
_ = SDL2.SDL.SDL_ShowCursor(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
nint sdlCursor = imGuiCursor switch
|
||||
{
|
||||
ImGuiMouseCursor.Arrow => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_ARROW),
|
||||
ImGuiMouseCursor.TextInput => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_IBEAM),
|
||||
ImGuiMouseCursor.ResizeAll => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZEALL),
|
||||
ImGuiMouseCursor.ResizeNS => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENS),
|
||||
ImGuiMouseCursor.ResizeEW => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZEWE),
|
||||
ImGuiMouseCursor.ResizeNESW => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENESW),
|
||||
ImGuiMouseCursor.ResizeNWSE => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENWSE),
|
||||
ImGuiMouseCursor.Hand => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_HAND),
|
||||
ImGuiMouseCursor.NotAllowed => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_NO),
|
||||
_ => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_ARROW),
|
||||
};
|
||||
SDL2.SDL.SDL_SetCursor(sdlCursor);
|
||||
_ = SDL2.SDL.SDL_ShowCursor(1);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void UpdateMonitors()
|
||||
{
|
||||
ImGuiPlatformIOPtr platformIO = ImGui.GetPlatformIO();
|
||||
Marshal.FreeHGlobal(platformIO.NativePtr->Monitors.Data);
|
||||
int videoDisplayCount = SDL2.SDL.SDL_GetNumVideoDisplays();
|
||||
IntPtr data = Marshal.AllocHGlobal(Unsafe.SizeOf<ImGuiPlatformMonitor>() * videoDisplayCount);
|
||||
platformIO.NativePtr->Monitors = new ImVector(videoDisplayCount, videoDisplayCount, data);
|
||||
|
||||
for (int i = 0; i < videoDisplayCount; i++)
|
||||
{
|
||||
_ = SDL2.SDL.SDL_GetDisplayUsableBounds(i, out SDL2.SDL.SDL_Rect usableBounds);
|
||||
_ = SDL2.SDL.SDL_GetDisplayBounds(i, out SDL2.SDL.SDL_Rect bounds);
|
||||
_ = SDL2.SDL.SDL_GetDisplayDPI(i, out float ddpi, out float hdpi, out float vdpi);
|
||||
ImGuiPlatformMonitorPtr monitor = platformIO.Monitors[i];
|
||||
float standardDpi = 96f; // Standard DPI typically used
|
||||
monitor.DpiScale = hdpi / standardDpi;
|
||||
monitor.MainPos = new Vector2(bounds.x, bounds.y);
|
||||
monitor.MainSize = new Vector2(bounds.w, bounds.h);
|
||||
monitor.WorkPos = new Vector2(usableBounds.x, usableBounds.y);
|
||||
monitor.WorkSize = new Vector2(usableBounds.w, usableBounds.h);
|
||||
}
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
if (!frameBegun)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
frameBegun = false;
|
||||
|
||||
if ((ImGui.GetIO().ConfigFlags & ImGuiConfigFlags.ViewportsEnable) != 0)
|
||||
{
|
||||
ImGui.Render();
|
||||
ImGui.UpdatePlatformWindows();
|
||||
ImGuiPlatformIOPtr platformIO = ImGui.GetPlatformIO();
|
||||
|
||||
for (int i = 0; i < platformIO.Viewports.Size; i++)
|
||||
{
|
||||
ImGuiViewportPtr vp = platformIO.Viewports[i];
|
||||
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
||||
|
||||
if (!window.Window.Claimed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
UpdateImGuiBuffers(vp.DrawData);
|
||||
|
||||
CommandBuffer commandBuffer = graphicsDevice.AcquireCommandBuffer();
|
||||
Texture swapchainTexture = commandBuffer.AcquireSwapchainTexture(window.Window);
|
||||
|
||||
if (swapchainTexture != null)
|
||||
{
|
||||
RenderCommandLists(commandBuffer, swapchainTexture, vp.DrawData);
|
||||
graphicsDevice.Submit(commandBuffer);
|
||||
//graphicsDevice.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.Render();
|
||||
|
||||
if (!mainWindow.Claimed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ImDrawDataPtr drawDataPtr = ImGui.GetDrawData();
|
||||
UpdateImGuiBuffers(drawDataPtr);
|
||||
|
||||
CommandBuffer commandBuffer = graphicsDevice.AcquireCommandBuffer();
|
||||
Texture swapchainTexture = commandBuffer.AcquireSwapchainTexture(mainWindow);
|
||||
|
||||
if (swapchainTexture != null)
|
||||
{
|
||||
RenderCommandLists(commandBuffer, swapchainTexture, drawDataPtr);
|
||||
graphicsDevice.Submit(commandBuffer);
|
||||
//graphicsDevice.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void UpdateImGuiBuffers(ImDrawDataPtr drawDataPtr)
|
||||
{
|
||||
if (drawDataPtr.TotalVtxCount == 0 || drawDataPtr.CmdListsCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (drawDataPtr.TotalVtxCount > vertexCount)
|
||||
{
|
||||
imGuiVertexBuffer?.Dispose();
|
||||
|
||||
vertexCount = (uint)(drawDataPtr.TotalVtxCount * 1.5f);
|
||||
imGuiVertexBuffer = Graphics.Buffer.Create<Position2DTextureColorVertex>(
|
||||
graphicsDevice,
|
||||
BufferUsageFlags.Vertex,
|
||||
vertexCount
|
||||
);
|
||||
}
|
||||
|
||||
if (drawDataPtr.TotalIdxCount > indexCount)
|
||||
{
|
||||
imGuiIndexBuffer?.Dispose();
|
||||
|
||||
indexCount = (uint)(drawDataPtr.TotalIdxCount * 1.5f);
|
||||
imGuiIndexBuffer = Graphics.Buffer.Create<ushort>(
|
||||
graphicsDevice,
|
||||
BufferUsageFlags.Index,
|
||||
indexCount
|
||||
);
|
||||
}
|
||||
|
||||
uint vertexOffset = 0;
|
||||
uint indexOffset = 0;
|
||||
|
||||
for (int n = 0; n < drawDataPtr.CmdListsCount; n++)
|
||||
{
|
||||
ImDrawListPtr cmdList = drawDataPtr.CmdLists[n];
|
||||
|
||||
resourceUploader.SetBufferData(
|
||||
imGuiVertexBuffer,
|
||||
vertexOffset,
|
||||
new Span<Position2DTextureColorVertex>(cmdList.VtxBuffer.Data.ToPointer(), cmdList.VtxBuffer.Size),
|
||||
n == 0
|
||||
);
|
||||
|
||||
resourceUploader.SetBufferData(
|
||||
imGuiIndexBuffer,
|
||||
indexOffset,
|
||||
new Span<ushort>(cmdList.IdxBuffer.Data.ToPointer(), cmdList.IdxBuffer.Size),
|
||||
n == 0
|
||||
);
|
||||
|
||||
vertexOffset += (uint)cmdList.VtxBuffer.Size;
|
||||
indexOffset += (uint)cmdList.IdxBuffer.Size;
|
||||
}
|
||||
|
||||
resourceUploader.Upload();
|
||||
}
|
||||
|
||||
private void RenderCommandLists(CommandBuffer commandBuffer, Texture renderTexture, ImDrawDataPtr drawDataPtr)
|
||||
{
|
||||
Vector2 pos = drawDataPtr.DisplayPos;
|
||||
|
||||
RenderPass renderPass = commandBuffer.BeginRenderPass(
|
||||
new ColorAttachmentInfo(renderTexture, false, clearColor)
|
||||
);
|
||||
|
||||
renderPass.BindGraphicsPipeline(imGuiPipeline);
|
||||
|
||||
// It is possible that the buffers are null (for example nothing is in our main windows viewport, then we exixt early but still clear it).
|
||||
if (imGuiVertexBuffer == null || imGuiIndexBuffer == null)
|
||||
{
|
||||
commandBuffer.EndRenderPass(renderPass);
|
||||
return;
|
||||
}
|
||||
|
||||
Matrix4x4 projectionMatrix = Matrix4x4.CreateOrthographicOffCenter(
|
||||
pos.X,
|
||||
pos.X + drawDataPtr.DisplaySize.X,
|
||||
pos.Y + drawDataPtr.DisplaySize.Y,
|
||||
pos.Y,
|
||||
-1.0f,
|
||||
1.0f
|
||||
);
|
||||
TransformVertexUniform vertexUniform = new TransformVertexUniform(projectionMatrix);
|
||||
|
||||
renderPass.BindVertexBuffer(imGuiVertexBuffer);
|
||||
renderPass.BindIndexBuffer(imGuiIndexBuffer, IndexElementSize.Sixteen);
|
||||
|
||||
commandBuffer.PushVertexUniformData(in vertexUniform);
|
||||
|
||||
uint vertexOffset = 0;
|
||||
uint indexOffset = 0;
|
||||
|
||||
for (int n = 0; n < drawDataPtr.CmdListsCount; n++)
|
||||
{
|
||||
ImDrawListPtr cmdList = drawDataPtr.CmdLists[n];
|
||||
|
||||
for (int cmdIndex = 0; cmdIndex < cmdList.CmdBuffer.Size; cmdIndex++)
|
||||
{
|
||||
ImDrawCmdPtr drawCmd = cmdList.CmdBuffer[cmdIndex];
|
||||
|
||||
Texture texture = textureStorage.GetTexture(drawCmd.TextureId);
|
||||
|
||||
if (texture == null)
|
||||
{
|
||||
Log.Error("Texture or drawCmd.TextureId became null. Fit it!");
|
||||
continue;
|
||||
}
|
||||
|
||||
renderPass.BindFragmentSampler(new TextureSamplerBinding(texture, imGuiSampler));
|
||||
|
||||
float width = drawCmd.ClipRect.Z - (int)drawCmd.ClipRect.X;
|
||||
float height = drawCmd.ClipRect.W - (int)drawCmd.ClipRect.Y;
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
renderPass.SetScissor(
|
||||
new Rect(
|
||||
(int)drawCmd.ClipRect.X - (int)pos.X,
|
||||
(int)drawCmd.ClipRect.Y - (int)pos.Y,
|
||||
(int)drawCmd.ClipRect.Z - (int)drawCmd.ClipRect.X,
|
||||
(int)drawCmd.ClipRect.W - (int)drawCmd.ClipRect.Y
|
||||
)
|
||||
);
|
||||
|
||||
renderPass.DrawIndexedPrimitives(vertexOffset, indexOffset, drawCmd.ElemCount / 3);
|
||||
|
||||
indexOffset += drawCmd.ElemCount;
|
||||
}
|
||||
|
||||
vertexOffset += (uint)cmdList.VtxBuffer.Size;
|
||||
}
|
||||
|
||||
commandBuffer.EndRenderPass(renderPass);
|
||||
}
|
||||
|
||||
#region Resources
|
||||
private unsafe void BuildFontAtlas()
|
||||
{
|
||||
ResourceUploader resourceUploader = new ResourceUploader(graphicsDevice);
|
||||
|
||||
ImGuiIOPtr io = ImGui.GetIO();
|
||||
|
||||
io.Fonts.GetTexDataAsRGBA32(
|
||||
out nint pixelData,
|
||||
out int width,
|
||||
out int height,
|
||||
out int bytesPerPixel
|
||||
);
|
||||
|
||||
Texture fontTexture = resourceUploader.CreateTexture2D(
|
||||
new Span<byte>((void*)pixelData, width * height * bytesPerPixel),
|
||||
(uint)width,
|
||||
(uint)height
|
||||
);
|
||||
|
||||
resourceUploader.Upload();
|
||||
resourceUploader.Dispose();
|
||||
|
||||
io.Fonts.SetTexID(fontTexture.Handle);
|
||||
io.Fonts.ClearTexData();
|
||||
|
||||
textureStorage.Add(fontTexture); // <-- The fontTexture seems to get lost after some time (CG?).
|
||||
this.fontTexture = fontTexture; // <-- So we also keep a reference to make sure it doesn't happen.
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Window
|
||||
private void CreateWindow(ImGuiViewportPtr vp)
|
||||
{
|
||||
GuiViewportWindow window = new GuiViewportWindow(graphicsDevice, vp);
|
||||
}
|
||||
|
||||
private void DestroyWindow(ImGuiViewportPtr vp)
|
||||
{
|
||||
if (vp.PlatformUserData == IntPtr.Zero) return;
|
||||
|
||||
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
||||
window.Dispose();
|
||||
|
||||
vp.PlatformUserData = IntPtr.Zero;
|
||||
}
|
||||
|
||||
private void ShowWindow(ImGuiViewportPtr vp)
|
||||
{
|
||||
if (vp.PlatformUserData == IntPtr.Zero) return;
|
||||
|
||||
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
||||
SDL2.SDL.SDL_ShowWindow(window.Window.Handle);
|
||||
}
|
||||
|
||||
private unsafe void GetWindowPos(ImGuiViewportPtr vp, Vector2* outPos)
|
||||
{
|
||||
if (vp.PlatformUserData == IntPtr.Zero) return;
|
||||
|
||||
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
||||
SDL2.SDL.SDL_GetWindowPosition(window.Window.Handle, out int x, out int y);
|
||||
*outPos = new Vector2(x, y);
|
||||
}
|
||||
|
||||
private void SetWindowPos(ImGuiViewportPtr vp, Vector2 pos)
|
||||
{
|
||||
if (vp.PlatformUserData == IntPtr.Zero) return;
|
||||
|
||||
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
||||
SDL2.SDL.SDL_SetWindowPosition(window.Window.Handle, (int)pos.X, (int)pos.Y);
|
||||
}
|
||||
|
||||
private void SetWindowSize(ImGuiViewportPtr vp, Vector2 size)
|
||||
{
|
||||
if (vp.PlatformUserData == IntPtr.Zero) return;
|
||||
|
||||
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
||||
SDL2.SDL.SDL_SetWindowSize(window.Window.Handle, (int)size.X, (int)size.Y);
|
||||
}
|
||||
|
||||
private unsafe void GetWindowSize(ImGuiViewportPtr vp, Vector2* outSize)
|
||||
{
|
||||
if (vp.PlatformUserData == IntPtr.Zero) return;
|
||||
|
||||
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
||||
SDL2.SDL.SDL_GetWindowSize(window.Window.Handle, out int w, out int h);
|
||||
*outSize = new Vector2(w, h);
|
||||
}
|
||||
|
||||
private void SetWindowFocus(ImGuiViewportPtr vp)
|
||||
{
|
||||
if (vp.PlatformUserData == IntPtr.Zero) return;
|
||||
|
||||
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
||||
//SDL2.SDL.SDL_SetWindowInputFocus(window.Handle);
|
||||
SDL2.SDL.SDL_RaiseWindow(window.Window.Handle);
|
||||
}
|
||||
|
||||
private byte GetWindowFocus(ImGuiViewportPtr vp)
|
||||
{
|
||||
if (vp.PlatformUserData == IntPtr.Zero) return (byte)0;
|
||||
|
||||
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
||||
SDL2.SDL.SDL_WindowFlags flags = (SDL2.SDL.SDL_WindowFlags)SDL2.SDL.SDL_GetWindowFlags(window.Window.Handle);
|
||||
|
||||
return (flags & SDL2.SDL.SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS) != 0 ? (byte)1 : (byte)0;
|
||||
}
|
||||
|
||||
private byte GetWindowMinimized(ImGuiViewportPtr vp)
|
||||
{
|
||||
if (vp.PlatformUserData == IntPtr.Zero) return 0;
|
||||
|
||||
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
||||
SDL2.SDL.SDL_WindowFlags flags = (SDL2.SDL.SDL_WindowFlags)SDL2.SDL.SDL_GetWindowFlags(window.Window.Handle);
|
||||
|
||||
return (flags & SDL2.SDL.SDL_WindowFlags.SDL_WINDOW_MINIMIZED) != 0 ? (byte)1 : (byte)0;
|
||||
}
|
||||
|
||||
private unsafe void SetWindowTitle(ImGuiViewportPtr vp, IntPtr title)
|
||||
{
|
||||
if (vp.PlatformUserData == IntPtr.Zero) return;
|
||||
|
||||
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
||||
byte* titlePtr = (byte*)title;
|
||||
int count = 0;
|
||||
while (titlePtr[count] != 0)
|
||||
{
|
||||
count += 1;
|
||||
}
|
||||
SDL2.SDL.SDL_SetWindowTitle(window.Window.Handle, System.Text.Encoding.ASCII.GetString(titlePtr, count));
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
fontTexture?.Dispose();
|
||||
imGuiVertexBuffer?.Dispose();
|
||||
imGuiIndexBuffer?.Dispose();
|
||||
imGuiFragmentShader?.Dispose();
|
||||
imGuiVertexShader?.Dispose();
|
||||
imGuiPipeline?.Dispose();
|
||||
imGuiSampler?.Dispose();
|
||||
resourceUploader?.Dispose();
|
||||
}
|
||||
}
|
33
Nerfed.Runtime/Gui/GuiStructs.cs
Normal file
33
Nerfed.Runtime/Gui/GuiStructs.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using Nerfed.Runtime.Graphics;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Nerfed.Runtime.Gui;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Position2DTextureColorVertex(Vector2 position, Vector2 texcoord, Color color) : IVertexType
|
||||
{
|
||||
public Vector2 Position = position;
|
||||
public Vector2 TexCoord = texcoord;
|
||||
public Color Color = color;
|
||||
|
||||
public static VertexElementFormat[] Formats { get; } = new VertexElementFormat[3]
|
||||
{
|
||||
VertexElementFormat.Vector2,
|
||||
VertexElementFormat.Vector2,
|
||||
VertexElementFormat.Color
|
||||
};
|
||||
|
||||
public static uint[] Offsets { get; } = new uint[3]
|
||||
{
|
||||
0,
|
||||
8,
|
||||
16
|
||||
};
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct TransformVertexUniform(Matrix4x4 projectionMatrix)
|
||||
{
|
||||
public Matrix4x4 ProjectionMatrix = projectionMatrix;
|
||||
}
|
35
Nerfed.Runtime/Gui/GuiTextureStorage.cs
Normal file
35
Nerfed.Runtime/Gui/GuiTextureStorage.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using Nerfed.Runtime.Graphics;
|
||||
|
||||
namespace Nerfed.Runtime.Gui;
|
||||
|
||||
public class GuiTextureStorage
|
||||
{
|
||||
private readonly Dictionary<IntPtr, WeakReference<Texture>> pointerToTexture = new Dictionary<IntPtr, WeakReference<Texture>>();
|
||||
|
||||
public IntPtr Add(Texture texture)
|
||||
{
|
||||
if (!pointerToTexture.ContainsKey(texture.Handle))
|
||||
{
|
||||
pointerToTexture.Add(texture.Handle, new WeakReference<Texture>(texture));
|
||||
}
|
||||
return texture.Handle;
|
||||
}
|
||||
|
||||
public Texture GetTexture(IntPtr pointer)
|
||||
{
|
||||
if (!pointerToTexture.TryGetValue(pointer, out WeakReference<Texture> value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
WeakReference<Texture> result = value;
|
||||
|
||||
if (!result.TryGetTarget(out Texture texture))
|
||||
{
|
||||
pointerToTexture.Remove(pointer);
|
||||
return null;
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
}
|
87
Nerfed.Runtime/Gui/GuiViewportWindow.cs
Normal file
87
Nerfed.Runtime/Gui/GuiViewportWindow.cs
Normal file
@ -0,0 +1,87 @@
|
||||
using ImGuiNET;
|
||||
using Nerfed.Runtime.Graphics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Nerfed.Runtime.Gui
|
||||
{
|
||||
internal class GuiViewportWindow
|
||||
{
|
||||
public Window Window => window;
|
||||
|
||||
private readonly GCHandle gcHandle;
|
||||
private readonly GraphicsDevice graphicsDevice;
|
||||
private readonly ImGuiViewportPtr vp;
|
||||
private readonly Window window;
|
||||
|
||||
public GuiViewportWindow(GraphicsDevice graphicsDevice, ImGuiViewportPtr vp, Window window)
|
||||
{
|
||||
this.graphicsDevice = graphicsDevice;
|
||||
this.vp = vp;
|
||||
this.window = window;
|
||||
|
||||
gcHandle = GCHandle.Alloc(this);
|
||||
|
||||
if (!window.Claimed)
|
||||
{
|
||||
graphicsDevice.ClaimWindow(window, SwapchainComposition.SDR, PresentMode.Immediate); // What PresentMode do we need?
|
||||
}
|
||||
|
||||
vp.PlatformUserData = (IntPtr)gcHandle;
|
||||
}
|
||||
|
||||
public GuiViewportWindow(GraphicsDevice graphicsDevice, ImGuiViewportPtr vp)
|
||||
{
|
||||
this.graphicsDevice = graphicsDevice;
|
||||
this.vp = vp;
|
||||
|
||||
gcHandle = GCHandle.Alloc(this);
|
||||
|
||||
// TODO: Handle all flags.
|
||||
ScreenMode screenMode = ScreenMode.Windowed;
|
||||
bool systemResizable = true;
|
||||
|
||||
if ((vp.Flags & ImGuiViewportFlags.NoDecoration) == ImGuiViewportFlags.NoDecoration)
|
||||
{
|
||||
screenMode = ScreenMode.WindowedBorderless;
|
||||
systemResizable = false;
|
||||
}
|
||||
|
||||
WindowCreateInfo info = new WindowCreateInfo("Window Title", (uint)vp.Pos.X, (uint)vp.Pos.Y, screenMode, systemResizable, false);
|
||||
|
||||
window = new Window(graphicsDevice, info);
|
||||
graphicsDevice.ClaimWindow(window, SwapchainComposition.SDR, PresentMode.Immediate); // What PresentMode do we need?
|
||||
|
||||
window.OnMovedEvent += HandleOnMovedEvent;
|
||||
window.OnResizedEvent += HandleOnResizedEvent;
|
||||
window.OnCloseEvent += HandleOnCloseEvent;
|
||||
|
||||
vp.PlatformUserData = (IntPtr)gcHandle;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
window.OnMovedEvent -= HandleOnMovedEvent;
|
||||
window.OnResizedEvent -= HandleOnResizedEvent;
|
||||
window.OnCloseEvent -= HandleOnCloseEvent;
|
||||
|
||||
graphicsDevice.UnclaimWindow(window);
|
||||
window.Dispose();
|
||||
gcHandle.Free();
|
||||
}
|
||||
|
||||
private void HandleOnMovedEvent(Window window, int x, int y)
|
||||
{
|
||||
vp.PlatformRequestMove = true;
|
||||
}
|
||||
|
||||
private void HandleOnResizedEvent(Window window, uint w, uint h)
|
||||
{
|
||||
vp.PlatformRequestResize = true;
|
||||
}
|
||||
|
||||
private void HandleOnCloseEvent(Window window)
|
||||
{
|
||||
vp.PlatformRequestClose = true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
using System.Numerics;
|
||||
using SDL2;
|
||||
using SDL2;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Nerfed.Runtime;
|
||||
|
||||
@ -65,6 +65,8 @@ public static int GetWheelHorizontal()
|
||||
|
||||
internal static void Update()
|
||||
{
|
||||
wheelX = 0;
|
||||
wheelY = 0;
|
||||
Array.Copy(buttonStates, lastButtonStates, buttonStates.Length);
|
||||
}
|
||||
|
||||
@ -105,8 +107,8 @@ private static void ProcessButtonUpEvent(ref SDL.SDL_MouseButtonEvent ev)
|
||||
|
||||
private static void ProcessWheelEvent(ref SDL.SDL_MouseWheelEvent ev)
|
||||
{
|
||||
wheelX += ev.x;
|
||||
wheelY += ev.y;
|
||||
wheelX = ev.x;
|
||||
wheelY = ev.y;
|
||||
}
|
||||
|
||||
private static void ProcessMotionEvent(ref SDL.SDL_MouseMotionEvent ev)
|
||||
|
1
Nerfed.Runtime/Libraries/ImGui.NET
Submodule
1
Nerfed.Runtime/Libraries/ImGui.NET
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit ae493d92a312810b66483af1922babe2eb434a47
|
@ -37,6 +37,7 @@
|
||||
<Compile Include="Libraries\FAudio\csharp\FAudio.cs"/>
|
||||
<Compile Include="Libraries\WellspringCS\WellspringCS.cs"/>
|
||||
<Compile Include="Libraries\dav1dfile\csharp\dav1dfile.cs"/>
|
||||
<Compile Include="Libraries\ImGui.NET\src\ImGui.NET\**\*.cs"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -3,6 +3,7 @@ namespace Nerfed.Runtime;
|
||||
public enum ScreenMode
|
||||
{
|
||||
Fullscreen,
|
||||
BorderlessFullscreen,
|
||||
Windowed
|
||||
FullscreenBorderless,
|
||||
Windowed,
|
||||
WindowedBorderless
|
||||
}
|
@ -11,6 +11,10 @@ namespace Nerfed.Runtime;
|
||||
/// </summary>
|
||||
public class Window
|
||||
{
|
||||
public event Action<Window, uint, uint> OnResizedEvent;
|
||||
public event Action<Window, int, int> OnMovedEvent;
|
||||
public event Action<Window> OnCloseEvent;
|
||||
|
||||
internal IntPtr Handle { get; }
|
||||
public ScreenMode ScreenMode { get; private set; }
|
||||
public uint Width { get; private set; }
|
||||
@ -33,8 +37,6 @@ public class Window
|
||||
public string Title { get; private set;}
|
||||
|
||||
private bool IsDisposed;
|
||||
private System.Action<uint, uint> SizeChangeCallback = null;
|
||||
|
||||
private static readonly Dictionary<uint, Window> windowsById = new Dictionary<uint, Window>();
|
||||
|
||||
public Window(GraphicsDevice graphicsDevice, WindowCreateInfo windowCreateInfo)
|
||||
@ -54,10 +56,14 @@ public Window(GraphicsDevice graphicsDevice, WindowCreateInfo windowCreateInfo)
|
||||
{
|
||||
flags |= SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN;
|
||||
}
|
||||
else if (windowCreateInfo.ScreenMode == ScreenMode.BorderlessFullscreen)
|
||||
else if (windowCreateInfo.ScreenMode == ScreenMode.FullscreenBorderless)
|
||||
{
|
||||
flags |= SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
}
|
||||
else if(windowCreateInfo.ScreenMode == ScreenMode.WindowedBorderless)
|
||||
{
|
||||
flags |= SDL.SDL_WindowFlags.SDL_WINDOW_BORDERLESS;
|
||||
}
|
||||
|
||||
if (windowCreateInfo.SystemResizable)
|
||||
{
|
||||
@ -104,6 +110,9 @@ internal static void ProcessEvent(ref SDL.SDL_Event ev)
|
||||
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
window.ProcessSizeChangedEvent(ref ev.window);
|
||||
break;
|
||||
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_MOVED:
|
||||
window.ProcessMovedChangedEvent(ref ev.window);
|
||||
break;
|
||||
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE:
|
||||
window.ProcessCloseEvent(ref ev.window);
|
||||
break;
|
||||
@ -117,14 +126,17 @@ private void ProcessSizeChangedEvent(ref SDL.SDL_WindowEvent ev)
|
||||
Width = newWidth;
|
||||
Height = newHeight;
|
||||
|
||||
if (SizeChangeCallback != null)
|
||||
{
|
||||
SizeChangeCallback(newWidth, newHeight);
|
||||
OnResizedEvent?.Invoke(this, Width, Height);
|
||||
}
|
||||
|
||||
private void ProcessMovedChangedEvent(ref SDL.SDL_WindowEvent ev)
|
||||
{
|
||||
OnMovedEvent?.Invoke(this, ev.data1, ev.data2);
|
||||
}
|
||||
|
||||
private void ProcessCloseEvent(ref SDL.SDL_WindowEvent ev)
|
||||
{
|
||||
OnCloseEvent?.Invoke(this);
|
||||
Engine.GraphicsDevice.UnclaimWindow(this);
|
||||
Dispose();
|
||||
}
|
||||
@ -140,7 +152,7 @@ public void SetScreenMode(ScreenMode screenMode)
|
||||
{
|
||||
windowFlag = SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN;
|
||||
}
|
||||
else if (screenMode == ScreenMode.BorderlessFullscreen)
|
||||
else if (screenMode == ScreenMode.FullscreenBorderless)
|
||||
{
|
||||
windowFlag = SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
}
|
||||
@ -195,14 +207,6 @@ internal void Show()
|
||||
SDL.SDL_ShowWindow(Handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// You can specify a method to run when the window size changes.
|
||||
/// </summary>
|
||||
public void RegisterSizeChangeCallback(System.Action<uint, uint> sizeChangeCallback)
|
||||
{
|
||||
SizeChangeCallback = sizeChangeCallback;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!IsDisposed)
|
||||
|
@ -1,3 +1,11 @@
|
||||
# Nerfed
|
||||
|
||||
nerfed game engine
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
||||
|
||||
## Third-Party Licenses
|
||||
|
||||
This project includes third-party libraries with their respective licenses, which can be found in the [THIRD_PARTY_LICENSES](THIRD_PARTY_LICENSES) file.
|
29
THIRD_PARTY_LICENSES
Normal file
29
THIRD_PARTY_LICENSES
Normal file
@ -0,0 +1,29 @@
|
||||
# Third-Party Licenses
|
||||
|
||||
## ImGui
|
||||
|
||||
**Name:** Dear ImGui
|
||||
**License:** MIT License
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014-2022 Omar Cornut
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
Loading…
Reference in New Issue
Block a user