diff options
author | Schark <jordan@schark.online> | 2023-05-11 01:57:35 -0700 |
---|---|---|
committer | Schark <jordan@schark.online> | 2023-05-11 01:57:35 -0700 |
commit | 70d9a26e5e80927f1c3f4b178b17037a7f6311e9 (patch) | |
tree | 8658a40d47998029147c47de5a68f7068b24263c /src/main.c | |
parent | 79173c530d1846082c631dd4fe81c6429fffa5f0 (diff) | |
download | gamedev-70d9a26e5e80927f1c3f4b178b17037a7f6311e9.tar.gz gamedev-70d9a26e5e80927f1c3f4b178b17037a7f6311e9.zip |
Base implementation of raycasting engine
Diffstat (limited to 'src/main.c')
-rw-r--r-- | src/main.c | 209 |
1 files changed, 209 insertions, 0 deletions
@@ -1,9 +1,159 @@ #include <GLFW/glfw3.h> #include <stdio.h> +#include <math.h> + +// TODO: move to implicit map definition with level editor in future +#define MAP_HEIGHT 10 +#define MAP_WIDTH 10 +int world_map[MAP_HEIGHT][MAP_WIDTH] = { + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 0, 0, 0, 0, 2, 2, 0, 1}, + {1, 0, 0, 0, 0, 0, 2, 2, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 3, 3, 0, 0, 4, 4, 0, 1}, + {1, 0, 3, 3, 0, 0, 4, 4, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1} +}; + +// TODO: move player code to dedicated file after complication demands it +// TODO: implement y-shearing to allow players to look vertically +typedef struct { + float pos_x, pos_y; + float dir_x, dir_y; + float plane_x, plane_y; + float move_speed; + float rot_speed; +} Player; + +typedef struct { + unsigned char r, g, b; +} ColorRGB; + +// honest- this function can from chatgpt- cross-reference this with other raycasting guides +void raycast(Player *player) { + int screen_width = 800; + int screen_height = 600; + + for (int x = 0; x < screen_width; ++x) { + // Calculate ray position and direction + double camera_x = 2.0 * x / (double)screen_width - 1; // x-coordinate in camera space + double ray_dir_x = player->dir_x + player->plane_x * camera_x; + double ray_dir_y = player->dir_y + player->plane_y * camera_x; + + // Which box of the map we're in + int map_x = (int)player->pos_x; + int map_y = (int)player->pos_y; + + // Length of ray from one x or y-side to next x or y-side + double delta_dist_x = fabs(1 / ray_dir_x); + double delta_dist_y = fabs(1 / ray_dir_y); + + // Length of ray from current position to next x or y-side + double side_dist_x; + double side_dist_y; + + // Direction to step in x or y-direction (either +1 or -1) + int step_x; + int step_y; + + // Was a wall hit? + int hit = 0; + // Was the wall hit a wall facing north, east-west, or south? + int side; + if (ray_dir_x < 0) { + step_x = -1; + side_dist_x = (player->pos_x - map_x) * delta_dist_x; + } else { + step_x = 1; + side_dist_x = (map_x + 1.0 - player->pos_x) * delta_dist_x; + } + if (ray_dir_y < 0) { + step_y = -1; + side_dist_y = (player->pos_y - map_y) * delta_dist_y; + } else { + step_y = 1; + side_dist_y = (map_y + 1.0 - player->pos_y) * delta_dist_y; + } + + // Perform DDA + while (hit == 0) { + // Jump to next map square + if (side_dist_x < side_dist_y) { + side_dist_x += delta_dist_x; + map_x += step_x; + side = (ray_dir_x > 0) ? 0 : 2; + } else { + side_dist_y += delta_dist_y; + map_y += step_y; + side = 1; + } + // Check if ray has hit a wall + if (world_map[map_x][map_y] > 0) { hit = 1; } + } + + // calculate distance projected on camera direction + double perp_wall_dist; + if (side == 0 || side == 2) { perp_wall_dist = (map_x - player->pos_x + (1 - step_x) / 2) / ray_dir_x; } + else { perp_wall_dist = (map_y - player->pos_y + (1 - step_y) / 2) / ray_dir_y; } + + // calculate height of line on screen + int line_height = (int)(screen_height / perp_wall_dist); + int draw_start = -line_height / 2 + screen_height / 2; + if (draw_start < 0) { draw_start = 0; } + int draw_end = line_height / 2 + screen_height / 2; + if (draw_end >= screen_height) { draw_end = screen_height - 1; } + + + // colors :) + + // TODO: currently, this effects both inside and outside faces + // which creates some awkward coloring issues with the outer walls. + // this may not be an issue in the final release, depending how walls + // are handled, but worth noting now. + ColorRGB color; + if (world_map[map_x][map_y] == 1) color = (ColorRGB){.r = 255, .g = 255, .b = 255}; + else if (world_map[map_x][map_y] == 2) color = (ColorRGB){.r = 255, .g = 0, .b = 0}; + else if (world_map[map_x][map_y] == 3) color = (ColorRGB){.r = 0, .g = 255, .b = 0}; + else if (world_map[map_x][map_y] == 4) color = (ColorRGB){.r = 0, .g = 0, .b = 255}; + else color = (ColorRGB){.r = 0, .g = 0, .b = 0}; + + if (side == 1) { + color.r /= 2; + color.g /= 2; + color.b /= 2; + } else if (side == 2) { + color.r /= 4; + color.g /= 4; + color.b /= 4; + } + + // TODO: recommended to put this in its own function or something at some point, + // as it deals with library calls outside of this function's scope + glBegin(GL_LINES); + glColor3ub(color.r, color.g, color.b); + glVertex2i(x, draw_start); + glVertex2i(x, draw_end); + glEnd(); + } +} int main(int argc, char *argv[]) { GLFWwindow* window; + // init player + Player player; + player.pos_y = 2.0f; + player.pos_x = 2.0f; + player.dir_x = 1.0f; // facing right + player.dir_y = 0.0f; // facing right + player.plane_x = 0.0f; // FOV related + player.plane_y = 0.66f; // FOV related + player.move_speed = 0.05f; + player.rot_speed = 0.1f; + // initiate glfw library if (!glfwInit()){ printf("Failed to initiate GLFW.\n"); @@ -21,14 +171,73 @@ int main(int argc, char *argv[]) { // make window current glfwMakeContextCurrent(window); + // setup orthographic projection camera + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, 800, 600, 0.0, -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + + // TODO: move timers into a header + double last_time = glfwGetTime(); + int nb_frames = 0; + // TODO: render FPS with freetype or something as opposed to updating title + char title[200]; + while (!glfwWindowShouldClose(window)) { + // timing section + double current_time = glfwGetTime(); + nb_frames++; + if (current_time - last_time >= 1.0) { + sprintf(title, "FPS: %d", nb_frames); + glfwSetWindowTitle(window, title); + nb_frames = 0; + last_time += 1.0; + } + + // clear window glClear(GL_COLOR_BUFFER_BIT); + glLoadIdentity(); + + // main raycast function + raycast(&player); + + // movement and camera rotation + // TODO: eventually translate movement from a header + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { + player.pos_x += player.dir_x * player.move_speed; + player.pos_y += player.dir_y * player.move_speed; + } + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { + player.pos_x -= player.dir_x * player.move_speed; + player.pos_y -= player.dir_y * player.move_speed; + } + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { + // this changes where we're casting our rays + double old_dir_x = player.dir_x; + player.dir_x = player.dir_x * cos(-player.rot_speed) - player.dir_y * sin(-player.rot_speed); + player.dir_y = old_dir_x * sin(-player.rot_speed) + player.dir_y * cos(-player.rot_speed); + // this updates perspective + double old_plane_x = player.plane_x; + player.plane_x = player.plane_x * cos(-player.rot_speed) - player.plane_y * sin(-player.rot_speed); + player.plane_y = old_plane_x * sin(-player.rot_speed) + player.plane_y * cos(-player.rot_speed); + } + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { + double old_dir_x = player.dir_x; + player.dir_x = player.dir_x * cos(player.rot_speed) - player.dir_y * sin(player.rot_speed); + player.dir_y = old_dir_x * sin(player.rot_speed) + player.dir_y * cos(player.rot_speed); + double old_plane_x = player.plane_x; + player.plane_x = player.plane_x * cos(player.rot_speed) - player.plane_y * sin(player.rot_speed); + player.plane_y = old_plane_x * sin(player.rot_speed) + player.plane_y * cos(player.rot_speed); + } + + glfwSwapBuffers(window); glfwPollEvents(); } glfwTerminate(); + printf("Terminating program...\n"); return 0; } |