1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
#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 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.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)(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;
}
void gfx_app_get_mouse_position(double* x, double* y) {
glfwGetCursorPos(g_gfx_app.window, x, y);
}
|