diff --git a/Makefile b/Makefile index b5dadbe..e8fc1c7 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,14 @@ reset: build: meson compile -j 10 -C builddir +release_build: + meson --wipe builddir --buildtype release + meson compile -j 10 -C builddir + +debug_build: + meson --wipe builddir --buildtype debug + meson compile -j 10 -C builddir + test: build ./builddir/runtests diff --git a/amt/main.cpp b/amt/main.cpp index 9ee9e15..442bb9b 100644 --- a/amt/main.cpp +++ b/amt/main.cpp @@ -1,4 +1,8 @@ #include "amt/raycaster.hpp" +#include +#include +#include +#include #define RAY_VIEW_WIDTH 960 #define RAY_VIEW_HEIGHT 720 @@ -8,8 +12,6 @@ static const int SCREEN_HEIGHT=720; static const int SCREEN_WIDTH=1280; -using Matrix = amt::Matrix; - Matrix MAP{ {8,8,8,8,8,8,8,8,8}, {8,0,2,0,0,0,0,0,8}, @@ -28,8 +30,8 @@ int main() { sf::RenderWindow window(sf::VideoMode(SCREEN_WIDTH, SCREEN_HEIGHT), "Zed's Ray Caster Game Thing"); //ZED this should set with a function - float player_x = MAP.cols() / 2; - float player_y = MAP.rows() / 2; + float player_x = MAP.rows() / 2; + float player_y = MAP.cols() / 2; Raycaster rayview(window, MAP, RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT); rayview.set_position(RAY_VIEW_X, RAY_VIEW_Y); @@ -38,8 +40,23 @@ int main() { double moveSpeed = 0.1; double rotSpeed = 0.1; + std::size_t const max_count = 100; + std::vector frames(max_count); + std::size_t it = 1; while(window.isOpen()) { + auto start = std::chrono::high_resolution_clock::now(); rayview.render(); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration(end - start); + auto frame = 1 / elapsed.count(); + frames.push_back(frame); + if (it % max_count == 0) { + auto frame = std::accumulate(frames.begin(), frames.end(), 0., std::plus<>{}) / max_count; + std::cout << "Frame: " << frame << '\n'; + frames.clear(); + it = 1; + } + ++it; // DRAW GUI window.display(); diff --git a/amt/matrix.hpp b/amt/matrix.hpp index 6bd67ab..81f9167 100644 --- a/amt/matrix.hpp +++ b/amt/matrix.hpp @@ -1,5 +1,4 @@ -#ifndef AMT_MATRIX_HPP -#define AMT_MATRIX_HPP +#pragma once #include #include @@ -10,6 +9,17 @@ namespace amt { + namespace detail { + [[nodiscard]] constexpr auto cal_index( + std::size_t r, + std::size_t c, + [[maybe_unused]] std::size_t rs, + [[maybe_unused]] std::size_t cs + ) -> std::size_t { + return r * cs + c; + } + } + template struct Matrix { using value_type = T; @@ -28,16 +38,20 @@ namespace amt { struct View { using base_type = std::conditional_t; base_type data; - size_type size; - - constexpr reference operator[](size_type k) noexcept requires (!IsConst) { - assert(k < size && "Out of bound access"); - return data[k]; + size_type r; + size_type rows; + size_type cols; + + constexpr reference operator[](size_type c) noexcept requires (!IsConst) { + assert(c < cols && "Out of bound access"); + auto const index = detail::cal_index(r, c, rows, cols); + return data[index]; } - constexpr const_reference operator[](size_type k) const noexcept { - assert(k < size && "Out of bound access"); - return data[k]; + constexpr const_reference operator[](size_type c) const noexcept { + assert(c < cols && "Out of bound access"); + auto const index = detail::cal_index(r, c, rows, cols); + return data[index]; } }; @@ -83,7 +97,7 @@ namespace amt { } Matrix(std::initializer_list> li) - : m_row(li.size()) + : m_row(li.size()) { for (auto const& row: li) { m_col = std::max(m_col, row.size()); @@ -120,28 +134,26 @@ namespace amt { constexpr const_reverse_iterator rbegin() const noexcept { return std::reverse_iterator(end()); } constexpr const_reverse_iterator rend() const noexcept { return std::reverse_iterator(begin()); } - constexpr auto operator()(size_type r, size_type c) noexcept -> reference { - auto const index = r + c * m_row; // row-major; + constexpr auto operator()(size_type r, size_type c) noexcept -> reference { + auto const index = detail::cal_index(r, c, rows(), cols()); assert(index < size() && "Out of bound access"); return m_data[index]; } constexpr auto operator()(size_type r, size_type c) const noexcept -> const_reference { - auto const index = r + c * m_row; // row-major; + auto const index = detail::cal_index(r, c, rows(), cols()); assert(index < size() && "Out of bound access"); return m_data[index]; } - constexpr auto operator[](size_type c) noexcept -> View { - auto const base = c * m_row; - assert(c < cols() && "Out of bound access"); - return { .data = m_data + base, .size = m_row }; + constexpr auto operator[](size_type r) noexcept -> View { + assert(r < rows() && "Out of bound access"); + return { .data = m_data, .r = r, .rows = m_row, .cols = m_col }; } - constexpr auto operator[](size_type c) const noexcept -> View { - auto const base = c * m_row; - assert(c < cols() && "Out of bound access"); - return { .data = m_data + base, .size = m_row }; + constexpr auto operator[](size_type r) const noexcept -> View { + assert(r < rows() && "Out of bound access"); + return { .data = m_data, .r = r, .rows = m_row, .cols = m_col }; } friend void swap(Matrix& lhs, Matrix& rhs) noexcept { @@ -159,7 +171,7 @@ namespace amt { } // namespace amt - +#if 0 #include namespace std { template @@ -167,7 +179,7 @@ namespace std { constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } - + auto format(amt::Matrix const& m, auto& ctx) const { std::string s = "[\n"; for (auto r = std::size_t{}; r < m.rows(); ++r) { @@ -175,12 +187,11 @@ namespace std { s += std::format("{}, ", m(r, c)); } s += '\n'; - } + } s += "]"; return format_to(ctx.out(), "{}", s); } }; } // namespace std - -#endif // AMT_MATRIX_HPP +#endif diff --git a/amt/pixel.hpp b/amt/pixel.hpp index b38040a..f9178a6 100644 --- a/amt/pixel.hpp +++ b/amt/pixel.hpp @@ -62,8 +62,20 @@ namespace amt { constexpr RGBA() noexcept = default; constexpr RGBA(RGBA const&) noexcept = default; constexpr RGBA(RGBA &&) noexcept = default; - constexpr RGBA& operator=(RGBA const&) noexcept = default; - constexpr RGBA& operator=(RGBA &&) noexcept = default; + RGBA& operator=(RGBA const& other) noexcept { + // HACK: clang was unable to optimize the copy using a single move instruction. + auto& self = *reinterpret_cast(this); + auto color = *reinterpret_cast(&other); + self = color; + return *this; + } + RGBA& operator=(RGBA && other) noexcept { + // HACK: clang was unable to optimize the copy using a single move instruction + auto& self = *reinterpret_cast(this); + auto color = *reinterpret_cast(&other); + self = color; + return *this; + } constexpr ~RGBA() noexcept = default; constexpr RGBA(pixel_t r, pixel_t g, pixel_t b, pixel_t a = 0xff) noexcept @@ -452,6 +464,7 @@ namespace amt { } } + PixelBuf() noexcept = default; PixelBuf(PixelBuf const&) = default; PixelBuf(PixelBuf &&) noexcept = default; PixelBuf& operator=(PixelBuf const&) = default; @@ -476,8 +489,8 @@ namespace amt { constexpr auto rbegin() const noexcept -> const_reverse_iterator { return m_data.rbegin(); } constexpr auto rend() const noexcept -> const_reverse_iterator { return m_data.rend(); } - constexpr auto operator[](size_type r) noexcept { return m_data[r]; } - constexpr auto operator[](size_type r) const noexcept { return m_data[r]; } + constexpr decltype(auto) operator[](size_type r) noexcept { return m_data[r]; } + constexpr decltype(auto) operator[](size_type r) const noexcept { return m_data[r]; } constexpr auto operator()(size_type r, size_type c) noexcept -> reference { return m_data(r, c); } constexpr auto operator()(size_type r, size_type c) const noexcept -> const_reference { return m_data(r, c); } @@ -579,4 +592,4 @@ namespace std { }; } // namespace std -#endif // AMT_PIXEL_HPP +#endif // AMT_PIXEL_HPP \ No newline at end of file diff --git a/amt/raycaster.cpp b/amt/raycaster.cpp index aaddaa2..794b715 100644 --- a/amt/raycaster.cpp +++ b/amt/raycaster.cpp @@ -1,4 +1,5 @@ #include "amt/raycaster.hpp" +#include "pixel.hpp" using namespace fmt; using std::make_unique; @@ -7,16 +8,11 @@ using std::make_unique; #define gray_color(c) rgba_color(c, c, c, 255) -std::vector TexturePack::load_image(const char *filename) { - std::vector texture(TEXTURE_WIDTH * TEXTURE_HEIGHT); +amt::PixelBuf TexturePack::load_image(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()); - - return texture; + return amt::PixelBuf(img.getPixelsPtr(), TEXTURE_HEIGHT, TEXTURE_WIDTH); } void TexturePack::load_textures() { @@ -31,7 +27,7 @@ void TexturePack::load_textures() { images.emplace_back(load_image("assets/portal.png")); } -std::vector& TexturePack::get(size_t num) { +amt::PixelBuf& TexturePack::get(size_t num) { return images[num]; } @@ -43,6 +39,7 @@ Sprite &TexturePack::get_sprite(size_t sprite_num) { Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map, int width, int height) : $width(width), $height(height), + pixels(static_cast(height), static_cast(width)), $window(window), $map(map), spriteOrder(textures.NUM_SPRITES), @@ -53,7 +50,6 @@ Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map, int width, int heigh view_texture.create($width, $height); view_sprite.setTexture(view_texture); view_sprite.setPosition(0, 0); - pixels = make_unique($width * $height); textures.load_textures(); } @@ -68,13 +64,13 @@ void Raycaster::position_camera(float player_x, float player_y) { } void Raycaster::draw_pixel_buffer() { - view_texture.update((uint8_t *)pixels.get(), $width, $height, 0, 0); + view_texture.update(pixels.to_raw_buf(), $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(), $width * $height, 0); + pixels.fill({}); $window.clear(); } @@ -149,16 +145,15 @@ void Raycaster::sprite_casting() { 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[textureWidth * texY + texX]; + auto color = sprite_texture[texY][texX]; // poor person's transparency, get current color from the texture - if((color & 0x00FFFFFF) != 0) { - RGBA pixel = color; - pixels[pixcoord(stripe, y)] = pixel; + if((color.to_hex() & 0xFFFFFF00) != 0) { + pixels[y][stripe] = color; } } } } - } + } } void Raycaster::cast_rays() { @@ -217,7 +212,7 @@ void Raycaster::cast_rays() { side = 1; } - if($map[mapX][mapY] > 0) hit = 1; + if($map[mapY][mapX] > 0) hit = 1; } if(side == 0) { @@ -234,7 +229,7 @@ void Raycaster::cast_rays() { int drawEnd = lineHeight / 2 + $height / 2 + PITCH; if(drawEnd >= $height) drawEnd = $height - 1; - auto &texture = textures.get($map[mapX][mapY] - 1); + auto &texture = textures.get($map[mapY][mapX] - 1); // calculate value of wallX double wallX; // where exactly the wall was hit @@ -260,8 +255,7 @@ void Raycaster::cast_rays() { for(int y = drawStart; y < drawEnd; y++) { int texY = (int)texPos & (textures.TEXTURE_HEIGHT - 1); texPos += step; - RGBA pixel = texture[textures.TEXTURE_HEIGHT * texY + texX]; - pixels[pixcoord(x, y)] = pixel; + pixels[y][x] = texture[texY][texX]; } // SET THE ZBUFFER FOR THE SPRITE CASTING @@ -270,8 +264,8 @@ void Raycaster::cast_rays() { } void Raycaster::draw_ceiling_floor() { - const int textureWidth = textures.TEXTURE_WIDTH; - const int textureHeight = textures.TEXTURE_HEIGHT; + const size_t textureWidth = textures.TEXTURE_WIDTH; + const size_t textureHeight = textures.TEXTURE_HEIGHT; auto& floorTexture = textures.get(textures.floor); auto& ceilingTexture = textures.get(textures.ceiling); @@ -322,19 +316,17 @@ void Raycaster::draw_ceiling_floor() { floorY += floorStepY; // now get the pixel from the texture - uint32_t color; // this uses the previous ty/tx fractional parts of // floorX cellX to find the texture x/y. How? // FLOOR - color = floorTexture[textureWidth * ty + tx]; - pixels[pixcoord(x, y)] = color; + pixels[y][x] = floorTexture[ty][tx]; // CEILING - color = ceilingTexture[textureWidth * ty + tx]; - pixels[pixcoord(x, $height - y - 1)] = color; + pixels[$height - y - 1][x] = ceilingTexture[ty][tx]; } } + } void Raycaster::render() { @@ -350,7 +342,7 @@ bool Raycaster::empty_space(int new_x, int new_y) { dbc::check((size_t)new_y < $map.rows(), format("y={} too high={}", new_y, $map.rows())); - return $map[new_x][new_y] == 0; + return $map[new_y][new_x] == 0; } diff --git a/amt/raycaster.hpp b/amt/raycaster.hpp index 9cfe107..b4c3aa2 100644 --- a/amt/raycaster.hpp +++ b/amt/raycaster.hpp @@ -6,10 +6,11 @@ #include #include #include -#include "amt/matrix.hpp" +#include "matrix.hpp" #include #include #include "dbc.hpp" +#include "pixel.hpp" #include using Matrix = amt::Matrix; @@ -32,15 +33,15 @@ struct TexturePack { int TEXTURE_WIDTH=256; // must be power of two int TEXTURE_HEIGHT=256; // must be power of two - std::vector> images; + std::vector images; std::vector SPRITE{{4.0, 3.55, 8}}; const int floor = 3; const int ceiling = 6; void load_textures(); - std::vector load_image(const char *filename); + amt::PixelBuf load_image(const char *filename); Sprite &get_sprite(size_t sprite_num); - std::vector& get(size_t num); + amt::PixelBuf& get(size_t num); }; struct Raycaster { @@ -61,10 +62,10 @@ struct Raycaster { sf::Sprite view_sprite; //ZED: USE smart pointer for this - std::unique_ptr pixels = nullptr; int $width; int $height; + amt::PixelBuf pixels; sf::RenderWindow& $window; Matrix& $map; std::vector spriteOrder; diff --git a/main.cpp b/main.cpp index 24a60be..faf3e81 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,8 @@ #include "raycaster.hpp" +#include +#include +#include +#include #define RAY_VIEW_WIDTH 960 #define RAY_VIEW_HEIGHT 720 @@ -38,6 +42,7 @@ int main() { while(window.isOpen()) { rayview.render(); + // DRAW GUI window.display(); diff --git a/meson.build b/meson.build index c0e0dae..3ab2807 100644 --- a/meson.build +++ b/meson.build @@ -36,4 +36,5 @@ executable('amtcaster', [ 'amt/raycaster.cpp', 'amt/main.cpp' ], + cpp_args: ['-std=c++23'], dependencies: dependencies)