Brought in Amit's latest code and converted it to use either dumb lighting or his new torch lighting, then threw in the FPS counter from last night.

master
Zed A. Shaw 2 months ago
parent 53a151511e
commit 831e15ca18
  1. 47
      amt/main.cpp
  2. 455
      amt/pixel.hpp
  3. 76
      amt/raycaster.cpp
  4. 2
      amt/raycaster.hpp
  5. 1
      main.cpp
  6. 74
      tests/amt_test.cpp

@ -3,14 +3,8 @@
#include <chrono> #include <chrono>
#include <numeric> #include <numeric>
#include <functional> #include <functional>
#include "constants.hpp"
#define RAY_VIEW_WIDTH 960 #include "stats.hpp"
#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;
Matrix MAP{ Matrix MAP{
{1,1,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1,1},
@ -24,16 +18,27 @@ Matrix MAP{
{1,1,1,1,1,1,1,1,1} {1,1,1,1,1,1,1,1,1}
}; };
void draw_gui(sf::RenderWindow &window) { void draw_gui(sf::RenderWindow &window, sf::Text &text, Stats &stats) {
sf::RectangleShape rect({SCREEN_WIDTH - RAY_VIEW_WIDTH, 300}); sf::RectangleShape rect({SCREEN_WIDTH - RAY_VIEW_WIDTH, SCREEN_HEIGHT});
rect.setPosition({0,0}); rect.setPosition({0,0});
rect.setFillColor({100, 100, 100}); rect.setFillColor({50, 50, 50});
window.draw(rect); 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() { int main() {
sf::RenderWindow window(sf::VideoMode({SCREEN_WIDTH, SCREEN_HEIGHT}), "Zed's Ray Caster Game Thing"); 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 //ZED this should set with a function
float player_x = MAP.rows() / 2; float player_x = MAP.rows() / 2;
float player_y = MAP.cols() / 2; float player_y = MAP.cols() / 2;
@ -50,26 +55,16 @@ int main() {
window.close(); window.close();
}; };
Stats stats;
std::size_t const max_count = 100;
std::vector<double> frames(max_count);
std::size_t it = 1;
while(window.isOpen()) { while(window.isOpen()) {
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
rayview.render(); rayview.render();
auto end = std::chrono::high_resolution_clock::now(); auto end = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration<double>(end - start); auto elapsed = std::chrono::duration<double>(end - start);
auto frame = 1 / elapsed.count(); stats.sample(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); draw_gui(window, text, stats);
window.display(); window.display();
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)) { if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)) {
@ -84,6 +79,10 @@ int main() {
rayview.rotate(rotSpeed, 1); rayview.rotate(rotSpeed, 1);
} }
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::R)) {
stats.reset();
}
window.handleEvents(onClose); window.handleEvents(onClose);
} }

@ -3,11 +3,14 @@
#include "matrix.hpp" #include "matrix.hpp"
#include <algorithm> #include <algorithm>
#include <bit>
#include <cmath> #include <cmath>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <limits> #include <limits>
#include <string>
#include <stdexcept> #include <stdexcept>
#include <type_traits>
namespace amt { namespace amt {
@ -54,74 +57,87 @@ namespace amt {
struct RGBA { struct RGBA {
using pixel_t = std::uint8_t; using pixel_t = std::uint8_t;
pixel_t r{}; // 0-255 using pixels_t = std::uint32_t;
pixel_t g{}; // 0-255
pixel_t b{}; // 0-255
pixel_t a{}; // 0-255
constexpr RGBA() noexcept = default; constexpr RGBA() noexcept = default;
constexpr RGBA(RGBA const&) noexcept = default; constexpr RGBA(RGBA const&) noexcept = default;
constexpr RGBA(RGBA &&) noexcept = default; constexpr RGBA(RGBA &&) noexcept = default;
RGBA& operator=(RGBA const& other) noexcept { constexpr RGBA& operator=(RGBA const& other) noexcept = default;
// HACK: clang was unable to optimize the copy using a single move instruction. constexpr RGBA& operator=(RGBA && other) noexcept = default;
auto& self = *reinterpret_cast<std::uint32_t*>(this);
auto color = *reinterpret_cast<std::uint32_t const*>(&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<std::uint32_t*>(this);
auto color = *reinterpret_cast<std::uint32_t const*>(&other);
self = color;
return *this;
}
constexpr ~RGBA() 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 constexpr RGBA(pixel_t r, pixel_t g, pixel_t b, pixel_t a = 0xff) noexcept
: r(r) : m_data({ .rgba = { r, g, b, a } })
, g(g)
, b(b)
, a(a)
{} {}
constexpr RGBA(pixel_t color, pixel_t a = 0xff) noexcept constexpr RGBA(pixel_t color, pixel_t a = 0xff) noexcept
: RGBA(color, color, color, a) : RGBA(color, color, color, a)
{} {}
// NOTE: RRGGBBAA // NOTE: Returns RRGGBBAA
constexpr static auto from_hex(std::uint32_t color) noexcept -> RGBA { constexpr auto to_hex() const noexcept -> pixels_t {
if constexpr (std::endian::native == std::endian::little) {
auto r = static_cast<pixels_t>(this->r());
auto b = static_cast<pixels_t>(this->b());
auto g = static_cast<pixels_t>(this->g());
auto a = static_cast<pixels_t>(this->a());
return (r << (8 * 3)) | (g << (8 * 2)) | (b << (8 * 1)) | (a << (8 * 0));
} else {
return m_data.color;
}
}
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);
}
template <typename T>
requires std::is_arithmetic_v<T>
constexpr auto operator/(T val) const noexcept {
auto d = static_cast<float>(val);
auto tr = float(r());
auto tg = float(g());
auto tb = float(b());
return RGBA( return RGBA(
((color >> (8 * 3)) & 0xff), static_cast<pixel_t>(tr / d),
((color >> (8 * 2)) & 0xff), static_cast<pixel_t>(tg / d),
((color >> (8 * 1)) & 0xff), static_cast<pixel_t>(tb / d),
((color >> (8 * 0)) & 0xff) a()
); );
} }
// NOTE: RRGGBBAA template <typename T>
constexpr auto to_hex() const noexcept -> std::uint32_t { requires std::is_arithmetic_v<T>
auto r = static_cast<std::uint32_t>(this->r); constexpr auto operator*(T val) const noexcept {
auto b = static_cast<std::uint32_t>(this->b); auto d = static_cast<float>(val);
auto g = static_cast<std::uint32_t>(this->g);
auto a = static_cast<std::uint32_t>(this->a);
return (r << (8 * 3)) | (g << (8 * 2)) | (b << (8 * 1)) | (a << (8 * 0));
}
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);
return RGBA( return RGBA(
to_pixel(nr), static_cast<pixel_t>(r() * d),
to_pixel(ng), static_cast<pixel_t>(g() * d),
to_pixel(nb), static_cast<pixel_t>(b() * d),
alpha a()
); );
} }
private: private:
@ -130,40 +146,42 @@ namespace amt {
} }
static constexpr auto to_pixel(float p) noexcept -> pixel_t { static constexpr auto to_pixel(float p) noexcept -> pixel_t {
return static_cast<pixel_t>(std::clamp(p, 0.f, 1.f) * 255); return static_cast<pixel_t>(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 <BlendMode M>
static constexpr auto blend_helper() noexcept {
constexpr auto mix_helper = [](float s, float b, float a) -> float { constexpr auto mix_helper = [](float s, float b, float a) -> float {
// (1 - αb) x Cs + αb x B(Cb, Cs) // (1 - αb) x Cs + αb x B(Cb, Cs)
return (1 - a) * s + a * b; return (1 - a) * s + a * b;
}; };
switch (mode) { if constexpr (M == BlendMode::normal) {
case BlendMode::normal: return mix_helper(bg, fg, alpha); return [mix_helper](float bg, float fg, float alpha) { return mix_helper(bg, fg, alpha); };
case BlendMode::multiply: { } else if constexpr (M == BlendMode::multiply) {
return mix_helper(bg, bg * fg, alpha); return [mix_helper](float bg, float fg, float alpha) { return mix_helper(bg, bg * fg, alpha); };
} } else if constexpr (M == BlendMode::screen) {
case BlendMode::screen: { return [mix_helper](float bg, float fg, float alpha) {
constexpr auto fn = [](float b, float s) -> float {
// Cb + Cs -(Cb x Cs) // Cb + Cs -(Cb x Cs)
return b + s - (b * s); auto bf = bg + fg - (bg * fg);
};
auto bf = fn(bg, fg);
return mix_helper(bg, bf, alpha); return mix_helper(bg, bf, alpha);
} };
case BlendMode::overlay: { } 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) // HardLight(Cs, Cb)
auto hl = blend_helper(bg, fg, alpha, BlendMode::hardLight); auto hl = hard_light_fn(bg, fg, alpha, multiply_fn, screen_fn);
return mix_helper(bg, hl, alpha); return mix_helper(bg, hl, alpha);
} };
case BlendMode::darken: return mix_helper(bg, std::min(bg, fg), alpha); } else if constexpr (M == BlendMode::darken) {
case BlendMode::lighten: return mix_helper(bg, std::max(bg, fg), alpha); return [mix_helper](float bg, float fg, float alpha) {
case BlendMode::colorDodge: { 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 { constexpr auto fn = [](float b, float s) -> float {
if (b == 0) return 0; if (b == 0) return 0;
if (s == 255) return 255; if (s == 255) return 255;
@ -171,8 +189,9 @@ namespace amt {
}; };
auto bf = fn(bg, fg); auto bf = fn(bg, fg);
return mix_helper(bg, bf, alpha); return mix_helper(bg, bf, alpha);
} };
case BlendMode::colorBurn: { } else if constexpr (M == BlendMode::colorBurn) {
return [mix_helper](float bg, float fg, float alpha) {
constexpr auto fn = [](float b, float s) -> float { constexpr auto fn = [](float b, float s) -> float {
if (b == 255) return 255; if (b == 255) return 255;
if (s == 0) return 0; if (s == 0) return 0;
@ -180,20 +199,22 @@ namespace amt {
}; };
auto bf = fn(bg, fg); auto bf = fn(bg, fg);
return mix_helper(bg, bf, alpha); return mix_helper(bg, bf, alpha);
} };
case BlendMode::hardLight: { } else if constexpr (M == BlendMode::hardLight) {
constexpr auto fn = [](float b, float s, float a) -> float { 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) { if (s <= 0.5f) {
return RGBA::blend_helper(b, 2.f * s, a, BlendMode::multiply); return multiply_fn(b, 2.f * s, a);
} else { } else {
return RGBA::blend_helper(b, 2.f * s - 1.f, a, BlendMode::screen); return screen_fn(b, 2.f * s - 1.f, a);
} }
}; };
auto bf = fn(bg, fg, alpha); auto bf = fn(bg, fg, alpha);
return mix_helper(bg, bf, alpha); return mix_helper(bg, bf, alpha);
} };
case BlendMode::softLight: { } else if constexpr (M == BlendMode::softLight) {
return [mix_helper](float bg, float fg, float alpha) {
constexpr auto fn = [](float b, float s) -> float { constexpr auto fn = [](float b, float s) -> float {
if (s <= 0.5f) { if (s <= 0.5f) {
// B(Cb, Cs) = Cb - (1 - 2 x Cs) x Cb x (1 - Cb) // B(Cb, Cs) = Cb - (1 - 2 x Cs) x Cb x (1 - Cb)
@ -216,12 +237,14 @@ namespace amt {
auto bf = fn(bg, fg); auto bf = fn(bg, fg);
return mix_helper(bg, bf, alpha); return mix_helper(bg, bf, alpha);
} };
case BlendMode::difference: { } else if constexpr (M == BlendMode::difference) {
return [mix_helper](float bg, float fg, float alpha) {
// B(Cb, Cs) = | Cb - Cs | // B(Cb, Cs) = | Cb - Cs |
return mix_helper(bg, (bg > fg ? (bg - fg) : (fg - bg)), alpha); return mix_helper(bg, (bg > fg ? (bg - fg) : (fg - bg)), alpha);
} };
case BlendMode::exclusion: { } else if constexpr (M == BlendMode::exclusion) {
return [mix_helper](float bg, float fg, float alpha) {
constexpr auto fn = [](float b, float s) -> float { constexpr auto fn = [](float b, float s) -> float {
// B(Cb, Cs) = Cb + Cs - 2 x Cb x Cs // B(Cb, Cs) = Cb + Cs - 2 x Cb x Cs
return b + s - 2 * b * s; return b + s - 2 * b * s;
@ -229,18 +252,146 @@ namespace amt {
auto bf = fn(bg, fg); auto bf = fn(bg, fg);
return mix_helper(bg, bf, alpha); return mix_helper(bg, bf, alpha);
};
} }
};
public:
template <BlendMode M>
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<BlendMode::normal>();
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<BlendMode::multiply>();
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<BlendMode::screen>();
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<BlendMode::overlay>();
auto hard_light_fn = blend_helper<BlendMode::hardLight>();
auto multiply_fn = blend_helper<BlendMode::multiply>();
auto screen_fn = blend_helper<BlendMode::screen>();
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<BlendMode::darken>();
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<BlendMode::lighten>();
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<BlendMode::colorDodge>();
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<BlendMode::colorDodge>();
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<BlendMode::hardLight>();
auto multiply_fn = blend_helper<BlendMode::multiply>();
auto screen_fn = blend_helper<BlendMode::screen>();
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<BlendMode::softLight>();
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<BlendMode::difference>();
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<BlendMode::exclusion>();
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<BlendMode::normal>(color);
case BlendMode::multiply: return blend<BlendMode::multiply>(color);
case BlendMode::screen: return blend<BlendMode::screen>(color);
case BlendMode::overlay: return blend<BlendMode::overlay>(color);
case BlendMode::darken: return blend<BlendMode::darken>(color);
case BlendMode::lighten: return blend<BlendMode::lighten>(color);
case BlendMode::colorDodge: return blend<BlendMode::colorDodge>(color);
case BlendMode::colorBurn: return blend<BlendMode::colorBurn>(color);
case BlendMode::hardLight: return blend<BlendMode::hardLight>(color);
case BlendMode::softLight: return blend<BlendMode::softLight>(color);
case BlendMode::difference: return blend<BlendMode::difference>(color);
case BlendMode::exclusion: return blend<BlendMode::exclusion>(color);
}
}
private:
union {
struct {
pixel_t r;
pixel_t g;
pixel_t b;
pixel_t a;
} rgba;
pixels_t color;
} m_data {};
}; };
struct HSLA { struct HSLA {
using pixel_t = float; using pixel_t = float;
pixel_t h{}; // hue: 0-360 using pixels_t = float[4];
pixel_t s{}; // saturation: 0-100%
pixel_t l{}; // lightness: 0-100% // ensures pixel to be in range
pixel_t a{}; // alpha: 0-100% template <unsigned min, unsigned max>
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() noexcept = default;
constexpr HSLA(HSLA const&) noexcept = default; constexpr HSLA(HSLA const&) noexcept = default;
@ -250,33 +401,29 @@ namespace amt {
constexpr ~HSLA() noexcept = default; constexpr ~HSLA() noexcept = default;
constexpr HSLA(pixel_t h, pixel_t s, pixel_t l, pixel_t a = 100) noexcept constexpr HSLA(pixel_t h, pixel_t s, pixel_t l, pixel_t a = 100) noexcept
: h(h) : m_data({ .hsla = { .h = h, .s = s, .l = l, .a = a } })
, s(s)
, l(l)
, a(a)
{} {}
constexpr HSLA(RGBA color) noexcept { constexpr HSLA(RGBA color) noexcept {
auto tr = float(color.r) / 255; auto min = std::min({color.r(), color.g(), color.b()});
auto tg = float(color.g) / 255; auto max = std::max({color.r(), color.g(), color.b()});
auto tb = float(color.b) / 255; auto c = (max - min) / 255.f;
auto ta = float(color.a) / 255;
auto min = std::min({tr, tg, tb}); auto tr = float(color.r()) / 255;
auto max = std::max({tr, tg, tb}); auto tg = float(color.g()) / 255;
auto c = max - min; auto tb = float(color.b()) / 255;
auto ta = float(color.a()) / 255;
float hue = 0; float hue = 0;
float s = 0; float s = 0;
auto l = (max + min) / 2; auto l = ((max + min) / 2.f) / 255.f;
if (!detail::compare_float(c, 0)) { if (min == max) {
auto temp_max = std::max({color.r, color.g, color.b}); if (max == color.r()) {
if (temp_max == color.r) {
auto seg = (tg - tb) / c; auto seg = (tg - tb) / c;
auto shift = (seg < 0 ? 360.f : 0.f) / 60.f; auto shift = (seg < 0 ? 360.f : 0.f) / 60.f;
hue = seg + shift; hue = seg + shift;
} else if (temp_max == color.g) { } else if (max == color.g()) {
auto seg = (tb - tr) / c; auto seg = (tb - tr) / c;
auto shift = 120.f / 60.f; auto shift = 120.f / 60.f;
hue = seg + shift; hue = seg + shift;
@ -292,27 +439,28 @@ namespace amt {
auto q = static_cast<float>(static_cast<int>(hue / 360.f)); auto q = static_cast<float>(static_cast<int>(hue / 360.f));
hue -= q * 360.f; hue -= q * 360.f;
this->h = hue; m_data.hsla.h = hue;
this->s = s * 100.f; m_data.hsla.s = s * 100.f;
this->l = l * 100.f; m_data.hsla.l = l * 100.f;
this->a = ta * 100.f; m_data.hsla.a = ta * 100.f;
} }
constexpr operator RGBA() const noexcept { constexpr operator RGBA() const noexcept {
auto th = std::clamp(h, 0.f, 360.f) / 360; auto ts = s() / 100.f;
auto ts = std::clamp(s, 0.f, 100.f) / 100; auto tl = l() / 100.f;
auto tl = std::clamp(l, 0.f, 100.f) / 100; auto ta = a() / 100.f;
auto ta = std::clamp(a, 0.f, 100.f) / 100; if (s() == 0) return RGBA(to_pixel(tl), to_pixel(ta));
if (detail::compare_float(ts, 0)) return RGBA(to_int(tl), to_int(ta));
auto th = h() / 360.f;
float const q = tl < 0.5 ? tl * (1 + ts) : tl + ts - tl * ts; float const q = tl < 0.5 ? tl * (1 + ts) : tl + ts - tl * ts;
float const p = 2 * tl - q; float const p = 2 * tl - q;
return RGBA( return RGBA(
to_int(convert_hue(p, q, th + 1.f / 3)), to_pixel(convert_hue(p, q, th + 1.f / 3)),
to_int(convert_hue(p, q, th)), to_pixel(convert_hue(p, q, th)),
to_int(convert_hue(p, q, th - 1.f / 3)), to_pixel(convert_hue(p, q, th - 1.f / 3)),
to_int(ta) to_pixel(ta)
); );
} }
@ -321,51 +469,70 @@ namespace amt {
auto rhs = RGBA(color); auto rhs = RGBA(color);
return HSLA(lhs.blend(rhs, mode)); 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: private:
static constexpr auto to_int(float a, float max = 1) noexcept -> std::uint8_t { static constexpr auto to_pixel(float a) noexcept -> RGBA::pixel_t {
return static_cast<std::uint8_t>((a / max) * 255); return static_cast<RGBA::pixel_t>(a * 255);
} }
static constexpr auto convert_hue(float p, float q, float t) noexcept -> float { static constexpr auto convert_hue(float p, float q, float t) noexcept -> float {
if (t < 0) t += 1; t = t - (t > 1) + (t < 0);
if (t > 1) t -= 1;
if (t * 6 < 1) return p + (q - p) * 6 * t; if (t * 6 < 1) return p + (q - p) * 6 * t;
if (t * 2 < 1) return q; if (t * 2 < 1) return q;
if (t * 3 < 2) return p + (q - p) * (2.f / 3 - t) * 6; if (t * 3 < 2) return p + (q - p) * (2.f / 3 - t) * 6;
return p; 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 { namespace detail {
template <PixelFormat F> template <PixelFormat F>
inline static constexpr auto parse_pixel_helper(RGBA color, std::uint8_t* out_ptr) noexcept { inline static constexpr auto parse_pixel_helper(RGBA color, std::uint8_t* out_ptr) noexcept {
if constexpr (F == PixelFormat::rgba) { if constexpr (F == PixelFormat::rgba) {
out_ptr[0] = color.r; out_ptr[0] = color.r();
out_ptr[1] = color.g; out_ptr[1] = color.g();
out_ptr[2] = color.b; out_ptr[2] = color.b();
out_ptr[3] = color.a; out_ptr[3] = color.a();
} else if constexpr (F == PixelFormat::abgr) { } else if constexpr (F == PixelFormat::abgr) {
out_ptr[0] = color.a; out_ptr[0] = color.a();
out_ptr[1] = color.b; out_ptr[1] = color.b();
out_ptr[2] = color.g; out_ptr[2] = color.g();
out_ptr[3] = color.r; out_ptr[3] = color.r();
} else if constexpr (F == PixelFormat::rgb) { } else if constexpr (F == PixelFormat::rgb) {
out_ptr[0] = color.r; out_ptr[0] = color.r();
out_ptr[1] = color.g; out_ptr[1] = color.g();
out_ptr[2] = color.b; out_ptr[2] = color.b();
} else if constexpr (F == PixelFormat::bgr) { } else if constexpr (F == PixelFormat::bgr) {
out_ptr[0] = color.b; out_ptr[0] = color.b();
out_ptr[1] = color.g; out_ptr[1] = color.g();
out_ptr[2] = color.r; out_ptr[2] = color.r();
} else if constexpr (F == PixelFormat::ga) { } else if constexpr (F == PixelFormat::ga) {
out_ptr[0] = color.r; out_ptr[0] = color.r();
out_ptr[1] = color.a; out_ptr[1] = color.a();
} else if constexpr (F == PixelFormat::ag) { } else if constexpr (F == PixelFormat::ag) {
out_ptr[0] = color.a; out_ptr[0] = color.a();
out_ptr[1] = color.r; out_ptr[1] = color.r();
} else { } 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 { 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 { 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());
} }
}; };

@ -1,44 +1,51 @@
#include "amt/raycaster.hpp" #include "amt/raycaster.hpp"
#include "amt/texture.hpp" #include "amt/texture.hpp"
#include "amt/pixel.hpp" #include "amt/pixel.hpp"
#include "constants.hpp"
using namespace fmt; 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}; static constexpr auto room_brightness = 0.3f; // increse this to increase the room brightness. Higher value means brighter room.
conv.as_color.r /= distance;
conv.as_color.g /= distance; #ifdef AMT_LIGHT
conv.as_color.b /= distance; 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);
return conv.as_int; amt::RGBA const yellow = amt::HSLA(40, 20, yellow_brightness);
auto temp = (pixel / pixel_brightness).blend<amt::BlendMode::softLight>(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) : Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map, unsigned width, unsigned height) :
view_texture({(unsigned int)width, (unsigned int)height}), view_texture(sf::Vector2u{width, height}),
view_sprite(view_texture), view_sprite(view_texture),
$width(width), $height(height), $width(static_cast<int>(width)),
pixels(static_cast<size_t>(height), static_cast<std::size_t>(width)), $height(static_cast<int>(height)),
pixels(height, width),
$window(window), $window(window),
$map(map), $map(map),
spriteOrder(textures.NUM_SPRITES), spriteOrder(textures.NUM_SPRITES),
spriteDistance(textures.NUM_SPRITES), spriteDistance(textures.NUM_SPRITES),
ZBuffer(width) ZBuffer(width)
{ {
$window.setVerticalSyncEnabled(true); $window.setVerticalSyncEnabled(VSYNC);
view_sprite.setPosition({0, 0}); view_sprite.setPosition({0, 0});
textures.load_textures(); textures.load_textures();
} }
@ -139,9 +146,7 @@ void Raycaster::sprite_casting() {
// BUG: this crashes sometimes when the math goes out of bounds // BUG: this crashes sometimes when the math goes out of bounds
auto color = sprite_texture[texY][texX]; auto color = sprite_texture[texY][texX];
// poor person's transparency, get current color from the texture // poor person's transparency, get current color from the texture
if((color.to_hex() & 0xFFFFFF00) != 0) { pixels[y][stripe] = (color.to_hex() & 0xffffff00) ? color: pixels[y][stripe];
pixels[y][stripe] = color;
}
} }
} }
} }
@ -149,7 +154,12 @@ void Raycaster::sprite_casting() {
} }
void Raycaster::cast_rays() { 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 // WALL CASTING
for(int x = 0; x < $width; x++) { for(int x = 0; x < $width; x++) {
@ -247,7 +257,11 @@ void Raycaster::cast_rays() {
for(int y = drawStart; y < drawEnd; y++) { for(int y = drawStart; y < drawEnd; y++) {
int texY = (int)texPos & (textures.TEXTURE_HEIGHT - 1); int texY = (int)texPos & (textures.TEXTURE_HEIGHT - 1);
texPos += step; 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 // 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? // floorX cellX to find the texture x/y. How?
// FLOOR // FLOOR
pixels[y][x] = textures.floor[ty][tx]; pixels[y][x] = textures.floor[ty][tx] * room_brightness;
// CEILING // CEILING
pixels[$height - y - 1][x] = textures.ceiling[ty][tx]; pixels[$height - y - 1][x] = textures.ceiling[ty][tx] * room_brightness;
} }
} }

@ -44,7 +44,7 @@ struct Raycaster {
std::vector<double> spriteDistance; std::vector<double> spriteDistance;
std::vector<double> ZBuffer; // width std::vector<double> 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 draw_pixel_buffer();
void clear(); void clear();

@ -66,7 +66,6 @@ int main() {
draw_gui(window, text, stats); draw_gui(window, text, stats);
window.display(); window.display();
rayview.rotate(rotSpeed, -1);
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)) { if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)) {
rayview.run(moveSpeed, 1); rayview.run(moveSpeed, 1);

@ -0,0 +1,74 @@
#include <cstdint>
#include <print>
#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<int>{
{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));
}
}
Loading…
Cancel
Save