]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/client/gui.c
11fa50a4f585699e3bcbb033163ed0f962fc606e
[dragonblocks_alpha.git] / src / client / gui.c
1 #include <assert.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <GL/glew.h>
6 #include <GL/gl.h>
7 #include "client/client.h"
8 #include "client/cube.h"
9 #include "client/gui.h"
10 #include "client/mesh.h"
11 #include "client/shader.h"
12 #include "client/vertex.h"
13 #include "util.h"
14
15 static struct
16 {
17         List elements;
18
19         GLuint background_prog;
20         GLint background_loc_model;
21         GLint background_loc_projection;
22         GLint background_loc_color;
23         Mesh *background_mesh;
24
25         GLuint image_prog;
26         GLint image_loc_model;
27         GLint image_loc_projection;
28         Mesh *image_mesh;
29
30         GLuint font_prog;
31         GLint font_loc_model;
32         GLint font_loc_projection;
33         GLint font_loc_color;
34
35         mat4x4 projection;
36 } gui;
37
38 GUIElement gui_root;
39
40 typedef struct
41 {
42         GLfloat x, y;
43 } __attribute__((packed)) VertexBackgroundPosition;
44
45 typedef struct
46 {
47         VertexBackgroundPosition position;
48 } __attribute__((packed)) VertexBackground;
49
50 static VertexAttribute background_vertex_attributes[1] = {
51         // position
52         {
53                 .type = GL_FLOAT,
54                 .length = 2,
55                 .size = sizeof(VertexBackgroundPosition),
56         },
57 };
58
59 static VertexLayout background_vertex_layout = {
60         .attributes = background_vertex_attributes,
61         .count = 1,
62         .size = sizeof(VertexBackground),
63 };
64
65 static VertexBackground background_vertices[6] = {
66         {{0.0, 0.0}},
67         {{1.0, 0.0}},
68         {{1.0, 1.0}},
69         {{1.0, 1.0}},
70         {{0.0, 1.0}},
71         {{0.0, 0.0}},
72 };
73
74 typedef struct
75 {
76         GLfloat x, y;
77 } __attribute__((packed)) VertexImagePosition;
78
79 typedef struct
80 {
81         GLfloat s, t;
82 } __attribute__((packed)) VertexImageTextureCoordinates;
83
84 typedef struct
85 {
86         VertexImagePosition position;
87         VertexImageTextureCoordinates textureCoordinates;
88 } __attribute__((packed)) VertexImage;
89
90 static VertexAttribute image_vertex_attributes[2] = {
91         // position
92         {
93                 .type = GL_FLOAT,
94                 .length = 2,
95                 .size = sizeof(VertexImagePosition),
96         },
97         // textureCoordinates
98         {
99                 .type = GL_FLOAT,
100                 .length = 2,
101                 .size = sizeof(VertexImageTextureCoordinates),
102         },
103 };
104
105 static VertexLayout image_vertex_layout = {
106         .attributes = image_vertex_attributes,
107         .count = 2,
108         .size = sizeof(VertexImage),
109 };
110
111 static VertexImage image_vertices[6] = {
112         {{0.0, 0.0}, {0.0, 0.0}},
113         {{1.0, 0.0}, {1.0, 0.0}},
114         {{1.0, 1.0}, {1.0, 1.0}},
115         {{1.0, 1.0}, {1.0, 1.0}},
116         {{0.0, 1.0}, {0.0, 1.0}},
117         {{0.0, 0.0}, {0.0, 0.0}},
118 };
119
120 static int bintree_compare_f32(void *v1, void *v2, unused Bintree *tree)
121 {
122         f32 diff = (*(f32 *) v1) - (*(f32 *) v2);
123         return CMPBOUNDS(diff);
124 }
125
126 bool gui_init()
127 {
128         // initialize background pipeline
129
130         if (! shader_program_create(RESSOURCE_PATH "shaders/gui/background", &gui.background_prog, NULL)) {
131                 fprintf(stderr, "Failed to create GUI background shader program\n");
132                 return false;
133         }
134
135         gui.background_loc_model = glGetUniformLocation(gui.background_prog, "model");
136         gui.background_loc_projection = glGetUniformLocation(gui.background_prog, "projection");
137         gui.background_loc_color = glGetUniformLocation(gui.background_prog, "color");
138
139         gui.background_mesh = mesh_create();
140         gui.background_mesh->textures = NULL;
141         gui.background_mesh->textures_count = 0;
142         gui.background_mesh->free_textures = false;
143         gui.background_mesh->vertices = background_vertices;
144         gui.background_mesh->vertices_count = 6;
145         gui.background_mesh->free_vertices = false;
146         gui.background_mesh->layout = &background_vertex_layout;
147
148         // initialize image pipeline
149
150         if (! shader_program_create(RESSOURCE_PATH "shaders/gui/image", &gui.image_prog, NULL)) {
151                 fprintf(stderr, "Failed to create GUI image shader program\n");
152                 return false;
153         }
154
155         gui.image_loc_model = glGetUniformLocation(gui.image_prog, "model");
156         gui.image_loc_projection = glGetUniformLocation(gui.image_prog, "projection");
157
158         gui.image_mesh = mesh_create();
159         gui.image_mesh->textures = NULL;
160         gui.image_mesh->textures_count = 1;
161         gui.image_mesh->free_textures = false;
162         gui.image_mesh->vertices = image_vertices;
163         gui.image_mesh->vertices_count = 6;
164         gui.image_mesh->free_vertices = false;
165         gui.image_mesh->layout = &image_vertex_layout;
166
167         // initialize font pipeline
168
169         if (! shader_program_create(RESSOURCE_PATH "shaders/gui/font", &gui.font_prog, NULL)) {
170                 fprintf(stderr, "Failed to create GUI font shader program\n");
171                 return false;
172         }
173
174         gui.font_loc_model = glGetUniformLocation(gui.font_prog, "model");
175         gui.font_loc_projection = glGetUniformLocation(gui.font_prog, "projection");
176         gui.font_loc_color = glGetUniformLocation(gui.font_prog, "color");
177
178         // font meshes are initialized in font.c
179
180         // initialize GUI root element
181
182         gui_root.def.pos = (v2f32) {0.0f, 0.0f};
183         gui_root.def.z_index = 0.0f;
184         gui_root.def.offset = (v2s32) {0, 0};
185         gui_root.def.align = (v2f32) {0.0f, 0.0f};
186         gui_root.def.scale = (v2f32) {0.0f, 0.0f};
187         gui_root.def.scale_type = GST_NONE;
188         gui_root.def.affect_parent_scale = false;
189         gui_root.def.text = NULL;
190         gui_root.def.image = NULL;
191         gui_root.def.text_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f};
192         gui_root.def.bg_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f};
193         gui_root.visible = true;
194         gui_root.pos = (v2f32)  {0.0f, 0.0f};
195         gui_root.scale = (v2f32) {0.0f, 0.0f};
196         gui_root.text = NULL;
197         gui_root.parent = &gui_root;
198         gui_root.children = bintree_create(sizeof(f32), &bintree_compare_f32);
199
200         return true;
201 }
202
203 static void free_element(BintreeNode *node, unused void *arg)
204 {
205         GUIElement *element = node->value;
206
207         bintree_clear(&element->children, &free_element, NULL);
208
209         if (element->def.text)
210                 free(element->def.text);
211
212         if (element->text)
213                 font_delete(element->text);
214
215         free(element);
216 }
217
218 void gui_deinit()
219 {
220         glDeleteProgram(gui.background_prog);
221         mesh_delete(gui.background_mesh);
222
223         glDeleteProgram(gui.image_prog);
224         mesh_delete(gui.image_mesh);
225
226         glDeleteProgram(gui.font_prog);
227
228         bintree_clear(&gui_root.children, &free_element, NULL);
229 }
230
231 void gui_on_resize(int width, int height)
232 {
233         mat4x4_ortho(gui.projection, 0, width, height, 0, -1.0f, 1.0f);
234         glProgramUniformMatrix4fv(gui.background_prog, gui.background_loc_projection, 1, GL_FALSE, gui.projection[0]);
235         glProgramUniformMatrix4fv(gui.image_prog, gui.image_loc_projection, 1, GL_FALSE, gui.projection[0]);
236         glProgramUniformMatrix4fv(gui.font_prog, gui.font_loc_projection, 1, GL_FALSE, gui.projection[0]);
237
238         gui_root.def.scale.x = width;
239         gui_root.def.scale.y = height;
240
241         gui_update_transform(&gui_root);
242 }
243
244 static void render_element(BintreeNode *node, unused void *arg)
245 {
246         GUIElement *element = node->value;
247
248         if (element->visible) {
249                 if (element->def.bg_color.w > 0.0f) {
250                         glUseProgram(gui.background_prog);
251                         glUniformMatrix4fv(gui.background_loc_model, 1, GL_FALSE, element->transform[0]);
252                         glUniform4f(gui.background_loc_color, element->def.bg_color.x, element->def.bg_color.y, element->def.bg_color.z, element->def.bg_color.w);
253                         mesh_render(gui.background_mesh);
254                 }
255
256                 if (element->def.image) {
257                         glUseProgram(gui.image_prog);
258                         glUniformMatrix4fv(gui.image_loc_model, 1, GL_FALSE, element->transform[0]);
259                         gui.image_mesh->textures = &element->def.image->id;
260                         mesh_render(gui.image_mesh);
261                 }
262
263                 if (element->text && element->def.text_color.w > 0.0f) {
264                         glUseProgram(gui.font_prog);
265                         glUniformMatrix4fv(gui.font_loc_model, 1, GL_FALSE, element->text_transform[0]);
266                         glUniform4f(gui.font_loc_color, element->def.text_color.x, element->def.text_color.y, element->def.text_color.z, element->def.text_color.w);
267                         font_render(element->text);
268                 }
269
270                 bintree_traverse(&element->children, BTT_INORDER, &render_element, NULL);
271         }
272 }
273
274 void gui_render()
275 {
276         glDisable(GL_CULL_FACE);
277         glDisable(GL_DEPTH_TEST);
278         bintree_traverse(&gui_root.children, BTT_INORDER, &render_element, NULL);
279         glEnable(GL_DEPTH_TEST);
280         glEnable(GL_CULL_FACE);
281 }
282
283 GUIElement *gui_add(GUIElement *parent, GUIElementDefinition def)
284 {
285         GUIElement *element = malloc(sizeof(GUIElement));
286         element->def = def;
287         element->visible = true;
288         element->parent = parent;
289
290         if (element->def.text) {
291                 element->def.text = strdup(element->def.text);
292                 element->text = font_create(element->def.text);
293         } else {
294                 element->text = NULL;
295         }
296
297         bintree_insert(&parent->children, &element->def.z_index, element);
298
299         element->children = bintree_create(sizeof(f32), &bintree_compare_f32);
300
301         if (element->def.affect_parent_scale)
302                 gui_update_transform(parent);
303         else
304                 gui_update_transform(element);
305
306         return element;
307 }
308
309 void gui_set_text(GUIElement *element, char *text)
310 {
311         if (element->def.text)
312                 free(element->def.text);
313
314         element->def.text = text;
315         font_delete(element->text);
316         element->text = font_create(text);
317         gui_update_transform(element);
318 }
319
320 // transform code
321
322 typedef struct
323 {
324         List left_nodes;
325         v2f32 result;
326 } PrecalculateChildrenScaleData;
327
328 static void precalculate_children_scale(BintreeNode *node, void *arg);
329 static void bintree_calculate_element_scale(BintreeNode *node, void *arg);
330 static void list_calculate_element_scale(void *key, void *value, void *arg);
331 static void bintree_calculate_element_transform(BintreeNode *node, unused void *arg);
332
333 static void calculate_element_scale(GUIElement *element)
334 {
335         element->scale = (v2f32) {
336                 element->def.scale.x,
337                 element->def.scale.y,
338         };
339
340         bool traversed_children = false;
341
342         switch (element->def.scale_type) {
343                 case GST_IMAGE:
344                         element->scale.x *= element->def.image->width;
345                         element->scale.y *= element->def.image->height;
346                         break;
347
348                 case GST_TEXT:
349                         element->scale.x *= element->text->size.x;
350                         element->scale.y *= element->text->size.y;
351                         break;
352
353                 case GST_PARENT:
354                         element->scale.x *= element->parent->scale.x;
355                         element->scale.y *= element->parent->scale.y;
356                         break;
357
358                 case GST_CHILDREN: {
359                         PrecalculateChildrenScaleData pdata = {
360                                 .left_nodes = list_create(NULL),
361                                 .result = {0.0f, 0.0f},
362                         };
363
364                         bintree_traverse(&element->children, BTT_INORDER, &precalculate_children_scale, &pdata);
365
366                         element->scale.x *= pdata.result.x;
367                         element->scale.y *= pdata.result.y;
368
369                         list_clear_func(&pdata.left_nodes, &list_calculate_element_scale, NULL);
370                         traversed_children = true;
371                 } break;
372
373                 case GST_NONE:
374                         break;
375         }
376
377         if (! traversed_children)
378                 bintree_traverse(&element->children, BTT_INORDER, &bintree_calculate_element_scale, NULL);
379 }
380
381 static void precalculate_children_scale(BintreeNode *node, void *arg)
382 {
383         GUIElement *element = node->value;
384         PrecalculateChildrenScaleData *pdata = arg;
385
386         if (element->def.affect_parent_scale) {
387                 assert(element->def.scale_type != GST_PARENT);
388                 calculate_element_scale(element);
389
390                 if (element->scale.x > pdata->result.x)
391                         pdata->result.x = element->scale.x;
392
393                 if (element->scale.y > pdata->result.y)
394                         pdata->result.y = element->scale.y;
395         } else {
396                 list_put(&pdata->left_nodes, element, NULL);
397         }
398 }
399
400 static void bintree_calculate_element_scale(BintreeNode *node, unused void *arg)
401 {
402         calculate_element_scale(node->value);
403 }
404
405 static void list_calculate_element_scale(void *key, unused void *value, unused void *arg)
406 {
407         calculate_element_scale(key);
408 }
409
410 static void calculate_element_transform(GUIElement *element)
411 {
412         element->pos = (v2f32) {
413                 floor(element->parent->pos.x + element->def.offset.x + element->def.pos.x * element->parent->scale.x - element->def.align.x * element->scale.x),
414                 floor(element->parent->pos.y + element->def.offset.y + element->def.pos.y * element->parent->scale.y - element->def.align.y * element->scale.y),
415         };
416
417         mat4x4_translate(element->transform, element->pos.x - element->def.margin.x, element->pos.y - element->def.margin.y, 0.0f);
418         mat4x4_translate(element->text_transform, element->pos.x, element->pos.y, 0.0f);
419         mat4x4_scale_aniso(element->transform, element->transform, element->scale.x + element->def.margin.x * 2.0f, element->scale.y + element->def.margin.y * 2.0f, 1.0f);
420
421         bintree_traverse(&element->children, BTT_INORDER, &bintree_calculate_element_transform, NULL);
422 }
423
424 static void bintree_calculate_element_transform(BintreeNode *node, unused void *arg)
425 {
426         calculate_element_transform(node->value);
427 }
428
429 void gui_update_transform(GUIElement *element)
430 {
431         calculate_element_scale(element);
432         calculate_element_transform(element);
433 }