diff --git a/amt/main.cpp b/amt/main.cpp deleted file mode 100644 index 6ed7227..0000000 --- a/amt/main.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "amt/raycaster.hpp" -#include -#include -#include -#include -#include "constants.hpp" -#include "stats.hpp" - -Matrix MAP{ - {1,1,1,1,1,1,1,1,1}, - {1,0,2,0,0,0,0,0,1}, - {1,0,4,0,0,5,2,0,1}, - {1,0,0,0,0,0,0,0,1}, - {1,1,0,0,0,0,0,1,1}, - {1,0,0,1,3,4,0,0,1}, - {1,0,0,0,0,0,1,1,1}, - {1,0,0,0,0,0,0,0,1}, - {1,1,1,1,1,1,1,1,1} -}; - -void draw_gui(sf::RenderWindow &window, sf::Text &text, Stats &stats) { - sf::RectangleShape rect({SCREEN_WIDTH - RAY_VIEW_WIDTH, SCREEN_HEIGHT}); - - rect.setPosition({0,0}); - rect.setFillColor({50, 50, 50}); - window.draw(rect); - - text.setString( - fmt::format("FPS\nmean:{:>8.5}\nsdev: {:>8.5}\nmin: {:>8.5}\nmax: {:>8.5}\ncount:{:<10}\n\nVSync? {}\nDebug? {}\n\nHit R to reset.", - stats.mean(), stats.stddev(), stats.min, stats.max, stats.n, VSYNC, DEBUG_BUILD)); - window.draw(text); -} - -int main() { - sf::RenderWindow window(sf::VideoMode({SCREEN_WIDTH, SCREEN_HEIGHT}), "Zed's Ray Caster Game Thing"); - - sf::Font font{"./assets/text.otf"}; - sf::Text text{font}; - text.setFillColor({255,255,255}); - text.setPosition({10,10}); - - //ZED this should set with a function - 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); - rayview.position_camera(player_x, player_y); - - double moveSpeed = 0.1; - double rotSpeed = 0.1; - - const auto onClose = [&window](const sf::Event::Closed&) - { - window.close(); - }; - - 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(); - auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration(end - start); - stats.sample(1/elapsed.count()); - - draw_gui(window, text, stats); - window.display(); - - if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)) { - rayview.run(moveSpeed, 1); - } else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S)) { - rayview.run(moveSpeed, -1); - } - - if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::D)) { - rayview.rotate(rotSpeed, -1); - } else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::A)) { - rayview.rotate(rotSpeed, 1); - } - - if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::R)) { - stats.reset(); - } - - window.handleEvents(onClose); - } - - return 0; -} diff --git a/amt/matrix.hpp b/amt/matrix.hpp deleted file mode 100644 index 7309b94..0000000 --- a/amt/matrix.hpp +++ /dev/null @@ -1,197 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -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; - using pointer = value_type*; - using const_pointer = value_type const*; - using reference = value_type&; - using const_reference = value_type const&; - using iterator = pointer; - using const_iterator = const_pointer; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using difference_type = std::ptrdiff_t; - using size_type = std::size_t; - - template - struct View { - using base_type = std::conditional_t; - base_type data; - 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 c) const noexcept { - assert(c < cols && "Out of bound access"); - auto const index = detail::cal_index(r, c, rows, cols); - return data[index]; - } - }; - - constexpr Matrix() noexcept = default; - Matrix(Matrix const& other) - : Matrix(other.rows(), other.cols()) - { - std::copy(other.begin(), other.end(), begin()); - } - Matrix& operator=(Matrix const& other) { - if (this == &other) return *this; - auto temp = Matrix(other); - swap(temp, *this); - return *this; - } - constexpr Matrix(Matrix && other) noexcept - : m_data(other.m_data) - , m_row(other.m_row) - , m_col(other.m_col) - { - other.m_data = nullptr; - } - constexpr Matrix& operator=(Matrix && other) noexcept { - if (this == &other) return *this; - swap(*this, other); - return *this; - } - ~Matrix() { - if (m_data) delete[] m_data; - } - - - Matrix(size_type row, size_type col) - : m_data(new value_type[row * col]) - , m_row(row) - , m_col(col) - {} - - Matrix(size_type row, size_type col, value_type def) - : Matrix(row, col) - { - std::fill(begin(), end(), def); - } - - Matrix(std::initializer_list> li) - : m_row(li.size()) - { - for (auto const& row: li) { - m_col = std::max(m_col, row.size()); - } - - auto const size = m_row * m_col; - - if (size == 0) return; - - m_data = new value_type[size]; - std::fill_n(m_data, size, 0); - - for (auto r = 0ul; auto const& row: li) { - for (auto c = 0ul; auto const& col: row) { - this->operator()(r, c++) = col; - } - ++r; - } - } - - constexpr bool empty() const noexcept { return size() == 0; } - constexpr size_type size() const noexcept { return rows() * cols(); } - constexpr size_type rows() const noexcept { return m_row; } - constexpr size_type cols() const noexcept { return m_col; } - constexpr auto data() noexcept -> pointer { return m_data; } - constexpr auto data() const noexcept -> const_pointer { return m_data; } - - constexpr iterator begin() noexcept { return m_data; } - constexpr iterator end() noexcept { return m_data + size(); } - constexpr const_iterator begin() const noexcept { return m_data; } - constexpr const_iterator end() const noexcept { return m_data + size(); } - constexpr reverse_iterator rbegin() noexcept { return std::reverse_iterator(end()); } - constexpr reverse_iterator rend() noexcept { return std::reverse_iterator(begin()); } - 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 = 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 = detail::cal_index(r, c, rows(), cols()); - assert(index < size() && "Out of bound access"); - return m_data[index]; - } - - 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 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 { - using std::swap; - swap(lhs.m_data, rhs.m_data); - swap(lhs.m_row, rhs.m_row); - swap(lhs.m_col, rhs.m_col); - } - - private: - pointer m_data{}; - size_type m_row{}; - size_type m_col{}; - }; - -} // namespace amt - -#if 0 -#include -namespace std { - template - struct formatter> { - 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) { - for (auto c = std::size_t{}; c < m.cols(); ++c) { - s += std::format("{}, ", m(r, c)); - } - s += '\n'; - } - s += "]"; - return format_to(ctx.out(), "{}", s); - } - }; - -} // namespace std -#endif diff --git a/amt/pixel.hpp b/amt/pixel.hpp deleted file mode 100644 index 13bba72..0000000 --- a/amt/pixel.hpp +++ /dev/null @@ -1,746 +0,0 @@ -#ifndef AMT_PIXEL_HPP -#define AMT_PIXEL_HPP - -#include "matrix.hpp" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace amt { - - enum class PixelFormat { - rgba, - abgr, - rgb , - bgr , - ga , // gray scale and alpha - ag , // alpha and gray scale - g // gray scale - }; - - inline static constexpr auto get_pixel_format_from_channel(std::size_t c, bool little_endian = false) -> PixelFormat { - switch (c) { - case 1: return PixelFormat::g; - case 2: return little_endian ? PixelFormat::ag : PixelFormat::ga; - case 3: return little_endian ? PixelFormat::bgr : PixelFormat::rgb; - case 4: return little_endian ? PixelFormat::abgr : PixelFormat::abgr; - } - throw std::runtime_error(std::string("get_pixel_format_from_channel: unknown channel ") + std::to_string(c)); - } - - namespace detail { - static constexpr auto compare_float(float l, float r) noexcept -> bool { - return std::abs(l - r) < std::numeric_limits::epsilon(); - } - } // namespace detail - - enum class BlendMode { - normal, - multiply, - screen, - overlay, - darken, - lighten, - colorDodge, - colorBurn, - hardLight, - softLight, - difference, - exclusion - }; - - struct RGBA { - using pixel_t = std::uint8_t; - using pixels_t = std::uint32_t; - - 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; - constexpr ~RGBA() noexcept = default; - - - // NOTE: Accepts RRGGBBAA - explicit constexpr RGBA(pixels_t color) noexcept - : RGBA((color >> (8 * 3)) & 0xff, (color >> (8 * 2)) & 0xff, (color >> (8 * 1)) & 0xff, (color >> (8 * 0)) & 0xff) - {} - - constexpr RGBA(pixel_t r, pixel_t g, pixel_t b, pixel_t a = 0xff) noexcept - : m_data {r, g, b, a} - {} - - constexpr RGBA(pixel_t color, pixel_t a = 0xff) noexcept - : RGBA(color, color, color, a) - {} - - // NOTE: Returns RRGGBBAA - constexpr auto to_hex() const noexcept -> pixels_t { - auto r = static_cast(this->r()); - auto b = static_cast(this->b()); - auto g = static_cast(this->g()); - auto a = static_cast(this->a()); - return (r << (8 * 3)) | (g << (8 * 2)) | (b << (8 * 1)) | (a << (8 * 0)); - } - - constexpr auto r() const noexcept -> pixel_t { return m_data[0]; } - constexpr auto g() const noexcept -> pixel_t { return m_data[1]; } - constexpr auto b() const noexcept -> pixel_t { return m_data[2]; } - constexpr auto a() const noexcept -> pixel_t { return m_data[3]; } - - constexpr auto r() noexcept -> pixel_t& { return m_data[0]; } - constexpr auto g() noexcept -> pixel_t& { return m_data[1]; } - constexpr auto b() noexcept -> pixel_t& { return m_data[2]; } - constexpr auto a() noexcept -> pixel_t& { return m_data[3]; } - - /** - * @returns the value is between 0 and 1 - */ - constexpr auto brightness() const noexcept -> float { - // 0.299*R + 0.587*G + 0.114*B - auto tr = normalize(r()); - auto tg = normalize(g()); - auto tb = normalize(b()); - return (0.299 * tr + 0.587 * tg + 0.114 * tb); - } - - template - requires std::is_arithmetic_v - constexpr auto operator/(T val) const noexcept { - auto d = static_cast(val); - return RGBA( - static_cast(r() / d), - static_cast(g() / d), - static_cast(b() / d), - a() - ); - } - - template - requires std::is_arithmetic_v - constexpr auto operator*(T val) const noexcept { - auto d = static_cast(val); - return RGBA( - static_cast(r() * d), - static_cast(g() * d), - static_cast(b() * d), - a() - ); - } - private: - static constexpr auto normalize(pixel_t p) noexcept -> float { - return float(p) / 255; - } - - static constexpr auto to_pixel(float p) noexcept -> pixel_t { - return static_cast(p * 255); - } - - template - static constexpr auto blend_helper() noexcept { - constexpr auto mix_helper = [](float s, float b, float a) -> float { - // (1 - αb) x Cs + αb x B(Cb, Cs) - return (1 - a) * s + a * b; - }; - - if constexpr (M == BlendMode::normal) { - return [mix_helper](float bg, float fg, float alpha) { return mix_helper(bg, fg, alpha); }; - } else if constexpr (M == BlendMode::multiply) { - return [mix_helper](float bg, float fg, float alpha) { return mix_helper(bg, bg * fg, alpha); }; - } else if constexpr (M == BlendMode::screen) { - return [mix_helper](float bg, float fg, float alpha) { - // Cb + Cs -(Cb x Cs) - auto bf = bg + fg - (bg * fg); - return mix_helper(bg, bf, alpha); - }; - } else if constexpr (M == BlendMode::overlay) { - return [mix_helper](float bg, float fg, float alpha, auto&& hard_light_fn, auto&& multiply_fn, auto&& screen_fn) { - // HardLight(Cs, Cb) - auto hl = hard_light_fn(bg, fg, alpha, multiply_fn, screen_fn); - return mix_helper(bg, hl, alpha); - }; - } else if constexpr (M == BlendMode::darken) { - return [mix_helper](float bg, float fg, float alpha) { - return mix_helper(bg, std::min(bg, fg), alpha); - }; - } else if constexpr (M == BlendMode::lighten) { - return [mix_helper](float bg, float fg, float alpha) { - return mix_helper(bg, std::max(bg, fg), alpha); - }; - } else if constexpr (M == BlendMode::colorDodge) { - return [mix_helper](float bg, float fg, float alpha) { - constexpr auto fn = [](float b, float s) -> float { - if (b == 0) return 0; - if (s == 255) return 255; - return std::min(1.f, b / (1.f - s)); - }; - auto bf = fn(bg, fg); - return mix_helper(bg, bf, alpha); - }; - } else if constexpr (M == BlendMode::colorBurn) { - return [mix_helper](float bg, float fg, float alpha) { - constexpr auto fn = [](float b, float s) -> float { - if (b == 255) return 255; - if (s == 0) return 0; - return 1.f - std::min(1.f, (1.f - b) / s); - }; - auto bf = fn(bg, fg); - return mix_helper(bg, bf, alpha); - }; - } else if constexpr (M == BlendMode::hardLight) { - return [mix_helper](float bg, float fg, float alpha, auto&& multiply_fn, auto&& screen_fn) { - auto fn = [&multiply_fn, &screen_fn](float b, float s, float a) -> float { - if (s <= 0.5f) { - return multiply_fn(b, 2.f * s, a); - } else { - return screen_fn(b, 2.f * s - 1.f, a); - } - }; - - auto bf = fn(bg, fg, alpha); - return mix_helper(bg, bf, alpha); - }; - } else if constexpr (M == BlendMode::softLight) { - return [mix_helper](float bg, float fg, float alpha) { - constexpr auto fn = [](float b, float s) -> float { - if (s <= 0.5f) { - // B(Cb, Cs) = Cb - (1 - 2 x Cs) x Cb x (1 - Cb) - return b - (1.f - 2.f * s) * b * (1 - b); - } else { - float d{}; - - if (b <= 0.5f) { - // D(Cb) = ((16 * Cb - 12) x Cb + 4) x Cb - d = ((16 * b - 12) * b + 4) * b; - } else { - // D(Cb) = sqrt(Cb) - d = std::sqrt(b); - } - - // B(Cb, Cs) = Cb + (2 x Cs - 1) x (D(Cb) - Cb) - return b + (2 * s - 1) * (d - b); - } - }; - - auto bf = fn(bg, fg); - return mix_helper(bg, bf, alpha); - }; - } else if constexpr (M == BlendMode::difference) { - return [mix_helper](float bg, float fg, float alpha) { - // B(Cb, Cs) = | Cb - Cs | - return mix_helper(bg, (bg > fg ? (bg - fg) : (fg - bg)), alpha); - }; - } else if constexpr (M == BlendMode::exclusion) { - return [mix_helper](float bg, float fg, float alpha) { - constexpr auto fn = [](float b, float s) -> float { - // B(Cb, Cs) = Cb + Cs - 2 x Cb x Cs - return b + s - 2 * b * s; - }; - - auto bf = fn(bg, fg); - return mix_helper(bg, bf, alpha); - }; - } - }; - - public: - template - constexpr auto blend(RGBA color) const noexcept -> RGBA { - auto ab = normalize(a()); - auto as = normalize(color.a()); - // αs x 1 + αb x (1 – αs) - auto alpha = to_pixel(as + ab * (1 - as)); - auto lr = normalize(r()); - auto lg = normalize(g()); - auto lb = normalize(b()); - auto rr = normalize(color.r()); - auto rg = normalize(color.g()); - auto rb = normalize(color.b()); - - auto nr = 0.f; - auto ng = 0.f; - auto nb = 0.f; - - if constexpr (M == BlendMode::normal) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::multiply) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::screen) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::overlay) { - auto fn = blend_helper(); - auto hard_light_fn = blend_helper(); - auto multiply_fn = blend_helper(); - auto screen_fn = blend_helper(); - nr = fn(lr, rr, ab, hard_light_fn, multiply_fn, screen_fn); - ng = fn(lg, rg, ab, hard_light_fn, multiply_fn, screen_fn); - nb = fn(lb, rb, ab, hard_light_fn, multiply_fn, screen_fn); - } else if constexpr (M == BlendMode::darken) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::lighten) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::colorDodge) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::colorBurn) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::hardLight) { - auto fn = blend_helper(); - auto multiply_fn = blend_helper(); - auto screen_fn = blend_helper(); - nr = fn(lr, rr, ab, multiply_fn, screen_fn); - ng = fn(lg, rg, ab, multiply_fn, screen_fn); - nb = fn(lb, rb, ab, multiply_fn, screen_fn); - } else if constexpr (M == BlendMode::softLight) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::difference) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::exclusion) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } - - return RGBA( - to_pixel(nr), - to_pixel(ng), - to_pixel(nb), - alpha - ); - } - - constexpr auto blend(RGBA color, BlendMode mode) const noexcept -> RGBA { - switch (mode) { - case BlendMode::normal: return blend(color); - case BlendMode::multiply: return blend(color); - case BlendMode::screen: return blend(color); - case BlendMode::overlay: return blend(color); - case BlendMode::darken: return blend(color); - case BlendMode::lighten: return blend(color); - case BlendMode::colorDodge: return blend(color); - case BlendMode::colorBurn: return blend(color); - case BlendMode::hardLight: return blend(color); - case BlendMode::softLight: return blend(color); - case BlendMode::difference: return blend(color); - case BlendMode::exclusion: return blend(color); - } - } - private: - pixel_t m_data[4]{}; - }; - - struct HSLA { - using pixel_t = float; - using pixels_t = float[4]; - - // ensures pixel to be in range - template - struct PixelWrapper { - pixel_t& p; - - constexpr PixelWrapper& operator=(float val) noexcept { - p = std::clamp(val, float(min), float(max)); - } - - constexpr operator pixel_t() const noexcept { return p; } - }; - - constexpr HSLA() noexcept = default; - constexpr HSLA(HSLA const&) noexcept = default; - constexpr HSLA(HSLA &&) noexcept = default; - constexpr HSLA& operator=(HSLA const&) noexcept = default; - constexpr HSLA& operator=(HSLA &&) noexcept = default; - constexpr ~HSLA() noexcept = default; - - constexpr HSLA(pixel_t h, pixel_t s, pixel_t l, pixel_t a = 100) noexcept - : m_data({ .hsla = { .h = h, .s = s, .l = l, .a = a } }) - {} - - constexpr HSLA(RGBA color) noexcept { - auto min = std::min({color.r(), color.g(), color.b()}); - auto max = std::max({color.r(), color.g(), color.b()}); - auto c = (max - min) / 255.f; - - auto tr = float(color.r()) / 255; - auto tg = float(color.g()) / 255; - auto tb = float(color.b()) / 255; - auto ta = float(color.a()) / 255; - - float hue = 0; - float s = 0; - auto l = ((max + min) / 2.f) / 255.f; - - if (min == max) { - if (max == color.r()) { - auto seg = (tg - tb) / c; - auto shift = (seg < 0 ? 360.f : 0.f) / 60.f; - hue = seg + shift; - } else if (max == color.g()) { - auto seg = (tb - tr) / c; - auto shift = 120.f / 60.f; - hue = seg + shift; - } else { - auto seg = (tr - tg) / c; - auto shift = 240.f / 60.f; - hue = seg + shift; - } - s = c / (1 - std::abs(2 * l - 1)); - } - - hue = hue * 60.f + 360.f; - auto q = static_cast(static_cast(hue / 360.f)); - hue -= q * 360.f; - - m_data.hsla.h = hue; - m_data.hsla.s = s * 100.f; - m_data.hsla.l = l * 100.f; - m_data.hsla.a = ta * 100.f; - } - - constexpr operator RGBA() const noexcept { - auto ts = s() / 100.f; - auto tl = l() / 100.f; - auto ta = a() / 100.f; - if (s() == 0) return RGBA(to_pixel(tl), to_pixel(ta)); - - auto th = h() / 360.f; - - float const q = tl < 0.5 ? tl * (1 + ts) : tl + ts - tl * ts; - float const p = 2 * tl - q; - - return RGBA( - to_pixel(convert_hue(p, q, th + 1.f / 3)), - to_pixel(convert_hue(p, q, th)), - to_pixel(convert_hue(p, q, th - 1.f / 3)), - to_pixel(ta) - ); - } - - constexpr auto blend(HSLA color, BlendMode mode) const noexcept -> HSLA { - auto lhs = RGBA(*this); - auto rhs = RGBA(color); - return HSLA(lhs.blend(rhs, mode)); - } - - constexpr auto h() const noexcept -> pixel_t { return m_data.hsla.h; } - constexpr auto s() const noexcept -> pixel_t { return m_data.hsla.s; } - constexpr auto l() const noexcept -> pixel_t { return m_data.hsla.l; } - constexpr auto a() const noexcept -> pixel_t { return m_data.hsla.a; } - - constexpr auto h() noexcept -> PixelWrapper<0, 360> { return { m_data.hsla.h }; } - constexpr auto s() noexcept -> PixelWrapper<0, 100> { return { m_data.hsla.s }; } - constexpr auto l() noexcept -> PixelWrapper<0, 100> { return { m_data.hsla.l }; } - constexpr auto a() noexcept -> PixelWrapper<0, 100> { return { m_data.hsla.a }; } - private: - - static constexpr auto to_pixel(float a) noexcept -> RGBA::pixel_t { - return static_cast(a * 255); - } - - static constexpr auto convert_hue(float p, float q, float t) noexcept -> float { - t = t - (t > 1) + (t < 0); - if (t * 6 < 1) return p + (q - p) * 6 * t; - if (t * 2 < 1) return q; - if (t * 3 < 2) return p + (q - p) * (2.f / 3 - t) * 6; - return p; - } - private: - union { - struct { - pixel_t h{}; // hue: 0-360 - pixel_t s{}; // saturation: 0-100% - pixel_t l{}; // lightness: 0-100% - pixel_t a{}; // alpha: 0-100% - } hsla; - pixels_t color; - } m_data{}; - }; - - namespace detail { - template - inline static constexpr auto parse_pixel_helper(RGBA color, std::uint8_t* out_ptr) noexcept { - if constexpr (F == PixelFormat::rgba) { - out_ptr[0] = color.r(); - out_ptr[1] = color.g(); - out_ptr[2] = color.b(); - out_ptr[3] = color.a(); - } else if constexpr (F == PixelFormat::abgr) { - out_ptr[0] = color.a(); - out_ptr[1] = color.b(); - out_ptr[2] = color.g(); - out_ptr[3] = color.r(); - } else if constexpr (F == PixelFormat::rgb) { - out_ptr[0] = color.r(); - out_ptr[1] = color.g(); - out_ptr[2] = color.b(); - } else if constexpr (F == PixelFormat::bgr) { - out_ptr[0] = color.b(); - out_ptr[1] = color.g(); - out_ptr[2] = color.r(); - } else if constexpr (F == PixelFormat::ga) { - out_ptr[0] = color.r(); - out_ptr[1] = color.a(); - } else if constexpr (F == PixelFormat::ag) { - out_ptr[0] = color.a(); - out_ptr[1] = color.r(); - } else { - out_ptr[0] = color.r(); - } - } - - template - inline static constexpr auto parse_pixel_helper(std::uint8_t const* in_ptr) noexcept -> RGBA { - if constexpr (F == PixelFormat::rgba) { - return { - in_ptr[0], - in_ptr[1], - in_ptr[2], - in_ptr[3] - }; - } else if constexpr (F == PixelFormat::abgr) { - return { - in_ptr[3], - in_ptr[2], - in_ptr[1], - in_ptr[0] - }; - } else if constexpr (F == PixelFormat::rgb) { - return { - in_ptr[0], - in_ptr[1], - in_ptr[2], - 0xff - }; - } else if constexpr (F == PixelFormat::bgr) { - return { - in_ptr[2], - in_ptr[1], - in_ptr[0], - 0xff - }; - } else if constexpr (F == PixelFormat::ga) { - return { - in_ptr[0], - in_ptr[1], - }; - } else if constexpr (F == PixelFormat::ag) { - return { - in_ptr[1], - in_ptr[0], - }; - } else { - return { in_ptr[0], 0xff }; - } - } - } // namespace detail - - inline static constexpr auto get_pixel_format_channel(PixelFormat format) noexcept -> std::size_t { - switch (format) { - case PixelFormat::rgba: case PixelFormat::abgr: return 4u; - case PixelFormat::rgb: case PixelFormat::bgr: return 3u; - case PixelFormat::ga: case PixelFormat::ag: return 2u; - case PixelFormat::g: return 1u; - } - assert(false && "unreachable"); - } - - struct PixelBuf { - private: - public: - using value_type = RGBA; - using base_type = Matrix; - using pointer = typename base_type::pointer; - using const_pointer = typename base_type::const_pointer; - using reference = typename base_type::reference; - using const_reference = typename base_type::const_reference; - using iterator = typename base_type::iterator; - using const_iterator = typename base_type::const_iterator; - using reverse_iterator = typename base_type::reverse_iterator; - using const_reverse_iterator = typename base_type::const_reverse_iterator; - using difference_type = typename base_type::difference_type; - using size_type = typename base_type::size_type; - - PixelBuf(size_type r, size_type c) - : m_data(r, c) - {} - PixelBuf(size_type r, size_type c, RGBA color) - : m_data(r, c, color) - {} - - PixelBuf(std::uint8_t const* in, size_type r, size_type c, PixelFormat format = PixelFormat::rgba) - : PixelBuf(r, c) - { - assert(in != nullptr); - - switch (format) { - case PixelFormat::rgba: from_helper(in, data(), size()); break; - case PixelFormat::abgr: from_helper(in, data(), size()); break; - case PixelFormat::rgb: from_helper(in, data(), size()); break; - case PixelFormat::bgr: from_helper(in, data(), size()); break; - case PixelFormat::ga: from_helper(in, data(), size()); break; - case PixelFormat::ag: from_helper(in, data(), size()); break; - case PixelFormat::g: from_helper(in, data(), size()); break; - } - } - - PixelBuf() noexcept = default; - PixelBuf(PixelBuf const&) = default; - PixelBuf(PixelBuf &&) noexcept = default; - PixelBuf& operator=(PixelBuf const&) = default; - PixelBuf& operator=(PixelBuf &&) noexcept = default; - ~PixelBuf() = default; - - constexpr auto size() const noexcept -> size_type { return m_data.size(); } - constexpr auto rows() const noexcept -> size_type { return m_data.rows(); } - constexpr auto cols() const noexcept -> size_type { return m_data.cols(); } - constexpr auto data() noexcept -> pointer { return m_data.data(); } - constexpr auto data() const noexcept -> const_pointer { return m_data.data(); } - auto to_raw_buf() noexcept -> std::uint8_t* { return reinterpret_cast(data()); } - auto to_raw_buf() const noexcept -> std::uint8_t const* { return reinterpret_cast(data()); } - constexpr auto raw_buf_size() const noexcept { return size() * sizeof(RGBA); } - - constexpr auto begin() noexcept -> iterator { return m_data.begin(); } - constexpr auto end() noexcept -> iterator { return m_data.end(); } - constexpr auto begin() const noexcept -> const_iterator { return m_data.begin(); } - constexpr auto end() const noexcept -> const_iterator { return m_data.end(); } - constexpr auto rbegin() noexcept -> reverse_iterator { return m_data.rbegin(); } - constexpr auto rend() noexcept -> reverse_iterator { return m_data.rend(); } - 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 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); } - - constexpr auto fill(RGBA color) noexcept -> void { - std::fill(begin(), end(), color); - } - - constexpr auto copy_to(std::uint8_t* out, PixelFormat format = PixelFormat::rgba) const noexcept { - assert(out != nullptr); - - switch (format) { - case PixelFormat::rgba: copy_to_helper(data(), out, size());return; - case PixelFormat::abgr: copy_to_helper(data(), out, size());return; - case PixelFormat::rgb: copy_to_helper(data(), out, size());return; - case PixelFormat::bgr: copy_to_helper(data(), out, size());return; - case PixelFormat::ga: copy_to_helper(data(), out, size());return; - case PixelFormat::ag: copy_to_helper(data(), out, size());return; - case PixelFormat::g: copy_to_helper(data(), out, size());return; - } - assert(false && "unreachable"); - } - - - private: - template - constexpr auto copy_to_helper(const_pointer in, std::uint8_t* out, size_type size) const noexcept -> void { - constexpr auto channels = get_pixel_format_channel(F); - for (auto i = size_type{}; i < size; ++i) { - detail::parse_pixel_helper(in[i], out + i * channels); - } - } - - template - constexpr auto from_helper(std::uint8_t const* in, pointer out, size_type size) const noexcept -> void { - constexpr auto channels = get_pixel_format_channel(F); - for (auto i = size_type{}; i < size; ++i) { - out[i] = detail::parse_pixel_helper(in + i * channels); - } - } - - private: - base_type m_data; - }; - - - -} // namespace amt - -#include -namespace std { - template <> - struct formatter { - constexpr auto parse(format_parse_context& ctx) { - return ctx.begin(); - } - - auto format(amt::RGBA const& color, auto& ctx) const { - return format_to(ctx.out(), "rgba({}, {}, {}, {})", color.r(), color.g(), color.b(), color.a()); - } - }; - - template <> - struct formatter { - constexpr auto parse(format_parse_context& ctx) { - return ctx.begin(); - } - - auto format(amt::HSLA const& color, auto& ctx) const { - return format_to(ctx.out(), "hsla({:.1f}deg, {:.1f}%, {:.1f}%, {:.1f}%)", color.h(), color.s(), color.l(), color.a()); - } - }; - - template <> - struct formatter { - bool hsla = false; - - constexpr auto parse(format_parse_context& ctx) { - auto it = ctx.begin(); - while (it != ctx.end() && *it != '}') { - if (*it == 'h') hsla = true; - ++it; - } - return it; - } - - auto format(amt::PixelBuf const& buf, auto& ctx) const { - std::string s = "[\n"; - for (auto r = std::size_t{}; r < buf.rows(); ++r) { - for (auto c = std::size_t{}; c < buf.cols(); ++c) { - auto color = buf(r, c); - if (hsla) s += std::format("{}, ", amt::HSLA(color)); - else s += std::format("{}, ", color); - } - s += '\n'; - } - s += "]"; - return format_to(ctx.out(), "{}", s); - } - }; -} // namespace std - -#endif // AMT_PIXEL_HPP diff --git a/amt/raycaster.cpp b/amt/raycaster.cpp deleted file mode 100644 index c440355..0000000 --- a/amt/raycaster.cpp +++ /dev/null @@ -1,417 +0,0 @@ -#include "amt/raycaster.hpp" -#include "amt/texture.hpp" -#include "amt/pixel.hpp" -#include "constants.hpp" -#include "thread.hpp" - -#define AMT_LIGHT - -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, float distance, float distance_from_center) { - auto const dim_pixel = pixel * room_brightness; - if (distance_from_center >= 0) { - 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); - amt::RGBA const yellow = amt::HSLA(40, 20, yellow_brightness); - - auto temp = (pixel / pixel_brightness).blend(yellow); - return temp.brightness() < 0.1f ? dim_pixel : temp; - } else { - return dim_pixel; - } -} -#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; -} -#endif - - -Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map, unsigned width, unsigned height) : - view_texture(sf::Vector2u{width, height}), - view_sprite(view_texture), - $width(static_cast(width)), - $height(static_cast(height)), - pixels(height, width), - $window(window), - $map(map), - spriteOrder(textures.NUM_SPRITES), - spriteDistance(textures.NUM_SPRITES), - ZBuffer(width), - $radius(std::min($height, $width) / 2), - $r_sq($radius * $radius) -{ - $window.setVerticalSyncEnabled(VSYNC); - view_sprite.setPosition({0, 0}); - textures.load_textures(); -} - -void Raycaster::set_position(int x, int y) { - view_sprite.setPosition({(float)x, (float)y}); -} - -void Raycaster::position_camera(float player_x, float player_y) { - // x and y start position - posX = player_x; - posY = player_y; -} - -void Raycaster::draw_pixel_buffer() { - view_texture.update(pixels.to_raw_buf(), {(unsigned int)$width, (unsigned int)$height}, {0, 0}); - $window.draw(view_sprite); -} - -void Raycaster::clear() { - pixels.fill({}); - $window.clear(); -} - -void Raycaster::sprite_casting() { - // sort sprites from far to close - for(int i = 0; i < textures.NUM_SPRITES; i++) { - auto& sprite = textures.get_sprite(i); - spriteOrder[i] = i; - // this is just the distance calculation - spriteDistance[i] = ((posX - sprite.x) * - (posX - sprite.x) + - (posY - sprite.y) * - (posY - sprite.y)); - } - - sort_sprites(spriteOrder, spriteDistance, textures.NUM_SPRITES); - - /*for(int i = 0; i < textures.NUM_SPRITES; i++) {*/ - // after sorting the sprites, do the projection - // 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); - - double spriteX = sprite_rec.x - posX; - double spriteY = sprite_rec.y - posY; - - //transform sprite with the inverse camera matrix - // [ planeX dirX ] -1 [ dirY -dirX ] - // [ ] = 1/(planeX*dirY-dirX*planeY) * [ ] - // [ planeY dirY ] [ -planeY planeX ] - - double invDet = 1.0 / (planeX * dirY - dirX * planeY); // required for correct matrix multiplication - - double transformX = invDet * (dirY * spriteX - dirX * spriteY); - //this is actually the depth inside the screen, that what Z is in 3D, the distance of sprite to player, matching sqrt(spriteDistance[i]) - - double transformY = invDet * (-planeY * spriteX + planeX * spriteY); - - int spriteScreenX = int(($width / 2) * (1 + transformX / transformY)); - - int vMoveScreen = int(sprite_rec.elevation * -1 / transformY); - - // calculate the height of the sprite on screen - //using "transformY" instead of the real distance prevents fisheye - int spriteHeight = abs(int($height / transformY)) / sprite_rec.vDiv; - - //calculate lowest and highest pixel to fill in current stripe - int drawStartY = -spriteHeight / 2 + $height / 2 + vMoveScreen; - if(drawStartY < 0) drawStartY = 0; - int drawEndY = spriteHeight / 2 + $height / 2 + vMoveScreen; - if(drawEndY >= $height) drawEndY = $height - 1; - - // calculate width the the sprite - // same as height of sprite, given that it's square - int spriteWidth = abs(int($height / transformY)) / sprite_rec.uDiv; - int drawStartX = -spriteWidth / 2 + spriteScreenX; - if(drawStartX < 0) drawStartX = 0; - int drawEndX = spriteWidth / 2 + spriteScreenX; - if(drawEndX > $width) drawEndX = $width; - - //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)) * 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 - 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 - auto color = sprite_texture[texY][texX]; - // poor person's transparency, get current color from the texture - if (!(color.to_hex() & 0xffffff00)) continue; - auto dist = get_distance_from_center(stripe, y); - pixels[y][stripe] = dumb_lighting(color, d, dist); - } - } - } - }); -} - -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; -} - -void Raycaster::cast_rays() { - - // WALL CASTING - /*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; - double rayDirY = dirY + planeY * cameraX; - - // which box of the map we're in - int mapX = int(posX); - int mapY = int(posY); - - // length of ray from current pos to next x or y-side - double sideDistX; - double sideDistY; - - // length of ray from one x or y-side to next x or y-side - double deltaDistX = std::abs(1.0 / rayDirX); - double deltaDistY = std::abs(1.0 / rayDirY); - - int stepX = 0; - int stepY = 0; - int hit = 0; - int side = 0; - - // calculate step and initial sideDist - if(rayDirX < 0) { - stepX = -1; - sideDistX = (posX - mapX) * deltaDistX; - } else { - stepX = 1; - sideDistX = (mapX + 1.0 - posX) * deltaDistX; - } - - if(rayDirY < 0) { - stepY = -1; - sideDistY = (posY - mapY) * deltaDistY; - } else { - stepY = 1; - sideDistY = (mapY + 1.0 - posY) * deltaDistY; - } - - // perform DDA - while(hit == 0) { - if(sideDistX < sideDistY) { - sideDistX += deltaDistX; - mapX += stepX; - side = 0; - } else { - sideDistY += deltaDistY; - mapY += stepY; - side = 1; - } - - if($map[mapY][mapX] > 0) hit = 1; - } - - if(side == 0) { - perpWallDist = (sideDistX - deltaDistX); - } else { - perpWallDist = (sideDistY - deltaDistY); - } - - int lineHeight = int($height / perpWallDist); - - int drawStart = -lineHeight / 2 + $height / 2 + PITCH; - if(drawStart < 0) drawStart = 0; - - int drawEnd = lineHeight / 2 + $height / 2 + PITCH; - if(drawEnd >= $height) drawEnd = $height - 1; - - auto &texture = textures.get_texture($map[mapY][mapX] - 1); - - // calculate value of wallX - double wallX; // where exactly the wall was hit - if(side == 0) { - wallX = posY + perpWallDist * rayDirY; - } else { - wallX = posX + perpWallDist * rayDirX; - } - wallX -= floor((wallX)); - - // x coorindate on the texture - 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 * 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 & (textures.TEXTURE_HEIGHT - 1); - texPos += step; - 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() { - - /*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; - float rayDirX1 = dirX + planeX; - float rayDirY1 = dirY + planeY; - - // current y position compared to the horizon - 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 * $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 - // See NOTE in Lode's code for more. - float rowDistance = posZ / p; - - // 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) / $width; - float floorStepY = rowDistance * (rayDirY1 - rayDirY0) / $width; - - - // real world coordinates of the leftmost column. - // This will be updated as we step to the right - float floorX = posX + rowDistance * rayDirX0; - float floorY = posY + rowDistance * rayDirY0; - - 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(textureWidth * (floorX - cellX)) & (textureWidth - 1); - int ty = int(textureWidth * (floorY - cellY)) & (textureHeight - 1); - - floorX += floorStepX; - floorY += floorStepY; - - // now get the pixel from the texture - // this uses the previous ty/tx fractional parts of - // floorX cellX to find the texture x/y. How? - - #ifdef AMT_LIGHT - // FLOOR - auto dist_floor = get_distance_from_center(x, y); - pixels[y][x] = dumb_lighting(textures.floor[ty][tx], p, dist_floor); - - // CEILING - 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]; - - // CEILING - pixels[$height - y - 1][x] = textures.ceiling[ty][tx]; - #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(); -} - -bool Raycaster::empty_space(int new_x, int new_y) { - dbc::check((size_t)new_x < $map.cols(), - format("x={} too wide={}", new_x, $map.cols())); - dbc::check((size_t)new_y < $map.rows(), - format("y={} too high={}", new_y, $map.rows())); - - return $map[new_y][new_x] == 0; -} - - -void Raycaster::sort_sprites(std::vector& order, std::vector& dist, int amount) -{ - std::vector> sprites(amount); - - for(int i = 0; i < amount; i++) { - sprites[i].first = dist[i]; - sprites[i].second = order[i]; - } - - std::sort(sprites.begin(), sprites.end()); - - // restore in reverse order - for(int i = 0; i < amount; i++) { - dist[i] = sprites[amount - i - 1].first; - order[i] = sprites[amount - i - 1].second; - } -} - -void Raycaster::run(double speed, int dir) { - double speed_and_dir = speed * dir; - if(empty_space(int(posX + dirX * speed_and_dir), int(posY))) { - posX += dirX * speed_and_dir; - } - - if(empty_space(int(posX), int(posY + dirY * speed_and_dir))) { - posY += dirY * speed_and_dir; - } -} - -void Raycaster::rotate(double speed, int dir) { - double speed_and_dir = speed * dir; - double oldDirX = dirX; - dirX = dirX * cos(speed_and_dir) - dirY * sin(speed_and_dir); - dirY = oldDirX * sin(speed_and_dir) + dirY * cos(speed_and_dir); - - double oldPlaneX = planeX; - planeX = planeX * cos(speed_and_dir) - planeY * sin(speed_and_dir); - planeY = oldPlaneX * sin(speed_and_dir) + planeY * cos(speed_and_dir); -} diff --git a/amt/raycaster.hpp b/amt/raycaster.hpp deleted file mode 100644 index b497b0d..0000000 --- a/amt/raycaster.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include "matrix.hpp" -#include -#include -#include "dbc.hpp" -#include "amt/pixel.hpp" -#include "amt/texture.hpp" -#include -#include "thread.hpp" - -using Matrix = amt::Matrix; - -struct Raycaster { - int PITCH=0; - - TexturePack textures; - double posX = 0; - double posY = 0; - - // initial direction vector - double dirX = -1; - double dirY = 0; - - // the 2d raycaster version of camera plane - double planeX = 0; - double planeY = 0.66; - sf::Texture view_texture; - sf::Sprite view_sprite; - - //ZED: USE smart pointer for this - - int $width; - int $height; - amt::PixelBuf pixels; - sf::RenderWindow& $window; - Matrix& $map; - 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); - - void draw_pixel_buffer(); - void clear(); - void cast_rays(); - 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 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); - } - -}; diff --git a/amt/texture.cpp b/amt/texture.cpp deleted file mode 100644 index 2fe78cf..0000000 --- a/amt/texture.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include "dbc.hpp" -#include -#include "config.hpp" -#include "amt/texture.hpp" - -Image TexturePack::load_image(std::string filename) { - sf::Image img; - bool good = img.loadFromFile(filename); - dbc::check(good, format("failed to load {}", filename)); - return amt::PixelBuf(img.getPixelsPtr(), TEXTURE_HEIGHT, TEXTURE_WIDTH); -} - -void TexturePack::load_textures() { - Config assets("assets/config.json"); - for(string tile_path : assets["textures"]) { - images.emplace_back(load_image(tile_path)); - } - - for(string tile_path : assets["sprites"]) { - images.emplace_back(load_image(tile_path)); - } - - floor = load_image(assets["floor"]); - ceiling = load_image(assets["ceiling"]); -} - -Image& TexturePack::get_texture(size_t num) { - return images[num]; -} - -Sprite &TexturePack::get_sprite(size_t sprite_num) { - return sprites[sprite_num]; -} diff --git a/amt/texture.hpp b/amt/texture.hpp deleted file mode 100644 index 179fb36..0000000 --- a/amt/texture.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include -#include -#include -#include "amt/pixel.hpp" - -struct Sprite { - double x; - double y; - int texture; - // ZED: this should be a separate transform parameter - double elevation=0; - int uDiv=1; - int vDiv=1; -}; - -using Image = amt::PixelBuf; - -struct TexturePack { - int NUM_SPRITES=1; - static const int TEXTURE_WIDTH=256; // must be power of two - static const int TEXTURE_HEIGHT=256; // must be power of two - - std::vector images; - std::vector sprites{{4.0, 3.55, 6}}; - Image floor; - Image ceiling; - - void load_textures(); - amt::PixelBuf load_image(std::string filename); - Sprite& get_sprite(size_t sprite_num); - Image& get_texture(size_t num); -}; diff --git a/amt/thread.hpp b/amt/thread.hpp deleted file mode 100644 index 841199b..0000000 --- a/amt/thread.hpp +++ /dev/null @@ -1,253 +0,0 @@ -#ifndef AMT_THREAD_HPP -#define AMT_THREAD_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace amt { - - // NOTE: Could implement lock-free queue. - template - struct Queue { - using base_type = std::deque; - using value_type = typename base_type::value_type; - using pointer = typename base_type::pointer; - using const_pointer = typename base_type::const_pointer; - using reference = typename base_type::reference; - using const_reference = typename base_type::const_reference; - using iterator = typename base_type::iterator; - using const_iterator = typename base_type::const_iterator; - using reverse_iterator = typename base_type::reverse_iterator; - using const_reverse_iterator = typename base_type::const_reverse_iterator; - using difference_type = typename base_type::difference_type; - using size_type = typename base_type::size_type; - - constexpr Queue() noexcept = default; - constexpr Queue(Queue const&) noexcept = delete; - constexpr Queue(Queue &&) noexcept = default; - constexpr Queue& operator=(Queue const&) noexcept = delete; - constexpr Queue& operator=(Queue &&) noexcept = default; - constexpr ~Queue() noexcept = default; - - template - requires std::same_as, value_type> - void push(U&& u) { - std::lock_guard m(m_mutex); - m_data.push_back(std::forward(u)); - } - - template - void emplace(Args&&... args) { - std::lock_guard m(m_mutex); - m_data.emplace_back(std::forward(args)...); - } - - std::optional pop() { - std::lock_guard m(m_mutex); - if (empty_unsafe()) return std::nullopt; - auto el = std::move(m_data.front()); - m_data.pop_front(); - return std::move(el); - } - - auto size() const noexcept -> size_type { - std::lock_guard m(m_mutex); - return m_data.size(); - } - auto empty() const noexcept -> bool { - std::lock_guard m(m_mutex); - return m_data.empty(); - } - constexpr auto size_unsafe() const noexcept -> size_type { return m_data.size(); } - constexpr auto empty_unsafe() const noexcept -> bool { return m_data.empty(); } - - private: - base_type m_data; - mutable std::mutex m_mutex; - }; - - template - struct ThreadPool; - - template - struct Worker { - using parent_t = ThreadPool*; - using work_t = Fn; - using size_type = std::size_t; - constexpr Worker() noexcept = default; - constexpr Worker(Worker const&) noexcept = default; - constexpr Worker(Worker &&) noexcept = default; - constexpr Worker& operator=(Worker const&) noexcept = default; - constexpr Worker& operator=(Worker &&) noexcept = default; - ~Worker() { - stop(); - } - - void start(parent_t pool, size_type id) { - assert((m_running.load(std::memory_order::acquire) == false) && "Thread is already running"); - m_running.store(true); - m_parent.store(pool); - m_id = id; - m_thread = std::thread([this]() { - while (m_running.load(std::memory_order::relaxed)) { - std::unique_lock lk(m_mutex); - m_cv.wait(lk, [this] { - return !m_queue.empty_unsafe() || !m_running.load(std::memory_order::relaxed); - }); - auto item = pop_task(); - if (!item) { - item = try_steal(); - if (!item) continue; - } - - process_work(std::move(*item)); - } - }); - } - - void process_work(work_t&& work) const noexcept { - std::invoke(std::move(work)); - auto ptr = m_parent.load(); - if (ptr) ptr->task_completed(); - } - - void stop() { - if (!m_running.load()) return; - { - std::lock_guard lock(m_mutex); - m_running.store(false); - } - m_cv.notify_all(); - m_thread.join(); - m_parent.store(nullptr); - } - - void add(work_t&& work) { - std::lock_guard lock(m_mutex); - m_queue.push(std::move(work)); - m_cv.notify_one(); - } - - std::optional pop_task() noexcept { - return m_queue.pop(); - } - - - std::optional try_steal() noexcept { - auto ptr = m_parent.load(); - if (ptr) return ptr->try_steal(m_id); - return {}; - } - - constexpr bool empty() const noexcept { return m_queue.empty_unsafe(); } - constexpr size_type size() const noexcept { return m_queue.size_unsafe(); } - constexpr size_type id() const noexcept { return m_id; } - constexpr bool running() const noexcept { return m_running.load(std::memory_order::relaxed); } - - private: - Queue m_queue{}; - std::thread m_thread; - std::atomic m_running{false}; - std::mutex m_mutex{}; - std::condition_variable m_cv{}; - std::atomic m_parent{nullptr}; - size_type m_id; - }; - - template - struct ThreadPool { - using worker_t = Worker; - using work_t = typename worker_t::work_t; - using size_type = std::size_t; - - constexpr ThreadPool(ThreadPool const&) noexcept = delete; - constexpr ThreadPool(ThreadPool &&) noexcept = default; - constexpr ThreadPool& operator=(ThreadPool const&) noexcept = delete; - constexpr ThreadPool& operator=(ThreadPool &&) noexcept = default; - ~ThreadPool() { - stop(); - } - - ThreadPool(size_type n = std::thread::hardware_concurrency()) - : m_workers(std::max(n, size_type{1})) - { - for (auto i = 0ul; i < m_workers.size(); ++i) { - m_workers[i].start(this, i); - } - } - - void stop() { - for (auto& w: m_workers) w.stop(); - } - - void add(Fn&& work) { - m_active_tasks.fetch_add(1, std::memory_order::relaxed); - m_workers[m_last_added].add(std::move(work)); - m_last_added = (m_last_added + 1) % m_workers.size(); - } - - std::optional try_steal(size_type id) { - for (auto& w: m_workers) { - if (w.id() == id) continue; - auto item = w.pop_task(); - if (item) return item; - } - return {}; - } - - void task_completed() { - if (m_active_tasks.fetch_sub(1, std::memory_order::release) == 1) { - m_wait_cv.notify_all(); - } - } - - void wait() { - std::unique_lock lock(m_wait_mutex); - m_wait_cv.wait(lock, [this] { - return m_active_tasks.load(std::memory_order::acquire) == 0; - }); - } - - - private: - std::vector m_workers; - size_type m_last_added{}; - std::mutex m_wait_mutex; - std::condition_variable m_wait_cv; - std::atomic m_active_tasks{0}; - }; - - using thread_pool_t = ThreadPool>; - - // WARNING: Do not capture the stack variable if you're defering wait on pool. - // If you want to capture them, either capture them value or do "pool.wait()" at the end of the scope. - template - requires (std::is_invocable_v) - constexpr auto parallel_for(thread_pool_t& pool, std::size_t start, std::size_t end, Fn&& body) noexcept { - if (start >= end) return; - - auto const size = (end - start); - auto const chunk_size = std::max(size_t{1}, (size + Split - 1) / Split); - auto const num_chunks = (size + chunk_size - 1) / chunk_size; - - for (auto chunk = 0ul; chunk < num_chunks; ++chunk) { - auto const chunk_start = std::min(start + (chunk * chunk_size), end); - auto const chunk_end = std::min(chunk_start + (chunk_size), end); - pool.add([chunk_start, chunk_end, body] { - for (auto i = chunk_start; i < chunk_end; ++i) { - std::invoke(body, i); - } - }); - } - } -} // nsmespace amt - -#endif // AMT_THREAD_HPP diff --git a/spatialmap.cpp b/spatialmap.cpp index 4263926..e437cc2 100644 --- a/spatialmap.cpp +++ b/spatialmap.cpp @@ -101,7 +101,7 @@ inline void update_sorted(SortedEntities& sprite_distance, PointEntityMap& table int inside = (from.x - sprite.x) * (from.x - sprite.x) + (from.y - sprite.y) * (from.y - sprite.y); - if(sprite == seen) { + if(sprite == seen && from != sprite) { wiggle += 0.02f; } else { wiggle = 0.0f;