#include #include #include #include #include #include /// Application state. typedef struct GfxApp { GfxAppState* 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 time_delta min_frame_time = app->max_fps > 0 ? sec_to_time_delta(1.0 / (double)(app->max_fps)) : 0; const time_delta update_dt = sec_to_time_delta(app->update_delta_time); time_delta time = 0; time_delta time_budget = 0; Timer timer = timer_make(); // Warm up the update to initialize the application's state. (*app->callbacks.update)( app->app_state, time_delta_to_sec(time), time_delta_to_sec(update_dt)); // 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 += timer.delta_time; while (time_budget >= update_dt) { (*app->callbacks.update)( app->app_state, time_delta_to_sec(time), time_delta_to_sec(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) && (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.app_state = desc->app_state; 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); const char* title = desc->title ? desc->title : "Gfx Application"; g_gfx_app.window = glfwCreateWindow(desc->width, desc->height, title, 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)( g_gfx_app.app_state, desc->argc, desc->argv)) { 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; } void gfx_app_get_mouse_position(double* x, double* y) { glfwGetCursorPos(g_gfx_app.window, x, y); } static int to_glfw_key(Key key); bool gfx_is_key_pressed(Key key) { return glfwGetKey(g_gfx_app.window, to_glfw_key(key)) == GLFW_PRESS; } static int to_glfw_key(Key key) { switch (key) { case KeyA: return GLFW_KEY_A; case KeyB: return GLFW_KEY_B; case KeyC: return GLFW_KEY_C; case KeyD: return GLFW_KEY_D; case KeyE: return GLFW_KEY_E; case KeyF: return GLFW_KEY_F; case KeyG: return GLFW_KEY_G; case KeyH: return GLFW_KEY_H; case KeyI: return GLFW_KEY_I; case KeyJ: return GLFW_KEY_J; case KeyK: return GLFW_KEY_K; case KeyL: return GLFW_KEY_L; case KeyM: return GLFW_KEY_M; case KeyN: return GLFW_KEY_N; case KeyO: return GLFW_KEY_O; case KeyP: return GLFW_KEY_P; case KeyQ: return GLFW_KEY_Q; case KeyR: return GLFW_KEY_R; case KeyS: return GLFW_KEY_S; case KeyT: return GLFW_KEY_T; case KeyU: return GLFW_KEY_U; case KeyV: return GLFW_KEY_V; case KeyW: return GLFW_KEY_W; case KeyX: return GLFW_KEY_X; case KeyY: return GLFW_KEY_Y; case KeyZ: return GLFW_KEY_Z; } }