Files
2026-05-19 11:43:24 -05:00

294 lines
8.2 KiB
C#

using System.Numerics;
using Hexa.NET.ImGui;
using Hexa.NET.ImGuizmo;
using Hexa.NET.ImPlot;
using Hexa.NET.OpenGL;
using HexaGen.Runtime;
using SDL3;
namespace SDL3_TestingSuite.SDL3;
public sealed unsafe class SDL3Window : IDisposable
{
public readonly nint Window;
public readonly Vector4 DefaultClearColor = new Vector4(0.06f, 0.06f, 0.06f, 1f);
private Vector4? _clearColor;
public Vector4 ClearColor
{
get => _clearColor ?? DefaultClearColor;
set => _clearColor = value;
}
public event Action? RenderCallback;
private ImGuiContextPtr _imGuiContext;
private ImPlotContextPtr _imPlotContext;
public bool Disposed => _disposed;
private bool _disposed;
private readonly FileSystemWatcher? _watcher;
private readonly GL _gl;
public SDL3Window(string name, int posX, int posY, int width, int height, SDL.WindowFlags flags = SDL.WindowFlags.Resizable | SDL.WindowFlags.HighPixelDensity)
{
if (!SDL.Init(SDL.InitFlags.Events | SDL.InitFlags.Video | SDL.InitFlags.Gamepad))
throw new Exception($"SDL_Init failed: {SDL.GetError()}");
SDL.GLSetAttribute(SDL.GLAttr.ContextFlags, (int)SDL.GLContextFlag.ForwardCompatible);
SDL.GLSetAttribute(SDL.GLAttr.ContextProfileMask, (int)SDL.GLProfile.Core);
SDL.GLSetAttribute(SDL.GLAttr.ContextMajorVersion, 3);
SDL.GLSetAttribute(SDL.GLAttr.ContextMinorVersion, 3);
SDL.SetHint(SDL.Hints.IMEImplementedUI, "1");
// Create window & renderer
SDL.GLSetAttribute(SDL.GLAttr.DoubleBuffer, 1);
SDL.GLSetAttribute(SDL.GLAttr.DepthSize, 24);
SDL.GLSetAttribute(SDL.GLAttr.StencilSize, 8);
Window = SDL.CreateWindow(name, width, height, flags | SDL.WindowFlags.OpenGL);
if (Window == nint.Zero)
throw new Exception($"SDL_CreateWindowAndRenderer failed: {SDL.GetError()}");
nint glContext = SDL.GLCreateContext(Window);
if (glContext == nint.Zero)
throw new Exception($"SDL_GL_CreateContext failed: {SDL.GetError()}");
SDL.GLMakeCurrent(Window, glContext);
SDL.GLSetSwapInterval(1);
SDL.SetWindowPosition(Window, posX, posY);
// SDL.SetRenderVSync(Renderer, 1);
SDL.ShowWindow(Window);
// Create ImGui context
_imGuiContext = ImGui.CreateContext();
_imPlotContext = ImPlot.CreateContext();
ImPlot.SetImGuiContext(_imGuiContext);
ImGuizmo.SetImGuiContext(_imGuiContext);
ImGuiIOPtr io = ImGui.GetIO();
io.ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard | ImGuiConfigFlags.NavEnableGamepad | ImGuiConfigFlags.DockingEnable;
io.ConfigFlags |= ImGuiConfigFlags.ViewportsEnable;
io.Fonts.Flags |= ImFontAtlasFlags.NoBakedLines;
io.Fonts.AddFontDefault();
try
{
string fontsPath = Path.Combine(AppContext.BaseDirectory, "Fonts");
if (!Path.Exists(fontsPath)) Directory.CreateDirectory(fontsPath);
_watcher = new FileSystemWatcher(fontsPath);
_watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.CreationTime;
_watcher.Created += (_, b) =>
{
if (!File.Exists(b.FullPath)) return;
if (Path.GetExtension(b.FullPath) != ".ttf") return;
ImGui.GetIO().AddFont(b.FullPath);
};
_watcher.Deleted += (_, b) =>
{
if (Path.GetExtension(b.FullPath) != ".ttf") return;
ImGui.GetIO().RemoveFont(b.Name);
};
_watcher.IncludeSubdirectories = false;
_watcher.EnableRaisingEvents = true;
string[] fonts = Directory.GetFiles(fontsPath, "*.ttf", SearchOption.AllDirectories);
foreach (string font in fonts)
{
io.AddFont(font);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
_gl = new GL(new BindingsContext(Window, glContext));
// Init platform and renderer
ImGuiSDL3Platform.Init(_gl, glContext, Window, this);
ImGuiSDL3Renderer.Init(_gl, "#version 330");
}
public bool ShouldClose;
public void Run()
{
while (!_disposed)
{
SDL.PumpEvents();
if (ImGui.GetIO().WantTextInput && !SDL.TextInputActive(Window))
SDL.StartTextInput(Window);
else if (!ImGui.GetIO().WantTextInput && SDL.TextInputActive(Window))
SDL.StopTextInput(Window);
while (SDL.PollEvent(out SDL.Event ev))
{
ImGuiSDL3Platform.ProcessEvent(ev);
switch ((SDL.EventType)ev.Type)
{
case SDL.EventType.Terminating:
case SDL.EventType.WindowCloseRequested:
case SDL.EventType.Quit:
ShouldClose = true;
break;
}
}
_gl.MakeCurrent();
_gl.ClearColor(ClearColor.X, ClearColor.Y, ClearColor.Z, ClearColor.W);
_gl.Clear(GLClearBufferMask.ColorBufferBit);
ImGuiSDL3Platform.NewFrame();
ImGuiSDL3Renderer.NewFrame();
ImGui.NewFrame();
RenderCallback?.Invoke();
ImGuiIOPtr io = ImGui.GetIO();
ImGui.Render();
_gl.MakeCurrent();
ImGuiSDL3Renderer.RenderDrawData(ImGui.GetDrawData());
if ((io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) != 0)
{
ImGui.UpdatePlatformWindows();
ImGui.RenderPlatformWindowsDefault();
}
_gl.MakeCurrent();
_gl.SwapBuffers();
if (ShouldClose)
{
Dispose();
break;
}
}
if (!_disposed)
Dispose();
}
~SDL3Window()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (_disposed) return;
_disposed = true;
if (disposing)
{
if (_imGuiContext.Handle != null)
{
ImGui.SetCurrentContext(_imGuiContext);
}
if (_imPlotContext.Handle != null)
{
ImPlot.SetCurrentContext(_imPlotContext);
}
ImGuiSDL3Renderer.Dispose();
ImGuiSDL3Platform.Dispose();
RenderCallback = null;
}
if (_imGuiContext.Handle != null)
{
ImGui.SetCurrentContext(null);
ImGui.DestroyContext(_imGuiContext);
_imGuiContext = null;
}
if (_imPlotContext.Handle != null)
{
ImPlot.SetCurrentContext(null);
ImPlot.DestroyContext(_imPlotContext);
_imPlotContext = null;
}
// if (Renderer != nint.Zero)
// {
// SDL.DestroyRenderer(Renderer);
// }
if (Window != nint.Zero)
{
SDL.DestroyWindow(Window);
}
}
}
public unsafe class BindingsContext : IGLContext
{
private readonly nint window;
private readonly nint context;
public BindingsContext(nint window, nint context)
{
this.window = window;
this.context = context;
}
public nint Handle => window;
public bool IsCurrent => SDL.GLGetCurrentContext() == context;
public void Dispose() { }
public nint GetProcAddress(string procName)
{
return (nint)SDL.GLGetProcAddress(procName).GetPtrForDelegate();
}
public bool IsExtensionSupported(string extensionName)
{
return SDL.GLExtensionSupported(extensionName);
}
public void MakeCurrent()
{
SDL.GLMakeCurrent(window, context);
}
public void SwapBuffers()
{
SDL.GLSwapWindow(window);
}
public void SwapInterval(int interval)
{
SDL.GLSetSwapInterval(interval);
}
public bool TryGetProcAddress(string procName, out nint procAddress)
{
procAddress = (nint)SDL.GLGetProcAddress(procName).GetPtrForDelegate();
return procAddress != 0;
}
}