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