]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/client/gui.c
9cda646956bec6e3297a98426e7ed7e03326a661
[dragonblocks_alpha.git] / src / client / gui.c
1 #include <GL/glew.h>
2 #include <GL/gl.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include "client/client.h"
7 #include "client/cube.h"
8 #include "client/gl_debug.h"
9 #include "client/gui.h"
10 #include "client/mesh.h"
11 #include "client/shader.h"
12 #include "client/window.h"
13
14 static GUIElement root_element = {
15         .def = {
16                 .pos = {0.0f, 0.0f},
17                 .z_index = 0.0f,
18                 .offset = {0, 0},
19                 .align = {0.0f, 0.0f},
20                 .scale = {0.0f, 0.0f},
21                 .scale_type = SCALE_NONE,
22                 .affect_parent_scale = false,
23                 .text = NULL,
24                 .image = NULL,
25                 .text_color = {0.0f, 0.0f, 0.0f, 0.0f},
26                 .bg_color = {0.0f, 0.0f, 0.0f, 0.0f},
27         },
28         .visible = true,
29         .pos = (v2f32)  {0.0f, 0.0f},
30         .scale = (v2f32) {0.0f, 0.0f},
31         .text = NULL,
32         .parent = &root_element,
33 };
34
35 static GLuint background_prog;
36 static GLint background_loc_model;
37 static GLint background_loc_projection;
38 static GLint background_loc_color;
39 typedef struct {
40         v2f32 position;
41 } __attribute__((packed)) BackgroundVertex;
42 static Mesh background_mesh = {
43         .layout = &(VertexLayout) {
44                 .attributes = (VertexAttribute[]) {
45                         {GL_FLOAT, 2, sizeof(v2f32)}, // position
46                 },
47                 .count = 1,
48                 .size = sizeof(BackgroundVertex),
49         },
50         .vao = 0,
51         .vbo = 0,
52         .data = (BackgroundVertex[]) {
53                 {{0.0, 0.0}},
54                 {{1.0, 0.0}},
55                 {{1.0, 1.0}},
56                 {{1.0, 1.0}},
57                 {{0.0, 1.0}},
58                 {{0.0, 0.0}},
59         },
60         .count = 6,
61         .free_data = false,
62 };
63
64 static GLuint image_prog;
65 static GLint image_loc_model;
66 static GLint image_loc_projection;
67 typedef struct {
68         v2f32 position;
69         v2f32 textureCoordinates;
70 } __attribute__((packed)) ImageVertex;
71 static Mesh image_mesh = {
72         .layout = &(VertexLayout) {
73                 .attributes = (VertexAttribute[]) {
74                         {GL_FLOAT, 2, sizeof(v2f32)}, // position
75                         {GL_FLOAT, 2, sizeof(v2f32)}, // textureCoordinates
76                 },
77                 .count = 2,
78                 .size = sizeof(ImageVertex),
79         },
80         .vao = 0,
81         .vbo = 0,
82         .data = (ImageVertex[]) {
83                 {{0.0, 0.0}, {0.0, 0.0}},
84                 {{1.0, 0.0}, {1.0, 0.0}},
85                 {{1.0, 1.0}, {1.0, 1.0}},
86                 {{1.0, 1.0}, {1.0, 1.0}},
87                 {{0.0, 1.0}, {0.0, 1.0}},
88                 {{0.0, 0.0}, {0.0, 0.0}},
89         },
90         .count = 6,
91         .free_data = false,
92 };
93
94 static GLuint font_prog;
95 static GLint font_loc_model;
96 static GLint font_loc_projection;
97 static GLint font_loc_color;
98 // font meshes are initialized in font.c
99
100 static mat4x4 projection;
101
102 // element functions
103
104 static void delete_element(GUIElement *element);
105 static void render_element(GUIElement *element);
106 static void scale_element(GUIElement *element);
107
108 static int cmp_element(const GUIElement *ea, const GUIElement *eb)
109 {
110         return -f32_cmp(&ea->def.z_index, &eb->def.z_index);
111 }
112
113 static void delete_elements(Array *elements)
114 {
115         for (size_t i = 0; i < elements->siz; i++)
116                 delete_element(((GUIElement **) elements->ptr)[i]);
117         array_clr(elements);
118 }
119
120 static void delete_element(GUIElement *element)
121 {
122         delete_elements(&element->children);
123
124         if (element->def.text)
125                 free(element->def.text);
126
127         if (element->text)
128                 font_delete(element->text);
129
130         free(element);
131 }
132
133 static void render_elements(Array *elements)
134 {
135         for (size_t i = 0; i < elements->siz; i++)
136                 render_element(((GUIElement **) elements->ptr)[i]);
137 }
138
139 static void render_element(GUIElement *element)
140 {
141         if (element->visible) {
142                 if (element->def.bg_color.w > 0.0f) {
143                         glUseProgram(background_prog); GL_DEBUG
144                         glUniformMatrix4fv(background_loc_model, 1, GL_FALSE, element->transform[0]); GL_DEBUG
145                         glUniform4f(background_loc_color, element->def.bg_color.x, element->def.bg_color.y, element->def.bg_color.z, element->def.bg_color.w); GL_DEBUG
146                         mesh_render(&background_mesh);
147                 }
148
149                 if (element->def.image) {
150                         glUseProgram(image_prog); GL_DEBUG
151                         glUniformMatrix4fv(image_loc_model, 1, GL_FALSE, element->transform[0]); GL_DEBUG
152                         glBindTextureUnit(0, element->def.image->txo); GL_DEBUG
153                         mesh_render(&image_mesh);
154                 }
155
156                 if (element->def.text && element->def.text_color.w > 0.0f) {
157                         if (!element->text) {
158                                 element->text = font_create(element->def.text);
159                                 gui_transform(element);
160                         }
161
162                         glUseProgram(font_prog); GL_DEBUG
163                         glUniformMatrix4fv(font_loc_model, 1, GL_FALSE, element->text_transform[0]); GL_DEBUG
164                         glUniform4f(font_loc_color, element->def.text_color.x, element->def.text_color.y, element->def.text_color.z, element->def.text_color.w); GL_DEBUG
165                         font_render(element->text);
166                 }
167
168                 render_elements(&element->children);
169         }
170 }
171
172 static void scale_elements(Array *elements, int mask, v3f32 *max)
173 {
174         for (size_t i = 0; i < elements->siz; i++) {
175                 GUIElement *element = ((GUIElement **) elements->ptr)[i];
176
177                 if ((1 << element->def.affect_parent_scale) & mask) {
178                         scale_element(element);
179
180                         if (max) {
181                                 if (element->scale.x > max->x)
182                                         max->x = element->scale.x;
183
184                                 if (element->scale.y > max->y)
185                                         max->y = element->scale.y;
186                         }
187                 }
188         }
189 }
190
191 static void scale_element(GUIElement *element)
192 {
193         element->scale = (v2f32) {
194                 element->def.scale.x,
195                 element->def.scale.y,
196         };
197
198         switch (element->def.scale_type) {
199                 case SCALE_IMAGE:
200                         element->scale.x *= element->def.image->width;
201                         element->scale.y *= element->def.image->height;
202                         break;
203
204                 case SCALE_TEXT:
205                         if (!element->text)
206                                 break;
207
208                         element->scale.x *= element->text->size.x;
209                         element->scale.y *= element->text->size.y;
210                         break;
211
212                 case SCALE_PARENT:
213                         element->scale.x *= element->parent->scale.x;
214                         element->scale.y *= element->parent->scale.y;
215                         break;
216
217                 case SCALE_CHILDREN: {
218                         v3f32 scale = {0.0f, 0.0f, 0.0f};
219                         scale_elements(&element->children, 1 << true, &scale);
220
221                         element->scale.x *= scale.x;
222                         element->scale.y *= scale.y;
223
224                         scale_elements(&element->children, 1 << false, NULL);
225                         break;
226                 }
227
228                 case SCALE_NONE:
229                         break;
230         }
231
232         if (element->def.scale_type != SCALE_CHILDREN)
233                 scale_elements(&element->children, (1 << true) | (1 << false), NULL);
234 }
235
236 static void transform_element(GUIElement *element)
237 {
238         element->pos = (v2f32) {
239                 floor(element->parent->pos.x + element->def.offset.x + element->def.pos.x * element->parent->scale.x - element->def.align.x * element->scale.x),
240                 floor(element->parent->pos.y + element->def.offset.y + element->def.pos.y * element->parent->scale.y - element->def.align.y * element->scale.y),
241         };
242
243         mat4x4_translate(element->transform, element->pos.x - element->def.margin.x, element->pos.y - element->def.margin.y, 0.0f);
244         mat4x4_translate(element->text_transform, element->pos.x, element->pos.y, 0.0f);
245         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);
246
247         for (size_t i = 0; i < element->children.siz; i++)
248                 transform_element(((GUIElement **) element->children.ptr)[i]);
249 }
250
251 // public functions
252
253 void gui_init()
254 {
255         // initialize background pipeline
256         background_prog = shader_program_create(RESSOURCE_PATH "shaders/gui/background", NULL);
257         background_loc_model = glGetUniformLocation(background_prog, "model"); GL_DEBUG
258         background_loc_projection = glGetUniformLocation(background_prog, "projection"); GL_DEBUG
259         background_loc_color = glGetUniformLocation(background_prog, "color"); GL_DEBUG
260
261         // initialize image pipeline
262         image_prog = shader_program_create(RESSOURCE_PATH "shaders/gui/image", NULL);
263         image_loc_model = glGetUniformLocation(image_prog, "model"); GL_DEBUG
264         image_loc_projection = glGetUniformLocation(image_prog, "projection"); GL_DEBUG
265
266         // initialize font pipeline
267         font_prog = shader_program_create(RESSOURCE_PATH "shaders/gui/font", NULL);
268         font_loc_model = glGetUniformLocation(font_prog, "model"); GL_DEBUG
269         font_loc_projection = glGetUniformLocation(font_prog, "projection"); GL_DEBUG
270         font_loc_color = glGetUniformLocation(font_prog, "color"); GL_DEBUG
271
272         // initialize GUI root element
273         array_ini(&root_element.children, sizeof(GUIElement *), 0);
274         gui_update_projection();
275 }
276
277 void gui_deinit()
278 {
279         glDeleteProgram(background_prog); GL_DEBUG
280         mesh_destroy(&background_mesh);
281
282         glDeleteProgram(image_prog); GL_DEBUG
283         mesh_destroy(&image_mesh);
284
285         glDeleteProgram(font_prog); GL_DEBUG
286
287         delete_elements(&root_element.children);
288 }
289
290 void gui_update_projection()
291 {
292         mat4x4_ortho(projection, 0, window.width, window.height, 0, -1.0f, 1.0f);
293         glProgramUniformMatrix4fv(background_prog, background_loc_projection, 1, GL_FALSE, projection[0]); GL_DEBUG
294         glProgramUniformMatrix4fv(image_prog, image_loc_projection, 1, GL_FALSE, projection[0]); GL_DEBUG
295         glProgramUniformMatrix4fv(font_prog, font_loc_projection, 1, GL_FALSE, projection[0]); GL_DEBUG
296
297         root_element.def.scale.x = window.width;
298         root_element.def.scale.y = window.height;
299
300         gui_transform(&root_element);
301 }
302
303 void gui_render()
304 {
305         glDisable(GL_CULL_FACE); GL_DEBUG
306         glDisable(GL_DEPTH_TEST); GL_DEBUG
307
308         render_elements(&root_element.children);
309
310         glEnable(GL_DEPTH_TEST); GL_DEBUG
311         glEnable(GL_CULL_FACE); GL_DEBUG
312 }
313
314 GUIElement *gui_add(GUIElement *parent, GUIElementDef def)
315 {
316         if (parent == NULL)
317                 parent = &root_element;
318
319         GUIElement *element = malloc(sizeof *element);
320         element->def = def;
321         element->visible = true;
322         element->parent = parent;
323         element->text = NULL;
324
325         if (element->def.text)
326                 element->def.text = strdup(element->def.text);
327
328         array_ins(&parent->children, &element, &cmp_element);
329         array_ini(&element->children, sizeof(GUIElement), 0);
330
331         if (element->def.affect_parent_scale)
332                 gui_transform(parent);
333         else
334                 gui_transform(element);
335
336         return element;
337 }
338
339 void gui_text(GUIElement *element, const char *text)
340 {
341         if (element->def.text)
342                 free(element->def.text);
343
344         if (element->text)
345                 font_delete(element->text);
346
347         element->def.text = strdup(text);
348         element->text = NULL;
349 }
350
351 void gui_transform(GUIElement *element)
352 {
353         scale_element(element);
354         transform_element(element);
355 }