From 113df851af46d8919f7cdb60f25df45dc38d8418 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Wed, 15 Jan 2025 15:32:54 -0500 Subject: [PATCH] Way better in the structure but still a lot of work to do. However, just by moving variables to better locations, taking things out of loops that don't need to be recalulated, etc. this is already 50% of the CPU/GPU usage as the previous version. --- main.cpp | 25 +++++++-- raycaster.cpp | 138 ++++++++++++++++++++++++-------------------------- raycaster.hpp | 58 ++++++++++----------- 3 files changed, 115 insertions(+), 106 deletions(-) diff --git a/main.cpp b/main.cpp index 680af1a..69d5adf 100644 --- a/main.cpp +++ b/main.cpp @@ -3,19 +3,38 @@ static const int SCREEN_HEIGHT=720; static const int SCREEN_WIDTH=1280; +Matrix MAP{{8,8,8,8,8,8,8,8,8}, + {8,0,2,0,0,0,0,0,8}, + {8,0,7,0,0,5,6,0,8}, + {8,0,0,0,0,0,0,0,8}, + {8,8,0,0,0,0,0,8,8}, + {8,0,0,1,3,4,0,0,8}, + {8,0,0,0,0,0,8,8,8}, + {8,0,0,0,0,0,0,0,8}, + {8,8,8,8,8,8,8,8,8} + }; + int main() { + using KB = sf::Keyboard; + sf::RenderWindow window(sf::VideoMode(SCREEN_WIDTH, SCREEN_HEIGHT), "Zed's Ray Caster Game Thing"); - Raycaster rayview(window); + int TILE_SIZE = RAY_VIEW_HEIGHT / matrix::width(MAP); - using KB = sf::Keyboard; - rayview.load_textures(); + //ZED this should set with a function + float player_x = RAY_VIEW_HEIGHT / 2; + float player_y = RAY_VIEW_HEIGHT / 2; + + Raycaster rayview(window, MAP); + rayview.position_camera(player_x, player_y, TILE_SIZE); double moveSpeed = 0.1; double rotSpeed = 0.1; while(window.isOpen()) { rayview.render(); + // DRAW GUI + window.display(); if(KB::isKeyPressed(KB::W)) { rayview.move_forward(moveSpeed); diff --git a/raycaster.cpp b/raycaster.cpp index 663018c..7e19596 100644 --- a/raycaster.cpp +++ b/raycaster.cpp @@ -1,6 +1,7 @@ #include "raycaster.hpp" using namespace fmt; +using std::make_unique; #define rgba_color(r,g,b,a) (r<<(0*8))|(g<<(1*8))|(b<<(2*8))|(a<<(3*8)) #define gray_color(c) rgba_color(c, c, c, 255) @@ -9,54 +10,14 @@ using namespace fmt; #define uDiv 1 #define vDiv 1 -inline void RGBA_brightness(RGBA& pixel, double distance) { - pixel.color.r /= distance; - pixel.color.g /= distance; - pixel.color.b /= distance; -} - inline size_t pixcoord(int x, int y) { return ((y) * RAY_VIEW_WIDTH) + (x); } -Raycaster::Raycaster(sf::RenderWindow& window) : - $window(window) -{ - $window.setVerticalSyncEnabled(true); - view_texture.create(RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT); - view_sprite.setTexture(view_texture); - view_sprite.setPosition(RAY_VIEW_X, 0); - - pixels = new RGBA[RAY_VIEW_WIDTH * RAY_VIEW_HEIGHT]; - - SPRITE = {{4.0, 3.55, 0, 8}}; - MAP = {{8,8,8,8,8,8,8,8,8}, - {8,0,2,0,0,0,0,0,8}, - {8,0,7,0,0,5,6,0,8}, - {8,0,0,0,0,0,0,0,8}, - {8,8,0,0,0,0,0,8,8}, - {8,0,0,1,3,4,0,0,8}, - {8,0,0,0,0,0,8,8,8}, - {8,0,0,0,0,0,0,0,8}, - {8,8,8,8,8,8,8,8,8} - }; - - TILE_SIZE = RAY_VIEW_HEIGHT / matrix::width(MAP); - - // x and y start position - posX = player_x / TILE_SIZE; - posY = player_y / TILE_SIZE; -} - -void Raycaster::load_image(std::vector& texture, const char *filename) { - sf::Image img; - bool good = img.loadFromFile(filename); - dbc::check(good, format("failed to load {}", filename)); - uint32_t *pixbuf = (uint32_t *)img.getPixelsPtr(); - std::copy_n(pixbuf, texture.size(), texture.begin()); -} - -void Raycaster::load_textures() { +void TexturePack::load_textures() { + // ZED: this needs to determine sprite vs. other textures + // so it can put the texture into the sprite rec immediately + // or....maybe just use SFML's sprite stuff? for(int i = 0; i < NUM_TEXTURES; i++) { texture[i].resize(TEXTURE_WIDTH * TEXTURE_HEIGHT); } @@ -72,18 +33,54 @@ void Raycaster::load_textures() { load_image(texture[8], "assets/portal.png"); } +std::vector& TexturePack::get(size_t num) { + return texture[num]; +} + +void TexturePack::load_image(std::vector& texture, const char *filename) { + sf::Image img; + bool good = img.loadFromFile(filename); + dbc::check(good, format("failed to load {}", filename)); + uint32_t *pixbuf = (uint32_t *)img.getPixelsPtr(); + std::copy_n(pixbuf, texture.size(), texture.begin()); +} + +Sprite &TexturePack::get_sprite(size_t sprite_num) { + return SPRITE[sprite_num]; +} + + + +Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map) : + $window(window), + $map(map) +{ + $window.setVerticalSyncEnabled(true); + view_texture.create(RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT); + view_sprite.setTexture(view_texture); + view_sprite.setPosition(RAY_VIEW_X, 0); + pixels = make_unique(RAY_VIEW_WIDTH * RAY_VIEW_HEIGHT); + textures.load_textures(); +} + +void Raycaster::position_camera(float player_x, float player_y, int tile_size) { + // x and y start position + posX = player_x / tile_size; + posY = player_y / tile_size; +} + void Raycaster::draw_pixel_buffer() { - view_texture.update((uint8_t *)pixels, RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT, 0, 0); + view_texture.update((uint8_t *)pixels.get(), RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT, 0, 0); // BUG: can I do this once and just update it? $window.draw(view_sprite); } void Raycaster::clear() { - std::fill_n((uint32_t *)pixels, RAY_VIEW_WIDTH * RAY_VIEW_HEIGHT, 0); + std::fill_n(pixels.get(), RAY_VIEW_WIDTH * RAY_VIEW_HEIGHT, 0); $window.clear(); } -void Raycaster::cast_rays(Matrix& map) { +void Raycaster::cast_rays() { int w = RAY_VIEW_WIDTH; int h = RAY_VIEW_HEIGHT; double perpWallDist; @@ -141,7 +138,7 @@ void Raycaster::cast_rays(Matrix& map) { side = 1; } - if(map[mapY][mapX] > 0) hit = 1; + if($map[mapY][mapX] > 0) hit = 1; } if(side == 0) { @@ -158,7 +155,7 @@ void Raycaster::cast_rays(Matrix& map) { int drawEnd = lineHeight / 2 + h / 2 + PITCH; if(drawEnd >= h) drawEnd = h - 1; - int texNum = MAP[mapY][mapX] - 1; + auto &texture = textures.get($map[mapY][mapX] - 1); // calculate value of wallX double wallX; // where exactly the wall was hit @@ -184,8 +181,7 @@ void Raycaster::cast_rays(Matrix& map) { for(int y = drawStart; y < drawEnd; y++) { int texY = (int)texPos & (TEXTURE_HEIGHT - 1); texPos += step; - RGBA pixel{.out=texture[texNum][TEXTURE_HEIGHT * texY + texX]}; - RGBA_brightness(pixel, perpWallDist); + RGBA pixel = texture[TEXTURE_HEIGHT * texY + texX]; pixels[pixcoord(x, y)] = pixel; } @@ -198,10 +194,10 @@ void Raycaster::cast_rays(Matrix& map) { for(int i = 0; i < NUM_SPRITES; i++) { spriteOrder[i] = i; // this is just the distance calculation - spriteDistance[i] = ((posX - SPRITE[i].x) * - (posX - SPRITE[i].x) + - (posY - SPRITE[i].y) * - (posY - SPRITE[i].y)); + spriteDistance[i] = ((posX - textures.SPRITE[i].x) * + (posX - textures.SPRITE[i].x) + + (posY - textures.SPRITE[i].y) * + (posY - textures.SPRITE[i].y)); } sort_sprites(spriteOrder, spriteDistance, NUM_SPRITES); @@ -209,11 +205,10 @@ void Raycaster::cast_rays(Matrix& map) { // after sorting the sprites, do the projection for(int i = 0; i < NUM_SPRITES; i++) { int sprite_index = spriteOrder[i]; - Sprite& sprite_rec = SPRITE[sprite_index]; + Sprite& sprite_rec = textures.get_sprite(sprite_index); double spriteX = sprite_rec.x - posX; double spriteY = sprite_rec.y - posY; - int sprite_texture_number = sprite_rec.texture; - auto sprite_texture = texture[sprite_texture_number]; + auto& sprite_texture = textures.get(sprite_rec.texture); //transform sprite with the inverse camera matrix // [ planeX dirX ] -1 [ dirY -dirX ] @@ -261,11 +256,11 @@ void Raycaster::cast_rays(Matrix& map) { int d = (y - vMoveScreen) * 256 - h * 128 + spriteHeight * 128; int texY = ((d * TEXTURE_HEIGHT) / spriteHeight) / 256; //get current color from the texture + // BUG: this crashes sometimes when the math goes out of bounds uint32_t color = sprite_texture[TEXTURE_WIDTH * texY + texX]; // poor person's transparency, get current color from the texture if((color & 0x00FFFFFF) != 0) { - RGBA pixel{.out=color}; - RGBA_brightness(pixel, perpWallDist); + RGBA pixel = color; pixels[pixcoord(stripe, y)] = pixel; } } @@ -277,6 +272,8 @@ void Raycaster::cast_rays(Matrix& map) { void Raycaster::draw_ceiling_floor() { int screenHeight = RAY_VIEW_HEIGHT; int screenWidth = RAY_VIEW_WIDTH; + auto& floorTexture = textures.get(textures.floor); + auto& ceilingTexture = textures.get(textures.ceiling); for(int y = screenHeight / 2 + 1; y < screenHeight; ++y) { // rayDir for leftmost ray (x=0) and rightmost (x = w) @@ -329,30 +326,29 @@ void Raycaster::draw_ceiling_floor() { // floorX cellX to find the texture x/y. How? // FLOOR - color = texture[floorTexture][TEXTURE_WIDTH * ty + tx]; - pixels[pixcoord(x, y)].out = color; + color = floorTexture[TEXTURE_WIDTH * ty + tx]; + pixels[pixcoord(x, y)] = color; // CEILING - color = texture[ceilingTexture][TEXTURE_WIDTH * ty + tx]; - pixels[pixcoord(x, screenHeight - y - 1)].out = color; + color = ceilingTexture[TEXTURE_WIDTH * ty + tx]; + pixels[pixcoord(x, screenHeight - y - 1)] = color; } } } void Raycaster::render() { draw_ceiling_floor(); - cast_rays(MAP); + cast_rays(); draw_pixel_buffer(); - $window.display(); } bool Raycaster::empty_space(int new_x, int new_y) { - dbc::check((size_t)new_x < matrix::width(MAP), - format("x={} too wide={}", new_x, matrix::width(MAP))); - dbc::check((size_t)new_y < matrix::height(MAP), - format("y={} too high={}", new_y, matrix::height(MAP))); + dbc::check((size_t)new_x < matrix::width($map), + format("x={} too wide={}", new_x, matrix::width($map))); + dbc::check((size_t)new_y < matrix::height($map), + format("y={} too high={}", new_y, matrix::height($map))); - return MAP[new_y][new_x] == 0; + return $map[new_y][new_x] == 0; } diff --git a/raycaster.hpp b/raycaster.hpp index acb6538..a00457e 100644 --- a/raycaster.hpp +++ b/raycaster.hpp @@ -10,6 +10,7 @@ #include #include #include "dbc.hpp" +#include using matrix::Matrix; @@ -31,32 +32,24 @@ struct Sprite { #define RAY_VIEW_X (1280 - RAY_VIEW_WIDTH) #define RAY_VIEW_Y 0 -union RGBA { - struct { - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; - } color; +using RGBA = uint32_t; - uint32_t out; -}; - -struct Raycaster { - std::vector SPRITE; +struct TexturePack { std::vector texture[NUM_TEXTURES]; - Matrix MAP; - int PITCH=0; - // I chose fixed textures for this instead - const int floorTexture = 3; - const int ceilingTexture = 6; + std::vector SPRITE{{4.0, 3.55, 0, 8}}; + const int floor = 3; + const int ceiling = 6; - float player_x = RAY_VIEW_HEIGHT / 2; - float player_y = RAY_VIEW_HEIGHT / 2; + void load_textures(); + void load_image(std::vector& texture, const char *filename); + Sprite &get_sprite(size_t sprite_num); + std::vector& get(size_t num); +}; - // x and y start position - double posX; - double posY; +struct Raycaster { + TexturePack textures; + double posX = 0; + double posY = 0; // initial direction vector double dirX = -1; @@ -66,9 +59,12 @@ struct Raycaster { double planeX = 0; double planeY = 0.66; + //ZED allocate this on the heap std::array ZBuffer; - RGBA *pixels = nullptr; + //ZED: USE smart pointer for this + std::unique_ptr pixels = nullptr; + //ZED: heap too? int spriteOrder[NUM_SPRITES]; double spriteDistance[NUM_SPRITES]; @@ -76,25 +72,23 @@ struct Raycaster { sf::Sprite view_sprite; sf::RenderWindow& $window; - int TILE_SIZE; - - Raycaster(sf::RenderWindow& window); + Matrix& $map; + int PITCH=0; - void load_image(std::vector& texture, const char *filename); - void load_textures(); + Raycaster(sf::RenderWindow& window, Matrix &map); void draw_pixel_buffer(); - void clear(); - - void cast_rays(Matrix& map); - + void cast_rays(); void draw_ceiling_floor(); void render(); bool empty_space(int new_x, int new_y); void sort_sprites(int* order, double* dist, int amount); + + // ZED these can be one or two functions void move_forward(double moveSpeed); void move_backward(double moveSpeed); void rotate_right(double rotSpeed); void rotate_left(double rotSpeed); + void position_camera(float player_x, float player_y, int tile_size); };