-Subproject commit df31600e08952b5911e0669da98491593997c623
+Subproject commit 4db792b46b498aca58e0c60c3693325b0b418fd9
--- /dev/null
+out vec4 outColor;
+
+uniform vec4 color;
+
+void main()
+{
+ outColor = color;
+}
--- /dev/null
+layout(location = 0) in vec2 vertexPosition;
+
+uniform mat4 model;
+uniform mat4 projection;
+
+void main()
+{
+ gl_Position = projection * model * vec4(vertexPosition, 0.0, 1.0);
+}
--- /dev/null
+in vec2 fragmentTextureCoords;
+
+out vec4 outColor;
+
+uniform sampler2D texture0;
+uniform vec4 color;
+
+void main()
+{
+ outColor = vec4(1.0, 1.0, 1.0, texture(texture0, fragmentTextureCoords).r) * color;
+}
--- /dev/null
+layout(location = 0) in vec2 vertexPosition;
+layout(location = 1) in vec2 vertexTextureCoords;
+
+out vec2 fragmentTextureCoords;
+
+uniform mat4 model;
+uniform mat4 projection;
+
+void main()
+{
+ gl_Position = projection * model * vec4(vertexPosition, 0.0, 1.0);
+ fragmentTextureCoords = vertexTextureCoords;
+}
--- /dev/null
+in vec2 fragmentTextureCoords;
+
+out vec4 outColor;
+
+uniform sampler2D texture0;
+
+void main()
+{
+ outColor = texture(texture0, fragmentTextureCoords);
+}
--- /dev/null
+layout(location = 0) in vec2 vertexPosition;
+layout(location = 1) in vec2 vertexTextureCoords;
+
+out vec2 fragmentTextureCoords;
+
+uniform mat4 model;
+uniform mat4 projection;
+
+void main()
+{
+ gl_Position = projection * model * vec4(vertexPosition, 0.0, 1.0);
+ fragmentTextureCoords = vertexTextureCoords;
+}
+++ /dev/null
-in vec2 fragmentTextureCoords;
-
-out vec4 outColor;
-
-uniform sampler2D texture0;
-uniform vec3 textColor;
-
-void main()
-{
- outColor = vec4(1.0, 1.0, 1.0, texture(texture0, fragmentTextureCoords).r) * vec4(textColor, 1.0);
-}
+++ /dev/null
-layout(location = 0) in vec2 vertexPosition;
-layout(location = 1) in vec2 vertexTextureCoords;
-
-out vec2 fragmentTextureCoords;
-
-uniform mat4 projection;
-uniform mat4 model;
-
-void main()
-{
- gl_Position = projection * model * vec4(vertexPosition, 0.0, 1.0);
- fragmentTextureCoords = vertexTextureCoords;
-}
+++ /dev/null
-in vec2 fragmentTextureCoords;
-
-out vec4 outColor;
-
-uniform sampler2D texture0;
-
-void main()
-{
- outColor = texture(texture0, fragmentTextureCoords);
-}
+++ /dev/null
-layout(location = 0) in vec2 vertexPosition;
-layout(location = 1) in vec2 vertexTextureCoords;
-
-out vec2 fragmentTextureCoords;
-
-uniform mat4 model;
-uniform mat4 projection;
-
-void main()
-{
- gl_Position = projection * model * vec4(vertexPosition, 0.0, 1.0);
- fragmentTextureCoords = vertexTextureCoords;
-}
-
client/facecache.c
client/font.c
client/game.c
- client/hud.c
+ client/gui.c
client/input.c
client/mesh.c
client/object.c
#include <pthread.h>
#include <dragontype/number.h>
#include "client/client.h"
-#include "client/hud.h"
#include "client/object.h"
extern struct ClientPlayer
bool fly; // can the player fly?
bool collision; // should the player collide with the floor?
Object *obj; // 3D mesh object (currently always invisible), not thread safe
- HUDElement *info_hud; // display position, temperature and humidity on HUD, not thread safe
} client_player;
void client_player_init(); // ClientPlayer singleton constructor
void client_player_deinit(); // ClientPlayer singleton destructor
-void client_player_add_to_scene(); // create mesh object and hud display
+void client_player_add_to_scene(); // create mesh object
void client_player_jump(); // jump if possible
v3f64 client_player_get_position(); // get position (thread-safe)
void client_player_tick(f64 dtime); // to be called every frame
-void client_player_update_info(); // update HUD info text
#endif
#include "environment.h"
#include "client/client_player.h"
#include "client/debug_menu.h"
-#include "client/hud.h"
+#include "client/gui.h"
#include "client/window.h"
#include "version.h"
DME_COUNT,
} DebugMenuEntry;
-static HUDElement *huds[DME_COUNT] = {NULL};
+static GUIElement *gui_elements[DME_COUNT] = {NULL};
static bool debug_menu_enabled = true;
static DebugMenuEntry last_always_visible = DME_POS;
s32 offset = -16;
for (DebugMenuEntry i = 0; i < DME_COUNT; i++) {
- huds[i] = hud_add((HUDElementDefinition) {
- .type = HUD_TEXT,
- .pos = {-1.0f, -1.0f, 0.0f},
+ gui_elements[i] = gui_add(&gui_root, (GUIElementDefinition) {
+ .pos = {0.0f, 0.0f},
+ .z_index = 0.1f,
.offset = {2, offset += 18},
- .type_def = {
- .text = {
- .text = "",
- .color = {1.0f, 1.0f, 1.0f},
- },
- },
+ .margin = {2, 2},
+ .align = {0.0f, 0.0f},
+ .scale = {1.0f, 1.0f},
+ .scale_type = GST_TEXT,
+ .affect_parent_scale = false,
+ .text = "",
+ .image = NULL,
+ .text_color = (v4f32) {1.0f, 1.0f, 1.0f, 1.0f},
+ .bg_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f},
});
}
}
{
debug_menu_enabled = ! debug_menu_enabled;
- for (DebugMenuEntry i = 0; i < DME_COUNT; i++)
- huds[i]->visible = debug_menu_enabled || i <= last_always_visible;
+ for (DebugMenuEntry i = 0; i < DME_COUNT; i++) {
+ gui_elements[i]->visible = debug_menu_enabled || i <= last_always_visible;
+ gui_elements[i]->def.bg_color.w = debug_menu_enabled ? 0.5f : 0.0f;
+ }
}
void debug_menu_update_version()
{
char text[BUFSIZ];
sprintf(text, "Dragonblocks Alpha %s", VERSION);
- hud_change_text(huds[DME_VERSION], text);
+ gui_set_text(gui_elements[DME_VERSION], text);
}
void debug_menu_update_fps(int fps)
{
char text[BUFSIZ];
sprintf(text, "%d FPS", fps);
- hud_change_text(huds[DME_FPS], text);
+ gui_set_text(gui_elements[DME_FPS], text);
}
void debug_menu_update_pos()
{
char text[BUFSIZ];
sprintf(text, "(%.1f %.1f %.1f)", client_player.pos.x, client_player.pos.y, client_player.pos.z);
- hud_change_text(huds[DME_POS], text);
+ gui_set_text(gui_elements[DME_POS], text);
}
void debug_menu_update_yaw()
{
char text[BUFSIZ];
sprintf(text, "yaw = %.1f", client_player.yaw / M_PI * 180.0);
- hud_change_text(huds[DME_YAW], text);
+ gui_set_text(gui_elements[DME_YAW], text);
}
void debug_menu_update_pitch()
{
char text[BUFSIZ];
sprintf(text, "pitch = %.1f", client_player.pitch / M_PI * 180.0);
- hud_change_text(huds[DME_PITCH], text);
+ gui_set_text(gui_elements[DME_PITCH], text);
}
void debug_menu_update_humidity()
{
char text[BUFSIZ];
sprintf(text, "humidity = %.2f", get_humidity((v3s32) {client_player.pos.x, client_player.pos.y, client_player.pos.z}));
- hud_change_text(huds[DME_HUMIDITY], text);
+ gui_set_text(gui_elements[DME_HUMIDITY], text);
}
void debug_menu_update_temperature()
{
char text[BUFSIZ];
sprintf(text, "temperature = %.2f", get_temperature((v3s32) {client_player.pos.x, client_player.pos.y, client_player.pos.z}));
- hud_change_text(huds[DME_TEMPERATURE], text);
+ gui_set_text(gui_elements[DME_TEMPERATURE], text);
}
void debug_menu_update_flight()
{
char text[BUFSIZ];
sprintf(text, "flight: %s", client_player.fly ? "enabled" : "disabled");
- hud_change_text(huds[DME_FLIGHT], text);
+ gui_set_text(gui_elements[DME_FLIGHT], text);
}
void debug_menu_update_collision()
{
char text[BUFSIZ];
sprintf(text, "collision: %s", client_player.collision ? "enabled" : "disabled");
- hud_change_text(huds[DME_COLLISION], text);
+ gui_set_text(gui_elements[DME_COLLISION], text);
}
void debug_menu_update_fullscreen()
{
char text[BUFSIZ];
sprintf(text, "fullscreen: %s", window.fullscreen ? "enabled" : "disabled");
- hud_change_text(huds[DME_FULLSCREEN], text);
+ gui_set_text(gui_elements[DME_FULLSCREEN], text);
}
void debug_menu_update_opengl()
{
char text[BUFSIZ];
sprintf(text, "OpenGL %s", glGetString(GL_VERSION));
- hud_change_text(huds[DME_OPENGL], text);
+ gui_set_text(gui_elements[DME_OPENGL], text);
}
void debug_menu_update_gpu()
{
char text[BUFSIZ];
sprintf(text, "%s", glGetString(GL_RENDERER));
- hud_change_text(huds[DME_GPU], text);
+ gui_set_text(gui_elements[DME_GPU], text);
}
#include FT_FREETYPE_H
#include "client/client.h"
#include "client/font.h"
-#include "client/hud.h"
#define NUM_CHARS 128
offset += ch->advance >> 6;
}
+ fnt->size = (v2f32) {offset, font.height};
+
return fnt;
}
typedef struct
{
+ v2f32 size;
Mesh **meshes;
size_t meshes_count;
} Font;
#include "client/client_node.h"
#include "client/client_player.h"
#include "client/debug_menu.h"
-#include "client/hud.h"
+#include "client/gui.h"
#include "client/input.h"
#include "client/font.h"
#include "client/window.h"
static void crosshair_init()
{
- hud_add((HUDElementDefinition) {
- .type = HUD_IMAGE,
- .pos = {0.0f, 0.0f, 0.0f},
+ gui_add(&gui_root, (GUIElementDefinition) {
+ .pos = {0.5f, 0.5f},
+ .z_index = 0.0f,
.offset = {0, 0},
- .type_def = {
- .image = {
- .texture = texture_get(RESSOURCEPATH "textures/crosshair.png"),
- .scale = {1.0f, 1.0f},
- .scale_type = HUD_SCALE_TEXTURE,
- },
- },
+ .margin = {0, 0},
+ .align = {0.5f, 0.5f},
+ .scale = {1.0f, 1.0f},
+ .scale_type = GST_IMAGE,
+ .affect_parent_scale = false,
+ .text = NULL,
+ .image = texture_get(RESSOURCEPATH "textures/crosshair.png"),
+ .text_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f},
+ .bg_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f},
});
}
client_player_tick(dtime);
scene_render();
- hud_render();
+
+ glDisable(GL_DEPTH_TEST);
+ gui_render();
glfwSwapBuffers(window.handle);
glfwPollEvents();
camera_set_position((v3f32) {0.0f, 0.0f, 0.0f});
camera_set_angle(0.0f, 0.0f);
- if (! hud_init())
+ if (! gui_init())
return false;
- hud_on_resize(width, height);
+ gui_on_resize(width, height);
debug_menu_init();
debug_menu_toggle();
client_map_stop();
font_deinit();
- hud_deinit();
+ gui_deinit();
scene_deinit();
return true;
--- /dev/null
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <GL/glew.h>
+#include <GL/gl.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"
+
+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),
+ },
+};
+
+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),
+ },
+ // textureCoordinates
+ {
+ .type = GL_FLOAT,
+ .length = 2,
+ .size = sizeof(VertexImageTextureCoordinates),
+ },
+};
+
+static VertexLayout image_vertex_layout = {
+ .attributes = image_vertex_attributes,
+ .count = 2,
+ .size = sizeof(VertexImage),
+};
+
+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 int bintree_compare_f32(void *v1, void *v2, unused Bintree *tree)
+{
+ f32 diff = (*(f32 *) v1) - (*(f32 *) v2);
+ return CMPBOUNDS(diff);
+}
+
+bool gui_init()
+{
+ // 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;
+}
+
+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(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)
+{
+ 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);
+}
+
+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);
+ }
+}
+
+void gui_render()
+{
+ bintree_traverse(&gui_root.children, BTT_INORDER, &render_element, NULL);
+}
+
+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);
+ }
+
+ bintree_insert(&parent->children, &element->def.z_index, element);
+
+ element->children = bintree_create(sizeof(f32), &bintree_compare_f32);
+
+ if (element->def.affect_parent_scale)
+ gui_update_transform(parent);
+ else
+ gui_update_transform(element);
+
+ return element;
+}
+
+void gui_set_text(GUIElement *element, const char *text)
+{
+ if (! element->def.text || strcmp(element->def.text, text)) {
+ element->def.text = strdup(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:
+ assert(element->def.image);
+ element->scale.x *= element->def.image->width;
+ element->scale.y *= element->def.image->height;
+ break;
+
+ case GST_TEXT:
+ assert(element->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) {
+ element->parent->pos.x + element->def.offset.x + element->def.pos.x * element->parent->scale.x - element->def.align.x * element->scale.x,
+ 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);
+
+#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
+
+ 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);
+}
+
+void gui_update_transform(GUIElement *element)
+{
+ calculate_element_scale(element);
+ calculate_element_transform(element);
+}
--- /dev/null
+#ifndef _GUI_H_
+#define _GUI_H_
+
+#include <stdbool.h>
+#include <linmath.h/linmath.h>
+#include <dragontype/bintree.h>
+#include <dragontype/list.h>
+#include <dragontype/number.h>
+#include "client/font.h"
+#include "client/texture.h"
+
+typedef enum
+{
+ GST_IMAGE,
+ GST_TEXT,
+ GST_PARENT,
+ GST_CHILDREN,
+ GST_NONE,
+} GUIScaleType;
+
+typedef struct
+{
+ v2f32 pos;
+ f32 z_index;
+ v2s32 offset;
+ v2s32 margin;
+ v2f32 align;
+ v2f32 scale;
+ GUIScaleType scale_type;
+ bool affect_parent_scale;
+ char *text;
+ Texture *image;
+ v4f32 text_color;
+ v4f32 bg_color;
+} GUIElementDefinition;
+
+typedef struct GUIElement
+{
+ GUIElementDefinition def;
+ bool visible;
+ v2f32 pos;
+ v2f32 scale;
+ mat4x4 transform;
+ mat4x4 text_transform;
+ Font *text;
+ struct GUIElement *parent;
+ Bintree children;
+} GUIElement;
+
+bool gui_init();
+void gui_deinit();
+void gui_on_resize(int width, int height);
+void gui_render();
+GUIElement *gui_add(GUIElement *parent, GUIElementDefinition def);
+void gui_set_text(GUIElement *element, const char *text);
+void gui_update_transform(GUIElement *element);
+
+extern GUIElement gui_root;
+
+#endif
+++ /dev/null
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <GL/glew.h>
-#include <GL/gl.h>
-#include "client/client.h"
-#include "client/cube.h"
-#include "client/hud.h"
-#include "client/mesh.h"
-#include "client/shader.h"
-#include "client/vertex.h"
-#include "util.h"
-
-static struct
-{
- List elements;
-
- 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_text_color;
-
- mat4x4 projection;
- int width, height;
-} hud;
-
-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),
- },
- // textureCoordinates
- {
- .type = GL_FLOAT,
- .length = 2,
- .size = sizeof(VertexImageTextureCoordinates),
- },
-};
-
-static VertexLayout image_vertex_layout = {
- .attributes = image_vertex_attributes,
- .count = 2,
- .size = sizeof(VertexImage),
-};
-
-static VertexImage image_vertices[6] = {
- {{-0.5, -0.5}, {+0.0, +0.0}},
- {{+0.5, -0.5}, {+1.0, +0.0}},
- {{+0.5, +0.5}, {+1.0, +1.0}},
- {{+0.5, +0.5}, {+1.0, +1.0}},
- {{-0.5, +0.5}, {+0.0, +1.0}},
- {{-0.5, -0.5}, {+0.0, +0.0}},
-};
-
-bool hud_init()
-{
- if (! shader_program_create(RESSOURCEPATH "shaders/hud/image", &hud.image_prog, NULL)) {
- fprintf(stderr, "Failed to create HUD image shader program\n");
- return false;
- }
-
- hud.image_loc_model = glGetUniformLocation(hud.image_prog, "model");
- hud.image_loc_projection = glGetUniformLocation(hud.image_prog, "projection");
-
- if (! shader_program_create(RESSOURCEPATH "shaders/hud/font", &hud.font_prog, NULL)) {
- fprintf(stderr, "Failed to create HUD font shader program\n");
- return false;
- }
-
- hud.font_loc_model = glGetUniformLocation(hud.font_prog, "model");
- hud.font_loc_projection = glGetUniformLocation(hud.font_prog, "projection");
- hud.font_loc_text_color = glGetUniformLocation(hud.font_prog, "textColor");
-
- hud.elements = list_create(NULL);
-
- hud.image_mesh = mesh_create();
- hud.image_mesh->textures = NULL;
- hud.image_mesh->textures_count = 1;
- hud.image_mesh->free_textures = false;
- hud.image_mesh->vertices = image_vertices;
- hud.image_mesh->vertices_count = 6;
- hud.image_mesh->free_vertices = false;
- hud.image_mesh->layout = &image_vertex_layout;
-
- return true;
-}
-
-static void free_element(void *key, unused void *value, unused void *arg)
-{
- HUDElement *element = key;
-
- if (element->def.type == HUD_TEXT) {
- font_delete(element->type_data.text);
- free(element->def.type_def.text.text);
- }
-
- free(element);
-}
-
-void hud_deinit()
-{
- glDeleteProgram(hud.image_prog);
- glDeleteProgram(hud.font_prog);
- mesh_delete(hud.image_mesh);
- list_clear_func(&hud.elements, &free_element, NULL);
-}
-
-static void element_transform(HUDElement *element)
-{
- v3f32 pos = {
- (f32) element->def.offset.x + (1.0f + element->def.pos.x) / 2.0f * (f32) hud.width,
- (f32) element->def.offset.y + (1.0f + element->def.pos.y) / 2.0f * (f32) hud.height,
- element->def.pos.z,
- };
-
- mat4x4_translate(element->transform, pos.x, pos.y, pos.z);
-
- if (element->def.type == HUD_IMAGE) {
- v2f32 scale = element->def.type_def.image.scale;
-
- switch (element->def.type_def.image.scale_type) {
- case HUD_SCALE_TEXTURE:
- scale.x *= element->def.type_def.image.texture->width;
- scale.y *= element->def.type_def.image.texture->height;
-
- break;
-
- case HUD_SCALE_SCREEN:
- scale.x *= hud.width * 2.0f;
- scale.y *= hud.height * 2.0f;
-
- break;
-
- default:
- break;
- }
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wpedantic"
- mat4x4_scale_aniso(element->transform, element->transform, scale.x, scale.y, 1.0f);
- }
-#pragma GCC diagnostic pop
-}
-
-void hud_on_resize(int width, int height)
-{
- hud.width = width;
- hud.height = height;
-
- mat4x4_ortho(hud.projection, 0, width, height, 0, -1.0f, 1.0f);
- glProgramUniformMatrix4fv(hud.image_prog, hud.image_loc_projection, 1, GL_FALSE, hud.projection[0]);
- glProgramUniformMatrix4fv(hud.font_prog, hud.font_loc_projection, 1, GL_FALSE, hud.projection[0]);
-
- for (ListPair *pair = hud.elements.first; pair != NULL; pair = pair->next)
- element_transform(pair->key);
-}
-
-void hud_render()
-{
- glActiveTexture(GL_TEXTURE0);
-
- for (ListPair *pair = hud.elements.first; pair != NULL; pair = pair->next) {
- HUDElement *element = pair->key;
-
- if (element->visible) {
- switch (element->def.type) {
- case HUD_IMAGE:
- glUseProgram(hud.image_prog);
- glUniformMatrix4fv(hud.image_loc_model, 1, GL_FALSE, element->transform[0]);
- hud.image_mesh->textures = &element->def.type_def.image.texture->id;
- mesh_render(hud.image_mesh);
-
- break;
-
- case HUD_TEXT:
- glUseProgram(hud.font_prog);
- glUniformMatrix4fv(hud.font_loc_model, 1, GL_FALSE, element->transform[0]);
- glUniform3f(hud.font_loc_text_color, element->def.type_def.text.color.x, element->def.type_def.text.color.y, element->def.type_def.text.color.z);
- font_render(element->type_data.text);
-
- break;
- };
- }
- }
-}
-
-HUDElement *hud_add(HUDElementDefinition def)
-{
- HUDElement *element = malloc(sizeof(HUDElement));
- element->def = def;
- element->visible = true;
-
- element_transform(element);
-
- if (element->def.type == HUD_TEXT) {
- element->def.type_def.text.text = strdup(element->def.type_def.text.text);
- element->type_data.text = font_create(element->def.type_def.text.text);
- }
-
- list_set(&hud.elements, element, NULL);
-
- return element;
-}
-
-void hud_change_text(HUDElement *element, const char *text)
-{
- if (strcmp(element->def.type_def.text.text, text)) {
- element->def.type_def.text.text = strdup(text);
- font_delete(element->type_data.text);
- element->type_data.text = font_create(text);
- }
-}
+++ /dev/null
-#ifndef _HUD_H_
-#define _HUD_H_
-
-#include <stdbool.h>
-#include <linmath.h/linmath.h>
-#include <dragontype/number.h>
-#include <dragontype/list.h>
-#include "client/font.h"
-#include "client/texture.h"
-
-typedef enum
-{
- HUD_SCALE_TEXTURE,
- HUD_SCALE_SCREEN,
- HUD_SCALE_NONE,
-} HUDImageScaleType;
-
-typedef enum
-{
- HUD_IMAGE,
- HUD_TEXT,
-} HUDElementType;
-
-typedef struct
-{
- HUDElementType type;
- v3f32 pos;
- v2s32 offset;
- union
- {
- struct {
- Texture *texture;
- v2f32 scale;
- HUDImageScaleType scale_type;
- } image;
- struct {
- char *text;
- v3f32 color;
- } text;
- } type_def;
-} HUDElementDefinition;
-
-typedef struct
-{
- HUDElementDefinition def;
- bool visible;
- mat4x4 transform;
- union
- {
- Font *text;
- } type_data;
-} HUDElement;
-
-bool hud_init();
-void hud_deinit();
-void hud_on_resize(int width, int height);
-void hud_render();
-HUDElement *hud_add(HUDElementDefinition def);
-void hud_change_text(HUDElement *element, const char *text);
-
-#endif
#include "client/client.h"
#include "client/client_player.h"
#include "client/debug_menu.h"
-#include "client/hud.h"
+#include "client/gui.h"
#include "client/input.h"
#include "client/window.h"
static struct
{
- HUDElement *pause_menu_hud;
+ GUIElement *pause_menu;
bool paused;
KeyListener pause_listener;
KeyListener fullscreen_listener;
static void enter_game()
{
glfwSetInputMode(window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
- input.pause_menu_hud->visible = false;
+ input.pause_menu->visible = false;
}
static void do_key_listener(KeyListener *listener)
if (input.paused) {
glfwSetInputMode(window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
- input.pause_menu_hud->visible = true;
+ input.pause_menu->visible = true;
} else {
enter_game();
}
input.collision_listener = create_key_listener(GLFW_KEY_C);
input.debug_menu_listener = create_key_listener(GLFW_KEY_F3);
- input.pause_menu_hud = hud_add((HUDElementDefinition) {
- .type = HUD_IMAGE,
- .pos = {-1.0f, -1.0f, 0.5f},
+ input.pause_menu = gui_add(&gui_root, (GUIElementDefinition) {
+ .pos = {0.0f, 0.0f},
+ .z_index = 0.5f,
.offset = {0, 0},
- .type_def = {
- .image = {
- .texture = texture_get(RESSOURCEPATH "textures/pause_layer.png"),
- .scale = {1.0f, 1.0f},
- .scale_type = HUD_SCALE_SCREEN
- },
- },
+ .margin = {0, 0},
+ .align = {0.0f, 0.0f},
+ .scale = {1.0f, 1.0f},
+ .scale_type = GST_PARENT,
+ .affect_parent_scale = false,
+ .text = NULL,
+ .image = NULL,
+ .text_color = {0.0f, 0.0f, 0.0f, 0.0f},
+ .bg_color = {0.0f, 0.0f, 0.0f, 0.4f},
});
glfwSetInputMode(window.handle, GLFW_STICKY_KEYS, GL_TRUE);
glBindVertexArray(mesh->VAO);
glDrawArrays(GL_TRIANGLES, 0, mesh->vertices_count);
-
}
#include <GL/glew.h>
#include <GL/gl.h>
#include "client/debug_menu.h"
-#include "client/hud.h"
+#include "client/gui.h"
#include "client/input.h"
#include "client/scene.h"
#include "client/window.h"
}
scene_on_resize(width, height);
- hud_on_resize(width, height);
+ gui_on_resize(width, height);
}
static void cursor_pos_callback(unused GLFWwindow *handle, double current_x, double current_y)
Map *map = malloc(sizeof(Map));
pthread_rwlock_init(&map->rwlck, NULL);
pthread_rwlock_init(&map->cached_rwlck, NULL);
- map->sectors = bintree_create(sizeof(v2s32));
+ map->sectors = bintree_create(sizeof(v2s32), NULL);
map->cached = NULL;
map->callbacks = callbacks;
return map;
}
-static void free_block(void *value, void *arg)
+static void free_block(BintreeNode *node, void *arg)
{
Map *map = arg;
if (map->callbacks.delete_block)
- map->callbacks.delete_block(value);
+ map->callbacks.delete_block(node->value);
- map_free_block(value);
+ map_free_block(node->value);
}
-static void free_sector(void *value, void *arg)
+static void free_sector(BintreeNode *node, void *arg)
{
- MapSector *sector = value;
+ MapSector *sector = node->value;
bintree_clear(§or->blocks, &free_block, arg);
pthread_rwlock_destroy(§or->rwlck);
sector = malloc(sizeof(MapSector));
pthread_rwlock_init(§or->rwlck, NULL);
sector->pos = pos;
- sector->blocks = bintree_create(sizeof(s32));
+ sector->blocks = bintree_create(sizeof(s32), NULL);
bintree_add_node(&map->sectors, nodeptr, &pos, sector);
}
#include <arpa/inet.h>
#include <dragontype/number.h>
-#define ever (;;) // infinite for loop with style
-#define INBRACES(str) str ? "(" : "", str ? str : "", str ? ")" : "" // wrapper for printf to optionally add a message in braces if message is not NULL
-#define CMPBOUNDS(x) x == 0 ? 0 : x > 0 ? 1 : -1 // resolves 1 if x > 0, 0 if x == 0 and -1 if x < 0
-#define fallthrough __attribute__ ((fallthrough)) // prevent compiler warning about implicit fallthrough with style
+#define ever (;;) // infinite for loop with style
+#define INBRACES(str) (str) ? "(" : "", (str) ? (str) : "", (str) ? ")" : "" // wrapper for printf to optionally add a message in braces if message is not NULL
+#define CMPBOUNDS(x) ((x) == 0 ? 0 : (x) > 0 ? 1 : -1) // resolves to 1 if x > 0, 0 if x == 0 and -1 if x < 0
+#define fallthrough __attribute__ ((fallthrough)) // prevent compiler warning about implicit fallthrough with style
#define unused __attribute__ ((unused))
-#define U32(x) (((u32) 1 << 31) + x)
+#define U32(x) (((u32) 1 << 31) + (x))
extern const char *program_name; // this has to be set to program name on startup