diff --git a/Pulsar4X/Pulsar4X.Client/IMGUISDL/ImGuiSDL2CSWindow.cs b/Pulsar4X/Pulsar4X.Client/IMGUISDL/ImGuiSDL2CSWindow.cs index 44b5942ed..1aeaffb02 100644 --- a/Pulsar4X/Pulsar4X.Client/IMGUISDL/ImGuiSDL2CSWindow.cs +++ b/Pulsar4X/Pulsar4X.Client/IMGUISDL/ImGuiSDL2CSWindow.cs @@ -116,31 +116,12 @@ protected unsafe virtual void Create() // Build texture atlas io.Fonts.GetTexDataAsAlpha8(out byte* pixels, out int width, out int height); - GL.GetIntegerv(GL.Enum.GL_TEXTURE_BINDING_2D, out int lastTexture); - - // Create OpenGL texture - GL.GenTextures(1, out int fontTextureID); - GL.BindTexture(GL.Enum.GL_TEXTURE_2D, fontTextureID); - GL.TexParameteri(GL.Enum.GL_TEXTURE_2D, GL.Enum.GL_TEXTURE_MIN_FILTER, (int) GL.Enum.GL_LINEAR); - GL.TexParameteri(GL.Enum.GL_TEXTURE_2D, GL.Enum.GL_TEXTURE_MAG_FILTER, (int) GL.Enum.GL_LINEAR); - GL.PixelStorei(GL.Enum.GL_UNPACK_ROW_LENGTH, 0); - GL.TexImage2D( - GL.Enum.GL_TEXTURE_2D, - 0, - (int) GL.Enum.GL_ALPHA, - width, - height, - 0, - GL.Enum.GL_ALPHA, - GL.Enum.GL_UNSIGNED_BYTE, - new IntPtr(pixels) - ); - g_FontTexture = new IntPtr(fontTextureID); + g_FontTexture = new IntPtr(Renderer.CreateDefaultFontTexture(width, height, (IntPtr)pixels)); + // Store the texture identifier in the ImFontAtlas substructure. io.Fonts.SetTexID(g_FontTexture); ImGuiSDL2CSHelper.FontTextureID = g_FontTexture; io.Fonts.ClearTexData(); // Clears CPU side texture data. - GL.BindTexture(GL.Enum.GL_TEXTURE_2D, lastTexture); } protected override void Dispose(bool disposing) diff --git a/Pulsar4X/Pulsar4X.Client/IMGUISDL/SDL2Window.cs b/Pulsar4X/Pulsar4X.Client/IMGUISDL/SDL2Window.cs index b4369a382..1225f698d 100644 --- a/Pulsar4X/Pulsar4X.Client/IMGUISDL/SDL2Window.cs +++ b/Pulsar4X/Pulsar4X.Client/IMGUISDL/SDL2Window.cs @@ -1,4 +1,5 @@ -using SDL2; +using Pulsar4X.Client.Rendering; +using SDL2; using System; namespace ImGuiSDL2CS @@ -10,8 +11,7 @@ public class SDL2Window : IDisposable protected IntPtr _Handle; public IntPtr Handle => _Handle; - protected IntPtr _GLContext; - public IntPtr GLContext => _GLContext; + public IRenderer Renderer { get; set; } /// /// Window title @@ -112,15 +112,15 @@ public void Init( _Handle = SDL.SDL_CreateWindow(title, x, y, width, height, flags); if ((flags & SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL) == SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL) { - _GLContext = SDL.SDL_GL_CreateContext(_Handle); - SDL.SDL_GL_MakeCurrent(_Handle, _GLContext); + Renderer = RendererFactory.CreateRenderer(RendererType.OpenGL); + Renderer.Initialize(_Handle); } } public bool IsVisible => (Flags & SDL.SDL_WindowFlags.SDL_WINDOW_HIDDEN) == 0; public void Show() => SDL.SDL_ShowWindow(_Handle); public void Hide() => SDL.SDL_HideWindow(_Handle); - public virtual void Swap() => SDL.SDL_GL_SwapWindow(_Handle); + public virtual void Swap() => Renderer.EndFrame(); public virtual void Run() { @@ -170,6 +170,7 @@ protected virtual void Dispose(bool disposing) public void Dispose() { + Renderer.Dispose(); Dispose(true); GC.SuppressFinalize(this); } diff --git a/Pulsar4X/Pulsar4X.Client/PulsarMainWindow.cs b/Pulsar4X/Pulsar4X.Client/PulsarMainWindow.cs index d97f48f74..56a363e95 100644 --- a/Pulsar4X/Pulsar4X.Client/PulsarMainWindow.cs +++ b/Pulsar4X/Pulsar4X.Client/PulsarMainWindow.cs @@ -198,8 +198,8 @@ public override void ImGuiRender() systemState.PreFrameSetup(); } - GL.ClearColor(backColor.X, backColor.Y, backColor.Z, 1f); - GL.Clear(GL.Enum.GL_COLOR_BUFFER_BIT); + Renderer.Clear(backColor.X, backColor.Y, backColor.Z, 1f); + Renderer.BeginFrame(); _state.GalacticMap.Draw(); diff --git a/Pulsar4X/Pulsar4X.Client/Rendering/IRenderer.cs b/Pulsar4X/Pulsar4X.Client/Rendering/IRenderer.cs new file mode 100644 index 000000000..91e34879b --- /dev/null +++ b/Pulsar4X/Pulsar4X.Client/Rendering/IRenderer.cs @@ -0,0 +1,21 @@ +using System; + +namespace Pulsar4X.Client.Rendering; + +public enum RendererType +{ + OpenGL, + // TODO: someone can write these renderers if they desire :D + //Vulkan, + //DirectX +} + +public interface IRenderer : IDisposable +{ + void Initialize(IntPtr windowHandle); + void BeginFrame(); + void EndFrame(); + void Clear(float r, float g, float b, float a); + + int CreateDefaultFontTexture(int width, int height, IntPtr pixels); +} \ No newline at end of file diff --git a/Pulsar4X/Pulsar4X.Client/Rendering/OpenGLRenderer.cs b/Pulsar4X/Pulsar4X.Client/Rendering/OpenGLRenderer.cs new file mode 100644 index 000000000..89b1dc918 --- /dev/null +++ b/Pulsar4X/Pulsar4X.Client/Rendering/OpenGLRenderer.cs @@ -0,0 +1,78 @@ +using System; +using ImGuiSDL2CS; +using SDL2; + +namespace Pulsar4X.Client.Rendering; + +public class OpenGLRenderer : IRenderer +{ + private IntPtr _glContext; + private IntPtr _windowHandle; + + public void Initialize(IntPtr windowHandle) + { + _windowHandle = windowHandle; + + // Set the OpenGL context attributes + SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 4); + SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 5); + SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, (int)SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE); + + // Create the OpenGL context + _glContext = SDL.SDL_GL_CreateContext(_windowHandle); + if(_glContext == IntPtr.Zero) + { + throw new Exception($"Failed to create OpenGL context: {SDL.SDL_GetError()}"); + } + + // Make the OpenGL context current + SDL.SDL_GL_MakeCurrent(_windowHandle, _glContext); + } + + public void BeginFrame() + { + GL.Clear(GL.Enum.GL_COLOR_BUFFER_BIT); + } + + public void EndFrame() + { + SDL.SDL_GL_SwapWindow(_windowHandle); + } + + public void Clear(float r, float g, float b, float a) + { + GL.ClearColor(r, g, b, a); + } + + public int CreateDefaultFontTexture(int width, int height, IntPtr pixels) + { + // Create OpenGL texture + GL.GenTextures(1, out int fontTextureID); + GL.BindTexture(GL.Enum.GL_TEXTURE_2D, fontTextureID); + GL.TexParameteri(GL.Enum.GL_TEXTURE_2D, GL.Enum.GL_TEXTURE_MIN_FILTER, (int) GL.Enum.GL_LINEAR); + GL.TexParameteri(GL.Enum.GL_TEXTURE_2D, GL.Enum.GL_TEXTURE_MAG_FILTER, (int) GL.Enum.GL_LINEAR); + GL.PixelStorei(GL.Enum.GL_UNPACK_ROW_LENGTH, 0); + GL.TexImage2D( + GL.Enum.GL_TEXTURE_2D, + 0, + (int) GL.Enum.GL_ALPHA, + width, + height, + 0, + GL.Enum.GL_ALPHA, + GL.Enum.GL_UNSIGNED_BYTE, + pixels + ); + + return fontTextureID; + } + + public void Dispose() + { + if(_glContext != IntPtr.Zero) + { + SDL.SDL_GL_DeleteContext(_glContext); + _glContext = IntPtr.Zero; + } + } +} \ No newline at end of file diff --git a/Pulsar4X/Pulsar4X.Client/Rendering/RendererFactory.cs b/Pulsar4X/Pulsar4X.Client/Rendering/RendererFactory.cs new file mode 100644 index 000000000..4e56ff12c --- /dev/null +++ b/Pulsar4X/Pulsar4X.Client/Rendering/RendererFactory.cs @@ -0,0 +1,15 @@ +using System; + +namespace Pulsar4X.Client.Rendering; + +public static class RendererFactory +{ + public static IRenderer CreateRenderer(RendererType rendererType) + { + return rendererType switch + { + RendererType.OpenGL => new OpenGLRenderer(), + _ => throw new ArgumentException("Unsupported renderer backend") + }; + } +} \ No newline at end of file