super basic UI constraint and alignment system with a super basic "game framework"

This commit is contained in:
2023-11-21 02:52:15 +02:00
parent 129e038c19
commit 3f840fcad7
20 changed files with 989 additions and 414 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
BIN=cudoku
CC=gcc
CFLAGS=-Wall -Wextra -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 3rdparty/glad/src/gl.o 3rdparty/glad/src/glx.o 3rdparty/stb/stb.o
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
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/fmod/include/fmod.h
+1 -1
View File
@@ -59,7 +59,7 @@ void audio_play_win() {
};
}
void audio_cleanup() {
void audio_close() {
FMOD_Sound_Release(win_sound);
FMOD_Sound_Release(scribble_sound);
FMOD_ChannelGroup_Release(sfx_channel_group);
+3 -1
View File
@@ -1,5 +1,7 @@
#pragma once
int audio_init();
void audio_cleanup();
void audio_close();
void audio_update();
void audio_play_scribble();
void audio_play_win();
+87 -43
View File
@@ -8,9 +8,9 @@
#include "3rdparty/stb/stb_image.h"
#include "cudoku.h"
#include "font.h"
#include "shader.h"
#include "audio.h"
#include "ui.h"
#define HELP_TEXT_SIZE 13
@@ -47,6 +47,32 @@ static const char *help_texts[HELP_TEXT_SIZE] = {
"Ctrl+Q/Esc - Quit"
};
void draw_win() {
Size window = zephr_get_window_size();
Color bg_color = {233.f, 84.f, 32.f, 200.f};
Color text_color = {237.f, 225.f, 215.f, 255.f};
UIConstraints constraints;
/* set_x_constraint(&constraints, 0, UI_CONSTRAINT_FIXED); */
/* set_y_constraint(&constraints, 0, UI_CONSTRAINT_FIXED); */
/* set_width_constraint(&constraints, window.width, UI_CONSTRAINT_FIXED); */
/* set_height_constraint(&constraints, window.height, UI_CONSTRAINT_FIXED); */
Alignment align = ALIGN_TOP_RIGHT;
float scale = 1.0f;
Sizef text_size = calculate_text_size(win_text, scale);
set_x_constraint(&constraints, 0, UI_CONSTRAINT_FIXED);
set_y_constraint(&constraints, 0, UI_CONSTRAINT_FIXED);
set_width_constraint(&constraints, text_size.width, UI_CONSTRAINT_FIXED);
set_height_constraint(&constraints, text_size.height, UI_CONSTRAINT_FIXED);
draw_quad(constraints, &bg_color, align);
draw_text(win_text, scale, (Vec2f){0.0, 0.0}, &text_color, align);
}
void set_scale_factor(int width, int height, float *x, float *y) {
if (width > height) {
float ratio = (float)height / (float)width;
@@ -134,7 +160,7 @@ void draw_numbers(Shader shader, unsigned int vao, unsigned int vbo, float *tran
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);
/* draw_number(shader, board[i][j], i, j, 5.0f, vao, vbo, transform); */
}
}
}
@@ -180,12 +206,30 @@ void draw_win_overlay(Shader win_shader, Shader font_shader, unsigned int vao, u
float time_elapsed = game->win_time - game->timer.time;
snprintf(time_text, 64, "Solved in: %02dm%02ds", (int)time_elapsed / 60, (int)time_elapsed % 60);
Size win_text_size = calculate_text_size(win_text, 1.0f);
Size time_text_size = calculate_text_size(time_text, 1.0f);
draw_text_center(font_shader, win_text, win_text_size, 1.5f, font_vao, font_vbo, transform, 0.0, 75.0);
draw_text_center(font_shader, time_text, time_text_size, 0.8f, font_vao, font_vbo, transform, 0.0, -75.0);
/* Size win_text_size = calculate_text_size(win_text, 1.0f); */
/* Size time_text_size = calculate_text_size(time_text, 1.0f); */
/* draw_text_center(font_shader, win_text, win_text_size, 1.5f, font_vao, font_vbo, transform, 0.0, 75.0); */
/* draw_text_center(font_shader, time_text, time_text_size, 0.8f, font_vao, font_vbo, transform, 0.0, -75.0); */
}
/* void draw_pause_overlay(Cudoku *game) { */
/* timer_pause(&game->timer); */
/* // */
/* // this is my basic idea of drawing ui elements */
/* // */
/* // examples of drawing shapes */
/* draw_quad(beginning_x, beginning_y, win_width, win_height); */
/* draw_circle(); */
/* draw_triangle(); */
/* // example of drawing text */
/* draw_text_at(pause_text, font_scale, posx, posy, color); */
/* // TODO: rotations, translation, scaling */
/* // TODO: buttons and other elements */
/* } */
void prepare_selection_box(unsigned int *vao, unsigned int *vbo) {
glGenVertexArrays(1, &(*vao));
glGenBuffers(1, &(*vbo));
@@ -625,9 +669,9 @@ void draw_help_overlay(Shader overlay_shader, Shader font_shader, unsigned int o
int overlay_width = 0;
for (int i = 0; i < HELP_TEXT_SIZE; i++) {
Size text_size = calculate_text_size(help_texts[i], text_scale);
overlay_height += text_size.height + text_padding;
overlay_width = max(overlay_width, text_size.width + text_padding * 2);
/* Size text_size = calculate_text_size(help_texts[i], text_scale); */
/* overlay_height += text_size.height + text_padding; */
/* overlay_width = max(overlay_width, text_size.width + text_padding * 2); */
}
Size size = {.width = overlay_width, .height = overlay_height + text_padding * 2};
Vec2 pos = {
@@ -643,30 +687,30 @@ void draw_help_overlay(Shader overlay_shader, Shader font_shader, unsigned int o
draw_ui_element_at(overlay_shader, overlay_vao, overlay_vbo, projection, pos, size, color);
for (int i = 0; i < HELP_TEXT_SIZE; i++) {
Size text_size = calculate_text_size(help_texts[i], text_scale);
Vec2 text_pos = {
.x = text_padding,
.y = window_height - total_help_texts_height - (text_padding * 2)
};
total_help_texts_height += text_size.height + text_padding;
draw_text_at(font_shader, help_texts[i], text_pos, text_scale, font_vao, font_vbo, (float *)projection->m, NULL);
/* Size text_size = calculate_text_size(help_texts[i], text_scale); */
/* Vec2 text_pos = { */
/* .x = text_padding, */
/* .y = window_height - total_help_texts_height - (text_padding * 2) */
/* }; */
/* total_help_texts_height += text_size.height + text_padding; */
/* draw_text_at(font_shader, help_texts[i], text_pos, text_scale, font_vao, font_vbo, (float *)projection->m, NULL); */
}
if (timer->is_running) {
char closing_in_text[128];
snprintf(closing_in_text, sizeof(closing_in_text), "Hiding in %ds", (int)timer_remaining(timer) + 1);
Size closing_in_text_size = calculate_text_size(closing_in_text, text_scale / 2.0);
Vec2 closing_in_text_pos = {
.x = overlay_width - closing_in_text_size.width,
.y = window_height - closing_in_text_size.height - text_padding,
};
Color closing_in_text_color = {
.r = 255.0f,
.g = 150.0f,
.b = 0.0f,
.a = 1.0f,
};
draw_text_at(font_shader, closing_in_text, closing_in_text_pos, text_scale / 2.0, font_vao, font_vbo, (float *)projection->m, &closing_in_text_color);
/* snprintf(closing_in_text, sizeof(closing_in_text), "Hiding in %ds", (int)timer_remaining(timer) + 1); */
/* Size closing_in_text_size = calculate_text_size(closing_in_text, text_scale / 2.0); */
/* Vec2 closing_in_text_pos = { */
/* .x = overlay_width - closing_in_text_size.width, */
/* .y = window_height - closing_in_text_size.height - text_padding, */
/* }; */
/* Color closing_in_text_color = { */
/* .r = 255.0f, */
/* .g = 150.0f, */
/* .b = 0.0f, */
/* .a = 1.0f, */
/* }; */
/* draw_text_at(font_shader, closing_in_text, closing_in_text_pos, text_scale / 2.0, font_vao, font_vbo, (float *)projection->m, &closing_in_text_color); */
}
}
@@ -679,19 +723,19 @@ void draw_timer(Shader shader, unsigned int vao, unsigned int vbo, float *projec
int minutes = elapsed / 60;
int seconds = (int)elapsed % 60;
snprintf(timer_text, sizeof(timer_text), "Time: %02d:%02d", minutes, seconds);
float text_scale = 0.35f;
Size text_size = calculate_text_size(timer_text, text_scale);
Vec2 text_pos = {
.x = win_width - text_size.width - 10,
.y = 10,
};
Color text_color = {
.r = 66.0f,
.g = 92.0f,
.b = 124.0f,
.a = 1.0f,
};
/* snprintf(timer_text, sizeof(timer_text), "Time: %02d:%02d", minutes, seconds); */
/* float text_scale = 0.35f; */
/* Size text_size = calculate_text_size(timer_text, text_scale); */
/* Vec2 text_pos = { */
/* .x = win_width - text_size.width - 10, */
/* .y = 10, */
/* }; */
/* Color text_color = { */
/* .r = 66.0f, */
/* .g = 92.0f, */
/* .b = 124.0f, */
/* .a = 1.0f, */
/* }; */
draw_text_at(shader, timer_text, text_pos, text_scale, vao, vbo, projection, &text_color);
/* draw_text_at(shader, timer_text, text_pos, text_scale, vao, vbo, projection, &text_color); */
}
+2 -12
View File
@@ -5,11 +5,7 @@
#include "helper.h"
#include "shader.h"
#include "timer.h"
typedef struct Vec2 {
int x;
int y;
} Vec2;
#include "ui.h"
typedef struct Cell {
int value;
@@ -29,13 +25,6 @@ typedef struct Cudoku {
float win_time;
} Cudoku;
typedef struct Color {
float r;
float g;
float b;
float a;
} Color;
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);
@@ -66,3 +55,4 @@ void draw_help_overlay(
Timer *timer
);
void draw_timer(Shader shader, unsigned int vao, unsigned int vbo, float *projection, Timer *timer, int win_width);
void draw_win();
+120 -213
View File
@@ -1,248 +1,155 @@
#include <ft2build.h>
#include FT_FREETYPE_H
#include <freetype/ftmm.h>
#include <glad/gl.h>
/* #include <glad/gl.h> */
#include "font.h"
#include "shader.h"
/* #include "font.h" */
/* #include "shader.h" */
Character characters[128];
/* 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); */
int init_fonts(const char *font_path) {
FT_Library ft;
if (FT_Init_FreeType(&ft)) {
return -1;
}
/* // get ascii value of cell value and use it to index into characters */
/* Character ch = characters['0' + cell.value]; */
FT_Face face;
if (FT_New_Face(ft, font_path, 0, &face)) {
return -2;
}
/* float w = ch.size.width; */
/* float h = ch.size.height; */
/* float ratio = w / h; */
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);
/* // 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); */
FT_Set_Var_Design_Coordinates(face, mm->num_namedstyles, mm->namedstyle[mm->num_namedstyles - 4].coords);
/* // 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 }, */
FT_Done_MM_Var(ft, mm);
}
/* // 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 } */
/* }; */
FT_Set_Pixel_Sizes(face, 0, 128);
/* glBindTexture(GL_TEXTURE_2D, ch.texture_id); */
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
/* glBindBuffer(GL_ARRAY_BUFFER, vbo); */
/* glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); */
for (int c = 0; c < 128; c++) {
if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
printf("[ERROR]: failed to load glyph for char '%c'\n", c);
}
/* glBindBuffer(GL_ARRAY_BUFFER, 0); */
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, face->glyph->bitmap.width,
face->glyph->bitmap.rows, 0, GL_RED, GL_UNSIGNED_BYTE,
face->glyph->bitmap.buffer);
/* glDrawArrays(GL_TRIANGLES, 0, 6); */
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);
/* glBindVertexArray(0); */
/* glBindTexture(GL_TEXTURE_2D, 0); */
/* } */
characters[c].texture_id = texture;
characters[c].advance = face->glyph->advance.x;
characters[c].size = (Size){ .width = face->glyph->bitmap.width, .height = face->glyph->bitmap.rows };
characters[c].bearing = (Size){ .width = face->glyph->bitmap_left, .height = face->glyph->bitmap_top };
}
/* 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); */
glBindTexture(GL_TEXTURE_2D, 0);
/* int c = 0; */
/* int x = 0; */
/* while (text[c] != '\0') { */
/* Character ch = characters[(int)text[c]]; */
FT_Done_Face(face);
FT_Done_FreeType(ft);
/* 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); */
return 0;
}
/* float w = (ch.size.width * scale / 900.f); */
/* float h = (ch.size.height * scale / 900.f); */
void prepare_font(unsigned int *vao, unsigned int *vbo) {
glGenVertexArrays(1, &(*vao));
glGenBuffers(1, &(*vbo));
/* 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 }, */
glBindVertexArray(*vao);
glBindBuffer(GL_ARRAY_BUFFER, *vbo);
/* // 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 } */
/* }; */
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6*4, NULL, GL_DYNAMIC_DRAW);
/* glBindTexture(GL_TEXTURE_2D, ch.texture_id); */
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
/* glBindBuffer(GL_ARRAY_BUFFER, vbo); */
/* glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); */
/* glBindBuffer(GL_ARRAY_BUFFER, 0); */
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
/* glDrawArrays(GL_TRIANGLES, 0, 6); */
Size calculate_text_size(const char *text, float scale) {
Size size = { .width = 0, .height = 0 };
float w = 0;
float h = 0;
for (uint i = 0; i < strlen(text); i++) {
Character ch = characters[(uint)text[i]];
w += ch.advance / 64.0;
if (ch.size.height > h) {
h = ch.size.height;
}
}
size.width = w * scale;
size.height = h * scale;
return size;
}
/* x += (ch.advance >> 6) * scale; */
/* c++; */
/* } */
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);
/* glBindVertexArray(0); */
/* glBindTexture(GL_TEXTURE_2D, 0); */
/* } */
// get ascii value of cell value and use it to index into characters
Character ch = characters['0' + cell.value];
/* Color default_text_color = { 200.f / 255.f, 200.f / 255.f, 200.f / 255.f, 1.f }; */
float w = ch.size.width;
float h = ch.size.height;
float ratio = w / h;
/* 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); */
// 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);
/* int c = 0; */
/* while (text[c] != '\0') { */
/* Character ch = characters[(int)text[c]]; */
// 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 },
/* float xpos = pos.x + ch.bearing.width * scale; */
/* float ypos = pos.y - (ch.size.height - ch.bearing.height) * scale; */
// 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 }
};
/* float w = (ch.size.width * scale); */
/* float h = (ch.size.height * scale); */
glBindTexture(GL_TEXTURE_2D, ch.texture_id);
/* 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 }, */
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
/* // 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 } */
/* }; */
glBindBuffer(GL_ARRAY_BUFFER, 0);
/* glBindTexture(GL_TEXTURE_2D, ch.texture_id); */
glDrawArrays(GL_TRIANGLES, 0, 6);
/* glBindBuffer(GL_ARRAY_BUFFER, vbo); */
/* glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); */
/* glBindBuffer(GL_ARRAY_BUFFER, 0); */
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
/* glDrawArrays(GL_TRIANGLES, 0, 6); */
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);
/* pos.x += (ch.advance >> 6) * scale; */
/* c++; */
/* } */
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);
}
/* glBindVertexArray(0); */
/* glBindTexture(GL_TEXTURE_2D, 0); */
/* } */
-11
View File
@@ -3,17 +3,6 @@
#include "cudoku.h"
#include "shader.h"
typedef struct Size {
int width;
int height;
} Size;
typedef struct Character {
unsigned int texture_id;
Size size;
Size bearing;
unsigned int advance;
} Character;
int init_fonts(const char *font_path);
void prepare_font(unsigned int *vao, unsigned int *vbo);
+14
View File
@@ -27,3 +27,17 @@ Matrix4x4 orthographic_projection_2d(float left, float right, float bottom, floa
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;
}
+3
View File
@@ -4,5 +4,8 @@ typedef struct {
float m[4][4];
} Matrix4x4;
Matrix4x4 identity();
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);
+132 -110
View File
@@ -1,3 +1,4 @@
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
@@ -5,15 +6,14 @@
#include <glad/glx.h>
#include "3rdparty/stb/stb_image.h"
#include "x11.h"
#include "cudoku.h"
#include "font.h"
#include "ui.h"
#include "audio.h"
#define DEBUG 1
Display *display;
Window window;
const char *font_path = "assets/fonts/Rubik/Rubik-VariableFont_wght.ttf";
const char *title = "Cudoku";
void usage() {
printf("Usage: cudoku [OPTION]\n\n");
@@ -73,7 +73,7 @@ void handle_keypress(XEvent xev, Cudoku *game) {
} else if (XLookupKeysym(&xev.xkey, 0) == XK_n) {
generate_random_board(game);
} else if (XLookupKeysym(&xev.xkey, 0) == XK_F11) {
toggle_fullscreen(display, window);
/* toggle_fullscreen(display, window); */
} else if (XLookupKeysym(&xev.xkey, 0) == XK_c) {
toggle_check(game);
/* } else if (XLookupKeysym(&xev.xkey, 0) == XK_m) { */
@@ -86,17 +86,8 @@ void handle_keypress(XEvent xev, Cudoku *game) {
}
int main(int argc, char *argv[]) {
int res = audio_init();
if (res != 0) {
printf("[ERROR]: failed to initialize audio\n");
return 1;
}
const char *font_path = "assets/fonts/Rubik/Rubik-VariableFont_wght.ttf";
bool use_texture = false;
// 900 is the initial width and height
int width = 900, height = 900;
// scale factors for the entire board to nicely fit as a square inside
// the window
float x_scale = 1.f, y_scale = 1.f;
@@ -130,15 +121,10 @@ int main(int argc, char *argv[]) {
}
}
res = init_x11(&display, &window);
if (res < 0) return 1;
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);
Color clear_color = {0.2f, 0.15f, 0.5f, 1.f};
int res = init_zephr(font_path, title, (Size){800, 600}, &clear_color);
if (res != 0) {
printf("[ERROR]: could not initialize zephr\n");
return 1;
}
@@ -148,10 +134,6 @@ int main(int argc, char *argv[]) {
game.should_draw_help = true;
generate_random_board(&game);
unsigned int font_vao, font_vbo;
prepare_font(&font_vao, &font_vbo);
Shader font_shader = create_shader("shaders/font_v.vert", "shaders/font_f.frag");
Shader grid_shader;
unsigned int grid_texture;
if (!use_texture)
@@ -170,107 +152,147 @@ int main(int argc, char *argv[]) {
unsigned int win_vao = prepare_win_overlay();
float delta_t, last_t = 0.0;
start_internal_timer();
timer_start(&game.help_timer, 5.0f);
timer_start(&game.timer, 0.0f);
bool quit = false;
while (!quit) {
while (!zephr_should_quit()) {
float now = get_time();
delta_t = now - last_t;
last_t = now;
while (XPending(display)) {
XEvent xev;
XNextEvent(display, &xev);
Size window_size = zephr_get_window_size();
if (xev.type == ConfigureNotify) {
XConfigureEvent xce = xev.xconfigure;
/* int width = 15.f; */
/* int height = 35.f; */
/* int padding = 10.f; */
if (xce.width != width || xce.height != height) {
width = xce.width;
height = xce.height;
resize_x11_window(display, window);
set_scale_factor(xce.width, xce.height, &x_scale, &y_scale);
}
float ratio_w = (float)window_size.width / 1920.f;
float ratio_h = (float)window_size.height / 1080.f;
} else if (xev.type == KeyPress) {
if (XLookupKeysym(&xev.xkey, 0) == XK_Escape ||
(XLookupKeysym(&xev.xkey, 0) == XK_q && xev.xkey.state & ControlMask)) {
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);
}
}
int width = window_size.height;
int height = window_size.width;
if (window_size.height > window_size.width) {
width = window_size.width;
height = window_size.width;
} else {
width = window_size.height;
height = window_size.height;
}
glClearColor(0.2, 0.2, 0.2, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
draw_win();
float transform[4][4] = {
{x_scale, 0, 0, 0},
{0, y_scale, 0, 0},
{0, 0, 1, 0},
{0, 0, 0, 1},
};
/* draw_quad( */
/* (Vec2){0.0, 0.0}, */
/* (Size){width, height}, */
/* &(Color){240.f, 235.f, 227.f, 255.f}, */
/* ALIGN_CENTER); */
/* UIConstraints constraints = {0}; */
/* set_x_constraint(&constraints, 0.0, UI_CONSTRAINT_FIXED); */
/* set_y_constraint(&constraints, 0.0, UI_CONSTRAINT_FIXED); */
/* set_width_constraint(&constraints, 50, UI_CONSTRAINT_FIXED); */
/* set_height_constraint(&constraints, 50, UI_CONSTRAINT_FIXED); */
Matrix4x4 projection = orthographic_projection_2d(0.0f, width, 0.0f, height);
/* draw_quad( */
/* constraints, */
/* &(Color){240.f, 235.f, 227.f, 255.f}, */
/* ALIGN_TOP_LEFT */
/* ); */
if (!use_texture)
draw_bg_grid_shader(grid_shader, board_vao, (float *)transform, width < height ? width : height);
else
draw_bg_grid_texture(grid_shader, board_vao, grid_texture, (float *)transform);
/* Size size = calculate_text_size("Bruh", 1.0f); */
/* float x = clampf(500.0f * timer_elapsed(&my_time), 0.0, window_size.width - size.width); */
/* draw_text("Bruh", 1.0f, (Vec2){0.0, size.height}, &(Color){255.f, 0.f, 0.f, 255.f}); */
if (game.should_draw_selection)
draw_selection_box(
selection_shader,
selection_vao,
selection_vbo,
game.selection.x,
game.selection.y,
(float *)transform,
selection_color);
draw_numbers(font_shader, font_vao, font_vbo, (float *)transform, game.board);
if (game.should_highlight_mistakes) {
highlight_mistakes(selection_shader, selection_vao, selection_vbo, (float *)transform, &game);
char* text = "Mistake highlighter is ON";
float text_scale = 0.2;
Size text_size = calculate_text_size(text, text_scale);
Vec2 text_pos = { 0.0f, text_size.height - 10.f };
Color text_color = { 200.0f, 50.0f, 50.0f, 1.0f };
draw_text_at(font_shader, text, text_pos, text_scale, font_vao, font_vbo, (float *)projection.m, &text_color);
}
draw_timer(font_shader, font_vao, font_vbo, (float *)projection.m, &game.timer, width);
if (timer_ended(&game.help_timer)) {
timer_stop(&game.help_timer);
game.should_draw_help = false;
}
if (game.should_draw_help)
draw_help_overlay(selection_shader, font_shader, selection_vao, selection_vbo, font_vao, font_vbo, &projection, height, &game.help_timer);
if (game.has_won) {
draw_win_overlay(win_shader, font_shader, win_vao, font_vao, font_vbo, (float *)transform, &game);
}
glXSwapBuffers(display, window);
audio_update();
zephr_swap_buffers();
}
/* while (!quit) { */
audio_cleanup();
close_x11(&display, &window);
/* /1* while (XPending(display)) { *1/ */
/* /1* XEvent xev; *1/ */
/* /1* XNextEvent(display, &xev); *1/ */
/* /1* if (xev.type == ConfigureNotify) { *1/ */
/* /1* XConfigureEvent xce = xev.xconfigure; *1/ */
/* /1* if (xce.width != width || xce.height != height) { *1/ */
/* /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/ */
/* /1* if (XLookupKeysym(&xev.xkey, 0) == XK_Escape || *1/ */
/* /1* (XLookupKeysym(&xev.xkey, 0) == XK_q && xev.xkey.state & ControlMask)) { *1/ */
/* /1* quit = true; *1/ */
/* /1* break; *1/ */
/* /1* } else { *1/ */
/* /1* handle_keypress(xev, &game); *1/ */
/* /1* } *1/ */
/* /1* } else if (xev.type == ButtonPress) { *1/ */
/* /1* if (xev.xbutton.button == Button1) { *1/ */
/* /1* int x = xev.xbutton.x; *1/ */
/* /1* int y = xev.xbutton.y; *1/ */
/* /1* do_selection(&game, x, y, width, height, x_scale, y_scale); *1/ */
/* /1* } *1/ */
/* /1* } *1/ */
/* /1* } *1/ */
/* float transform[4][4] = { */
/* {x_scale, 0, 0, 0}, */
/* {0, y_scale, 0, 0}, */
/* {0, 0, 1, 0}, */
/* {0, 0, 0, 1}, */
/* }; */
/* /1* Matrix4x4 projection = orthographic_projection_2d(0.0f, width, 0.0f, height); *1/ */
/* /1* if (!use_texture) *1/ */
/* /1* draw_bg_grid_shader(grid_shader, board_vao, (float *)transform, width < height ? width : height); *1/ */
/* /1* else *1/ */
/* /1* draw_bg_grid_texture(grid_shader, board_vao, grid_texture, (float *)transform); *1/ */
/* if (game.should_draw_selection) */
/* draw_selection_box( */
/* selection_shader, */
/* selection_vao, */
/* selection_vbo, */
/* game.selection.x, */
/* game.selection.y, */
/* (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/ */
/* /1* char* text = "Mistake highlighter is ON"; *1/ */
/* /1* float text_scale = 0.2; *1/ */
/* /1* Size text_size = calculate_text_size(text, text_scale); *1/ */
/* /1* Vec2 text_pos = { 0.0f, text_size.height - 10.f }; *1/ */
/* /1* Color text_color = { 200.0f, 50.0f, 50.0f, 1.0f }; *1/ */
/* /1* draw_text_at(font_shader, text, text_pos, text_scale, font_vao, font_vbo, (float *)projection.m, &text_color); *1/ */
/* /1* } *1/ */
/* /1* draw_timer(font_shader, font_vao, font_vbo, (float *)projection.m, &game.timer, width); *1/ */
/* if (timer_ended(&game.help_timer)) { */
/* timer_stop(&game.help_timer); */
/* game.should_draw_help = false; */
/* } */
/* /1* if (game.should_draw_help) *1/ */
/* /1* draw_help_overlay(selection_shader, font_shader, selection_vao, selection_vbo, font_vao, font_vbo, &projection, height, &game.help_timer); *1/ */
/* /1* if (game.has_won) { *1/ */
/* /1* draw_win_overlay(win_shader, font_shader, win_vao, font_vao, font_vbo, (float *)transform, &game); *1/ */
/* /1* } *1/ */
/* /1* glXSwapBuffers(display, window); *1/ */
/* } */
deinit_zephr();
return 0;
}
+2 -2
View File
@@ -3,9 +3,9 @@ in vec2 TexCoords;
out vec4 FragColor;
uniform sampler2D text;
uniform vec3 textColor;
uniform vec4 textColor;
void main() {
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
FragColor = vec4(textColor, 1.0) * sampled;
FragColor = textColor * sampled;
}
+3 -2
View File
@@ -2,9 +2,10 @@
layout (location = 0) in vec4 vertex;
out vec2 TexCoords;
uniform mat4 transform;
uniform mat4 projection;
uniform mat4 model;
void main() {
gl_Position = transform * vec4(vertex.xy, 0.0, 1.0);
gl_Position = projection * model * vec4(vertex.xy, 0.0, 1.0);
TexCoords = vertex.zw;
}
+9
View File
@@ -0,0 +1,9 @@
#version 330 core
in vec2 texCoords;
out vec4 FragColor;
uniform vec4 aColor;
void main() {
FragColor = vec4(aColor);
}
+11
View File
@@ -0,0 +1,11 @@
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;
out vec2 texCoords;
uniform mat4 projection;
void main() {
gl_Position = projection * vec4(aPos, 1.0f);
texCoords = aTexCoords;
}
+4 -4
View File
@@ -25,6 +25,10 @@ void timer_start(Timer *timer, float duration) {
timer->is_running = true;
}
void timer_stop(Timer *timer) {
timer->is_running = false;
}
void timer_reset(Timer *timer) {
timer->time = get_time();
timer->is_running = true;
@@ -34,10 +38,6 @@ float timer_remaining(Timer *timer) {
return timer->duration - (get_time() - timer->time);
}
void timer_stop(Timer *timer) {
timer->is_running = false;
}
float timer_elapsed(Timer *timer) {
return get_time() - timer->time;
}
+1 -1
View File
@@ -16,7 +16,7 @@ double get_time();
void start_internal_timer();
bool timer_ended(Timer *timer);
void timer_start(Timer *timer, float duration);
void timer_stop(Timer *timer);
void timer_reset(Timer *timer);
float timer_remaining(Timer *timer);
void timer_stop(Timer *timer);
float timer_elapsed(Timer *timer);
+499
View File
@@ -0,0 +1,499 @@
#include <stdio.h>
#include <math.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <freetype/ftmm.h>
#include <X11/Xlib.h>
#include <glad/glx.h>
#include "cudoku.h"
#include "shader.h"
#include "audio.h"
#include "ui.h"
#include "x11.h"
Display *display;
Window window;
Context zephr_context;
Character characters[128];
Shader font_shader;
Shader ui_shader;
unsigned int font_vao;
unsigned int ui_vao;
unsigned int font_vbo;
unsigned int ui_vbo;
// TODO: instanced rendering of ui elements and text??
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;
}
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, 128);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (int c = 0; c < 128; c++) {
if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
printf("[ERROR]: failed to load glyph for char '%c'\n", c);
}
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, face->glyph->bitmap.width,
face->glyph->bitmap.rows, 0, GL_RED, GL_UNSIGNED_BYTE,
face->glyph->bitmap.buffer);
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);
characters[c].texture_id = texture;
characters[c].advance = face->glyph->advance.x;
characters[c].size = (Size){ .width = face->glyph->bitmap.width, .height = face->glyph->bitmap.rows };
characters[c].bearing = (Size){ .width = face->glyph->bitmap_left, .height = face->glyph->bitmap_top };
}
glBindTexture(GL_TEXTURE_2D, 0);
FT_Done_Face(face);
FT_Done_FreeType(ft);
return 0;
}
int init_ui(const char* font_path, Size window_size) {
zephr_context.window_size = window_size;
zephr_context.projection = orthographic_projection_2d(0.f, window_size.width, window_size.height, 0.f);
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;
}
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, &ui_vbo);
glBindVertexArray(font_vao);
glBindBuffer(GL_ARRAY_BUFFER, font_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);
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;
}
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};
}
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);
/* set_scale_factor(xce.width, xce.height, &x_scale, &y_scale); */
}
} 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;
}
void zephr_swap_buffers() {
glXSwapBuffers(display, window);
}
Size zephr_get_window_size() {
return zephr_context.window_size;
}
Sizef calculate_text_size(const char *text, float scale) {
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 (uint i = 0; i < strlen(text); i++) {
Character ch = characters[(uint)text[i]];
max_bearing_h = max(max_bearing_h, ch.bearing.height);
}
for (uint i = 0; i < strlen(text); i++) {
Character ch = 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:
case UI_CONSTRAINT_ASPECT_RATIO:
// no-op. fallback to FIXED
case UI_CONSTRAINT_FIXED:
constraints->x = value;
break;
case UI_CONSTRAINT_RELATIVE:
constraints->x = (value * zephr_context.window_size.width);
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:
constraints->y = (value * zephr_context.window_size.height);
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:
constraints->width = (value * zephr_context.window_size.width);
break;
case UI_CONSTRAINT_RELATIVE_PIXELS:
constraints->width = zephr_context.window_size.width / 1920.f * value;
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:
constraints->height = (value * zephr_context.window_size.height);
break;
case UI_CONSTRAINT_RELATIVE_PIXELS:
constraints->height = zephr_context.window_size.height / 1080.f * value;
break;
case UI_CONSTRAINT_ASPECT_RATIO:
constraints->height = constraints->width * value;
break;
}
}
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:
pos->x += zephr_context.window_size.width / 2.f - size.width / 2.f;
pos->y = pos->y;
break;
case ALIGN_TOP_RIGHT:
pos->x += zephr_context.window_size.width - size.width;
pos->y = pos->y;
break;
case ALIGN_BOTTOM_LEFT:
pos->x = pos->x;
pos->y += zephr_context.window_size.height - size.height;
break;
case ALIGN_BOTTOM_CENTER:
pos->x += zephr_context.window_size.width / 2.f - size.width / 2.f;
pos->y += zephr_context.window_size.height - size.height;
break;
case ALIGN_BOTTOM_RIGHT:
pos->x += zephr_context.window_size.width - size.width;
pos->y += zephr_context.window_size.height - size.height;
break;
case ALIGN_LEFT_CENTER:
pos->x = pos->x;
pos->y += zephr_context.window_size.height / 2.f - size.height / 2.f;
break;
case ALIGN_RIGHT_CENTER:
pos->x += zephr_context.window_size.width - size.width;
pos->y += zephr_context.window_size.height / 2.f - size.height / 2.f;
break;
case ALIGN_CENTER:
pos->x += zephr_context.window_size.width / 2.f - size.width / 2.f;
pos->y += zephr_context.window_size.height / 2.f - size.height / 2.f;
break;
}
}
void draw_quad(UIConstraints constraints, Color *color, Alignment align) {
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);
}
set_mat4f(ui_shader, "projection", (float *)zephr_context.projection.m);
glBindVertexArray(ui_vao);
Vec2f pos = { 0.f, 0.f };
Sizef size = { 0.f, 0.f };
apply_constraints(constraints, &pos, &size);
apply_alignment(align, &pos, size);
float vertices[6][4] = {
{pos.x, pos.y + size.height, 0.0, 1.0},
{pos.x, pos.y, 0.0, 0.0},
{pos.x + size.width, pos.y, 1.0, 0.0},
{pos.x, pos.y + size.height, 0.0, 1.0},
{pos.x + size.width, pos.y, 1.0, 0.0},
{pos.x + size.width, pos.y + 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);
}
// TODO: remove scaling and replace it with standardized font sizes with 16.0f being the default
void draw_text(const char* text, float scale, Vec2f pos, Color *color, Alignment alignment) {
use_shader(font_shader);
if (color) {
set_vec4f(font_shader, "textColor", color->r / 255.f, color->g / 255.f, color->b / 255.f, color->a / 255.f);
} else {
set_vec4f(font_shader, "textColor", 0.f, 0.f, 0.f, 1.f);
}
set_mat4f(font_shader, "projection", (float *)zephr_context.projection.m);
Matrix4x4 model = identity();
model.m[3][0] = pos.x;
model.m[3][1] = pos.y;
set_mat4f(font_shader, "model", (float *)model.m);
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(font_vao);
Sizef text_size = calculate_text_size(text, 1.0);
int max_bearing_h = 0;
for (uint i = 0; i < strlen(text); i++) {
Character ch = characters[(int)text[i]];
max_bearing_h = max(max_bearing_h, ch.bearing.height);
}
float first_char_bearing_w = characters[(int)text[0]].bearing.width;
int c = 0;
int x = 0;
while (text[c] != '\0') {
Character ch = 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)) * scale;
float ypos = (text_size.height - ch.bearing.height - (text_size.height - max_bearing_h)) * scale;
Vec2f final_pos = { xpos, ypos };
float w = (ch.size.width * scale);
float h = (ch.size.height * scale);
apply_alignment(alignment, &final_pos, (Sizef){ text_size.width * scale, text_size.height * scale });
float vertices[6][4] = {
// top left tri
{final_pos.x, final_pos.y + h, 0.0, 1.0},
{final_pos.x, final_pos.y, 0.0, 0.0},
{final_pos.x + w, final_pos.y, 1.0, 0.0},
// bottom right tri
{final_pos.x, final_pos.y + h, 0.0, 1.0},
{final_pos.x + w, final_pos.y, 1.0, 0.0},
{final_pos.x + w, final_pos.y + h, 1.0, 1.0},
};
glBindTexture(GL_TEXTURE_2D, ch.texture_id);
glBindBuffer(GL_ARRAY_BUFFER, font_vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
x += (ch.advance >> 6);
c++;
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 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; */
/* } */
+86
View File
@@ -0,0 +1,86 @@
#pragma once
#include <stdbool.h>
#include "helper.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 Color {
float r;
float g;
float b;
float a;
} Color;
typedef struct Character {
unsigned int texture_id;
Size size;
Size bearing;
unsigned int advance;
} Character;
typedef struct {
bool should_quit;
Color clear_color;
Size window_size;
Matrix4x4 projection;
} Context;
typedef enum Alignment {
ALIGN_TOP_LEFT,
ALIGN_TOP_CENTER,
ALIGN_TOP_RIGHT,
ALIGN_LEFT_CENTER,
ALIGN_CENTER,
ALIGN_RIGHT_CENTER,
ALIGN_BOTTOM_LEFT,
ALIGN_BOTTOM_CENTER,
ALIGN_BOTTOM_RIGHT,
} Alignment;
typedef enum UIConstraint {
UI_CONSTRAINT_FIXED,
UI_CONSTRAINT_RELATIVE,
UI_CONSTRAINT_RELATIVE_PIXELS,
UI_CONSTRAINT_ASPECT_RATIO,
} UIConstraint;
typedef struct UIConstraints {
float x;
float y;
float width;
float height;
} UIConstraints;
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 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 draw_quad(UIConstraints constraints, Color *color, Alignment align);
void draw_text(const char* text, float scale, Vec2f pos, Color *color, Alignment align);
Sizef calculate_text_size(const char *text, float scale);
+8 -10
View File
@@ -4,12 +4,10 @@
#include <glad/glx.h>
static const uint win_width = 900;
static const uint win_height = 900;
static bool fullscreen = false;
Colormap colormap;
GLXContext context;
GLXContext glx_context;
void resize_x11_window(Display *display, Window window) {
XWindowAttributes win_attrs;
@@ -17,7 +15,7 @@ void resize_x11_window(Display *display, Window window) {
glViewport(0, 0, win_attrs.width, win_attrs.height);
}
int init_x11(Display **display, Window *window) {
int init_x11(Display **display, Window *window, const char* title, int window_width, int window_height) {
*display = XOpenDisplay(NULL);
if (display == NULL) {
@@ -36,12 +34,12 @@ int init_x11(Display **display, Window *window) {
StructureNotifyMask | ButtonPressMask;
attributes.colormap = colormap;
*window = XCreateWindow(*display, root, 0, 0, win_width, win_height, 0,
*window = XCreateWindow(*display, root, 0, 0, window_width, window_height, 0,
DefaultDepth(*display, screen), InputOutput, visual,
CWColormap | CWEventMask, &attributes);
XMapWindow(*display, *window);
XStoreName(*display, *window, "Cudoku");
XStoreName(*display, *window, title);
if (!window) {
printf("Failed to create window\n");
@@ -74,15 +72,15 @@ int init_x11(Display **display, Window *window) {
None
};
context = glXCreateContextAttribsARB(*display, fbc[0], NULL, 1,
glx_context = glXCreateContextAttribsARB(*display, fbc[0], NULL, 1,
context_attributes);
if (!context) {
if (!glx_context) {
printf("Failed to create GLX context\n");
return 1;
}
glXMakeCurrent(*display, *window, context);
glXMakeCurrent(*display, *window, glx_context);
int gl_version = gladLoaderLoadGL();
@@ -108,7 +106,7 @@ int init_x11(Display **display, Window *window) {
void close_x11(Display **display, Window *window) {
glXMakeCurrent(*display, 0, 0);
glXDestroyContext(*display, context);
glXDestroyContext(*display, glx_context);
XDestroyWindow(*display, *window);
XFreeColormap(*display, colormap);
+2 -2
View File
@@ -2,7 +2,7 @@
#include <X11/Xlib.h>
void resize_x11_window(Display *display, Window window);
int init_x11(Display **display, Window *window);
int init_x11(Display **display, Window *window, const char* title, int window_width, int window_height);
void close_x11(Display **display, Window *window);
void resize_x11_window(Display *display, Window window);
void toggle_fullscreen(Display *display, Window window);