diff --git a/Makefile b/Makefile index e1e7b79..d0734dd 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ BIN=cudoku CC=gcc CFLAGS=-Wall -Wextra -pedantic -O3 -g `pkg-config --cflags freetype2` -I3rdparty/glad/include -I3rdparty/stb -OBJ=main.o x11.o cudoku.o shader.o font.o 3rdparty/glad/src/gl.o 3rdparty/glad/src/glx.o 3rdparty/stb/stb.o +OBJ=main.o x11.o cudoku.o shader.o font.o helper.o 3rdparty/glad/src/gl.o 3rdparty/glad/src/glx.o 3rdparty/stb/stb.o LDFLAGS=`pkg-config --libs x11 freetype2` -lm DEPS=3rdparty/glad/include/glad/gl.h 3rdparty/glad/include/glad/glx.h 3rdparty/stb/stb_image.h diff --git a/cudoku.c b/cudoku.c index 99b5fa1..fb14019 100644 --- a/cudoku.c +++ b/cudoku.c @@ -10,6 +10,9 @@ #include "cudoku.h" #include "font.h" #include "shader.h" +#include "helper.h" + +#define help_text_size 12 static const float quad_vertices[] = { // positions // texture coords @@ -26,6 +29,23 @@ static const int quad_indices[] = { static const Color mistake_color = {1.f, 0.f, 0.f, 0.5f}; +static const char *win_text = "You won!"; + +static const char *help_texts[help_text_size] = { + "F1 - Toggle help", + "F11 - Toggle fullscreen", + "1-9 - Set number", + "0/Del/Backspace - Remove number", + "Left/A/H - Move selection left", + "Right/D/L - Move selection right", + "Up/W/K - Move selection up", + "Down/S/J - Move selection down", + "Space/Enter - Toggle selection", + "N - New board", + "C - Check for mistakes", + "R - Reset board" +}; + void set_scale_factor(int width, int height, float *x, float *y) { if (width > height) { float ratio = (float)height / (float)width; @@ -155,9 +175,8 @@ void draw_win_overlay(Shader win_shader, Shader font_shader, unsigned int vao, u glBindVertexArray(0); - const char *text = "You won!"; - Size text_size = calculate_text_size(text, 1.0f); - draw_text(font_shader, text, text_size, 1.5f, font_vao, font_vbo, transform); + Size text_size = calculate_text_size(win_text, 1.0f); + draw_text(font_shader, win_text, text_size, 1.5f, font_vao, font_vbo, transform); } void prepare_selection_box(unsigned int *vao, unsigned int *vbo) { @@ -206,7 +225,6 @@ void draw_selection_box(Shader shader, unsigned int vao, unsigned int vbo, int x glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); - glBindBuffer(GL_ARRAY_BUFFER, 0); glDrawArrays(GL_TRIANGLES, 0, 6); @@ -261,6 +279,7 @@ void reset_state(Cudoku *game) { game->should_draw_selection = false; game->should_highlight_mistakes = false; game->has_won = false; + game->should_draw_help = true; } bool check_win(Cudoku *game) { @@ -274,6 +293,7 @@ bool check_win(Cudoku *game) { game->has_won = true; game->should_draw_selection = false; game->should_highlight_mistakes = false; + game->should_draw_help = false; return true; } @@ -541,4 +561,53 @@ void reset_board(Cudoku *game) { } } +void toggle_help(Cudoku *game) { + if (!game->has_won) { + game->should_draw_help = !game->should_draw_help; + } +} +void draw_ui_element_at(Shader shader, unsigned int vao, unsigned int vbo, Matrix4x4 projection, Vec2 pos, Size size, Color color) { + use_shader(shader); + + set_mat4f(shader, "transform", (float *)projection.m); + set_vec4f(shader, "aColor", color.r, color.g, color.b, color.a); + + glBindVertexArray(vao); + + float vertices[6][2] = { + {pos.x, pos.y + size.height}, + {pos.x, pos.y}, + {pos.x + size.width, pos.y}, + + {pos.x, pos.y + size.height}, + {pos.x + size.width, pos.y}, + {pos.x + size.width, pos.y + size.height}, + }; + + 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 draw_help_overlay(Shader overlay_shader, Shader font_shader, unsigned int overlay_vao, unsigned int overlay_vbo, unsigned int font_vao, unsigned int font_vbo, Matrix4x4 projection, int window_height) { + float scale = 0.3f; + int total_help_texts_height = 20; + int text_padding = 10; + + Size size = {.width = 700, .height = 500}; + Vec2 pos = {.x = 0, .y = window_height - size.height}; + Color color = { .r = 0.1f, .g = 0.1f, .b = 0.1f, .a = 0.95f }; + 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], 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, scale, font_vao, font_vbo, (float *)projection.m); + } +} diff --git a/cudoku.h b/cudoku.h index 4ba5001..b5a7b7b 100644 --- a/cudoku.h +++ b/cudoku.h @@ -21,6 +21,7 @@ typedef struct Cudoku { Vec2 selection; bool should_draw_selection; bool should_highlight_mistakes; + bool should_draw_help; } Cudoku; typedef struct Color { @@ -47,3 +48,5 @@ void move_selection(Cudoku *game, int x, int y); void toggle_selection(Cudoku *game); void generate_random_board(Cudoku *game); void reset_board(Cudoku *game); +void toggle_help(Cudoku *game); +void draw_help_overlay(); diff --git a/font.c b/font.c index 35e7447..3e59344 100644 --- a/font.c +++ b/font.c @@ -223,3 +223,47 @@ void draw_text(Shader shader, const char *text, Size text_size, float scale, uns glBindTexture(GL_TEXTURE_2D, 0); } +void draw_text_at(Shader shader, const char *text, Vec2 pos, float scale, unsigned int vao, unsigned int vbo, float *projection) { + use_shader(shader); + 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 index 9060e40..a6a9ba8 100644 --- a/font.h +++ b/font.h @@ -20,3 +20,4 @@ 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(Shader shader, const char *text, Size text_size, float scale, unsigned int vao, unsigned int vbo, float *transform); +void draw_text_at(Shader shader, const char *text, Vec2 pos, float scale, unsigned int vao, unsigned int vbo, float *projection); diff --git a/helper.c b/helper.c new file mode 100644 index 0000000..270f976 --- /dev/null +++ b/helper.c @@ -0,0 +1,21 @@ +#include "helper.h" + +Matrix4x4 orthographicProjection2D(float left, float right, float bottom, float top) { + Matrix4x4 result; + + // Identity matrix + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + result.m[i][j] = 0.0f; + } + } + + 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; +} diff --git a/helper.h b/helper.h new file mode 100644 index 0000000..a2784a2 --- /dev/null +++ b/helper.h @@ -0,0 +1,6 @@ + +typedef struct { + float m[4][4]; +} Matrix4x4; + +Matrix4x4 orthographicProjection2D(float left, float right, float bottom, float top); diff --git a/main.c b/main.c index 396878c..64e0f42 100644 --- a/main.c +++ b/main.c @@ -9,6 +9,7 @@ #include "x11.h" #include "cudoku.h" #include "font.h" +#include "helper.h" Display *display; Window window; @@ -60,8 +61,8 @@ void handle_keypress(XEvent xev, Cudoku *game) { 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); */ + } else if (XLookupKeysym(&xev.xkey, 0) == XK_F1) { + toggle_help(game); } } @@ -120,6 +121,7 @@ int main(int argc, char *argv[]) { srand(time(NULL)); Cudoku game = {0}; + game.should_draw_help = true; generate_random_board(&game); for (int i = 0; i < 9; i++) { @@ -198,6 +200,8 @@ int main(int argc, char *argv[]) { {0, 0, 0, 1}, }; + Matrix4x4 projection = orthographicProjection2D(0.0f, width, 0.0f, height); + if (!use_texture) draw_bg_grid_shader(grid_shader, board_vao, (float *)transform, width < height ? width : height); else @@ -218,6 +222,9 @@ int main(int argc, char *argv[]) { if (game.should_highlight_mistakes) highlight_mistakes(selection_shader, selection_vao, selection_vbo, (float *)transform, &game); + if (game.should_draw_help) + draw_help_overlay(selection_shader, font_shader, selection_vao, selection_vbo, font_vao, font_vbo, projection, height); + if (game.has_won) { draw_win_overlay(win_shader, font_shader, win_vao, font_vao, font_vbo, (float *)transform); }