-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
#include <GL/glew.h>
#include <GL/gl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include "client/client.h"
#include "client/cube.h"
#include "client/gui.h"
#include "client/mesh.h"
#include "client/shader.h"
-#include "client/vertex.h"
-#include "util.h"
+#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 struct
-{
- List elements;
+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),
+ },
+ .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,
+};
- GLuint background_prog;
- GLint background_loc_model;
- GLint background_loc_projection;
- GLint background_loc_color;
- Mesh *background_mesh;
+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
- GLuint image_prog;
- GLint image_loc_model;
- GLint image_loc_projection;
- Mesh *image_mesh;
+static mat4x4 projection;
- GLuint font_prog;
- GLint font_loc_model;
- GLint font_loc_projection;
- GLint font_loc_color;
+// element functions
- mat4x4 projection;
-} gui;
+static void delete_element(GUIElement *element);
+static void render_element(GUIElement *element);
+static void scale_element(GUIElement *element);
-GUIElement gui_root;
+static int cmp_element(const GUIElement *ea, const GUIElement *eb)
+{
+ return f32_cmp(&ea->def.z_index, &eb->def.z_index);
+}
-typedef struct
+static void delete_elements(Array *elements)
{
- GLfloat x, y;
-} __attribute__((packed)) VertexBackgroundPosition;
+ for (size_t i = 0; i < elements->siz; i++)
+ delete_element(((GUIElement **) elements->ptr)[i]);
+ array_clr(elements);
+}
-typedef struct
+static void delete_element(GUIElement *element)
{
- VertexBackgroundPosition position;
-} __attribute__((packed)) VertexBackground;
-
-static VertexAttribute background_vertex_attributes[1] = {
- // position
- {
- .type = GL_FLOAT,
- .length = 2,
- .size = sizeof(VertexBackgroundPosition),
- },
-};
+ delete_elements(&element->children);
-static VertexLayout background_vertex_layout = {
- .attributes = background_vertex_attributes,
- .count = 1,
- .size = sizeof(VertexBackground),
-};
+ if (element->def.text)
+ free(element->def.text);
-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}},
-};
+ if (element->text)
+ font_delete(element->text);
-typedef struct
+ free(element);
+}
+
+static void render_elements(Array *elements)
{
- GLfloat x, y;
-} __attribute__((packed)) VertexImagePosition;
+ for (size_t i = 0; i < elements->siz; i++)
+ render_element(((GUIElement **) elements->ptr)[i]);
+}
-typedef struct
+static void render_element(GUIElement *element)
{
- GLfloat s, t;
-} __attribute__((packed)) VertexImageTextureCoordinates;
+ if (element->visible) {
+ if (element->def.bg_color.w > 0.0f) {
+ glUseProgram(background_prog);
+ glUniformMatrix4fv(background_loc_model, 1, GL_FALSE, element->transform[0]);
+ glUniform4f(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(&background_mesh);
+ }
+
+ if (element->def.image) {
+ glUseProgram(image_prog);
+ glUniformMatrix4fv(image_loc_model, 1, GL_FALSE, element->transform[0]);
+ glBindTextureUnit(0, element->def.image->txo);
+ mesh_render(&image_mesh);
+ }
-typedef struct
+ if (element->text && element->def.text_color.w > 0.0f) {
+ glUseProgram(font_prog);
+ glUniformMatrix4fv(font_loc_model, 1, GL_FALSE, element->text_transform[0]);
+ glUniform4f(font_loc_color, element->def.text_color.x, element->def.text_color.y, element->def.text_color.z, element->def.text_color.w);
+ font_render(element->text);
+ }
+
+ render_elements(&element->children);
+ }
+}
+
+static void scale_elements(Array *elements, int mask, v3f32 *max)
{
- VertexImagePosition position;
- VertexImageTextureCoordinates textureCoordinates;
-} __attribute__((packed)) VertexImage;
-
-static VertexAttribute image_vertex_attributes[2] = {
- // position
- {
- .type = GL_FLOAT,
- .length = 2,
- .size = sizeof(VertexImagePosition),
- },
- // textureCoordinates
- {
- .type = GL_FLOAT,
- .length = 2,
- .size = sizeof(VertexImageTextureCoordinates),
- },
-};
+ for (size_t i = 0; i < elements->siz; i++) {
+ GUIElement *element = ((GUIElement **) elements->ptr)[i];
-static VertexLayout image_vertex_layout = {
- .attributes = image_vertex_attributes,
- .count = 2,
- .size = sizeof(VertexImage),
-};
+ if ((1 << element->def.affect_parent_scale) & mask) {
+ scale_element(element);
-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}},
-};
+ if (max) {
+ if (element->scale.x > max->x)
+ max->x = element->scale.x;
-static int bintree_compare_f32(void *v1, void *v2, unused Bintree *tree)
+ if (element->scale.y > max->y)
+ max->y = element->scale.y;
+ }
+ }
+ }
+}
+
+static void scale_element(GUIElement *element)
+{
+ element->scale = (v2f32) {
+ element->def.scale.x,
+ element->def.scale.y,
+ };
+
+ switch (element->def.scale_type) {
+ case SCALE_IMAGE:
+ element->scale.x *= element->def.image->width;
+ element->scale.y *= element->def.image->height;
+ break;
+
+ case SCALE_TEXT:
+ element->scale.x *= element->text->size.x;
+ element->scale.y *= element->text->size.y;
+ break;
+
+ case SCALE_PARENT:
+ element->scale.x *= element->parent->scale.x;
+ element->scale.y *= element->parent->scale.y;
+ break;
+
+ case SCALE_CHILDREN: {
+ v3f32 scale = {0.0f, 0.0f, 0.0f};
+ scale_elements(&element->children, 1 << true, &scale);
+
+ element->scale.x *= scale.x;
+ element->scale.y *= scale.y;
+
+ scale_elements(&element->children, 1 << false, NULL);
+ break;
+ }
+
+ case SCALE_NONE:
+ break;
+ }
+
+ if (element->def.scale_type != SCALE_CHILDREN)
+ scale_elements(&element->children, (1 << true) | (1 << false), NULL);
+}
+
+static void transform_element(GUIElement *element)
{
- f32 diff = (*(f32 *) v1) - (*(f32 *) v2);
- return CMPBOUNDS(diff);
+ 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]);
}
+// public functions
+
bool gui_init()
{
// initialize background pipeline
- if (! shader_program_create(RESSOURCE_PATH "shaders/gui/background", &gui.background_prog, NULL)) {
- fprintf(stderr, "Failed to create GUI background shader program\n");
+ 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;
}
- 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;
+ background_loc_model = glGetUniformLocation(background_prog, "model");
+ background_loc_projection = glGetUniformLocation(background_prog, "projection");
+ background_loc_color = glGetUniformLocation(background_prog, "color");
// initialize image pipeline
- if (! shader_program_create(RESSOURCE_PATH "shaders/gui/image", &gui.image_prog, NULL)) {
- fprintf(stderr, "Failed to create GUI image shader program\n");
+ 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;
}
- 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;
+ image_loc_model = glGetUniformLocation(image_prog, "model");
+ image_loc_projection = glGetUniformLocation(image_prog, "projection");
// initialize font pipeline
- if (! shader_program_create(RESSOURCE_PATH "shaders/gui/font", &gui.font_prog, NULL)) {
- fprintf(stderr, "Failed to create GUI font shader program\n");
+ 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;
}
- 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
+ font_loc_model = glGetUniformLocation(font_prog, "model");
+ font_loc_projection = glGetUniformLocation(font_prog, "projection");
+ font_loc_color = glGetUniformLocation(font_prog, "color");
// 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);
+ 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 free_element(BintreeNode *node, unused void *arg)
-{
- GUIElement *element = node->value;
-
- bintree_clear(&element->children, &free_element, NULL);
-
- if (element->def.text)
- free(element->def.text);
-
- if (element->text)
- font_delete(element->text);
-
- free(element);
-}
-
void gui_deinit()
{
- glDeleteProgram(gui.background_prog);
- mesh_delete(gui.background_mesh);
+ glDeleteProgram(background_prog);
+ mesh_destroy(&background_mesh);
- glDeleteProgram(gui.image_prog);
- mesh_delete(gui.image_mesh);
+ glDeleteProgram(image_prog);
+ mesh_destroy(&image_mesh);
- glDeleteProgram(gui.font_prog);
+ glDeleteProgram(font_prog);
- bintree_clear(&gui_root.children, &free_element, NULL);
+ delete_elements(&root_element.children);
}
-void gui_on_resize(int width, int height)
+void gui_update_projection()
{
- 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]);
+ mat4x4_ortho(projection, 0, window.width, window.height, 0, -1.0f, 1.0f);
+ glProgramUniformMatrix4fv(background_prog, background_loc_projection, 1, GL_FALSE, projection[0]);
+ glProgramUniformMatrix4fv(image_prog, image_loc_projection, 1, GL_FALSE, projection[0]);
+ glProgramUniformMatrix4fv(font_prog, font_loc_projection, 1, GL_FALSE, projection[0]);
- gui_root.def.scale.x = width;
- gui_root.def.scale.y = height;
+ root_element.def.scale.x = window.width;
+ root_element.def.scale.y = window.height;
- gui_update_transform(&gui_root);
-}
-
-static void render_element(BintreeNode *node, unused void *arg)
-{
- 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);
- }
-
- 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);
- }
-
- 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);
- font_render(element->text);
- }
-
- bintree_traverse(&element->children, BTT_INORDER, &render_element, NULL);
- }
+ gui_transform(&root_element);
}
void gui_render()
{
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
- bintree_traverse(&gui_root.children, BTT_INORDER, &render_element, NULL);
+
+ render_elements(&root_element.children);
+
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
}
GUIElement *gui_add(GUIElement *parent, GUIElementDefinition def)
{
- GUIElement *element = malloc(sizeof(GUIElement));
+ if (parent == NULL)
+ parent = &root_element;
+
+ GUIElement *element = malloc(sizeof *element);
element->def = def;
element->visible = true;
element->parent = parent;
element->text = NULL;
}
- bintree_insert(&parent->children, &element->def.z_index, element);
-
- element->children = bintree_create(sizeof(f32), &bintree_compare_f32);
+ array_ins(&parent->children, &element, (void *) &cmp_element);
+ array_ini(&element->children, sizeof(GUIElement), 0);
if (element->def.affect_parent_scale)
- gui_update_transform(parent);
+ gui_transform(parent);
else
- gui_update_transform(element);
+ gui_transform(element);
return element;
}
-void gui_set_text(GUIElement *element, char *text)
+void gui_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);
-}
-
-// 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)
-{
- element->scale = (v2f32) {
- element->def.scale.x,
- element->def.scale.y,
- };
-
- bool traversed_children = false;
-
- switch (element->def.scale_type) {
- case GST_IMAGE:
- element->scale.x *= element->def.image->width;
- element->scale.y *= element->def.image->height;
- break;
-
- case GST_TEXT:
- element->scale.x *= element->text->size.x;
- element->scale.y *= element->text->size.y;
- break;
-
- case GST_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);
-
- element->scale.x *= pdata.result.x;
- element->scale.y *= pdata.result.y;
-
- list_clear_func(&pdata.left_nodes, &list_calculate_element_scale, NULL);
- traversed_children = true;
- } break;
-
- case GST_NONE:
- break;
- }
-
- if (! traversed_children)
- bintree_traverse(&element->children, BTT_INORDER, &bintree_calculate_element_scale, NULL);
-}
-
-static void precalculate_children_scale(BintreeNode *node, void *arg)
-{
- GUIElement *element = node->value;
- PrecalculateChildrenScaleData *pdata = arg;
-
- if (element->def.affect_parent_scale) {
- assert(element->def.scale_type != GST_PARENT);
- calculate_element_scale(element);
-
- if (element->scale.x > pdata->result.x)
- pdata->result.x = element->scale.x;
-
- if (element->scale.y > pdata->result.y)
- pdata->result.y = element->scale.y;
- } else {
- list_put(&pdata->left_nodes, element, NULL);
- }
-}
-
-static void bintree_calculate_element_scale(BintreeNode *node, unused void *arg)
-{
- calculate_element_scale(node->value);
-}
-
-static void list_calculate_element_scale(void *key, unused void *value, unused void *arg)
-{
- calculate_element_scale(key);
-}
-
-static void calculate_element_transform(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);
-
- bintree_traverse(&element->children, BTT_INORDER, &bintree_calculate_element_transform, NULL);
-}
-
-static void bintree_calculate_element_transform(BintreeNode *node, unused void *arg)
-{
- calculate_element_transform(node->value);
+ gui_transform(element);
}
-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);
}