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. 551
      amt/pixel.hpp
  3. 76
      amt/raycaster.cpp
  4. 4
      amt/raycaster.hpp
  5. 1
      main.cpp
  6. 74
      tests/amt_test.cpp

@ -3,14 +3,8 @@
#include <chrono>
#include <numeric>
#include <functional>
#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<double> 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<double>(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);
}

@ -3,11 +3,14 @@
#include "matrix.hpp"
#include <algorithm>
#include <bit>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <string>
#include <stdexcept>
#include <type_traits>
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<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& 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<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;
}
}
// NOTE: RRGGBBAA
constexpr auto to_hex() const noexcept -> std::uint32_t {
auto r = static_cast<std::uint32_t>(this->r);
auto b = static_cast<std::uint32_t>(this->b);
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 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 <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(
static_cast<pixel_t>(tr / d),
static_cast<pixel_t>(tg / d),
static_cast<pixel_t>(tb / d),
a()
);
}
template <typename T>
requires std::is_arithmetic_v<T>
constexpr auto operator*(T val) const noexcept {
auto d = static_cast<float>(val);
return RGBA(
to_pixel(nr),
to_pixel(ng),
to_pixel(nb),
alpha
static_cast<pixel_t>(r() * d),
static_cast<pixel_t>(g() * d),
static_cast<pixel_t>(b() * d),
a()
);
}
private:
@ -130,118 +146,253 @@ namespace amt {
}
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 {
// (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 <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 {
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 <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(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<float>(static_cast<int>(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<std::uint8_t>((a / max) * 255);
static constexpr auto to_pixel(float a) noexcept -> RGBA::pixel_t {
return static_cast<RGBA::pixel_t>(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 <PixelFormat F>
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
#endif // AMT_PIXEL_HPP

@ -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<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) :
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<size_t>(height), static_cast<std::size_t>(width)),
$width(static_cast<int>(width)),
$height(static_cast<int>(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;
}
}

@ -44,7 +44,7 @@ struct Raycaster {
std::vector<double> spriteDistance;
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 clear();
@ -65,4 +65,4 @@ struct Raycaster {
return ((y) * $width) + (x);
}
};
};

@ -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);

@ -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