diff options
-rw-r--r-- | src/main.c | 210 |
1 files changed, 122 insertions, 88 deletions
@@ -7,6 +7,8 @@ | |||
7 | 7 | ||
8 | #include "shorthand.h" | 8 | #include "shorthand.h" |
9 | 9 | ||
10 | #define TEXT_OVERLAY_SIZE 5 | ||
11 | |||
10 | // | 12 | // |
11 | // Callbacks. | 13 | // Callbacks. |
12 | // | 14 | // |
@@ -27,6 +29,13 @@ glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mod | |||
27 | } | 29 | } |
28 | } | 30 | } |
29 | 31 | ||
32 | // Letter for a the monospace text overlay layer. | ||
33 | struct Letter { | ||
34 | f32 x; | ||
35 | f32 y; | ||
36 | f32 idx; | ||
37 | }; | ||
38 | |||
30 | typedef struct Context { | 39 | typedef struct Context { |
31 | GLFWwindow *window; | 40 | GLFWwindow *window; |
32 | u32 gl_version_major; | 41 | u32 gl_version_major; |
@@ -41,6 +50,20 @@ typedef struct Context { | |||
41 | f64 elapsed; | 50 | f64 elapsed; |
42 | size_t n_frames; | 51 | size_t n_frames; |
43 | } frame_time; | 52 | } frame_time; |
53 | |||
54 | struct { | ||
55 | u32 program; | ||
56 | u32 tex_id; | ||
57 | u32 vao; | ||
58 | u32 vbo; | ||
59 | u32 ebo; | ||
60 | |||
61 | struct Letter letters[2048]; | ||
62 | u32 cur_x; | ||
63 | u32 cur_y; | ||
64 | u32 n_chars; | ||
65 | u32 size; | ||
66 | } text_overlay; | ||
44 | } Context; | 67 | } Context; |
45 | 68 | ||
46 | u32 | 69 | u32 |
@@ -133,53 +156,6 @@ init_context(Context *ctx) { | |||
133 | 156 | ||
134 | void | 157 | void |
135 | init_debug_overlay(Context *ctx) { | 158 | init_debug_overlay(Context *ctx) { |
136 | // TODO: Add debug text to context and initialize here | ||
137 | (void)ctx; | ||
138 | } | ||
139 | |||
140 | void | ||
141 | setup_callbacks(Context *ctx) { | ||
142 | glfwSetErrorCallback(glfw_error_callback); | ||
143 | glfwSetKeyCallback(ctx->window, glfw_key_callback); | ||
144 | } | ||
145 | |||
146 | void | ||
147 | update_frame_time(Context *ctx) { | ||
148 | // Measure frame times and fps. | ||
149 | f64 cur_time = glfwGetTime(); | ||
150 | f64 delta_time = cur_time - ctx->frame_time.prev; | ||
151 | ctx->frame_time.elapsed += delta_time; | ||
152 | ctx->frame_time.n_frames++; | ||
153 | if (ctx->frame_time.elapsed >= 1.0) { | ||
154 | f64 fps = 0; | ||
155 | fps = ctx->frame_time.n_frames / ctx->frame_time.elapsed; | ||
156 | ctx->frame_time.elapsed = 0; | ||
157 | ctx->frame_time.n_frames = 0; | ||
158 | |||
159 | // Update title with timing data. | ||
160 | char title_buf[256 * 2]; | ||
161 | sprintf(title_buf, | ||
162 | "%s [%.2fms, %.2f FPS]", | ||
163 | ctx->win_title, delta_time * 1000, fps); | ||
164 | glfwSetWindowTitle(ctx->window, title_buf); | ||
165 | } | ||
166 | ctx->frame_time.prev = cur_time; | ||
167 | } | ||
168 | |||
169 | void | ||
170 | update(Context *ctx) { | ||
171 | update_frame_time(ctx); | ||
172 | } | ||
173 | |||
174 | void | ||
175 | render(Context *ctx) { | ||
176 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f); | ||
177 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | ||
178 | |||
179 | glfwGetFramebufferSize(ctx->window, &ctx->win_width, &ctx->win_height); | ||
180 | glViewport(0, 0, ctx->win_width, ctx->win_height); | ||
181 | |||
182 | // TODO: Only need to be done once on the initialization function. | ||
183 | // Prepare quad rendering. | 159 | // Prepare quad rendering. |
184 | f32 vertices[] = { | 160 | f32 vertices[] = { |
185 | // position // tex_coords | 161 | // position // tex_coords |
@@ -194,9 +170,8 @@ render(Context *ctx) { | |||
194 | }; | 170 | }; |
195 | 171 | ||
196 | // Vertex array object. | 172 | // Vertex array object. |
197 | u32 vao; | 173 | glGenVertexArrays(1, &ctx->text_overlay.vao); |
198 | glGenVertexArrays(1, &vao); | 174 | glBindVertexArray(ctx->text_overlay.vao); |
199 | glBindVertexArray(vao); | ||
200 | 175 | ||
201 | // Vertex buffer object setup. | 176 | // Vertex buffer object setup. |
202 | u32 vbo; | 177 | u32 vbo; |
@@ -208,33 +183,19 @@ render(Context *ctx) { | |||
208 | glEnableVertexAttribArray(1); | 183 | glEnableVertexAttribArray(1); |
209 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), (void*)(2 * sizeof(f32))); | 184 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), (void*)(2 * sizeof(f32))); |
210 | 185 | ||
211 | // NOTE: This should be a dynamic array of offsets and index into the | 186 | glGenBuffers(1, &ctx->text_overlay.vbo); |
212 | // texture atlas. | 187 | glBindBuffer(GL_ARRAY_BUFFER, ctx->text_overlay.vbo); |
213 | struct Letter { | ||
214 | f32 x; | ||
215 | f32 y; | ||
216 | f32 idx; | ||
217 | }; | ||
218 | struct Letter letters[] = { | ||
219 | {0, 0.0, 0}, | ||
220 | {1, 0.0, 1}, | ||
221 | {2, 0.0, 2}, | ||
222 | }; | ||
223 | u32 offset_vbo; | ||
224 | glGenBuffers(1, &offset_vbo); | ||
225 | glBindBuffer(GL_ARRAY_BUFFER, offset_vbo); | ||
226 | glBufferData(GL_ARRAY_BUFFER, sizeof(letters), letters, GL_STATIC_DRAW); | ||
227 | glEnableVertexAttribArray(2); | 188 | glEnableVertexAttribArray(2); |
228 | glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(f32), (void*)0); | 189 | glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(f32), (void*)0); |
229 | glVertexAttribDivisor(2, 1); | 190 | glVertexAttribDivisor(2, 1); |
230 | 191 | ||
231 | // Element buffer object setup. | 192 | // Element buffer object setup. |
232 | u32 ebo; | 193 | glGenBuffers(1, &ctx->text_overlay.ebo); |
233 | glGenBuffers(1, &ebo); | 194 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ctx->text_overlay.ebo); |
234 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); | ||
235 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); | 195 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); |
236 | 196 | ||
237 | // Texture atlas. | 197 | // Texture atlas. |
198 | // TODO: Full 256 ascii font. | ||
238 | u8 texture[] = { | 199 | u8 texture[] = { |
239 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 200 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
240 | 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, | 201 | 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, |
@@ -245,9 +206,8 @@ render(Context *ctx) { | |||
245 | 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, | 206 | 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, |
246 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 207 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
247 | }; | 208 | }; |
248 | u32 tex_id; | 209 | glGenTextures(1, &ctx->text_overlay.tex_id); |
249 | glGenTextures(1, &tex_id); | 210 | glBindTexture(GL_TEXTURE_2D, ctx->text_overlay.tex_id); |
250 | glBindTexture(GL_TEXTURE_2D, tex_id); | ||
251 | glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 8 * 3, 8, 0, GL_RED, | 211 | glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 8 * 3, 8, 0, GL_RED, |
252 | GL_UNSIGNED_BYTE, &texture); | 212 | GL_UNSIGNED_BYTE, &texture); |
253 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 213 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
@@ -266,21 +226,24 @@ render(Context *ctx) { | |||
266 | "layout (location = 1) in vec2 tex_coords;\n" | 226 | "layout (location = 1) in vec2 tex_coords;\n" |
267 | "layout (location = 2) in vec3 offset;\n" | 227 | "layout (location = 2) in vec3 offset;\n" |
268 | "out vec2 tex;\n" | 228 | "out vec2 tex;\n" |
269 | "void main() {\n" | ||
270 | // TODO: Uniforms | ||
271 | " float w = 800;\n" | ||
272 | " float h = 600;\n" | ||
273 | " float size = 5;\n" | ||
274 | " float N = 3;\n" | ||
275 | 229 | ||
230 | "uniform float w;\n" | ||
231 | "uniform float h;\n" | ||
232 | "uniform float size;\n" | ||
233 | |||
234 | "void main() {\n" | ||
235 | " float N = 3;\n" // TODO: should be 256 for the full font. | ||
276 | " float idx = offset.z;\n" | 236 | " float idx = offset.z;\n" |
277 | " float m = 1.0 / N;\n" | 237 | " float m = 1.0 / N;\n" |
278 | " float k = m * idx;\n" | 238 | " float k = m * idx;\n" |
239 | |||
279 | // TODO: Only 8x8 monospace fonts for now | 240 | // TODO: Only 8x8 monospace fonts for now |
280 | " float font_w = 8;\n" | 241 | " float font_w = 8;\n" |
281 | " float font_h = 8;\n" | 242 | " float font_h = 8;\n" |
243 | |||
282 | // NOTE: This makes sures that the fonts have the right size. | 244 | // NOTE: This makes sures that the fonts have the right size. |
283 | " vec2 scalar = vec2(font_w / w, font_h / h) * size;\n" | 245 | " vec2 scalar = vec2(font_w / w, font_h / h) * size;\n" |
246 | |||
284 | " vec2 pos = tex_coords * scalar;\n" | 247 | " vec2 pos = tex_coords * scalar;\n" |
285 | " pos += vec2(0.0, 1.0 - scalar.y);\n" | 248 | " pos += vec2(0.0, 1.0 - scalar.y);\n" |
286 | " pos += scalar * offset.xy;\n" | 249 | " pos += scalar * offset.xy;\n" |
@@ -300,14 +263,87 @@ render(Context *ctx) { | |||
300 | " frag = vec4(val, val, val, 1.0);\n" | 263 | " frag = vec4(val, val, val, 1.0);\n" |
301 | "}\n"; | 264 | "}\n"; |
302 | 265 | ||
303 | u32 program = compile_program(ctx, vert_src, frag_src); | 266 | ctx->text_overlay.program = compile_program(ctx, vert_src, frag_src); |
267 | ctx->text_overlay.cur_x = 0; | ||
268 | ctx->text_overlay.cur_y = 0; | ||
269 | ctx->text_overlay.n_chars = 0; | ||
270 | ctx->text_overlay.size = TEXT_OVERLAY_SIZE; | ||
271 | } | ||
272 | |||
273 | void | ||
274 | setup_callbacks(Context *ctx) { | ||
275 | glfwSetErrorCallback(glfw_error_callback); | ||
276 | glfwSetKeyCallback(ctx->window, glfw_key_callback); | ||
277 | } | ||
278 | |||
279 | void | ||
280 | update_frame_time(Context *ctx) { | ||
281 | // Measure frame times and fps. | ||
282 | f64 cur_time = glfwGetTime(); | ||
283 | f64 delta_time = cur_time - ctx->frame_time.prev; | ||
284 | ctx->frame_time.elapsed += delta_time; | ||
285 | ctx->frame_time.n_frames++; | ||
286 | if (ctx->frame_time.elapsed >= 1.0) { | ||
287 | f64 fps = 0; | ||
288 | fps = ctx->frame_time.n_frames / ctx->frame_time.elapsed; | ||
289 | ctx->frame_time.elapsed = 0; | ||
290 | ctx->frame_time.n_frames = 0; | ||
291 | |||
292 | // Update title with timing data. | ||
293 | char title_buf[256 * 2]; | ||
294 | sprintf(title_buf, | ||
295 | "%s [%.2fms, %.2f FPS]", | ||
296 | ctx->win_title, delta_time * 1000, fps); | ||
297 | glfwSetWindowTitle(ctx->window, title_buf); | ||
298 | } | ||
299 | ctx->frame_time.prev = cur_time; | ||
300 | } | ||
301 | |||
302 | void | ||
303 | update_text(Context *ctx) { | ||
304 | // TODO: Test with the full font atlas and characters. | ||
305 | // TODO: add_text function for setting up letters[] based on a char* + n_chars. | ||
306 | ctx->text_overlay.letters[0].x = 0; | ||
307 | ctx->text_overlay.letters[0].y = 0; | ||
308 | ctx->text_overlay.letters[0].idx = 0; | ||
309 | ctx->text_overlay.letters[1].x = 1; | ||
310 | ctx->text_overlay.letters[1].y = 0; | ||
311 | ctx->text_overlay.letters[1].idx = 1; | ||
312 | ctx->text_overlay.letters[2].x = 2; | ||
313 | ctx->text_overlay.letters[2].y = 0; | ||
314 | ctx->text_overlay.letters[2].idx = 2; | ||
315 | ctx->text_overlay.n_chars = 3; | ||
316 | glBindBuffer(GL_ARRAY_BUFFER, ctx->text_overlay.vbo); | ||
317 | glBufferData(GL_ARRAY_BUFFER, | ||
318 | sizeof(struct Letter) * ctx->text_overlay.n_chars, | ||
319 | ctx->text_overlay.letters, | ||
320 | GL_STATIC_DRAW); | ||
321 | } | ||
322 | |||
323 | void | ||
324 | update(Context *ctx) { | ||
325 | update_frame_time(ctx); | ||
326 | update_text(ctx); | ||
327 | } | ||
328 | |||
329 | void | ||
330 | render(Context *ctx) { | ||
331 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f); | ||
332 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | ||
333 | |||
334 | glfwGetFramebufferSize(ctx->window, &ctx->win_width, &ctx->win_height); | ||
335 | glViewport(0, 0, ctx->win_width, ctx->win_height); | ||
336 | |||
337 | glUseProgram(ctx->text_overlay.program); | ||
338 | glBindVertexArray(ctx->text_overlay.vao); | ||
339 | glBindTexture(GL_TEXTURE_2D, ctx->text_overlay.tex_id); | ||
304 | 340 | ||
305 | glUseProgram(program); | 341 | // Update uniforms. |
306 | glBindVertexArray(vao); | 342 | glUniform1f(glGetUniformLocation(ctx->text_overlay.program, "w"), ctx->win_width); |
307 | glBindTexture(GL_TEXTURE_2D, tex_id); | 343 | glUniform1f(glGetUniformLocation(ctx->text_overlay.program, "h"), ctx->win_height); |
344 | glUniform1f(glGetUniformLocation(ctx->text_overlay.program, "size"), ctx->text_overlay.size); | ||
308 | 345 | ||
309 | // TODO: How to pass the parameters for each box? | 346 | // TODO: Which blend mode to use? |
310 | // NOTE: We want to render the text as a single draw call if possible. | ||
311 | // glEnable(GL_BLEND); | 347 | // glEnable(GL_BLEND); |
312 | // glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); | 348 | // glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
313 | // // glBlendFunc(GL_ONE, GL_ONE); | 349 | // // glBlendFunc(GL_ONE, GL_ONE); |
@@ -322,10 +358,8 @@ render(Context *ctx) { | |||
322 | // // glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA); | 358 | // // glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA); |
323 | // // glBlendEquation(GL_FUNC_ADD); | 359 | // // glBlendEquation(GL_FUNC_ADD); |
324 | // | 360 | // |
325 | // glBindTexture(GL_TEXTURE_2D, text_tex); // TODO: Bind the texture atlas. | 361 | glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0, |
326 | // TODO: Send the number of instances as a uniform or see if you can query | 362 | ctx->text_overlay.n_chars); |
327 | // it from the GLSL for N. In this case N == 3. | ||
328 | glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0, 3); | ||
329 | // glDisable(GL_BLEND); | 363 | // glDisable(GL_BLEND); |
330 | 364 | ||
331 | glfwSwapBuffers(ctx->window); | 365 | glfwSwapBuffers(ctx->window); |