diff --git a/amt/main.cpp b/amt/main.cpp index 069d30f..d46e888 100644 --- a/amt/main.cpp +++ b/amt/main.cpp @@ -3,14 +3,8 @@ #include #include #include - -#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; +#include "constants.hpp" +#include "stats.hpp" Matrix MAP{ {1,1,1,1,1,1,1,1,1}, @@ -24,16 +18,27 @@ Matrix MAP{ {1,1,1,1,1,1,1,1,1} }; -void draw_gui(sf::RenderWindow &window) { - sf::RectangleShape rect({SCREEN_WIDTH - RAY_VIEW_WIDTH, 300}); +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({100, 100, 100}); + 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; @@ -50,26 +55,16 @@ int main() { window.close(); }; + Stats stats; - 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; + stats.sample(1/elapsed.count()); - draw_gui(window); + draw_gui(window, text, stats); window.display(); if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)) { @@ -84,6 +79,10 @@ int main() { rayview.rotate(rotSpeed, 1); } + if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::R)) { + stats.reset(); + } + window.handleEvents(onClose); } diff --git a/amt/pixel.hpp b/amt/pixel.hpp index f9178a6..3bbefa8 100644 --- a/amt/pixel.hpp +++ b/amt/pixel.hpp @@ -3,11 +3,14 @@ #include "matrix.hpp" #include +#include #include #include #include #include +#include #include +#include namespace amt { @@ -54,74 +57,87 @@ namespace amt { struct RGBA { using pixel_t = std::uint8_t; - pixel_t r{}; // 0-255 - pixel_t g{}; // 0-255 - pixel_t b{}; // 0-255 - pixel_t a{}; // 0-255 + using pixels_t = std::uint32_t; constexpr RGBA() noexcept = default; constexpr RGBA(RGBA const&) noexcept = default; constexpr RGBA(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& operator=(RGBA const& other) noexcept = default; + constexpr RGBA& operator=(RGBA && other) noexcept = default; constexpr ~RGBA() noexcept = default; + + // NOTE: Accepts RRGGBBAA + explicit constexpr RGBA(pixels_t color) noexcept + : m_data({ .color = color }) + {} + constexpr RGBA(pixel_t r, pixel_t g, pixel_t b, pixel_t a = 0xff) noexcept - : r(r) - , g(g) - , b(b) - , a(a) + : m_data({ .rgba = { r, g, b, a } }) {} constexpr RGBA(pixel_t color, pixel_t a = 0xff) noexcept : RGBA(color, color, color, a) {} - // NOTE: RRGGBBAA - constexpr static auto from_hex(std::uint32_t color) noexcept -> RGBA { - return RGBA( - ((color >> (8 * 3)) & 0xff), - ((color >> (8 * 2)) & 0xff), - ((color >> (8 * 1)) & 0xff), - ((color >> (8 * 0)) & 0xff) - ); + // NOTE: Returns RRGGBBAA + constexpr auto to_hex() const noexcept -> pixels_t { + if constexpr (std::endian::native == std::endian::little) { + 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)); + } else { + return m_data.color; + } } - - // NOTE: RRGGBBAA - constexpr auto to_hex() const noexcept -> std::uint32_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.rgba.r; } + constexpr auto g() const noexcept -> pixel_t { return m_data.rgba.g; } + constexpr auto b() const noexcept -> pixel_t { return m_data.rgba.b; } + constexpr auto a() const noexcept -> pixel_t { return m_data.rgba.a; } + + constexpr auto r() noexcept -> pixel_t& { return m_data.rgba.r; } + constexpr auto g() noexcept -> pixel_t& { return m_data.rgba.g; } + constexpr auto b() noexcept -> pixel_t& { return m_data.rgba.b; } + constexpr auto a() noexcept -> pixel_t& { return m_data.rgba.a; } + + /** + * @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); } - constexpr auto blend(RGBA color, BlendMode mode) 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 nr = blend_helper(normalize(r), normalize(color.r), normalize(a), mode); - auto ng = blend_helper(normalize(g), normalize(color.g), normalize(a), mode); - auto nb = blend_helper(normalize(b), normalize(color.b), normalize(a), mode); + template + requires std::is_arithmetic_v + constexpr auto operator/(T val) const noexcept { + auto d = static_cast(val); + auto tr = float(r()); + auto tg = float(g()); + auto tb = float(b()); + return RGBA( + static_cast(tr / d), + static_cast(tg / d), + static_cast(tb / d), + a() + ); + } + template + requires std::is_arithmetic_v + constexpr auto operator*(T val) const noexcept { + auto d = static_cast(val); return RGBA( - to_pixel(nr), - to_pixel(ng), - to_pixel(nb), - alpha + static_cast(r() * d), + static_cast(g() * d), + static_cast(b() * d), + a() ); } private: @@ -130,118 +146,253 @@ namespace amt { } static constexpr auto to_pixel(float p) noexcept -> pixel_t { - return static_cast(std::clamp(p, 0.f, 1.f) * 255); + return static_cast(p * 255); } - static constexpr auto apply_op(pixel_t l, pixel_t r, auto&& fn) noexcept -> pixel_t { - return RGBA::to_pixel(fn(RGBA::normalize(l), RGBA::normalize(r))); - } - - static constexpr auto blend_helper(float bg, float fg, float alpha, BlendMode mode) noexcept -> float { + 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; }; - switch (mode) { - case BlendMode::normal: return mix_helper(bg, fg, alpha); - case BlendMode::multiply: { - return mix_helper(bg, bg * fg, alpha); - } - case BlendMode::screen: { - constexpr auto fn = [](float b, float s) -> float { + 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) - return b + s - (b * s); + auto bf = bg + fg - (bg * fg); + return mix_helper(bg, bf, alpha); }; - auto bf = fn(bg, fg); - return mix_helper(bg, bf, alpha); - } - case BlendMode::overlay: { - // HardLight(Cs, Cb) - auto hl = blend_helper(bg, fg, alpha, BlendMode::hardLight); - return mix_helper(bg, hl, alpha); - } - case BlendMode::darken: return mix_helper(bg, std::min(bg, fg), alpha); - case BlendMode::lighten: return mix_helper(bg, std::max(bg, fg), alpha); - case BlendMode::colorDodge: { - 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)); + } 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); }; - auto bf = fn(bg, fg); - return mix_helper(bg, bf, alpha); - } - case BlendMode::colorBurn: { - 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); + } else if constexpr (M == BlendMode::darken) { + return [mix_helper](float bg, float fg, float alpha) { + return mix_helper(bg, std::min(bg, fg), alpha); }; - auto bf = fn(bg, fg); - return mix_helper(bg, bf, alpha); - } - case BlendMode::hardLight: { - constexpr auto fn = [](float b, float s, float a) -> float { - if (s <= 0.5f) { - return RGBA::blend_helper(b, 2.f * s, a, BlendMode::multiply); - } else { - return RGBA::blend_helper(b, 2.f * s - 1.f, a, BlendMode::screen); - } + } else if constexpr (M == BlendMode::lighten) { + return [mix_helper](float bg, float fg, float alpha) { + return mix_helper(bg, std::max(bg, fg), alpha); }; - - auto bf = fn(bg, fg, alpha); - return mix_helper(bg, bf, alpha); - } - case BlendMode::softLight: { - 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 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 { - // D(Cb) = sqrt(Cb) - d = std::sqrt(b); + return screen_fn(b, 2.f * s - 1.f, a); } - - // B(Cb, Cs) = Cb + (2 x Cs - 1) x (D(Cb) - Cb) - return b + (2 * s - 1) * (d - b); - } + }; + + auto bf = fn(bg, fg, alpha); + return mix_helper(bg, bf, alpha); }; - - auto bf = fn(bg, fg); - return mix_helper(bg, bf, alpha); - } - case BlendMode::difference: { - // B(Cb, Cs) = | Cb - Cs | - return mix_helper(bg, (bg > fg ? (bg - fg) : (fg - bg)), alpha); - } - case BlendMode::exclusion: { - constexpr auto fn = [](float b, float s) -> float { - // B(Cb, Cs) = Cb + Cs - 2 x Cb x Cs - return b + s - 2 * b * s; + } 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); }; - - 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: + union { + struct { + pixel_t r; + pixel_t g; + pixel_t b; + pixel_t a; + } rgba; + pixels_t color; + } m_data {}; }; struct HSLA { using pixel_t = float; - pixel_t h{}; // hue: 0-360 - pixel_t s{}; // saturation: 0-100% - pixel_t l{}; // lightness: 0-100% - pixel_t a{}; // alpha: 0-100% + 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; @@ -250,33 +401,29 @@ namespace amt { constexpr ~HSLA() noexcept = default; constexpr HSLA(pixel_t h, pixel_t s, pixel_t l, pixel_t a = 100) noexcept - : h(h) - , s(s) - , l(l) - , a(a) + : m_data({ .hsla = { .h = h, .s = s, .l = l, .a = a } }) {} constexpr HSLA(RGBA color) noexcept { - auto tr = float(color.r) / 255; - auto tg = float(color.g) / 255; - auto tb = float(color.b) / 255; - auto ta = float(color.a) / 255; + 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 min = std::min({tr, tg, tb}); - auto max = std::max({tr, tg, tb}); - auto c = max - min; + 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; + auto l = ((max + min) / 2.f) / 255.f; - if (!detail::compare_float(c, 0)) { - auto temp_max = std::max({color.r, color.g, color.b}); - if (temp_max == color.r) { + 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 (temp_max == color.g) { + } else if (max == color.g()) { auto seg = (tb - tr) / c; auto shift = 120.f / 60.f; hue = seg + shift; @@ -292,27 +439,28 @@ namespace amt { auto q = static_cast(static_cast(hue / 360.f)); hue -= q * 360.f; - this->h = hue; - this->s = s * 100.f; - this->l = l * 100.f; - this->a = ta * 100.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 th = std::clamp(h, 0.f, 360.f) / 360; - auto ts = std::clamp(s, 0.f, 100.f) / 100; - auto tl = std::clamp(l, 0.f, 100.f) / 100; - auto ta = std::clamp(a, 0.f, 100.f) / 100; - if (detail::compare_float(ts, 0)) return RGBA(to_int(tl), to_int(ta)); + 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_int(convert_hue(p, q, th + 1.f / 3)), - to_int(convert_hue(p, q, th)), - to_int(convert_hue(p, q, th - 1.f / 3)), - to_int(ta) + 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) ); } @@ -321,51 +469,70 @@ namespace amt { 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_int(float a, float max = 1) noexcept -> std::uint8_t { - return static_cast((a / max) * 255); + 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 { - if (t < 0) t += 1; - if (t > 1) t -= 1; + 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; + 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; + 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; + 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; + 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; + 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; + out_ptr[0] = color.a(); + out_ptr[1] = color.r(); } else { - out_ptr[0] = color.r; + out_ptr[0] = color.r(); } } @@ -548,7 +715,7 @@ namespace std { } auto format(amt::RGBA const& color, auto& ctx) const { - return format_to(ctx.out(), "rgba({}, {}, {}, {})", color.r, color.g, color.b, color.a); + return format_to(ctx.out(), "rgba({}, {}, {}, {})", color.r(), color.g(), color.b(), color.a()); } }; @@ -559,7 +726,7 @@ namespace std { } 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); + return format_to(ctx.out(), "hsla({:.1f}deg, {:.1f}%, {:.1f}%, {:.1f}%)", color.h(), color.s(), color.l(), color.a()); } }; @@ -592,4 +759,4 @@ namespace std { }; } // namespace std -#endif // AMT_PIXEL_HPP \ No newline at end of file +#endif // AMT_PIXEL_HPP diff --git a/amt/raycaster.cpp b/amt/raycaster.cpp index c765ff2..852a4e4 100644 --- a/amt/raycaster.cpp +++ b/amt/raycaster.cpp @@ -1,44 +1,51 @@ #include "amt/raycaster.hpp" #include "amt/texture.hpp" #include "amt/pixel.hpp" +#include "constants.hpp" + using namespace fmt; -using std::make_unique; - -union ColorConv { - struct { - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; - } as_color; - uint32_t as_int; -}; - -inline uint32_t dumb_lighting(uint32_t pixel, double distance) { - if(distance < 0.9) return pixel; - ColorConv conv{.as_int=pixel}; - conv.as_color.r /= distance; - conv.as_color.g /= distance; - conv.as_color.b /= distance; +static constexpr auto room_brightness = 0.3f; // increse this to increase the room brightness. Higher value means brighter room. + +#ifdef AMT_LIGHT +inline static constexpr amt::RGBA dumb_lighting(amt::RGBA pixel, double distance, double 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 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); - return conv.as_int; + 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) { + if(distance < 0.9) return pixel; + return pixel / distance; } +#endif -Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map, int width, int height) : - view_texture({(unsigned int)width, (unsigned int)height}), +Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map, unsigned width, unsigned height) : + view_texture(sf::Vector2u{width, height}), view_sprite(view_texture), - $width(width), $height(height), - pixels(static_cast(height), static_cast(width)), + $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) { - $window.setVerticalSyncEnabled(true); + $window.setVerticalSyncEnabled(VSYNC); view_sprite.setPosition({0, 0}); textures.load_textures(); } @@ -139,9 +146,7 @@ void Raycaster::sprite_casting() { // BUG: this crashes sometimes when the math goes out of bounds auto color = sprite_texture[texY][texX]; // poor person's transparency, get current color from the texture - if((color.to_hex() & 0xFFFFFF00) != 0) { - pixels[y][stripe] = color; - } + pixels[y][stripe] = (color.to_hex() & 0xffffff00) ? color: pixels[y][stripe]; } } } @@ -149,7 +154,12 @@ void Raycaster::sprite_casting() { } void Raycaster::cast_rays() { - double perpWallDist; + double perpWallDist = 0; + + auto const cx = $width / 2; + auto const cy = $height / 2; + double const radius = std::min($height, $width) / 2; + double const r_sq = radius * radius; // WALL CASTING for(int x = 0; x < $width; x++) { @@ -247,7 +257,11 @@ void Raycaster::cast_rays() { for(int y = drawStart; y < drawEnd; y++) { int texY = (int)texPos & (textures.TEXTURE_HEIGHT - 1); texPos += step; - pixels[y][x] = texture[texY][texX]; + 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); + pixels[y][x] = color; } // SET THE ZBUFFER FOR THE SPRITE CASTING @@ -309,10 +323,10 @@ void Raycaster::draw_ceiling_floor() { // floorX cellX to find the texture x/y. How? // FLOOR - pixels[y][x] = textures.floor[ty][tx]; + pixels[y][x] = textures.floor[ty][tx] * room_brightness; // CEILING - pixels[$height - y - 1][x] = textures.ceiling[ty][tx]; + pixels[$height - y - 1][x] = textures.ceiling[ty][tx] * room_brightness; } } diff --git a/amt/raycaster.hpp b/amt/raycaster.hpp index e740119..dd40d8b 100644 --- a/amt/raycaster.hpp +++ b/amt/raycaster.hpp @@ -44,7 +44,7 @@ struct Raycaster { std::vector spriteDistance; std::vector ZBuffer; // width - Raycaster(sf::RenderWindow& window, Matrix &map, int width, int height); + Raycaster(sf::RenderWindow& window, Matrix &map, unsigned width, unsigned height); void draw_pixel_buffer(); void clear(); @@ -65,4 +65,4 @@ struct Raycaster { return ((y) * $width) + (x); } -}; +}; \ No newline at end of file diff --git a/main.cpp b/main.cpp index 17d82f3..d6f567f 100644 --- a/main.cpp +++ b/main.cpp @@ -66,7 +66,6 @@ int main() { draw_gui(window, text, stats); window.display(); - rayview.rotate(rotSpeed, -1); if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)) { rayview.run(moveSpeed, 1); diff --git a/tests/amt_test.cpp b/tests/amt_test.cpp new file mode 100644 index 0000000..e8ea6b9 --- /dev/null +++ b/tests/amt_test.cpp @@ -0,0 +1,74 @@ +#include +#include +#include "matrix.hpp" +#include "pixel.hpp" + +using namespace amt; + +int main() { + + auto const format = PixelFormat::abgr; + PixelBuf b(2, 2, RGBA(0x01, 0x02, 0x03, 0x04)); + b[1][1] = HSLA(280, 20, 50, 80); + std::println("{:h}", b); + + + std::uint8_t ps[4 * 4] = {}; + b.copy_to(ps, format); + + /*for (auto i = 0zu; i < sizeof(ps); ++i) {*/ + /* std::println("[{}]: 0x{:0x}", i, ps[i]);*/ + /*}*/ + + PixelBuf test(ps, 2, 2, format); + + for (auto i = 0zu; auto color: test) { + std::println("[{}]: {}", i++, color); + } + + auto m = Matrix{ + {0, 1}, + {3, 4} + }; + + std::println("{}", m); + + + + { + auto ca = RGBA::from_hex(0x333333ff); + auto cb = RGBA::from_hex(0xaabbccff); + std::println("========= RGBA ==========="); + std::println("Normal: 0x{:0x}", ca.blend(cb, BlendMode::normal).to_hex()); + std::println("Multiply: 0x{:0x}", ca.blend(cb, BlendMode::multiply).to_hex()); + std::println("Screen: 0x{:0x}", ca.blend(cb, BlendMode::screen).to_hex()); + std::println("Overlay: 0x{:0x}", ca.blend(cb, BlendMode::overlay).to_hex()); + std::println("darken: 0x{:0x}", ca.blend(cb, BlendMode::darken).to_hex()); + std::println("lighten: 0x{:0x}", ca.blend(cb, BlendMode::lighten).to_hex()); + std::println("Dodge: 0x{:0x}", ca.blend(cb, BlendMode::colorDodge).to_hex()); + std::println("Burn: 0x{:0x}", ca.blend(cb, BlendMode::colorBurn).to_hex()); + std::println("hard light: 0x{:0x}", ca.blend(cb, BlendMode::hardLight).to_hex()); + std::println("soft light: 0x{:0x}", ca.blend(cb, BlendMode::softLight).to_hex()); + std::println("difference: 0x{:0x}", ca.blend(cb, BlendMode::difference).to_hex()); + std::println("exclusion: 0x{:0x}", ca.blend(cb, BlendMode::exclusion).to_hex()); + } + + { + HSLA ca = RGBA::from_hex(0x333333ff); + HSLA cb = RGBA::from_hex(0xaabbccff); + std::println("========= HSLA ==========="); + std::println("Normal: {}", ca.blend(cb, BlendMode::normal)); + std::println("Multiply: {}", ca.blend(cb, BlendMode::multiply)); + std::println("Screen: {}", ca.blend(cb, BlendMode::screen)); + std::println("Overlay: {}", ca.blend(cb, BlendMode::overlay)); + std::println("darken: {}", ca.blend(cb, BlendMode::darken)); + std::println("lighten: {}", ca.blend(cb, BlendMode::lighten)); + std::println("Dodge: {}", ca.blend(cb, BlendMode::colorDodge)); + std::println("Burn: {}", ca.blend(cb, BlendMode::colorBurn)); + std::println("hard light: {}", ca.blend(cb, BlendMode::hardLight)); + std::println("soft light: {}", ca.blend(cb, BlendMode::softLight)); + std::println("difference: {}", ca.blend(cb, BlendMode::difference)); + std::println("exclusion: {}", ca.blend(cb, BlendMode::exclusion)); + } + +}