From 6fcb46123c154b3360640c0c0be611fde74ffe57 Mon Sep 17 00:00:00 2001 From: Kirill Petrashin Date: Mon, 29 Jun 2026 19:51:00 +0300 Subject: Implement a raycaster --- Makefile | 4 +- framebuffer.c | 6 ++- main.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++---------- map.c | 6 +-- 4 files changed, 129 insertions(+), 31 deletions(-) diff --git a/Makefile b/Makefile index fde9f38..8df8c2a 100644 --- a/Makefile +++ b/Makefile @@ -4,10 +4,10 @@ CFLAGS=-Wall -Wpedantic -Wextra -Werror -O3 LINKERFLAGS=-lm wafflestone: *.c *.h - $(CC) $(CFLAGS) -o $(EXECUTABLE) framebuffer.c main.c $(LINKERFLAGS) + $(CC) $(CFLAGS) -o $(EXECUTABLE) framebuffer.c map.c main.c $(LINKERFLAGS) debug: *.c *.h - $(CC) -Wall -g -o $(EXECUTABLE) framebuffer.c main.c $(LINKERFLAGS) + $(CC) -Wall -g -o $(EXECUTABLE) framebuffer.c map.c main.c $(LINKERFLAGS) .PHONY: clean clean: diff --git a/framebuffer.c b/framebuffer.c index 32c0f0f..ce48f35 100644 --- a/framebuffer.c +++ b/framebuffer.c @@ -73,7 +73,11 @@ void fb_put(Framebuffer fb, size_t row, size_t col, Pixel pixel) { } void fb_vert_line(Framebuffer fb, size_t x, size_t y1, size_t y2, Pixel pixel) { - assert(y2 >= y1); + if (y2 < y1) { + y2 ^= y1; + y1 ^= y2; + y2 ^= y1; + }; for (size_t y = y1; y <= y2; y++) { fb.fb[y][x] = pixel; diff --git a/main.c b/main.c index 75bc451..fcfe166 100644 --- a/main.c +++ b/main.c @@ -4,49 +4,143 @@ #include #include "framebuffer.h" +#include "map.h" int main(void) { setlocale(LC_ALL,""); term_init(); - //fprintf(stderr, "%zux%zu\n", term_width, term_height); Framebuffer fb = fb_new(term_width, term_height); - fb_fill(fb, P_RED); + fb_clear(fb); - char c; - const float k_x = 0.1; - const float k_t = 0.1; - size_t t = 0; - long text_x = 0, text_y = 0; - while(1) { - fb_fill(fb, P_RED); + double pos_x = 15, pos_y = 15; /* Player pos */ + double dir_x = -1, dir_y = 0; /* Player direction vector */ + double plane_x = 0, plane_y = 1; /* Player camera plane vector, perpendicular to dir vector */ + + Map map = map_default(); - /* Sine wave */ + /* TODO: figure out the clock */ + + while(1) { for (size_t x = 0; x < term_width; x++) { - size_t y = ((sin(k_x * x + k_t * t) + 1.0) / 2 * term_height / 2) + (size_t)(term_height / 4); - fb_vert_line(fb, x, y, term_height - 1, P_GREEN); - } + double cam_x = 2 * x / (double)term_width - 1; + double raydir_x = dir_x + plane_x * cam_x; + double raydir_y = dir_y + plane_y * cam_x; - fb_text_no_bg(fb, text_x, text_y, "testing", C_BLACK); + size_t map_x = (size_t) pos_x; + size_t map_y = (size_t) pos_y; - fb_print(fb); + double sidedist_x, sidedist_y; + + double deltadist_x = (raydir_x == 0) ? 1e30 : fabs(1 / raydir_x); + double deltadist_y = (raydir_y == 0) ? 1e30 : fabs(1 / raydir_y); - /* Handle input */ - if (get_input(c) != 0) { - switch(c) { - case 'q': goto quit; break; - case 'h': text_x -= 1; break; - case 'l': text_x += 1; break; - case 'k': text_y -= 1; break; - case 'j': text_y += 1; break; + int step_x, step_y; + + int hit = 0; + int side; + + if (raydir_x < 0) { + step_x = -1; + sidedist_x = (pos_x - map_x) * deltadist_x; + } else { + step_x = 1; + sidedist_x = (map_x + 1.0 - pos_x) * deltadist_x; } - }; + if (raydir_y < 0) { + step_y = -1; + sidedist_y = (pos_y - map_y) * deltadist_y; + } else { + step_y = 1; + sidedist_y = (map_y + 1.0 - pos_y) * deltadist_y; + } + + /* DDA */ + while (!hit) { + if (sidedist_x < sidedist_y) { + sidedist_x += deltadist_x; + map_x += step_x; + side = 0; + } else { + sidedist_y += deltadist_y; + map_y += step_y; + side = 1; + } + + if (map.tiles[map_y][map_x].ch != '\0') hit = 1; + } + + double dist; + if (side == 0) dist = sidedist_x - deltadist_x; + else dist = sidedist_y - deltadist_y; - t++; + int lineheight = term_height / dist; + + int line_start = -lineheight / 2 + term_height / 2; + if (line_start < 0) line_start = 0; + + int line_end = lineheight / 2 + term_height / 2; + if ((size_t)line_end >= term_height) line_end = term_height - 1; + + Pixel p = map.tiles[map_y][map_x]; + if (side == 1) { + p.fg.r /= 2; + p.fg.g /= 2; + p.fg.b /= 2; + p.bg.r /= 2; + p.bg.g /= 2; + p.bg.b /= 2; + } + + fb_vert_line(fb, x, line_start, line_end, p); + } + + fb_print(fb); + fb_clear(fb); + + char c; + double old_dir_x; + double old_plane_x; + if(get_input(c)) { + switch (c) { + case 'q': goto quit; break; + case 'w': + if(map.tiles[(size_t)(pos_y)][(size_t)(pos_x + dir_x * 0.1)].ch == '\0') pos_x += dir_x * 0.1; + if(map.tiles[(size_t)(pos_y + dir_y * 0.1)][(size_t)(pos_x)].ch == '\0') pos_y += dir_y * 0.1; + break; + case 's': + if(map.tiles[(size_t)(pos_y)][(size_t)(pos_x - dir_x * 0.1)].ch == '\0') pos_x -= dir_x * 0.1; + if(map.tiles[(size_t)(pos_y - dir_y * 0.1)][(size_t)(pos_x)].ch == '\0') pos_y -= dir_y * 0.1; + break; + /* TODO: figure out strafing + case 'a': pos_y -= 1; break; + case 'd': pos_y += 1; break; + */ + case 'j': + old_dir_x = dir_x; + dir_x = dir_x * cos(M_PI / 12) - dir_y * sin(M_PI / 12); + dir_y = old_dir_x * sin(M_PI / 12) + dir_y * cos(M_PI / 12); + + old_plane_x = plane_x; + plane_x = plane_x * cos(M_PI / 12) - plane_y * sin(M_PI / 12); + plane_y = old_plane_x * sin(M_PI / 12) + plane_y * cos(M_PI / 12); + break; + case 'l': + old_dir_x = dir_x; + dir_x = dir_x * cos(-M_PI / 12) - dir_y * sin(-M_PI / 12); + dir_y = old_dir_x * sin(-M_PI / 12) + dir_y * cos(-M_PI / 12); + + old_plane_x = plane_x; + plane_x = plane_x * cos(-M_PI / 12) - plane_y * sin(-M_PI / 12); + plane_y = old_plane_x * sin(-M_PI / 12) + plane_y * cos(-M_PI / 12); + break; + } + } } quit: + map_free(&map); fb_free(&fb); term_cleanup(); } diff --git a/map.c b/map.c index 3d81c00..dcf009d 100644 --- a/map.c +++ b/map.c @@ -10,7 +10,7 @@ Map map_new(size_t width, size_t height) { map.tiles = malloc(sizeof(Pixel *) * height); if (map.tiles == NULL) return map; for (size_t row = 0; row < height; row++) { - map.tiles[row] = malloc(sizeof(Pixel *) * width); + map.tiles[row] = malloc(sizeof(Pixel) * width); if (map.tiles[row] == NULL) { /* FIXME: free prev rows */ map.tiles = NULL; @@ -45,11 +45,11 @@ Map map_default(void) { /* Boundaries */ for (size_t x = 0; x < map.width; x++) { /* Horizontal */ - map.tiles[0][x] = P_MAGENTA; + map.tiles[0][x] = P_RED; map.tiles[map.height - 1][x] = P_RED; } for (size_t y = 0; y < map.height; y++) { /* Vertical */ - map.tiles[y][0] = P_MAGENTA; + map.tiles[y][0] = P_RED; map.tiles[y][map.width - 1] = P_RED; } -- cgit v1.2.3