From 1e3fcf5b38d67fb54102786be74af42be5c6792f Mon Sep 17 00:00:00 2001
From: 3gg <3gg@shellblade.net>
Date: Tue, 3 Jan 2023 08:49:54 -0800
Subject: Initial commit.

---
 gfx-app/src/gfx_app.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)
 create mode 100644 gfx-app/src/gfx_app.c

(limited to 'gfx-app/src')

diff --git a/gfx-app/src/gfx_app.c b/gfx-app/src/gfx_app.c
new file mode 100644
index 0000000..f3f0473
--- /dev/null
+++ b/gfx-app/src/gfx_app.c
@@ -0,0 +1,126 @@
+#include <gfx/gfx_app.h>
+
+#include <GLFW/glfw3.h>
+#include <log/log.h>
+#include <timer.h>
+
+#include <assert.h>
+#include <stdlib.h>
+
+/// Application state.
+typedef struct GfxApp {
+  void* app_state;
+  GfxAppCallbacks callbacks;
+  int max_fps;
+  double update_delta_time;
+  GLFWwindow* window;
+} GfxApp;
+
+/// Storing the application state in a global variable so that we can call the
+/// application's callbacks from GLFW callbacks.
+static GfxApp g_gfx_app;
+
+/// Called by GLFW when the window is resized.
+static void on_resize(GLFWwindow* window, int width, int height) {
+  (*g_gfx_app.callbacks.resize)(g_gfx_app.app_state, width, height);
+}
+
+/// Run the application's main loop.
+static void loop(GfxApp* app) {
+  assert(app);
+  assert(app->window);
+
+  const double min_frame_time =
+      app->max_fps > 0 ? 1.0 / (double)(app->max_fps) : 0.0;
+  const double update_dt = app->update_delta_time;
+  double time = 0.0;
+  double time_budget = 0.0;
+  Timer timer = timer_make();
+
+  // Warm up the rendering before entering the main loop. A renderer can compile
+  // shaders and do other initialization the first time it renders a scene.
+  (*app->callbacks.render)(app->app_state);
+  glfwSwapBuffers(app->window);
+
+  timer_start(&timer);
+  while (!glfwWindowShouldClose(app->window)) {
+    timer_tick(&timer);
+    time_budget += time_delta_to_sec(timer.delta_time);
+
+    while (time_budget >= update_dt) {
+      (*app->callbacks.update)(app->app_state, time, update_dt);
+      time += update_dt;
+      time_budget -= update_dt;
+    }
+
+    (*app->callbacks.render)(app->app_state);
+    glfwSwapBuffers(app->window);
+    glfwPollEvents();
+
+    const time_point frame_end = time_now();
+    const time_delta frame_time = time_diff(timer.last_tick, frame_end);
+    if (min_frame_time > 0.0 && frame_time < min_frame_time) {
+      time_sleep(min_frame_time - frame_time);
+    }
+  }
+}
+
+bool gfx_app_run(const GfxAppDesc* desc, const GfxAppCallbacks* callbacks) {
+  assert(desc);
+  assert(callbacks);
+
+  bool success = false;
+
+  g_gfx_app.callbacks = *callbacks;
+  g_gfx_app.max_fps = desc->max_fps;
+  g_gfx_app.update_delta_time = desc->update_delta_time;
+  g_gfx_app.window = 0;
+
+  if (!glfwInit()) {
+    LOGE("glfwInit() failed");
+    return false;
+  }
+
+  const int major = 4;
+  const int minor = 4;
+  glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
+  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, major);
+  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, minor);
+  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+  glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
+  // TODO: Test antialiasing later on.
+  // glfwWindowHint(GLFW_SAMPLES, 4);
+
+  g_gfx_app.window =
+      glfwCreateWindow(desc->width, desc->height, "space", NULL, NULL);
+  if (!g_gfx_app.window) {
+    LOGE("glfwCreateWindow() failed");
+    goto cleanup;
+  }
+  glfwMakeContextCurrent(g_gfx_app.window);
+
+  // Initialize the application's state before setting any callbacks.
+  if (!(*g_gfx_app.callbacks.init)(desc, &g_gfx_app.app_state)) {
+    LOGE("Failed to initialize application");
+    goto cleanup;
+  }
+
+  // Trigger an initial resize for convenience.
+  (*g_gfx_app.callbacks.resize)(g_gfx_app.app_state, desc->width, desc->height);
+
+  // Set GLFW callbacks now that the application has been initialized.
+  glfwSetWindowSizeCallback(g_gfx_app.window, on_resize);
+
+  loop(&g_gfx_app);
+
+  (*g_gfx_app.callbacks.shutdown)(g_gfx_app.app_state);
+
+  success = true;
+
+cleanup:
+  if (g_gfx_app.window) {
+    glfwDestroyWindow(g_gfx_app.window);
+  }
+  glfwTerminate();
+  return success;
+}
-- 
cgit v1.2.3