From 77ec3991c77c568e27406aac6ae8df4440535072 Mon Sep 17 00:00:00 2001 From: IllusionMan1212 Date: Wed, 29 Nov 2023 01:42:44 +0200 Subject: [PATCH] restructure zephr --- Makefile | 2 +- cudoku.c | 15 +- cudoku.h | 6 - font.c | 155 -------------- font.h | 12 -- main.c | 7 +- text.c | 364 ++++++++++++++++++++++++++++++++ text.h | 32 +++ todo.txt | 3 +- ui.c | 444 +-------------------------------------- ui.h | 49 +---- z_math.h | 39 ---- zephr.c | 111 ++++++++++ zephr.h | 22 ++ helper.c => zephr_math.c | 71 ++++--- helper.h => zephr_math.h | 46 +++- 16 files changed, 618 insertions(+), 760 deletions(-) delete mode 100644 font.c delete mode 100644 font.h create mode 100644 text.c create mode 100644 text.h delete mode 100644 z_math.h create mode 100644 zephr.c create mode 100644 zephr.h rename helper.c => zephr_math.c (98%) rename helper.h => zephr_math.h (52%) diff --git a/Makefile b/Makefile index 7a0cce3..e793022 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ BIN=cudoku CC=gcc CFLAGS=-Wall -Wextra -O3 -pedantic -g `pkg-config --cflags freetype2` -I3rdparty/glad/include -I3rdparty/stb -I3rdparty/fmod/include -OBJ=main.o x11.o cudoku.o shader.o font.o helper.o audio.o timer.o ui.o 3rdparty/glad/src/gl.o 3rdparty/glad/src/glx.o 3rdparty/stb/stb.o +OBJ=main.o x11.o cudoku.o shader.o text.o audio.o timer.o ui.o zephr.o zephr_math.o 3rdparty/glad/src/gl.o 3rdparty/glad/src/glx.o 3rdparty/stb/stb.o LDFLAGS=`pkg-config --libs x11 freetype2` -lm -L3rdparty/fmod/lib -Wl,-rpath=3rdparty/fmod/lib -lfmod DEPS=3rdparty/glad/include/glad/gl.h 3rdparty/glad/include/glad/glx.h 3rdparty/stb/stb_image.h 3rdparty/stb/stb_ds.h 3rdparty/fmod/include/fmod.h diff --git a/cudoku.c b/cudoku.c index 469a4cf..d3d2541 100644 --- a/cudoku.c +++ b/cudoku.c @@ -8,9 +8,10 @@ #include "3rdparty/stb/stb_image.h" #include "cudoku.h" -#include "shader.h" #include "audio.h" #include "ui.h" +#include "text.h" +#include "zephr.h" #define HELP_TEXT_SIZE 13 @@ -199,18 +200,6 @@ void draw_bg_grid_texture(Shader shader, unsigned int vao, unsigned int texture, glBindVertexArray(0); } -void draw_numbers(Shader shader, unsigned int vao, unsigned int vbo, float *transform, Cell board[9][9]) { - for (int i = 0; i < 9; i++) { - for (int j = 0; j < 9; j++) { - if (board[i][j].value != 0) { - /* draw_number(shader, board[i][j], i, j, 5.0f, vao, vbo, transform); */ - } - } - } - - glBindVertexArray(0); -} - /* void draw_pause_overlay(Cudoku *game) { */ /* timer_pause(&game->timer); */ diff --git a/cudoku.h b/cudoku.h index d047f3a..318407b 100644 --- a/cudoku.h +++ b/cudoku.h @@ -2,7 +2,6 @@ #include -#include "helper.h" #include "shader.h" #include "timer.h" #include "ui.h" @@ -25,12 +24,7 @@ typedef struct Cudoku { float win_time; } Cudoku; -void set_scale_factor(int width, int height, float *x, float *y); -unsigned int prepare_bg(bool use_texture, unsigned int *texture); -void draw_bg_grid_shader(Shader shader, unsigned int vao, float* transform, float resolution); void draw_bg_grid_texture(Shader shader, unsigned int vao, unsigned int texture, float* transform); -void draw_numbers(Shader shader, unsigned int vao, unsigned int vbo, float *transform, Cell board[9][9]); -void draw_win_overlay(Shader win_shader, Shader font_shader, unsigned int vao, unsigned int font_vao, unsigned int font_vbo, float *transform, Cudoku *game); void prepare_selection_box(unsigned int *vao, unsigned int *vbo); void draw_selection_box(Shader shader, unsigned int vao, unsigned int vbo, int x, int y, float *transform, Color color); void highlight_mistakes(Shader shader, unsigned int vao, unsigned vbo, float *transform, Cudoku *game); diff --git a/font.c b/font.c deleted file mode 100644 index 383113c..0000000 --- a/font.c +++ /dev/null @@ -1,155 +0,0 @@ -/* #include */ - -/* #include "font.h" */ -/* #include "shader.h" */ - -/* void draw_number(Shader shader, Cell cell, int row, int column, float scale, unsigned int vao, unsigned int vbo, float *transform) { */ -/* use_shader(shader); */ -/* if (cell.is_locked) { */ -/* set_vec3f(shader, "textColor", 44.f / 255.f, 54.f / 255.f, 57.f / 255.f); */ -/* } else { */ -/* set_vec3f(shader, "textColor", 110.f / 255.f, 133.f / 255.f, 183.f / 255.f); */ -/* } */ -/* set_mat4f(shader, "transform", transform); */ -/* glActiveTexture(GL_TEXTURE0); */ -/* glBindVertexArray(vao); */ - -/* // get ascii value of cell value and use it to index into characters */ -/* Character ch = characters['0' + cell.value]; */ - -/* float w = ch.size.width; */ -/* float h = ch.size.height; */ -/* float ratio = w / h; */ - -/* // draw the glyph over the entire board and then scale it down to the size */ -/* // of a single cell */ -/* float vert_h = 1.0; */ -/* float vert_w = vert_h * ratio; */ -/* // get size of a single cell in opengl coords (-1, 1) */ -/* float single_cell = (1.f/9.f) * 2; */ -/* // we minus 4 since we start at column and row 4 (0-indexed) */ -/* float offset_x = single_cell * (column - 4); */ -/* float offset_y = single_cell * (row - 4); */ - -/* // texture coords are y-flipped */ -/* float vertices[6][4] = { */ -/* // top left tri */ -/* { ((-(vert_w) / w * ratio) * scale) + offset_x, (-vert_h / h * scale) - offset_y, 0.0f, 1.0f }, */ -/* { ((-(vert_w) / w * ratio) * scale) + offset_x, ( vert_h / h * scale) - offset_y, 0.0f, 0.0f }, */ -/* { (( (vert_w) / w * ratio) * scale) + offset_x, ( vert_h / h * scale) - offset_y, 1.0f, 0.0f }, */ - -/* // bottom right tri */ -/* { ((-(vert_w) / w * ratio) * scale) + offset_x, (-vert_h / h * scale) - offset_y, 0.0f, 1.0f }, */ -/* { (( (vert_w) / w * ratio) * scale) + offset_x, ( vert_h / h * scale) - offset_y, 1.0f, 0.0f }, */ -/* { (( (vert_w) / w * ratio) * scale) + offset_x, (-vert_h / h * scale) - offset_y, 1.0f, 1.0f } */ -/* }; */ - -/* glBindTexture(GL_TEXTURE_2D, ch.texture_id); */ - -/* glBindBuffer(GL_ARRAY_BUFFER, vbo); */ -/* glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); */ - -/* glBindBuffer(GL_ARRAY_BUFFER, 0); */ - -/* glDrawArrays(GL_TRIANGLES, 0, 6); */ - -/* glBindVertexArray(0); */ -/* glBindTexture(GL_TEXTURE_2D, 0); */ -/* } */ - -/* void draw_text_center(Shader shader, const char *text, Size text_size, float scale, unsigned int vao, unsigned int vbo, float *transform, float offset_x, float offset_y) { */ -/* use_shader(shader); */ -/* set_vec3f(shader, "textColor", 200.f / 255.f, 200.f / 255.f, 200.f / 255.f); */ -/* set_mat4f(shader, "transform", transform); */ -/* glActiveTexture(GL_TEXTURE0); */ -/* glBindVertexArray(vao); */ - -/* int c = 0; */ -/* int x = 0; */ -/* while (text[c] != '\0') { */ -/* Character ch = characters[(int)text[c]]; */ - -/* float xpos = ((x + ch.bearing.width * scale + offset_x) / 900.f) - ((text_size.width / 2.f) * scale / 900.f); */ -/* float ypos = (((ch.bearing.height - ch.size.height + offset_y) * scale) / 900.f) - ((text_size.height / 2.f) * scale / 900.f); */ - -/* float w = (ch.size.width * scale / 900.f); */ -/* float h = (ch.size.height * scale / 900.f); */ - -/* float vertices[6][4] = { */ -/* // top left tri */ -/* { xpos, ypos + h, 0.0f, 0.0f }, */ -/* { xpos, ypos, 0.0f, 1.0f }, */ -/* { xpos + w, ypos, 1.0f, 1.0f }, */ - -/* // bottom right tri */ -/* { xpos, ypos + h, 0.0f, 0.0f }, */ -/* { xpos + w, ypos, 1.0f, 1.0f }, */ -/* { xpos + w, ypos + h, 1.0f, 0.0f } */ -/* }; */ - -/* glBindTexture(GL_TEXTURE_2D, ch.texture_id); */ - -/* glBindBuffer(GL_ARRAY_BUFFER, vbo); */ -/* glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); */ -/* glBindBuffer(GL_ARRAY_BUFFER, 0); */ - -/* glDrawArrays(GL_TRIANGLES, 0, 6); */ - -/* x += (ch.advance >> 6) * scale; */ -/* c++; */ -/* } */ - -/* glBindVertexArray(0); */ -/* glBindTexture(GL_TEXTURE_2D, 0); */ -/* } */ - -/* Color default_text_color = { 200.f / 255.f, 200.f / 255.f, 200.f / 255.f, 1.f }; */ - -/* void draw_text_at(Shader shader, const char *text, Vec2 pos, float scale, unsigned int vao, unsigned int vbo, float *projection, Color *color) { */ -/* use_shader(shader); */ -/* if (color != NULL) { */ -/* set_vec3f(shader, "textColor", color->r / 255.f, color->g / 255.f, color->b / 255.f); */ -/* } else { */ -/* set_vec3f(shader, "textColor", 200.f / 255.f, 200.f / 255.f, 200.f / 255.f); */ -/* } */ -/* set_mat4f(shader, "transform", (float *)projection); */ -/* glActiveTexture(GL_TEXTURE0); */ -/* glBindVertexArray(vao); */ - -/* int c = 0; */ -/* while (text[c] != '\0') { */ -/* Character ch = characters[(int)text[c]]; */ - -/* float xpos = pos.x + ch.bearing.width * scale; */ -/* float ypos = pos.y - (ch.size.height - ch.bearing.height) * scale; */ - -/* float w = (ch.size.width * scale); */ -/* float h = (ch.size.height * scale); */ - -/* float vertices[6][4] = { */ -/* // top left tri */ -/* { xpos, ypos + h, 0.0f, 0.0f }, */ -/* { xpos, ypos, 0.0f, 1.0f }, */ -/* { xpos + w, ypos, 1.0f, 1.0f }, */ - -/* // bottom right tri */ -/* { xpos, ypos + h, 0.0f, 0.0f }, */ -/* { xpos + w, ypos, 1.0f, 1.0f }, */ -/* { xpos + w, ypos + h, 1.0f, 0.0f } */ -/* }; */ - -/* glBindTexture(GL_TEXTURE_2D, ch.texture_id); */ - -/* glBindBuffer(GL_ARRAY_BUFFER, vbo); */ -/* glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); */ -/* glBindBuffer(GL_ARRAY_BUFFER, 0); */ - -/* glDrawArrays(GL_TRIANGLES, 0, 6); */ - -/* pos.x += (ch.advance >> 6) * scale; */ -/* c++; */ -/* } */ - -/* glBindVertexArray(0); */ -/* glBindTexture(GL_TEXTURE_2D, 0); */ -/* } */ diff --git a/font.h b/font.h deleted file mode 100644 index d7dee29..0000000 --- a/font.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "cudoku.h" -#include "shader.h" - - -int init_fonts(const char *font_path); -void prepare_font(unsigned int *vao, unsigned int *vbo); -Size calculate_text_size(const char *text, float scale); -void draw_number(Shader shader, Cell cell, int row, int column, float scale,unsigned int vao, unsigned int vbo, float* transform); -void draw_text_center(Shader shader, const char *text, Size text_size, float scale, unsigned int vao, unsigned int vbo, float *transform, float offset_x, float offset_y); -void draw_text_at(Shader shader, const char *text, Vec2 pos, float scale, unsigned int vao, unsigned int vbo, float *projection, Color *color); diff --git a/main.c b/main.c index 43cbf5b..5d60a72 100644 --- a/main.c +++ b/main.c @@ -7,8 +7,8 @@ #include #include "cudoku.h" -#include "ui.h" #include "audio.h" +#include "zephr.h" #define DEBUG 1 @@ -124,7 +124,7 @@ int main(int argc, char *argv[]) { printf("[ERROR]: could not initialize zephr\n"); return 1; } - make_window_non_resizable(); + zephr_make_window_non_resizable(); srand(time(NULL)); @@ -184,7 +184,6 @@ int main(int argc, char *argv[]) { /* /1* width = xce.width; *1/ */ /* /1* height = xce.height; *1/ */ /* /1* resize_x11_window(display, window); *1/ */ - /* /1* set_scale_factor(xce.width, xce.height, &x_scale, &y_scale); *1/ */ /* /1* } *1/ */ /* /1* } else if (xev.type == KeyPress) { *1/ */ @@ -217,8 +216,6 @@ int main(int argc, char *argv[]) { /* (float *)transform, */ /* selection_color); */ - /* /1* draw_numbers(font_shader, font_vao, font_vbo, (float *)transform, game.board); *1/ */ - /* /1* if (game.should_highlight_mistakes) { *1/ */ /* /1* highlight_mistakes(selection_shader, selection_vao, selection_vbo, (float *)transform, &game); *1/ */ diff --git a/text.c b/text.c new file mode 100644 index 0000000..af8fd5e --- /dev/null +++ b/text.c @@ -0,0 +1,364 @@ +#include +#include +#include + +#include +#include FT_FREETYPE_H +#include +#include + +#include "zephr.h" +#include "shader.h" +#include "text.h" + +#define FONT_PIXEL_SIZE 64 + +extern Context zephr_context; +Shader font_shader; +unsigned int font_vao; +unsigned int font_instance_vbo; + +void new_glyph_instance_list(GlyphInstanceList *list, uint capacity) { + list->size = 0; + list->capacity = capacity; + list->data = malloc(list->capacity * sizeof(TextInstance)); +} + +void clear_glyph_instance_list(GlyphInstanceList *list) { + list->size = 0; + memset(list->data, 0, list->size * sizeof(TextInstance)); +} + +void add_glyph_instance(GlyphInstanceList *list, TextInstance instance) { + if (list->size >= list->capacity) { + list->capacity *= 2; + TextInstance* temp = realloc(list->data, list->capacity * sizeof(TextInstance)); + if (!temp) { + printf("[FATAL] Failed to reallocate memory for glyph instance list\n"); + exit(1); + } + list->data = temp; + } + + list->data[list->size++] = instance; +} + +int init_freetype(const char* font_path) { + FT_Library ft; + if (FT_Init_FreeType(&ft)) { + return -1; + } + + FT_Face face; + if (FT_New_Face(ft, font_path, 0, &face)) { + return -2; + } + + // sets the variable font to be bold + /* if ((face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) { */ + /* printf("[INFO] Got a variable font\n"); */ + /* FT_MM_Var *mm; */ + /* FT_Get_MM_Var(face, &mm); */ + + /* FT_Set_Var_Design_Coordinates(face, mm->num_namedstyles, mm->namedstyle[mm->num_namedstyles - 4].coords); */ + + /* FT_Done_MM_Var(ft, mm); */ + /* } */ + + FT_Set_Pixel_Sizes(face, 0, FONT_PIXEL_SIZE); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + int pen_x = 0, pen_y = 0; + + /* FT_UInt glyph_idx; */ + /* FT_ULong c = FT_Get_First_Char(face, &glyph_idx); */ + + uint tex_width = 0, tex_height = 0; + for (uint i = 32; i < 128; i++) { + if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { + printf("[ERROR]: failed to load glyph for char '0x%x'\n", i); + } + + /* FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF); */ + + tex_width += face->glyph->bitmap.width + 1; + tex_height = max(tex_height, face->glyph->bitmap.rows); + } + + char *pixels = (char *)calloc(tex_width * tex_height, 1); + + for (uint i = 32; i < 128; i++) { + /* while (glyph_idx) { */ + if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { + printf("[ERROR]: failed to load glyph for char '0x%x'\n", i); + } + + /* FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF); */ + + FT_Bitmap *bmp = &face->glyph->bitmap; + + if (pen_x + bmp->width >= tex_width) { + pen_x = 0; + pen_y += (1 + (face->size->metrics.height >> 6)); + } + + for (uint row = 0; row < bmp->rows; row++) { + for (uint col = 0; col < bmp->width; col++) { + int x = pen_x + col; + int y = pen_y + row; + pixels[y * tex_width + x] = bmp->buffer[row * bmp->pitch + col]; + } + } + + float atlas_x0 = (float)pen_x / (float)tex_width; + float atlas_y0 = (float)pen_y / (float)tex_height; + float atlas_x1 = (float)(pen_x + bmp->width) / (float)tex_width; + float atlas_y1 = (float)(pen_y + bmp->rows) / (float)tex_height; + + Vec2f top_left = (Vec2f){ .x = atlas_x0, .y = atlas_y1 }; + Vec2f top_right = (Vec2f){ .x = atlas_x1, .y = atlas_y1 }; + Vec2f bottom_right = (Vec2f){ .x = atlas_x1, .y = atlas_y0 }; + Vec2f bottom_left = (Vec2f){ .x = atlas_x0, .y = atlas_y0 }; + + Character character; + + character.tex_coords[0] = top_left; + character.tex_coords[1] = top_right; + character.tex_coords[2] = bottom_right; + character.tex_coords[3] = bottom_left; + character.advance = face->glyph->advance.x; + character.size = (Size){ .width = face->glyph->bitmap.width, .height = face->glyph->bitmap.rows }; + character.bearing = (Size){ .width = face->glyph->bitmap_left, .height = face->glyph->bitmap_top }; + + zephr_context.font.characters[i] = character; + + pen_x += bmp->width + 1; + + /* c = FT_Get_Next_Char(face, c, &glyph_idx); */ + } + + unsigned int texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, tex_width, + tex_height, 0, GL_RED, GL_UNSIGNED_BYTE, + pixels); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + zephr_context.font.atlas_texture_id = texture; + + glBindTexture(GL_TEXTURE_2D, 0); + + FT_Done_Face(face); + FT_Done_FreeType(ft); + + free(pixels); + + return 0; +} + +int init_fonts(const char *font_path) { + uint font_vbo; + uint font_ebo; + + int res = init_freetype(font_path); + if (res != 0) { + return res; + } + + font_shader = create_shader("shaders/font.vert", "shaders/font.frag"); + + glGenVertexArrays(1, &font_vao); + glGenBuffers(1, &font_vbo); + glGenBuffers(1, &font_instance_vbo); + glGenBuffers(1, &font_ebo); + + float quad_vertices[4][2] = { + {0.0, 1.0}, // top left + {1.0, 1.0}, // top right + {1.0, 0.0}, // bottom right + {0.0, 0.0}, // bottom left + }; + + int quad_indices[6] = { + 0, 1, 2, + 2, 3, 0 + }; + + glBindVertexArray(font_vao); + + // font quad vbo + glBindBuffer(GL_ARRAY_BUFFER, font_vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(quad_vertices), quad_vertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0); + + // font instance vbo + glBindBuffer(GL_ARRAY_BUFFER, font_instance_vbo); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(TextInstance), (void *)0); + glVertexAttribDivisor(1, 1); + glEnableVertexAttribArray(2); + glVertexAttribIPointer(2, 1, GL_INT, sizeof(TextInstance), (void *)sizeof(Vec4f)); + glVertexAttribDivisor(2, 1); + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(TextInstance), (void *)(sizeof(Vec4f) + sizeof(int))); + glVertexAttribDivisor(3, 1); + glEnableVertexAttribArray(4); + glEnableVertexAttribArray(5); + glEnableVertexAttribArray(6); + glEnableVertexAttribArray(7); + glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(TextInstance), (void *)(sizeof(Vec4f) * 2 + sizeof(int))); + glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(TextInstance), (void *)(sizeof(Vec4f) * 3 + sizeof(int))); + glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(TextInstance), (void *)(sizeof(Vec4f) * 4 + sizeof(int))); + glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, sizeof(TextInstance), (void *)(sizeof(Vec4f) * 5 + sizeof(int))); + glVertexAttribDivisor(4, 1); + glVertexAttribDivisor(5, 1); + glVertexAttribDivisor(6, 1); + glVertexAttribDivisor(7, 1); + + // font ebo + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quad_indices), quad_indices, GL_STATIC_DRAW); + + use_shader(font_shader); + + for (int i = 32; i < 128; i++) { + for (int j = 0; j < 4; j++) { + char text[32]; + snprintf(text, 32, "texcoords[%d]", (i - 32) * 4 + j); + set_vec2f(font_shader, text, zephr_context.font.characters[i].tex_coords[j].x, zephr_context.font.characters[i].tex_coords[j].y); + } + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + return 0; +} + +Sizef calculate_text_size(const char *text, int font_size) { + float scale = (float)font_size / FONT_PIXEL_SIZE; + Sizef size = { .width = 0, .height = 0 }; + int w = 0; + int h = 0; + int max_bearing_h = 0; + + // NOTE: I don't like looping through the characters twice, but it's fine for now + for (int i = 0; text[i] != '\0'; i++) { + Character ch = zephr_context.font.characters[(uint)text[i]]; + max_bearing_h = max(max_bearing_h, ch.bearing.height); + } + + for (int i = 0; text[i] != '\0'; i++) { + Character ch = zephr_context.font.characters[(uint)text[i]]; + w += ch.advance / 64; + + // remove bearing of first character + if (i == 0 && strlen(text) > 1) { + w -= ch.bearing.width; + } + + // remove the trailing width of the last character + if (i == strlen(text) - 1) { + w -= ((ch.advance / 64) - (ch.bearing.width + ch.size.width)); + } + + // if we only have one character in the text, then remove the bearing width + if (strlen(text) == 1) { + w -= (ch.bearing.width); + } + + h = max(h, max_bearing_h - ch.bearing.height + ch.size.height); + } + size.width = (float)w * scale; + size.height = (float)h * scale; + return size; +} + +void draw_text(const char* text, int font_size, UIConstraints constraints, const Color *color, Alignment alignment) { + use_shader(font_shader); + Color text_color = { 0 }; + if (color) { + text_color = (Color){ color->r / 255.f, color->g / 255.f, color->b / 255.f, color->a / 255.f }; + } + set_mat4f(font_shader, "projection", (float *)zephr_context.projection.m); + + Vec2f pos = { 0.f, 0.f }; + Sizef size = {0}; + + apply_constraints(constraints, &pos, &size); + + Sizef text_size = calculate_text_size(text, FONT_PIXEL_SIZE); + float font_scale = (float)font_size / FONT_PIXEL_SIZE * size.width; + + apply_alignment(alignment, &pos, (Sizef){ text_size.width * font_scale, text_size.height * font_scale }); + + Matrix4x4 model = identity(); + apply_scale(&model, (Sizef){font_scale, font_scale}); + + // rotate around the center point of the text + apply_translation(&model, (Vec2f){-text_size.width * font_scale / 2.f, -text_size.height * font_scale / 2.f}); + apply_rotation(&model, to_radians(constraints.rotation)); + apply_translation(&model, (Vec2f){text_size.width *font_scale / 2.f, text_size.height * font_scale / 2.f}); + + apply_translation(&model, pos); + + int max_bearing_h = 0; + for (int i = 0; (i < text[i]) != '\0'; i++) { + Character ch = zephr_context.font.characters[(int)text[i]]; + max_bearing_h = max(max_bearing_h, ch.bearing.height); + } + + float first_char_bearing_w = zephr_context.font.characters[(int)text[0]].bearing.width; + + GlyphInstanceList glyph_instance_list; + new_glyph_instance_list(&glyph_instance_list, 16); + + // we use the original text and character sizes in the loop and then we just + // scale up or down the model matrix to get the desired font size. + // this way everything works out fine and we get to transform the text using the + // model matrix + int c = 0; + int x = 0; + while (text[c] != '\0') { + Character ch = zephr_context.font.characters[(int)text[c]]; + // subtract the bearing width of the first character to remove the extra space + // at the start of the text and move every char to the left by that width + float xpos = (x + (ch.bearing.width - first_char_bearing_w)); + float ypos = (text_size.height - ch.bearing.height - (text_size.height - max_bearing_h)); + + TextInstance instance = { + .position = (Vec4f){xpos, ypos, ch.size.width, ch.size.height}, + .tex_coords_index = (int)text[c] - 32, + .color = text_color, + .model = {0}, + }; + + memcpy(instance.model, model.m, sizeof(float[4][4])); + + add_glyph_instance(&glyph_instance_list, instance); + + x += (ch.advance >> 6); + c++; + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, zephr_context.font.atlas_texture_id); + glBindVertexArray(font_vao); + + glBindBuffer(GL_ARRAY_BUFFER, font_instance_vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(TextInstance) * glyph_instance_list.size, glyph_instance_list.data, GL_DYNAMIC_DRAW); + + glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL, glyph_instance_list.size); + + glBindVertexArray(0); + glBindTexture(GL_TEXTURE_2D, 0); + + free(glyph_instance_list.data); +} diff --git a/text.h b/text.h new file mode 100644 index 0000000..f7b6fa5 --- /dev/null +++ b/text.h @@ -0,0 +1,32 @@ +#pragma once + +#include "ui.h" + +typedef struct Character { + Size size; + Size bearing; + unsigned int advance; + Vec2f tex_coords[4]; // the 4 corners of the character quad +} Character; + +typedef struct ZephFont { + Character characters[128]; + unsigned int atlas_texture_id; +} ZephFont; + +typedef struct TextInstance { + Vec4f position; + int tex_coords_index; + Color color; + float model[4][4]; +} TextInstance; + +typedef struct GlyphInstanceList { + TextInstance *data; + int size; + int capacity; +} GlyphInstanceList; + +int init_fonts(const char *font_path); +Sizef calculate_text_size(const char *text, int font_size); +void draw_text(const char* text, int font_size, UIConstraints constraints, const Color *color, Alignment alignment); diff --git a/todo.txt b/todo.txt index e3a299e..9650dc9 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,8 @@ +zephr stuff - "premade" (but also customizable) ui elements (buttons, text boxes, etc) with instanced rendering - border decorations for shapes (maybe. or maybe only for ui elements) - some kind of ui layout/hierarchy system (for later, when we actually need it) -- grid texture (draw_image? or draw_texture? func) +- texture func (draw_image? or draw_texture? func) text rendering improvements - [ ] unicode support (allows for non-ascii characters) diff --git a/ui.c b/ui.c index e9e664b..51804a9 100644 --- a/ui.c +++ b/ui.c @@ -1,181 +1,21 @@ #include #include -#include -#include FT_FREETYPE_H -#include #include #include #include "cudoku.h" #include "shader.h" -#include "audio.h" #include "ui.h" #include "x11.h" +#include "zephr.h" -#define FONT_PIXEL_SIZE 64 - -Display *display; -Window window; - -Context zephr_context = {0}; -Shader font_shader; +extern Context zephr_context; Shader ui_shader; -unsigned int font_vao; unsigned int ui_vao; -unsigned int font_instance_vbo; unsigned int ui_vbo; -void new_glyph_instance_list(GlyphInstanceList *list, uint capacity) { - list->size = 0; - list->capacity = capacity; - list->data = malloc(list->capacity * sizeof(TextInstance)); -} - -void clear_glyph_instance_list(GlyphInstanceList *list) { - list->size = 0; - memset(list->data, 0, list->size * sizeof(TextInstance)); -} - -void add_glyph_instance(GlyphInstanceList *list, TextInstance instance) { - if (list->size >= list->capacity) { - list->capacity *= 2; - TextInstance* temp = realloc(list->data, list->capacity * sizeof(TextInstance)); - if (!temp) { - printf("[FATAL] Failed to reallocate memory for glyph instance list\n"); - exit(1); - } - list->data = temp; - } - - list->data[list->size++] = instance; -} - -int init_fonts(const char *font_path) { - FT_Library ft; - if (FT_Init_FreeType(&ft)) { - return -1; - } - - FT_Face face; - if (FT_New_Face(ft, font_path, 0, &face)) { - return -2; - } - - // sets the variable font to be bold - /* if ((face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) { */ - /* printf("[INFO] Got a variable font\n"); */ - /* FT_MM_Var *mm; */ - /* FT_Get_MM_Var(face, &mm); */ - - /* FT_Set_Var_Design_Coordinates(face, mm->num_namedstyles, mm->namedstyle[mm->num_namedstyles - 4].coords); */ - - /* FT_Done_MM_Var(ft, mm); */ - /* } */ - - FT_Set_Pixel_Sizes(face, 0, FONT_PIXEL_SIZE); - - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - int pen_x = 0, pen_y = 0; - - /* FT_UInt glyph_idx; */ - /* FT_ULong c = FT_Get_First_Char(face, &glyph_idx); */ - - uint tex_width = 0, tex_height = 0; - for (uint i = 32; i < 128; i++) { - if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { - printf("[ERROR]: failed to load glyph for char '0x%x'\n", i); - } - - /* FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF); */ - - tex_width += face->glyph->bitmap.width + 1; - tex_height = max(tex_height, face->glyph->bitmap.rows); - } - - printf("[INFO] Font atlas texture size: %d,%d\n", tex_width, tex_height); - - char *pixels = (char *)calloc(tex_width * tex_height, 1); - - for (uint i = 32; i < 128; i++) { - /* while (glyph_idx) { */ - if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { - printf("[ERROR]: failed to load glyph for char '0x%x'\n", i); - } - - /* FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF); */ - - FT_Bitmap *bmp = &face->glyph->bitmap; - - if (pen_x + bmp->width >= tex_width) { - pen_x = 0; - pen_y += (1 + (face->size->metrics.height >> 6)); - } - - for (uint row = 0; row < bmp->rows; row++) { - for (uint col = 0; col < bmp->width; col++) { - int x = pen_x + col; - int y = pen_y + row; - pixels[y * tex_width + x] = bmp->buffer[row * bmp->pitch + col]; - } - } - - float atlas_x0 = (float)pen_x / (float)tex_width; - float atlas_y0 = (float)pen_y / (float)tex_height; - float atlas_x1 = (float)(pen_x + bmp->width) / (float)tex_width; - float atlas_y1 = (float)(pen_y + bmp->rows) / (float)tex_height; - - Vec2f top_left = (Vec2f){ .x = atlas_x0, .y = atlas_y1 }; - Vec2f top_right = (Vec2f){ .x = atlas_x1, .y = atlas_y1 }; - Vec2f bottom_right = (Vec2f){ .x = atlas_x1, .y = atlas_y0 }; - Vec2f bottom_left = (Vec2f){ .x = atlas_x0, .y = atlas_y0 }; - - Character character; - - character.tex_coords[0] = top_left; - character.tex_coords[1] = top_right; - character.tex_coords[2] = bottom_right; - character.tex_coords[3] = bottom_left; - character.advance = face->glyph->advance.x; - character.size = (Size){ .width = face->glyph->bitmap.width, .height = face->glyph->bitmap.rows }; - character.bearing = (Size){ .width = face->glyph->bitmap_left, .height = face->glyph->bitmap_top }; - - zephr_context.font.characters[i] = character; - - pen_x += bmp->width + 1; - - /* c = FT_Get_Next_Char(face, c, &glyph_idx); */ - } - - unsigned int texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, tex_width, - tex_height, 0, GL_RED, GL_UNSIGNED_BYTE, - pixels); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - zephr_context.font.atlas_texture_id = texture; - - glBindTexture(GL_TEXTURE_2D, 0); - - FT_Done_Face(face); - FT_Done_FreeType(ft); - - free(pixels); - - return 0; -} - int init_ui(const char* font_path, Size window_size) { - uint font_vbo; - uint font_ebo; - zephr_context.window_size = window_size; zephr_context.projection = orthographic_projection_2d(0.f, window_size.width, window_size.height, 0.f); @@ -188,74 +28,11 @@ int init_ui(const char* font_path, Size window_size) { return 1; } - font_shader = create_shader("shaders/font.vert", "shaders/font.frag"); ui_shader = create_shader("shaders/ui.vert", "shaders/ui.frag"); - glGenVertexArrays(1, &font_vao); glGenVertexArrays(1, &ui_vao); - glGenBuffers(1, &font_vbo); - glGenBuffers(1, &font_instance_vbo); - glGenBuffers(1, &font_ebo); glGenBuffers(1, &ui_vbo); - float quad_vertices[4][2] = { - {0.0, 1.0}, // top left - {1.0, 1.0}, // top right - {1.0, 0.0}, // bottom right - {0.0, 0.0}, // bottom left - }; - - int quad_indices[6] = { - 0, 1, 2, - 2, 3, 0 - }; - - glBindVertexArray(font_vao); - - // font quad vbo - glBindBuffer(GL_ARRAY_BUFFER, font_vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(quad_vertices), quad_vertices, GL_STATIC_DRAW); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0); - - // font instance vbo - glBindBuffer(GL_ARRAY_BUFFER, font_instance_vbo); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(TextInstance), (void *)0); - glVertexAttribDivisor(1, 1); - glEnableVertexAttribArray(2); - glVertexAttribIPointer(2, 1, GL_INT, sizeof(TextInstance), (void *)sizeof(Vec4f)); - glVertexAttribDivisor(2, 1); - glEnableVertexAttribArray(3); - glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(TextInstance), (void *)(sizeof(Vec4f) + sizeof(int))); - glVertexAttribDivisor(3, 1); - glEnableVertexAttribArray(4); - glEnableVertexAttribArray(5); - glEnableVertexAttribArray(6); - glEnableVertexAttribArray(7); - glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(TextInstance), (void *)(sizeof(Vec4f) * 2 + sizeof(int))); - glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(TextInstance), (void *)(sizeof(Vec4f) * 3 + sizeof(int))); - glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(TextInstance), (void *)(sizeof(Vec4f) * 4 + sizeof(int))); - glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, sizeof(TextInstance), (void *)(sizeof(Vec4f) * 5 + sizeof(int))); - glVertexAttribDivisor(4, 1); - glVertexAttribDivisor(5, 1); - glVertexAttribDivisor(6, 1); - glVertexAttribDivisor(7, 1); - - // font ebo - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_ebo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quad_indices), quad_indices, GL_STATIC_DRAW); - - use_shader(font_shader); - - for (int i = 32; i < 128; i++) { - for (int j = 0; j < 4; j++) { - char text[32]; - snprintf(text, 32, "texcoords[%d]", (i - 32) * 4 + j); - set_vec2f(font_shader, text, zephr_context.font.characters[i].tex_coords[j].x, zephr_context.font.characters[i].tex_coords[j].y); - } - } - glBindVertexArray(ui_vao); glBindBuffer(GL_ARRAY_BUFFER, ui_vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW); @@ -268,141 +45,6 @@ int init_ui(const char* font_path, Size window_size) { return 0; } -// Has to be called after calling init_zephr() -void make_window_non_resizable() { - x11_make_window_non_resizable(display, window, zephr_context.window_size.width, zephr_context.window_size.height); -} - -int init_zephr(const char* font_path, const char* window_title, Size window_size, Color *clear_color) { - int res = audio_init(); - if (res != 0) { - printf("[ERROR]: failed to initialize audio\n"); - return 1; - } - - res = init_x11(&display, &window, window_title, window_size.width, window_size.height); - if (res < 0) return 1; - - res = init_ui(font_path, (Size){ .width = window_size.width, .height = window_size.height }); - if (res != 0) { - printf("[ERROR]: failed to initialize ui\n"); - return 1; - } - - if (clear_color) { - zephr_context.clear_color = *clear_color; - } else { - zephr_context.clear_color = (Color){0.0, 0.0, 0.0, 1.0}; - } - - get_screen_size(display, &zephr_context.screen_size.width, &zephr_context.screen_size.height); - start_internal_timer(); - - return 0; -} - -void deinit_zephr() { - close_x11(&display, &window); - audio_close(); -} - -bool zephr_should_quit() { - // TODO: input events - // TODO: window events - // TODO: ui update?? - - while (XPending(display)) { - XEvent xev; - XNextEvent(display, &xev); - - if (xev.type == ConfigureNotify) { - XConfigureEvent xce = xev.xconfigure; - - if (xce.width != zephr_context.window_size.width || xce.height != zephr_context.window_size.height) { - zephr_context.window_size = (Size){ .width = xce.width, .height = xce.height }; - zephr_context.projection = orthographic_projection_2d(0.f, xce.width, xce.height, 0.f); - resize_x11_window(display, window); - } - - } else if (xev.type == KeyPress) { - if (XLookupKeysym(&xev.xkey, 0) == XK_Escape || - (XLookupKeysym(&xev.xkey, 0) == XK_q && xev.xkey.state & ControlMask)) { - zephr_context.should_quit = true; - break; - } else { - /* handle_keypress(xev, &game); */ - } - } else if (xev.type == ButtonPress) { - /* if (xev.xbutton.button == Button1) { */ - /* int x = xev.xbutton.x; */ - /* int y = xev.xbutton.y; */ - /* do_selection(&game, x, y, width, height, x_scale, y_scale); */ - /* } */ - } - } - - float r = zephr_context.clear_color.r; - float g = zephr_context.clear_color.g; - float b = zephr_context.clear_color.b; - float a = zephr_context.clear_color.a; - glClearColor(r, g, b, a); - glClear(GL_COLOR_BUFFER_BIT); - - /* update_ui(); */ - audio_update(); - - - return zephr_context.should_quit; -} - -// This MUST be called at the end of the frame -void zephr_swap_buffers() { - glXSwapBuffers(display, window); -} - -Size zephr_get_window_size() { - return zephr_context.window_size; -} - -Sizef calculate_text_size(const char *text, int font_size) { - float scale = (float)font_size / FONT_PIXEL_SIZE; - Sizef size = { .width = 0, .height = 0 }; - int w = 0; - int h = 0; - int max_bearing_h = 0; - - // NOTE: I don't like looping through the characters twice, but it's fine for now - for (int i = 0; text[i] != '\0'; i++) { - Character ch = zephr_context.font.characters[(uint)text[i]]; - max_bearing_h = max(max_bearing_h, ch.bearing.height); - } - - for (int i = 0; text[i] != '\0'; i++) { - Character ch = zephr_context.font.characters[(uint)text[i]]; - w += ch.advance / 64; - - // remove bearing of first character - if (i == 0 && strlen(text) > 1) { - w -= ch.bearing.width; - } - - // remove the trailing width of the last character - if (i == strlen(text) - 1) { - w -= ((ch.advance / 64) - (ch.bearing.width + ch.size.width)); - } - - // if we only have one character in the text, then remove the bearing width - if (strlen(text) == 1) { - w -= (ch.bearing.width); - } - - h = max(h, max_bearing_h - ch.bearing.height + ch.size.height); - } - size.width = (float)w * scale; - size.height = (float)h * scale; - return size; -} - void set_x_constraint(UIConstraints *constraints, float value, UIConstraint type) { switch (type) { case UI_CONSTRAINT_RELATIVE_PIXELS: @@ -620,88 +262,6 @@ void draw_triangle(UIConstraints constraints, const Color *color, Alignment alig glBindVertexArray(0); } -void draw_text(const char* text, int font_size, UIConstraints constraints, const Color *color, Alignment alignment) { - use_shader(font_shader); - Color text_color = { 0 }; - if (color) { - text_color = (Color){ color->r / 255.f, color->g / 255.f, color->b / 255.f, color->a / 255.f }; - } - set_mat4f(font_shader, "projection", (float *)zephr_context.projection.m); - - Vec2f pos = { 0.f, 0.f }; - Sizef size = {0}; - - apply_constraints(constraints, &pos, &size); - - Sizef text_size = calculate_text_size(text, FONT_PIXEL_SIZE); - float font_scale = (float)font_size / FONT_PIXEL_SIZE * size.width; - - apply_alignment(alignment, &pos, (Sizef){ text_size.width * font_scale, text_size.height * font_scale }); - - Matrix4x4 model = identity(); - apply_scale(&model, (Sizef){font_scale, font_scale}); - - // rotate around the center point of the text - apply_translation(&model, (Vec2f){-text_size.width * font_scale / 2.f, -text_size.height * font_scale / 2.f}); - apply_rotation(&model, to_radians(constraints.rotation)); - apply_translation(&model, (Vec2f){text_size.width *font_scale / 2.f, text_size.height * font_scale / 2.f}); - - apply_translation(&model, pos); - - int max_bearing_h = 0; - for (int i = 0; (i < text[i]) != '\0'; i++) { - Character ch = zephr_context.font.characters[(int)text[i]]; - max_bearing_h = max(max_bearing_h, ch.bearing.height); - } - - float first_char_bearing_w = zephr_context.font.characters[(int)text[0]].bearing.width; - - GlyphInstanceList glyph_instance_list; - new_glyph_instance_list(&glyph_instance_list, 16); - - // we use the original text and character sizes in the loop and then we just - // scale up or down the model matrix to get the desired font size. - // this way everything works out fine and we get to transform the text using the - // model matrix - int c = 0; - int x = 0; - while (text[c] != '\0') { - Character ch = zephr_context.font.characters[(int)text[c]]; - // subtract the bearing width of the first character to remove the extra space - // at the start of the text and move every char to the left by that width - float xpos = (x + (ch.bearing.width - first_char_bearing_w)); - float ypos = (text_size.height - ch.bearing.height - (text_size.height - max_bearing_h)); - - TextInstance instance = { - .position = (Vec4f){xpos, ypos, ch.size.width, ch.size.height}, - .tex_coords_index = (int)text[c] - 32, - .color = text_color, - .model = {0}, - }; - - memcpy(instance.model, model.m, sizeof(float[4][4])); - - add_glyph_instance(&glyph_instance_list, instance); - - x += (ch.advance >> 6); - c++; - } - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, zephr_context.font.atlas_texture_id); - glBindVertexArray(font_vao); - - glBindBuffer(GL_ARRAY_BUFFER, font_instance_vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(TextInstance) * glyph_instance_list.size, glyph_instance_list.data, GL_DYNAMIC_DRAW); - - glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL, glyph_instance_list.size); - - glBindVertexArray(0); - glBindTexture(GL_TEXTURE_2D, 0); - - free(glyph_instance_list.data); -} - /* enum Element { */ /* Rect, */ /* Circle, */ diff --git a/ui.h b/ui.h index e334527..5b9dcaf 100644 --- a/ui.h +++ b/ui.h @@ -3,42 +3,7 @@ #include #include -#include "z_math.h" - -typedef struct Character { - Size size; - Size bearing; - unsigned int advance; - Vec2f tex_coords[4]; // the 4 corners of the character quad -} Character; - -typedef struct ZephFont { - Character characters[128]; - unsigned int atlas_texture_id; -} ZephFont; - -typedef struct TextInstance { - Vec4f position; - int tex_coords_index; - Color color; - float model[4][4]; -} TextInstance; - -typedef struct GlyphInstanceList { - TextInstance *data; - int size; - int capacity; -} GlyphInstanceList; - -typedef struct Context { - bool should_quit; - Color clear_color; - Size screen_size; - Size window_size; - ZephFont font; - - Matrix4x4 projection; -} Context; +#include "zephr_math.h" typedef enum Alignment { ALIGN_TOP_LEFT, @@ -67,20 +32,14 @@ typedef struct UIConstraints { float rotation; } UIConstraints; -int init_zephr(const char* font_path, const char* window_title, Size window_size, Color *clear_color); -void deinit_zephr(); -void make_window_non_resizable(); -bool zephr_should_quit(); -void zephr_swap_buffers(); -Size zephr_get_window_size(); -void zephr_batch_text_draw(); +int init_ui(const char* font_path, Size window_size); void set_x_constraint(UIConstraints *constraints, float value, UIConstraint type); void set_y_constraint(UIConstraints *constraints, float value, UIConstraint type); void set_width_constraint(UIConstraints *constraints, float value, UIConstraint type); void set_height_constraint(UIConstraints *constraints, float value, UIConstraint type); void set_rotation_constraint(UIConstraints *constraints, float angle_d); +void apply_constraints(UIConstraints constraints, Vec2f *pos, Sizef *size); +void apply_alignment(Alignment align, Vec2f *pos, Sizef size); void draw_quad(UIConstraints constraints, const Color *color, float border_radius, Alignment align); void draw_circle(UIConstraints constraints, const Color *color, Alignment align); void draw_triangle(UIConstraints constraints, const Color *color, Alignment align); -void draw_text(const char* text, int font_size, UIConstraints constraints, const Color *color, Alignment align); -Sizef calculate_text_size(const char *text, int font_size); diff --git a/z_math.h b/z_math.h deleted file mode 100644 index 901891d..0000000 --- a/z_math.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -typedef struct { - float m[4][4]; -} Matrix4x4; - -typedef struct Size { - int width; - int height; -} Size; - -typedef struct Sizef { - float width; - float height; -} Sizef; - -typedef struct Vec2 { - int x; - int y; -} Vec2; - -typedef struct Vec2f { - float x; - float y; -} Vec2f; - -typedef struct Vec4f { - float x; - float y; - float z; - float w; -} Vec4f; - -typedef struct Color { - float r; - float g; - float b; - float a; -} Color; diff --git a/zephr.c b/zephr.c new file mode 100644 index 0000000..3d467c7 --- /dev/null +++ b/zephr.c @@ -0,0 +1,111 @@ +#include + +#include +#include + +#include "zephr.h" +#include "audio.h" +#include "x11.h" +#include "ui.h" +#include "timer.h" + +Display *display; +Window window; + +Context zephr_context = {0}; + +int init_zephr(const char* font_path, const char* window_title, Size window_size, Color *clear_color) { + int res = audio_init(); + if (res != 0) { + printf("[ERROR]: failed to initialize audio\n"); + return 1; + } + + res = init_x11(&display, &window, window_title, window_size.width, window_size.height); + if (res < 0) return 1; + + res = init_ui(font_path, (Size){ .width = window_size.width, .height = window_size.height }); + if (res != 0) { + printf("[ERROR]: failed to initialize ui\n"); + return 1; + } + + if (clear_color) { + zephr_context.clear_color = *clear_color; + } else { + zephr_context.clear_color = (Color){0.0, 0.0, 0.0, 1.0}; + } + + get_screen_size(display, &zephr_context.screen_size.width, &zephr_context.screen_size.height); + start_internal_timer(); + + return 0; +} + +void deinit_zephr() { + close_x11(&display, &window); + audio_close(); +} + +bool zephr_should_quit() { + // TODO: input events + // TODO: window events + // TODO: ui update?? + + while (XPending(display)) { + XEvent xev; + XNextEvent(display, &xev); + + if (xev.type == ConfigureNotify) { + XConfigureEvent xce = xev.xconfigure; + + if (xce.width != zephr_context.window_size.width || xce.height != zephr_context.window_size.height) { + zephr_context.window_size = (Size){ .width = xce.width, .height = xce.height }; + zephr_context.projection = orthographic_projection_2d(0.f, xce.width, xce.height, 0.f); + resize_x11_window(display, window); + } + + } else if (xev.type == KeyPress) { + if (XLookupKeysym(&xev.xkey, 0) == XK_Escape || + (XLookupKeysym(&xev.xkey, 0) == XK_q && xev.xkey.state & ControlMask)) { + zephr_context.should_quit = true; + break; + } else { + /* handle_keypress(xev, &game); */ + } + } else if (xev.type == ButtonPress) { + /* if (xev.xbutton.button == Button1) { */ + /* int x = xev.xbutton.x; */ + /* int y = xev.xbutton.y; */ + /* do_selection(&game, x, y, width, height, x_scale, y_scale); */ + /* } */ + } + } + + float r = zephr_context.clear_color.r; + float g = zephr_context.clear_color.g; + float b = zephr_context.clear_color.b; + float a = zephr_context.clear_color.a; + glClearColor(r, g, b, a); + glClear(GL_COLOR_BUFFER_BIT); + + /* update_ui(); */ + audio_update(); + + + return zephr_context.should_quit; +} + +// This MUST be called at the end of the frame +void zephr_swap_buffers() { + glXSwapBuffers(display, window); +} + +Size zephr_get_window_size() { + return zephr_context.window_size; +} + +// Has to be called after calling init_zephr() +void zephr_make_window_non_resizable() { + x11_make_window_non_resizable(display, window, zephr_context.window_size.width, zephr_context.window_size.height); +} diff --git a/zephr.h b/zephr.h new file mode 100644 index 0000000..a24ea68 --- /dev/null +++ b/zephr.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include "text.h" + +typedef struct Context { + bool should_quit; + Color clear_color; + Size screen_size; + Size window_size; + ZephFont font; + + Matrix4x4 projection; +} Context; + +int init_zephr(const char* font_path, const char* window_title, Size window_size, Color *clear_color); +void deinit_zephr(); +bool zephr_should_quit(); +void zephr_swap_buffers(); +Size zephr_get_window_size(); +void zephr_make_window_non_resizable(); diff --git a/helper.c b/zephr_math.c similarity index 98% rename from helper.c rename to zephr_math.c index 1741794..b92d8c7 100644 --- a/helper.c +++ b/zephr_math.c @@ -1,6 +1,41 @@ #include -#include "helper.h" +#include "zephr_math.h" + +int max(int a, int b) { + return a > b ? a : b; +} + +float maxf(float a, float b) { + return a > b ? a : b; +} + +float clampf(float value, float min, float max) { + if (value < min) { + return min; + } else if (value > max) { + return max; + } + + return value; +} + +float to_radians(float degrees) { + return degrees * (M_PI / 180.f); +} + +Matrix4x4 orthographic_projection_2d(float left, float right, float bottom, float top) { + Matrix4x4 result = identity(); + + result.m[0][0] = 2.0f / (right - left); + result.m[3][0] = -(right + left) / (right - left); + result.m[1][1] = 2.0f / (top - bottom); + result.m[3][1] = -(top + bottom) / (top - bottom); + result.m[2][2] = -1.0f; + result.m[3][3] = 1.0f; + + return result; +} Matrix4x4 identity() { Matrix4x4 result = {0}; @@ -57,37 +92,3 @@ void apply_translation(Matrix4x4 *matrix, Vec2f pos) { mult_4x4(matrix, &temp); } -Matrix4x4 orthographic_projection_2d(float left, float right, float bottom, float top) { - Matrix4x4 result = identity(); - - result.m[0][0] = 2.0f / (right - left); - result.m[3][0] = -(right + left) / (right - left); - result.m[1][1] = 2.0f / (top - bottom); - result.m[3][1] = -(top + bottom) / (top - bottom); - result.m[2][2] = -1.0f; - result.m[3][3] = 1.0f; - - return result; -} - -int max(int a, int b) { - return a > b ? a : b; -} - -float maxf(float a, float b) { - return a > b ? a : b; -} - -float clampf(float value, float min, float max) { - if (value < min) { - return min; - } else if (value > max) { - return max; - } - - return value; -} - -float to_radians(float degrees) { - return degrees * (M_PI / 180.f); -} diff --git a/helper.h b/zephr_math.h similarity index 52% rename from helper.h rename to zephr_math.h index c1e0061..118faaa 100644 --- a/helper.h +++ b/zephr_math.h @@ -1,16 +1,50 @@ #pragma once -#include +typedef struct { + float m[4][4]; +} Matrix4x4; -#include "z_math.h" +typedef struct Size { + int width; + int height; +} Size; +typedef struct Sizef { + float width; + float height; +} Sizef; + +typedef struct Vec2 { + int x; + int y; +} Vec2; + +typedef struct Vec2f { + float x; + float y; +} Vec2f; + +typedef struct Vec4f { + float x; + float y; + float z; + float w; +} Vec4f; + +typedef struct Color { + float r; + float g; + float b; + float a; +} Color; + +int max(int a, int b); +float maxf(float a, float b); +float clampf(float value, float min, float max); +float to_radians(float degrees); Matrix4x4 identity(); void mult_4x4(Matrix4x4 *multiplicant, Matrix4x4 *multiplier); void apply_scale(Matrix4x4 *matrix, Sizef scale); void apply_rotation(Matrix4x4 *model, float angle); void apply_translation(Matrix4x4 *matrix, Vec2f pos); Matrix4x4 orthographic_projection_2d(float left, float right, float bottom, float top); -int max(int a, int b); -float maxf(float a, float b); -float clampf(float value, float min, float max); -float to_radians(float degrees);