From ad04490ef84d7565fbec0fa878a21694ad2d61f0 Mon Sep 17 00:00:00 2001 From: pryazha Date: Tue, 26 Aug 2025 10:55:18 +0500 Subject: guess that's all --- .gitignore | 9 + advanced_lighting/6.hdr/hdr.c | 4 +- in_practice/breakout/Ac437_IBM_VGA_9x16.ttf | Bin 0 -> 26004 bytes in_practice/breakout/audio.c | 14 + in_practice/breakout/audio.h | 18 + in_practice/breakout/audio/background.wav | Bin 0 -> 21173934 bytes in_practice/breakout/audio/powerup.wav | Bin 0 -> 240614 bytes in_practice/breakout/audio/solid.wav | Bin 0 -> 27164 bytes in_practice/breakout/audio/tile.wav | Bin 0 -> 336440 bytes in_practice/breakout/ball.c | 17 +- in_practice/breakout/ball.h | 18 +- in_practice/breakout/breakout | Bin 355952 -> 0 bytes in_practice/breakout/build.sh | 15 +- in_practice/breakout/first.txt | 3 - in_practice/breakout/fonts/ibm.ttf | Bin 0 -> 26004 bytes in_practice/breakout/game.c | 397 +++++++++++++++++++-- in_practice/breakout/game.h | 43 ++- in_practice/breakout/game.o | Bin 16296 -> 0 bytes in_practice/breakout/input.c | 15 +- in_practice/breakout/input.h | 29 +- in_practice/breakout/input.o | Bin 4704 -> 0 bytes in_practice/breakout/level.c | 18 +- in_practice/breakout/level.h | 15 +- in_practice/breakout/levels/first.txt | 8 + in_practice/breakout/levels/second.txt | 8 + in_practice/breakout/levels/third.txt | 3 + in_practice/breakout/linux.c | 93 ++++- in_practice/breakout/linux.o | Bin 38976 -> 0 bytes in_practice/breakout/my_math.c | 61 +++- in_practice/breakout/my_math.h | 13 +- in_practice/breakout/my_math.o | Bin 11328 -> 0 bytes in_practice/breakout/object.c | 10 +- in_practice/breakout/object.h | 8 +- in_practice/breakout/object.o | Bin 4424 -> 0 bytes in_practice/breakout/particle.c | 104 ++++++ in_practice/breakout/particle.h | 38 ++ in_practice/breakout/post_processor.c | 109 ++++++ in_practice/breakout/post_processor.h | 25 ++ in_practice/breakout/powerup.c | 31 ++ in_practice/breakout/powerup.h | 36 ++ in_practice/breakout/shader.c | 19 + in_practice/breakout/shader.h | 3 + in_practice/breakout/shader.o | Bin 11912 -> 0 bytes in_practice/breakout/shaders/particle.frag | 15 + in_practice/breakout/shaders/particle.vert | 20 ++ in_practice/breakout/shaders/post.frag | 38 ++ in_practice/breakout/shaders/post.vert | 33 ++ in_practice/breakout/shaders/text.frag | 16 + in_practice/breakout/shaders/text.vert | 15 + in_practice/breakout/sprite.c | 6 +- in_practice/breakout/sprite.h | 8 +- in_practice/breakout/sprite.o | Bin 10272 -> 0 bytes in_practice/breakout/sys.h | 12 +- in_practice/breakout/sys.o | Bin 9560 -> 0 bytes in_practice/breakout/text.c | 127 +++++++ in_practice/breakout/text.h | 27 ++ in_practice/breakout/texture.c | 2 +- in_practice/breakout/texture.h | 4 +- in_practice/breakout/texture.o | Bin 259232 -> 0 bytes in_practice/breakout/textures/particle.png | Bin 0 -> 18912 bytes in_practice/breakout/textures/powerup_chaos.png | Bin 0 -> 31586 bytes in_practice/breakout/textures/powerup_confuse.png | Bin 0 -> 25342 bytes in_practice/breakout/textures/powerup_increase.png | Bin 0 -> 18972 bytes .../breakout/textures/powerup_passthrough.png | Bin 0 -> 18356 bytes in_practice/breakout/textures/powerup_speed.png | Bin 0 -> 21825 bytes in_practice/breakout/textures/powerup_sticky.png | Bin 0 -> 15379 bytes 66 files changed, 1376 insertions(+), 131 deletions(-) create mode 100644 in_practice/breakout/Ac437_IBM_VGA_9x16.ttf create mode 100644 in_practice/breakout/audio.c create mode 100644 in_practice/breakout/audio.h create mode 100644 in_practice/breakout/audio/background.wav create mode 100644 in_practice/breakout/audio/powerup.wav create mode 100644 in_practice/breakout/audio/solid.wav create mode 100644 in_practice/breakout/audio/tile.wav delete mode 100755 in_practice/breakout/breakout delete mode 100644 in_practice/breakout/first.txt create mode 100644 in_practice/breakout/fonts/ibm.ttf delete mode 100644 in_practice/breakout/game.o delete mode 100644 in_practice/breakout/input.o create mode 100644 in_practice/breakout/levels/first.txt create mode 100644 in_practice/breakout/levels/second.txt create mode 100644 in_practice/breakout/levels/third.txt delete mode 100644 in_practice/breakout/linux.o delete mode 100644 in_practice/breakout/my_math.o delete mode 100644 in_practice/breakout/object.o create mode 100644 in_practice/breakout/particle.c create mode 100644 in_practice/breakout/particle.h create mode 100644 in_practice/breakout/post_processor.c create mode 100644 in_practice/breakout/post_processor.h create mode 100644 in_practice/breakout/powerup.c create mode 100644 in_practice/breakout/powerup.h delete mode 100644 in_practice/breakout/shader.o create mode 100644 in_practice/breakout/shaders/particle.frag create mode 100644 in_practice/breakout/shaders/particle.vert create mode 100644 in_practice/breakout/shaders/post.frag create mode 100644 in_practice/breakout/shaders/post.vert create mode 100644 in_practice/breakout/shaders/text.frag create mode 100644 in_practice/breakout/shaders/text.vert delete mode 100644 in_practice/breakout/sprite.o delete mode 100644 in_practice/breakout/sys.o create mode 100644 in_practice/breakout/text.c create mode 100644 in_practice/breakout/text.h delete mode 100644 in_practice/breakout/texture.o create mode 100644 in_practice/breakout/textures/particle.png create mode 100644 in_practice/breakout/textures/powerup_chaos.png create mode 100644 in_practice/breakout/textures/powerup_confuse.png create mode 100644 in_practice/breakout/textures/powerup_increase.png create mode 100644 in_practice/breakout/textures/powerup_passthrough.png create mode 100644 in_practice/breakout/textures/powerup_speed.png create mode 100644 in_practice/breakout/textures/powerup_sticky.png diff --git a/.gitignore b/.gitignore index ed9677a..551efe3 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,12 @@ advanced_lighting/3.2.point_shadows/point_shadows advanced_lighting/4.normal_mapping/normal_mapping advanced_lighting/6.hdr/hdr advanced_lighting/5.parallax_mapping/parallax_mapping +advanced_lighting/7.bloom/bloom +advanced_lighting/8.deferred_shading/deferred +advanced_lighting/9.ssao/ssao +in_practice/breakout/breakout +pbr/ibl_irradiance/ibl_irradiance +pbr/ibl_irradiance_conversion/ibl_irradiance_conversion +pbr/ibl_specular/ibl_specular +pbr/lighting/lighting +pbr/textured/textured diff --git a/advanced_lighting/6.hdr/hdr.c b/advanced_lighting/6.hdr/hdr.c index 655ab06..9177b8b 100644 --- a/advanced_lighting/6.hdr/hdr.c +++ b/advanced_lighting/6.hdr/hdr.c @@ -70,7 +70,7 @@ int main(void) /* NOTE(pryazha): Create HDR framebuffer, color buffer texture and * renderbuffer */ - U32 hdrfbo, hdrcolorbuf; + U32 hdrfbo, hdrcolorbuf; glGenFramebuffers(1, &hdrfbo); glGenTextures(1, &hdrcolorbuf); glBindTexture(GL_TEXTURE_2D, hdrcolorbuf); @@ -78,7 +78,7 @@ int main(void) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - U32 hdrrbo; + U32 hdrrbo; glGenRenderbuffers(1, &hdrrbo); glBindRenderbuffer(GL_RENDERBUFFER, hdrrbo); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); diff --git a/in_practice/breakout/Ac437_IBM_VGA_9x16.ttf b/in_practice/breakout/Ac437_IBM_VGA_9x16.ttf new file mode 100644 index 0000000..5cc1f3d Binary files /dev/null and b/in_practice/breakout/Ac437_IBM_VGA_9x16.ttf differ diff --git a/in_practice/breakout/audio.c b/in_practice/breakout/audio.c new file mode 100644 index 0000000..2a62de4 --- /dev/null +++ b/in_practice/breakout/audio.c @@ -0,0 +1,14 @@ +#include "audio.h" + +#include + +struct sound sounds[max_sounds] = {0}; + +i32 get_sound(const char *name) +{ + for (i32 i = 0; i < max_sounds; i++) { + if (sounds[i].name && strstr(sounds[i].name, name)) + return i; + } + return -1; +} diff --git a/in_practice/breakout/audio.h b/in_practice/breakout/audio.h new file mode 100644 index 0000000..71b232f --- /dev/null +++ b/in_practice/breakout/audio.h @@ -0,0 +1,18 @@ +#ifndef AUDIO_H +#define AUDIO_H + +#include "types.h" + +struct sound { + u8 *data; + u32 size; + const char *name; + i32 loop; +}; + +#define max_sounds 8 +extern struct sound sounds[max_sounds]; + +i32 get_sound(const char *name); + +#endif diff --git a/in_practice/breakout/audio/background.wav b/in_practice/breakout/audio/background.wav new file mode 100644 index 0000000..2ec2882 Binary files /dev/null and b/in_practice/breakout/audio/background.wav differ diff --git a/in_practice/breakout/audio/powerup.wav b/in_practice/breakout/audio/powerup.wav new file mode 100644 index 0000000..bf0a0cd Binary files /dev/null and b/in_practice/breakout/audio/powerup.wav differ diff --git a/in_practice/breakout/audio/solid.wav b/in_practice/breakout/audio/solid.wav new file mode 100644 index 0000000..1fa2fc4 Binary files /dev/null and b/in_practice/breakout/audio/solid.wav differ diff --git a/in_practice/breakout/audio/tile.wav b/in_practice/breakout/audio/tile.wav new file mode 100644 index 0000000..3cead3a Binary files /dev/null and b/in_practice/breakout/audio/tile.wav differ diff --git a/in_practice/breakout/ball.c b/in_practice/breakout/ball.c index 9aa42a2..1ec502e 100644 --- a/in_practice/breakout/ball.c +++ b/in_practice/breakout/ball.c @@ -1,6 +1,6 @@ #include "ball.h" -void move_ball(ball_t *ball, f32 dt, i32 width) +void move_ball(struct ball *ball, f32 dt, i32 width) { if (ball->stuck) return; @@ -18,7 +18,18 @@ void move_ball(ball_t *ball, f32 dt, i32 width) ball->o.pos.y = 0.0f; } } - -void reset_ball(ball_t *ball, v2 pos, v2 vel) + +struct collision check_ball_collision(struct ball ball, struct object obj) { + v2 ball_center = add_v2(ball.o.pos, v2a(ball.radius)); + v2 obj_half_size = scale_v2(obj.size, 0.5f); + v2 obj_center = add_v2(obj.pos, obj_half_size); + v2 diff = sub_v2(ball_center, obj_center); + v2 clamped = clamp_v2(diff, scale_v2(obj_half_size, -1.0f), obj_half_size); + v2 closest = add_v2(obj_center, clamped); + diff = sub_v2(closest, ball_center); + struct collision collision = {0}; + if (length_v2(diff) <= ball.radius) + collision = (struct collision){1, direction_v2(diff), diff}; + return collision; } diff --git a/in_practice/breakout/ball.h b/in_practice/breakout/ball.h index f33773a..60654a3 100644 --- a/in_practice/breakout/ball.h +++ b/in_practice/breakout/ball.h @@ -4,13 +4,21 @@ #include "object.h" #include "types.h" -typedef struct { - object_t o; +struct ball { + struct object o; f32 radius; i32 stuck; -} ball_t; + i32 sticky; + i32 pass; +}; -extern void move_ball(ball_t *ball, f32 dt, i32 width); -extern void reset_ball(ball_t *ball, v2 pos, v2 vel); +struct collision { + i32 collide; + direction_enum dir; + v2 diff; +}; + +void move_ball(struct ball *ball, f32 dt, i32 width); +struct collision check_ball_collision(struct ball ball, struct object obj); #endif diff --git a/in_practice/breakout/breakout b/in_practice/breakout/breakout deleted file mode 100755 index a9cb77c..0000000 Binary files a/in_practice/breakout/breakout and /dev/null differ diff --git a/in_practice/breakout/build.sh b/in_practice/breakout/build.sh index 63e4b93..1cd66f0 100755 --- a/in_practice/breakout/build.sh +++ b/in_practice/breakout/build.sh @@ -1,7 +1,8 @@ #!/bin/sh +include='-I../include -I/usr/include/freetype2' cflags='-g -Wall' -libs='-lSDL3 -lGLEW -lGL -lm' +libs='-lSDL3 -lGLEW -lGL -lm -lfreetype' src=' game.c input.c @@ -15,8 +16,18 @@ sys.c texture.c gl_utils.c ball.c +particle.c +post_processor.c +powerup.c +audio.c +text.c ' +dir=$(dirname $0) +pushd $dir + set -x -gcc -o breakout $cflags $src $libs +gcc -o breakout $cflags $include $src $libs + +popd diff --git a/in_practice/breakout/first.txt b/in_practice/breakout/first.txt deleted file mode 100644 index a46885b..0000000 --- a/in_practice/breakout/first.txt +++ /dev/null @@ -1,3 +0,0 @@ -1 1 1 1 1 -2 3 0 3 2 -2 4 2 4 2 diff --git a/in_practice/breakout/fonts/ibm.ttf b/in_practice/breakout/fonts/ibm.ttf new file mode 100644 index 0000000..5cc1f3d Binary files /dev/null and b/in_practice/breakout/fonts/ibm.ttf differ diff --git a/in_practice/breakout/game.c b/in_practice/breakout/game.c index cf3c0a2..3a84f01 100644 --- a/in_practice/breakout/game.c +++ b/in_practice/breakout/game.c @@ -9,8 +9,11 @@ #include #include +#include -static void init_gl(game_t game) +static v2 initial_ball_velocity = {100.0f, -350.0f}; + +static void init_gl(struct game game) { i32 flags; glGetIntegerv(GL_CONTEXT_FLAGS, &flags); @@ -27,56 +30,360 @@ static void init_gl(game_t game) glClearColor(0.16f, 0.16f, 0.16f, 1.0f); } -game_t init_game(i32 width, i32 height) +static void reset_powerups(struct game *game) +{ + for (i32 i = 0; i < max_powerups; i++) { + game->powerups[i].type = powerup_none; + game->powerups[i].destroyed = 1; + } +} + +struct game init_game(i32 width, i32 height) { - game_t game = {0}; - game.state = game_active; + struct game game = {0}; + game.state = game_menu; game.running = 1; game.width = width; game.height = height; game.input = init_input(); init_gl(game); game.bindir = get_bin_dir(); + char dir[512] = {0}; snprintf(dir, 512, "%s/%s", game.bindir, "shaders"); - u32 shader = load_shader(dir, "shader.vert", "shader.frag", 0, "shader"); - glUseProgram(shader); + load_shader(dir, "shader.vert", "shader.frag", 0, "shader"); + load_shader(dir, "particle.vert", "particle.frag", 0, "particle"); + load_shader(dir, "post.vert", "post.frag", 0, "post"); + load_shader(dir, "text.vert", "text.frag", 0, "text"); + mat projection = ortho(0.0f, game.width, game.height, 0.0f, -1.0f, 1.0f); + u32 shader = get_shader("shader"); + glUseProgram(shader); + uniform_mat(shader, "projection", projection); + glUseProgram(0); + shader = get_shader("particle"); + glUseProgram(shader); uniform_mat(shader, "projection", projection); glUseProgram(0); + snprintf(dir, 512, "%s/%s", game.bindir, "textures"); load_texture(dir, "gentleman.jpg", "gentleman", 0); load_texture(dir, "block.png", "block", 0); load_texture(dir, "solid.png", "solid", 0); load_texture(dir, "paddle.png", "paddle", 1); load_texture(dir, "ball.png", "ball", 1); - game.renderer = init_renderer(shader); + load_texture(dir, "particle.png", "particle", 1); + load_texture(dir, "powerup_chaos.png", "powerup_chaos", 1); + load_texture(dir, "powerup_confuse.png", "powerup_confuse", 1); + load_texture(dir, "powerup_increase.png", "powerup_increase", 1); + load_texture(dir, "powerup_passthrough.png", "powerup_passthrough", 1); + load_texture(dir, "powerup_speed.png", "powerup_speed", 1); + load_texture(dir, "powerup_sticky.png", "powerup_sticky", 1); + + game.renderer = init_renderer(get_shader("shader")); + snprintf(dir, 512, "%s/%s", game.bindir, "levels"); - game.level = load_level(0, "first.txt", game.width, game.height / 2.0f); - game.player = (object_t)default_object; + game.levels[0] = load_level(dir, "first.txt", game.width, game.height / 2.0f); + game.levels[1] = load_level(dir, "second.txt", game.width, game.height / 2.0f); + game.levels[2] = load_level(dir, "third.txt", game.width, game.height / 2.0f); + + snprintf(dir, 512, "%s/%s", game.bindir, "audio"); + load_wav(dir, "background.wav", "back"); + load_wav(dir, "solid.wav", "solid"); + load_wav(dir, "tile.wav", "tile"); + load_wav(dir, "powerup.wav", "powerup"); + play_audio("back", 1); + + game.text_renderer = init_text_renderer(game.width, game.height); + snprintf(dir, 512, "%s/%s", game.bindir, "fonts"); + load_font(&game.text_renderer, dir, "ibm.ttf", 32); + + game.player = (struct object)default_object; v2 size = get_texture_size("paddle"); - game.player.size = scale_v2(size, 0.4f); - game.player.pos = (v2){width / 2.0f - game.player.size.x / 2.0f, height - game.player.size.y}; - game.player.vel = v2a(500.0f); + game.player.size = scale_v2(size, 0.2f); + game.player.pos = (v2){ + game.width / 2.0f - game.player.size.x / 2.0f, + game.height - game.player.size.y + }; + game.player.vel = (v2){200.0f, 0.0f}; game.player.texture = get_texture_id("paddle"); - game.ball.o = (object_t)default_object; + game.lives = 3; + game.ball.o = (struct object)default_object; game.ball.radius = 12.5f; game.ball.o.size = v2a(2.0f * game.ball.radius); - game.ball.o.pos = (v2){game.player.pos.x + game.player.size.x / 2.0f - game.ball.o.size.x / 2.0f, game.player.pos.y - game.ball.o.size.y}; - game.ball.o.vel = (v2){100.0f, -350.0f}; + game.ball.o.pos = (v2){ + game.player.pos.x + game.player.size.x / 2.0f - game.ball.o.size.x / 2.0f, + game.player.pos.y - game.ball.o.size.y + }; + game.ball.o.vel = initial_ball_velocity; game.ball.o.texture = get_texture_id("ball"); game.ball.stuck = 1; + game.generator = init_generator(2, get_shader("particle"), get_texture_id("particle")); + game.post_processor = init_post_processor(game.width, game.height, get_shader("post")); + reset_powerups(&game); return game; } -void process_input(game_t *game) +static void reset_level(struct game *game) +{ + for (i32 i = 0; i < game->levels[game->level].count; i++) + game->levels[game->level].tiles[i].destroyed = 0; + game->lives = 3; +} + +static void reset_player(struct game *game) +{ + game->player.pos = (v2){ + game->width / 2.0f - game->player.size.x / 2.0f, + game->height - game->player.size.y + }; + game->ball.o.pos = (v2){ + game->player.pos.x + game->player.size.x / 2.0f - game->ball.o.size.x / 2.0f, + game->player.pos.y - game->ball.o.size.y + }; + game->ball.o.vel = initial_ball_velocity; + game->ball.stuck = 1; +} + +void process_input(struct game *game) { if (key_first_press(game->input.escape)) game->running = 0; + if (key_first_press(game->input.start)) { + if (game->state == game_menu) + game->state = game_active; + else if (game->state == game_win) { + game->post_processor.chaos = 0; + game->state = game_menu; + } + else if (game->state == game_active) { + game->state = game_menu; + } + } + if (game->state != game_menu) + return; + if (key_first_press(game->input.up)) { + game->level = (game->level + 1) % max_levels; + if (!game->levels[game->level].tiles) + game->level--; + reset_level(game); + reset_player(game); + } + if (key_first_press(game->input.down)) { + i32 new = game->level - 1; + if (new == -1) + new = max_levels - 1; + if (game->levels[new].tiles) { + game->level = new; + reset_level(game); + reset_player(game); + } + } +} + +static i32 should_spawn(u32 chance) +{ + u32 random = rand() % chance; + return random == 0; +} + +static i32 can_spawn_powerup(struct game *game) +{ + for (i32 i = 0; i < max_powerups; i++) + if (game->powerups[i].type == powerup_none) + return 1; + return 0; +} + +static void spawn_powerups(struct game *game, v2 pos) +{ + if (!can_spawn_powerup(game)) + return; + struct powerup *powerup; + for (i32 i = 0; i < max_powerups; i++) { + powerup = game->powerups + i; + if (powerup->type == powerup_none) { + break; + } + } + if (should_spawn(75)) { + *powerup = spawn_powerup(pos, (v3){0.5f, 0.5f, 1.0f}, + powerup_speed, 0.0f, get_texture_id("powerup_speed")); + return; + } + if (should_spawn(75)) { + *powerup = spawn_powerup(pos, (v3){1.0f, 0.5f, 1.0f}, + powerup_sticky, 20.0f, get_texture_id("powerup_sticky")); + return; + } + if (should_spawn(75)) { + *powerup = spawn_powerup(pos, (v3){0.5f, 1.0f, 0.5f}, + powerup_pass, 10.0f, get_texture_id("powerup_pass")); + return; + } + if (should_spawn(75)) { + *powerup = spawn_powerup(pos, (v3){1.0f, 0.6f, 0.4f}, + powerup_increase, 0.0f, get_texture_id("powerup_increase")); + return; + } + if (should_spawn(15)) { + *powerup = spawn_powerup(pos, (v3){1.0f, 0.3f, 0.3f}, + powerup_confuse, 15.0f, get_texture_id("powerup_confuse")); + return; + } + if (should_spawn(15)) { + *powerup = spawn_powerup(pos, (v3){0.9f, 0.25f, 0.25f}, + powerup_chaos, 15.0f, get_texture_id("powerup_chaos")); + return; + } +} + +static void activate_powerup(struct game *game, struct powerup powerup) +{ + switch (powerup.type) { + case powerup_speed: + game->ball.o.vel = scale_v2(game->ball.o.vel, 1.2f); + break; + case powerup_sticky: + game->ball.sticky = 1; + game->player.color = (v3){1.0f, 0.5f, 1.0f}; + break; + case powerup_pass: + game->ball.pass = 1; + game->ball.o.color = (v3){1.0f, 0.5f, 0.5f}; + break; + case powerup_increase: + game->player.size.x += 50.0f; + break; + case powerup_confuse: + if (!game->post_processor.confuse) + game->post_processor.confuse = 1; + break; + case powerup_chaos: + if (!game->post_processor.chaos) + game->post_processor.chaos = 1; + break; + default: + break; + } +} + +static void process_tile_collision(struct object *tile, struct game *game) +{ + if (tile->destroyed) + return; + struct collision collision = check_ball_collision(game->ball, *tile); + if (!collision.collide) + return; + if (!tile->solid) { + tile->destroyed = 1; + v2 pos = sub_v2(add_v2(tile->pos, scale_v2(tile->size, 0.5f)), + scale_v2(default_powerup_size, 0.5f)); + spawn_powerups(game, pos); + } else { + game->shake_time = 0.05f; + game->post_processor.shake = 1; + } + direction_enum dir = collision.dir; + v2 diff = collision.diff; + if (game->ball.pass && !tile->solid) + return; + if (dir == direction_left || dir == direction_right) { + game->ball.o.vel.x = -game->ball.o.vel.x; + f32 offset = game->ball.radius - abs(diff.x); + game->ball.o.pos.x += (dir == direction_left) ? offset : -offset; + } else { + game->ball.o.vel.y = -game->ball.o.vel.y; + f32 offset = game->ball.radius - abs(diff.y); + game->ball.o.pos.y += (dir == direction_up) ? -offset : offset; + } + if (tile->solid) + play_audio("solid", 0); + else + play_audio("tile", 0); +} + +static void process_powerup_collisions(struct game *game, struct powerup *powerup) +{ + if (powerup->destroyed) + return; + if (powerup->pos.y >= game->height) { + powerup->destroyed = 1; + powerup->active = 0; + powerup->type = powerup_none; + } + if (check_powerup_collision(*powerup, game->player)) { + activate_powerup(game, *powerup); + powerup->destroyed = 1; + powerup->active = 1; + play_audio("powerup", 0); + } +} + +static void check_collisions(struct game *game) +{ + for (i32 i = 0; i < game->levels[game->level].count; i++) { + struct object *tile = game->levels[game->level].tiles + i; + process_tile_collision(tile, game); + } + struct collision collision = check_ball_collision(game->ball, game->player); + if (!game->ball.stuck && collision.collide) { + f32 center = game->player.pos.x + game->player.size.x / 2.0f; + f32 distance = game->ball.o.pos.x + game->ball.radius - center; + f32 percentage = distance / game->player.size.x / 2.0f; + f32 strength = 10.0f; + v2 old_vel = game->ball.o.vel; + game->ball.o.vel.x = initial_ball_velocity.x * percentage * strength; + game->ball.o.vel.y = -1.0f * abs(game->ball.o.vel.y); + game->ball.o.vel = scale_v2(norm_v2(game->ball.o.vel), length_v2(old_vel)); + game->ball.stuck = game->ball.sticky; + play_audio("tile", 0); + } + for (i32 i = 0; i < max_powerups; i++) { + struct powerup *powerup = game->powerups + i; + process_powerup_collisions(game, powerup); + } +} + +static void update_powerups(struct game *game, f32 dt) +{ + for (i32 i = 0; i < max_powerups; i++) { + struct powerup *powerup = game->powerups + i; + if (!powerup->destroyed) + powerup->pos = add_v2(powerup->pos, scale_v2(powerup->vel, dt)); + if (powerup->active) { + powerup->duration -= dt; + if (powerup->duration <= 0.0f) { + powerup->duration = 0.0f; + powerup->active = 0; + switch (powerup->type) { + case powerup_sticky: + game->ball.sticky = 0; + game->player.color = v3a(1.0f); + break; + case powerup_pass: + game->ball.pass = 0; + game->ball.o.color = v3a(1.0f); + break; + case powerup_confuse: + game->post_processor.confuse = 0; + break; + case powerup_chaos: + game->post_processor.chaos = 0; + break; + default: + break; + } + powerup->type = powerup_none; + } + } + } } -void update_game(game_t *game, f32 dt) +void update_game(struct game *game, f32 dt) { + if ((game->state != game_active) && (game->state != game_win)) + return; f32 vel = game->player.vel.x * dt; if (game->state == game_active) { if (key_is_pressed(game->input.right)) { @@ -97,21 +404,59 @@ void update_game(game_t *game, f32 dt) game->ball.stuck = 0; } move_ball(&game->ball, dt, game->width); - /* collisions */ - for (i32 i = 0; i < game->level.count; i++) { - object_t *tile = game->level.tiles + i; - if (!tile->destroyed && check_collision(game->ball.o, *tile) && (!tile->solid)) { - tile->destroyed = 1; + check_collisions(game); + if (game->ball.o.pos.y >= game->height) { + game->lives--; + if (game->lives == 0) { + reset_level(game); + game->state = game_menu; } + reset_player(game); + } + update_particles(&game->generator, game->ball.o, dt, v2a(game->ball.radius / 2.0f)); + game->time += dt; + if (game->shake_time > 0.0f) { + game->shake_time -= dt; + if (game->shake_time < 0.0f) + game->post_processor.shake = 0; + } + update_powerups(game, dt); + if (game->state == game_active && level_is_complete(game->levels[game->level])) { + reset_level(game); + reset_player(game); + game->post_processor.chaos = 1; + game->state = game_win; } } -void render_game(game_t game) +static void render_powerups(struct game game) { - glClear(GL_COLOR_BUFFER_BIT); - if (game.state == game_active) { - render_level(game.renderer, game.level); + for (i32 i = 0; i < max_powerups; i++) + if (!game.powerups[i].destroyed) + render_powerup(game.renderer, game.powerups[i]); +} + +void render_game(struct game game) +{ + if ((game.state == game_active) || (game.state == game_menu) || (game.state == game_win)) { + begin_render(game.post_processor); + render_level(game.renderer, game.levels[game.level]); + render_powerups(game); render_object(game.renderer, game.player); + render_particles(game.generator); render_object(game.renderer, game.ball.o); + end_render(game.post_processor); + render_post(game.post_processor, game.time); + char lives_str[512] = {0}; + snprintf(lives_str, 512, "lives: %d", game.lives); + render_text(game.text_renderer, lives_str, v2a(5.0f), 1.0f, v3a(1.0f)); + } + if (game.state == game_menu) { + render_text_centered(game.text_renderer, "Press Enter to start", (v2){game.width * 0.5f, game.height * 0.5f}, 1.0f, v3a(1.0f)); + render_text_centered(game.text_renderer, "Press UP and DOWN to select the level", (v2){game.width * 0.5f, game.height * 0.55f}, 0.75f, v3a(1.0f)); + } + if (game.state == game_win) { + render_text_centered(game.text_renderer, "You WON!!!", (v2){game.width * 0.5f, game.height * 0.5f}, 1.0f, v3a(1.0f)); + render_text_centered(game.text_renderer, "Press Enter to retry or ESC to quit", (v2){game.width * 0.5f, game.height * 0.55f}, 0.75f, v3a(1.0f)); } } diff --git a/in_practice/breakout/game.h b/in_practice/breakout/game.h index 31f5dcd..5bc7abe 100644 --- a/in_practice/breakout/game.h +++ b/in_practice/breakout/game.h @@ -6,28 +6,43 @@ #include "sprite.h" #include "level.h" #include "ball.h" +#include "particle.h" +#include "post_processor.h" +#include "powerup.h" +#include "text.h" -typedef enum { +enum game_state { game_active, game_menu, game_win -} game_state_enum; +}; -typedef struct { - game_state_enum state; +#define max_powerups 4 +#define max_levels 4 + +struct game { + enum game_state state; i32 running; - input_t input; + struct input input; i32 width, height; const char *bindir; - sprite_renderer_t renderer; - level_t level; - object_t player; - ball_t ball; -} game_t; + struct sprite_renderer renderer; + struct level levels[max_levels]; + i32 level; + struct object player; + u32 lives; + struct ball ball; + struct particle_generator generator; + struct post_processor post_processor; + f32 time; + f32 shake_time; + struct powerup powerups[max_powerups]; + struct text_renderer text_renderer; +}; -extern game_t init_game(i32 width, i32 height); -extern void process_input(game_t *game); -extern void update_game(game_t *game, f32 dt); -extern void render_game(game_t game); +struct game init_game(i32 width, i32 height); +void process_input(struct game *game); +void update_game(struct game *game, f32 dt); +void render_game(struct game game); #endif diff --git a/in_practice/breakout/game.o b/in_practice/breakout/game.o deleted file mode 100644 index 9c81917..0000000 Binary files a/in_practice/breakout/game.o and /dev/null differ diff --git a/in_practice/breakout/input.c b/in_practice/breakout/input.c index 1a4b76e..43f54bc 100644 --- a/in_practice/breakout/input.c +++ b/in_practice/breakout/input.c @@ -1,30 +1,33 @@ #include "input.h" -input_t init_input(void) +struct input init_input(void) { - input_t input = {0}; + struct input input = {0}; return input; } -void update_input(input_t *input) +void update_input(struct input *input) { input->escape.last = input->escape.current; input->right.last = input->right.current; input->left.last = input->left.current; + input->up.last = input->up.current; + input->down.last = input->down.current; input->space.last = input->space.current; + input->start.last = input->start.current; } -i32 key_first_press(key_state_t key) +i32 key_first_press(struct key key) { return (key.current && !key.last); } -i32 key_is_pressed(key_state_t key) +i32 key_is_pressed(struct key key) { return key.current; } -i32 key_was_pressed(key_state_t key) +i32 key_was_pressed(struct key key) { return (key.last && !key.current); } diff --git a/in_practice/breakout/input.h b/in_practice/breakout/input.h index d5b9306..58b69f4 100644 --- a/in_practice/breakout/input.h +++ b/in_practice/breakout/input.h @@ -3,22 +3,25 @@ #include "types.h" -typedef struct { +struct key { i32 last; i32 current; -} key_state_t; +}; -typedef struct { - key_state_t escape; - key_state_t right; - key_state_t left; - key_state_t space; -} input_t; +struct input { + struct key escape; + struct key right; + struct key left; + struct key up; + struct key down; + struct key space; + struct key start; +}; -extern input_t init_input(void); -extern void update_input(input_t *input); -extern i32 key_first_press(key_state_t key); -extern i32 key_is_pressed(key_state_t key); -extern i32 key_was_pressed(key_state_t key); +struct input init_input(void); +void update_input(struct input *input); +i32 key_first_press(struct key key); +i32 key_is_pressed(struct key key); +i32 key_was_pressed(struct key key); #endif diff --git a/in_practice/breakout/input.o b/in_practice/breakout/input.o deleted file mode 100644 index eda5524..0000000 Binary files a/in_practice/breakout/input.o and /dev/null differ diff --git a/in_practice/breakout/level.c b/in_practice/breakout/level.c index 6134959..619c05f 100644 --- a/in_practice/breakout/level.c +++ b/in_practice/breakout/level.c @@ -5,12 +5,12 @@ #include #include -level_t load_level(const char *dir, const char *filename, i32 width, i32 height) +struct level load_level(const char *dir, const char *filename, i32 width, i32 height) { assert(filename); assert(width); assert(height); - level_t level = {0}; + struct level level = {0}; char path[512] = {0}; snprintf(path, 512, "%s/%s", (dir ? dir : "."), filename); u8 *data = (u8 *)read_entire_file(path); @@ -53,10 +53,10 @@ level_t load_level(const char *dir, const char *filename, i32 width, i32 height) return level; } -level_t init_level(u8 *tiles, i32 rows, i32 cols, i32 width, i32 height) +struct level init_level(u8 *tiles, i32 rows, i32 cols, i32 width, i32 height) { assert(tiles); - level_t level = {0}; + struct level level = {0}; level.rows = rows; level.cols = cols; f32 tile_width = width / cols; @@ -65,14 +65,14 @@ level_t init_level(u8 *tiles, i32 rows, i32 cols, i32 width, i32 height) for (i32 i = 0; i < rows * cols; i++) if (tiles[i]) level.count++; - level.tiles = malloc(sizeof(object_t) * level.count); + level.tiles = malloc(sizeof(struct object) * level.count); i32 i = 0; for (i32 row = 0; row < rows; row++) { for (i32 col = 0; col < cols; col++) { u8 *tile = tiles + col + row * cols; if (!*tile) continue; - object_t obj = {0}; + struct object obj = {0}; obj.pos = (v2){tile_width * col, tile_height * row}; obj.size = size; obj.texture = get_texture_id("solid"); @@ -104,7 +104,7 @@ level_t init_level(u8 *tiles, i32 rows, i32 cols, i32 width, i32 height) return level; } -void clear_level(level_t *level) +void clear_level(struct level *level) { free(level->tiles); level->tiles = 0; @@ -112,14 +112,14 @@ void clear_level(level_t *level) level->cols = 0; } -void render_level(sprite_renderer_t renderer, level_t level) +void render_level(struct sprite_renderer renderer, struct level level) { for (i32 i = 0; i < level.count; i++) if (!level.tiles[i].destroyed) render_object(renderer, level.tiles[i]); } -i32 level_is_completed(level_t level) +i32 level_is_complete(struct level level) { for (i32 i = 0; i < level.count; i++) if (!level.tiles[i].solid && !level.tiles[i].destroyed) diff --git a/in_practice/breakout/level.h b/in_practice/breakout/level.h index 91e3b85..97b2b35 100644 --- a/in_practice/breakout/level.h +++ b/in_practice/breakout/level.h @@ -3,16 +3,17 @@ #include "object.h" -typedef struct { - object_t *tiles; +struct level { + struct object *tiles; i32 count; i32 rows; i32 cols; -} level_t; +}; -extern level_t load_level(const char *dir, const char *filename, i32 width, i32 height); -extern level_t init_level(u8 *tiles, i32 rows, i32 cols, i32 width, i32 height); -extern void render_level(sprite_renderer_t renderer, level_t level); -extern i32 level_is_completed(level_t level); +struct level load_level(const char *dir, const char *filename, i32 width, i32 height); +struct level init_level(u8 *tiles, i32 rows, i32 cols, i32 width, i32 height); +void clear_level(struct level *level); +void render_level(struct sprite_renderer renderer, struct level level); +i32 level_is_complete(struct level level); #endif diff --git a/in_practice/breakout/levels/first.txt b/in_practice/breakout/levels/first.txt new file mode 100644 index 0000000..f762ca4 --- /dev/null +++ b/in_practice/breakout/levels/first.txt @@ -0,0 +1,8 @@ +2 2 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 2 2 +0 0 0 3 2 2 2 2 2 2 2 2 2 2 2 2 3 0 0 0 +3 2 3 4 2 3 3 3 2 2 2 2 3 3 3 2 4 3 2 3 +4 3 2 2 1 1 2 2 1 1 1 1 2 2 1 1 2 2 3 4 +0 0 2 2 2 2 3 3 3 2 2 3 3 3 2 2 2 0 0 0 +0 4 2 2 0 0 2 0 0 0 0 0 0 2 0 0 2 2 4 0 +0 4 2 2 2 0 0 0 0 0 0 0 0 0 0 2 2 2 4 0 +0 0 2 2 2 2 0 0 0 0 0 0 0 0 2 2 2 2 0 0 diff --git a/in_practice/breakout/levels/second.txt b/in_practice/breakout/levels/second.txt new file mode 100644 index 0000000..e41d1a3 --- /dev/null +++ b/in_practice/breakout/levels/second.txt @@ -0,0 +1,8 @@ +0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 +0 0 0 0 2 2 2 2 2 2 2 2 2 2 2 2 0 0 0 0 +0 1 3 4 4 4 3 3 4 1 1 4 3 3 4 4 4 3 1 0 +0 1 2 4 4 4 2 2 4 4 4 4 2 2 4 4 4 2 1 0 +0 0 2 2 2 2 3 3 3 2 2 3 3 3 2 2 2 0 3 0 +0 4 2 2 0 0 2 0 0 0 0 0 0 2 0 0 2 3 0 0 +0 4 2 2 2 0 0 0 0 0 0 0 0 0 0 2 2 0 0 0 +0 0 0 0 0 0 0 0 3 3 3 3 0 0 0 0 0 0 0 0 diff --git a/in_practice/breakout/levels/third.txt b/in_practice/breakout/levels/third.txt new file mode 100644 index 0000000..cb53cca --- /dev/null +++ b/in_practice/breakout/levels/third.txt @@ -0,0 +1,3 @@ +0 0 0 0 0 +0 0 2 0 0 +0 0 0 0 0 diff --git a/in_practice/breakout/linux.c b/in_practice/breakout/linux.c index 2150ed9..8b7f28a 100644 --- a/in_practice/breakout/linux.c +++ b/in_practice/breakout/linux.c @@ -1,26 +1,34 @@ #include "game.h" #include "sys.h" +#include "audio.h" #include #include -#define DEBUG 1 +#include -typedef struct { +#define DEBUG 0 + +struct sdl_context { SDL_Window *window; i32 width; i32 height; SDL_Renderer *renderer; + SDL_AudioDeviceID audio_device; + SDL_AudioStream *streams[max_sounds]; + i32 last_sound; u64 last_counter; f32 dt; -} sdl_context_t; +}; + +static struct sdl_context ctx; -static sdl_context_t init_sdl(i32 width, i32 height, i32 debug) +static struct sdl_context init_sdl(i32 width, i32 height, i32 debug) { - sdl_context_t ctx = {0}; + struct sdl_context ctx = {0}; ctx.width = width; ctx.height = height; - SDL_Init(SDL_INIT_VIDEO); + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); u64 flags = SDL_WINDOW_OPENGL; SDL_CreateWindowAndRenderer("breakout", ctx.width, ctx.height, flags, &ctx.window, &ctx.renderer); if (debug) @@ -30,10 +38,13 @@ static sdl_context_t init_sdl(i32 width, i32 height, i32 debug) SDL_GL_SetSwapInterval(0); ctx.last_counter = SDL_GetPerformanceCounter(); glewInit(); + ctx.audio_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, 0); + if (!ctx.audio_device) + die("failed to open audio device"); return ctx; } -static void process_sdl_key(input_t *input, i32 scancode, i32 pressed) +static void process_sdl_key(struct input *input, i32 scancode, i32 pressed) { switch (scancode) { case SDL_SCANCODE_ESCAPE: @@ -45,15 +56,24 @@ static void process_sdl_key(input_t *input, i32 scancode, i32 pressed) case SDL_SCANCODE_LEFT: input->left.current = pressed; break; + case SDL_SCANCODE_UP: + input->up.current = pressed; + break; + case SDL_SCANCODE_DOWN: + input->down.current = pressed; + break; case SDL_SCANCODE_SPACE: input->space.current = pressed; break; + case SDL_SCANCODE_RETURN: + input->start.current = pressed; + break; default: break; } } -static void process_sdl_events(game_t *game) +static void process_sdl_events(struct game *game) { SDL_Event event; while (SDL_PollEvent(&event)) { @@ -74,11 +94,11 @@ static f32 get_elapsed(u64 last_counter) return (f32)(current_counter - last_counter) / (f32)frequency; } -static void lock_framerate_sdl(sdl_context_t *ctx, i32 fps) +static void lock_framerate_sdl(struct sdl_context *ctx, i32 fps) { f32 elapsed = get_elapsed(ctx->last_counter) * 1000.0f; f32 target = 1000.0f / (f32)fps; - f32 sleep = target - elapsed; + f32 sleep = target - elapsed - 1.0f; if (sleep > 0) SDL_Delay((u32)sleep); do { @@ -88,6 +108,7 @@ static void lock_framerate_sdl(sdl_context_t *ctx, i32 fps) ctx->last_counter = SDL_GetPerformanceCounter(); } +#if DEBUG static void fps_info(f32 dt, i32 again) { static f32 time = 0; @@ -112,18 +133,66 @@ static void fps_info(f32 dt, i32 again) } } } +#endif + +void load_wav(const char *dir, const char *filename, const char *name) +{ + char path[512] = {0}; + snprintf(path, 512, "%s/%s", (dir ? dir : "."), filename); + SDL_AudioSpec spec; + u8 *data; + u32 size; + if (!SDL_LoadWAV(path, &spec, &data, &size)) + die("failed to load wav \"%s\"", path); + if (ctx.last_sound >= max_sounds) { + info("max sounds reached"); + return; + } + sounds[ctx.last_sound] = (struct sound){data, size, name}; + if (!(ctx.streams[ctx.last_sound] = SDL_CreateAudioStream(&spec, 0))) + die("failed to create audio stream"); + if (!SDL_BindAudioStream(ctx.audio_device, ctx.streams[ctx.last_sound])) + die("failed to bind audio stream"); + ctx.last_sound++; + info("sound \"%s\" loaded successfuly", path); +} + +void play_audio(const char *name, i32 loop) +{ + i32 i = get_sound(name); + if (i == -1) + info("no audio with the name \"%s\"", name); + sounds[i].loop = loop; + if (SDL_GetAudioStreamQueued(ctx.streams[i])) + SDL_ClearAudioStream(ctx.streams[i]); + SDL_PutAudioStreamData(ctx.streams[i], sounds[i].data, sounds[i].size); +} + +static void update_audio(void) +{ + for (i32 i = 0; i < max_sounds; i++) { + if (sounds[i].loop) { + i32 queued = SDL_GetAudioStreamQueued(ctx.streams[i]); + if (queued < (i32)sounds[i].size) + SDL_PutAudioStreamData(ctx.streams[i], sounds[i].data, sounds[i].size); + } + } +} i32 main(void) { - sdl_context_t ctx = init_sdl(1600, 800, DEBUG); - game_t game = init_game(ctx.width, ctx.height); + ctx = init_sdl(800, 600, DEBUG); + struct game game = init_game(ctx.width, ctx.height); while (game.running) { process_sdl_events(&game); process_input(&game); update_game(&game, ctx.dt); + update_audio(); update_input(&game.input); lock_framerate_sdl(&ctx, 60); +#if DEBUG fps_info(ctx.dt, 10); +#endif render_game(game); SDL_GL_SwapWindow(ctx.window); } diff --git a/in_practice/breakout/linux.o b/in_practice/breakout/linux.o deleted file mode 100644 index 732d25b..0000000 Binary files a/in_practice/breakout/linux.o and /dev/null differ diff --git a/in_practice/breakout/my_math.c b/in_practice/breakout/my_math.c index 5d69b92..ac5606b 100644 --- a/in_practice/breakout/my_math.c +++ b/in_practice/breakout/my_math.c @@ -1,6 +1,16 @@ #include "my_math.h" #include +v2 v2a(f32 x) +{ + return (v2){x, x}; +} + +v3 v3a(f32 x) +{ + return (v3){x, x, x}; +} + v2 scale_v2(v2 v, f32 x) { return (v2){v.x * x, v.y * x}; @@ -11,14 +21,57 @@ v2 add_v2(v2 a, v2 b) return (v2){a.x + b.x, a.y + b.y}; } -v2 v2a(f32 x) +v2 addf_v2(v2 a, f32 b) { - return (v2){x, x}; + return (v2){a.x + b, a.y + b}; } -v3 v3a(f32 x) +v2 sub_v2(v2 a, v2 b) { - return (v3){x, x, x}; + return (v2){a.x - b.x, a.y - b.y}; +} + +v2 clamp_v2(v2 v, v2 min, v2 max) +{ + f32 x = v.x < min.x ? min.x : v.x > max.x ? max.x : v.x; + f32 y = v.y < min.y ? min.y : v.y > max.y ? max.y : v.y; + return (v2){x, y}; +} + +f32 length_v2(v2 v) +{ + return sqrtf(v.x * v.x + v.y * v.y); +} + +f32 dot_v2(v2 a, v2 b) +{ + return a.x * b.x + a.y * b.y; +} + +v2 norm_v2(v2 a) +{ + f32 len = length_v2(a); + return (v2){a.x / len, a.y / len}; +} + +direction_enum direction_v2(v2 v) +{ + v2 directions[] = { + {1.0f, 0.0f}, + {0.0f, 1.0f}, + {-1.0f, 0.0f}, + {0.0f, -1.0f} + }; + f32 max = 0.0f; + i32 best = 0; + for (i32 i = 0; i < 4; i++) { + f32 dot = dot_v2(v, directions[i]); + if (dot > max) { + max = dot; + best = i; + } + } + return best; } mat mul_mat(mat l, mat r) diff --git a/in_practice/breakout/my_math.h b/in_practice/breakout/my_math.h index 29efb45..4c69e23 100644 --- a/in_practice/breakout/my_math.h +++ b/in_practice/breakout/my_math.h @@ -8,6 +8,8 @@ typedef struct {f32 x, y, z;} v3; typedef struct {f32 x, y, z, w;} v4; typedef struct {v4 c0, c1, c2, c3;} mat; +typedef enum {direction_right, direction_up, direction_left, direction_down} direction_enum; + #define mat_identity { \ {1.0f, 0.0f, 0.0f, 0.0f}, \ {0.0f, 1.0f, 0.0f, 0.0f}, \ @@ -18,10 +20,17 @@ typedef struct {v4 c0, c1, c2, c3;} mat; #define deg2rad(a) (a) * f32pi / 180.0f #define rad2deg(a) (a) * 180.0f / f32pi -extern v2 scale_v2(v2 v, f32 x); -extern v2 add_v2(v2 a, v2 b); extern v2 v2a(f32 x); extern v3 v3a(f32 x); +extern v2 scale_v2(v2 v, f32 x); +extern v2 add_v2(v2 a, v2 b); +extern v2 addf_v2(v2 a, f32 b); +extern v2 sub_v2(v2 a, v2 b); +extern v2 clamp_v2(v2 v, v2 min, v2 max); +extern f32 length_v2(v2 v); +extern f32 dot_v2(v2 a, v2 b); +extern v2 norm_v2(v2 a); +extern direction_enum direction_v2(v2 v); extern mat mul_mat(mat l, mat r); extern mat make_scale_mat(v3 v); extern mat make_rotate_mat(v3 x, v3 y, v3 z); diff --git a/in_practice/breakout/my_math.o b/in_practice/breakout/my_math.o deleted file mode 100644 index 5516ac5..0000000 Binary files a/in_practice/breakout/my_math.o and /dev/null differ diff --git a/in_practice/breakout/object.c b/in_practice/breakout/object.c index 8fafb6e..3cfc2f7 100644 --- a/in_practice/breakout/object.c +++ b/in_practice/breakout/object.c @@ -1,13 +1,13 @@ #include "object.h" -void render_object(sprite_renderer_t renderer, object_t obj) +void render_object(struct sprite_renderer renderer, struct object obj) { render_sprite(renderer, obj.texture, obj.pos, obj.size, obj.rotation, obj.color); } -i32 check_collision(object_t a, object_t b) +i32 check_collision(struct object a, struct object b) { - i32 collision_x = (a.pos.x + a.size.x >= b.pos.x) && (b.pos.x + b.size.x >= a.pos.x); - i32 collision_y = (a.pos.y + a.size.y >= b.pos.y) && (b.pos.y + b.size.y >= a.pos.y); - return collision_x && collision_y; + i32 x = (a.pos.x + a.size.x >= b.pos.x) && (b.pos.x + b.size.x >= a.pos.x); + i32 y = (a.pos.y + a.size.y >= b.pos.y) && (b.pos.y + b.size.y >= a.pos.y); + return x && y; } diff --git a/in_practice/breakout/object.h b/in_practice/breakout/object.h index e1cfedb..8311d9e 100644 --- a/in_practice/breakout/object.h +++ b/in_practice/breakout/object.h @@ -4,7 +4,7 @@ #include "my_math.h" #include "sprite.h" -typedef struct { +struct object { v2 pos; v2 vel; v2 size; @@ -13,7 +13,7 @@ typedef struct { i32 solid; i32 destroyed; u32 texture; -} object_t; +}; #define default_object { \ .pos = {0.0f, 0.0f}, \ @@ -26,7 +26,7 @@ typedef struct { .texture = 0 \ } -extern void render_object(sprite_renderer_t renderer, object_t obj); -extern i32 check_collision(object_t a, object_t b); +void render_object(struct sprite_renderer renderer, struct object obj); +i32 check_collision(struct object a, struct object b); #endif diff --git a/in_practice/breakout/object.o b/in_practice/breakout/object.o deleted file mode 100644 index ea2ad29..0000000 Binary files a/in_practice/breakout/object.o and /dev/null differ diff --git a/in_practice/breakout/particle.c b/in_practice/breakout/particle.c new file mode 100644 index 0000000..8c71520 --- /dev/null +++ b/in_practice/breakout/particle.c @@ -0,0 +1,104 @@ +#include "particle.h" +#include "types.h" +#include "shader.h" + +#include +#include + +void respawn_particle(struct particle *particle, struct object object, v2 offset) +{ + f32 random = ((rand() % 100) - 50) / 10.0f; + particle->pos = addf_v2(add_v2(object.pos, offset), random); + particle->vel = scale_v2(object.vel, 0.1f); + random = 0.5f + ((rand() % 100) / 100.0f); + particle->color = (v4){random, random, random, 1.0f}; + particle->life = 1.0f; +} + +i32 find_unused_particle(struct particle_generator generator) +{ + f32 min = FLT_MAX; + i32 mini = 0; + for (i32 i = 0; i < MAX_PARTICLES; i++) { + f32 life = generator.particles[i].life; + if (life <= 0.0f) { + return i; + } else if (life < min) { + min = life; + mini = i; + } + } + return mini; +} + +struct particle_generator init_generator(i32 new, u32 shader, u32 texture) +{ + struct particle_generator generator = {0}; + reset_particles(&generator); + generator.new = new; + generator.shader = shader; + generator.texture = texture; + f32 vertices[] = { + 0.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 1.0f, + + 1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + 0.0f, 1.0f, 0.0f, 1.0f + }; + u32 vao, vbo; + glGenVertexArrays(1, &vao); + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + glBindVertexArray(vao); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 4, GL_FLOAT, 0, 4 * sizeof(f32), 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + generator.vao = vao; + return generator; +} + +void update_particles(struct particle_generator *generator, struct object object, f32 dt, v2 offset) +{ + for (i32 i = 0; i < generator->new; i++) { + i32 unused = find_unused_particle(*generator); + respawn_particle(generator->particles + unused, object, offset); + } + for (i32 i = 0; i < MAX_PARTICLES; i++) { + struct particle *particle = generator->particles + i; + particle->life -= dt; + if (particle->life > 0.0f) { + particle->pos = sub_v2(particle->pos, scale_v2(particle->vel, dt)); + particle->color.w -= dt * 2.0f; + } + } +} + +void render_particles(struct particle_generator generator) +{ + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glUseProgram(generator.shader); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, generator.texture); + glBindVertexArray(generator.vao); + for (i32 i = 0; i < MAX_PARTICLES; i++) { + struct particle *particle = generator.particles + i; + if (particle->life > 0.0f) { + uniform_v2(generator.shader, "offset", particle->pos); + uniform_v4(generator.shader, "color", particle->color); + glDrawArrays(GL_TRIANGLES, 0, 6); + } + } + glBindVertexArray(0); + glUseProgram(0); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void reset_particles(struct particle_generator *generator) +{ + for (i32 i = 0; i < MAX_PARTICLES; i++) + generator->particles[i] = (struct particle)default_particle; +} diff --git a/in_practice/breakout/particle.h b/in_practice/breakout/particle.h new file mode 100644 index 0000000..3e5d9a6 --- /dev/null +++ b/in_practice/breakout/particle.h @@ -0,0 +1,38 @@ +#ifndef PARTICLE_H +#define PARTICLE_H + +#include "my_math.h" +#include "object.h" + +struct particle { + v2 pos; + v2 vel; + v4 color; + f32 life; +}; + +#define default_particle { \ + .pos = {0.0f, 0.0f}, \ + .vel = {0.0f, 0.0f}, \ + .color = {1.0f, 1.0f, 1.0f, 1.0f}, \ + .life = 0.0f \ +} + +#define MAX_PARTICLES 500 + +struct particle_generator { + struct particle particles[MAX_PARTICLES]; + i32 new; + u32 shader; + u32 texture; + u32 vao; +}; + +void respawn_particle(struct particle *particle, struct object object, v2 offset); +i32 find_unused_particle(struct particle_generator generator); +struct particle_generator init_generator(i32 new, u32 shader, u32 texture); +void update_particles(struct particle_generator *generator, struct object object, f32 dt, v2 offset); +void render_particles(struct particle_generator generator); +void reset_particles(struct particle_generator *generator); + +#endif diff --git a/in_practice/breakout/post_processor.c b/in_practice/breakout/post_processor.c new file mode 100644 index 0000000..70691c2 --- /dev/null +++ b/in_practice/breakout/post_processor.c @@ -0,0 +1,109 @@ +#include "post_processor.h" +#include "sys.h" +#include "shader.h" +#include "texture.h" + +#include + +struct post_processor init_post_processor(i32 width, i32 height, u32 shader) +{ + struct post_processor processor = {0}; + processor.width = width; + processor.height = height; + glGenFramebuffers(1, &processor.msfbo); + glGenFramebuffers(1, &processor.fbo); + glGenRenderbuffers(1, &processor.rbo); + glBindFramebuffer(GL_FRAMEBUFFER, processor.msfbo); + glBindRenderbuffer(GL_RENDERBUFFER, processor.rbo); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGB, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, processor.rbo); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + die("failed to create msfbo"); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, processor.fbo); + gl_texture_t texture = (gl_texture_t)default_texture; + generate_texture(&texture, width, height, 0); + processor.texture = texture.id; + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, processor.texture, 0); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + die("failed to create fbo"); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + u32 vbo; + f32 vertices[] = { + -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f + }; + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + glGenVertexArrays(1, &processor.vao); + glBindVertexArray(processor.vao); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), 0); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + processor.shader = shader; + glUseProgram(processor.shader); + uniform_i32(processor.shader, "colorbuffer", 0); + f32 offset = 1.0f / 300.0f; + f32 offsets[] = { + -offset, offset, /* top-left */ + 0.0f, offset, /* top-center */ + offset, offset, /* top-right */ + -offset, 0.0f, /* center-left */ + 0.0f, 0.0f, /* center */ + offset, 0.0f, /* center-right */ + -offset, -offset, /* bottom-left */ + 0.0f, -offset, /* bottom-center */ + offset, -offset /* bottom-right */ + }; + glUniform2fv(glGetUniformLocation(processor.shader, "offsets"), 9, offsets); + i32 edge_kernel[] = { + -1, -1, -1, + -1, 8, -1, + -1, -1, -1 + }; + glUniform1iv(glGetUniformLocation(processor.shader, "edge_kernel"), 9, edge_kernel); + f32 blur_kernel[] = { + 1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f, + 2.0f / 16.0f, 4.0f / 16.0f, 2.0f / 16.0f, + 1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f + }; + glUniform1fv(glGetUniformLocation(processor.shader, "blur_kernel"), 9, blur_kernel); + glUseProgram(0); + return processor; +} + +void begin_render(struct post_processor processor) +{ + glBindFramebuffer(GL_FRAMEBUFFER, processor.msfbo); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); +} + +void end_render(struct post_processor processor) +{ + glBindFramebuffer(GL_READ_FRAMEBUFFER, processor.msfbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, processor.fbo); + glBlitFramebuffer(0, 0, processor.width, processor.height, 0, 0, processor.width, processor.height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void render_post(struct post_processor processor, f32 time) +{ + glUseProgram(processor.shader); + uniform_f32(processor.shader, "time", time); + uniform_i32(processor.shader, "confuse", processor.confuse); + uniform_i32(processor.shader, "chaos", processor.chaos); + uniform_i32(processor.shader, "shake", processor.shake); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, processor.texture); + glBindVertexArray(processor.vao); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + glUseProgram(0); +} diff --git a/in_practice/breakout/post_processor.h b/in_practice/breakout/post_processor.h new file mode 100644 index 0000000..5056aee --- /dev/null +++ b/in_practice/breakout/post_processor.h @@ -0,0 +1,25 @@ +#ifndef POST_PROCESSOR_H +#define POST_PROCESSOR_H + +#include "types.h" + +struct post_processor { + i32 width, height; + u32 msfbo; + u32 fbo; + u32 rbo; + u32 texture; + u32 vao; + i32 confuse, chaos, shake; + u32 shader; +}; + +struct post_processor init_post_processor(i32 width, i32 height, u32 shader); +void begin_render(struct post_processor processor); +void end_render(struct post_processor processor); +void render_post(struct post_processor processor, f32 time); + +#endif + + + diff --git a/in_practice/breakout/powerup.c b/in_practice/breakout/powerup.c new file mode 100644 index 0000000..ab09b9f --- /dev/null +++ b/in_practice/breakout/powerup.c @@ -0,0 +1,31 @@ +#include "powerup.h" + +v2 default_powerup_size = {60.0f, 20.0}; + +struct powerup spawn_powerup(v2 pos, v3 color, enum powerup_type type, f32 duration, u32 texture) +{ + struct powerup powerup = {0}; + powerup.pos = pos; + powerup.vel = (v2){0.0f, 150.0f}; + powerup.size = default_powerup_size; + powerup.color = color; + powerup.type = type; + powerup.duration = duration; + powerup.texture = texture; + return powerup; +} + +i32 check_powerup_collision(struct powerup powerup, struct object obj) +{ + + i32 x = (powerup.pos.x + powerup.size.x >= obj.pos.x) && + (obj.pos.x + obj.size.x >= powerup.pos.x); + i32 y = (powerup.pos.y + powerup.size.y >= obj.pos.y) && + (obj.pos.y + obj.size.y >= powerup.pos.y); + return x && y; +} + +void render_powerup(struct sprite_renderer renderer, struct powerup powerup) +{ + render_sprite(renderer, powerup.texture, powerup.pos, powerup.size, 0.0f, powerup.color); +} diff --git a/in_practice/breakout/powerup.h b/in_practice/breakout/powerup.h new file mode 100644 index 0000000..b80ea74 --- /dev/null +++ b/in_practice/breakout/powerup.h @@ -0,0 +1,36 @@ +#ifndef POWERUP_H +#define POWERUP_H + +#include "types.h" +#include "my_math.h" +#include "object.h" + +enum powerup_type { + powerup_none, + powerup_speed, + powerup_sticky, + powerup_pass, + powerup_increase, + powerup_confuse, + powerup_chaos +}; + +struct powerup { + v2 pos; + v2 vel; + v2 size; + v3 color; + enum powerup_type type; + f32 duration; + i32 active; + i32 destroyed; + u32 texture; +}; + +extern v2 default_powerup_size; + +struct powerup spawn_powerup(v2 pos, v3 color, enum powerup_type type, f32 duration, u32 texture); +i32 check_powerup_collision(struct powerup powerup, struct object obj); +void render_powerup(struct sprite_renderer renderer, struct powerup powerup); + +#endif diff --git a/in_practice/breakout/shader.c b/in_practice/breakout/shader.c index 2fff3c4..fa1ff2c 100644 --- a/in_practice/breakout/shader.c +++ b/in_practice/breakout/shader.c @@ -88,12 +88,31 @@ void uniform_f32(u32 id, const char *name, f32 v) glUniform1f(loc, v); } +void uniform_i32(u32 id, const char *name, i32 v) +{ + u32 loc = glGetUniformLocation(id, name); + glUniform1i(loc, v); +} + + +void uniform_v2(u32 id, const char *name, v2 v) +{ + u32 loc = glGetUniformLocation(id, name); + glUniform2fv(loc, 1, (const f32 *)&v); +} + void uniform_v3(u32 id, const char *name, v3 v) { u32 loc = glGetUniformLocation(id, name); glUniform3fv(loc, 1, (const f32 *)&v); } +void uniform_v4(u32 id, const char *name, v4 v) +{ + u32 loc = glGetUniformLocation(id, name); + glUniform4fv(loc, 1, (const f32 *)&v); +} + void uniform_mat(u32 id, const char *name, mat m) { u32 loc = glGetUniformLocation(id, name); diff --git a/in_practice/breakout/shader.h b/in_practice/breakout/shader.h index 19c3044..922539d 100644 --- a/in_practice/breakout/shader.h +++ b/in_practice/breakout/shader.h @@ -14,7 +14,10 @@ extern shader_t shaders[MAX_SHADERS]; extern u32 load_shader(const char *dir, const char *vertex_filename, const char *fragment_filename, const char *geometry_filename, const char *name); extern u32 get_shader(const char *name); extern void uniform_f32(u32 id, const char *name, f32 v); +extern void uniform_i32(u32 id, const char *name, i32 v); +extern void uniform_v2(u32 id, const char *name, v2 v); extern void uniform_v3(u32 id, const char *name, v3 v); +extern void uniform_v4(u32 id, const char *name, v4 v); extern void uniform_mat(u32 id, const char *name, mat m); #endif diff --git a/in_practice/breakout/shader.o b/in_practice/breakout/shader.o deleted file mode 100644 index b96b63c..0000000 Binary files a/in_practice/breakout/shader.o and /dev/null differ diff --git a/in_practice/breakout/shaders/particle.frag b/in_practice/breakout/shaders/particle.frag new file mode 100644 index 0000000..ef62554 --- /dev/null +++ b/in_practice/breakout/shaders/particle.frag @@ -0,0 +1,15 @@ +#version 330 core + +in vert_t { + vec2 tex_coords; + vec4 color; +} vert; + +out vec4 frag_color; + +uniform sampler2D sprite; + +void main() +{ + frag_color = texture(sprite, vert.tex_coords) * vert.color; +} diff --git a/in_practice/breakout/shaders/particle.vert b/in_practice/breakout/shaders/particle.vert new file mode 100644 index 0000000..9827a95 --- /dev/null +++ b/in_practice/breakout/shaders/particle.vert @@ -0,0 +1,20 @@ +#version 330 core + +layout (location = 0) in vec4 vertex; + +out vert_t { + vec2 tex_coords; + vec4 color; +} vert; + +uniform mat4 projection; +uniform vec2 offset; +uniform vec4 color; + +void main() +{ + float scale = 10.0; + vert.tex_coords = vertex.zw; + vert.color = color; + gl_Position = projection * vec4((vertex.xy * scale) + offset, 0.0, 1.0); +} diff --git a/in_practice/breakout/shaders/post.frag b/in_practice/breakout/shaders/post.frag new file mode 100644 index 0000000..899b098 --- /dev/null +++ b/in_practice/breakout/shaders/post.frag @@ -0,0 +1,38 @@ +#version 330 core + +in vert_t { + vec2 tex_coords; +} vert; + +out vec4 frag_color; + +uniform sampler2D colorbuffer; +uniform vec2 offsets[9]; +uniform int edge_kernel[9]; +uniform float blur_kernel[9]; + +uniform bool chaos; +uniform bool confuse; +uniform bool shake; + +void main() +{ + frag_color = vec4(0.0); + vec3 sample[9]; + if (chaos || shake) + for (int i = 0; i < 9; i++) + sample[i] = vec3(texture(colorbuffer, vert.tex_coords + offsets[i])); + if (chaos) { + for (int i = 0; i < 9; i++) + frag_color += vec4(sample[i] * edge_kernel[i], 0.0); + frag_color.a = 1.0; + } else if (confuse) { + frag_color = vec4(1.0 - texture(colorbuffer, vert.tex_coords).rgb, 1.0); + } else if (shake) { + for (int i = 0; i < 9; i++) + frag_color += vec4(sample[i] * blur_kernel[i], 0.0); + frag_color.a = 1.0; + } else { + frag_color = texture(colorbuffer, vert.tex_coords); + } +} diff --git a/in_practice/breakout/shaders/post.vert b/in_practice/breakout/shaders/post.vert new file mode 100644 index 0000000..7a8ac4f --- /dev/null +++ b/in_practice/breakout/shaders/post.vert @@ -0,0 +1,33 @@ +#version 330 core + +layout (location = 0) in vec4 vertex; + +out vert_t { + vec2 tex_coords; +} vert; + +uniform bool chaos; +uniform bool confuse; +uniform bool shake; +uniform float time; + +void main() +{ + gl_Position = vec4(vertex.xy, 0.0, 1.0); + vec2 texture = vertex.zw; + if (chaos) { + float strength = 0.3; + vec2 pos = vec2(texture.x + sin(time) * strength, + texture.y + cos(time) * strength); + vert.tex_coords = pos; + } else if (confuse) { + vert.tex_coords = vec2(1.0) - texture; + } else { + vert.tex_coords = texture; + } + if (shake) { + float strength = 0.01; + gl_Position.x += cos(time * 10) * strength; + gl_Position.y += cos(time * 15) * strength; + } +} diff --git a/in_practice/breakout/shaders/text.frag b/in_practice/breakout/shaders/text.frag new file mode 100644 index 0000000..a6fae4b --- /dev/null +++ b/in_practice/breakout/shaders/text.frag @@ -0,0 +1,16 @@ +#version 330 core + +in vert_t { + vec2 tex_coords; +} vert; + +out vec4 frag_color; + +uniform sampler2D glyph; +uniform vec3 text_color; + +void main() +{ + vec4 glyph_color = vec4(1.0, 1.0, 1.0, texture(glyph, vert.tex_coords).r); + frag_color = vec4(text_color, 1.0) * glyph_color; +} diff --git a/in_practice/breakout/shaders/text.vert b/in_practice/breakout/shaders/text.vert new file mode 100644 index 0000000..aa22caa --- /dev/null +++ b/in_practice/breakout/shaders/text.vert @@ -0,0 +1,15 @@ +#version 330 core + +layout(location = 0) in vec4 vertex; + +out vert_t { + vec2 tex_coords; +} vert; + +uniform mat4 projection; + +void main() +{ + gl_Position = projection * vec4(vertex.xy, 0.0, 1.0); + vert.tex_coords = vertex.zw; +} diff --git a/in_practice/breakout/sprite.c b/in_practice/breakout/sprite.c index 07a357f..a455bd2 100644 --- a/in_practice/breakout/sprite.c +++ b/in_practice/breakout/sprite.c @@ -1,9 +1,9 @@ #include "sprite.h" #include "shader.h" -sprite_renderer_t init_renderer(u32 shader) +struct sprite_renderer init_renderer(u32 shader) { - sprite_renderer_t renderer = {0}; + struct sprite_renderer renderer = {0}; renderer.shader = shader; f32 vertices[] = { 0.0f, 0.0f, 0.0f, 0.0f, @@ -28,7 +28,7 @@ sprite_renderer_t init_renderer(u32 shader) return renderer; } -void render_sprite(sprite_renderer_t renderer, u32 texture, v2 pos, v2 size, f32 rotate, v3 color) +void render_sprite(struct sprite_renderer renderer, u32 texture, v2 pos, v2 size, f32 rotate, v3 color) { glUseProgram(renderer.shader); mat model = mat_identity; diff --git a/in_practice/breakout/sprite.h b/in_practice/breakout/sprite.h index 7c07eb9..dc60b0c 100644 --- a/in_practice/breakout/sprite.h +++ b/in_practice/breakout/sprite.h @@ -4,12 +4,12 @@ #include "texture.h" #include "my_math.h" -typedef struct { +struct sprite_renderer { u32 shader; u32 vao; -} sprite_renderer_t; +}; -extern sprite_renderer_t init_renderer(u32 shader); -extern void render_sprite(sprite_renderer_t renderer, u32 texture, v2 pos, v2 size, f32 rotate, v3 color); +struct sprite_renderer init_renderer(u32 shader); +void render_sprite(struct sprite_renderer renderer, u32 texture, v2 pos, v2 size, f32 rotate, v3 color); #endif diff --git a/in_practice/breakout/sprite.o b/in_practice/breakout/sprite.o deleted file mode 100644 index 0a9116f..0000000 Binary files a/in_practice/breakout/sprite.o and /dev/null differ diff --git a/in_practice/breakout/sys.h b/in_practice/breakout/sys.h index ca7aaec..ce7b6c4 100644 --- a/in_practice/breakout/sys.h +++ b/in_practice/breakout/sys.h @@ -1,9 +1,13 @@ #ifndef SYS_H #define SYS_H -extern void info(const char *fmt, ...); -extern void die(const char *fmt, ...); -extern void *read_entire_file(const char *filename); -extern const char *get_bin_dir(void); +#include "types.h" + +void info(const char *fmt, ...); +void die(const char *fmt, ...); +void *read_entire_file(const char *filename); +const char *get_bin_dir(void); +void load_wav(const char *dir, const char *filename, const char *name); +void play_audio(const char *name, i32 loop); #endif diff --git a/in_practice/breakout/sys.o b/in_practice/breakout/sys.o deleted file mode 100644 index 1a7be71..0000000 Binary files a/in_practice/breakout/sys.o and /dev/null differ diff --git a/in_practice/breakout/text.c b/in_practice/breakout/text.c new file mode 100644 index 0000000..994ffb3 --- /dev/null +++ b/in_practice/breakout/text.c @@ -0,0 +1,127 @@ +#include "text.h" +#include "shader.h" +#include "sys.h" +#include +#include +#include FT_FREETYPE_H + +struct text_renderer init_text_renderer(i32 width, i32 height) +{ + struct text_renderer renderer = {0}; + renderer.shader = get_shader("text"); + glUseProgram(renderer.shader); + mat projection = ortho(0.0f, width, height, 0.0f, -1.0f, 1.0f); + uniform_mat(renderer.shader, "projection", projection); + uniform_i32(renderer.shader, "glyph", 0); + glUseProgram(0); + glGenVertexArrays(1, &renderer.vao); + glGenBuffers(1, &renderer.vbo); + glBindVertexArray(renderer.vao); + glBindBuffer(GL_ARRAY_BUFFER, renderer.vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(f32) * 6 * 4, 0, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + return renderer; +} + +void load_font(struct text_renderer *renderer, const char *dir, const char *filename, u32 size) +{ + for (i32 i = 0; i < 128; i++) { + struct text_char *c = renderer->chars + i; + c->texture = 0; + c->size = (v2){0.0f, 0.0f}; + c->bearing = (v2){0.0f, 0.0f}; + c->advance = 0; + } + FT_Library ft; + if (FT_Init_FreeType(&ft)) + die("failed to initialize freetype library"); + FT_Face face; + char path[512] = {0}; + snprintf(path, 512, "%s/%s", (dir ? dir : "."), filename); + if (FT_New_Face(ft, path, 0, &face)) + die("failed to load \"%s\"", path); + FT_Set_Pixel_Sizes(face, 0, size); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + for (u8 c = 0; c < 128; c++) { + if (FT_Load_Char(face, c, FT_LOAD_RENDER)) + die("failed to load char \"%c\"", c); + u32 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_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + renderer->chars[c] = (struct text_char){ + texture, + {face->glyph->bitmap.width, face->glyph->bitmap.rows}, + {face->glyph->bitmap_left, face->glyph->bitmap_top}, + face->glyph->advance.x + }; + glBindTexture(GL_TEXTURE_2D, 0); + } + FT_Done_Face(face); + FT_Done_FreeType(ft); + info("font \"%s\" loaded successfully", path); +} + +v2 get_text_size(struct text_renderer renderer, const char *text, f32 scale) +{ + v2 size = {0}; + for (const char *c = text; *c; c++) { + struct text_char ch = renderer.chars[(u8)*c]; + f32 h = ch.size.y * scale; + if (h > size.y) + size.y = h; + size.x += (ch.advance >> 6) * scale; + } + return size; +} + +void render_text(struct text_renderer renderer, const char *text, v2 pos, f32 scale, v3 color) +{ + glUseProgram(renderer.shader); + uniform_v3(renderer.shader, "text_color", color); + glActiveTexture(GL_TEXTURE0); + glBindVertexArray(renderer.vao); + for (const char *c = text; *c; c++) { + struct text_char ch = renderer.chars[(u8)*c]; + f32 x = pos.x + ch.bearing.x * scale; + f32 y = pos.y + (renderer.chars['H'].bearing.y - ch.bearing.y) * scale; + f32 w = ch.size.x * scale; + f32 h = ch.size.y * scale; + f32 vertices[] = { + x, y + h, 0.0f, 1.0f, + x + w, y, 1.0f, 0.0f, + x, y, 0.0f, 0.0f, + + x, y + h, 0.0f, 1.0f, + x + w, y + h, 1.0f, 1.0f, + x + w, y, 1.0f, 0.0f, + }; + glBindTexture(GL_TEXTURE_2D, ch.texture); + glBindBuffer(GL_ARRAY_BUFFER, renderer.vbo); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindTexture(GL_TEXTURE_2D, 0); + pos.x += (ch.advance >> 6) * scale; + } + glBindVertexArray(0); + glUseProgram(0); +} + +void render_text_centered(struct text_renderer renderer, const char *text, v2 pos, f32 scale, v3 color) +{ + v2 size = get_text_size(renderer, text, scale); + v2 top_left = sub_v2(pos, scale_v2(size, 0.5f)); + render_text(renderer, text, top_left, scale, color); +} diff --git a/in_practice/breakout/text.h b/in_practice/breakout/text.h new file mode 100644 index 0000000..1056498 --- /dev/null +++ b/in_practice/breakout/text.h @@ -0,0 +1,27 @@ +#ifndef TEXT_H +#define TEXT_H + +#include "types.h" +#include "my_math.h" + +struct text_char { + u32 texture; + v2 size; + v2 bearing; + u32 advance; +}; + +struct text_renderer { + struct text_char chars[128]; + u32 shader; + u32 vao; + u32 vbo; +}; + +struct text_renderer init_text_renderer(i32 width, i32 height); +void load_font(struct text_renderer *renderer, const char *dir, const char *filename, u32 size); +v2 get_text_size(struct text_renderer renderer, const char *text, f32 scale); +void render_text(struct text_renderer renderer, const char *text, v2 pos, f32 scale, v3 color); +void render_text_centered(struct text_renderer renderer, const char *text, v2 pos, f32 scale, v3 color); + +#endif diff --git a/in_practice/breakout/texture.c b/in_practice/breakout/texture.c index ac672f3..c44cc4b 100644 --- a/in_practice/breakout/texture.c +++ b/in_practice/breakout/texture.c @@ -27,7 +27,7 @@ gl_texture_t load_texture(const char *dir, const char *filename, const char *nam info("texture with name \"%s\" already exist", name); return texture; } - texture = (gl_texture_t)DEFAULT_TEXTURE; + texture = (gl_texture_t)default_texture; texture.name = name; if (alpha) { texture.internal_format = GL_RGBA; diff --git a/in_practice/breakout/texture.h b/in_practice/breakout/texture.h index 2e9d867..388a100 100644 --- a/in_practice/breakout/texture.h +++ b/in_practice/breakout/texture.h @@ -17,9 +17,9 @@ typedef struct { u32 mag_filter; } gl_texture_t; -#define DEFAULT_TEXTURE {0, 0, 0, 0, GL_RGB, GL_RGB, GL_REPEAT, GL_REPEAT, GL_NEAREST, GL_NEAREST} +#define default_texture {0, 0, 0, 0, GL_RGB, GL_RGB, GL_REPEAT, GL_REPEAT, GL_NEAREST, GL_NEAREST} -#define MAX_TEXTURES 8 +#define MAX_TEXTURES 16 extern gl_texture_t textures[MAX_TEXTURES]; extern void generate_texture(gl_texture_t *texture, u32 width, u32 height, void *data); diff --git a/in_practice/breakout/texture.o b/in_practice/breakout/texture.o deleted file mode 100644 index 6973e36..0000000 Binary files a/in_practice/breakout/texture.o and /dev/null differ diff --git a/in_practice/breakout/textures/particle.png b/in_practice/breakout/textures/particle.png new file mode 100644 index 0000000..bc0f6ba Binary files /dev/null and b/in_practice/breakout/textures/particle.png differ diff --git a/in_practice/breakout/textures/powerup_chaos.png b/in_practice/breakout/textures/powerup_chaos.png new file mode 100644 index 0000000..ef63643 Binary files /dev/null and b/in_practice/breakout/textures/powerup_chaos.png differ diff --git a/in_practice/breakout/textures/powerup_confuse.png b/in_practice/breakout/textures/powerup_confuse.png new file mode 100644 index 0000000..3a35153 Binary files /dev/null and b/in_practice/breakout/textures/powerup_confuse.png differ diff --git a/in_practice/breakout/textures/powerup_increase.png b/in_practice/breakout/textures/powerup_increase.png new file mode 100644 index 0000000..3e16390 Binary files /dev/null and b/in_practice/breakout/textures/powerup_increase.png differ diff --git a/in_practice/breakout/textures/powerup_passthrough.png b/in_practice/breakout/textures/powerup_passthrough.png new file mode 100644 index 0000000..bab3baf Binary files /dev/null and b/in_practice/breakout/textures/powerup_passthrough.png differ diff --git a/in_practice/breakout/textures/powerup_speed.png b/in_practice/breakout/textures/powerup_speed.png new file mode 100644 index 0000000..29fdbff Binary files /dev/null and b/in_practice/breakout/textures/powerup_speed.png differ diff --git a/in_practice/breakout/textures/powerup_sticky.png b/in_practice/breakout/textures/powerup_sticky.png new file mode 100644 index 0000000..80836d6 Binary files /dev/null and b/in_practice/breakout/textures/powerup_sticky.png differ -- cgit v1.2.3-70-g09d2