X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fclient%2Fgui.c;h=4aecb482527e54cf2fd030a0b6de4ab6af49cfce;hb=6d60079b8f06b9701c4e823d33ac11a843814183;hp=86f8547bd9dd3aacdb910e8463a618699a88ecb1;hpb=d59a59ac8b98841abaee8b9bf8af1c7962cabfac;p=dragonblocks_alpha.git diff --git a/src/client/gui.c b/src/client/gui.c index 86f8547..4aecb48 100644 --- a/src/client/gui.c +++ b/src/client/gui.c @@ -1,210 +1,106 @@ -#include -#include -#include -#include #include #include +#include +#include +#include #include "client/client.h" #include "client/cube.h" +#include "client/gl_debug.h" #include "client/gui.h" #include "client/mesh.h" #include "client/shader.h" -#include "client/vertex.h" -#include "util.h" - -static struct -{ - List elements; - - GLuint background_prog; - GLint background_loc_model; - GLint background_loc_projection; - GLint background_loc_color; - Mesh *background_mesh; - - GLuint image_prog; - GLint image_loc_model; - GLint image_loc_projection; - Mesh *image_mesh; - - GLuint font_prog; - GLint font_loc_model; - GLint font_loc_projection; - GLint font_loc_color; - - mat4x4 projection; -} gui; - -GUIElement gui_root; - -typedef struct -{ - GLfloat x, y; -} __attribute__((packed)) VertexBackgroundPosition; - -typedef struct -{ - VertexBackgroundPosition position; -} __attribute__((packed)) VertexBackground; - -static VertexAttribute background_vertex_attributes[1] = { - // position - { - .type = GL_FLOAT, - .length = 2, - .size = sizeof(VertexBackgroundPosition), +#include "client/window.h" + +static GUIElement root_element; + +static GLuint background_prog; +static GLint background_loc_model; +static GLint background_loc_projection; +static GLint background_loc_color; +typedef struct { + v2f32 position; +} __attribute__((packed)) BackgroundVertex; +static Mesh background_mesh = { + .layout = &(VertexLayout) { + .attributes = (VertexAttribute[]) { + {GL_FLOAT, 2, sizeof(v2f32)}, // position + }, + .count = 1, + .size = sizeof(BackgroundVertex), }, + .vao = 0, + .vbo = 0, + .data = (BackgroundVertex[]) { + {{0.0, 0.0}}, + {{1.0, 0.0}}, + {{1.0, 1.0}}, + {{1.0, 1.0}}, + {{0.0, 1.0}}, + {{0.0, 0.0}}, + }, + .count = 6, + .free_data = false, }; -static VertexLayout background_vertex_layout = { - .attributes = background_vertex_attributes, - .count = 1, - .size = sizeof(VertexBackground), -}; - -static VertexBackground background_vertices[6] = { - {{0.0, 0.0}}, - {{1.0, 0.0}}, - {{1.0, 1.0}}, - {{1.0, 1.0}}, - {{0.0, 1.0}}, - {{0.0, 0.0}}, -}; - -typedef struct -{ - GLfloat x, y; -} __attribute__((packed)) VertexImagePosition; - -typedef struct -{ - GLfloat s, t; -} __attribute__((packed)) VertexImageTextureCoordinates; - -typedef struct -{ - VertexImagePosition position; - VertexImageTextureCoordinates textureCoordinates; -} __attribute__((packed)) VertexImage; - -static VertexAttribute image_vertex_attributes[2] = { - // position - { - .type = GL_FLOAT, - .length = 2, - .size = sizeof(VertexImagePosition), +static GLuint image_prog; +static GLint image_loc_model; +static GLint image_loc_projection; +typedef struct { + v2f32 position; + v2f32 textureCoordinates; +} __attribute__((packed)) ImageVertex; +static Mesh image_mesh = { + .layout = &(VertexLayout) { + .attributes = (VertexAttribute[]) { + {GL_FLOAT, 2, sizeof(v2f32)}, // position + {GL_FLOAT, 2, sizeof(v2f32)}, // textureCoordinates + }, + .count = 2, + .size = sizeof(ImageVertex), }, - // textureCoordinates - { - .type = GL_FLOAT, - .length = 2, - .size = sizeof(VertexImageTextureCoordinates), + .vao = 0, + .vbo = 0, + .data = (ImageVertex[]) { + {{0.0, 0.0}, {0.0, 0.0}}, + {{1.0, 0.0}, {1.0, 0.0}}, + {{1.0, 1.0}, {1.0, 1.0}}, + {{1.0, 1.0}, {1.0, 1.0}}, + {{0.0, 1.0}, {0.0, 1.0}}, + {{0.0, 0.0}, {0.0, 0.0}}, }, + .count = 6, + .free_data = false, }; -static VertexLayout image_vertex_layout = { - .attributes = image_vertex_attributes, - .count = 2, - .size = sizeof(VertexImage), -}; +static GLuint font_prog; +static GLint font_loc_model; +static GLint font_loc_projection; +static GLint font_loc_color; +// font meshes are initialized in font.c -static VertexImage image_vertices[6] = { - {{0.0, 0.0}, {0.0, 0.0}}, - {{1.0, 0.0}, {1.0, 0.0}}, - {{1.0, 1.0}, {1.0, 1.0}}, - {{1.0, 1.0}, {1.0, 1.0}}, - {{0.0, 1.0}, {0.0, 1.0}}, - {{0.0, 0.0}, {0.0, 0.0}}, -}; +static mat4x4 projection; + +// element functions + +static void delete_element(GUIElement *element); +static void render_element(GUIElement *element); +static void scale_element(GUIElement *element); -static int bintree_compare_f32(void *v1, void *v2, unused Bintree *tree) +static int cmp_element(const GUIElement *ea, const GUIElement *eb) { - f32 diff = (*(f32 *) v1) - (*(f32 *) v2); - return CMPBOUNDS(diff); + return f32_cmp(&ea->def.z_index, &eb->def.z_index); } -bool gui_init() +static void delete_elements(Array *elements) { - // initialize background pipeline - - if (! shader_program_create(RESSOURCEPATH "shaders/gui/background", &gui.background_prog, NULL)) { - fprintf(stderr, "Failed to create GUI background shader program\n"); - return false; - } - - gui.background_loc_model = glGetUniformLocation(gui.background_prog, "model"); - gui.background_loc_projection = glGetUniformLocation(gui.background_prog, "projection"); - gui.background_loc_color = glGetUniformLocation(gui.background_prog, "color"); - - gui.background_mesh = mesh_create(); - gui.background_mesh->textures = NULL; - gui.background_mesh->textures_count = 0; - gui.background_mesh->free_textures = false; - gui.background_mesh->vertices = background_vertices; - gui.background_mesh->vertices_count = 6; - gui.background_mesh->free_vertices = false; - gui.background_mesh->layout = &background_vertex_layout; - - // initialize image pipeline - - if (! shader_program_create(RESSOURCEPATH "shaders/gui/image", &gui.image_prog, NULL)) { - fprintf(stderr, "Failed to create GUI image shader program\n"); - return false; - } - - gui.image_loc_model = glGetUniformLocation(gui.image_prog, "model"); - gui.image_loc_projection = glGetUniformLocation(gui.image_prog, "projection"); - - gui.image_mesh = mesh_create(); - gui.image_mesh->textures = NULL; - gui.image_mesh->textures_count = 1; - gui.image_mesh->free_textures = false; - gui.image_mesh->vertices = image_vertices; - gui.image_mesh->vertices_count = 6; - gui.image_mesh->free_vertices = false; - gui.image_mesh->layout = &image_vertex_layout; - - // initialize font pipeline - - if (! shader_program_create(RESSOURCEPATH "shaders/gui/font", &gui.font_prog, NULL)) { - fprintf(stderr, "Failed to create GUI font shader program\n"); - return false; - } - - gui.font_loc_model = glGetUniformLocation(gui.font_prog, "model"); - gui.font_loc_projection = glGetUniformLocation(gui.font_prog, "projection"); - gui.font_loc_color = glGetUniformLocation(gui.font_prog, "color"); - - // font meshes are initialized in font.c - - // initialize GUI root element - - gui_root.def.pos = (v2f32) {0.0f, 0.0f}; - gui_root.def.z_index = 0.0f; - gui_root.def.offset = (v2s32) {0, 0}; - gui_root.def.align = (v2f32) {0.0f, 0.0f}; - gui_root.def.scale = (v2f32) {0.0f, 0.0f}; - gui_root.def.scale_type = GST_NONE; - gui_root.def.affect_parent_scale = false; - gui_root.def.text = NULL; - gui_root.def.image = NULL; - gui_root.def.text_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f}; - gui_root.def.bg_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f}; - gui_root.visible = true; - gui_root.pos = (v2f32) {0.0f, 0.0f}; - gui_root.scale = (v2f32) {0.0f, 0.0f}; - gui_root.text = NULL; - gui_root.parent = &gui_root; - gui_root.children = bintree_create(sizeof(f32), &bintree_compare_f32); - - return true; + for (size_t i = 0; i < elements->siz; i++) + delete_element(((GUIElement **) elements->ptr)[i]); + array_clr(elements); } -static void free_element(BintreeNode *node, unused void *arg) +static void delete_element(GUIElement *element) { - GUIElement *element = node->value; - - bintree_clear(&element->children, &free_element, NULL); + delete_elements(&element->children); if (element->def.text) free(element->def.text); @@ -215,219 +111,261 @@ static void free_element(BintreeNode *node, unused void *arg) free(element); } -void gui_deinit() -{ - glDeleteProgram(gui.background_prog); - mesh_delete(gui.background_mesh); - - glDeleteProgram(gui.image_prog); - mesh_delete(gui.image_mesh); - - glDeleteProgram(gui.font_prog); - - bintree_clear(&gui_root.children, &free_element, NULL); -} - -void gui_on_resize(int width, int height) +static void render_elements(Array *elements) { - mat4x4_ortho(gui.projection, 0, width, height, 0, -1.0f, 1.0f); - glProgramUniformMatrix4fv(gui.background_prog, gui.background_loc_projection, 1, GL_FALSE, gui.projection[0]); - glProgramUniformMatrix4fv(gui.image_prog, gui.image_loc_projection, 1, GL_FALSE, gui.projection[0]); - glProgramUniformMatrix4fv(gui.font_prog, gui.font_loc_projection, 1, GL_FALSE, gui.projection[0]); - - gui_root.def.scale.x = width; - gui_root.def.scale.y = height; - - gui_update_transform(&gui_root); + for (size_t i = 0; i < elements->siz; i++) + render_element(((GUIElement **) elements->ptr)[i]); } -static void render_element(BintreeNode *node, unused void *arg) +static void render_element(GUIElement *element) { - GUIElement *element = node->value; - if (element->visible) { if (element->def.bg_color.w > 0.0f) { - glUseProgram(gui.background_prog); - glUniformMatrix4fv(gui.background_loc_model, 1, GL_FALSE, element->transform[0]); - 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); - mesh_render(gui.background_mesh); + glUseProgram(background_prog); GL_DEBUG + glUniformMatrix4fv(background_loc_model, 1, GL_FALSE, element->transform[0]); GL_DEBUG + 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 + mesh_render(&background_mesh); } if (element->def.image) { - glUseProgram(gui.image_prog); - glUniformMatrix4fv(gui.image_loc_model, 1, GL_FALSE, element->transform[0]); - gui.image_mesh->textures = &element->def.image->id; - mesh_render(gui.image_mesh); + glUseProgram(image_prog); GL_DEBUG + glUniformMatrix4fv(image_loc_model, 1, GL_FALSE, element->transform[0]); GL_DEBUG + glBindTextureUnit(0, element->def.image->txo); GL_DEBUG + mesh_render(&image_mesh); } - if (element->text && element->def.text_color.w > 0.0f) { - glUseProgram(gui.font_prog); - glUniformMatrix4fv(gui.font_loc_model, 1, GL_FALSE, element->text_transform[0]); - 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); + if (element->def.text && element->def.text_color.w > 0.0f) { + if (!element->text) { + element->text = font_create(element->def.text); + gui_transform(element); + } + + glUseProgram(font_prog); GL_DEBUG + glUniformMatrix4fv(font_loc_model, 1, GL_FALSE, element->text_transform[0]); GL_DEBUG + 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 font_render(element->text); } - bintree_traverse(&element->children, BTT_INORDER, &render_element, NULL); + render_elements(&element->children); } } -void gui_render() +static void scale_elements(Array *elements, int mask, v3f32 *max) { - bintree_traverse(&gui_root.children, BTT_INORDER, &render_element, NULL); -} + for (size_t i = 0; i < elements->siz; i++) { + GUIElement *element = ((GUIElement **) elements->ptr)[i]; -GUIElement *gui_add(GUIElement *parent, GUIElementDefinition def) -{ - GUIElement *element = malloc(sizeof(GUIElement)); - element->def = def; - element->visible = true; - element->parent = parent; - - if (element->def.text) { - element->def.text = strdup(element->def.text); - element->text = font_create(element->def.text); - } else { - element->text = NULL; - } - - bintree_insert(&parent->children, &element->def.z_index, element); + if ((1 << element->def.affect_parent_scale) & mask) { + scale_element(element); - element->children = bintree_create(sizeof(f32), &bintree_compare_f32); + if (max) { + if (element->scale.x > max->x) + max->x = element->scale.x; - if (element->def.affect_parent_scale) - gui_update_transform(parent); - else - gui_update_transform(element); - - return element; -} - -void gui_set_text(GUIElement *element, char *text) -{ - if (element->def.text) - free(element->def.text); - - element->def.text = text; - font_delete(element->text); - element->text = font_create(text); - gui_update_transform(element); + if (element->scale.y > max->y) + max->y = element->scale.y; + } + } + } } -// transform code - -typedef struct -{ - List left_nodes; - v2f32 result; -} PrecalculateChildrenScaleData; - -static void precalculate_children_scale(BintreeNode *node, void *arg); -static void bintree_calculate_element_scale(BintreeNode *node, void *arg); -static void list_calculate_element_scale(void *key, void *value, void *arg); -static void bintree_calculate_element_transform(BintreeNode *node, unused void *arg); - -static void calculate_element_scale(GUIElement *element) +static void scale_element(GUIElement *element) { element->scale = (v2f32) { element->def.scale.x, element->def.scale.y, }; - bool traversed_children = false; - switch (element->def.scale_type) { - case GST_IMAGE: + case SCALE_IMAGE: element->scale.x *= element->def.image->width; element->scale.y *= element->def.image->height; break; - case GST_TEXT: + case SCALE_TEXT: + if (!element->text) + break; + element->scale.x *= element->text->size.x; element->scale.y *= element->text->size.y; break; - case GST_PARENT: + case SCALE_PARENT: element->scale.x *= element->parent->scale.x; element->scale.y *= element->parent->scale.y; break; - case GST_CHILDREN: { - PrecalculateChildrenScaleData pdata = { - .left_nodes = list_create(NULL), - .result = {0.0f, 0.0f}, - }; - - bintree_traverse(&element->children, BTT_INORDER, &precalculate_children_scale, &pdata); + case SCALE_CHILDREN: { + v3f32 scale = {0.0f, 0.0f, 0.0f}; + scale_elements(&element->children, 1 << true, &scale); - element->scale.x *= pdata.result.x; - element->scale.y *= pdata.result.y; + element->scale.x *= scale.x; + element->scale.y *= scale.y; - list_clear_func(&pdata.left_nodes, &list_calculate_element_scale, NULL); - traversed_children = true; - } break; + scale_elements(&element->children, 1 << false, NULL); + break; + } - case GST_NONE: + case SCALE_NONE: break; } - if (! traversed_children) - bintree_traverse(&element->children, BTT_INORDER, &bintree_calculate_element_scale, NULL); + if (element->def.scale_type != SCALE_CHILDREN) + scale_elements(&element->children, (1 << true) | (1 << false), NULL); +} + +static void transform_element(GUIElement *element) +{ + element->pos = (v2f32) { + floor(element->parent->pos.x + element->def.offset.x + element->def.pos.x * element->parent->scale.x - element->def.align.x * element->scale.x), + floor(element->parent->pos.y + element->def.offset.y + element->def.pos.y * element->parent->scale.y - element->def.align.y * element->scale.y), + }; + + mat4x4_translate(element->transform, element->pos.x - element->def.margin.x, element->pos.y - element->def.margin.y, 0.0f); + mat4x4_translate(element->text_transform, element->pos.x, element->pos.y, 0.0f); + 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); + + for (size_t i = 0; i < element->children.siz; i++) + transform_element(((GUIElement **) element->children.ptr)[i]); } -static void precalculate_children_scale(BintreeNode *node, void *arg) +// public functions + +bool gui_init() { - GUIElement *element = node->value; - PrecalculateChildrenScaleData *pdata = arg; + // initialize background pipeline - if (element->def.affect_parent_scale) { - assert(element->def.scale_type != GST_PARENT); - calculate_element_scale(element); + if (!shader_program_create(RESSOURCE_PATH "shaders/gui/background", &background_prog, NULL)) { + fprintf(stderr, "[error] failed to create GUI background shader program\n"); + return false; + } + + background_loc_model = glGetUniformLocation(background_prog, "model"); GL_DEBUG + background_loc_projection = glGetUniformLocation(background_prog, "projection"); GL_DEBUG + background_loc_color = glGetUniformLocation(background_prog, "color"); GL_DEBUG + + // initialize image pipeline + + if (!shader_program_create(RESSOURCE_PATH "shaders/gui/image", &image_prog, NULL)) { + fprintf(stderr, "[error] failed to create GUI image shader program\n"); + return false; + } - if (element->scale.x > pdata->result.x) - pdata->result.x = element->scale.x; + image_loc_model = glGetUniformLocation(image_prog, "model"); GL_DEBUG + image_loc_projection = glGetUniformLocation(image_prog, "projection"); GL_DEBUG - if (element->scale.y > pdata->result.y) - pdata->result.y = element->scale.y; - } else { - list_put(&pdata->left_nodes, element, NULL); + // initialize font pipeline + + if (!shader_program_create(RESSOURCE_PATH "shaders/gui/font", &font_prog, NULL)) { + fprintf(stderr, "[error] failed to create GUI font shader program\n"); + return false; } + + font_loc_model = glGetUniformLocation(font_prog, "model"); GL_DEBUG + font_loc_projection = glGetUniformLocation(font_prog, "projection"); GL_DEBUG + font_loc_color = glGetUniformLocation(font_prog, "color"); GL_DEBUG + + // initialize GUI root element + + root_element.def.pos = (v2f32) {0.0f, 0.0f}; + root_element.def.z_index = 0.0f; + root_element.def.offset = (v2s32) {0, 0}; + root_element.def.align = (v2f32) {0.0f, 0.0f}; + root_element.def.scale = (v2f32) {0.0f, 0.0f}; + root_element.def.scale_type = SCALE_NONE; + root_element.def.affect_parent_scale = false; + root_element.def.text = NULL; + root_element.def.image = NULL; + root_element.def.text_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f}; + root_element.def.bg_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f}; + root_element.visible = true; + root_element.pos = (v2f32) {0.0f, 0.0f}; + root_element.scale = (v2f32) {0.0f, 0.0f}; + root_element.text = NULL; + root_element.parent = &root_element; + array_ini(&root_element.children, sizeof(GUIElement *), 0); + + gui_update_projection(); + + return true; } -static void bintree_calculate_element_scale(BintreeNode *node, unused void *arg) +void gui_deinit() { - calculate_element_scale(node->value); + glDeleteProgram(background_prog); GL_DEBUG + mesh_destroy(&background_mesh); + + glDeleteProgram(image_prog); GL_DEBUG + mesh_destroy(&image_mesh); + + glDeleteProgram(font_prog); GL_DEBUG + + delete_elements(&root_element.children); } -static void list_calculate_element_scale(void *key, unused void *value, unused void *arg) +void gui_update_projection() { - calculate_element_scale(key); + mat4x4_ortho(projection, 0, window.width, window.height, 0, -1.0f, 1.0f); + glProgramUniformMatrix4fv(background_prog, background_loc_projection, 1, GL_FALSE, projection[0]); GL_DEBUG + glProgramUniformMatrix4fv(image_prog, image_loc_projection, 1, GL_FALSE, projection[0]); GL_DEBUG + glProgramUniformMatrix4fv(font_prog, font_loc_projection, 1, GL_FALSE, projection[0]); GL_DEBUG + + root_element.def.scale.x = window.width; + root_element.def.scale.y = window.height; + + gui_transform(&root_element); } -static void calculate_element_transform(GUIElement *element) +void gui_render() { - element->pos = (v2f32) { - floor(element->parent->pos.x + element->def.offset.x + element->def.pos.x * element->parent->scale.x - element->def.align.x * element->scale.x), - floor(element->parent->pos.y + element->def.offset.y + element->def.pos.y * element->parent->scale.y - element->def.align.y * element->scale.y), - }; + glDisable(GL_CULL_FACE); GL_DEBUG + glDisable(GL_DEPTH_TEST); GL_DEBUG - mat4x4_translate(element->transform, element->pos.x - element->def.margin.x, element->pos.y - element->def.margin.y, 0.0f); - mat4x4_translate(element->text_transform, element->pos.x, element->pos.y, 0.0f); + render_elements(&root_element.children); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpedantic" - 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); -#pragma GCC diagnostic pop + glEnable(GL_DEPTH_TEST); GL_DEBUG + glEnable(GL_CULL_FACE); GL_DEBUG +} - bintree_traverse(&element->children, BTT_INORDER, &bintree_calculate_element_transform, NULL); +GUIElement *gui_add(GUIElement *parent, GUIElementDefinition def) +{ + if (parent == NULL) + parent = &root_element; + + GUIElement *element = malloc(sizeof *element); + element->def = def; + element->visible = true; + element->parent = parent; + element->text = NULL; + + if (element->def.text) + element->def.text = strdup(element->def.text); + + array_ins(&parent->children, &element, &cmp_element); + array_ini(&element->children, sizeof(GUIElement), 0); + + if (element->def.affect_parent_scale) + gui_transform(parent); + else + gui_transform(element); + + return element; } -static void bintree_calculate_element_transform(BintreeNode *node, unused void *arg) +void gui_text(GUIElement *element, const char *text) { - calculate_element_transform(node->value); + if (element->def.text) + free(element->def.text); + + if (element->text) + font_delete(element->text); + + element->def.text = strdup(text); + element->text = NULL; } -void gui_update_transform(GUIElement *element) +void gui_transform(GUIElement *element) { - calculate_element_scale(element); - calculate_element_transform(element); + scale_element(element); + transform_element(element); }