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