More UI additions

This commit is contained in:
2025-06-10 20:33:51 +02:00
parent e80663b621
commit 438083503d
9 changed files with 365 additions and 104 deletions
+2 -1
View File
@@ -4,6 +4,7 @@ attribute vec3 position;
attribute vec2 uv;
uniform mat3 model;
uniform mat4 projection;
uniform float character;
varying vec2 f_uv;
@@ -26,5 +27,5 @@ void main ()
f_uv = char_offset + uv * char_size;
gl_Position = vec4 (model * vec3 (position.xy, 1.0), 1.0);
gl_Position = projection * vec4 (model * vec3 (position.xy, 1.0), 1.0);
}
+1 -1
View File
@@ -16,6 +16,6 @@ void main ()
}
else
{
gl_FragColor = vec4 (1.0, 1.0, 1.0, 1.0);
gl_FragColor = vec4 (0.0, 0.0, 0.0, 0.0);
}
}
+5 -10
View File
@@ -164,16 +164,11 @@ chunkman_render (vec3 position, vec4 planes[6])
program_use (RESPRG ("chunk"));
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, RESTEX ("terrain0"));
glActiveTexture (GL_TEXTURE1);
glBindTexture (GL_TEXTURE_2D, RESTEX ("terrain1"));
glActiveTexture (GL_TEXTURE2);
glBindTexture (GL_TEXTURE_2D, RESTEX ("terrain2"));
glActiveTexture (GL_TEXTURE3);
glBindTexture (GL_TEXTURE_2D, RESTEX ("terrain3"));
glActiveTexture (GL_TEXTURE4);
glBindTexture (GL_TEXTURE_2D, RESTEX ("terrain4"));
texture_use (RESTEX ("terrain0"), 0);
texture_use (RESTEX ("terrain1"), 1);
texture_use (RESTEX ("terrain2"), 2);
texture_use (RESTEX ("terrain3"), 3);
texture_use (RESTEX ("terrain4"), 4);
n_frame_chunks = 0;
+4 -5
View File
@@ -18,9 +18,9 @@ static GArray *entities;
static GQueue *spawn_requests;
static SimpleMesh mesh_quad1, mesh_quad2, mesh_quad_x;
static unsigned int last_program = -1;
static unsigned int last_type = -1;
static unsigned int last_texture = -1;
static unsigned int last_program = 0;
static unsigned int last_type = 0;
static unsigned int last_texture = 0;
void
character_on_tick (Entity *entity)
@@ -224,8 +224,7 @@ entman_render_entity (Entity *entity, void *userdata)
if (entity->texture != last_texture)
{
glActiveTexture (GL_TEXTURE0);
last_texture = texture_use (entity->texture);
last_texture = texture_use (entity->texture, 0);
}
if (entity->mesh_type != last_type)
+2 -1
View File
@@ -1,8 +1,9 @@
#include "texture.h"
unsigned int
texture_use (unsigned int texture)
texture_use (unsigned int texture, unsigned int slot)
{
glActiveTexture (GL_TEXTURE0 + slot);
glBindTexture (GL_TEXTURE_2D, texture);
return texture;
}
+1 -1
View File
@@ -2,5 +2,5 @@
#include <SDL3/SDL_opengles2.h>
unsigned int texture_use (unsigned int texture);
unsigned int texture_use (unsigned int texture, unsigned int slot);
unsigned int texture_create (int width, int height, unsigned char *data);
+40 -67
View File
@@ -5,6 +5,12 @@
extern SDL_Window *sdl_window;
extern int sdl_window_w, sdl_window_h;
extern float delta_time;
extern int n_frame_ents;
extern int n_frame_chunks;
extern int render_distance;
extern vec3 camera_position;
static Widget *ui_root;
static mat4 projection;
@@ -15,11 +21,25 @@ uiman_init ()
ui_widget_mesh_init ();
glm_ortho (0.0, (float) sdl_window_w, (float) sdl_window_h, 0.0, -1.0, 1.0, projection);
Widget *debug_menu = Widget_plain_new ();
Widget *debug_menu = Widget_container_new (FALSE);
Widget_set_texture (debug_menu, RESTEX ("terrain4"));
Widget_set_position (debug_menu, 32, 32);
Widget_set_size (debug_menu, 300, 300);
Widget_set_size (debug_menu, 300, 150);
Widget_add_child (ui_root, debug_menu);
Widget_add_child (debug_menu, Widget_label_make_float ("Frame time: %.3f", &delta_time, TEXT_ALIGN_LEFT));
Widget_add_child (debug_menu, Widget_label_make_int ("Render distance: %d", &render_distance, TEXT_ALIGN_LEFT));
Widget_add_child (debug_menu, Widget_label_make_int ("Chunks rendered: %d", &n_frame_chunks, TEXT_ALIGN_LEFT));
Widget_add_child (debug_menu, Widget_label_make_int ("Entities rendered: %d", &n_frame_ents, TEXT_ALIGN_LEFT));
Widget *poscont = Widget_container_new (TRUE);
Widget_add_child (poscont, Widget_label_make_float ("X: %.0f", &camera_position[0], TEXT_ALIGN_LEFT));
Widget_add_child (poscont, Widget_label_make_float ("Y: %.0f", &camera_position[1], TEXT_ALIGN_CENTER));
Widget_add_child (poscont, Widget_label_make_float ("Z: %.0f", &camera_position[2], TEXT_ALIGN_RIGHT));
Widget_add_child (debug_menu, poscont);
Widget_add_child (debug_menu, Widget_label_make_flag_switch_button ("Toggle wireframe", "wireframe_enabled"));
Widget_add_child (debug_menu, Widget_label_make_flag_switch_button ("Toggle flight", "flight_enabled"));
}
void
@@ -68,30 +88,11 @@ uiman_cb_key_up (SDL_Event *event, void *data)
gboolean
uiman_cb_mouse_motion (SDL_Event *event, void *data)
{
UNUSED (event, data);
/* int win_w, win_h; */
/* SDL_GetWindowSize (sdl_window, &win_w, &win_h); */
UNUSED (data);
/* vec2 ui_pos; */
/* screen_to_ui_coords (event->motion.x, event->motion.y, win_w, win_h, ui_pos); */
/* Widget *widget = Widget_find_xy (ui_root, GLM_MAT3_IDENTITY, ui_pos); */
/* if (widget != hovered_widget) */
/* { */
/* if (hovered_widget) */
/* { */
/* hovered_widget->hovered = false; */
/* } */
/* if (widget) */
/* { */
/* widget->hovered = true; */
/* } */
/* hovered_widget = widget; */
/* } */
Widget *widget = Widget_find_xy (ui_root, (vec2) { event->motion.x, event->motion.y });
UNUSED (widget);
/* This callback always eats the event, because why would the game
need mouse motion if the UI has stolen the focus? */
return TRUE;
@@ -102,43 +103,22 @@ uiman_cb_button_down (SDL_Event *event, void *data)
{
UNUSED (event, data);
/* int win_w, win_h; */
/* SDL_GetWindowSize (sdl_window, &win_w, &win_h); */
/* vec2 ui_pos; */
/* screen_to_ui_coords (event->motion.x, event->motion.y, win_w, win_h, ui_pos); */
/* Widget *widget = Widget_find_xy (ui_root, GLM_MAT3_IDENTITY, ui_pos); */
/* if (widget != clicked_widget) */
/* { */
/* if (clicked_widget) */
/* { */
/* clicked_widget->clicked = FALSE; */
/* } */
/* if (widget) */
/* { */
/* widget->clicked = TRUE; */
/* } */
/* clicked_widget = widget; */
/* } */
Widget *widget = Widget_find_xy (ui_root, (vec2) { event->motion.x, event->motion.y });
/* if (widget) */
/* { */
/* if (event->button.button == SDL_BUTTON_LEFT && widget->on_left_click) */
/* { */
/* widget->on_left_click (widget, widget->left_click_data); */
/* return true; */
/* } */
if (widget)
{
if (event->button.button == SDL_BUTTON_LEFT && widget->on_left_click)
{
widget->on_left_click (widget, widget->left_click_data);
return TRUE;
}
/* if (event->button.button == SDL_BUTTON_RIGHT && widget->on_right_click) */
/* { */
/* widget->on_right_click (widget, widget->right_click_data); */
/* return true; */
/* } */
/* } */
if (event->button.button == SDL_BUTTON_RIGHT && widget->on_right_click)
{
widget->on_right_click (widget, widget->right_click_data);
return TRUE;
}
}
return FALSE;
}
@@ -147,13 +127,6 @@ gboolean
uiman_cb_button_up (SDL_Event *event, void *data)
{
UNUSED (event, data);
/* if (clicked_widget) */
/* { */
/* clicked_widget->clicked = FALSE; */
/* clicked_widget = NULL; */
/* } */
return FALSE;
}
+248 -13
View File
@@ -1,5 +1,10 @@
#include "widget.h"
/* TODO: padding */
/* TODO: movable and resizable windows */
/* TODO: "spinner" UI element */
/* TODO: "checkbox" UI element */
static SimpleMesh quad;
static unsigned int last_program;
@@ -12,6 +17,7 @@ ui_el_new_internal (WidgetKind kind)
e->kind = kind;
e->visible = true;
e->children = g_array_new (false, false, sizeof (Widget *));
e->layout_weight = 1;
return e;
}
@@ -22,6 +28,24 @@ Widget_plain_new ()
return e;
}
Widget *
Widget_container_new (gboolean horizontal)
{
Widget *e = ui_el_new_internal (WIDGET_CONTAINER);
e->container.horizontal = horizontal;
return e;
}
Widget *
Widget_label_new (const char *text, TextAlignment alignment)
{
Widget *e = ui_el_new_internal (WIDGET_LABEL);
e->label.text = g_string_new (text);
e->label.text_alignment = alignment;
glm_vec4_one (e->label.text_color);
return e;
}
void
Widget_add_child (Widget *parent, Widget *child)
{
@@ -29,9 +53,16 @@ Widget_add_child (Widget *parent, Widget *child)
g_array_append_val (parent->children, child);
}
void
Widget_add_child_weighed (Widget *parent, Widget *child, float layout_weight)
{
child->layout_weight = layout_weight;
Widget_add_child (parent, child);
}
void
Widget_set_on_tick (Widget *widget,
void (*on_tick) (struct Widget *, void *),
void (*on_tick) (Widget *, void *),
void *userdata)
{
widget->on_tick = on_tick;
@@ -40,7 +71,7 @@ Widget_set_on_tick (Widget *widget,
void
Widget_set_on_left_click (Widget *widget,
void (*on_left_click) (struct Widget *, void *),
void (*on_left_click) (Widget *, void *),
void *userdata)
{
widget->on_left_click = on_left_click;
@@ -66,19 +97,60 @@ Widget_set_texture (Widget *widget, unsigned int texture)
widget->has_texture = TRUE;
}
void
Widget_label_set_text (Widget *widget, const char *text)
{
g_string_overwrite (widget->label.text, 0, text);
}
void
Widget_label_printf (Widget *widget, const char *format, ...)
{
va_list args;
va_start (args, format);
g_string_vprintf (widget->label.text, format, args);
va_end (args);
}
gboolean
Widget_hit_test (Widget *widget, vec2 point)
{
UNUSED (widget, point);
/* TODO: figure out. */
return FALSE;
float x = point[0];
float y = point[1];
float left = widget->position[0];
float top = widget->position[1];
float right = left + widget->size[0];
float bottom = top + widget->size[1];
return (x >= left && x <= right
&& y >= top && y <= bottom);
}
Widget *
Widget_find_xy (Widget *widget, vec2 pos)
{
UNUSED (widget, pos);
/* TODO: figure out. */
if (!widget->visible)
{
return NULL;
}
for (gint i = widget->children->len - 1; i >= 0; --i)
{
Widget *child = g_array_index (widget->children, Widget *, i);
Widget *found = Widget_find_xy (child, pos);
if (found != NULL)
{
return found;
}
}
if (Widget_hit_test (widget, pos))
{
return widget;
}
return NULL;
}
@@ -96,13 +168,102 @@ Widget_tick (Widget *widget)
widget->on_tick (widget, widget->tick_data);
}
for (guint i = 0; i < widget->children->len; ++i)
for (unsigned int i = 0; i < widget->children->len; ++i)
{
Widget *child = g_array_index (widget->children, Widget *, i);
Widget_tick (child);
}
}
void
Widget_label_render_text (Widget *widget, mat4 projection)
{
if (last_program != RESPRG ("uitext"))
{
last_program = program_use (RESPRG ("uitext"));
}
program_set_mat4 (RESPRG ("uitext"), "projection", projection);
texture_use (RESTEX ("font0"), 0);
float char_width = widget->size[1] * 0.5;
float total_width = g_utf8_strlen (widget->label.text->str, -1) * char_width;
float offset = 0.0;
switch (widget->label.text_alignment)
{
case TEXT_ALIGN_LEFT:
offset = -char_width;
break;
case TEXT_ALIGN_CENTER:
offset = (widget->size[0] / 2) - (total_width / 2.0) -char_width;
break;
case TEXT_ALIGN_RIGHT:
offset = widget->size[0] - total_width - char_width;
break;
}
const char *p = widget->label.text->str;
while (*p)
{
gunichar ch = g_utf8_get_char (p);
program_set_float (RESPRG ("uitext"), "character", ch);
program_set_vec4 (RESPRG ("uitext"), "text_color", widget->label.text_color);
mat3 model;
glm_translate2d_make (model, (vec2) { widget->position[0] + offset + char_width, widget->position[1] });
glm_scale2d (model, (vec2) { char_width, widget->size[1] });
offset += char_width;
program_set_mat3 (RESPRG ("uitext"), "model", model);
SimpleMesh_render (&quad, FALSE);
p = g_utf8_next_char (p);
}
}
void
Widget_container_layout (Widget *parent, gboolean horizontal)
{
float total_weights = 0.0;
for (unsigned int i = 0; i < parent->children->len; ++i)
{
Widget *child = g_array_index (parent->children, Widget *, i);
total_weights += child->layout_weight;
}
float offset = 0.0;
for (unsigned int i = 0; i < parent->children->len; ++i)
{
Widget *child = g_array_index (parent->children, Widget *, i);
if (horizontal)
{
float weights_size = parent->size[0] / total_weights;
child->size[0] = child->layout_weight * weights_size;
child->size[1] = parent->size[1];
child->position[0] = parent->position[0] + offset;
child->position[1] = parent->position[1];
offset += child->size[0];
}
else
{
float weights_size = parent->size[1] / total_weights;
child->size[1] = child->layout_weight * weights_size;
child->size[0] = parent->size[0];
child->position[1] = parent->position[1] + offset;
child->position[0] = parent->position[0];
offset += child->size[1];
}
}
}
void
Widget_render (Widget *widget, mat4 projection)
{
@@ -124,20 +285,94 @@ Widget_render (Widget *widget, mat4 projection)
}
program_set_int (RESPRG ("uiwidget"), "has_texture", widget->has_texture);
glActiveTexture (GL_TEXTURE0);
texture_use (widget->texture); /* If it doesn't have a texture, it'll be 0 anyway. */
texture_use (widget->texture, 0); /* If it doesn't have a texture, it'll be 0 anyway. */
mat3 model;
mat3 model;
glm_translate2d_make (model, widget->position);
glm_scale2d (model, widget->size);
program_set_mat3 (RESPRG ("uiwidget"), "model", model);
program_set_mat4 (RESPRG ("uiwidget"), "projection", projection);
SimpleMesh_render (&quad, false);
SimpleMesh_render (&quad, FALSE);
if (widget->kind == WIDGET_CONTAINER)
{
Widget_container_layout (widget, widget->container.horizontal);
}
if (widget->kind == WIDGET_LABEL)
{
Widget_label_render_text (widget, projection);
}
for (guint i = 0; i < widget->children->len; ++i)
for (unsigned int i = 0; i < widget->children->len; ++i)
{
Widget *child = g_array_index (widget->children, Widget *, i);
Widget_render (child, projection);
}
}
/* --------- */
LabelFormatData *
LabelFormatData_new_int (const char *fmt, int *ptr)
{
LabelFormatData *data = g_new (LabelFormatData, 1);
data->fmt = fmt;
data->ptr_int = ptr;
return data;
}
LabelFormatData *
LabelFormatData_new_float (const char *fmt, float *ptr)
{
LabelFormatData *data = g_new (LabelFormatData, 1);
data->fmt = fmt;
data->ptr_float = ptr;
return data;
}
void
uicb_label_show_int (Widget *self, void *userdata)
{
LabelFormatData *format_data = userdata;
Widget_label_printf (self, format_data->fmt, *format_data->ptr_int);
}
void uicb_label_show_float (Widget *self, void *userdata)
{
LabelFormatData *format_data = userdata;
Widget_label_printf (self, format_data->fmt, *format_data->ptr_float);
}
void
uicb_switch_flag (Widget *self, void *userdata)
{
UNUSED (self);
char *flag = userdata;
flags_switch (flag);
}
Widget *
Widget_label_make_int (const char *fmt, int *ptr, TextAlignment alignment)
{
Widget *w = Widget_label_new (NULL, alignment);
Widget_set_on_tick (w, uicb_label_show_int, LabelFormatData_new_int (fmt, ptr));
return w;
}
Widget *
Widget_label_make_float (const char *fmt, float *ptr, TextAlignment alignment)
{
Widget *w = Widget_label_new (NULL, alignment);
Widget_set_on_tick (w, uicb_label_show_float, LabelFormatData_new_float (fmt, ptr));
return w;
}
Widget *
Widget_label_make_flag_switch_button (const char *text, char *flag)
{
Widget *w = Widget_label_new (text, TEXT_ALIGN_CENTER);
Widget_set_on_left_click (w, uicb_switch_flag, flag);
return w;
}
+62 -5
View File
@@ -11,21 +11,46 @@
typedef enum WidgetKind
{
WIDGET_PLAIN
WIDGET_PLAIN,
WIDGET_CONTAINER,
WIDGET_LABEL
} WidgetKind;
typedef struct ContainerWidget
{
gboolean horizontal;
} ContainerWidget;
typedef enum TextAlignment
{
TEXT_ALIGN_LEFT,
TEXT_ALIGN_CENTER,
TEXT_ALIGN_RIGHT
} TextAlignment;
typedef struct LabelWidget
{
GString *text;
vec4 text_color;
TextAlignment text_alignment;
} LabelWidget;
typedef struct Widget
{
struct Widget *parent;
GArray *children;
WidgetKind kind;
union {
ContainerWidget container;
LabelWidget label;
};
gboolean visible;
vec2 position;
vec2 size;
float padding;
float layout_weight;
gboolean has_texture;
unsigned int texture;
@@ -39,18 +64,50 @@ typedef struct Widget
} Widget;
Widget *Widget_plain_new ();
Widget *Widget_container_new (gboolean horizontal);
Widget *Widget_label_new (const char *text, TextAlignment alignment);
void Widget_add_child (Widget *parent, Widget *child);
void Widget_add_child_weighed (Widget *parent, Widget *child,
float layout_weight);
void Widget_set_on_tick (Widget *widget, void (*on_tick) (struct Widget *, void *), void *userdata);
void Widget_set_on_left_click (Widget *widget, void (*on_left_click) (struct Widget *, void *), void *userdata);
void Widget_set_on_tick (Widget *widget,
void (*on_tick) (Widget *, void *),
void *userdata);
void Widget_set_on_left_click (Widget *widget,
void (*on_left_click) (Widget *, void *),
void *userdata);
void Widget_set_position (Widget *widget, float x, float y);
void Widget_set_size (Widget *widget, float x, float y);
void Widget_set_texture (Widget *widget, unsigned int texture);
void Widget_label_set_text (Widget *widget, const char *text);
void Widget_label_printf (Widget *widget, const char *format, ...);
Widget *Widget_find_xy (Widget *widget, vec2 point);
void ui_widget_mesh_init ();
void Widget_tick (Widget *widget);
void Widget_render (Widget *widget, mat4 projection);
/* ---------------------------------------------- */
typedef struct LabelFormatData
{
const char *fmt;
union {
int *ptr_int;
float *ptr_float;
};
} LabelFormatData;
LabelFormatData *LabelFormatData_new_int (const char *fmt, int *ptr);
LabelFormatData *LabelFormatData_new_float (const char *fmt, float *ptr);
void uicb_label_show_int (Widget *self, void *userdata);
void uicb_label_show_float (Widget *self, void *userdata);
void uicb_switch_flag (Widget *self, void *userdata);
Widget *Widget_label_make_int (const char *fmt, int *ptr, TextAlignment alignment);
Widget *Widget_label_make_float (const char *fmt, float *ptr, TextAlignment alignment);
Widget *Widget_label_make_flag_switch_button (const char *text, char *flag);