Monad Engine da02fba2
> Undercity Codex_
Loading...
Searching...
No Matches
Engine.cpp
Go to the documentation of this file.
1#include <chrono>
2#include <cstdio>
3#include <cstring>
4#include <vector>
5
6#include "Engine.h"
7
8#include "Application.h"
9#include "Common.h"
10#include "GL/glew.h"
11#include "GLFW/glfw3.h"
12#include "Log.h"
13#include "backends/imgui_impl_glfw.h"
14#include "backends/imgui_impl_opengl3.h"
15#include "graphics/GraphicsAPI.h"
17#include "imgui.h"
18#include "physics/PhysicsManager.h"
19#include "render/RenderQueue.h"
20#include "scene/components/CameraComponent.h"
21
22namespace mnd
23{
24void KeyCallback(GLFWwindow *window, int key, int, int action, int)
25{
26 auto &inputManager = Engine::GetInstance().GetInputManager();
27 if (action == GLFW_PRESS)
28 {
29 inputManager.SetKeyPressed(static_cast<Key>(key), true);
30 } else if (action == GLFW_RELEASE)
31 {
32 inputManager.SetKeyPressed(static_cast<Key>(key), false);
33 }
34}
35
36void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
37{
38 auto &inputManager = Engine::GetInstance().GetInputManager();
39 if (action == GLFW_PRESS)
40 {
41 inputManager.SetMouseButtonPressed(static_cast<MouseButton>(button), true);
42 } else if (action == GLFW_RELEASE)
43 {
44 inputManager.SetMouseButtonPressed(static_cast<MouseButton>(button), false);
45 }
46}
47
48void CursorPositionCallback(GLFWwindow *window, f64 xpos, f64 ypos)
49{
50 auto &inputManager = Engine::GetInstance().GetInputManager();
51
52 inputManager.SetMousePositionOld(inputManager.GetMousePositionCurrent());
53
54 vec2 currentPos(static_cast<f32>(xpos), static_cast<f32>(ypos));
55 inputManager.SetMousePositionCurrent(currentPos);
56 inputManager.SetMousePositionChanged(true);
57}
58
60{
61 static Engine instance;
62 return instance;
63}
64
65bool Engine::Init(int width, int height)
66{
67 LOG_INFO("Engine::Init requested (%dx%d)", width, height);
68
69 if (!m_application)
70 {
71 LOG_ERROR("No application set");
72 return false;
73 }
74
75#if defined(__linux__)
76 glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11);
77#endif
78
80 m_application->RegisterTypes();
81
82 if (glfwInit() == 0)
83 {
84 LOG_ERROR("Failed to initialize GLFW");
85 return false;
86 }
87 LOG_INFO("GLFW initialized");
88
89 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
90 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
91 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
92
93 m_window = glfwCreateWindow(width, height, kDefaultWindowTitle, nullptr, nullptr);
94
95 if (m_window == nullptr)
96 {
97 LOG_ERROR("Error creating window");
98 glfwTerminate();
99 return false;
100 }
101 LOG_INFO("Window created (%dx%d)", width, height);
102
103 glfwSetKeyCallback(m_window, KeyCallback);
104 glfwSetMouseButtonCallback(m_window, MouseButtonCallback);
105 glfwSetCursorPosCallback(m_window, CursorPositionCallback);
106
107 glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
108
109 glfwMakeContextCurrent(m_window);
110
111 glewExperimental = GL_TRUE;
112 GLenum glewStatus = glewInit();
113#ifdef GLEW_ERROR_NO_GLX_DISPLAY
114 if (glewStatus == GLEW_ERROR_NO_GLX_DISPLAY)
115 {
116 glewStatus = GLEW_OK;
117 }
118#endif
119 if (glewStatus != GLEW_OK)
120 {
121 LOG_ERROR("Failed to initialize GLEW: %s", reinterpret_cast<const char *>(glewGetErrorString(glewStatus)));
122 glfwTerminate();
123 return false;
124 }
125 while (glGetError() != GL_NO_ERROR)
126 {
127 }
128 LOG_INFO("GLEW initialized (GL %s)", reinterpret_cast<const char *>(glGetString(GL_VERSION)));
129
130 m_rendererBackend = CreateOpenGLRendererBackend(m_graphicsAPI);
131 if (!m_rendererBackend->Init())
132 {
133 LOG_ERROR("Renderer backend init failed");
134 glfwTerminate();
135 return false;
136 }
137 m_physicsManager.Init();
138 if (!m_audioManager.Init())
139 {
140 LOG_ERROR("AudioManager Init failed");
141 }
142
143 m_sceneTarget.Create(m_renderSettings.internalW, m_renderSettings.internalH);
144 if (!m_postProcess.Init())
145 {
146 LOG_ERROR("PostProcess Init failed — outline pass disabled");
147 m_renderSettings.useOutline = false;
148 }
149 if (!m_editor.Init(m_window))
150 {
151 LOG_ERROR("Editor Init failed");
152 }
153 if (!m_spriteRenderer.Init())
154 {
155 LOG_ERROR("SpriteRenderer Init failed");
156 }
157 m_particleSystem.Init();
158
159 DrawLoadingScreen("Loading...", 0.0F);
160
161 bool appOk = m_application->Init();
162
163 UpdateLoadingProgress(1.0F, "Ready");
164 if (!appOk)
165 {
166 LOG_ERROR("Application Init failed");
167 } else
168 {
169 LOG_INFO("Application initialized");
170 }
171 return appOk;
172}
173
175{
176 if (!m_application)
177 {
178 LOG_ERROR("Engine::Run called with no application");
179 return;
180 }
181
182 LOG_INFO("Engine main loop starting");
183 m_lastTimePoint = std::chrono::steady_clock::now();
184
185 while ((glfwWindowShouldClose(m_window) == 0) && !m_application->NeedsToBeClosed())
186 {
187 glfwPollEvents();
188 m_editor.BeginFrame();
189
190 auto now = std::chrono::steady_clock::now();
191 float deltaTime = std::chrono::duration<float>(now - m_lastTimePoint).count();
192 m_lastTimePoint = now;
193
194 float scaledDt = m_paused ? 0.0F : deltaTime * m_timeScale;
195
196 m_physicsManager.Update(scaledDt);
197 m_application->Update(scaledDt);
198 m_particleSystem.Update(scaledDt);
199
200 // Bind scene target or default framebuffer for the scene pass.
201 // Path B keeps the scene target at framebuffer size and performs
202 // pixelation in the post-pass by snapping shader sample UVs.
203 int winW = 0, winH = 0;
204 glfwGetFramebufferSize(m_window, &winW, &winH);
205 if (winW <= 0) winW = kDefaultInternalWidth;
206 if (winH <= 0) winH = kDefaultInternalHeight;
207
208 if (m_renderSettings.pixelSize < 1)
209 {
210 m_renderSettings.pixelSize = 1;
211 }
212 else if (m_renderSettings.pixelSize > 32)
213 {
214 m_renderSettings.pixelSize = 32;
215 }
216
217 if (m_renderSettings.useInternalRes)
218 {
219 m_renderSettings.internalW = winW;
220 m_renderSettings.internalH = winH;
221 m_sceneTarget.Resize(m_renderSettings.internalW, m_renderSettings.internalH);
222 if (m_sceneTarget.IsValid())
223 {
224 m_sceneTarget.Bind();
225 }
226 else
227 {
228 RenderTarget::BindDefault(winW, winH);
229 }
230 }
231 else
232 {
233 RenderTarget::BindDefault(winW, winH);
234 }
235
236 m_graphicsAPI.SetClearColor(m_renderSettings.clearColor.r,
237 m_renderSettings.clearColor.g,
238 m_renderSettings.clearColor.b,
239 m_renderSettings.clearColor.a);
240 m_graphicsAPI.ClearBuffers();
241
242 CameraData cameraData;
243 std::vector<LightData> lights;
244
245 f32 aspect = static_cast<f32>(winW) / static_cast<f32>(winH);
246
247 if (m_currentScene)
248 {
249 if (auto cameraObject = m_currentScene->GetMainCamera())
250 {
251 // logic for matrices
252 auto cameraComponent = cameraObject->GetComponent<CameraComponent>();
253 if (cameraComponent)
254 {
255 cameraData.viewMatrix = cameraComponent->GetViewMatrix();
256 cameraData.projectionMatrix = cameraComponent->GetProjectionMatrix(aspect);
257 cameraData.position = cameraObject->GetWorldPosition();
258 cameraData.nearPlane = cameraComponent->GetNearPlane();
259 cameraData.farPlane = cameraComponent->GetFarPlane();
260 } else
261 {
262 static bool warned = false;
263 if (!warned)
264 {
265 LOG_WARN("Main camera GameObject '%s' has no CameraComponent", cameraObject->GetName().c_str());
266 warned = true;
267 }
268 }
269 } else
270 {
271 static bool warned = false;
272 if (!warned)
273 {
274 LOG_WARN("Scene has no main camera set — rendering with identity matrices");
275 warned = true;
276 }
277 }
278
279 lights = m_currentScene->CollectLight();
280 }
281
282 m_renderQueue.Draw(m_graphicsAPI, cameraData, lights);
283 m_particleSystem.Render(cameraData);
284
285 // Run the pixelation/outline post-pass for the production color view.
286 // Debug-view selector overrides it and shows raw MRT attachments.
287 if (m_renderSettings.useInternalRes && m_sceneTarget.IsValid())
288 {
289 const bool runPostProcess = m_renderSettings.debugView == DebugView::Color
290 && m_postProcess.IsValid();
291 if (runPostProcess)
292 {
293 const float oldDepthStrength = m_postProcess.depthEdgeStrength;
294 const float oldNormalStrength = m_postProcess.normalEdgeStrength;
295 if (!m_renderSettings.useOutline)
296 {
297 m_postProcess.depthEdgeStrength = 0.0F;
298 m_postProcess.normalEdgeStrength = 0.0F;
299 }
300
301 m_postProcess.RunOutline(m_sceneTarget, cameraData);
302
303 m_postProcess.depthEdgeStrength = oldDepthStrength;
304 m_postProcess.normalEdgeStrength = oldNormalStrength;
305 }
306
307 RenderTarget::BindDefault(winW, winH);
308 GLuint tex = m_sceneTarget.ColorTex();
310 switch (m_renderSettings.debugView)
311 {
313 tex = m_sceneTarget.NormalTex();
314 mode = BlitMode::DecodeNrm;
315 break;
316 case DebugView::Depth:
317 tex = m_sceneTarget.DepthTex();
318 mode = BlitMode::SplatRed;
319 break;
320 case DebugView::Color:
321 default:
322 if (runPostProcess && m_postProcess.OutputTex() != 0)
323 {
324 tex = m_postProcess.OutputTex();
325 }
326 break;
327 }
328 BlitNearest(tex, winW, winH, mode);
329 }
330
331 if (m_renderSettings.showFps)
332 {
333 const float instantFps = deltaTime > 0.0F ? 1.0F / deltaTime : 0.0F;
334 const float emaAlpha = 0.1F;
335 m_fpsSmoothed = (m_fpsSmoothed <= 0.0F)
336 ? instantFps
337 : m_fpsSmoothed + (instantFps - m_fpsSmoothed) * emaAlpha;
338 m_fpsRefreshTimer += deltaTime;
339 if (m_fpsRefreshTimer >= 0.2F)
340 {
341 m_fpsDisplayed = m_fpsSmoothed;
342 m_fpsRefreshTimer = 0.0F;
343 }
344
345 char fpsBuf[64];
346 std::snprintf(fpsBuf, sizeof(fpsBuf), "FPS %.0f %.2fms", m_fpsDisplayed,
347 m_fpsDisplayed > 0.0F ? 1000.0F / m_fpsDisplayed : 0.0F);
348
349 const float padding = 12.0F;
350 const float textSize = 22.0F;
351 const float boxWidth = textSize * 0.55F * static_cast<float>(std::strlen(fpsBuf)) + padding * 2.0F;
352 const float boxHeight = textSize + padding;
353 m_spriteRenderer.DrawRect(vec2(padding, padding),
354 vec2(boxWidth, boxHeight),
355 vec4(0.04F, 0.05F, 0.07F, 0.65F));
356 m_spriteRenderer.DrawText(fpsBuf,
357 vec2(padding + padding * 0.5F + 1.0F,
358 padding + padding * 0.5F * 0.5F + 1.0F),
359 textSize,
360 vec4(0.0F, 0.0F, 0.0F, 0.85F));
361 m_spriteRenderer.DrawText(fpsBuf,
362 vec2(padding + padding * 0.5F,
363 padding + padding * 0.5F * 0.5F),
364 textSize,
365 vec4(0.85F, 1.0F, 0.85F, 1.0F));
366 }
367
368 m_spriteRenderer.Flush(winW, winH);
369
370 // Editor overlays the scene on the default framebuffer.
371 m_editor.Draw();
372 m_editor.EndFrame();
373
374 glfwSwapBuffers(m_window);
375
376 m_inputManager.SetMousePositionOld(m_inputManager.GetMousePositionCurrent());
377
378 m_inputManager.SetMousePositionChanged(false);
379 }
380}
381
383{
384 LOG_INFO("Engine::Destroy requested");
385 if (m_application)
386 {
387 m_editor.Shutdown();
388 m_particleSystem.Shutdown();
389 m_spriteRenderer.Shutdown();
390 m_postProcess.Destroy();
391 m_sceneTarget.Destroy();
392 m_application->Destroy();
393 m_application.reset();
394 glfwTerminate();
395 m_window = nullptr;
396 LOG_INFO("Engine shut down");
397 } else
398 {
399 LOG_WARN("Engine::Destroy called with no application");
400 }
401}
402
404{
405 m_application.reset(app);
406}
407
409{
410 return m_application.get();
411}
412
414{
415 return m_inputManager;
416}
417
419{
420 return m_graphicsAPI;
421}
422
424{
425 return m_renderQueue;
426}
427
429{
430 m_currentScene.reset(scene);
431}
432
434{
435 return m_textureManager;
436}
437
439{
440 return m_audioManager;
441}
442
444{
445 return m_physicsManager;
446}
447
449{
450 return m_currentScene.get();
451}
452
454{
455 return m_fileSystem;
456}
457
458namespace
459{
460void RenderLoadingFrameImpl(GLFWwindow *window, const char *message, float progress)
461{
462 glfwPollEvents();
463
464 int winW = 0, winH = 0;
465 glfwGetFramebufferSize(window, &winW, &winH);
466
467 RenderTarget::BindDefault(winW, winH);
468 glClearColor(0.05F, 0.05F, 0.07F, 1.0F);
469 glClear(GL_COLOR_BUFFER_BIT);
470
471 ImGui_ImplOpenGL3_NewFrame();
472 ImGui_ImplGlfw_NewFrame();
473 ImGui::NewFrame();
474
475 const ImGuiViewport *vp = ImGui::GetMainViewport();
476 ImGui::SetNextWindowPos(ImVec2(vp->WorkPos.x + vp->WorkSize.x * 0.5F, vp->WorkPos.y + vp->WorkSize.y * 0.5F),
477 ImGuiCond_Always,
478 ImVec2(0.5F, 0.5F));
479 ImGui::SetNextWindowSize(ImVec2(420.0F, 0.0F), ImGuiCond_Always);
480 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(32.0F, 24.0F));
481 ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.10F, 0.10F, 0.13F, 1.0F));
482 ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(0.35F, 0.65F, 1.0F, 1.0F));
483 ImGui::Begin("##loading",
484 nullptr,
485 ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove
486 | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNav
487 | ImGuiWindowFlags_NoFocusOnAppearing);
488
489 ImGui::SetWindowFontScale(1.6F);
490 ImGui::TextUnformatted(message);
491 ImGui::SetWindowFontScale(1.0F);
492 ImGui::Spacing();
493
494 float barFraction = (progress < 0.0F) ? -1.0F * static_cast<float>(ImGui::GetTime()) : progress;
495 char overlay[16] = "";
496 if (progress >= 0.0F)
497 {
498 std::snprintf(overlay, sizeof(overlay), "%d%%", static_cast<int>(progress * 100.0F + 0.5F));
499 }
500 ImGui::ProgressBar(barFraction, ImVec2(-1.0F, 18.0F), progress >= 0.0F ? overlay : "");
501
502 ImGui::End();
503 ImGui::PopStyleColor(2);
504 ImGui::PopStyleVar();
505
506 ImGui::Render();
507 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
508
509 glfwSwapBuffers(window);
510}
511} // namespace
512
513void Engine::DrawLoadingScreen(const char *message, float progress)
514{
515 if (m_window == nullptr)
516 {
517 return;
518 }
519 // Swap twice: on some platforms/compositors the very first swap after
520 // window creation isn't presented until the next frame is queued.
521 RenderLoadingFrameImpl(m_window, message, progress);
522 RenderLoadingFrameImpl(m_window, message, progress);
523}
524
525void Engine::UpdateLoadingProgress(float progress, const char *message)
526{
527 if (m_window == nullptr)
528 {
529 return;
530 }
531 RenderLoadingFrameImpl(m_window, message, progress);
532}
533
534} // namespace mnd
constexpr mnd::i32 kDefaultInternalHeight
Definition Constants.h:44
constexpr const char * kDefaultWindowTitle
Definition Constants.h:27
constexpr mnd::i32 kDefaultInternalWidth
Definition Constants.h:43
unsigned int GLenum
Definition GLForward.h:12
unsigned int GLuint
Definition GLForward.h:11
Console logging macros for all engine and game code.
#define LOG_WARN(fmt,...)
Definition Log.h:80
#define LOG_INFO(fmt,...)
Definition Log.h:79
#define LOG_ERROR(fmt,...)
Definition Log.h:81
Interface that every runnable game or lab must implement.
Definition Application.h:36
Global audio backend wrapper.
bool Init()
Initialise the underlying miniaudio engine.
Perspective camera — provides view and projection matrices each frame.
void EndFrame()
Renders the ImGui draw data to the current framebuffer.
Definition Editor.cpp:241
void Shutdown()
Tear down ImGui state. Call before destroying the GL context.
Definition Editor.cpp:60
bool Init(GLFWwindow *window)
Initialise ImGui against the given GLFW window. Call once at startup.
Definition Editor.cpp:35
void BeginFrame()
Call after glfwPollEvents.
Definition Editor.cpp:72
void Draw()
Builds all panels (no GL state changes yet).
Definition Editor.cpp:182
Singleton façade that drives the full engine lifecycle.
Definition Engine.h:65
GraphicsAPI & GetGraphicsAPI()
Returns the low-level OpenGL wrapper used for all draw calls.
Definition Engine.cpp:418
RenderQueue & GetRenderQueue()
Returns the per-frame command queue that batches and issues draw calls.
Definition Engine.cpp:423
Scene * GetScene()
Returns the currently active scene, or nullptr if none is set.
Definition Engine.cpp:448
InputManager & GetInputManager()
Returns the InputManager that tracks keyboard and mouse state.
Definition Engine.cpp:413
FileSystem & GetFileSystem()
Returns the file system helper for asset-relative path resolution.
Definition Engine.cpp:453
void SetScene(Scene *scene)
Replace the active scene (takes ownership).
Definition Engine.cpp:428
bool Init(int width, int height)
Initialise the window, OpenGL context, and subsystems.
Definition Engine.cpp:65
void Run()
Enter the blocking main loop.
Definition Engine.cpp:174
Application * GetApplication()
Returns a raw pointer to the active application (owned by the engine).
Definition Engine.cpp:408
AudioManager & GetAudioManager()
Definition Engine.cpp:438
static Engine & GetInstance()
Returns the single Engine instance (created on first call).
Definition Engine.cpp:59
void SetApplication(Application *app)
Transfer ownership of the application to the engine.
Definition Engine.cpp:403
void DrawLoadingScreen(const char *message, float progress=-1.0F)
Definition Engine.cpp:513
TextureManager & GetTextureManager()
Returns the TextureManager that caches loaded textures by path.
Definition Engine.cpp:433
void UpdateLoadingProgress(float progress, const char *message)
Update the loading panel mid-load (single swap). Cheap to call per step.
Definition Engine.cpp:525
PhysicsManager & GetPhysicsManager()
Definition Engine.cpp:443
void Destroy()
Shut down the application and release all GL resources.
Definition Engine.cpp:382
Resolves asset paths and loads files into memory.
Definition FileSystem.h:28
OpenGL 3.3 Core Profile abstraction layer.
Definition GraphicsAPI.h:41
void ClearBuffers()
Clear the colour and depth buffers ready for the next frame.
void SetClearColor(float r, float g, float b, float a)
Set the colour that glClear() fills the framebuffer with.
Stores per-frame keyboard and mouse input state.
void SetMouseButtonPressed(MouseButton button, bool pressed)
Record a mouse-button press or release event.
void SetMousePositionChanged(bool changed)
const vec2 GetMousePositionCurrent() const
Retrieve the cursor position for the current frame.
void SetMousePositionOld(const vec2 &pos)
Store the cursor position from the previous frame.
void SetKeyPressed(Key key, bool pressed)
Record a key press or release event.
void Update(f32 deltaTime)
Step every live particle, swap-erase dead ones.
void Render(const CameraData &cameraData)
Camera-aligned billboard pass. Call after the lit pass, before UI.
Owns the Bullet btDiscreteDynamicsWorld and all auxiliary collision structures (broadphase,...
void Update(float deltaTime)
Step the simulation by deltaTime seconds.
void Init()
Allocate broadphase, dispatcher, solver, and dynamics world. Call once.
bool IsValid() const
Definition PostProcess.h:39
float normalEdgeStrength
Definition PostProcess.h:43
float depthEdgeStrength
Definition PostProcess.h:42
void RunOutline(const RenderTarget &scene, const CameraData &cam)
Run the outline pass, sampling scene and writing to OutputTex().
GLuint OutputTex() const
Definition PostProcess.h:38
Accumulates RenderCommands during Update and draws them in one pass.
Definition RenderQueue.h:57
void Draw(GraphicsAPI &graphicsAPI, const CameraData &cameraData, const std::vector< LightData > &lights)
Draw all submitted commands, then clear the queue.
static void BindDefault(int winW, int winH)
bool IsValid() const
GLuint NormalTex() const
GLuint DepthTex() const
bool Create(int w, int h)
void Resize(int w, int h)
GLuint ColorTex() const
Container for the entire game object graph.
Definition Scene.h:59
static void RegisterTypes()
Update all root objects (which recursively update their children).
Definition Scene.cpp:29
void DrawText(const std::string &text, const vec2 &position, f32 pixelHeight, const vec4 &color=vec4(1.0F))
void DrawRect(const vec2 &position, const vec2 &size, const vec4 &color)
void Flush(int viewportWidth, int viewportHeight)
Path-keyed cache that prevents the same image from being uploaded twice.
Definition Texture.h:94
Abstract base class for the user-defined game or lab application.
Lightweight data-transfer structs shared across subsystems.
Central singleton that owns and coordinates all engine subsystems.
glm::vec2 vec2
2-component float vector (e.g. UV coordinates, mouse position).
Definition Types.h:50
glm::vec4 vec4
4-component float vector (e.g. RGBA colour, homogeneous coords).
Definition Types.h:52
double f64
64-bit double — used for high-precision timing.
Definition Types.h:41
float f32
32-bit IEEE float — the standard GL scalar type.
Definition Types.h:40
Key
Keyboard key + mouse button identifier (GLFW-compatible values).
Definition Key.h:29
BlitMode
Blit modes for BlitNearest.
@ SplatRed
Output (r,r,r,1) — used for visualising depth.
@ DecodeNrm
Output rgb as-is from a packed-normal texture (no remap).
@ Color
Sample RGB straight through (default).
void BlitNearest(GLuint srcTexture, int dstW, int dstH, BlitMode mode)
void CursorPositionCallback(GLFWwindow *window, f64 xpos, f64 ypos)
Definition Engine.cpp:48
void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
Definition Engine.cpp:36
MouseButton
Definition Key.h:149
void KeyCallback(GLFWwindow *window, int key, int, int action, int)
Definition Engine.cpp:24
std::unique_ptr< RendererBackend > CreateOpenGLRendererBackend(GraphicsAPI &graphicsAPI)
Per-frame camera matrices and world-space position.
Definition Common.h:35
mat4 projectionMatrix
Camera → Clip space transform (perspective or ortho).
Definition Common.h:37
mat4 viewMatrix
World → Camera space transform (glm::lookAt result).
Definition Common.h:36
vec3 position
Camera world-space position, used for specular calculations.
Definition Common.h:38
f32 nearPlane
Frustum near distance — used by post passes for depth linearisation.
Definition Common.h:39
vec4 clearColor
RGBA value used by glClear at the start of each frame.
DebugView debugView
Which scene-target attachment to blit. Defaults to Color.
bool showFps
Draw FPS counter overlay in top-left corner.