summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpryazha <pryadeiniv@mail.ru>2025-08-26 10:55:18 +0500
committerpryazha <pryadeiniv@mail.ru>2025-08-26 10:55:18 +0500
commitad04490ef84d7565fbec0fa878a21694ad2d61f0 (patch)
treeaccfa142da0b3f99f957de6c030dc2c76639be53
parent1f93c3ef62af6c71217f06491ca2b859d4065740 (diff)
guess that's all
-rw-r--r--.gitignore9
-rw-r--r--advanced_lighting/6.hdr/hdr.c4
-rw-r--r--in_practice/breakout/Ac437_IBM_VGA_9x16.ttfbin0 -> 26004 bytes
-rw-r--r--in_practice/breakout/audio.c14
-rw-r--r--in_practice/breakout/audio.h18
-rw-r--r--in_practice/breakout/audio/background.wavbin0 -> 21173934 bytes
-rw-r--r--in_practice/breakout/audio/powerup.wavbin0 -> 240614 bytes
-rw-r--r--in_practice/breakout/audio/solid.wavbin0 -> 27164 bytes
-rw-r--r--in_practice/breakout/audio/tile.wavbin0 -> 336440 bytes
-rw-r--r--in_practice/breakout/ball.c17
-rw-r--r--in_practice/breakout/ball.h18
-rwxr-xr-xin_practice/breakout/breakoutbin355952 -> 0 bytes
-rwxr-xr-xin_practice/breakout/build.sh15
-rw-r--r--in_practice/breakout/first.txt3
-rw-r--r--in_practice/breakout/fonts/ibm.ttfbin0 -> 26004 bytes
-rw-r--r--in_practice/breakout/game.c397
-rw-r--r--in_practice/breakout/game.h43
-rw-r--r--in_practice/breakout/game.obin16296 -> 0 bytes
-rw-r--r--in_practice/breakout/input.c15
-rw-r--r--in_practice/breakout/input.h29
-rw-r--r--in_practice/breakout/input.obin4704 -> 0 bytes
-rw-r--r--in_practice/breakout/level.c18
-rw-r--r--in_practice/breakout/level.h15
-rw-r--r--in_practice/breakout/levels/first.txt8
-rw-r--r--in_practice/breakout/levels/second.txt8
-rw-r--r--in_practice/breakout/levels/third.txt3
-rw-r--r--in_practice/breakout/linux.c93
-rw-r--r--in_practice/breakout/linux.obin38976 -> 0 bytes
-rw-r--r--in_practice/breakout/my_math.c61
-rw-r--r--in_practice/breakout/my_math.h13
-rw-r--r--in_practice/breakout/my_math.obin11328 -> 0 bytes
-rw-r--r--in_practice/breakout/object.c10
-rw-r--r--in_practice/breakout/object.h8
-rw-r--r--in_practice/breakout/object.obin4424 -> 0 bytes
-rw-r--r--in_practice/breakout/particle.c104
-rw-r--r--in_practice/breakout/particle.h38
-rw-r--r--in_practice/breakout/post_processor.c109
-rw-r--r--in_practice/breakout/post_processor.h25
-rw-r--r--in_practice/breakout/powerup.c31
-rw-r--r--in_practice/breakout/powerup.h36
-rw-r--r--in_practice/breakout/shader.c19
-rw-r--r--in_practice/breakout/shader.h3
-rw-r--r--in_practice/breakout/shader.obin11912 -> 0 bytes
-rw-r--r--in_practice/breakout/shaders/particle.frag15
-rw-r--r--in_practice/breakout/shaders/particle.vert20
-rw-r--r--in_practice/breakout/shaders/post.frag38
-rw-r--r--in_practice/breakout/shaders/post.vert33
-rw-r--r--in_practice/breakout/shaders/text.frag16
-rw-r--r--in_practice/breakout/shaders/text.vert15
-rw-r--r--in_practice/breakout/sprite.c6
-rw-r--r--in_practice/breakout/sprite.h8
-rw-r--r--in_practice/breakout/sprite.obin10272 -> 0 bytes
-rw-r--r--in_practice/breakout/sys.h12
-rw-r--r--in_practice/breakout/sys.obin9560 -> 0 bytes
-rw-r--r--in_practice/breakout/text.c127
-rw-r--r--in_practice/breakout/text.h27
-rw-r--r--in_practice/breakout/texture.c2
-rw-r--r--in_practice/breakout/texture.h4
-rw-r--r--in_practice/breakout/texture.obin259232 -> 0 bytes
-rw-r--r--in_practice/breakout/textures/particle.pngbin0 -> 18912 bytes
-rw-r--r--in_practice/breakout/textures/powerup_chaos.pngbin0 -> 31586 bytes
-rw-r--r--in_practice/breakout/textures/powerup_confuse.pngbin0 -> 25342 bytes
-rw-r--r--in_practice/breakout/textures/powerup_increase.pngbin0 -> 18972 bytes
-rw-r--r--in_practice/breakout/textures/powerup_passthrough.pngbin0 -> 18356 bytes
-rw-r--r--in_practice/breakout/textures/powerup_speed.pngbin0 -> 21825 bytes
-rw-r--r--in_practice/breakout/textures/powerup_sticky.pngbin0 -> 15379 bytes
66 files changed, 1376 insertions, 131 deletions
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
--- /dev/null
+++ b/in_practice/breakout/Ac437_IBM_VGA_9x16.ttf
Binary files 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 <string.h>
+
+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
--- /dev/null
+++ b/in_practice/breakout/audio/background.wav
Binary files differ
diff --git a/in_practice/breakout/audio/powerup.wav b/in_practice/breakout/audio/powerup.wav
new file mode 100644
index 0000000..bf0a0cd
--- /dev/null
+++ b/in_practice/breakout/audio/powerup.wav
Binary files differ
diff --git a/in_practice/breakout/audio/solid.wav b/in_practice/breakout/audio/solid.wav
new file mode 100644
index 0000000..1fa2fc4
--- /dev/null
+++ b/in_practice/breakout/audio/solid.wav
Binary files differ
diff --git a/in_practice/breakout/audio/tile.wav b/in_practice/breakout/audio/tile.wav
new file mode 100644
index 0000000..3cead3a
--- /dev/null
+++ b/in_practice/breakout/audio/tile.wav
Binary files 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
--- a/in_practice/breakout/breakout
+++ /dev/null
Binary files 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
--- /dev/null
+++ b/in_practice/breakout/fonts/ibm.ttf
Binary files 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 <stdio.h>
#include <math.h>
+#include <stdlib.h>
-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
--- a/in_practice/breakout/game.o
+++ /dev/null
Binary files 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
--- a/in_practice/breakout/input.o
+++ /dev/null
Binary files 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 <ctype.h>
#include <stdio.h>
-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 <SDL3/SDL.h>
#include <GL/glew.h>
-#define DEBUG 1
+#include <stdio.h>
-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
--- a/in_practice/breakout/linux.o
+++ /dev/null
Binary files 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 <math.h>
+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
--- a/in_practice/breakout/my_math.o
+++ /dev/null
Binary files 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
--- a/in_practice/breakout/object.o
+++ /dev/null
Binary files 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 <stdlib.h>
+#include <float.h>
+
+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 <GL/glew.h>
+
+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
--- a/in_practice/breakout/shader.o
+++ /dev/null
Binary files 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
--- a/in_practice/breakout/sprite.o
+++ /dev/null
Binary files 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
--- a/in_practice/breakout/sys.o
+++ /dev/null
Binary files 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 <GL/glew.h>
+#include <ft2build.h>
+#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
--- a/in_practice/breakout/texture.o
+++ /dev/null
Binary files differ
diff --git a/in_practice/breakout/textures/particle.png b/in_practice/breakout/textures/particle.png
new file mode 100644
index 0000000..bc0f6ba
--- /dev/null
+++ b/in_practice/breakout/textures/particle.png
Binary files 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
--- /dev/null
+++ b/in_practice/breakout/textures/powerup_chaos.png
Binary files 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
--- /dev/null
+++ b/in_practice/breakout/textures/powerup_confuse.png
Binary files 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
--- /dev/null
+++ b/in_practice/breakout/textures/powerup_increase.png
Binary files 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
--- /dev/null
+++ b/in_practice/breakout/textures/powerup_passthrough.png
Binary files 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
--- /dev/null
+++ b/in_practice/breakout/textures/powerup_speed.png
Binary files 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
--- /dev/null
+++ b/in_practice/breakout/textures/powerup_sticky.png
Binary files differ