2023-11-21 02:52:15 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
#include <glad/glx.h>
|
|
|
|
|
|
|
|
|
|
#include "shader.h"
|
2023-11-29 01:42:44 +02:00
|
|
|
#include "zephr.h"
|
2023-11-21 02:52:15 +02:00
|
|
|
|
|
|
|
|
Shader ui_shader;
|
|
|
|
|
unsigned int ui_vao;
|
|
|
|
|
unsigned int ui_vbo;
|
|
|
|
|
|
|
|
|
|
int init_ui(const char* font_path, Size window_size) {
|
2023-12-11 22:42:19 +02:00
|
|
|
zephr_ctx.window.size = window_size;
|
|
|
|
|
zephr_ctx.projection = orthographic_projection_2d(0.f, window_size.width, window_size.height, 0.f);
|
2023-11-21 02:52:15 +02:00
|
|
|
|
|
|
|
|
int res = init_fonts(font_path);
|
|
|
|
|
if (res == -1) {
|
|
|
|
|
printf("[ERROR]: could not initialize freetype library\n");
|
|
|
|
|
return 1;
|
|
|
|
|
} else if (res == -2) {
|
|
|
|
|
printf("[ERROR]: could not load font file: \"%s\"\n", font_path);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ui_shader = create_shader("shaders/ui.vert", "shaders/ui.frag");
|
|
|
|
|
|
|
|
|
|
glGenVertexArrays(1, &ui_vao);
|
|
|
|
|
glGenBuffers(1, &ui_vbo);
|
|
|
|
|
|
|
|
|
|
glBindVertexArray(ui_vao);
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, ui_vbo);
|
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
|
|
|
|
|
glEnableVertexAttribArray(0);
|
|
|
|
|
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
|
|
|
|
|
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
|
glBindVertexArray(0);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void set_x_constraint(UIConstraints *constraints, float value, UIConstraint type) {
|
|
|
|
|
switch (type) {
|
|
|
|
|
case UI_CONSTRAINT_RELATIVE_PIXELS:
|
|
|
|
|
case UI_CONSTRAINT_ASPECT_RATIO:
|
|
|
|
|
// no-op. fallback to FIXED
|
|
|
|
|
case UI_CONSTRAINT_FIXED:
|
|
|
|
|
constraints->x = value;
|
|
|
|
|
break;
|
|
|
|
|
case UI_CONSTRAINT_RELATIVE:
|
2023-12-11 22:42:19 +02:00
|
|
|
constraints->x = (value * zephr_ctx.window.size.width);
|
2023-11-21 02:52:15 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void set_y_constraint(UIConstraints *constraints, float value, UIConstraint type) {
|
|
|
|
|
switch (type) {
|
|
|
|
|
case UI_CONSTRAINT_RELATIVE_PIXELS:
|
|
|
|
|
case UI_CONSTRAINT_ASPECT_RATIO:
|
|
|
|
|
// no-op. fallback to FIXED
|
|
|
|
|
case UI_CONSTRAINT_FIXED:
|
|
|
|
|
constraints->y = value;
|
|
|
|
|
break;
|
|
|
|
|
case UI_CONSTRAINT_RELATIVE:
|
2023-12-11 22:42:19 +02:00
|
|
|
constraints->y = (value * zephr_ctx.window.size.height);
|
2023-11-21 02:52:15 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void set_width_constraint(UIConstraints *constraints, float value, UIConstraint type) {
|
|
|
|
|
switch (type) {
|
|
|
|
|
case UI_CONSTRAINT_FIXED:
|
|
|
|
|
constraints->width = value;
|
|
|
|
|
break;
|
|
|
|
|
case UI_CONSTRAINT_RELATIVE:
|
2023-12-11 22:42:19 +02:00
|
|
|
constraints->width = (value * zephr_ctx.window.size.width);
|
2023-11-21 02:52:15 +02:00
|
|
|
break;
|
|
|
|
|
case UI_CONSTRAINT_RELATIVE_PIXELS:
|
2023-12-11 22:42:19 +02:00
|
|
|
constraints->width = zephr_ctx.window.size.width / (float)zephr_ctx.screen_size.width * value;
|
2023-11-21 02:52:15 +02:00
|
|
|
break;
|
|
|
|
|
case UI_CONSTRAINT_ASPECT_RATIO:
|
|
|
|
|
constraints->width = constraints->height * value;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void set_height_constraint(UIConstraints *constraints, float value, UIConstraint type) {
|
|
|
|
|
switch (type) {
|
|
|
|
|
case UI_CONSTRAINT_FIXED:
|
|
|
|
|
constraints->height = value;
|
|
|
|
|
break;
|
|
|
|
|
case UI_CONSTRAINT_RELATIVE:
|
2023-12-11 22:42:19 +02:00
|
|
|
constraints->height = (value * zephr_ctx.window.size.height);
|
2023-11-21 02:52:15 +02:00
|
|
|
break;
|
|
|
|
|
case UI_CONSTRAINT_RELATIVE_PIXELS:
|
2023-12-11 22:42:19 +02:00
|
|
|
constraints->height = zephr_ctx.window.size.height / (float)zephr_ctx.screen_size.height * value;
|
2023-11-21 02:52:15 +02:00
|
|
|
break;
|
|
|
|
|
case UI_CONSTRAINT_ASPECT_RATIO:
|
|
|
|
|
constraints->height = constraints->width * value;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-22 01:29:21 +02:00
|
|
|
void set_rotation_constraint(UIConstraints *constraints, float angle_d) {
|
|
|
|
|
constraints->rotation = angle_d;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-21 02:52:15 +02:00
|
|
|
void apply_constraints(UIConstraints constraints, Vec2f *pos, Sizef *size) {
|
|
|
|
|
*pos = (Vec2f){constraints.x, constraints.y};
|
|
|
|
|
*size = (Sizef){constraints.width, constraints.height};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void apply_alignment(Alignment align, Vec2f *pos, Sizef size) {
|
|
|
|
|
switch (align) {
|
|
|
|
|
case ALIGN_TOP_LEFT:
|
|
|
|
|
pos->x = pos->x;
|
|
|
|
|
pos->y = pos->y;
|
|
|
|
|
break;
|
|
|
|
|
case ALIGN_TOP_CENTER:
|
2023-12-11 22:42:19 +02:00
|
|
|
pos->x += zephr_ctx.window.size.width / 2.f - size.width / 2.f;
|
2023-11-21 02:52:15 +02:00
|
|
|
pos->y = pos->y;
|
|
|
|
|
break;
|
|
|
|
|
case ALIGN_TOP_RIGHT:
|
2023-12-11 22:42:19 +02:00
|
|
|
pos->x += zephr_ctx.window.size.width - size.width;
|
2023-11-21 02:52:15 +02:00
|
|
|
pos->y = pos->y;
|
|
|
|
|
break;
|
|
|
|
|
case ALIGN_BOTTOM_LEFT:
|
|
|
|
|
pos->x = pos->x;
|
2023-12-11 22:42:19 +02:00
|
|
|
pos->y += zephr_ctx.window.size.height - size.height;
|
2023-11-21 02:52:15 +02:00
|
|
|
break;
|
|
|
|
|
case ALIGN_BOTTOM_CENTER:
|
2023-12-11 22:42:19 +02:00
|
|
|
pos->x += zephr_ctx.window.size.width / 2.f - size.width / 2.f;
|
|
|
|
|
pos->y += zephr_ctx.window.size.height - size.height;
|
2023-11-21 02:52:15 +02:00
|
|
|
break;
|
|
|
|
|
case ALIGN_BOTTOM_RIGHT:
|
2023-12-11 22:42:19 +02:00
|
|
|
pos->x += zephr_ctx.window.size.width - size.width;
|
|
|
|
|
pos->y += zephr_ctx.window.size.height - size.height;
|
2023-11-21 02:52:15 +02:00
|
|
|
break;
|
|
|
|
|
case ALIGN_LEFT_CENTER:
|
|
|
|
|
pos->x = pos->x;
|
2023-12-11 22:42:19 +02:00
|
|
|
pos->y += zephr_ctx.window.size.height / 2.f - size.height / 2.f;
|
2023-11-21 02:52:15 +02:00
|
|
|
break;
|
|
|
|
|
case ALIGN_RIGHT_CENTER:
|
2023-12-11 22:42:19 +02:00
|
|
|
pos->x += zephr_ctx.window.size.width - size.width;
|
|
|
|
|
pos->y += zephr_ctx.window.size.height / 2.f - size.height / 2.f;
|
2023-11-21 02:52:15 +02:00
|
|
|
break;
|
|
|
|
|
case ALIGN_CENTER:
|
2023-12-11 22:42:19 +02:00
|
|
|
pos->x += zephr_ctx.window.size.width / 2.f - size.width / 2.f;
|
|
|
|
|
pos->y += zephr_ctx.window.size.height / 2.f - size.height / 2.f;
|
2023-11-21 02:52:15 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-26 21:32:19 +02:00
|
|
|
void draw_quad(UIConstraints constraints, const Color *color, float border_radius, Alignment align) {
|
2023-11-21 02:52:15 +02:00
|
|
|
use_shader(ui_shader);
|
|
|
|
|
|
|
|
|
|
if (color) {
|
|
|
|
|
set_vec4f(ui_shader, "aColor", color->r / 255.f, color->g / 255.f, color->b / 255.f, color->a / 255.f);
|
|
|
|
|
} else {
|
|
|
|
|
set_vec4f(ui_shader, "aColor", 0.f, 0.f, 0.f, 1.f);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-21 06:45:54 +02:00
|
|
|
set_float(ui_shader, "uiWidth", constraints.width);
|
|
|
|
|
set_float(ui_shader, "uiHeight", constraints.height);
|
|
|
|
|
set_float(ui_shader, "borderRadius", border_radius);
|
2023-12-11 22:42:19 +02:00
|
|
|
set_mat4f(ui_shader, "projection", (float *)zephr_ctx.projection.m);
|
2023-11-21 22:29:02 +02:00
|
|
|
|
|
|
|
|
Vec2f pos = { 0.f, 0.f };
|
|
|
|
|
Sizef size = { 0.f, 0.f };
|
|
|
|
|
|
|
|
|
|
apply_constraints(constraints, &pos, &size);
|
|
|
|
|
apply_alignment(align, &pos, size);
|
|
|
|
|
|
2023-11-21 06:45:54 +02:00
|
|
|
Matrix4x4 model = identity();
|
2023-11-22 01:29:21 +02:00
|
|
|
apply_translation(&model, (Vec2f){-size.width / 2.f, -size.height / 2.f});
|
|
|
|
|
apply_rotation(&model, to_radians(constraints.rotation));
|
|
|
|
|
apply_translation(&model, (Vec2f){size.width / 2.f, size.height / 2.f});
|
|
|
|
|
|
|
|
|
|
apply_translation(&model, pos);
|
2023-11-21 06:45:54 +02:00
|
|
|
|
2023-11-26 03:55:13 +02:00
|
|
|
set_mat4f(ui_shader, "model", (float *)model.m);
|
2023-11-21 02:52:15 +02:00
|
|
|
|
|
|
|
|
glBindVertexArray(ui_vao);
|
|
|
|
|
|
2023-11-21 22:29:02 +02:00
|
|
|
float vertices[6][4] = {
|
|
|
|
|
// bottom left tri
|
|
|
|
|
{0, 0 + size.height, 0.0, 1.0},
|
|
|
|
|
{0, 0, 0.0, 0.0},
|
|
|
|
|
{0 + size.width, 0, 1.0, 0.0},
|
|
|
|
|
|
|
|
|
|
// top right tri
|
|
|
|
|
{0, 0 + size.height, 0.0, 1.0},
|
|
|
|
|
{0 + size.width, 0, 1.0, 0.0},
|
|
|
|
|
{0 + size.width, 0 + size.height, 1.0, 1.0},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, ui_vbo);
|
|
|
|
|
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
|
|
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
|
|
|
|
|
|
|
|
glBindVertexArray(0);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-26 21:32:19 +02:00
|
|
|
void draw_circle(UIConstraints constraints, const Color *color, Alignment align) {
|
2023-11-21 22:29:02 +02:00
|
|
|
float radius = 0.f;
|
|
|
|
|
|
|
|
|
|
if (constraints.width > constraints.height) {
|
|
|
|
|
radius = constraints.height / 2.f;
|
|
|
|
|
} else {
|
|
|
|
|
radius = constraints.width / 2.f;
|
|
|
|
|
}
|
|
|
|
|
draw_quad(constraints, color, radius, align);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-26 21:32:19 +02:00
|
|
|
void draw_triangle(UIConstraints constraints, const Color *color, Alignment align) {
|
2023-11-21 22:29:02 +02:00
|
|
|
use_shader(ui_shader);
|
|
|
|
|
|
|
|
|
|
if (color) {
|
|
|
|
|
set_vec4f(ui_shader, "aColor", color->r / 255.f, color->g / 255.f, color->b / 255.f, color->a / 255.f);
|
|
|
|
|
} else {
|
|
|
|
|
set_vec4f(ui_shader, "aColor", 0.f, 0.f, 0.f, 1.f);
|
|
|
|
|
}
|
2023-12-11 22:42:19 +02:00
|
|
|
set_mat4f(ui_shader, "projection", (float *)zephr_ctx.projection.m);
|
2023-11-21 22:29:02 +02:00
|
|
|
|
2023-11-21 02:52:15 +02:00
|
|
|
Vec2f pos = { 0.f, 0.f };
|
|
|
|
|
Sizef size = { 0.f, 0.f };
|
|
|
|
|
|
|
|
|
|
apply_constraints(constraints, &pos, &size);
|
|
|
|
|
apply_alignment(align, &pos, size);
|
|
|
|
|
|
2023-11-21 22:29:02 +02:00
|
|
|
Matrix4x4 model = identity();
|
2023-11-22 01:29:21 +02:00
|
|
|
apply_translation(&model, (Vec2f){-size.width / 2.f, -size.height / 2.f});
|
|
|
|
|
apply_rotation(&model, to_radians(constraints.rotation));
|
|
|
|
|
apply_translation(&model, (Vec2f){size.width / 2.f, size.height / 2.f});
|
|
|
|
|
|
|
|
|
|
apply_translation(&model, pos);
|
2023-11-21 06:45:54 +02:00
|
|
|
|
2023-11-26 03:55:13 +02:00
|
|
|
set_mat4f(ui_shader, "model", (float *)model.m);
|
2023-11-21 22:29:02 +02:00
|
|
|
|
|
|
|
|
glBindVertexArray(ui_vao);
|
|
|
|
|
|
|
|
|
|
float vertices[3][4] = {
|
|
|
|
|
{0 + size.width, 0, 0.0, 0.0},
|
|
|
|
|
{0, 0, 0.0, 0.0},
|
|
|
|
|
{0 + size.width / 2, 0 + size.height, 0.0, 0.0},
|
2023-11-21 02:52:15 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, ui_vbo);
|
|
|
|
|
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
|
|
2023-11-21 22:29:02 +02:00
|
|
|
glDrawArrays(GL_TRIANGLES, 0, 3);
|
2023-11-21 02:52:15 +02:00
|
|
|
|
|
|
|
|
glBindVertexArray(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* enum Element { */
|
|
|
|
|
/* Rect, */
|
|
|
|
|
/* Circle, */
|
|
|
|
|
/* }; */
|
|
|
|
|
|
|
|
|
|
/* struct UIElement { */
|
|
|
|
|
/* Vec2 pos; */
|
|
|
|
|
/* Size size; */
|
|
|
|
|
/* Color *color; */
|
|
|
|
|
/* enum Element type; */
|
|
|
|
|
/* }; */
|
|
|
|
|
|
|
|
|
|
/* struct UIText { */
|
|
|
|
|
/* Vec2 pos; */
|
|
|
|
|
/* float scale; */
|
|
|
|
|
/* Color *color; */
|
|
|
|
|
/* const char* text; */
|
|
|
|
|
/* }; */
|
|
|
|
|
|
|
|
|
|
/* struct UIElement *create_element(Vec2 pos, Size size, Color *color, enum Element type) { */
|
|
|
|
|
/* struct UIElement *element = malloc(sizeof(struct UIElement)); */
|
|
|
|
|
/* element->pos = pos; */
|
|
|
|
|
/* element->size = size; */
|
|
|
|
|
/* element->color = color; */
|
|
|
|
|
/* element->type = type; */
|
|
|
|
|
/* return element; */
|
|
|
|
|
/* } */
|
|
|
|
|
|