diff --git a/main.cpp b/main.cpp index 67adc1d..6b952ed 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,10 @@ #include "raycaster.hpp" +#define RAY_VIEW_WIDTH 960 +#define RAY_VIEW_HEIGHT 720 +#define RAY_VIEW_X (1280 - RAY_VIEW_WIDTH) +#define RAY_VIEW_Y 0 + static const int SCREEN_HEIGHT=720; static const int SCREEN_WIDTH=1280; @@ -24,8 +29,8 @@ int main() { float player_x = matrix::width(MAP) / 2; float player_y = matrix::height(MAP) / 2; - Raycaster rayview(window, MAP); - + Raycaster rayview(window, MAP, RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT); + rayview.set_position(RAY_VIEW_X, RAY_VIEW_Y); rayview.position_camera(player_x, player_y); double moveSpeed = 0.1; diff --git a/raycaster.cpp b/raycaster.cpp index 117f327..64dfddc 100644 --- a/raycaster.cpp +++ b/raycaster.cpp @@ -7,40 +7,32 @@ using std::make_unique; #define gray_color(c) rgba_color(c, c, c, 255) -inline size_t pixcoord(int x, int y) { - return ((y) * RAY_VIEW_WIDTH) + (x); -} - -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); - } - - load_image(texture[0], "assets/tile16.png"); - load_image(texture[1], "assets/tile02.png"); - load_image(texture[2], "assets/tile03.png"); - load_image(texture[3], "assets/tile32.png"); - load_image(texture[4], "assets/tile05.png"); - load_image(texture[5], "assets/tile17.png"); - load_image(texture[6], "assets/tile10.png"); - load_image(texture[7], "assets/tile01.png"); - 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) { +std::vector TexturePack::load_image(const char *filename) { + std::vector texture(TEXTURE_WIDTH * TEXTURE_HEIGHT); 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()); + + return texture; +} + +void TexturePack::load_textures() { + images.emplace_back(load_image("assets/tile16.png")); + images.emplace_back(load_image("assets/tile02.png")); + images.emplace_back(load_image("assets/tile03.png")); + images.emplace_back(load_image("assets/tile32.png")); + images.emplace_back(load_image("assets/tile05.png")); + images.emplace_back(load_image("assets/tile17.png")); + images.emplace_back(load_image("assets/tile10.png")); + images.emplace_back(load_image("assets/tile01.png")); + images.emplace_back(load_image("assets/portal.png")); +} + +std::vector& TexturePack::get(size_t num) { + return images[num]; } Sprite &TexturePack::get_sprite(size_t sprite_num) { @@ -49,18 +41,26 @@ Sprite &TexturePack::get_sprite(size_t sprite_num) { -Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map) : +Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map, int width, int height) : + $width(width), $height(height), $window(window), - $map(map) + $map(map), + spriteOrder(textures.NUM_SPRITES), + spriteDistance(textures.NUM_SPRITES), + ZBuffer(width) { $window.setVerticalSyncEnabled(true); - view_texture.create(RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT); + view_texture.create($width, $height); view_sprite.setTexture(view_texture); - view_sprite.setPosition(RAY_VIEW_X, 0); - pixels = make_unique(RAY_VIEW_WIDTH * RAY_VIEW_HEIGHT); + view_sprite.setPosition(0, 0); + pixels = make_unique($width * $height); textures.load_textures(); } +void Raycaster::set_position(int x, int y) { + view_sprite.setPosition(x, y); +} + void Raycaster::position_camera(float player_x, float player_y) { // x and y start position posX = player_x; @@ -68,19 +68,22 @@ void Raycaster::position_camera(float player_x, float player_y) { } void Raycaster::draw_pixel_buffer() { - view_texture.update((uint8_t *)pixels.get(), RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT, 0, 0); + view_texture.update((uint8_t *)pixels.get(), $width, $height, 0, 0); // BUG: can I do this once and just update it? $window.draw(view_sprite); } void Raycaster::clear() { - std::fill_n(pixels.get(), RAY_VIEW_WIDTH * RAY_VIEW_HEIGHT, 0); + std::fill_n(pixels.get(), $width * $height, 0); $window.clear(); } void Raycaster::sprite_casting() { + const int textureWidth = textures.TEXTURE_WIDTH; + const int textureHeight = textures.TEXTURE_HEIGHT; + // sort sprites from far to close - for(int i = 0; i < NUM_SPRITES; i++) { + for(int i = 0; i < textures.NUM_SPRITES; i++) { spriteOrder[i] = i; // this is just the distance calculation spriteDistance[i] = ((posX - textures.SPRITE[i].x) * @@ -89,10 +92,10 @@ void Raycaster::sprite_casting() { (posY - textures.SPRITE[i].y)); } - sort_sprites(spriteOrder, spriteDistance, NUM_SPRITES); + sort_sprites(spriteOrder, spriteDistance, textures.NUM_SPRITES); // after sorting the sprites, do the projection - for(int i = 0; i < NUM_SPRITES; i++) { + for(int i = 0; i < textures.NUM_SPRITES; i++) { int sprite_index = spriteOrder[i]; Sprite& sprite_rec = textures.get_sprite(sprite_index); double spriteX = sprite_rec.x - posX; @@ -135,7 +138,7 @@ void Raycaster::sprite_casting() { //loop through every vertical stripe of the sprite on screen for(int stripe = drawStartX; stripe < drawEndX; stripe++) { - int texX = int(256 * (stripe - (-spriteWidth / 2 + spriteScreenX)) * TEXTURE_WIDTH / spriteWidth) / 256; + int texX = int(256 * (stripe - (-spriteWidth / 2 + spriteScreenX)) * textureWidth / spriteWidth) / 256; // the conditions in the if are: // 1) it's in front of the camera plane so you don't see things behind you // 2) ZBuffer, with perpendicular distance @@ -143,10 +146,10 @@ void Raycaster::sprite_casting() { for(int y = drawStartY; y < drawEndY; y++) { //256 and 128 factors to avoid floats int d = (y - vMoveScreen) * 256 - $height * 128 + spriteHeight * 128; - int texY = ((d * TEXTURE_HEIGHT) / spriteHeight) / 256; + int texY = ((d * textureHeight) / 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]; + uint32_t color = sprite_texture[textureWidth * texY + texX]; // poor person's transparency, get current color from the texture if((color & 0x00FFFFFF) != 0) { RGBA pixel = color; @@ -243,21 +246,21 @@ void Raycaster::cast_rays() { wallX -= floor((wallX)); // x coorindate on the texture - int texX = int(wallX * double(TEXTURE_WIDTH)); - if(side == 0 && rayDirX > 0) texX = TEXTURE_WIDTH - texX - 1; - if(side == 1 && rayDirY < 0) texX = TEXTURE_WIDTH - texX - 1; + int texX = int(wallX * double(textures.TEXTURE_WIDTH)); + if(side == 0 && rayDirX > 0) texX = textures.TEXTURE_WIDTH - texX - 1; + if(side == 1 && rayDirY < 0) texX = textures.TEXTURE_WIDTH - texX - 1; // LODE: an integer-only bresenham or DDA like algorithm could make the texture coordinate stepping faster // How much to increase the texture coordinate per screen pixel - double step = 1.0 * TEXTURE_HEIGHT / lineHeight; + double step = 1.0 * textures.TEXTURE_HEIGHT / lineHeight; // Starting texture coordinate double texPos = (drawStart - PITCH - $height / 2 + lineHeight / 2) * step; for(int y = drawStart; y < drawEnd; y++) { - int texY = (int)texPos & (TEXTURE_HEIGHT - 1); + int texY = (int)texPos & (textures.TEXTURE_HEIGHT - 1); texPos += step; - RGBA pixel = texture[TEXTURE_HEIGHT * texY + texX]; + RGBA pixel = texture[textures.TEXTURE_HEIGHT * texY + texX]; pixels[pixcoord(x, y)] = pixel; } @@ -267,12 +270,13 @@ void Raycaster::cast_rays() { } void Raycaster::draw_ceiling_floor() { - int screenHeight = RAY_VIEW_HEIGHT; - int screenWidth = RAY_VIEW_WIDTH; + const int textureWidth = textures.TEXTURE_WIDTH; + const int textureHeight = textures.TEXTURE_HEIGHT; + auto& floorTexture = textures.get(textures.floor); auto& ceilingTexture = textures.get(textures.ceiling); - for(int y = screenHeight / 2 + 1; y < screenHeight; ++y) { + for(int y = $height / 2 + 1; y < $height; ++y) { // rayDir for leftmost ray (x=0) and rightmost (x = w) float rayDirX0 = dirX - planeX; float rayDirY0 = dirY - planeY; @@ -280,13 +284,13 @@ void Raycaster::draw_ceiling_floor() { float rayDirY1 = dirY + planeY; // current y position compared to the horizon - int p = y - screenHeight / 2; + int p = y - $height / 2; // vertical position of the camera // 0.5 will the camera at the center horizon. For a // different value you need a separate loop for ceiling // and floor since they're no longer symmetrical. - float posZ = 0.5 * screenHeight; + float posZ = 0.5 * $height; // horizontal distance from the camera to the floor for the current row // 0.5 is the z position exactly in the middle between floor and ceiling @@ -295,8 +299,8 @@ void Raycaster::draw_ceiling_floor() { // calculate the real world step vector we have to add for each x (parallel to camera plane) // adding step by step avoids multiplications with a wight in the inner loop - float floorStepX = rowDistance * (rayDirX1 - rayDirX0) / screenWidth; - float floorStepY = rowDistance * (rayDirY1 - rayDirY0) / screenWidth; + float floorStepX = rowDistance * (rayDirX1 - rayDirX0) / $width; + float floorStepY = rowDistance * (rayDirY1 - rayDirY0) / $width; // real world coordinates of the leftmost column. @@ -304,15 +308,15 @@ void Raycaster::draw_ceiling_floor() { float floorX = posX + rowDistance * rayDirX0; float floorY = posY + rowDistance * rayDirY0; - for(int x = 0; x < screenWidth; ++x) { + for(int x = 0; x < $width; ++x) { // the cell coord is simply taken from the int parts of // floorX and floorY. int cellX = int(floorX); int cellY = int(floorY); // get the texture coordinat from the fractional part - int tx = int(TEXTURE_WIDTH * (floorX - cellX)) & (TEXTURE_WIDTH - 1); - int ty = int(TEXTURE_WIDTH * (floorY - cellY)) & (TEXTURE_HEIGHT - 1); + int tx = int(textureWidth * (floorX - cellX)) & (textureWidth - 1); + int ty = int(textureWidth * (floorY - cellY)) & (textureHeight - 1); floorX += floorStepX; floorY += floorStepY; @@ -323,12 +327,12 @@ void Raycaster::draw_ceiling_floor() { // floorX cellX to find the texture x/y. How? // FLOOR - color = floorTexture[TEXTURE_WIDTH * ty + tx]; + color = floorTexture[textureWidth * ty + tx]; pixels[pixcoord(x, y)] = color; // CEILING - color = ceilingTexture[TEXTURE_WIDTH * ty + tx]; - pixels[pixcoord(x, screenHeight - y - 1)] = color; + color = ceilingTexture[textureWidth * ty + tx]; + pixels[pixcoord(x, $height - y - 1)] = color; } } } @@ -350,7 +354,7 @@ bool Raycaster::empty_space(int new_x, int new_y) { } -void Raycaster::sort_sprites(int* order, double* dist, int amount) +void Raycaster::sort_sprites(std::vector& order, std::vector& dist, int amount) { std::vector> sprites(amount); diff --git a/raycaster.hpp b/raycaster.hpp index b63aaad..45a8742 100644 --- a/raycaster.hpp +++ b/raycaster.hpp @@ -14,12 +14,6 @@ using matrix::Matrix; -#define TEXTURE_WIDTH 256 // must be power of two -#define TEXTURE_HEIGHT 256 // must be power of two - -#define NUM_SPRITES 1 -#define NUM_TEXTURES 11 - struct Sprite { double x; double y; @@ -30,28 +24,28 @@ struct Sprite { int vDiv=1; }; -#define RAY_VIEW_WIDTH 960 -#define RAY_VIEW_HEIGHT 720 -#define RAY_VIEW_X (1280 - RAY_VIEW_WIDTH) -#define RAY_VIEW_Y 0 - using RGBA = uint32_t; struct TexturePack { - std::vector texture[NUM_TEXTURES]; + int NUM_SPRITES=1; + int NUM_TEXTURES=11; + int TEXTURE_WIDTH=256; // must be power of two + int TEXTURE_HEIGHT=256; // must be power of two + + std::vector> images; std::vector SPRITE{{4.0, 3.55, 8}}; const int floor = 3; const int ceiling = 6; void load_textures(); - void load_image(std::vector& texture, const char *filename); + std::vector load_image(const char *filename); Sprite &get_sprite(size_t sprite_num); std::vector& get(size_t num); }; struct Raycaster { - int $width=RAY_VIEW_WIDTH; - int $height=RAY_VIEW_HEIGHT; + int PITCH=0; + TexturePack textures; double posX = 0; double posY = 0; @@ -63,36 +57,39 @@ struct Raycaster { // the 2d raycaster version of camera plane double planeX = 0; double planeY = 0.66; + sf::Texture view_texture; + sf::Sprite view_sprite; - //ZED allocate this on the heap - std::array ZBuffer; //ZED: USE smart pointer for this std::unique_ptr pixels = nullptr; - //ZED: heap too? - int spriteOrder[NUM_SPRITES]; - double spriteDistance[NUM_SPRITES]; - - sf::Texture view_texture; - sf::Sprite view_sprite; - + int $width; + int $height; sf::RenderWindow& $window; Matrix& $map; - int PITCH=0; + std::vector spriteOrder; + std::vector spriteDistance; + std::vector ZBuffer; // width - Raycaster(sf::RenderWindow& window, Matrix &map); + Raycaster(sf::RenderWindow& window, Matrix &map, int width, int height); void draw_pixel_buffer(); void clear(); void cast_rays(); - void sprite_casting(); void draw_ceiling_floor(); + void sprite_casting(); + void sort_sprites(std::vector& order, std::vector& dist, int amount); 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 run(double speed, int dir); void rotate(double speed, int dir); void position_camera(float player_x, float player_y); + + void set_position(int x, int y); + inline size_t pixcoord(int x, int y) { + return ((y) * $width) + (x); + } + };