using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using Hexa.NET.ImGui; using Hexa.NET.ImPlot; using HexaGen.Runtime; using SDL3_TestingSuite.SDL3; using SDL3; namespace SDL3_TestingSuite; public class Program { private static bool _demoWindowVisible = true; private static bool _imPlotDemoVisible; private static bool _fontStuff; private static readonly Dictionary?> GlyphsByName = new Dictionary?>(); private static bool _fontStuffInitialized; private static bool _init; private static ImFontPtr _font; private static float size = 1f; private static SDL3Window _window = null!; private static OpenGLTexture? _imageTexture; private static string? _imageLoadError; public static void Main() { string baseDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; string osPlatform = typeof(LibraryLoader).GetMethod("GetOSPlatform", BindingFlags.Static | BindingFlags.NonPublic)?.Invoke(null, null) as string ?? ""; // LibraryLoader.GetOSPlatform(); string architecture = typeof(LibraryLoader).GetMethod("GetArchitecture", BindingFlags.Static | BindingFlags.NonPublic)?.Invoke(null, null) as string ?? ""; // LibraryLoader.GetArchitecture()); LibraryLoader.CustomLoadFolders.AddRange(new string[] { Path.Combine(baseDirectory), Path.Combine(baseDirectory, "runtimes", osPlatform, "native"), Path.Combine(baseDirectory, "runtimes", $"{osPlatform}-{architecture}", "debug"), // allows debug builds sideload. Path.Combine(baseDirectory, "runtimes", $"{osPlatform}-{architecture}", "native"), }); _window = new SDL3Window("SDL3 Testing Suite", 100, 100, 1280, 720); _window.Disposing += DisposeImages; _window.RenderCallback += () => { if (!_init) { _init = true; LoadImageTexture(""); } ImGuiViewportPtr viewport = ImGui.GetMainViewport(); ImGui.SetNextWindowPos(viewport.WorkPos); ImGui.SetNextWindowSize(viewport.WorkSize); ImGui.SetNextWindowViewport(viewport.ID); ImGui.Begin("MainWindow", ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.MenuBar | ImGuiWindowFlags.NoDocking | ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoBringToFrontOnFocus); if (ImGui.BeginMenuBar()) { if (ImGui.BeginMenu("Stuff")) { if (ImGui.MenuItem("Quit")) { _window.ShouldClose = true; } ImGui.EndMenu(); } ImGui.Spacing(); ImGui.MenuItem("Demo Window", "", ref _demoWindowVisible); ImGui.Spacing(); ImGui.MenuItem("ImPlot Demo Window", "", ref _imPlotDemoVisible); ImGui.Spacing(); ImGui.MenuItem("Font Stuff", "", ref _fontStuff); ImGui.EndMenuBar(); } ImGui.End(); if (_fontStuff) { ImGui.SetNextWindowSize(new Vector2(250, 500), ImGuiCond.FirstUseEver); ImGui.Begin("FontStuff", ref _fontStuff); ImGui.ShowFontSelector("Font"); bool change = false; if (ImGui.GetFont() != _font) { _font = ImGui.GetFont(); change = true; } string fontName = _font.GetDebugNameS(); fontName = string.IsNullOrEmpty(fontName) ? _font.FontId.ToString() : fontName; if (!_fontStuffInitialized || change) { if (!GlyphsByName.TryGetValue(fontName, out List? glyphs)) { glyphs = new List(); ImFontPtr font = _font; for (uint codepoint = 0; codepoint <= 0x10FFFF; codepoint++) { if (codepoint >= 0xD800 && codepoint <= 0xDFFF) { continue; } if (!font.IsGlyphInFont(codepoint)) continue; glyphs.Add(codepoint); } GlyphsByName[fontName] = glyphs; Logger.Log($"Initialized font {fontName} with {glyphs.Count} glyphs"); } _fontStuffInitialized = true; } ImGui.PushFont(null, 24); if (GlyphsByName.TryGetValue(fontName, out List? glyphs2)) { float cursorXStart = ImGui.GetCursorPosX(); float maxWidth = ImGui.GetContentRegionAvail().X; foreach (uint codepoint in glyphs2!) { string text = char.ConvertFromUtf32((int)codepoint); float glyphWidth = ImGui.CalcTextSize(text).X; float cursorX = ImGui.GetCursorPosX(); if (cursorX > cursorXStart && (cursorX + glyphWidth) > (cursorXStart + maxWidth)) { ImGui.NewLine(); } ImGui.TextUnformatted(text); ImGui.SameLine(); } } ImGui.PopFont(); ImGui.End(); } ImGui.Begin("Thing"); float time = (float)ImGui.GetTime() * 5; if (ImPlot.BeginPlot("Moving Rainbow Sine Wave")) { int count = 200; float[] xs = new float[2]; float[] ys = new float[2]; for (int i = 0; i < count - 1; i++) { float x0 = i * 0.1f; float x1 = (i + 1) * 0.1f; float y0 = MathF.Sin(x0 * size + time); float y1 = MathF.Sin(x1 * size + time); xs[0] = x0; xs[1] = x1; ys[0] = y0; ys[1] = y1; float t = i / (float)count; float r = 0.5f + 1f * MathF.Sin(6.2831f * (t)); float g = 0.5f + 1f * MathF.Sin(6.2831f * (t + 0.33f)); float b = 0.5f + 1f * MathF.Sin(6.2831f * (t + 0.66f)); ImPlot.PushStyleColor(ImPlotCol.Line, new Vector4(r, g, b, 1f)); ImPlot.PlotLine("##seg", ref xs[0], ref ys[0], 2); ImPlot.PopStyleColor(); } ImPlot.EndPlot(); } ImGui.SliderFloat("Sine", ref size, 1f, 120f); ImGui.End(); bool regen1 = true; if (_imageTexture != null) { Vector2 displaySize = ImGui.GetIO().DisplaySize; Vector2 imageSize = new Vector2(_imageTexture.Width, _imageTexture.Height); float maxWidth = displaySize.X * 0.5f; float maxHeight = displaySize.Y * 0.5f; float scale = MathF.Min(maxWidth / imageSize.X, maxHeight / imageSize.Y); Vector2 scaledSize = imageSize * MathF.Min(scale, 1.0f); Vector2 padding = ImGui.GetStyle().WindowPadding; Vector2 windowSize = new Vector2(scaledSize.X + padding.X * 2, scaledSize.Y + padding.Y * 2 + (ImGui.GetFrameHeight() * 4)); ImGui.SetNextWindowSize(windowSize, ImGuiCond.Always); ImGui.Begin(_imageTexture.FileName ?? "Image", ref regen1, ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoSavedSettings); regen1 = !ImGui.Button("Regen"); ImGui.Image(_imageTexture.ImGuiTexture, scaledSize); string text1 = $"{_imageTexture.Width}x{_imageTexture.Height}"; ImGui.TextUnformatted(text1); ImGui.SameLine(ImGui.CalcTextSize(text1).X + padding.X * 2); ImGui.TextUnformatted($"EXT: {(_imageTexture.Extension ?? "Unknown Format")}"); ImGui.TextUnformatted($"{(_imageTexture.FilePath ?? "Unknown Path")}"); } else { ImGui.Begin("Image", ref regen1, ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoSavedSettings); ImGui.TextUnformatted(_imageLoadError ?? "No image loaded."); } ImGui.End(); bool regen = !regen1; if (regen) { DisposeImages(); LoadImageTexture(""); } if (_demoWindowVisible) { ImGui.ShowDemoWindow(ref _demoWindowVisible); } if (_imPlotDemoVisible) { ImPlot.ShowDemoWindow(ref _imPlotDemoVisible); } }; _window.Run(); } private static void LoadImageTexture(string path) { if (string.IsNullOrEmpty(path)) return; try { _imageTexture = OpenGLTexture.FromFile(_window.GL, path); _imageLoadError = null; } catch (Exception e) { DisposeImages(); _imageLoadError = e.Message; Logger.Log(e); } } private static void DisposeImages() { _imageTexture?.Dispose(); _imageTexture = null; } public static class Logger { private const int MessageWidth = 80; private const int CallerWidth = 35; public static void Log(object? value, [CallerFilePath] string path = "", [CallerMemberName] string caller = "", [CallerLineNumber] int line = 0) { if (value == null) { Write("null", path, caller, line); return; } Write(value.ToString(), path, caller, line); } public static void Log(object?[] values, [CallerFilePath] string path = "", [CallerMemberName] string caller = "", [CallerLineNumber] int line = 0) { if (values == null) { Write("null", path, caller, line); return; } Write(string.Join(" ", values), path, caller, line); } private static void Write(string? str, string path, string caller, int line) { string message = (str ?? string.Empty).PadRight(MessageWidth); string method = caller.PadRight(CallerWidth); Console.WriteLine($"{message} | {method} | {path}:{line}"); } } }