diff --git a/amt/main.cpp b/amt/main.cpp index d46e888..6ed7227 100644 --- a/amt/main.cpp +++ b/amt/main.cpp @@ -57,6 +57,9 @@ int main() { Stats stats; + // NOTE: Enable this to lock frames at 60 + window.setFramerateLimit(60); + while(window.isOpen()) { auto start = std::chrono::high_resolution_clock::now(); rayview.render(); diff --git a/amt/raycaster.cpp b/amt/raycaster.cpp index 517a546..4658c29 100644 --- a/amt/raycaster.cpp +++ b/amt/raycaster.cpp @@ -2,6 +2,7 @@ #include "amt/texture.hpp" #include "amt/pixel.hpp" #include "constants.hpp" +#include "thread.hpp" #define AMT_LIGHT @@ -11,14 +12,14 @@ using namespace fmt; #ifdef AMT_LIGHT static constexpr auto room_brightness = 0.3f; // increse this to increase the room brightness. Higher value means brighter room. -inline static constexpr amt::RGBA dumb_lighting(amt::RGBA pixel, double distance, double distance_from_center) { +inline static constexpr amt::RGBA dumb_lighting(amt::RGBA pixel, float distance, float distance_from_center) { auto const dim_pixel = pixel * room_brightness; if (distance_from_center >= 0) { - auto const min_brightness = 1. / std::max(distance_from_center, 0.5); // farther away from the center darker it gets - auto const max_brightness = 1.; // brighness should not exceed 1 + auto const min_brightness = 1.f / std::max(distance_from_center, 0.5f); // farther away from the center darker it gets + auto const max_brightness = 1.f; // brighness should not exceed 1 auto const pixel_brightness = std::max(min_brightness, std::min(max_brightness, distance)); - auto const yellow_brightness = float(distance_from_center * 60); + auto const yellow_brightness = float(distance_from_center * 60); amt::RGBA const yellow = amt::HSLA(40, 20, yellow_brightness); auto temp = (pixel / pixel_brightness).blend(yellow); @@ -29,6 +30,7 @@ inline static constexpr amt::RGBA dumb_lighting(amt::RGBA pixel, double distance } #else inline static constexpr amt::RGBA dumb_lighting(amt::RGBA pixel, double distance, double distance_from_center) { + (void)distance_from_center; if(distance < 0.9) return pixel; return pixel / distance; } @@ -45,7 +47,9 @@ Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map, unsigned width, unsi $map(map), spriteOrder(textures.NUM_SPRITES), spriteDistance(textures.NUM_SPRITES), - ZBuffer(width) + ZBuffer(width), + $radius(std::min($height, $width) / 2), + $r_sq($radius * $radius) { $window.setVerticalSyncEnabled(VSYNC); view_sprite.setPosition({0, 0}); @@ -74,9 +78,6 @@ void Raycaster::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 < textures.NUM_SPRITES; i++) { auto& sprite = textures.get_sprite(i); @@ -90,8 +91,10 @@ void Raycaster::sprite_casting() { sort_sprites(spriteOrder, spriteDistance, textures.NUM_SPRITES); + /*for(int i = 0; i < textures.NUM_SPRITES; i++) {*/ // after sorting the sprites, do the projection - for(int i = 0; i < textures.NUM_SPRITES; i++) { + // Be careful about capturing stack variables. + amt::parallel_for<1>(pool, 0, textures.NUM_SPRITES, [this, textureWidth = textures.TEXTURE_WIDTH, textureHeight = textures.TEXTURE_HEIGHT](size_t i){ int sprite_index = spriteOrder[i]; Sprite& sprite_rec = textures.get_sprite(sprite_index); auto& sprite_texture = textures.get_texture(sprite_rec.texture); @@ -139,33 +142,39 @@ void Raycaster::sprite_casting() { // 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 + if (texX < 0) continue; if(transformY > 0 && transformY < ZBuffer[stripe]) { 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 * textureHeight) / spriteHeight) / 256; + if ((size_t)texY >= sprite_texture.rows()) continue; //get current color from the texture - // BUG: this crashes sometimes when the math goes out of bounds - if(texY < 0 || texY >= (int)sprite_texture.rows()) continue; auto color = sprite_texture[texY][texX]; // poor person's transparency, get current color from the texture - pixels[y][stripe] = (color.to_hex() & 0xffffff00) ? color: pixels[y][stripe]; + if (!(color.to_hex() & 0xffffff00)) continue; + auto dist = get_distance_from_center(stripe, y); + pixels[y][stripe] = dumb_lighting(color, d, dist); } } } - } + }); } -void Raycaster::cast_rays() { - double perpWallDist = 0; +float Raycaster::get_distance_from_center(int x, int y) const noexcept { + float cx = $width / 2; + float cy = $height / 2; + auto dx = cx - x; + auto dy = cy - y; + return ($r_sq - dx * dx - dy * dy) / $r_sq; +} - auto const cx = $width / 2; - auto const cy = $height / 2; - double const radius = std::min($height, $width) / 2; - double const r_sq = radius * radius; +void Raycaster::cast_rays() { // WALL CASTING - for(int x = 0; x < $width; x++) { + /*for(int x = 0; x < $width; x++) {*/ + amt::parallel_for<32>(pool, 0, static_cast($width), [this](size_t x){ + double perpWallDist = 0; // calculate ray position and direction double cameraX = 2 * x / double($width) - 1; // x-coord in camera space double rayDirX = dirX + planeX * cameraX; @@ -260,23 +269,24 @@ void Raycaster::cast_rays() { for(int y = drawStart; y < drawEnd; y++) { int texY = (int)texPos & (textures.TEXTURE_HEIGHT - 1); texPos += step; - auto const dx = cx - x; - auto const dy = cy - y; - double const dist = dx * dx + dy * dy; - auto color = dumb_lighting(texture[texY][texX], perpWallDist, (r_sq - dist) / r_sq); + auto dist = get_distance_from_center(x, y); + auto color = dumb_lighting(texture[texY][texX], perpWallDist, dist); pixels[y][x] = color; } // SET THE ZBUFFER FOR THE SPRITE CASTING ZBuffer[x] = perpWallDist; - } + }); } void Raycaster::draw_ceiling_floor() { - const size_t textureWidth = textures.TEXTURE_WIDTH; - const size_t textureHeight = textures.TEXTURE_HEIGHT; - for(int y = $height / 2 + 1; y < $height; ++y) { + /*for(int y = $height / 2 + 1; y < $height; ++y) {*/ + + auto const h = static_cast($height); + amt::parallel_for<32>(pool, h / 2, h, [this, $height=h](size_t y){ + const size_t textureWidth = textures.TEXTURE_WIDTH; + const size_t textureHeight = textures.TEXTURE_HEIGHT; // rayDir for leftmost ray (x=0) and rightmost (x = w) float rayDirX0 = dirX - planeX; float rayDirY0 = dirY - planeY; @@ -327,10 +337,12 @@ void Raycaster::draw_ceiling_floor() { #ifdef AMT_LIGHT // FLOOR - pixels[y][x] = textures.floor[ty][tx] * room_brightness; + auto dist_floor = get_distance_from_center(x, y); + pixels[y][x] = dumb_lighting(textures.floor[ty][tx], p, dist_floor); // CEILING - pixels[$height - y - 1][x] = textures.ceiling[ty][tx] * room_brightness; + auto dist_ceiling = get_distance_from_center(x, $height - y - 1); + pixels[$height - y - 1][x] = dumb_lighting(textures.ceiling[ty][tx], p, dist_ceiling); #else // FLOOR pixels[y][x] = textures.floor[ty][tx]; @@ -340,14 +352,18 @@ void Raycaster::draw_ceiling_floor() { #endif } - } + }); } void Raycaster::render() { draw_ceiling_floor(); + // This wait to prevent data-race + pool.wait(); // Try to remove this to see unbelievable performance cast_rays(); + pool.wait(); // Try to remove this too sprite_casting(); + pool.wait(); draw_pixel_buffer(); } diff --git a/amt/raycaster.hpp b/amt/raycaster.hpp index dd40d8b..b497b0d 100644 --- a/amt/raycaster.hpp +++ b/amt/raycaster.hpp @@ -13,6 +13,7 @@ #include "amt/pixel.hpp" #include "amt/texture.hpp" #include +#include "thread.hpp" using Matrix = amt::Matrix; @@ -43,6 +44,9 @@ struct Raycaster { std::vector spriteOrder; std::vector spriteDistance; std::vector ZBuffer; // width + float $radius; // std::min($height, $width) / 2; + float $r_sq; // = radius * radius; + amt::thread_pool_t pool; Raycaster(sf::RenderWindow& window, Matrix &map, unsigned width, unsigned height); @@ -59,10 +63,11 @@ struct Raycaster { void run(double speed, int dir); void rotate(double speed, int dir); void position_camera(float player_x, float player_y); + float get_distance_from_center(int x, int y) const noexcept; void set_position(int x, int y); inline size_t pixcoord(int x, int y) { return ((y) * $width) + (x); } -}; \ No newline at end of file +}; diff --git a/constants.hpp b/constants.hpp index 68d535a..0d20c1a 100644 --- a/constants.hpp +++ b/constants.hpp @@ -7,6 +7,7 @@ constexpr const int RAY_VIEW_Y=0; constexpr const int SCREEN_HEIGHT=720; constexpr const int SCREEN_WIDTH=1280; constexpr const bool VSYNC=false; +constexpr const int FRAME_LIMIT=30; #ifdef NDEBUG constexpr const bool DEBUG_BUILD=false; #else diff --git a/main.cpp b/main.cpp index d6f567f..4692524 100644 --- a/main.cpp +++ b/main.cpp @@ -57,6 +57,8 @@ int main() { Stats stats; + window.setFramerateLimit(FRAME_LIMIT); + while(window.isOpen()) { auto start = std::chrono::high_resolution_clock::now(); rayview.render(); diff --git a/meson.build b/meson.build index 43ff6c7..d37a42d 100644 --- a/meson.build +++ b/meson.build @@ -63,5 +63,7 @@ executable('amtcaster', [ 'amt/texture.cpp', 'amt/raycaster.cpp', 'amt/main.cpp' - ], override_options: exe_defaults, + ], + cpp_args: ['-std=c++23'], + override_options: exe_defaults, dependencies: dependencies)