From 88a5bff3df5c5e65845b288c8de06074aa784541 Mon Sep 17 00:00:00 2001 From: IllusionMan1212 Date: Tue, 12 Sep 2023 23:32:58 +0200 Subject: [PATCH] cell selection and number input --- cudoku.c | 114 ++++++++++++++++++++++++++++++++++++++++++-- cudoku.h | 18 +++++++ font.c | 5 +- main.c | 96 ++++++++++++++++++++++++------------- shaders/grid_f.frag | 9 ++-- shaders/quad.frag | 7 +++ 6 files changed, 207 insertions(+), 42 deletions(-) create mode 100644 shaders/quad.frag diff --git a/cudoku.c b/cudoku.c index 1b0ee63..60ea1ec 100644 --- a/cudoku.c +++ b/cudoku.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "3rdparty/stb/stb_image.h" @@ -99,17 +100,20 @@ void draw_bg_grid_texture(Shader shader, unsigned int vao, unsigned int texture, glBindVertexArray(vao); glBindTexture(GL_TEXTURE_2D, texture); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + + glBindVertexArray(0); } void draw_numbers(Shader shader, unsigned int vao, unsigned int vbo, float *transform, int board[9][9]) { - // TODO: go thru the board state and call draw_text() with the appropriate number - // and position - for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { - draw_number(shader, board[i][j], i, j, 5.0f, vao, vbo, transform); + if (board[i][j] != 0) { + draw_number(shader, board[i][j], i, j, 5.0f, vao, vbo, transform); + } } } + + glBindVertexArray(0); } void draw_win_overlay(Shader shader, unsigned int vao, unsigned int vbo, float *transform) { @@ -117,3 +121,105 @@ void draw_win_overlay(Shader shader, unsigned int vao, unsigned int vbo, float * Size text_size = calculate_text_size(text, 1.0f); draw_text(shader, text, text_size, 1.5f, vao, vbo, transform); } + +void prepare_selection_box(unsigned int *vao, unsigned int *vbo) { + glGenVertexArrays(1, &(*vao)); + glGenBuffers(1, &(*vbo)); + + glBindVertexArray(*vao); + glBindBuffer(GL_ARRAY_BUFFER, *vbo); + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6*2, NULL, GL_DYNAMIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), 0); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +void draw_selection_box(Shader shader, unsigned int vao, unsigned int vbo, int x, int y, float *transform) { + use_shader(shader); + + set_mat4f(shader, "transform", transform); + + glBindVertexArray(vao); + + float vert = 0.11f; + // 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 * (y - 4); + float offset_y = single_cell * (x - 4); + + float vertices[6][2] = { + // top left tri + { -vert + offset_x, -vert - offset_y }, + { -vert + offset_x, vert - offset_y }, + { vert + offset_x, vert - offset_y }, + + // bottom right tri + { -vert + offset_x, -vert - offset_y }, + { vert + offset_x, vert - offset_y }, + { vert + offset_x, -vert - offset_y } + }; + + 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); +} + +void do_selection(Cudoku *game, int x, int y, int width, int height, float x_scale, float y_scale) { + float board_x_start = (width / 2.f) - ((width * x_scale) / 2); + float board_y_start = (height / 2.f) - ((height * y_scale) / 2); + + float cell_width = (width * x_scale) / 9.f; + float cell_height = (height * y_scale) / 9.f; + + + int cell_x = floor((x - board_x_start) / cell_width); + int cell_y = floor((y - board_y_start) / cell_height); + + if (cell_x < 0 || cell_x > 8 || cell_y < 0 || cell_y > 8) { + game->should_draw_selection = false; + return; + } + + game->should_draw_selection = true; + game->selection.x = cell_y; + game->selection.y = cell_x; +} + +void set_selected_number(Cudoku *game, int number) { + if (game->should_draw_selection) { + game->board[game->selection.x][game->selection.y] = number; + } +} + +void move_selection(Cudoku *game, int x, int y) { + if (game->should_draw_selection) { + game->selection.x += y; + game->selection.y += x; + + if (game->selection.x < 0) { + game->selection.x = 0; + } else if (game->selection.x > 8) { + game->selection.x = 8; + } + + if (game->selection.y < 0) { + game->selection.y = 0; + } else if (game->selection.y > 8) { + game->selection.y = 8; + } + } +} + +void toggle_selection(Cudoku *game) { + game->should_draw_selection = !game->should_draw_selection; +} diff --git a/cudoku.h b/cudoku.h index 8ded31e..07c9891 100644 --- a/cudoku.h +++ b/cudoku.h @@ -4,9 +4,27 @@ #include "shader.h" +typedef struct Vec2 { + int x; + int y; +} Vec2; + +typedef struct Cudoku { + int board[9][9]; + bool has_won; + Vec2 selection; + bool should_draw_selection; +} 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, int board[9][9]); void draw_win_overlay(Shader shader, unsigned int vao, unsigned int vbo, float *transform); +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); +void do_selection(Cudoku *game, int x, int y, int width, int height, float x_scale, float y_scale); +void set_selected_number(Cudoku *game, int number); +void move_selection(Cudoku *game, int x, int y); +void toggle_selection(Cudoku *game); diff --git a/font.c b/font.c index eed1da8..783a5a1 100644 --- a/font.c +++ b/font.c @@ -36,7 +36,7 @@ int init_fonts(const char *font_path) { for (int i = 0; i < 9; i++) { if (FT_Load_Char(face, '0' + (i + 1), FT_LOAD_RENDER)) { - printf("Error: failed to load glyph for number '%c'\n", '0' + i); + printf("[ERROR]: failed to load glyph for number '%c'\n", '0' + i); } unsigned int texture; @@ -61,7 +61,7 @@ int init_fonts(const char *font_path) { 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); + printf("[ERROR]: failed to load glyph for char '%c'\n", c); } unsigned int texture; @@ -164,6 +164,7 @@ void draw_number(Shader shader, int text, int row, int column, float scale, unsi glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); + glBindBuffer(GL_ARRAY_BUFFER, 0); glDrawArrays(GL_TRIANGLES, 0, 6); diff --git a/main.c b/main.c index ba1128a..7820b32 100644 --- a/main.c +++ b/main.c @@ -10,6 +10,9 @@ #include "cudoku.h" #include "font.h" +Display *display; +Window window; + void usage() { printf("Usage: cudoku [OPTION]\n\n"); printf(" %-20s%-20s", "-h, --help", "prints this help message\n"); @@ -17,8 +20,35 @@ void usage() { printf(" %-20s%-20s", "-f, --font FONT", "use a custom font file to render text\n"); } -Display *display; -Window window; +void handle_keypress(XEvent xev, Cudoku *game) { + if (XLookupKeysym(&xev.xkey, 0) == XK_r) { + /* reset_board(&game); */ + } else if (XLookupKeysym(&xev.xkey, 0) >= XK_0 && XLookupKeysym(&xev.xkey, 0) <= XK_9) { + set_selected_number(game, XLookupKeysym(&xev.xkey, 0) - XK_0); + } else if (XLookupKeysym(&xev.xkey, 0) == XK_BackSpace || XLookupKeysym(&xev.xkey, 0) == XK_Delete) { + set_selected_number(game, 0); + } else if (XLookupKeysym(&xev.xkey, 0) == XK_Left || XLookupKeysym(&xev.xkey, 0) == XK_a || XLookupKeysym(&xev.xkey, 0) == XK_h) { + move_selection(game, -1, 0); + } else if (XLookupKeysym(&xev.xkey, 0) == XK_Right || XLookupKeysym(&xev.xkey, 0) == XK_d || XLookupKeysym(&xev.xkey, 0) == XK_l) { + move_selection(game, 1, 0); + } else if (XLookupKeysym(&xev.xkey, 0) == XK_Up || XLookupKeysym(&xev.xkey, 0) == XK_w || XLookupKeysym(&xev.xkey, 0) == XK_k) { + move_selection(game, 0, -1); + } else if (XLookupKeysym(&xev.xkey, 0) == XK_Down || XLookupKeysym(&xev.xkey, 0) == XK_s || XLookupKeysym(&xev.xkey, 0) == XK_j) { + move_selection(game, 0, 1); + } else if (XLookupKeysym(&xev.xkey, 0) == XK_Return || XLookupKeysym(&xev.xkey, 0) == XK_space) { + toggle_selection(game); + /* } else if (XLookupKeysym(&xev.xkey, 0) == XK_n) { */ + /* new_board(&game); */ + /* } else if (XLookupKeysym(&xev.xkey, 0) == XK_f) { */ + /* toggle_fullscreen(display, window); */ + /* } else if (XLookupKeysym(&xev.xkey, 0) == XK_c) { */ + /* toggle_check(&game); */ + /* } else if (XLookupKeysym(&xev.xkey, 0) == XK_m) { */ + /* toggle_mute(&game); */ + /* } else if (XLookupKeysym(&xev.xkey, 0) == XK_F1) { */ + /* toggle_help(&game); */ + } +} int main(int argc, char *argv[]) { const char *font_path = "assets/fonts/Rubik/Rubik-VariableFont_wght.ttf"; @@ -46,23 +76,31 @@ int main(int argc, char *argv[]) { font_path = argv[i + 1]; i++; } else { - printf("Error: used font flag with no provided font, defaulting to Rubik\n"); + printf("[ERROR]: used font flag with no provided font, defaulting to Rubik\n"); } } else if (strcmp(flag, "--use-texture") == 0) { - use_texture = true; + FILE *fp = fopen("assets/sudoku-grid.png", "r"); + if (fp == NULL) { + printf("[ERROR]: could not open board texture, falling back to shader grid\n"); + } else { + fclose(fp); + use_texture = true; + } } } } + Cudoku game = {0}; + int 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"); + 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); + printf("[ERROR]: could not load font file: \"%s\"\n", font_path); return 1; } @@ -77,31 +115,17 @@ int main(int argc, char *argv[]) { else grid_shader = create_shader("shaders/board_v.vert", "shaders/board_f.frag"); - int vao = prepare_bg(use_texture, &grid_texture); + int board_vao = prepare_bg(use_texture, &grid_texture); + + Shader selection_shader = create_shader("shaders/board_v.vert", "shaders/quad.frag"); + unsigned int selection_vao, selection_vbo; + prepare_selection_box(&selection_vao, &selection_vbo); - int board[9][9] = { - {0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0}, - }; srand(time(NULL)); for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { - int r = (rand() % 9) + 1; - board[i][j] = r; - } - } - - for (int i = 0; i < 9; i++) { - for (int j = 0; j < 9; j++) { - printf("%d | ", board[i][j]); + printf("%d | ", game.board[i][j]); } printf("\n"); } @@ -127,13 +151,14 @@ int main(int argc, char *argv[]) { if (XLookupKeysym(&xev.xkey, 0) == XK_Escape || XLookupKeysym(&xev.xkey, 0) == XK_q) { 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; - // TOOD: - printf("x: %d, y: %d\n", x, y); + do_selection(&game, x, y, width, height, x_scale, y_scale); } } } @@ -149,11 +174,18 @@ int main(int argc, char *argv[]) { }; if (!use_texture) - draw_bg_grid_shader(grid_shader, vao, (float *)transform, width < height ? width : height); + draw_bg_grid_shader(grid_shader, board_vao, (float *)transform, width < height ? width : height); else - draw_bg_grid_texture(grid_shader, vao, grid_texture, (float *)transform); - /* draw_numbers(font_shader, font_vao, font_vbo, (float *)transform, board); */ - draw_win_overlay(font_shader, font_vao, font_vbo, (float *)transform); + draw_bg_grid_texture(grid_shader, board_vao, grid_texture, (float *)transform); + + if (game.should_draw_selection) + draw_selection_box(selection_shader, selection_vao, selection_vbo, game.selection.x, game.selection.y, (float *)transform); + + draw_numbers(font_shader, font_vao, font_vbo, (float *)transform, game.board); + + if (game.has_won) { + draw_win_overlay(font_shader, font_vao, font_vbo, (float *)transform); + } glXSwapBuffers(display, window); } diff --git a/shaders/grid_f.frag b/shaders/grid_f.frag index 09bd11a..4475bd7 100644 --- a/shaders/grid_f.frag +++ b/shaders/grid_f.frag @@ -31,12 +31,13 @@ void main() bool isInsideGrid = isInsideGridHorizontally && isInsideGridVertically; // Determine if the current fragment is on a thicker grid line - bool isThickLineX = isInsideGrid && mod(fragCoordX, stepSize * 3) < 0.01; - bool isThickLineY = isInsideGrid && mod(fragCoordY, stepSize * 3) < 0.01; + bool isThickLineX = isInsideGrid && mod(fragCoordX, (stepSize + 0.0007) * 3) <= 0.01; + bool isThickLineY = isInsideGrid && mod(fragCoordY, (stepSize + 0.0007) * 3) <= 0.01; // Check if the current fragment is on a grid line - bool isGridX = isInsideGrid && mod(fragCoordX, stepSize) < 0.005; - bool isGridY = isInsideGrid && mod(fragCoordY, stepSize) < 0.005; + // 0.0007 to shift the grid lines a bit to the left + bool isGridX = isInsideGrid && mod(fragCoordX, stepSize + 0.0007) <= 0.005; + bool isGridY = isInsideGrid && mod(fragCoordY, stepSize + 0.0007) <= 0.005; // Set the fragment color based on whether it's a grid line or a thicker grid line if ((isThickLineX) || (isThickLineY)) { diff --git a/shaders/quad.frag b/shaders/quad.frag new file mode 100644 index 0000000..e774429 --- /dev/null +++ b/shaders/quad.frag @@ -0,0 +1,7 @@ +#version 330 core + +out vec4 FragColor; + +void main() { + FragColor = vec4(0.4, 0.4, 1.0, 0.5); +}