diff --git a/imgui.ini b/imgui.ini
index 6d24b31..5a1a38d 100644
--- a/imgui.ini
+++ b/imgui.ini
@@ -14,7 +14,7 @@ Size=250,398
Collapsed=0
[Window][Inspector]
-Pos=36,302
+Pos=112,297
Size=300,425
Collapsed=0
diff --git a/src/CortexEngine.App/CortexEngine.App.csproj b/src/CortexEngine.App/CortexEngine.App.csproj
index f04bd6b..0e30318 100644
--- a/src/CortexEngine.App/CortexEngine.App.csproj
+++ b/src/CortexEngine.App/CortexEngine.App.csproj
@@ -26,6 +26,7 @@
+
diff --git a/src/CortexEngine.App/ObjectManipulator.cs b/src/CortexEngine.App/ObjectManipulator.cs
index 2466776..a7271af 100644
--- a/src/CortexEngine.App/ObjectManipulator.cs
+++ b/src/CortexEngine.App/ObjectManipulator.cs
@@ -28,10 +28,14 @@ public sealed class ObjectManipulator
/// Process input for object picking and dragging.
/// Returns true if input was consumed (ImGui should be ignored).
///
+ private bool _imGuiAvailable;
+
+ public void SetImGuiAvailable(bool available) => _imGuiAvailable = available;
+
public bool ProcessInput(World world, Camera camera, IInputState input)
{
// Skip if ImGui is capturing mouse
- if (ImGuiNET.ImGui.GetIO().WantCaptureMouse)
+ if (_imGuiAvailable && ImGuiNET.ImGui.GetIO().WantCaptureMouse)
{
_isDragging = false;
return false;
diff --git a/src/CortexEngine.App/Program.cs b/src/CortexEngine.App/Program.cs
index c6a0239..98f6547 100644
--- a/src/CortexEngine.App/Program.cs
+++ b/src/CortexEngine.App/Program.cs
@@ -11,6 +11,7 @@ using Engine.Core;
using Engine.Core.Components;
using Engine.Graphics;
using Engine.Graphics.Loaders;
+using Engine.Graphics.OpenGL;
using Engine.Graphics.RaylibBackend;
using Engine.Graphics.Vulkan;
using Engine.Physics;
@@ -43,19 +44,22 @@ class Program
RaylibBackendRegistrar.EnsureRegistered();
VulkanBackendRegistrar.EnsureRegistered();
- using var renderContext = RenderBackendFactory.Create("raylib", 1280, 720, enableValidation: false);
+ OpenGLBackendRegistrar.EnsureRegistered();
+ using var renderContext = RenderBackendFactory.Create("opengl", 1280, 720, enableValidation: false);
var window = renderContext.Window;
var input = window.Input;
using var renderer = renderContext.CreateRenderer();
- // ImGui editor layer
- var imGuiLayer = new ImGuiLayer();
+ // ImGui editor layer (Raylib only)
+ ImGuiLayer? imGuiLayer = null;
var objectManipulator = new ObjectManipulator();
- if (!cameraTour)
+ objectManipulator.SetImGuiAvailable(false);
+ if (!cameraTour && renderer is RaylibRenderer rlRenderer)
{
+ imGuiLayer = new ImGuiLayer();
imGuiLayer.Initialize();
- if (renderer is RaylibRenderer rlRenderer)
- rlRenderer.ImGuiLayer = imGuiLayer;
+ rlRenderer.ImGuiLayer = imGuiLayer;
+ objectManipulator.SetImGuiAvailable(true);
}
var (modelPath, mcpPort) = ParseArgs(args);
@@ -283,15 +287,18 @@ class Program
}
// Feed frame data to ImGui before rendering.
- imGuiLayer.SetFrameData(world, timing, currentFps);
+ if (imGuiLayer != null)
+ imGuiLayer.SetFrameData(world, timing, currentFps);
- // Sync selection between manipulator and ImGui
- if (objectManipulator.IsDragging && !string.IsNullOrEmpty(objectManipulator.SelectedEntityName))
- imGuiLayer.SetSelectedEntity(objectManipulator.SelectedEntityName);
+ objectManipulator.SetImGuiAvailable(imGuiLayer != null);
renderer.RenderWorld(world);
queue.CompletePendingScreenshots();
+ // Swap buffers for OpenGL backend
+ if (renderContext is OpenGLRenderContext glCtx2)
+ glCtx2.SwapBuffers();
+
frames++;
if (timing.TotalTime - lastFpsTime >= 1.0)
{
@@ -303,7 +310,7 @@ class Program
}
Console.WriteLine("Shutting down...");
- imGuiLayer.Dispose();
+ imGuiLayer?.Dispose();
#if !RELEASE_AOT
if (mcpApp != null)
await mcpApp.StopAsync();
diff --git a/src/Engine.Graphics.OpenGL/OpenGLRenderContext.cs b/src/Engine.Graphics.OpenGL/OpenGLRenderContext.cs
index 6d4c539..538accd 100644
--- a/src/Engine.Graphics.OpenGL/OpenGLRenderContext.cs
+++ b/src/Engine.Graphics.OpenGL/OpenGLRenderContext.cs
@@ -1,7 +1,9 @@
using Engine.Core;
using Engine.Graphics;
using Silk.NET.OpenGL;
+using Silk.NET.Windowing;
using SilkWindow = Silk.NET.Windowing.IWindow;
+using CoreIWindow = Engine.Core.IWindow;
namespace Engine.Graphics.OpenGL;
@@ -12,16 +14,15 @@ public sealed class OpenGLRenderContext : IRenderContext
private OpenGLRenderer? _renderer;
private bool _disposed;
- public IWindow Window => _window;
+ public CoreIWindow Window => _window;
public OpenGLRenderContext(int width, int height, bool enableValidation = false)
{
_window = new OpenGLWindow("Cortex Engine (OpenGL)", width, height);
- }
-
- public void OnLoad()
- {
- _gl = _window.SilkView.CreateOpenGL();
+ // Initialize() triggers the Load callback which creates the GL context
+ _window.SilkView.Initialize();
+ // After Initialize, CreateOpenGL should work
+ _gl = _window.CreateGL();
_window.OnLoad();
}
@@ -31,10 +32,9 @@ public sealed class OpenGLRenderContext : IRenderContext
return _renderer;
}
- public void Resize(int width, int height)
- {
- _renderer?.SetScreenSize(width, height);
- }
+ public void Resize(int width, int height) => _renderer?.SetScreenSize(width, height);
+
+ public void SwapBuffers() => _window.SilkView.SwapBuffers();
public void Dispose()
{
diff --git a/src/Engine.Graphics.OpenGL/OpenGLWindow.cs b/src/Engine.Graphics.OpenGL/OpenGLWindow.cs
index 854e642..3bf832c 100644
--- a/src/Engine.Graphics.OpenGL/OpenGLWindow.cs
+++ b/src/Engine.Graphics.OpenGL/OpenGLWindow.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Engine.Core;
using Silk.NET.Input;
+using Silk.NET.OpenGL;
using Silk.NET.Windowing;
using SilkWindow = Silk.NET.Windowing.IWindow;
@@ -31,6 +32,8 @@ public sealed class OpenGLWindow : Engine.Core.IWindow, IDisposable
_silkWindow = Window.Create(options);
_silkWindow.Closing += () => _shouldClose = true;
+ // Initialize without Run() — enables non-blocking event loop
+ _silkWindow.Initialize();
}
public void OnLoad()
@@ -39,6 +42,8 @@ public sealed class OpenGLWindow : Engine.Core.IWindow, IDisposable
_input.Initialize(inputContext);
}
+ public GL CreateGL() => _silkWindow.CreateOpenGL();
+
public void PumpEvents() => _silkWindow.DoEvents();
public void Close() => _shouldClose = true;
public string[] GetRequiredVulkanExtensions() => Array.Empty();