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"
19 GLuint background_prog;
20 GLint background_loc_model;
21 GLint background_loc_projection;
22 GLint background_loc_color;
23 Mesh *background_mesh;
26 GLint image_loc_model;
27 GLint image_loc_projection;
32 GLint font_loc_projection;
43 } __attribute__((packed)) VertexBackgroundPosition;
47 VertexBackgroundPosition position;
48 } __attribute__((packed)) VertexBackground;
50 static VertexAttribute background_vertex_attributes[1] = {
55 .size = sizeof(VertexBackgroundPosition),
59 static VertexLayout background_vertex_layout = {
60 .attributes = background_vertex_attributes,
62 .size = sizeof(VertexBackground),
65 static VertexBackground background_vertices[6] = {
77 } __attribute__((packed)) VertexImagePosition;
82 } __attribute__((packed)) VertexImageTextureCoordinates;
86 VertexImagePosition position;
87 VertexImageTextureCoordinates textureCoordinates;
88 } __attribute__((packed)) VertexImage;
90 static VertexAttribute image_vertex_attributes[2] = {
95 .size = sizeof(VertexImagePosition),
101 .size = sizeof(VertexImageTextureCoordinates),
105 static VertexLayout image_vertex_layout = {
106 .attributes = image_vertex_attributes,
108 .size = sizeof(VertexImage),
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}},
120 static int bintree_compare_f32(void *v1, void *v2, unused Bintree *tree)
122 f32 diff = (*(f32 *) v1) - (*(f32 *) v2);
123 return CMPBOUNDS(diff);
128 // initialize background pipeline
130 if (! shader_program_create(RESSOURCEPATH "shaders/gui/background", &gui.background_prog, NULL)) {
131 fprintf(stderr, "Failed to create GUI background shader program\n");
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");
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;
148 // initialize image pipeline
150 if (! shader_program_create(RESSOURCEPATH "shaders/gui/image", &gui.image_prog, NULL)) {
151 fprintf(stderr, "Failed to create GUI image shader program\n");
155 gui.image_loc_model = glGetUniformLocation(gui.image_prog, "model");
156 gui.image_loc_projection = glGetUniformLocation(gui.image_prog, "projection");
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;
167 // initialize font pipeline
169 if (! shader_program_create(RESSOURCEPATH "shaders/gui/font", &gui.font_prog, NULL)) {
170 fprintf(stderr, "Failed to create GUI font shader program\n");
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");
178 // font meshes are initialized in font.c
180 // initialize GUI root element
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);
203 static void free_element(BintreeNode *node, unused void *arg)
205 GUIElement *element = node->value;
207 bintree_clear(&element->children, &free_element, NULL);
209 if (element->def.text)
210 free(element->def.text);
213 font_delete(element->text);
220 glDeleteProgram(gui.background_prog);
221 mesh_delete(gui.background_mesh);
223 glDeleteProgram(gui.image_prog);
224 mesh_delete(gui.image_mesh);
226 glDeleteProgram(gui.font_prog);
228 bintree_clear(&gui_root.children, &free_element, NULL);
231 void gui_on_resize(int width, int height)
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]);
238 gui_root.def.scale.x = width;
239 gui_root.def.scale.y = height;
241 gui_update_transform(&gui_root);
244 static void render_element(BintreeNode *node, unused void *arg)
246 GUIElement *element = node->value;
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);
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);
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);
270 bintree_traverse(&element->children, BTT_INORDER, &render_element, NULL);
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);
283 GUIElement *gui_add(GUIElement *parent, GUIElementDefinition def)
285 GUIElement *element = malloc(sizeof(GUIElement));
287 element->visible = true;
288 element->parent = parent;
290 if (element->def.text) {
291 element->def.text = strdup(element->def.text);
292 element->text = font_create(element->def.text);
294 element->text = NULL;
297 bintree_insert(&parent->children, &element->def.z_index, element);
299 element->children = bintree_create(sizeof(f32), &bintree_compare_f32);
301 if (element->def.affect_parent_scale)
302 gui_update_transform(parent);
304 gui_update_transform(element);
309 void gui_set_text(GUIElement *element, char *text)
311 if (element->def.text)
312 free(element->def.text);
314 element->def.text = text;
315 font_delete(element->text);
316 element->text = font_create(text);
317 gui_update_transform(element);
326 } PrecalculateChildrenScaleData;
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);
333 static void calculate_element_scale(GUIElement *element)
335 element->scale = (v2f32) {
336 element->def.scale.x,
337 element->def.scale.y,
340 bool traversed_children = false;
342 switch (element->def.scale_type) {
344 element->scale.x *= element->def.image->width;
345 element->scale.y *= element->def.image->height;
349 element->scale.x *= element->text->size.x;
350 element->scale.y *= element->text->size.y;
354 element->scale.x *= element->parent->scale.x;
355 element->scale.y *= element->parent->scale.y;
359 PrecalculateChildrenScaleData pdata = {
360 .left_nodes = list_create(NULL),
361 .result = {0.0f, 0.0f},
364 bintree_traverse(&element->children, BTT_INORDER, &precalculate_children_scale, &pdata);
366 element->scale.x *= pdata.result.x;
367 element->scale.y *= pdata.result.y;
369 list_clear_func(&pdata.left_nodes, &list_calculate_element_scale, NULL);
370 traversed_children = true;
377 if (! traversed_children)
378 bintree_traverse(&element->children, BTT_INORDER, &bintree_calculate_element_scale, NULL);
381 static void precalculate_children_scale(BintreeNode *node, void *arg)
383 GUIElement *element = node->value;
384 PrecalculateChildrenScaleData *pdata = arg;
386 if (element->def.affect_parent_scale) {
387 assert(element->def.scale_type != GST_PARENT);
388 calculate_element_scale(element);
390 if (element->scale.x > pdata->result.x)
391 pdata->result.x = element->scale.x;
393 if (element->scale.y > pdata->result.y)
394 pdata->result.y = element->scale.y;
396 list_put(&pdata->left_nodes, element, NULL);
400 static void bintree_calculate_element_scale(BintreeNode *node, unused void *arg)
402 calculate_element_scale(node->value);
405 static void list_calculate_element_scale(void *key, unused void *value, unused void *arg)
407 calculate_element_scale(key);
410 static void calculate_element_transform(GUIElement *element)
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),
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);
421 bintree_traverse(&element->children, BTT_INORDER, &bintree_calculate_element_transform, NULL);
424 static void bintree_calculate_element_transform(BintreeNode *node, unused void *arg)
426 calculate_element_transform(node->value);
429 void gui_update_transform(GUIElement *element)
431 calculate_element_scale(element);
432 calculate_element_transform(element);