From 03be0884a490ea24d40f4ed6d2d0655e950611f9 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Thu, 26 Feb 2026 23:43:30 -0500 Subject: [PATCH] Clean up the source tree before reorganizing it. --- .gitignore | 2 + .ttarpit.json | 20 + assets/sprites/bonfire.png | Bin 0 -> 28660 bytes assets/sprites/standing_anvil.png | Bin 0 -> 19844 bytes meson.build | 1 + scratchpad/a_star.cpp | 82 -- scratchpad/amt/main.cpp | 93 -- scratchpad/amt/matrix.hpp | 197 --- scratchpad/amt/pixel.hpp | 746 ----------- scratchpad/amt/raycaster.cpp | 417 ------ scratchpad/amt/raycaster.hpp | 73 -- scratchpad/amt/texture.cpp | 34 - scratchpad/amt/texture.hpp | 34 - scratchpad/amt/thread.hpp | 253 ---- scratchpad/corostate.cpp | 97 -- scratchpad/dbg.h | 39 - scratchpad/dnd_loot_2.cpp | 436 ------- scratchpad/dnd_loot_2.rl | 175 --- scratchpad/fenscaster.cpp | 359 ------ scratchpad/fenster/fenster.h | 373 ------ scratchpad/fenstertest.cpp | 32 - scratchpad/quickcg.cpp | 2010 ----------------------------- scratchpad/quickcg.h | 334 ----- scratchpad/raycaster.cpp | 187 --- scratchpad/raycaster_flat.cpp | 250 ---- scratchpad/raycaster_floor.cpp | 406 ------ scratchpad/raycaster_pixels.cpp | 228 ---- scratchpad/raycaster_sprites.cpp | 471 ------- scratchpad/raycaster_textured.cpp | 293 ----- scratchpad/sdlprog.cpp | 36 - scratchpad/timcaster.cpp | 553 -------- scripts/animation_thing.ps1 | 1 + 32 files changed, 24 insertions(+), 8208 deletions(-) create mode 100644 .ttarpit.json create mode 100644 assets/sprites/bonfire.png create mode 100644 assets/sprites/standing_anvil.png delete mode 100644 scratchpad/a_star.cpp delete mode 100644 scratchpad/amt/main.cpp delete mode 100644 scratchpad/amt/matrix.hpp delete mode 100644 scratchpad/amt/pixel.hpp delete mode 100644 scratchpad/amt/raycaster.cpp delete mode 100644 scratchpad/amt/raycaster.hpp delete mode 100644 scratchpad/amt/texture.cpp delete mode 100644 scratchpad/amt/texture.hpp delete mode 100644 scratchpad/amt/thread.hpp delete mode 100644 scratchpad/corostate.cpp delete mode 100644 scratchpad/dbg.h delete mode 100644 scratchpad/dnd_loot_2.cpp delete mode 100644 scratchpad/dnd_loot_2.rl delete mode 100644 scratchpad/fenscaster.cpp delete mode 100644 scratchpad/fenster/fenster.h delete mode 100644 scratchpad/fenstertest.cpp delete mode 100644 scratchpad/quickcg.cpp delete mode 100644 scratchpad/quickcg.h delete mode 100644 scratchpad/raycaster.cpp delete mode 100644 scratchpad/raycaster_flat.cpp delete mode 100644 scratchpad/raycaster_floor.cpp delete mode 100644 scratchpad/raycaster_pixels.cpp delete mode 100644 scratchpad/raycaster_sprites.cpp delete mode 100644 scratchpad/raycaster_textured.cpp delete mode 100644 scratchpad/sdlprog.cpp delete mode 100644 scratchpad/timcaster.cpp create mode 100644 scripts/animation_thing.ps1 diff --git a/.gitignore b/.gitignore index 9509980..2391fca 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ coverage/* *.log gdb.txt releases +WORK +tmp diff --git a/.ttarpit.json b/.ttarpit.json new file mode 100644 index 0000000..a08ca11 --- /dev/null +++ b/.ttarpit.json @@ -0,0 +1,20 @@ +{ + "StartingHP": 100, + "Deadline": "1h0m0s", + "OneErrorPerLine": true, + "Includes": [ + "^.*.cpp$", + "^.*.hpp$", + "^.*.json$" + ], + "Processes": { + "tester1": { + "Command": "make", + "Args": ["build"] + } + }, + "Triggers": [ + "^(?.*?):(?[0-9]+):(?[0-9]+):\\s*(?.*):\\s*(?.*)", + "^(?.*?):(?[0-9]+):(?[0-9]+):\\s*(?.*)" + ] +} diff --git a/assets/sprites/bonfire.png b/assets/sprites/bonfire.png new file mode 100644 index 0000000000000000000000000000000000000000..4ee9b2f3f20bcb06a54fcc0f663d6317322cf61f GIT binary patch literal 28660 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 D6c-~N literal 0 HcmV?d00001 diff --git a/assets/sprites/standing_anvil.png b/assets/sprites/standing_anvil.png new file mode 100644 index 0000000000000000000000000000000000000000..b52d5bd89141d7a0915ac85f11a2ac0e35ebece2 GIT binary patch literal 19844 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 ov@|yT4es)P::max() - -using AStarPath = std::deque; - -void update_map(Matrix& map, std::deque& total_path) { - for(auto &point : total_path) { - map[point.y][point.x] = 10; - } -} - -AStarPath reconstruct_path(std::unordered_map& came_from, Point current) { - std::deque total_path{current}; - - while(came_from.contains(current)) { - current = came_from[current]; - total_path.push_front(current); - } - - return total_path; -} - -inline float h(Point from, Point to) { - return std::hypot(float(from.x) - float(to.x), - float(from.y) - float(to.y)); -} - -inline float d(Point current, Point neighbor) { - return std::hypot(float(current.x) - float(neighbor.x), - float(current.y) - float(neighbor.y)); -} - -inline Point find_lowest(std::unordered_map& open_set) { - dbc::check(!open_set.empty(), "open set can't be empty in find_lowest"); - Point result; - float lowest_score = SCORE_MAX; - - for(auto [point, score] : open_set) { - if(score < lowest_score) { - lowest_score = score; - result = point; - } - } - - return result; -} - - -std::optional path_to_player(Matrix& map, Point start, Point goal) { - std::unordered_map open_set; - std::unordered_map came_from; - std::unordered_map g_score; - g_score[start] = 0; - - open_set[start] = g_score[start] + h(start, goal); - - while(!open_set.empty()) { - auto current = find_lowest(open_set); - - if(current == goal) { - return std::make_optional(reconstruct_path(came_from, current)); - } - - open_set.erase(current); - - for(matrix::compass it{map, current.x, current.y}; it.next();) { - Point neighbor{it.x, it.y}; - - float d_score = d(current, neighbor) + map[it.y][it.x] * SCORE_MAX; - float tentative_g_score = g_score[current] + d_score; - float neighbor_g_score = g_score.contains(neighbor) ? g_score[neighbor] : SCORE_MAX; - if(tentative_g_score < neighbor_g_score) { - came_from[neighbor] = current; - g_score[neighbor] = tentative_g_score; - // open_set gets the fScore - open_set[neighbor] = tentative_g_score + h(neighbor, goal); - } - } - } - - return std::nullopt; -} diff --git a/scratchpad/amt/main.cpp b/scratchpad/amt/main.cpp deleted file mode 100644 index 6ed7227..0000000 --- a/scratchpad/amt/main.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "amt/raycaster.hpp" -#include -#include -#include -#include -#include "constants.hpp" -#include "stats.hpp" - -Matrix MAP{ - {1,1,1,1,1,1,1,1,1}, - {1,0,2,0,0,0,0,0,1}, - {1,0,4,0,0,5,2,0,1}, - {1,0,0,0,0,0,0,0,1}, - {1,1,0,0,0,0,0,1,1}, - {1,0,0,1,3,4,0,0,1}, - {1,0,0,0,0,0,1,1,1}, - {1,0,0,0,0,0,0,0,1}, - {1,1,1,1,1,1,1,1,1} -}; - -void draw_gui(sf::RenderWindow &window, sf::Text &text, Stats &stats) { - sf::RectangleShape rect({SCREEN_WIDTH - RAY_VIEW_WIDTH, SCREEN_HEIGHT}); - - rect.setPosition({0,0}); - rect.setFillColor({50, 50, 50}); - window.draw(rect); - - text.setString( - fmt::format("FPS\nmean:{:>8.5}\nsdev: {:>8.5}\nmin: {:>8.5}\nmax: {:>8.5}\ncount:{:<10}\n\nVSync? {}\nDebug? {}\n\nHit R to reset.", - stats.mean(), stats.stddev(), stats.min, stats.max, stats.n, VSYNC, DEBUG_BUILD)); - window.draw(text); -} - -int main() { - sf::RenderWindow window(sf::VideoMode({SCREEN_WIDTH, SCREEN_HEIGHT}), "Zed's Ray Caster Game Thing"); - - sf::Font font{"./assets/text.otf"}; - sf::Text text{font}; - text.setFillColor({255,255,255}); - text.setPosition({10,10}); - - //ZED this should set with a function - float player_x = MAP.rows() / 2; - float player_y = MAP.cols() / 2; - - Raycaster rayview(window, MAP, RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT); - rayview.set_position(RAY_VIEW_X, RAY_VIEW_Y); - rayview.position_camera(player_x, player_y); - - double moveSpeed = 0.1; - double rotSpeed = 0.1; - - const auto onClose = [&window](const sf::Event::Closed&) - { - window.close(); - }; - - Stats stats; - - // NOTE: Enable this to lock frames at 60 - window.setFramerateLimit(60); - - while(window.isOpen()) { - auto start = std::chrono::high_resolution_clock::now(); - rayview.render(); - auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration(end - start); - stats.sample(1/elapsed.count()); - - draw_gui(window, text, stats); - window.display(); - - if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)) { - rayview.run(moveSpeed, 1); - } else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S)) { - rayview.run(moveSpeed, -1); - } - - if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::D)) { - rayview.rotate(rotSpeed, -1); - } else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::A)) { - rayview.rotate(rotSpeed, 1); - } - - if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key::R)) { - stats.reset(); - } - - window.handleEvents(onClose); - } - - return 0; -} diff --git a/scratchpad/amt/matrix.hpp b/scratchpad/amt/matrix.hpp deleted file mode 100644 index 7309b94..0000000 --- a/scratchpad/amt/matrix.hpp +++ /dev/null @@ -1,197 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace amt { - - namespace detail { - [[nodiscard]] constexpr auto cal_index( - std::size_t r, - std::size_t c, - [[maybe_unused]] std::size_t rs, - [[maybe_unused]] std::size_t cs - ) -> std::size_t { - return r * cs + c; - } - } - - template - struct Matrix { - using value_type = T; - using pointer = value_type*; - using const_pointer = value_type const*; - using reference = value_type&; - using const_reference = value_type const&; - using iterator = pointer; - using const_iterator = const_pointer; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using difference_type = std::ptrdiff_t; - using size_type = std::size_t; - - template - struct View { - using base_type = std::conditional_t; - base_type data; - size_type r; - size_type rows; - size_type cols; - - constexpr reference operator[](size_type c) noexcept requires (!IsConst) { - assert(c < cols && "Out of bound access"); - auto const index = detail::cal_index(r, c, rows, cols); - return data[index]; - } - - constexpr const_reference operator[](size_type c) const noexcept { - assert(c < cols && "Out of bound access"); - auto const index = detail::cal_index(r, c, rows, cols); - return data[index]; - } - }; - - constexpr Matrix() noexcept = default; - Matrix(Matrix const& other) - : Matrix(other.rows(), other.cols()) - { - std::copy(other.begin(), other.end(), begin()); - } - Matrix& operator=(Matrix const& other) { - if (this == &other) return *this; - auto temp = Matrix(other); - swap(temp, *this); - return *this; - } - constexpr Matrix(Matrix && other) noexcept - : m_data(other.m_data) - , m_row(other.m_row) - , m_col(other.m_col) - { - other.m_data = nullptr; - } - constexpr Matrix& operator=(Matrix && other) noexcept { - if (this == &other) return *this; - swap(*this, other); - return *this; - } - ~Matrix() { - if (m_data) delete[] m_data; - } - - - Matrix(size_type row, size_type col) - : m_data(new value_type[row * col]) - , m_row(row) - , m_col(col) - {} - - Matrix(size_type row, size_type col, value_type def) - : Matrix(row, col) - { - std::fill(begin(), end(), def); - } - - Matrix(std::initializer_list> li) - : m_row(li.size()) - { - for (auto const& row: li) { - m_col = std::max(m_col, row.size()); - } - - auto const size = m_row * m_col; - - if (size == 0) return; - - m_data = new value_type[size]; - std::fill_n(m_data, size, 0); - - for (auto r = 0ul; auto const& row: li) { - for (auto c = 0ul; auto const& col: row) { - this->operator()(r, c++) = col; - } - ++r; - } - } - - constexpr bool empty() const noexcept { return size() == 0; } - constexpr size_type size() const noexcept { return rows() * cols(); } - constexpr size_type rows() const noexcept { return m_row; } - constexpr size_type cols() const noexcept { return m_col; } - constexpr auto data() noexcept -> pointer { return m_data; } - constexpr auto data() const noexcept -> const_pointer { return m_data; } - - constexpr iterator begin() noexcept { return m_data; } - constexpr iterator end() noexcept { return m_data + size(); } - constexpr const_iterator begin() const noexcept { return m_data; } - constexpr const_iterator end() const noexcept { return m_data + size(); } - constexpr reverse_iterator rbegin() noexcept { return std::reverse_iterator(end()); } - constexpr reverse_iterator rend() noexcept { return std::reverse_iterator(begin()); } - constexpr const_reverse_iterator rbegin() const noexcept { return std::reverse_iterator(end()); } - constexpr const_reverse_iterator rend() const noexcept { return std::reverse_iterator(begin()); } - - constexpr auto operator()(size_type r, size_type c) noexcept -> reference { - auto const index = detail::cal_index(r, c, rows(), cols()); - assert(index < size() && "Out of bound access"); - return m_data[index]; - } - - constexpr auto operator()(size_type r, size_type c) const noexcept -> const_reference { - auto const index = detail::cal_index(r, c, rows(), cols()); - assert(index < size() && "Out of bound access"); - return m_data[index]; - } - - constexpr auto operator[](size_type r) noexcept -> View { - assert(r < rows() && "Out of bound access"); - return { .data = m_data, .r = r, .rows = m_row, .cols = m_col }; - } - - constexpr auto operator[](size_type r) const noexcept -> View { - assert(r < rows() && "Out of bound access"); - return { .data = m_data, .r = r, .rows = m_row, .cols = m_col }; - } - - friend void swap(Matrix& lhs, Matrix& rhs) noexcept { - using std::swap; - swap(lhs.m_data, rhs.m_data); - swap(lhs.m_row, rhs.m_row); - swap(lhs.m_col, rhs.m_col); - } - - private: - pointer m_data{}; - size_type m_row{}; - size_type m_col{}; - }; - -} // namespace amt - -#if 0 -#include -namespace std { - template - struct formatter> { - constexpr auto parse(format_parse_context& ctx) { - return ctx.begin(); - } - - auto format(amt::Matrix const& m, auto& ctx) const { - std::string s = "[\n"; - for (auto r = std::size_t{}; r < m.rows(); ++r) { - for (auto c = std::size_t{}; c < m.cols(); ++c) { - s += std::format("{}, ", m(r, c)); - } - s += '\n'; - } - s += "]"; - return format_to(ctx.out(), "{}", s); - } - }; - -} // namespace std -#endif diff --git a/scratchpad/amt/pixel.hpp b/scratchpad/amt/pixel.hpp deleted file mode 100644 index 13bba72..0000000 --- a/scratchpad/amt/pixel.hpp +++ /dev/null @@ -1,746 +0,0 @@ -#ifndef AMT_PIXEL_HPP -#define AMT_PIXEL_HPP - -#include "matrix.hpp" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace amt { - - enum class PixelFormat { - rgba, - abgr, - rgb , - bgr , - ga , // gray scale and alpha - ag , // alpha and gray scale - g // gray scale - }; - - inline static constexpr auto get_pixel_format_from_channel(std::size_t c, bool little_endian = false) -> PixelFormat { - switch (c) { - case 1: return PixelFormat::g; - case 2: return little_endian ? PixelFormat::ag : PixelFormat::ga; - case 3: return little_endian ? PixelFormat::bgr : PixelFormat::rgb; - case 4: return little_endian ? PixelFormat::abgr : PixelFormat::abgr; - } - throw std::runtime_error(std::string("get_pixel_format_from_channel: unknown channel ") + std::to_string(c)); - } - - namespace detail { - static constexpr auto compare_float(float l, float r) noexcept -> bool { - return std::abs(l - r) < std::numeric_limits::epsilon(); - } - } // namespace detail - - enum class BlendMode { - normal, - multiply, - screen, - overlay, - darken, - lighten, - colorDodge, - colorBurn, - hardLight, - softLight, - difference, - exclusion - }; - - struct RGBA { - using pixel_t = std::uint8_t; - using pixels_t = std::uint32_t; - - constexpr RGBA() noexcept = default; - constexpr RGBA(RGBA const&) noexcept = default; - constexpr RGBA(RGBA &&) noexcept = default; - constexpr RGBA& operator=(RGBA const&) noexcept = default; - constexpr RGBA& operator=(RGBA &&) noexcept = default; - constexpr ~RGBA() noexcept = default; - - - // NOTE: Accepts RRGGBBAA - explicit constexpr RGBA(pixels_t color) noexcept - : RGBA((color >> (8 * 3)) & 0xff, (color >> (8 * 2)) & 0xff, (color >> (8 * 1)) & 0xff, (color >> (8 * 0)) & 0xff) - {} - - constexpr RGBA(pixel_t r, pixel_t g, pixel_t b, pixel_t a = 0xff) noexcept - : m_data {r, g, b, a} - {} - - constexpr RGBA(pixel_t color, pixel_t a = 0xff) noexcept - : RGBA(color, color, color, a) - {} - - // NOTE: Returns RRGGBBAA - constexpr auto to_hex() const noexcept -> pixels_t { - auto r = static_cast(this->r()); - auto b = static_cast(this->b()); - auto g = static_cast(this->g()); - auto a = static_cast(this->a()); - return (r << (8 * 3)) | (g << (8 * 2)) | (b << (8 * 1)) | (a << (8 * 0)); - } - - constexpr auto r() const noexcept -> pixel_t { return m_data[0]; } - constexpr auto g() const noexcept -> pixel_t { return m_data[1]; } - constexpr auto b() const noexcept -> pixel_t { return m_data[2]; } - constexpr auto a() const noexcept -> pixel_t { return m_data[3]; } - - constexpr auto r() noexcept -> pixel_t& { return m_data[0]; } - constexpr auto g() noexcept -> pixel_t& { return m_data[1]; } - constexpr auto b() noexcept -> pixel_t& { return m_data[2]; } - constexpr auto a() noexcept -> pixel_t& { return m_data[3]; } - - /** - * @returns the value is between 0 and 1 - */ - constexpr auto brightness() const noexcept -> float { - // 0.299*R + 0.587*G + 0.114*B - auto tr = normalize(r()); - auto tg = normalize(g()); - auto tb = normalize(b()); - return (0.299 * tr + 0.587 * tg + 0.114 * tb); - } - - template - requires std::is_arithmetic_v - constexpr auto operator/(T val) const noexcept { - auto d = static_cast(val); - return RGBA( - static_cast(r() / d), - static_cast(g() / d), - static_cast(b() / d), - a() - ); - } - - template - requires std::is_arithmetic_v - constexpr auto operator*(T val) const noexcept { - auto d = static_cast(val); - return RGBA( - static_cast(r() * d), - static_cast(g() * d), - static_cast(b() * d), - a() - ); - } - private: - static constexpr auto normalize(pixel_t p) noexcept -> float { - return float(p) / 255; - } - - static constexpr auto to_pixel(float p) noexcept -> pixel_t { - return static_cast(p * 255); - } - - template - static constexpr auto blend_helper() noexcept { - constexpr auto mix_helper = [](float s, float b, float a) -> float { - // (1 - αb) x Cs + αb x B(Cb, Cs) - return (1 - a) * s + a * b; - }; - - if constexpr (M == BlendMode::normal) { - return [mix_helper](float bg, float fg, float alpha) { return mix_helper(bg, fg, alpha); }; - } else if constexpr (M == BlendMode::multiply) { - return [mix_helper](float bg, float fg, float alpha) { return mix_helper(bg, bg * fg, alpha); }; - } else if constexpr (M == BlendMode::screen) { - return [mix_helper](float bg, float fg, float alpha) { - // Cb + Cs -(Cb x Cs) - auto bf = bg + fg - (bg * fg); - return mix_helper(bg, bf, alpha); - }; - } else if constexpr (M == BlendMode::overlay) { - return [mix_helper](float bg, float fg, float alpha, auto&& hard_light_fn, auto&& multiply_fn, auto&& screen_fn) { - // HardLight(Cs, Cb) - auto hl = hard_light_fn(bg, fg, alpha, multiply_fn, screen_fn); - return mix_helper(bg, hl, alpha); - }; - } else if constexpr (M == BlendMode::darken) { - return [mix_helper](float bg, float fg, float alpha) { - return mix_helper(bg, std::min(bg, fg), alpha); - }; - } else if constexpr (M == BlendMode::lighten) { - return [mix_helper](float bg, float fg, float alpha) { - return mix_helper(bg, std::max(bg, fg), alpha); - }; - } else if constexpr (M == BlendMode::colorDodge) { - return [mix_helper](float bg, float fg, float alpha) { - constexpr auto fn = [](float b, float s) -> float { - if (b == 0) return 0; - if (s == 255) return 255; - return std::min(1.f, b / (1.f - s)); - }; - auto bf = fn(bg, fg); - return mix_helper(bg, bf, alpha); - }; - } else if constexpr (M == BlendMode::colorBurn) { - return [mix_helper](float bg, float fg, float alpha) { - constexpr auto fn = [](float b, float s) -> float { - if (b == 255) return 255; - if (s == 0) return 0; - return 1.f - std::min(1.f, (1.f - b) / s); - }; - auto bf = fn(bg, fg); - return mix_helper(bg, bf, alpha); - }; - } else if constexpr (M == BlendMode::hardLight) { - return [mix_helper](float bg, float fg, float alpha, auto&& multiply_fn, auto&& screen_fn) { - auto fn = [&multiply_fn, &screen_fn](float b, float s, float a) -> float { - if (s <= 0.5f) { - return multiply_fn(b, 2.f * s, a); - } else { - return screen_fn(b, 2.f * s - 1.f, a); - } - }; - - auto bf = fn(bg, fg, alpha); - return mix_helper(bg, bf, alpha); - }; - } else if constexpr (M == BlendMode::softLight) { - return [mix_helper](float bg, float fg, float alpha) { - constexpr auto fn = [](float b, float s) -> float { - if (s <= 0.5f) { - // B(Cb, Cs) = Cb - (1 - 2 x Cs) x Cb x (1 - Cb) - return b - (1.f - 2.f * s) * b * (1 - b); - } else { - float d{}; - - if (b <= 0.5f) { - // D(Cb) = ((16 * Cb - 12) x Cb + 4) x Cb - d = ((16 * b - 12) * b + 4) * b; - } else { - // D(Cb) = sqrt(Cb) - d = std::sqrt(b); - } - - // B(Cb, Cs) = Cb + (2 x Cs - 1) x (D(Cb) - Cb) - return b + (2 * s - 1) * (d - b); - } - }; - - auto bf = fn(bg, fg); - return mix_helper(bg, bf, alpha); - }; - } else if constexpr (M == BlendMode::difference) { - return [mix_helper](float bg, float fg, float alpha) { - // B(Cb, Cs) = | Cb - Cs | - return mix_helper(bg, (bg > fg ? (bg - fg) : (fg - bg)), alpha); - }; - } else if constexpr (M == BlendMode::exclusion) { - return [mix_helper](float bg, float fg, float alpha) { - constexpr auto fn = [](float b, float s) -> float { - // B(Cb, Cs) = Cb + Cs - 2 x Cb x Cs - return b + s - 2 * b * s; - }; - - auto bf = fn(bg, fg); - return mix_helper(bg, bf, alpha); - }; - } - }; - - public: - template - constexpr auto blend(RGBA color) const noexcept -> RGBA { - auto ab = normalize(a()); - auto as = normalize(color.a()); - // αs x 1 + αb x (1 – αs) - auto alpha = to_pixel(as + ab * (1 - as)); - auto lr = normalize(r()); - auto lg = normalize(g()); - auto lb = normalize(b()); - auto rr = normalize(color.r()); - auto rg = normalize(color.g()); - auto rb = normalize(color.b()); - - auto nr = 0.f; - auto ng = 0.f; - auto nb = 0.f; - - if constexpr (M == BlendMode::normal) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::multiply) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::screen) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::overlay) { - auto fn = blend_helper(); - auto hard_light_fn = blend_helper(); - auto multiply_fn = blend_helper(); - auto screen_fn = blend_helper(); - nr = fn(lr, rr, ab, hard_light_fn, multiply_fn, screen_fn); - ng = fn(lg, rg, ab, hard_light_fn, multiply_fn, screen_fn); - nb = fn(lb, rb, ab, hard_light_fn, multiply_fn, screen_fn); - } else if constexpr (M == BlendMode::darken) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::lighten) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::colorDodge) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::colorBurn) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::hardLight) { - auto fn = blend_helper(); - auto multiply_fn = blend_helper(); - auto screen_fn = blend_helper(); - nr = fn(lr, rr, ab, multiply_fn, screen_fn); - ng = fn(lg, rg, ab, multiply_fn, screen_fn); - nb = fn(lb, rb, ab, multiply_fn, screen_fn); - } else if constexpr (M == BlendMode::softLight) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::difference) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } else if constexpr (M == BlendMode::exclusion) { - auto fn = blend_helper(); - nr = fn(lr, rr, ab); - ng = fn(lg, rg, ab); - nb = fn(lb, rb, ab); - } - - return RGBA( - to_pixel(nr), - to_pixel(ng), - to_pixel(nb), - alpha - ); - } - - constexpr auto blend(RGBA color, BlendMode mode) const noexcept -> RGBA { - switch (mode) { - case BlendMode::normal: return blend(color); - case BlendMode::multiply: return blend(color); - case BlendMode::screen: return blend(color); - case BlendMode::overlay: return blend(color); - case BlendMode::darken: return blend(color); - case BlendMode::lighten: return blend(color); - case BlendMode::colorDodge: return blend(color); - case BlendMode::colorBurn: return blend(color); - case BlendMode::hardLight: return blend(color); - case BlendMode::softLight: return blend(color); - case BlendMode::difference: return blend(color); - case BlendMode::exclusion: return blend(color); - } - } - private: - pixel_t m_data[4]{}; - }; - - struct HSLA { - using pixel_t = float; - using pixels_t = float[4]; - - // ensures pixel to be in range - template - struct PixelWrapper { - pixel_t& p; - - constexpr PixelWrapper& operator=(float val) noexcept { - p = std::clamp(val, float(min), float(max)); - } - - constexpr operator pixel_t() const noexcept { return p; } - }; - - constexpr HSLA() noexcept = default; - constexpr HSLA(HSLA const&) noexcept = default; - constexpr HSLA(HSLA &&) noexcept = default; - constexpr HSLA& operator=(HSLA const&) noexcept = default; - constexpr HSLA& operator=(HSLA &&) noexcept = default; - constexpr ~HSLA() noexcept = default; - - constexpr HSLA(pixel_t h, pixel_t s, pixel_t l, pixel_t a = 100) noexcept - : m_data({ .hsla = { .h = h, .s = s, .l = l, .a = a } }) - {} - - constexpr HSLA(RGBA color) noexcept { - auto min = std::min({color.r(), color.g(), color.b()}); - auto max = std::max({color.r(), color.g(), color.b()}); - auto c = (max - min) / 255.f; - - auto tr = float(color.r()) / 255; - auto tg = float(color.g()) / 255; - auto tb = float(color.b()) / 255; - auto ta = float(color.a()) / 255; - - float hue = 0; - float s = 0; - auto l = ((max + min) / 2.f) / 255.f; - - if (min == max) { - if (max == color.r()) { - auto seg = (tg - tb) / c; - auto shift = (seg < 0 ? 360.f : 0.f) / 60.f; - hue = seg + shift; - } else if (max == color.g()) { - auto seg = (tb - tr) / c; - auto shift = 120.f / 60.f; - hue = seg + shift; - } else { - auto seg = (tr - tg) / c; - auto shift = 240.f / 60.f; - hue = seg + shift; - } - s = c / (1 - std::abs(2 * l - 1)); - } - - hue = hue * 60.f + 360.f; - auto q = static_cast(static_cast(hue / 360.f)); - hue -= q * 360.f; - - m_data.hsla.h = hue; - m_data.hsla.s = s * 100.f; - m_data.hsla.l = l * 100.f; - m_data.hsla.a = ta * 100.f; - } - - constexpr operator RGBA() const noexcept { - auto ts = s() / 100.f; - auto tl = l() / 100.f; - auto ta = a() / 100.f; - if (s() == 0) return RGBA(to_pixel(tl), to_pixel(ta)); - - auto th = h() / 360.f; - - float const q = tl < 0.5 ? tl * (1 + ts) : tl + ts - tl * ts; - float const p = 2 * tl - q; - - return RGBA( - to_pixel(convert_hue(p, q, th + 1.f / 3)), - to_pixel(convert_hue(p, q, th)), - to_pixel(convert_hue(p, q, th - 1.f / 3)), - to_pixel(ta) - ); - } - - constexpr auto blend(HSLA color, BlendMode mode) const noexcept -> HSLA { - auto lhs = RGBA(*this); - auto rhs = RGBA(color); - return HSLA(lhs.blend(rhs, mode)); - } - - constexpr auto h() const noexcept -> pixel_t { return m_data.hsla.h; } - constexpr auto s() const noexcept -> pixel_t { return m_data.hsla.s; } - constexpr auto l() const noexcept -> pixel_t { return m_data.hsla.l; } - constexpr auto a() const noexcept -> pixel_t { return m_data.hsla.a; } - - constexpr auto h() noexcept -> PixelWrapper<0, 360> { return { m_data.hsla.h }; } - constexpr auto s() noexcept -> PixelWrapper<0, 100> { return { m_data.hsla.s }; } - constexpr auto l() noexcept -> PixelWrapper<0, 100> { return { m_data.hsla.l }; } - constexpr auto a() noexcept -> PixelWrapper<0, 100> { return { m_data.hsla.a }; } - private: - - static constexpr auto to_pixel(float a) noexcept -> RGBA::pixel_t { - return static_cast(a * 255); - } - - static constexpr auto convert_hue(float p, float q, float t) noexcept -> float { - t = t - (t > 1) + (t < 0); - if (t * 6 < 1) return p + (q - p) * 6 * t; - if (t * 2 < 1) return q; - if (t * 3 < 2) return p + (q - p) * (2.f / 3 - t) * 6; - return p; - } - private: - union { - struct { - pixel_t h{}; // hue: 0-360 - pixel_t s{}; // saturation: 0-100% - pixel_t l{}; // lightness: 0-100% - pixel_t a{}; // alpha: 0-100% - } hsla; - pixels_t color; - } m_data{}; - }; - - namespace detail { - template - inline static constexpr auto parse_pixel_helper(RGBA color, std::uint8_t* out_ptr) noexcept { - if constexpr (F == PixelFormat::rgba) { - out_ptr[0] = color.r(); - out_ptr[1] = color.g(); - out_ptr[2] = color.b(); - out_ptr[3] = color.a(); - } else if constexpr (F == PixelFormat::abgr) { - out_ptr[0] = color.a(); - out_ptr[1] = color.b(); - out_ptr[2] = color.g(); - out_ptr[3] = color.r(); - } else if constexpr (F == PixelFormat::rgb) { - out_ptr[0] = color.r(); - out_ptr[1] = color.g(); - out_ptr[2] = color.b(); - } else if constexpr (F == PixelFormat::bgr) { - out_ptr[0] = color.b(); - out_ptr[1] = color.g(); - out_ptr[2] = color.r(); - } else if constexpr (F == PixelFormat::ga) { - out_ptr[0] = color.r(); - out_ptr[1] = color.a(); - } else if constexpr (F == PixelFormat::ag) { - out_ptr[0] = color.a(); - out_ptr[1] = color.r(); - } else { - out_ptr[0] = color.r(); - } - } - - template - inline static constexpr auto parse_pixel_helper(std::uint8_t const* in_ptr) noexcept -> RGBA { - if constexpr (F == PixelFormat::rgba) { - return { - in_ptr[0], - in_ptr[1], - in_ptr[2], - in_ptr[3] - }; - } else if constexpr (F == PixelFormat::abgr) { - return { - in_ptr[3], - in_ptr[2], - in_ptr[1], - in_ptr[0] - }; - } else if constexpr (F == PixelFormat::rgb) { - return { - in_ptr[0], - in_ptr[1], - in_ptr[2], - 0xff - }; - } else if constexpr (F == PixelFormat::bgr) { - return { - in_ptr[2], - in_ptr[1], - in_ptr[0], - 0xff - }; - } else if constexpr (F == PixelFormat::ga) { - return { - in_ptr[0], - in_ptr[1], - }; - } else if constexpr (F == PixelFormat::ag) { - return { - in_ptr[1], - in_ptr[0], - }; - } else { - return { in_ptr[0], 0xff }; - } - } - } // namespace detail - - inline static constexpr auto get_pixel_format_channel(PixelFormat format) noexcept -> std::size_t { - switch (format) { - case PixelFormat::rgba: case PixelFormat::abgr: return 4u; - case PixelFormat::rgb: case PixelFormat::bgr: return 3u; - case PixelFormat::ga: case PixelFormat::ag: return 2u; - case PixelFormat::g: return 1u; - } - assert(false && "unreachable"); - } - - struct PixelBuf { - private: - public: - using value_type = RGBA; - using base_type = Matrix; - using pointer = typename base_type::pointer; - using const_pointer = typename base_type::const_pointer; - using reference = typename base_type::reference; - using const_reference = typename base_type::const_reference; - using iterator = typename base_type::iterator; - using const_iterator = typename base_type::const_iterator; - using reverse_iterator = typename base_type::reverse_iterator; - using const_reverse_iterator = typename base_type::const_reverse_iterator; - using difference_type = typename base_type::difference_type; - using size_type = typename base_type::size_type; - - PixelBuf(size_type r, size_type c) - : m_data(r, c) - {} - PixelBuf(size_type r, size_type c, RGBA color) - : m_data(r, c, color) - {} - - PixelBuf(std::uint8_t const* in, size_type r, size_type c, PixelFormat format = PixelFormat::rgba) - : PixelBuf(r, c) - { - assert(in != nullptr); - - switch (format) { - case PixelFormat::rgba: from_helper(in, data(), size()); break; - case PixelFormat::abgr: from_helper(in, data(), size()); break; - case PixelFormat::rgb: from_helper(in, data(), size()); break; - case PixelFormat::bgr: from_helper(in, data(), size()); break; - case PixelFormat::ga: from_helper(in, data(), size()); break; - case PixelFormat::ag: from_helper(in, data(), size()); break; - case PixelFormat::g: from_helper(in, data(), size()); break; - } - } - - PixelBuf() noexcept = default; - PixelBuf(PixelBuf const&) = default; - PixelBuf(PixelBuf &&) noexcept = default; - PixelBuf& operator=(PixelBuf const&) = default; - PixelBuf& operator=(PixelBuf &&) noexcept = default; - ~PixelBuf() = default; - - constexpr auto size() const noexcept -> size_type { return m_data.size(); } - constexpr auto rows() const noexcept -> size_type { return m_data.rows(); } - constexpr auto cols() const noexcept -> size_type { return m_data.cols(); } - constexpr auto data() noexcept -> pointer { return m_data.data(); } - constexpr auto data() const noexcept -> const_pointer { return m_data.data(); } - auto to_raw_buf() noexcept -> std::uint8_t* { return reinterpret_cast(data()); } - auto to_raw_buf() const noexcept -> std::uint8_t const* { return reinterpret_cast(data()); } - constexpr auto raw_buf_size() const noexcept { return size() * sizeof(RGBA); } - - constexpr auto begin() noexcept -> iterator { return m_data.begin(); } - constexpr auto end() noexcept -> iterator { return m_data.end(); } - constexpr auto begin() const noexcept -> const_iterator { return m_data.begin(); } - constexpr auto end() const noexcept -> const_iterator { return m_data.end(); } - constexpr auto rbegin() noexcept -> reverse_iterator { return m_data.rbegin(); } - constexpr auto rend() noexcept -> reverse_iterator { return m_data.rend(); } - constexpr auto rbegin() const noexcept -> const_reverse_iterator { return m_data.rbegin(); } - constexpr auto rend() const noexcept -> const_reverse_iterator { return m_data.rend(); } - - constexpr decltype(auto) operator[](size_type r) noexcept { return m_data[r]; } - constexpr decltype(auto) operator[](size_type r) const noexcept { return m_data[r]; } - constexpr auto operator()(size_type r, size_type c) noexcept -> reference { return m_data(r, c); } - constexpr auto operator()(size_type r, size_type c) const noexcept -> const_reference { return m_data(r, c); } - - constexpr auto fill(RGBA color) noexcept -> void { - std::fill(begin(), end(), color); - } - - constexpr auto copy_to(std::uint8_t* out, PixelFormat format = PixelFormat::rgba) const noexcept { - assert(out != nullptr); - - switch (format) { - case PixelFormat::rgba: copy_to_helper(data(), out, size());return; - case PixelFormat::abgr: copy_to_helper(data(), out, size());return; - case PixelFormat::rgb: copy_to_helper(data(), out, size());return; - case PixelFormat::bgr: copy_to_helper(data(), out, size());return; - case PixelFormat::ga: copy_to_helper(data(), out, size());return; - case PixelFormat::ag: copy_to_helper(data(), out, size());return; - case PixelFormat::g: copy_to_helper(data(), out, size());return; - } - assert(false && "unreachable"); - } - - - private: - template - constexpr auto copy_to_helper(const_pointer in, std::uint8_t* out, size_type size) const noexcept -> void { - constexpr auto channels = get_pixel_format_channel(F); - for (auto i = size_type{}; i < size; ++i) { - detail::parse_pixel_helper(in[i], out + i * channels); - } - } - - template - constexpr auto from_helper(std::uint8_t const* in, pointer out, size_type size) const noexcept -> void { - constexpr auto channels = get_pixel_format_channel(F); - for (auto i = size_type{}; i < size; ++i) { - out[i] = detail::parse_pixel_helper(in + i * channels); - } - } - - private: - base_type m_data; - }; - - - -} // namespace amt - -#include -namespace std { - template <> - struct formatter { - constexpr auto parse(format_parse_context& ctx) { - return ctx.begin(); - } - - auto format(amt::RGBA const& color, auto& ctx) const { - return format_to(ctx.out(), "rgba({}, {}, {}, {})", color.r(), color.g(), color.b(), color.a()); - } - }; - - template <> - struct formatter { - constexpr auto parse(format_parse_context& ctx) { - return ctx.begin(); - } - - auto format(amt::HSLA const& color, auto& ctx) const { - return format_to(ctx.out(), "hsla({:.1f}deg, {:.1f}%, {:.1f}%, {:.1f}%)", color.h(), color.s(), color.l(), color.a()); - } - }; - - template <> - struct formatter { - bool hsla = false; - - constexpr auto parse(format_parse_context& ctx) { - auto it = ctx.begin(); - while (it != ctx.end() && *it != '}') { - if (*it == 'h') hsla = true; - ++it; - } - return it; - } - - auto format(amt::PixelBuf const& buf, auto& ctx) const { - std::string s = "[\n"; - for (auto r = std::size_t{}; r < buf.rows(); ++r) { - for (auto c = std::size_t{}; c < buf.cols(); ++c) { - auto color = buf(r, c); - if (hsla) s += std::format("{}, ", amt::HSLA(color)); - else s += std::format("{}, ", color); - } - s += '\n'; - } - s += "]"; - return format_to(ctx.out(), "{}", s); - } - }; -} // namespace std - -#endif // AMT_PIXEL_HPP diff --git a/scratchpad/amt/raycaster.cpp b/scratchpad/amt/raycaster.cpp deleted file mode 100644 index c440355..0000000 --- a/scratchpad/amt/raycaster.cpp +++ /dev/null @@ -1,417 +0,0 @@ -#include "amt/raycaster.hpp" -#include "amt/texture.hpp" -#include "amt/pixel.hpp" -#include "constants.hpp" -#include "thread.hpp" - -#define AMT_LIGHT - -using namespace fmt; - - -#ifdef AMT_LIGHT -static constexpr auto room_brightness = 0.3f; // increse this to increase the room brightness. Higher value means brighter room. - -inline static constexpr amt::RGBA dumb_lighting(amt::RGBA pixel, float distance, float distance_from_center) { - auto const dim_pixel = pixel * room_brightness; - if (distance_from_center >= 0) { - auto const min_brightness = 1.f / std::max(distance_from_center, 0.5f); // farther away from the center darker it gets - auto const max_brightness = 1.f; // brighness should not exceed 1 - auto const pixel_brightness = std::max(min_brightness, std::min(max_brightness, distance)); - - auto const yellow_brightness = float(distance_from_center * 60); - amt::RGBA const yellow = amt::HSLA(40, 20, yellow_brightness); - - auto temp = (pixel / pixel_brightness).blend(yellow); - return temp.brightness() < 0.1f ? dim_pixel : temp; - } else { - return dim_pixel; - } -} -#else -inline static constexpr amt::RGBA dumb_lighting(amt::RGBA pixel, double distance, double distance_from_center) { - (void)distance_from_center; - if(distance < 0.9) return pixel; - return pixel / distance; -} -#endif - - -Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map, unsigned width, unsigned height) : - view_texture(sf::Vector2u{width, height}), - view_sprite(view_texture), - $width(static_cast(width)), - $height(static_cast(height)), - pixels(height, width), - $window(window), - $map(map), - spriteOrder(textures.NUM_SPRITES), - spriteDistance(textures.NUM_SPRITES), - ZBuffer(width), - $radius(std::min($height, $width) / 2), - $r_sq($radius * $radius) -{ - $window.setVerticalSyncEnabled(VSYNC); - view_sprite.setPosition({0, 0}); - textures.load_textures(); -} - -void Raycaster::set_position(int x, int y) { - view_sprite.setPosition({(float)x, (float)y}); -} - -void Raycaster::position_camera(float player_x, float player_y) { - // x and y start position - posX = player_x; - posY = player_y; -} - -void Raycaster::draw_pixel_buffer() { - view_texture.update(pixels.to_raw_buf(), {(unsigned int)$width, (unsigned int)$height}, {0, 0}); - $window.draw(view_sprite); -} - -void Raycaster::clear() { - pixels.fill({}); - $window.clear(); -} - -void Raycaster::sprite_casting() { - // sort sprites from far to close - for(int i = 0; i < textures.NUM_SPRITES; i++) { - auto& sprite = textures.get_sprite(i); - spriteOrder[i] = i; - // this is just the distance calculation - spriteDistance[i] = ((posX - sprite.x) * - (posX - sprite.x) + - (posY - sprite.y) * - (posY - sprite.y)); - } - - sort_sprites(spriteOrder, spriteDistance, textures.NUM_SPRITES); - - /*for(int i = 0; i < textures.NUM_SPRITES; i++) {*/ - // after sorting the sprites, do the projection - // Be careful about capturing stack variables. - amt::parallel_for<1>(pool, 0, textures.NUM_SPRITES, [this, textureWidth = textures.TEXTURE_WIDTH, textureHeight = textures.TEXTURE_HEIGHT](size_t i){ - int sprite_index = spriteOrder[i]; - Sprite& sprite_rec = textures.get_sprite(sprite_index); - auto& sprite_texture = textures.get_texture(sprite_rec.texture); - - double spriteX = sprite_rec.x - posX; - double spriteY = sprite_rec.y - posY; - - //transform sprite with the inverse camera matrix - // [ planeX dirX ] -1 [ dirY -dirX ] - // [ ] = 1/(planeX*dirY-dirX*planeY) * [ ] - // [ planeY dirY ] [ -planeY planeX ] - - double invDet = 1.0 / (planeX * dirY - dirX * planeY); // required for correct matrix multiplication - - double transformX = invDet * (dirY * spriteX - dirX * spriteY); - //this is actually the depth inside the screen, that what Z is in 3D, the distance of sprite to player, matching sqrt(spriteDistance[i]) - - double transformY = invDet * (-planeY * spriteX + planeX * spriteY); - - int spriteScreenX = int(($width / 2) * (1 + transformX / transformY)); - - int vMoveScreen = int(sprite_rec.elevation * -1 / transformY); - - // calculate the height of the sprite on screen - //using "transformY" instead of the real distance prevents fisheye - int spriteHeight = abs(int($height / transformY)) / sprite_rec.vDiv; - - //calculate lowest and highest pixel to fill in current stripe - int drawStartY = -spriteHeight / 2 + $height / 2 + vMoveScreen; - if(drawStartY < 0) drawStartY = 0; - int drawEndY = spriteHeight / 2 + $height / 2 + vMoveScreen; - if(drawEndY >= $height) drawEndY = $height - 1; - - // calculate width the the sprite - // same as height of sprite, given that it's square - int spriteWidth = abs(int($height / transformY)) / sprite_rec.uDiv; - int drawStartX = -spriteWidth / 2 + spriteScreenX; - if(drawStartX < 0) drawStartX = 0; - int drawEndX = spriteWidth / 2 + spriteScreenX; - if(drawEndX > $width) drawEndX = $width; - - //loop through every vertical stripe of the sprite on screen - for(int stripe = drawStartX; stripe < drawEndX; stripe++) { - int texX = int(256 * (stripe - (-spriteWidth / 2 + spriteScreenX)) * textureWidth / spriteWidth) / 256; - // the conditions in the if are: - // 1) it's in front of the camera plane so you don't see things behind you - // 2) ZBuffer, with perpendicular distance - if (texX < 0) continue; - if(transformY > 0 && transformY < ZBuffer[stripe]) { - for(int y = drawStartY; y < drawEndY; y++) { - //256 and 128 factors to avoid floats - int d = (y - vMoveScreen) * 256 - $height * 128 + spriteHeight * 128; - int texY = ((d * textureHeight) / spriteHeight) / 256; - if ((size_t)texY >= sprite_texture.rows()) continue; - //get current color from the texture - auto color = sprite_texture[texY][texX]; - // poor person's transparency, get current color from the texture - if (!(color.to_hex() & 0xffffff00)) continue; - auto dist = get_distance_from_center(stripe, y); - pixels[y][stripe] = dumb_lighting(color, d, dist); - } - } - } - }); -} - -float Raycaster::get_distance_from_center(int x, int y) const noexcept { - float cx = $width / 2; - float cy = $height / 2; - auto dx = cx - x; - auto dy = cy - y; - return ($r_sq - dx * dx - dy * dy) / $r_sq; -} - -void Raycaster::cast_rays() { - - // WALL CASTING - /*for(int x = 0; x < $width; x++) {*/ - amt::parallel_for<32>(pool, 0, static_cast($width), [this](size_t x){ - double perpWallDist = 0; - // calculate ray position and direction - double cameraX = 2 * x / double($width) - 1; // x-coord in camera space - double rayDirX = dirX + planeX * cameraX; - double rayDirY = dirY + planeY * cameraX; - - // which box of the map we're in - int mapX = int(posX); - int mapY = int(posY); - - // length of ray from current pos to next x or y-side - double sideDistX; - double sideDistY; - - // length of ray from one x or y-side to next x or y-side - double deltaDistX = std::abs(1.0 / rayDirX); - double deltaDistY = std::abs(1.0 / rayDirY); - - int stepX = 0; - int stepY = 0; - int hit = 0; - int side = 0; - - // calculate step and initial sideDist - if(rayDirX < 0) { - stepX = -1; - sideDistX = (posX - mapX) * deltaDistX; - } else { - stepX = 1; - sideDistX = (mapX + 1.0 - posX) * deltaDistX; - } - - if(rayDirY < 0) { - stepY = -1; - sideDistY = (posY - mapY) * deltaDistY; - } else { - stepY = 1; - sideDistY = (mapY + 1.0 - posY) * deltaDistY; - } - - // perform DDA - while(hit == 0) { - if(sideDistX < sideDistY) { - sideDistX += deltaDistX; - mapX += stepX; - side = 0; - } else { - sideDistY += deltaDistY; - mapY += stepY; - side = 1; - } - - if($map[mapY][mapX] > 0) hit = 1; - } - - if(side == 0) { - perpWallDist = (sideDistX - deltaDistX); - } else { - perpWallDist = (sideDistY - deltaDistY); - } - - int lineHeight = int($height / perpWallDist); - - int drawStart = -lineHeight / 2 + $height / 2 + PITCH; - if(drawStart < 0) drawStart = 0; - - int drawEnd = lineHeight / 2 + $height / 2 + PITCH; - if(drawEnd >= $height) drawEnd = $height - 1; - - auto &texture = textures.get_texture($map[mapY][mapX] - 1); - - // calculate value of wallX - double wallX; // where exactly the wall was hit - if(side == 0) { - wallX = posY + perpWallDist * rayDirY; - } else { - wallX = posX + perpWallDist * rayDirX; - } - wallX -= floor((wallX)); - - // x coorindate on the texture - int texX = int(wallX * double(textures.TEXTURE_WIDTH)); - if(side == 0 && rayDirX > 0) texX = textures.TEXTURE_WIDTH - texX - 1; - if(side == 1 && rayDirY < 0) texX = textures.TEXTURE_WIDTH - texX - 1; - - // LODE: an integer-only bresenham or DDA like algorithm could make the texture coordinate stepping faster - - // How much to increase the texture coordinate per screen pixel - double step = 1.0 * textures.TEXTURE_HEIGHT / lineHeight; - // Starting texture coordinate - double texPos = (drawStart - PITCH - $height / 2 + lineHeight / 2) * step; - - for(int y = drawStart; y < drawEnd; y++) { - int texY = (int)texPos & (textures.TEXTURE_HEIGHT - 1); - texPos += step; - auto dist = get_distance_from_center(x, y); - auto color = dumb_lighting(texture[texY][texX], perpWallDist, dist); - pixels[y][x] = color; - } - - // SET THE ZBUFFER FOR THE SPRITE CASTING - ZBuffer[x] = perpWallDist; - }); -} - -void Raycaster::draw_ceiling_floor() { - - /*for(int y = $height / 2 + 1; y < $height; ++y) {*/ - - auto const h = static_cast($height); - amt::parallel_for<32>(pool, h / 2, h, [this, $height=h](size_t y){ - const size_t textureWidth = textures.TEXTURE_WIDTH; - const size_t textureHeight = textures.TEXTURE_HEIGHT; - // rayDir for leftmost ray (x=0) and rightmost (x = w) - float rayDirX0 = dirX - planeX; - float rayDirY0 = dirY - planeY; - float rayDirX1 = dirX + planeX; - float rayDirY1 = dirY + planeY; - - // current y position compared to the horizon - int p = y - $height / 2; - - // vertical position of the camera - // 0.5 will the camera at the center horizon. For a - // different value you need a separate loop for ceiling - // and floor since they're no longer symmetrical. - float posZ = 0.5 * $height; - - // horizontal distance from the camera to the floor for the current row - // 0.5 is the z position exactly in the middle between floor and ceiling - // See NOTE in Lode's code for more. - float rowDistance = posZ / p; - - // calculate the real world step vector we have to add for each x (parallel to camera plane) - // adding step by step avoids multiplications with a wight in the inner loop - float floorStepX = rowDistance * (rayDirX1 - rayDirX0) / $width; - float floorStepY = rowDistance * (rayDirY1 - rayDirY0) / $width; - - - // real world coordinates of the leftmost column. - // This will be updated as we step to the right - float floorX = posX + rowDistance * rayDirX0; - float floorY = posY + rowDistance * rayDirY0; - - for(int x = 0; x < $width; ++x) { - // the cell coord is simply taken from the int parts of - // floorX and floorY. - int cellX = int(floorX); - int cellY = int(floorY); - - // get the texture coordinat from the fractional part - int tx = int(textureWidth * (floorX - cellX)) & (textureWidth - 1); - int ty = int(textureWidth * (floorY - cellY)) & (textureHeight - 1); - - floorX += floorStepX; - floorY += floorStepY; - - // now get the pixel from the texture - // this uses the previous ty/tx fractional parts of - // floorX cellX to find the texture x/y. How? - - #ifdef AMT_LIGHT - // FLOOR - auto dist_floor = get_distance_from_center(x, y); - pixels[y][x] = dumb_lighting(textures.floor[ty][tx], p, dist_floor); - - // CEILING - auto dist_ceiling = get_distance_from_center(x, $height - y - 1); - pixels[$height - y - 1][x] = dumb_lighting(textures.ceiling[ty][tx], p, dist_ceiling); - #else - // FLOOR - pixels[y][x] = textures.floor[ty][tx]; - - // CEILING - pixels[$height - y - 1][x] = textures.ceiling[ty][tx]; - #endif - - } - }); - -} - -void Raycaster::render() { - draw_ceiling_floor(); - // This wait to prevent data-race - pool.wait(); // Try to remove this to see unbelievable performance - cast_rays(); - pool.wait(); // Try to remove this too - sprite_casting(); - pool.wait(); - draw_pixel_buffer(); -} - -bool Raycaster::empty_space(int new_x, int new_y) { - dbc::check((size_t)new_x < $map.cols(), - format("x={} too wide={}", new_x, $map.cols())); - dbc::check((size_t)new_y < $map.rows(), - format("y={} too high={}", new_y, $map.rows())); - - return $map[new_y][new_x] == 0; -} - - -void Raycaster::sort_sprites(std::vector& order, std::vector& dist, int amount) -{ - std::vector> sprites(amount); - - for(int i = 0; i < amount; i++) { - sprites[i].first = dist[i]; - sprites[i].second = order[i]; - } - - std::sort(sprites.begin(), sprites.end()); - - // restore in reverse order - for(int i = 0; i < amount; i++) { - dist[i] = sprites[amount - i - 1].first; - order[i] = sprites[amount - i - 1].second; - } -} - -void Raycaster::run(double speed, int dir) { - double speed_and_dir = speed * dir; - if(empty_space(int(posX + dirX * speed_and_dir), int(posY))) { - posX += dirX * speed_and_dir; - } - - if(empty_space(int(posX), int(posY + dirY * speed_and_dir))) { - posY += dirY * speed_and_dir; - } -} - -void Raycaster::rotate(double speed, int dir) { - double speed_and_dir = speed * dir; - double oldDirX = dirX; - dirX = dirX * cos(speed_and_dir) - dirY * sin(speed_and_dir); - dirY = oldDirX * sin(speed_and_dir) + dirY * cos(speed_and_dir); - - double oldPlaneX = planeX; - planeX = planeX * cos(speed_and_dir) - planeY * sin(speed_and_dir); - planeY = oldPlaneX * sin(speed_and_dir) + planeY * cos(speed_and_dir); -} diff --git a/scratchpad/amt/raycaster.hpp b/scratchpad/amt/raycaster.hpp deleted file mode 100644 index b497b0d..0000000 --- a/scratchpad/amt/raycaster.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include "matrix.hpp" -#include -#include -#include "dbc.hpp" -#include "amt/pixel.hpp" -#include "amt/texture.hpp" -#include -#include "thread.hpp" - -using Matrix = amt::Matrix; - -struct Raycaster { - int PITCH=0; - - TexturePack textures; - double posX = 0; - double posY = 0; - - // initial direction vector - double dirX = -1; - double dirY = 0; - - // the 2d raycaster version of camera plane - double planeX = 0; - double planeY = 0.66; - sf::Texture view_texture; - sf::Sprite view_sprite; - - //ZED: USE smart pointer for this - - int $width; - int $height; - amt::PixelBuf pixels; - sf::RenderWindow& $window; - Matrix& $map; - std::vector spriteOrder; - std::vector spriteDistance; - std::vector ZBuffer; // width - float $radius; // std::min($height, $width) / 2; - float $r_sq; // = radius * radius; - amt::thread_pool_t pool; - - Raycaster(sf::RenderWindow& window, Matrix &map, unsigned width, unsigned height); - - void draw_pixel_buffer(); - void clear(); - void cast_rays(); - void draw_ceiling_floor(); - void sprite_casting(); - void sort_sprites(std::vector& order, std::vector& dist, int amount); - void render(); - - bool empty_space(int new_x, int new_y); - - void run(double speed, int dir); - void rotate(double speed, int dir); - void position_camera(float player_x, float player_y); - float get_distance_from_center(int x, int y) const noexcept; - - void set_position(int x, int y); - inline size_t pixcoord(int x, int y) { - return ((y) * $width) + (x); - } - -}; diff --git a/scratchpad/amt/texture.cpp b/scratchpad/amt/texture.cpp deleted file mode 100644 index 2fe78cf..0000000 --- a/scratchpad/amt/texture.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include "dbc.hpp" -#include -#include "config.hpp" -#include "amt/texture.hpp" - -Image TexturePack::load_image(std::string filename) { - sf::Image img; - bool good = img.loadFromFile(filename); - dbc::check(good, format("failed to load {}", filename)); - return amt::PixelBuf(img.getPixelsPtr(), TEXTURE_HEIGHT, TEXTURE_WIDTH); -} - -void TexturePack::load_textures() { - Config assets("assets/config.json"); - for(string tile_path : assets["textures"]) { - images.emplace_back(load_image(tile_path)); - } - - for(string tile_path : assets["sprites"]) { - images.emplace_back(load_image(tile_path)); - } - - floor = load_image(assets["floor"]); - ceiling = load_image(assets["ceiling"]); -} - -Image& TexturePack::get_texture(size_t num) { - return images[num]; -} - -Sprite &TexturePack::get_sprite(size_t sprite_num) { - return sprites[sprite_num]; -} diff --git a/scratchpad/amt/texture.hpp b/scratchpad/amt/texture.hpp deleted file mode 100644 index 179fb36..0000000 --- a/scratchpad/amt/texture.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include -#include -#include -#include "amt/pixel.hpp" - -struct Sprite { - double x; - double y; - int texture; - // ZED: this should be a separate transform parameter - double elevation=0; - int uDiv=1; - int vDiv=1; -}; - -using Image = amt::PixelBuf; - -struct TexturePack { - int NUM_SPRITES=1; - static const int TEXTURE_WIDTH=256; // must be power of two - static const int TEXTURE_HEIGHT=256; // must be power of two - - std::vector images; - std::vector sprites{{4.0, 3.55, 6}}; - Image floor; - Image ceiling; - - void load_textures(); - amt::PixelBuf load_image(std::string filename); - Sprite& get_sprite(size_t sprite_num); - Image& get_texture(size_t num); -}; diff --git a/scratchpad/amt/thread.hpp b/scratchpad/amt/thread.hpp deleted file mode 100644 index 841199b..0000000 --- a/scratchpad/amt/thread.hpp +++ /dev/null @@ -1,253 +0,0 @@ -#ifndef AMT_THREAD_HPP -#define AMT_THREAD_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace amt { - - // NOTE: Could implement lock-free queue. - template - struct Queue { - using base_type = std::deque; - using value_type = typename base_type::value_type; - using pointer = typename base_type::pointer; - using const_pointer = typename base_type::const_pointer; - using reference = typename base_type::reference; - using const_reference = typename base_type::const_reference; - using iterator = typename base_type::iterator; - using const_iterator = typename base_type::const_iterator; - using reverse_iterator = typename base_type::reverse_iterator; - using const_reverse_iterator = typename base_type::const_reverse_iterator; - using difference_type = typename base_type::difference_type; - using size_type = typename base_type::size_type; - - constexpr Queue() noexcept = default; - constexpr Queue(Queue const&) noexcept = delete; - constexpr Queue(Queue &&) noexcept = default; - constexpr Queue& operator=(Queue const&) noexcept = delete; - constexpr Queue& operator=(Queue &&) noexcept = default; - constexpr ~Queue() noexcept = default; - - template - requires std::same_as, value_type> - void push(U&& u) { - std::lock_guard m(m_mutex); - m_data.push_back(std::forward(u)); - } - - template - void emplace(Args&&... args) { - std::lock_guard m(m_mutex); - m_data.emplace_back(std::forward(args)...); - } - - std::optional pop() { - std::lock_guard m(m_mutex); - if (empty_unsafe()) return std::nullopt; - auto el = std::move(m_data.front()); - m_data.pop_front(); - return std::move(el); - } - - auto size() const noexcept -> size_type { - std::lock_guard m(m_mutex); - return m_data.size(); - } - auto empty() const noexcept -> bool { - std::lock_guard m(m_mutex); - return m_data.empty(); - } - constexpr auto size_unsafe() const noexcept -> size_type { return m_data.size(); } - constexpr auto empty_unsafe() const noexcept -> bool { return m_data.empty(); } - - private: - base_type m_data; - mutable std::mutex m_mutex; - }; - - template - struct ThreadPool; - - template - struct Worker { - using parent_t = ThreadPool*; - using work_t = Fn; - using size_type = std::size_t; - constexpr Worker() noexcept = default; - constexpr Worker(Worker const&) noexcept = default; - constexpr Worker(Worker &&) noexcept = default; - constexpr Worker& operator=(Worker const&) noexcept = default; - constexpr Worker& operator=(Worker &&) noexcept = default; - ~Worker() { - stop(); - } - - void start(parent_t pool, size_type id) { - assert((m_running.load(std::memory_order::acquire) == false) && "Thread is already running"); - m_running.store(true); - m_parent.store(pool); - m_id = id; - m_thread = std::thread([this]() { - while (m_running.load(std::memory_order::relaxed)) { - std::unique_lock lk(m_mutex); - m_cv.wait(lk, [this] { - return !m_queue.empty_unsafe() || !m_running.load(std::memory_order::relaxed); - }); - auto item = pop_task(); - if (!item) { - item = try_steal(); - if (!item) continue; - } - - process_work(std::move(*item)); - } - }); - } - - void process_work(work_t&& work) const noexcept { - std::invoke(std::move(work)); - auto ptr = m_parent.load(); - if (ptr) ptr->task_completed(); - } - - void stop() { - if (!m_running.load()) return; - { - std::lock_guard lock(m_mutex); - m_running.store(false); - } - m_cv.notify_all(); - m_thread.join(); - m_parent.store(nullptr); - } - - void add(work_t&& work) { - std::lock_guard lock(m_mutex); - m_queue.push(std::move(work)); - m_cv.notify_one(); - } - - std::optional pop_task() noexcept { - return m_queue.pop(); - } - - - std::optional try_steal() noexcept { - auto ptr = m_parent.load(); - if (ptr) return ptr->try_steal(m_id); - return {}; - } - - constexpr bool empty() const noexcept { return m_queue.empty_unsafe(); } - constexpr size_type size() const noexcept { return m_queue.size_unsafe(); } - constexpr size_type id() const noexcept { return m_id; } - constexpr bool running() const noexcept { return m_running.load(std::memory_order::relaxed); } - - private: - Queue m_queue{}; - std::thread m_thread; - std::atomic m_running{false}; - std::mutex m_mutex{}; - std::condition_variable m_cv{}; - std::atomic m_parent{nullptr}; - size_type m_id; - }; - - template - struct ThreadPool { - using worker_t = Worker; - using work_t = typename worker_t::work_t; - using size_type = std::size_t; - - constexpr ThreadPool(ThreadPool const&) noexcept = delete; - constexpr ThreadPool(ThreadPool &&) noexcept = default; - constexpr ThreadPool& operator=(ThreadPool const&) noexcept = delete; - constexpr ThreadPool& operator=(ThreadPool &&) noexcept = default; - ~ThreadPool() { - stop(); - } - - ThreadPool(size_type n = std::thread::hardware_concurrency()) - : m_workers(std::max(n, size_type{1})) - { - for (auto i = 0ul; i < m_workers.size(); ++i) { - m_workers[i].start(this, i); - } - } - - void stop() { - for (auto& w: m_workers) w.stop(); - } - - void add(Fn&& work) { - m_active_tasks.fetch_add(1, std::memory_order::relaxed); - m_workers[m_last_added].add(std::move(work)); - m_last_added = (m_last_added + 1) % m_workers.size(); - } - - std::optional try_steal(size_type id) { - for (auto& w: m_workers) { - if (w.id() == id) continue; - auto item = w.pop_task(); - if (item) return item; - } - return {}; - } - - void task_completed() { - if (m_active_tasks.fetch_sub(1, std::memory_order::release) == 1) { - m_wait_cv.notify_all(); - } - } - - void wait() { - std::unique_lock lock(m_wait_mutex); - m_wait_cv.wait(lock, [this] { - return m_active_tasks.load(std::memory_order::acquire) == 0; - }); - } - - - private: - std::vector m_workers; - size_type m_last_added{}; - std::mutex m_wait_mutex; - std::condition_variable m_wait_cv; - std::atomic m_active_tasks{0}; - }; - - using thread_pool_t = ThreadPool>; - - // WARNING: Do not capture the stack variable if you're defering wait on pool. - // If you want to capture them, either capture them value or do "pool.wait()" at the end of the scope. - template - requires (std::is_invocable_v) - constexpr auto parallel_for(thread_pool_t& pool, std::size_t start, std::size_t end, Fn&& body) noexcept { - if (start >= end) return; - - auto const size = (end - start); - auto const chunk_size = std::max(size_t{1}, (size + Split - 1) / Split); - auto const num_chunks = (size + chunk_size - 1) / chunk_size; - - for (auto chunk = 0ul; chunk < num_chunks; ++chunk) { - auto const chunk_start = std::min(start + (chunk * chunk_size), end); - auto const chunk_end = std::min(chunk_start + (chunk_size), end); - pool.add([chunk_start, chunk_end, body] { - for (auto i = chunk_start; i < chunk_end; ++i) { - std::invoke(body, i); - } - }); - } - } -} // nsmespace amt - -#endif // AMT_THREAD_HPP diff --git a/scratchpad/corostate.cpp b/scratchpad/corostate.cpp deleted file mode 100644 index a11662c..0000000 --- a/scratchpad/corostate.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include -#include -#include - - -template -struct Generator { - struct promise_type; - using handle_type = std::coroutine_handle; - - struct promise_type { - T value_; - std::exception_ptr exception_; - - Generator get_return_object() { - return Generator(handle_type::from_promise(*this)); - } - std::suspend_always initial_suspend() { return {}; } - std::suspend_always final_suspend() noexcept { return {}; } - void unhandled_exception() { exception_ = std::current_exception(); } - - template From> - std::suspend_always yield_value(From&& from) { - value_ = std::forward(from); - return {}; - } - void return_void() {} - }; - - handle_type h_; - - Generator(handle_type h) : h_(h) {} - ~Generator() { h_.destroy(); } - explicit operator bool() { - fill(); - return !h_.done(); - } - - T operator()() { - fill(); - full_ = false; - return std::move(h_.promise().value_); - } - -private: - bool full_ = false; - - void fill() { - if(!full_) { - h_(); - if(h_.promise().exception_) { - std::rethrow_exception(h_.promise().exception_); - } - - full_ = true; - } - } -}; - -Generator -fib(unsigned n) { - if(n == 0) co_return; - if(n > 94) { - throw std::runtime_error("Too big"); - } - - if(n == 1) co_return; - - co_yield 1; - - if(n == 2) co_return; - - std::uint64_t a = 0; - std::uint64_t b = 1; - for(unsigned i = 2; i < n; ++i) { - std::uint64_t s = a + b; - co_yield s; - a = b; - b = s; - } -} - -int main() { - try { - auto gen = fib(50); - for(int j = 0; gen; ++j) { - fmt::println("fib({})={}", j, gen()); - } - } catch(const std::exception& ex) { - fmt::println("Exception: {}", ex.what()); - } catch(...) { - fmt::println("Unknown exception"); - } - - return 0; -} diff --git a/scratchpad/dbg.h b/scratchpad/dbg.h deleted file mode 100644 index 9f11809..0000000 --- a/scratchpad/dbg.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef __dbg_h__ -#define __dbg_h__ - -#include -#include -#include - -#ifdef NDEBUG -#define debug(M, ...) -#else -#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\ - __FILE__, __LINE__, ##__VA_ARGS__) -#endif - -#define clean_errno() (errno == 0 ? "None" : strerror(errno)) - -#define log_err(M, ...) fprintf(stderr,\ - "[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\ - clean_errno(), ##__VA_ARGS__) - -#define log_warn(M, ...) fprintf(stderr,\ - "[WARN] (%s:%d: errno: %s) " M "\n",\ - __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__) - -#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\ - __FILE__, __LINE__, ##__VA_ARGS__) - -#define check(A, M, ...) if(!(A)) {\ - log_err(M, ##__VA_ARGS__); errno=0; goto error; } - -#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\ - errno=0; goto error; } - -#define check_mem(A) check((A), "Out of memory.") - -#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\ - errno=0; goto error; } - -#endif diff --git a/scratchpad/dnd_loot_2.cpp b/scratchpad/dnd_loot_2.cpp deleted file mode 100644 index 10d5578..0000000 --- a/scratchpad/dnd_loot_2.cpp +++ /dev/null @@ -1,436 +0,0 @@ - -#line 1 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" -#include "gui/dnd_loot_2.hpp" -#include "gui/guecstra.hpp" -#include "gui/uisystems.hpp" -#include -#include -#include "dbc.hpp" - -#define _log(M, F) {$cur_state = F; fmt::println("| {}:{} action={}, fcurs={}", __FILE_NAME__, __LINE__, #M, F);} - - - -#line 131 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - - - -#line 14 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.cpp" -static const char _DNDLoot_eof_actions[] = { - 0, 0, 0, 0, 0, 10, 0, 10 -}; - -static const int DNDLoot_start = 1; -static const int DNDLoot_first_final = 8; -static const int DNDLoot_error = 0; - -static const int DNDLoot_en_main = 1; -static const int DNDLoot_en_main_looting = 2; - - -#line 134 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - -namespace gui { - sf::Vector2f DNDLoot2::mouse_position() { - return $window.mapPixelToCoords($router.position); - } - - void DNDLoot2::mouse_action(bool hover) { - sf::Vector2f pos = mouse_position(); - $status_ui.mouse(pos.x, pos.y, hover); - if($loot_ui.active) $loot_ui.mouse(pos.x, pos.y, hover); - } - - DNDLoot2::DNDLoot2(StatusUI& status_ui, LootUI& loot_ui, sf::RenderWindow &window, routing::Router& router) : - $status_ui(status_ui), - $loot_ui(loot_ui), - $window(window), - $router(router) - { - -#line 43 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.cpp" - { - cs = DNDLoot_start; - } - -#line 153 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - - dbc::log("===================================="); - event(Event::STARTED); - dbc::log("---------------- END CONSTRICT ------"); - } - - bool DNDLoot2::event(Event event, std::any data) { - if(event == Event::TICK) return true; - - int *p = (int *)&event; - int *pe = p+1; - int *eof = pe; - - dbc::log(fmt::format(">>>> DND EVENT {}, state={}, cs={}, end={}", - int(event), $cur_state, cs, $at_end)); - - -#line 62 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.cpp" - { - int _ps = 0; - if ( cs == 0 ) - goto _out; -_resume: - switch ( cs ) { -case 1: - if ( (*p) == 0 ) - goto tr0; - goto tr1; -case 0: - goto _out; -case 2: - switch( (*p) ) { - case 14: goto tr2; - case 17: goto tr4; - case 19: goto tr5; - } - if ( (*p) < 20 ) { - if ( 15 <= (*p) && (*p) <= 16 ) - goto tr3; - } else if ( (*p) > 21 ) { - if ( 22 <= (*p) && (*p) <= 23 ) - goto tr5; - } else - goto tr6; - goto tr1; -case 3: - switch( (*p) ) { - case 14: goto tr7; - case 16: goto tr8; - case 17: goto tr9; - case 19: goto tr10; - } - if ( (*p) > 21 ) { - if ( 22 <= (*p) && (*p) <= 23 ) - goto tr10; - } else if ( (*p) >= 20 ) - goto tr11; - goto tr1; -case 4: - switch( (*p) ) { - case 14: goto tr12; - case 15: goto tr13; - } - goto tr1; -case 5: - switch( (*p) ) { - case 14: goto tr15; - case 17: goto tr17; - case 19: goto tr18; - } - if ( (*p) < 20 ) { - if ( 15 <= (*p) && (*p) <= 16 ) - goto tr16; - } else if ( (*p) > 21 ) { - if ( 22 <= (*p) && (*p) <= 23 ) - goto tr18; - } else - goto tr19; - goto tr14; -case 6: - switch( (*p) ) { - case 14: goto tr20; - case 16: goto tr21; - case 17: goto tr22; - case 19: goto tr23; - } - if ( (*p) > 21 ) { - if ( 22 <= (*p) && (*p) <= 23 ) - goto tr23; - } else if ( (*p) >= 20 ) - goto tr24; - goto tr1; -case 7: - switch( (*p) ) { - case 14: goto tr25; - case 16: goto tr26; - case 17: goto tr27; - case 19: goto tr28; - } - if ( (*p) > 21 ) { - if ( 22 <= (*p) && (*p) <= 23 ) - goto tr28; - } else if ( (*p) >= 20 ) - goto tr29; - goto tr14; - } - - tr1: cs = 0; goto _again; - tr14: _ps = cs;cs = 0; goto f9; - tr0: _ps = cs;cs = 2; goto f0; - tr2: _ps = cs;cs = 2; goto f1; - tr8: _ps = cs;cs = 2; goto f2; - tr22: _ps = cs;cs = 2; goto f3; - tr5: _ps = cs;cs = 2; goto f4; - tr6: _ps = cs;cs = 2; goto f5; - tr9: _ps = cs;cs = 2; goto f6; - tr15: _ps = cs;cs = 2; goto f10; - tr26: _ps = cs;cs = 2; goto f11; - tr18: _ps = cs;cs = 2; goto f13; - tr19: _ps = cs;cs = 2; goto f14; - tr21: _ps = cs;cs = 2; goto f15; - tr27: _ps = cs;cs = 2; goto f16; - tr3: _ps = cs;cs = 3; goto f2; - tr10: _ps = cs;cs = 3; goto f4; - tr11: _ps = cs;cs = 3; goto f5; - tr16: _ps = cs;cs = 3; goto f11; - tr28: _ps = cs;cs = 3; goto f13; - tr29: _ps = cs;cs = 3; goto f14; - tr20: _ps = cs;cs = 4; goto f1; - tr7: _ps = cs;cs = 4; goto f2; - tr25: _ps = cs;cs = 4; goto f11; - tr12: _ps = cs;cs = 5; goto f7; - tr4: _ps = cs;cs = 6; goto f3; - tr23: _ps = cs;cs = 6; goto f4; - tr24: _ps = cs;cs = 6; goto f5; - tr17: _ps = cs;cs = 6; goto f12; - tr13: _ps = cs;cs = 7; goto f8; - -f9: -#line 17 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - $cur_state = (_ps); - fmt::println("!!! ERROR fcurs={}", (_ps)); - } - goto _again; -f0: -#line 22 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(started, (_ps)); - {p++; goto _out; } - } - goto _again; -f1: -#line 27 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(loot_close, (_ps)); - $loot_ui.active = false; - {p++; goto _out; } - } - goto _again; -f2: -#line 33 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(loot_grab, (_ps)); - // NOTE: when grab_source could work to do the if that was here - $grab_source = UISystem::loot_grab($loot_ui.$gui, data); - {p++; goto _out; } - } - goto _again; -f3: -#line 40 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(inv_grab, (_ps)); - $grab_source = UISystem::loot_grab($status_ui.$gui, data); - {p++; goto _out; } - } - goto _again; -f15: -#line 46 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(loot_drop, (_ps)); - if(UISystem::loot_drop($status_ui.$gui, - $loot_ui.$gui, $grab_source, data)) - { - cs = 2; - } - - {p++; goto _out; } - } - goto _again; -f6: -#line 57 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(inv_drop, (_ps)); - if(UISystem::loot_drop($loot_ui.$gui, - $status_ui.$gui, $grab_source, data)) - { - cs = 2; - } - - {p++; goto _out; } - } - goto _again; -f7: -#line 68 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(at_end, (_ps)); - fmt::println("> AT END"); - $grab_source = std::nullopt; - $at_end = true; - {p++; goto _out; } - } - goto _again; -f4: -#line 83 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(mouse_click, (_ps)); - mouse_action(false); - {p++; goto _out; } - } - goto _again; -f5: -#line 89 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(mouse_move, (_ps)); - if($grab_source) { - auto& source = $loot_ui.$gui.get(*$grab_source); - source.move($window.mapPixelToCoords($router.position)); - } - mouse_action(true); - {p++; goto _out; } - } - goto _again; -f10: -#line 27 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(loot_close, (_ps)); - $loot_ui.active = false; - {p++; goto _out; } - } -#line 76 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(not_end, (_ps)); - fmt::println("% NOT_END"); - $at_end = false; - {p++; goto _out; } - } - goto _again; -f11: -#line 33 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(loot_grab, (_ps)); - // NOTE: when grab_source could work to do the if that was here - $grab_source = UISystem::loot_grab($loot_ui.$gui, data); - {p++; goto _out; } - } -#line 76 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(not_end, (_ps)); - fmt::println("% NOT_END"); - $at_end = false; - {p++; goto _out; } - } - goto _again; -f12: -#line 40 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(inv_grab, (_ps)); - $grab_source = UISystem::loot_grab($status_ui.$gui, data); - {p++; goto _out; } - } -#line 76 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(not_end, (_ps)); - fmt::println("% NOT_END"); - $at_end = false; - {p++; goto _out; } - } - goto _again; -f16: -#line 57 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(inv_drop, (_ps)); - if(UISystem::loot_drop($loot_ui.$gui, - $status_ui.$gui, $grab_source, data)) - { - cs = 2; - } - - {p++; goto _out; } - } -#line 76 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(not_end, (_ps)); - fmt::println("% NOT_END"); - $at_end = false; - {p++; goto _out; } - } - goto _again; -f8: -#line 68 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(at_end, (_ps)); - fmt::println("> AT END"); - $grab_source = std::nullopt; - $at_end = true; - {p++; goto _out; } - } -#line 33 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(loot_grab, (_ps)); - // NOTE: when grab_source could work to do the if that was here - $grab_source = UISystem::loot_grab($loot_ui.$gui, data); - {p++; goto _out; } - } - goto _again; -f13: -#line 83 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(mouse_click, (_ps)); - mouse_action(false); - {p++; goto _out; } - } -#line 76 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(not_end, (_ps)); - fmt::println("% NOT_END"); - $at_end = false; - {p++; goto _out; } - } - goto _again; -f14: -#line 89 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(mouse_move, (_ps)); - if($grab_source) { - auto& source = $loot_ui.$gui.get(*$grab_source); - source.move($window.mapPixelToCoords($router.position)); - } - mouse_action(true); - {p++; goto _out; } - } -#line 76 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - _log(not_end, (_ps)); - fmt::println("% NOT_END"); - $at_end = false; - {p++; goto _out; } - } - goto _again; - -_again: - if ( cs == 0 ) - goto _out; - p += 1; - goto _resume; - if ( p == eof ) - { - switch ( _DNDLoot_eof_actions[cs] ) { - case 10: -#line 17 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - { - $cur_state = (_ps); - fmt::println("!!! ERROR fcurs={}", (_ps)); - } - break; -#line 385 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.cpp" - } - } - - _out: {} - } - -#line 170 "C:/Users/lcthw/Projects/raycaster//gui/dnd_loot_2.rl" - - dbc::log(fmt::format("<<<< DND EVENT {}, state={}, cs={}, end={}", - int(event), $cur_state, cs, $at_end)); - return $at_end; - } -} diff --git a/scratchpad/dnd_loot_2.rl b/scratchpad/dnd_loot_2.rl deleted file mode 100644 index 8d82ff9..0000000 --- a/scratchpad/dnd_loot_2.rl +++ /dev/null @@ -1,175 +0,0 @@ -#include "gui/dnd_loot_2.hpp" -#include "gui/guecstra.hpp" -#include "gui/uisystems.hpp" -#include -#include -#include "dbc.hpp" - -#define _log(M, F) {$cur_state = F; fmt::println("| {}:{} action={}, fcurs={}", __FILE_NAME__, __LINE__, #M, F);} - - -%%{ - machine DNDLoot; - alphtype int; - - import "gui/fsm_events.hpp"; - - action error { - $cur_state = fcurs; - fmt::println("!!! ERROR fcurs={}", fcurs); - } - - action started { - _log(started, fcurs); - fbreak; - } - - action loot_close { - _log(loot_close, fcurs); - $loot_ui.active = false; - fbreak; - } - - action loot_grab { - _log(loot_grab, fcurs); - // NOTE: when grab_source could work to do the if that was here - $grab_source = UISystem::loot_grab($loot_ui.$gui, data); - fbreak; - } - - action inv_grab { - _log(inv_grab, fcurs); - $grab_source = UISystem::loot_grab($status_ui.$gui, data); - fbreak; - } - - action loot_drop { - _log(loot_drop, fcurs); - if(UISystem::loot_drop($status_ui.$gui, - $loot_ui.$gui, $grab_source, data)) - { - fnext looting; - } - - fbreak; - } - - action inv_drop { - _log(inv_drop, fcurs); - if(UISystem::loot_drop($loot_ui.$gui, - $status_ui.$gui, $grab_source, data)) - { - fnext looting; - } - - fbreak; - } - - action at_end { - _log(at_end, fcurs); - fmt::println("> AT END"); - $grab_source = std::nullopt; - $at_end = true; - fbreak; - } - - action not_end { - _log(not_end, fcurs); - fmt::println("% NOT_END"); - $at_end = false; - fbreak; - } - - action mouse_click { - _log(mouse_click, fcurs); - mouse_action(false); - fbreak; - } - - action mouse_move { - _log(mouse_move, fcurs); - if($grab_source) { - auto& source = $loot_ui.$gui.get(*$grab_source); - source.move($window.mapPixelToCoords($router.position)); - } - mouse_action(true); - fbreak; - } - -mouse_click = (MOUSE_DRAG_START | MOUSE_CLICK | MOUSE_DROP); -mouse_move = (MOUSE_MOVE | MOUSE_DRAG); - -main := start: ( - STARTED @started -> looting -), -looting: ( - LOOT_OPEN @loot_close -> looting | - LOOT_ITEM @loot_grab -> loot_grab | - LOOT_SELECT @loot_grab -> loot_grab | - INV_SELECT @inv_grab -> inv_grab | - mouse_click @mouse_click -> looting | - mouse_move @mouse_move -> looting -), -loot_grab: ( - LOOT_OPEN @loot_grab -> end | - LOOT_SELECT @loot_grab -> looting | - INV_SELECT @inv_drop -> looting | - mouse_click @mouse_click -> loot_grab | - mouse_move @mouse_move -> loot_grab -), -inv_grab: ( - LOOT_OPEN @loot_close -> end | - LOOT_SELECT @loot_drop -> looting | - INV_SELECT @inv_grab -> looting | - mouse_click @mouse_click -> inv_grab | - mouse_move @mouse_move -> inv_grab -), -end: ( - LOOT_ITEM @loot_grab -> loot_grab | - LOOT_OPEN -> looting -) >at_end %not_end %err(error); -}%% - -%% write data; - -namespace gui { - sf::Vector2f DNDLoot2::mouse_position() { - return $window.mapPixelToCoords($router.position); - } - - void DNDLoot2::mouse_action(bool hover) { - sf::Vector2f pos = mouse_position(); - $status_ui.mouse(pos.x, pos.y, hover); - if($loot_ui.active) $loot_ui.mouse(pos.x, pos.y, hover); - } - - DNDLoot2::DNDLoot2(StatusUI& status_ui, LootUI& loot_ui, sf::RenderWindow &window, routing::Router& router) : - $status_ui(status_ui), - $loot_ui(loot_ui), - $window(window), - $router(router) - { - %%write init; - - dbc::log("===================================="); - event(Event::STARTED); - dbc::log("---------------- END CONSTRICT ------"); - } - - bool DNDLoot2::event(Event event, std::any data) { - if(event == Event::TICK) return true; - - int *p = (int *)&event; - int *pe = p+1; - int *eof = pe; - - dbc::log(fmt::format(">>>> DND EVENT {}, state={}, cs={}, end={}", - int(event), $cur_state, cs, $at_end)); - - %%write exec noend; - - dbc::log(fmt::format("<<<< DND EVENT {}, state={}, cs={}, end={}", - int(event), $cur_state, cs, $at_end)); - return $at_end; - } -} diff --git a/scratchpad/fenscaster.cpp b/scratchpad/fenscaster.cpp deleted file mode 100644 index fe74063..0000000 --- a/scratchpad/fenscaster.cpp +++ /dev/null @@ -1,359 +0,0 @@ -#include -#include -#include -#include -#include "matrix.hpp" -#include -#include "fenster/fenster.h" -#include "dbc.hpp" - -using matrix::Matrix; -using namespace fmt; - -Matrix MAP{ - {2,2,2,2,2,2,2,2,2}, - {2,0,8,0,0,0,0,0,2}, - {2,0,7,0,0,5,6,0,2}, - {2,0,0,0,0,0,0,0,2}, - {2,2,0,0,0,0,0,2,2}, - {2,0,0,1,3,4,0,0,2}, - {2,0,0,0,0,0,2,2,2}, - {2,2,2,2,2,2,2,2,2} -}; - -const int SCREEN_HEIGHT=480; -const int SCREEN_WIDTH=SCREEN_HEIGHT * 2; - -const int THREED_VIEW_WIDTH=480; -const int THREED_VIEW_HEIGHT=480; -const int MAP_SIZE=matrix::width(MAP); -const int TILE_SIZE=(SCREEN_WIDTH/2) / MAP_SIZE; -const float FOV = std::numbers::pi / 3.0; -const float HALF_FOV = FOV / 2; -const int CASTED_RAYS=120; -const float STEP_ANGLE = FOV / CASTED_RAYS; -const int MAX_DEPTH = MAP_SIZE * TILE_SIZE; -const float SCALE = (SCREEN_WIDTH / 2) / CASTED_RAYS; -int PITCH=25; - -float player_x = SCREEN_WIDTH / 4; -float player_y = SCREEN_WIDTH / 4; - -// x and y start position -double posX = player_x / TILE_SIZE; -double posY = player_y / TILE_SIZE; - -// initial direction vector -double dirX = -1; -double dirY = 0; - -// the 2d raycaster version of camera plane -double planeX = 0; -double planeY = 0.66; - -#define rgba_color(r,g,b,a) (b<<(0*8))|(g<<(1*8))|(r<<(2*8))|(a<<(3*8)) -#define gray_color(c) rgba_color(c, c, c, 255) - -std::vector texture[8]; -#define texWidth 64 -#define texHeight 64 - - -void load_textures() { - for(int i = 0; i < 8; i++) { - texture[i].resize(texWidth * texHeight); - } - - for(int x = 0; x < texWidth; x++) { - for(int y = 0; y < texHeight; y++) - { - int xorcolor = (x * 256 / texWidth) ^ (y * 256 / texHeight); - //int xcolor = x * 256 / texWidth; - int ycolor = y * 256 / texHeight; - int xycolor = y * 128 / texHeight + x * 128 / texWidth; - texture[0][texWidth * y + x] = 65536 * 254 * (x != y && x != texWidth - y); //flat red texture with black cross - texture[1][texWidth * y + x] = xycolor + 256 * xycolor + 65536 * xycolor; //sloped greyscale - texture[2][texWidth * y + x] = 256 * xycolor + 65536 * xycolor; //sloped yellow gradient - texture[3][texWidth * y + x] = xorcolor + 256 * xorcolor + 65536 * xorcolor; //xor greyscale - texture[4][texWidth * y + x] = 256 * xorcolor; //xor green - texture[5][texWidth * y + x] = 65536 * 192 * (x % 16 && y % 16); //red bricks - texture[6][texWidth * y + x] = 65536 * ycolor; //red gradient - texture[7][texWidth * y + x] = 128 + 256 * 128 + 65536 * 128; //flat grey texture - } - } -} - -void draw_rect(Fenster &window, Point pos, Point size, uint32_t color) { - size_t x_start = size_t(pos.x); - size_t y_start = size_t(pos.y); - size_t width = size_t(size.x); - size_t height = size_t(size.y); - - dbc::check(x_start <= size_t(window.f.width), format("pos.x {} is greater than width {}", x_start, window.f.width)); - dbc::check(y_start <= size_t(window.f.height), format("pos.y {} is greater than height {}", y_start, window.f.height)); - dbc::check(x_start + width <= size_t(window.f.width), format("size width {} is greater than width {}", x_start + width, window.f.width)); - dbc::check(y_start + height <= size_t(window.f.height), format("size height {} is greater than height {}", y_start + height, window.f.height)); - - for(size_t y = y_start; y < y_start + height; y++) { - for(size_t x = x_start; x < x_start + width; x++) { - window.px(x, y) = color; - } - } -} - -void draw_map_rect(Fenster &window, int x, int y, uint32_t color) { - draw_rect(window, - {size_t(x * TILE_SIZE), size_t(y * TILE_SIZE)}, - {size_t(TILE_SIZE-1), size_t(TILE_SIZE-1)}, - color); -} - -void draw_map(Fenster &window, Matrix &map) { - uint32_t light_grey = gray_color(191); - uint32_t dark_grey = gray_color(65); - - for(size_t y = 0; y < matrix::height(map); y++) { - for(size_t x = 0; x < matrix::width(map); x++) { - draw_map_rect(window, x, y, map[y][x] == 0 ? dark_grey : light_grey); - } - } -} - -void draw_line(Fenster &window, Point start, Point end, uint32_t color) { - int x = int(start.x); - int y = int(start.y); - int x1 = int(end.x); - int y1 = int(end.y); - int dx = std::abs(x1 - x); - int sx = x < x1 ? 1 : -1; - int dy = std::abs(y1 - y) * -1; - int sy = y < y1 ? 1 : -1; - int error = dx + dy; - - while(x != x1 || y != y1) { - int e2 = 2 * error; - - if(e2 >= dy) { - error = error + dy; - x = x + sx; - } - - if(e2 <= dx) { - error = error + dx; - y = y + sy; - } - - window.px(x, y) = color; - } -} - -void clear(Fenster &window) { - for(int y = 0; y < window.f.height; y++) { - for(int x = 0; x < window.f.width; x++) { - window.px(x, y) = 0; - } - } -} - -void draw_map_blocks(Fenster &window, int col, int row) { - draw_map_rect(window, col, row, rgba_color(100, 20, 20, 255)); -} - -void ray_casting(Fenster &window, Matrix& map) { - int w = THREED_VIEW_WIDTH; - int h = THREED_VIEW_HEIGHT; - - for(int x = 0; x < w; x++) { - // calculate ray position and direction - double cameraX = 2 * x / double(w) - 1; // x-coord in camera space - double rayDirX = dirX + planeX * cameraX; - double rayDirY = dirY + planeY * cameraX; - - // which box of the map we're in - int mapX = int(posX); - int mapY = int(posY); - - // length of ray from current pos to next x or y-side - double sideDistX; - double sideDistY; - - // length of ray from one x or y-side to next x or y-side - double deltaDistX = std::abs(1.0 / rayDirX); - double deltaDistY = std::abs(1.0 / rayDirY); - double perpWallDist; - - int stepX = 0; - int stepY = 0; - int hit = 0; - int side = 0; - - // calculate step and initial sideDist - if(rayDirX < 0) { - stepX = -1; - sideDistX = (posX - mapX) * deltaDistX; - } else { - stepX = 1; - sideDistX = (mapX + 1.0 - posX) * deltaDistX; - } - - if(rayDirY < 0) { - stepY = -1; - sideDistY = (posY - mapY) * deltaDistY; - } else { - stepY = 1; - sideDistY = (mapY + 1.0 - posY) * deltaDistY; - } - - // perform DDA - while(hit == 0) { - if(sideDistX < sideDistY) { - sideDistX += deltaDistX; - mapX += stepX; - side = 0; - } else { - sideDistY += deltaDistY; - mapY += stepY; - side = 1; - } - - if(map[mapY][mapX] > 0) hit = 1; - } - - if(side == 0) { - perpWallDist = (sideDistX - deltaDistX); - } else { - perpWallDist = (sideDistY - deltaDistY); - } - - draw_map_blocks(window, mapX, mapY); - - - // player direction ray - draw_line(window, {size_t(posX * TILE_SIZE), size_t(posY * TILE_SIZE)}, - {(size_t)mapX * TILE_SIZE, (size_t)mapY * TILE_SIZE}, rgba_color(0, 255, 0, 255)); - - int lineHeight = int(h / perpWallDist); - - int drawStart = -lineHeight / 2 + h / 2 + PITCH; - if(drawStart < 0) drawStart = 0; - - int drawEnd = lineHeight / 2 + h / 2 + PITCH; - if(drawEnd >= h) drawEnd = h - 1; - - int texNum = MAP[mapY][mapX] - 1; - - // calculate value of wallX - double wallX; // where exactly the wall was hit - if(side == 0) { - wallX = posY + perpWallDist * rayDirY; - } else { - wallX = posX + perpWallDist * rayDirX; - } - wallX -= floor((wallX)); - - // x coorindate on the texture - int texX = int(wallX * double(texWidth)); - if(side == 0 && rayDirX > 0) texX = texWidth - texX - 1; - if(side == 1 && rayDirY < 0) texX = texWidth - texX - 1; - - // LODE: an integer-only bresenham or DDA like algorithm could make the texture coordinate stepping faster - - // How much to increase the texture coordinate per screen pixel - double step = 1.0 * texHeight / lineHeight; - // Starting texture coordinate - double texPos = (drawStart - PITCH - h / 2 + lineHeight / 2) * step; - - for(int y = drawStart; y < drawEnd; y++) { - // BUG? Why bitwise and here? - int texY = (int)texPos & (texHeight - 1); - texPos += step; - uint32_t color = texture[texNum][texHeight * texY + texX]; - - if(side == 1) color = (color >> 1) & 8355711; - window.px(x + THREED_VIEW_WIDTH, y) = color; - } - } -} - -void draw_ceiling_floor(Fenster &window) { - draw_rect(window, - {size_t(window.width() / 2), size_t(window.height() / 2)}, - {size_t(window.width() / 2), size_t(window.height() / 2)}, - gray_color(200)); - - draw_rect(window, - {size_t(window.width() / 2), 0}, - {size_t(window.height()), size_t(window.height() / 2 + PITCH)}, - gray_color(100)); -} - -void draw_everything(Fenster &window) { - clear(window); - draw_map(window, MAP); - draw_ceiling_floor(window); - ray_casting(window, MAP); -} - -bool empty_space(int new_x, int new_y) { - dbc::check((size_t)new_x < matrix::width(MAP), - format("x={} too wide={}", new_x, matrix::width(MAP))); - dbc::check((size_t)new_y < matrix::height(MAP), - format("y={} too high={}", new_y, matrix::height(MAP))); - - return MAP[new_y][new_x] == 0; -} - -int main() { - Fenster window(SCREEN_WIDTH, SCREEN_HEIGHT, "Fenscaster"); - const int fps = 60; - double moveSpeed = 0.1; - double rotSpeed = 0.1; - - load_textures(); - - while(window.loop(fps)) { - draw_everything(window); - - if(window.key('W')) { - if(empty_space(int(posX + dirX * moveSpeed), int(posY))) posX += dirX * moveSpeed; - if(empty_space(int(posX), int(posY + dirY * moveSpeed))) posY += dirY * moveSpeed; - } else if(window.key('S')) { - if(empty_space(int(posX - dirX * moveSpeed), int(posY))) posX -= dirX * moveSpeed; - if(empty_space(int(posX), int(posY - dirY * moveSpeed))) posY -= dirY * moveSpeed; - } - - if(window.key('D')) { - double oldDirX = dirX; - dirX = dirX * cos(-rotSpeed) - dirY * sin(-rotSpeed); - dirY = oldDirX * sin(-rotSpeed) + dirY * cos(-rotSpeed); - - double oldPlaneX = planeX; - planeX = planeX * cos(-rotSpeed) - planeY * sin(-rotSpeed); - planeY = oldPlaneX * sin(-rotSpeed) + planeY * cos(-rotSpeed); - } else if(window.key('A')) { - double oldDirX = dirX; - dirX = dirX * cos(rotSpeed) - dirY * sin(rotSpeed); - dirY = oldDirX * sin(rotSpeed) + dirY * cos(rotSpeed); - - double oldPlaneX = planeX; - planeX = planeX * cos(rotSpeed) - planeY * sin(rotSpeed); - planeY = oldPlaneX * sin(rotSpeed) + planeY * cos(rotSpeed); - } - - if(window.key('E')) { - PITCH = std::clamp(PITCH + 10, -60, 240); - } else if(window.key('Q')) { - PITCH = std::clamp(PITCH - 10, -60, 240); - } - } - - return 0; -} - -#if defined(_WIN32) -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, - int nCmdShow) { - (void)hInstance, (void)hPrevInstance, (void)pCmdLine, (void)nCmdShow; - return main(); -} -#endif diff --git a/scratchpad/fenster/fenster.h b/scratchpad/fenster/fenster.h deleted file mode 100644 index 9e35a03..0000000 --- a/scratchpad/fenster/fenster.h +++ /dev/null @@ -1,373 +0,0 @@ -#ifndef FENSTER_H -#define FENSTER_H - -#if defined(__APPLE__) -#include -#include -#include -#elif defined(_WIN32) -#include -#else -#define _DEFAULT_SOURCE 1 -#include -#include -#include -#include -#endif - -#include -#include - -struct fenster { - const char *title; - const int width; - const int height; - uint32_t *buf; - int keys[256]; /* keys are mostly ASCII, but arrows are 17..20 */ - int mod; /* mod is 4 bits mask, ctrl=1, shift=2, alt=4, meta=8 */ - int x; - int y; - int mouse; -#if defined(__APPLE__) - id wnd; -#elif defined(_WIN32) - HWND hwnd; -#else - Display *dpy; - Window w; - GC gc; - XImage *img; -#endif -}; - -#ifndef FENSTER_API -#define FENSTER_API extern -#endif -FENSTER_API int fenster_open(struct fenster *f); -FENSTER_API int fenster_loop(struct fenster *f); -FENSTER_API void fenster_close(struct fenster *f); -FENSTER_API void fenster_sleep(int64_t ms); -FENSTER_API int64_t fenster_time(void); -#define fenster_pixel(f, x, y) ((f)->buf[((y) * (f)->width) + (x)]) - -#ifndef FENSTER_HEADER -#if defined(__APPLE__) -#define msg(r, o, s) ((r(*)(id, SEL))objc_msgSend)(o, sel_getUid(s)) -#define msg1(r, o, s, A, a) \ - ((r(*)(id, SEL, A))objc_msgSend)(o, sel_getUid(s), a) -#define msg2(r, o, s, A, a, B, b) \ - ((r(*)(id, SEL, A, B))objc_msgSend)(o, sel_getUid(s), a, b) -#define msg3(r, o, s, A, a, B, b, C, c) \ - ((r(*)(id, SEL, A, B, C))objc_msgSend)(o, sel_getUid(s), a, b, c) -#define msg4(r, o, s, A, a, B, b, C, c, D, d) \ - ((r(*)(id, SEL, A, B, C, D))objc_msgSend)(o, sel_getUid(s), a, b, c, d) - -#define cls(x) ((id)objc_getClass(x)) - -extern id const NSDefaultRunLoopMode; -extern id const NSApp; - -static void fenster_draw_rect(id v, SEL s, CGRect r) { - (void)r, (void)s; - struct fenster *f = (struct fenster *)objc_getAssociatedObject(v, "fenster"); - CGContextRef context = - msg(CGContextRef, msg(id, cls("NSGraphicsContext"), "currentContext"), - "graphicsPort"); - CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); - CGDataProviderRef provider = CGDataProviderCreateWithData( - NULL, f->buf, f->width * f->height * 4, NULL); - CGImageRef img = - CGImageCreate(f->width, f->height, 8, 32, f->width * 4, space, - kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, - provider, NULL, false, kCGRenderingIntentDefault); - CGColorSpaceRelease(space); - CGDataProviderRelease(provider); - CGContextDrawImage(context, CGRectMake(0, 0, f->width, f->height), img); - CGImageRelease(img); -} - -static BOOL fenster_should_close(id v, SEL s, id w) { - (void)v, (void)s, (void)w; - msg1(void, NSApp, "terminate:", id, NSApp); - return YES; -} - -FENSTER_API int fenster_open(struct fenster *f) { - msg(id, cls("NSApplication"), "sharedApplication"); - msg1(void, NSApp, "setActivationPolicy:", NSInteger, 0); - f->wnd = msg4(id, msg(id, cls("NSWindow"), "alloc"), - "initWithContentRect:styleMask:backing:defer:", CGRect, - CGRectMake(0, 0, f->width, f->height), NSUInteger, 3, - NSUInteger, 2, BOOL, NO); - Class windelegate = - objc_allocateClassPair((Class)cls("NSObject"), "FensterDelegate", 0); - class_addMethod(windelegate, sel_getUid("windowShouldClose:"), - (IMP)fenster_should_close, "c@:@"); - objc_registerClassPair(windelegate); - msg1(void, f->wnd, "setDelegate:", id, - msg(id, msg(id, (id)windelegate, "alloc"), "init")); - Class c = objc_allocateClassPair((Class)cls("NSView"), "FensterView", 0); - class_addMethod(c, sel_getUid("drawRect:"), (IMP)fenster_draw_rect, "i@:@@"); - objc_registerClassPair(c); - - id v = msg(id, msg(id, (id)c, "alloc"), "init"); - msg1(void, f->wnd, "setContentView:", id, v); - objc_setAssociatedObject(v, "fenster", (id)f, OBJC_ASSOCIATION_ASSIGN); - - id title = msg1(id, cls("NSString"), "stringWithUTF8String:", const char *, - f->title); - msg1(void, f->wnd, "setTitle:", id, title); - msg1(void, f->wnd, "makeKeyAndOrderFront:", id, nil); - msg(void, f->wnd, "center"); - msg1(void, NSApp, "activateIgnoringOtherApps:", BOOL, YES); - return 0; -} - -FENSTER_API void fenster_close(struct fenster *f) { - msg(void, f->wnd, "close"); -} - -// clang-format off -static const uint8_t FENSTER_KEYCODES[128] = {65,83,68,70,72,71,90,88,67,86,0,66,81,87,69,82,89,84,49,50,51,52,54,53,61,57,55,45,56,48,93,79,85,91,73,80,10,76,74,39,75,59,92,44,47,78,77,46,9,32,96,8,0,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,2,3,127,0,5,0,4,0,20,19,18,17,0}; -// clang-format on -FENSTER_API int fenster_loop(struct fenster *f) { - msg1(void, msg(id, f->wnd, "contentView"), "setNeedsDisplay:", BOOL, YES); - id ev = msg4(id, NSApp, - "nextEventMatchingMask:untilDate:inMode:dequeue:", NSUInteger, - NSUIntegerMax, id, NULL, id, NSDefaultRunLoopMode, BOOL, YES); - if (!ev) - return 0; - NSUInteger evtype = msg(NSUInteger, ev, "type"); - switch (evtype) { - case 1: /* NSEventTypeMouseDown */ - f->mouse |= 1; - break; - case 2: /* NSEventTypeMouseUp*/ - f->mouse &= ~1; - break; - case 5: - case 6: { /* NSEventTypeMouseMoved */ - CGPoint xy = msg(CGPoint, ev, "locationInWindow"); - f->x = (int)xy.x; - f->y = (int)(f->height - xy.y); - return 0; - } - case 10: /*NSEventTypeKeyDown*/ - case 11: /*NSEventTypeKeyUp:*/ { - NSUInteger k = msg(NSUInteger, ev, "keyCode"); - f->keys[k < 127 ? FENSTER_KEYCODES[k] : 0] = evtype == 10; - NSUInteger mod = msg(NSUInteger, ev, "modifierFlags") >> 17; - f->mod = (mod & 0xc) | ((mod & 1) << 1) | ((mod >> 1) & 1); - return 0; - } - } - msg1(void, NSApp, "sendEvent:", id, ev); - return 0; -} -#elif defined(_WIN32) -// clang-format off -static const uint8_t FENSTER_KEYCODES[] = {0,27,49,50,51,52,53,54,55,56,57,48,45,61,8,9,81,87,69,82,84,89,85,73,79,80,91,93,10,0,65,83,68,70,71,72,74,75,76,59,39,96,0,92,90,88,67,86,66,78,77,44,46,47,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,17,3,0,20,0,19,0,5,18,4,26,127}; -// clang-format on -typedef struct BINFO{ - BITMAPINFOHEADER bmiHeader; - RGBQUAD bmiColors[3]; -}BINFO; -static LRESULT CALLBACK fenster_wndproc(HWND hwnd, UINT msg, WPARAM wParam, - LPARAM lParam) { - struct fenster *f = (struct fenster *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - switch (msg) { - case WM_PAINT: { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hwnd, &ps); - HDC memdc = CreateCompatibleDC(hdc); - HBITMAP hbmp = CreateCompatibleBitmap(hdc, f->width, f->height); - HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbmp); - BINFO bi = {{sizeof(bi), f->width, -f->height, 1, 32, BI_BITFIELDS}}; - bi.bmiColors[0].rgbRed = 0xff; - bi.bmiColors[1].rgbGreen = 0xff; - bi.bmiColors[2].rgbBlue = 0xff; - SetDIBitsToDevice(memdc, 0, 0, f->width, f->height, 0, 0, 0, f->height, - f->buf, (BITMAPINFO *)&bi, DIB_RGB_COLORS); - BitBlt(hdc, 0, 0, f->width, f->height, memdc, 0, 0, SRCCOPY); - SelectObject(memdc, oldbmp); - DeleteObject(hbmp); - DeleteDC(memdc); - EndPaint(hwnd, &ps); - } break; - case WM_CLOSE: - DestroyWindow(hwnd); - break; - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - f->mouse = (msg == WM_LBUTTONDOWN); - break; - case WM_MOUSEMOVE: - f->y = HIWORD(lParam), f->x = LOWORD(lParam); - break; - case WM_KEYDOWN: - case WM_KEYUP: { - f->mod = ((GetKeyState(VK_CONTROL) & 0x8000) >> 15) | - ((GetKeyState(VK_SHIFT) & 0x8000) >> 14) | - ((GetKeyState(VK_MENU) & 0x8000) >> 13) | - (((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000) >> 12); - f->keys[FENSTER_KEYCODES[HIWORD(lParam) & 0x1ff]] = !((lParam >> 31) & 1); - } break; - case WM_DESTROY: - PostQuitMessage(0); - break; - default: - return DefWindowProc(hwnd, msg, wParam, lParam); - } - return 0; -} - -FENSTER_API int fenster_open(struct fenster *f) { - HINSTANCE hInstance = GetModuleHandle(NULL); - WNDCLASSEX wc = {0}; - wc.cbSize = sizeof(WNDCLASSEX); - wc.style = CS_VREDRAW | CS_HREDRAW; - wc.lpfnWndProc = fenster_wndproc; - wc.hInstance = hInstance; - wc.lpszClassName = f->title; - RegisterClassEx(&wc); - f->hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, f->title, f->title, - WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, - f->width, f->height, NULL, NULL, hInstance, NULL); - - if (f->hwnd == NULL) - return -1; - SetWindowLongPtr(f->hwnd, GWLP_USERDATA, (LONG_PTR)f); - ShowWindow(f->hwnd, SW_NORMAL); - UpdateWindow(f->hwnd); - return 0; -} - -FENSTER_API void fenster_close(struct fenster *f) { (void)f; } - -FENSTER_API int fenster_loop(struct fenster *f) { - MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - if (msg.message == WM_QUIT) - return -1; - TranslateMessage(&msg); - DispatchMessage(&msg); - } - InvalidateRect(f->hwnd, NULL, TRUE); - return 0; -} -#else -// clang-format off -static int FENSTER_KEYCODES[124] = {XK_BackSpace,8,XK_Delete,127,XK_Down,18,XK_End,5,XK_Escape,27,XK_Home,2,XK_Insert,26,XK_Left,20,XK_Page_Down,4,XK_Page_Up,3,XK_Return,10,XK_Right,19,XK_Tab,9,XK_Up,17,XK_apostrophe,39,XK_backslash,92,XK_bracketleft,91,XK_bracketright,93,XK_comma,44,XK_equal,61,XK_grave,96,XK_minus,45,XK_period,46,XK_semicolon,59,XK_slash,47,XK_space,32,XK_a,65,XK_b,66,XK_c,67,XK_d,68,XK_e,69,XK_f,70,XK_g,71,XK_h,72,XK_i,73,XK_j,74,XK_k,75,XK_l,76,XK_m,77,XK_n,78,XK_o,79,XK_p,80,XK_q,81,XK_r,82,XK_s,83,XK_t,84,XK_u,85,XK_v,86,XK_w,87,XK_x,88,XK_y,89,XK_z,90,XK_0,48,XK_1,49,XK_2,50,XK_3,51,XK_4,52,XK_5,53,XK_6,54,XK_7,55,XK_8,56,XK_9,57}; -// clang-format on -FENSTER_API int fenster_open(struct fenster *f) { - f->dpy = XOpenDisplay(NULL); - int screen = DefaultScreen(f->dpy); - f->w = XCreateSimpleWindow(f->dpy, RootWindow(f->dpy, screen), 0, 0, f->width, - f->height, 0, BlackPixel(f->dpy, screen), - WhitePixel(f->dpy, screen)); - f->gc = XCreateGC(f->dpy, f->w, 0, 0); - XSelectInput(f->dpy, f->w, - ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | - ButtonReleaseMask | PointerMotionMask); - XStoreName(f->dpy, f->w, f->title); - XMapWindow(f->dpy, f->w); - XSync(f->dpy, f->w); - f->img = XCreateImage(f->dpy, DefaultVisual(f->dpy, 0), 24, ZPixmap, 0, - (char *)f->buf, f->width, f->height, 32, 0); - return 0; -} -FENSTER_API void fenster_close(struct fenster *f) { XCloseDisplay(f->dpy); } -FENSTER_API int fenster_loop(struct fenster *f) { - XEvent ev; - XPutImage(f->dpy, f->w, f->gc, f->img, 0, 0, 0, 0, f->width, f->height); - XFlush(f->dpy); - while (XPending(f->dpy)) { - XNextEvent(f->dpy, &ev); - switch (ev.type) { - case ButtonPress: - case ButtonRelease: - f->mouse = (ev.type == ButtonPress); - break; - case MotionNotify: - f->x = ev.xmotion.x, f->y = ev.xmotion.y; - break; - case KeyPress: - case KeyRelease: { - int m = ev.xkey.state; - int k = XkbKeycodeToKeysym(f->dpy, ev.xkey.keycode, 0, 0); - for (unsigned int i = 0; i < 124; i += 2) { - if (FENSTER_KEYCODES[i] == k) { - f->keys[FENSTER_KEYCODES[i + 1]] = (ev.type == KeyPress); - break; - } - } - f->mod = (!!(m & ControlMask)) | (!!(m & ShiftMask) << 1) | - (!!(m & Mod1Mask) << 2) | (!!(m & Mod4Mask) << 3); - } break; - } - } - return 0; -} -#endif - -#ifdef _WIN32 -FENSTER_API void fenster_sleep(int64_t ms) { Sleep(ms); } -FENSTER_API int64_t fenster_time() { - LARGE_INTEGER freq, count; - QueryPerformanceFrequency(&freq); - QueryPerformanceCounter(&count); - return (int64_t)(count.QuadPart * 1000.0 / freq.QuadPart); -} -#else -FENSTER_API void fenster_sleep(int64_t ms) { - struct timespec ts; - ts.tv_sec = ms / 1000; - ts.tv_nsec = (ms % 1000) * 1000000; - nanosleep(&ts, NULL); -} -FENSTER_API int64_t fenster_time(void) { - struct timespec time; - clock_gettime(CLOCK_REALTIME, &time); - return time.tv_sec * 1000 + (time.tv_nsec / 1000000); -} -#endif - -#ifdef __cplusplus -class Fenster { -public: - struct fenster f; - int64_t now; - - Fenster(const int w, const int h, const char *title) - : f{.title = title, .width = w, .height = h} { - this->f.buf = new uint32_t[w * h]; - this->now = fenster_time(); - fenster_open(&this->f); - } - ~Fenster() { - fenster_close(&this->f); - delete[] this->f.buf; - } - bool loop(const int fps) { - int64_t t = fenster_time(); - if (t - this->now < 1000 / fps) { - fenster_sleep(t - now); - } - this->now = t; - return fenster_loop(&this->f) == 0; - } - inline uint32_t &px(const int x, const int y) { - return fenster_pixel(&this->f, x, y); - } - bool key(int c) { return c >= 0 && c < 128 ? this->f.keys[c] : false; } - int x() { return this->f.x; } - int y() { return this->f.y; } - int mouse() { return this->f.mouse; } - int mod() { return this->f.mod; } - int width() { return this->f.width; } - int height() { return this->f.height; } -}; -#endif /* __cplusplus */ - -#endif /* !FENSTER_HEADER */ -#endif /* FENSTER_H */ diff --git a/scratchpad/fenstertest.cpp b/scratchpad/fenstertest.cpp deleted file mode 100644 index 612c77b..0000000 --- a/scratchpad/fenstertest.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "fenster/fenster.h" -#include "miniaudio.h" - -#define W 480*2 -#define H 480 - -static int run() { - Fenster f(W, H, "hello c++"); - int t = 0; - while (f.loop(60)) { - for (int i = 0; i < W; i++) { - for (int j = 0; j < H; j++) { - f.px(i, j) = i ^ j ^ t; - } - } - if (f.key(0x1b)) { - break; - } - t++; - } - return 0; -} - -#if defined(_WIN32) -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, - int nCmdShow) { - (void)hInstance, (void)hPrevInstance, (void)pCmdLine, (void)nCmdShow; - return run(); -} -#else -int main() { return run(); } -#endif diff --git a/scratchpad/quickcg.cpp b/scratchpad/quickcg.cpp deleted file mode 100644 index 123e6d6..0000000 --- a/scratchpad/quickcg.cpp +++ /dev/null @@ -1,2010 +0,0 @@ -/* -QuickCG SDL2 20190709 - -Copyright (c) 2004-2007, Lode Vandevenne - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -/* -QuickCG is an SDL codebase that wraps some of the SDL functionality. -It's used by Lode's Computer Graphics Tutorial to work with simple C++ calls -to demonstrate graphical programs. This SDL2.0 port was made by Chrissy573. - -QuickCG can handle some things that standard C++ doesn't but that are commonly useful, such as: --drawing graphics --a bitmap font --simplified saving and loading of files --reading keyboard and mouse input --playing sound --color models --loading images -*/ - -#include -#include "quickcg.h" -#include -#include -#include -#include -#include -#include -#include - -namespace QuickCG -{ - - - //////////////////////////////////////////////////////////////////////////////// - //VARIABLES///////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - - int w; //width of the screen - int h; //height of the screen - - std::map keypressed; //for the "keyPressed" function to detect a keypress only once - SDL_Window* scr; //the single SDL window used - SDL_Surface* srf; - SDL_Renderer* render; - SDL_Texture* tex; - SDL_PixelFormat *fmt; - const Uint8* inkeys = NULL; - SDL_Event event = { 0 }; - - - //////////////////////////////////////////////////////////////////////////////// - //KEYBOARD FUNCTIONS//////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - - bool keyDown(int key) //this checks if the key is held down, returns true all the time until the key is up - { - assert(inkeys != NULL && "inkeys is not initialized!"); - // return (inkeys[key] != 0); - return false; - } - - bool keyPressed(int key) //this checks if the key is *just* pressed, returns true only once until the key is up again - { - if (keypressed.find(key) == keypressed.end()) keypressed[key] = false; - if (inkeys[key]) - { - if (keypressed[key] == false) - { - keypressed[key] = true; - return true; - } - } - else keypressed[key] = false; - - return false; - } - - - //////////////////////////////////////////////////////////////////////////////// - //BASIC SCREEN FUNCTIONS//////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - - //The screen function: sets up the window for 32-bit color graphics. - //Creates a graphical screen of width*height pixels in 32-bit color. - //Set fullscreen to 0 for a window, or to 1 for fullscreen output - //text is the caption or title of the window - //also inits SDL - void screen(int width, int height, bool fullscreen, const std::string& text) - { - w = width; - h = height; - - if (SDL_Init(SDL_INIT_EVERYTHING) < 0) - { - printf("Unable to init SDL: %s\n", SDL_GetError()); - SDL_Quit(); - std::exit(1); - } - - std::atexit(SDL_Quit); - if (fullscreen) - { - scr = SDL_CreateWindow(text.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL); - lock(); - } - else - { - scr = SDL_CreateWindow(text.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_OPENGL); - } - if (scr == NULL) - { - printf("Unable to set video: %s\n", SDL_GetError()); - SDL_Quit(); - std::exit(1); - } - - render = SDL_CreateRenderer(scr, -1, 0); - if (render == NULL) - { - printf("Unable to set renderer: %s\n", SDL_GetError()); - SDL_Quit(); - std::exit(1); - } - - srf = SDL_CreateRGBSurface(0, w, h, 32, - 0xFF000000, - 0x00FF0000, - 0x0000FF00, - 0x000000FF); - if (srf == NULL) - { - printf("Unable to set surface: %s\n", SDL_GetError()); - SDL_Quit(); - std::exit(1); - } - - tex = SDL_CreateTexture(render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h); - if (tex == NULL) - { - printf("Unable to set texture: %s\n", SDL_GetError()); - SDL_Quit(); - std::exit(1); - } - SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND); - - fmt = srf->format; - if (fmt == NULL) - { - printf("Unable to set pixel format: %s\n", SDL_GetError()); - SDL_Quit(); - std::exit(1); - } - - inkeys = SDL_GetKeyboardState(NULL); - } - - //Locks the screen - void lock() - { - if (SDL_MUSTLOCK(srf)) - if (SDL_LockSurface(srf) < 0) - return; - } - - //Unlocks the screen - void unlock() - { - if (SDL_MUSTLOCK(srf)) - SDL_UnlockSurface(srf); - } - - //Updates the screen. Has to be called to view new pixels, but use only after - //drawing the whole screen because it's slow. - void redraw() - { - SDL_UpdateTexture(tex, NULL, srf->pixels, srf->pitch); - SDL_RenderClear(render); - SDL_RenderCopy(render, tex, NULL, NULL); - SDL_RenderPresent(render); - } - - //Clears the screen to black - void cls(const ColorRGBA& color) - { - SDL_SetRenderDrawColor(render, color.r, color.g, color.b, color.a); - SDL_RenderClear(render); - SDL_RenderPresent(render); - } - - //Puts an RGBA color pixel at position x,y - void pset(int x, int y, const ColorRGBA& color) - { - if (x < 0 || y < 0 || x >= w || y >= h) return; - Uint32 colorSDL = SDL_MapRGBA(fmt, color.r, color.g, color.b, color.a); - Uint32* bufp; - bufp = (Uint32*)srf->pixels + y * srf->pitch / 4 + x; - *bufp = colorSDL; - } - - //Gets RGB color of pixel at position x,y - ColorRGBA pget(int x, int y) - { - if (x < 0 || y < 0 || x >= w || y >= h) return RGB_Black; - Uint32* bufp; - bufp = (Uint32*)srf->pixels + y * srf->pitch / 4 + x; - Uint32 colorSDL = *bufp; - ColorRGBA8bit colorRGBA; - SDL_GetRGBA(colorSDL, fmt, &colorRGBA.r, &colorRGBA.g, &colorRGBA.b, &colorRGBA.a); - return ColorRGBA(colorRGBA); - } - - //Draws a buffer of pixels to the screen - void drawBuffer(Uint32* buffer) - { - Uint32* bufp; - bufp = (Uint32*)srf->pixels; - - for (int y = 0; y < h; y++) - { - for (int x = 0; x < w; x++) - { - *bufp = buffer[y * w + x]; - bufp++; - } - bufp += srf->pitch / 4; - bufp -= w; - } - } - - void getScreenBuffer(std::vector& buffer) - { - Uint32* bufp; - bufp = (Uint32*)srf->pixels; - - buffer.resize(w * h); - - for (int y = 0; y < h; y++) - { - for (int x = 0; x < w; x++) - { - buffer[y * w + x] = *bufp; - bufp++; - } - bufp += srf->pitch / 4; - bufp -= w; - } - } - - bool onScreen(int x, int y) - { - return (x >= 0 && y >= 0 && x < w && y < h); - } - - - //////////////////////////////////////////////////////////////////////////////// - //NON GRAPHICAL FUNCTIONS/////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - - //Waits until you press a key. First the key has to be loose, this means, if you put two sleep functions in a row, the second will only work after you first released the key. - void sleep() - { - int done = 0; - SDL_PollEvent(&event); - while (done == 0) - { - while (SDL_PollEvent(&event)) - { - if (event.type == SDL_QUIT) end(); - if (event.type == SDL_KEYDOWN) done = 1; - } - SDL_Delay(5); //so it consumes less processing power - } - } - - void sleep(double seconds) - { - SDL_Delay((Uint32)seconds * 1000); - } - - void waitFrame(double oldTime, double frameDuration) //in seconds - { - double time = getTime(); - while (time - oldTime < frameDuration) - { - time = getTime(); - SDL_PollEvent(&event); - if (event.type == SDL_QUIT) end(); - inkeys = SDL_GetKeyboardState(NULL); - if (inkeys[SDLK_ESCAPE]) end(); - SDL_Delay(5); //so it consumes less processing power - } - } - - //Returns 1 if you close the window or press the escape key. Also handles everything that's needed per frame. - //Never put key input code right before done() or SDL may see the key as SDL_QUIT - bool done(bool quit_if_esc, bool delay) //delay makes CPU have some free time, use once per frame to avoid 100% usage of a CPU core - { - if (delay) SDL_Delay(5); //so it consumes less processing power - bool done = false; - if (!SDL_PollEvent(&event)) return 0; - readKeys(); - if (quit_if_esc && inkeys[SDLK_ESCAPE]) done = true; - if (event.type == SDL_QUIT) done = true; - return done; - } - - //Ends the program - void end() - { - SDL_Quit(); - std::exit(1); - } - - //Gives value of pressed key to inkeys. - //the variable inkeys can then be used anywhere to check for input - //Normally you have to use readkeys every time you want to use inkeys, but the done() function also uses inkeys so it's not needed to use readkeys if you use done(). - void readKeys() - { - SDL_PollEvent(&event); - inkeys = SDL_GetKeyboardState(NULL); - } - - void getMouseState(int& mouseX, int& mouseY) - { - SDL_GetMouseState(&mouseX, &mouseY); - } - - void getMouseState(int& mouseX, int& mouseY, bool& LMB, bool& RMB) - { - Uint8 mouseState = SDL_GetMouseState(&mouseX, &mouseY); - - if (mouseState & 1) LMB = true; - else LMB = false; - if (mouseState & 4) RMB = true; - else RMB = false; - } - - //Returns the time in milliseconds since the program started - unsigned long getTicks() - { - return SDL_GetTicks(); - } - - - //////////////////////////////////////////////////////////////////////////////// - //2D SHAPES///////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - - //Fast horizontal line from (x1,y) to (x2,y), with rgb color - bool horLine(int y, int x1, int x2, const ColorRGBA& color) - { - if (x2 < x1) { x1 += x2; x2 = x1 - x2; x1 -= x2; } //swap x1 and x2, x1 must be the leftmost endpoint - if (x2 < 0 || x1 >= w || y < 0 || y >= h) return 0; //no single point of the line is on screen - if (x1 < 0) x1 = 0; //clip - if (x2 >= w) x2 = w - 1; //clip - - Uint32 colorSDL = SDL_MapRGBA(fmt, color.r, color.g, color.b, color.a); - Uint32* bufp; - bufp = (Uint32*)srf->pixels + y * srf->pitch / 4 + x1; - for (int x = x1; x <= x2; x++) - { - *bufp = colorSDL; - bufp++; - } - return 1; - } - - - //Fast vertical line from (x,y1) to (x,y2), with rgb color - bool verLine(int x, int y1, int y2, const ColorRGBA& color) - { - if (y2 < y1) { y1 += y2; y2 = y1 - y2; y1 -= y2; } //swap y1 and y2 - if (y2 < 0 || y1 >= h || x < 0 || x >= w) return 0; //no single point of the line is on screen - if (y1 < 0) y1 = 0; //clip - if (y2 >= w) y2 = h - 1; //clip - - Uint32 colorSDL = SDL_MapRGBA(fmt, color.r, color.g, color.b, color.a); - Uint32* bufp; - - bufp = (Uint32*)srf->pixels + y1 * srf->pitch / 4 + x; - for (int y = y1; y <= y2; y++) - { - *bufp = colorSDL; - bufp += srf->pitch / 4; - } - return 1; - } - - - //Bresenham line from (x1,y1) to (x2,y2) with rgb color - bool drawLine(int x1, int y1, int x2, int y2, const ColorRGBA& color) - { - if (x1 < 0 || x1 > w - 1 || x2 < 0 || x2 > w - 1 || y1 < 0 || y1 > h - 1 || y2 < 0 || y2 > h - 1) return 0; - - int deltax = std::abs(x2 - x1); //The difference between the x's - int deltay = std::abs(y2 - y1); //The difference between the y's - int x = x1; //Start x off at the first pixel - int y = y1; //Start y off at the first pixel - int xinc1, xinc2, yinc1, yinc2, den, num, numadd, numpixels, curpixel; - - if (x2 >= x1) //The x-values are increasing - { - xinc1 = 1; - xinc2 = 1; - } - else //The x-values are decreasing - { - xinc1 = -1; - xinc2 = -1; - } - if (y2 >= y1) //The y-values are increasing - { - yinc1 = 1; - yinc2 = 1; - } - else //The y-values are decreasing - { - yinc1 = -1; - yinc2 = -1; - } - if (deltax >= deltay) //There is at least one x-value for every y-value - { - xinc1 = 0; //Don't change the x when numerator >= denominator - yinc2 = 0; //Don't change the y for every iteration - den = deltax; - num = deltax / 2; - numadd = deltay; - numpixels = deltax; //There are more x-values than y-values - } - else //There is at least one y-value for every x-value - { - xinc2 = 0; //Don't change the x for every iteration - yinc1 = 0; //Don't change the y when numerator >= denominator - den = deltay; - num = deltay / 2; - numadd = deltax; - numpixels = deltay; //There are more y-values than x-values - } - for (curpixel = 0; curpixel <= numpixels; curpixel++) - { - pset(x % w, y % h, color); //Draw the current pixel - num += numadd; //Increase the numerator by the top of the fraction - if (num >= den) //Check if numerator >= denominator - { - num -= den; //Calculate the new numerator value - x += xinc1; //Change the x as appropriate - y += yinc1; //Change the y as appropriate - } - x += xinc2; //Change the x as appropriate - y += yinc2; //Change the y as appropriate - } - - return 1; - } - - - //Bresenham circle with center at (xc,yc) with radius and red green blue color - bool drawCircle(int xc, int yc, int radius, const ColorRGBA& color) - { - if (xc - radius < 0 || xc + radius >= w || yc - radius < 0 || yc + radius >= h) return 0; - int x = 0; - int y = radius; - int p = 3 - (radius << 1); - int a, b, c, d, e, f, g, h; - while (x <= y) - { - a = xc + x; //8 pixels can be calculated at once thanks to the symmetry - b = yc + y; - c = xc - x; - d = yc - y; - e = xc + y; - f = yc + x; - g = xc - y; - h = yc - x; - pset(a, b, color); - pset(c, d, color); - pset(e, f, color); - pset(g, f, color); - if (x > 0) //avoid drawing pixels at same position as the other ones - { - pset(a, d, color); - pset(c, b, color); - pset(e, h, color); - pset(g, h, color); - } - if (p < 0) p += (x++ << 2) + 6; - else p += ((x++ - y--) << 2) + 10; - } - - return 1; - } - - - //Filled bresenham circle with center at (xc,yc) with radius and red green blue color - bool drawDisk(int xc, int yc, int radius, const ColorRGBA& color) - { - if (xc + radius < 0 || xc - radius >= w || yc + radius < 0 || yc - radius >= h) return 0; //every single pixel outside screen, so don't waste time on it - int x = 0; - int y = radius; - int p = 3 - (radius << 1); - int a, b, c, d, e, f, g, h; - int pb = yc + radius + 1, pd = yc + radius + 1; //previous values: to avoid drawing horizontal lines multiple times (ensure initial value is outside the range) - while (x <= y) - { - // write data - a = xc + x; - b = yc + y; - c = xc - x; - d = yc - y; - e = xc + y; - f = yc + x; - g = xc - y; - h = yc - x; - if (b != pb) horLine(b, a, c, color); - if (d != pd) horLine(d, a, c, color); - if (f != b) horLine(f, e, g, color); - if (h != d && h != f) horLine(h, e, g, color); - pb = b; - pd = d; - if (p < 0) p += (x++ << 2) + 6; - else p += ((x++ - y--) << 2) + 10; - } - - return 1; - } - - //Rectangle with corners (x1,y1) and (x2,y2) and rgb color - bool drawRect(int x1, int y1, int x2, int y2, const ColorRGBA& color) - { - if (x1 < 0 || x1 > w - 1 || x2 < 0 || x2 > w - 1 || y1 < 0 || y1 > h - 1 || y2 < 0 || y2 > h - 1) return 0; - SDL_Rect rec; - rec.x = x1; - rec.y = y1; - rec.w = x2 - x1 + 1; - rec.h = y2 - y1 + 1; - Uint32 colorSDL = SDL_MapRGBA(fmt, color.r, color.g, color.b, color.a); - SDL_FillRect(srf, &rec, colorSDL); //SDL's ability to draw a hardware rectangle is used for now - return 1; - } - - //Functions for clipping a 2D line to the screen, which is the rectangle (0,0)-(w,h) - //This is the Cohen-Sutherland Clipping Algorithm - //Each of 9 regions gets an outcode, based on if it's at the top, bottom, left or right of the screen - // 1001 1000 1010 9 8 10 - // 0001 0000 0010 1 0 2 - // 0101 0100 0110 5 4 6 - //int findregion returns which of the 9 regions a point is in, void clipline does the actual clipping - int findRegion(int x, int y) - { - int code = 0; - if (y >= h) - code |= 1; //top - else if (y < 0) - code |= 2; //bottom - if (x >= w) - code |= 4; //right - else if (x < 0) - code |= 8; //left - return(code); - } - bool clipLine(int x1, int y1, int x2, int y2, int & x3, int & y3, int & x4, int & y4) - { - int code1, code2, codeout; - bool accept = 0, done = 0; - code1 = findRegion(x1, y1); //the region outcodes for the endpoints - code2 = findRegion(x2, y2); - do //In theory, this can never end up in an infinite loop, it'll always come in one of the trivial cases eventually - { - if (!(code1 | code2)) accept = done = 1; //accept because both endpoints are in screen or on the border, trivial accept - else if (code1 & code2) done = 1; //the line isn't visible on screen, trivial reject - else //if no trivial reject or accept, continue the loop - { - int x, y; - codeout = code1 ? code1 : code2; - if (codeout & 1) //top - { - x = x1 + (x2 - x1) * (h - y1) / (y2 - y1); - y = h - 1; - } - else if (codeout & 2) //bottom - { - x = x1 + (x2 - x1) * -y1 / (y2 - y1); - y = 0; - } - else if (codeout & 4) //right - { - y = y1 + (y2 - y1) * (w - x1) / (x2 - x1); - x = w - 1; - } - else //left - { - y = y1 + (y2 - y1) * -x1 / (x2 - x1); - x = 0; - } - if (codeout == code1) //first endpoint was clipped - { - x1 = x; y1 = y; - code1 = findRegion(x1, y1); - } - else //second endpoint was clipped - { - x2 = x; y2 = y; - code2 = findRegion(x2, y2); - } - } - } while (done == 0); - - if (accept) - { - x3 = x1; - x4 = x2; - y3 = y1; - y4 = y2; - return 1; - } - else - { - x3 = x4 = y3 = y4 = 0; - return 0; - } - } - - - //////////////////////////////////////////////////////////////////////////////// - //COLOR STRUCTS///////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ColorRGBA::ColorRGBA(Uint8 r, Uint8 g, Uint8 b, Uint8 a) - { - this->r = r; - this->g = g; - this->b = b; - this->a = a; - } - ColorRGBA::ColorRGBA(const ColorRGBA8bit& color) - { - this->r = color.r; - this->g = color.g; - this->b = color.b; - this->a = color.a; - } - ColorRGBA::ColorRGBA() - { - this->r = 0; - this->g = 0; - this->b = 0; - this->a = 0; - } - ColorRGBA8bit::ColorRGBA8bit(Uint8 r, Uint8 g, Uint8 b, Uint8 a) - { - this->r = r; - this->g = g; - this->b = b; - this->a = a; - } - ColorRGBA8bit::ColorRGBA8bit(const ColorRGBA& color) - { - this->r = color.r; - this->g = color.g; - this->b = color.b; - this->a = color.a; - } - ColorRGBA8bit::ColorRGBA8bit() - { - this->r = 0; - this->g = 0; - this->b = 0; - this->a = 0; - } - - //Add two colors - ColorRGBA operator+(const ColorRGBA& color, const ColorRGBA& color2) - { - ColorRGBA c; - c.r = color.r + color2.r; - c.g = color.g + color2.g; - c.b = color.b + color2.b; - c.a = color.a + color2.a; - return c; - } - - //Subtract two colors - ColorRGBA operator-(const ColorRGBA& color, const ColorRGBA& color2) - { - ColorRGBA c; - c.r = color.r - color2.r; - c.g = color.g - color2.g; - c.b = color.b - color2.b; - c.a = color.a - color2.a; - return c; - } - - //Multiplies a color with an integer - ColorRGBA operator*(const ColorRGBA& color, int a) - { - ColorRGBA c; - c.r = color.r * a; - c.g = color.g * a; - c.b = color.b * a; - c.a = color.a * a; - return c; - } - - //Multiplies a color with an integer - ColorRGBA operator*(int a, const ColorRGBA& color) - { - ColorRGBA c; - c.r = color.r * a; - c.g = color.g * a; - c.b = color.b * a; - c.a = color.a * a; - return c; - } - - //Divides a color through an integer - ColorRGBA operator/(const ColorRGBA& color, int a) - { - if (a == 0) return color; - ColorRGBA c; - c.r = color.r / a; - c.g = color.g / a; - c.b = color.b / a; - c.a = color.a / a; - return c; - } - - ColorRGBA overlay(const ColorRGBA& color, const ColorRGBA& color2) - { - ColorRGBA c; - c.r = (color.r * color.a / 255) + (color2.r * color2.a * (255 - color.a) / (255 * 255)); - c.g = (color.g * color.a / 255) + (color2.g * color2.a * (255 - color.a) / (255 * 255)); - c.b = (color.b * color.a / 255) + (color2.b * color2.a * (255 - color.a) / (255 * 255)); - c.a = color.a + (color2.a * (255 - color.a) / 255); - return c; - } - - //Are both colors equal? - bool operator==(const ColorRGBA& color, const ColorRGBA& color2) - { - return(color.r == color2.r && color.g == color2.g && color.b == color2.b && color.a == color2.a); - } - - //Are both colors not equal? - bool operator!=(const ColorRGBA& color, const ColorRGBA& color2) - { - return(!(color.r == color2.r && color.g == color2.g && color.b == color2.b && color.a == color2.a)); - } - - ColorHSL::ColorHSL(Uint8 h, Uint8 s, Uint8 l, Uint8 a) - { - this->h = h; - this->s = s; - this->l = l; - this->a = a; - } - ColorHSL::ColorHSL() - { - this->h = 0; - this->s = 0; - this->l = 0; - this->a = 0; - } - ColorHSV::ColorHSV(Uint8 h, Uint8 s, Uint8 v, Uint8 a) - { - this->h = h; - this->s = s; - this->v = v; - this->a = a; - } - ColorHSV::ColorHSV() - { - this->h = 0; - this->s = 0; - this->v = 0; - this->a = 0; - } - - - //////////////////////////////////////////////////////////////////////////////// - //COLOR CONVERSIONS///////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - /* - Convert colors from one type to another - r=red g=green b=blue h=hue s=saturation l=lightness v=value - Color components from the color structs are Uint8's between 0 and 255 - color components used in the calculations are normalized between 0.0-1.0 - */ - - //Converts an RGB color to HSL color - ColorHSL RGBtoHSL(const ColorRGBA& ColorRGBA) - { - double r, g, b, a, h = 0, s = 0, l; //this function works with floats between 0 and 1 - r = ColorRGBA.r / 256.0; - g = ColorRGBA.g / 256.0; - b = ColorRGBA.b / 256.0; - a = ColorRGBA.a / 256.0; - - const double maxColor = std::max(r, std::max(g, b)); - const double minColor = std::min(r, std::min(g, b)); - - if (minColor == maxColor) //R = G = B, so it's a shade of grey - { - h = 0; //it doesn't matter what value it has - s = 0; - l = r; //doesn't matter if you pick r, g, or b - } - else - { - l = (minColor + maxColor) / 2; - - if (l < 0.5) s = (maxColor - minColor) / (maxColor + minColor); - if (l >= 0.5) s = (maxColor - minColor) / (2.0 - maxColor - minColor); - - if (r == maxColor) h = (g - b) / (maxColor - minColor); - if (g == maxColor) h = 2.0 + (b - r) / (maxColor - minColor); - if (b == maxColor) h = 4.0 + (r - g) / (maxColor - minColor); - - h /= 6; //to bring it to a number between 0 and 1 - if (h < 0) h += 1; - } - - ColorHSL colorHSL; - colorHSL.h = int(h * 255.0); - colorHSL.s = int(s * 255.0); - colorHSL.l = int(l * 255.0); - colorHSL.a = int(a * 255.0); - return colorHSL; - } - - //Converts an HSL color to RGB color - ColorRGBA HSLtoRGB(const ColorHSL& colorHSL) - { - double r, g, b, a, h, s, l; //this function works with floats between 0 and 1 - double temp1, temp2, tempr, tempg, tempb; - h = colorHSL.h / 256.0; - s = colorHSL.s / 256.0; - l = colorHSL.l / 256.0; - a = colorHSL.a / 256.0; - - //If saturation is 0, the color is a shade of grey - if (s == 0) r = g = b = l; - //If saturation > 0, more complex calculations are needed - else - { - //set the temporary values - if (l < 0.5) temp2 = l * (1 + s); - else temp2 = (l + s) - (l * s); - temp1 = 2 * l - temp2; - tempr = h + 1.0 / 3.0; - if (tempr > 1.0) tempr--; - tempg = h; - tempb = h - 1.0 / 3.0; - if (tempb < 0.0) tempb++; - - //red - if (tempr < 1.0 / 6.0) r = temp1 + (temp2 - temp1) * 6.0 * tempr; - else if (tempr < 0.5) r = temp2; - else if (tempr < 2.0 / 3.0) r = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - tempr) * 6.0; - else r = temp1; - - //green - if (tempg < 1.0 / 6.0) g = temp1 + (temp2 - temp1) * 6.0 * tempg; - else if (tempg < 0.5) g = temp2; - else if (tempg < 2.0 / 3.0) g = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - tempg) * 6.0; - else g = temp1; - - //blue - if (tempb < 1.0 / 6.0) b = temp1 + (temp2 - temp1) * 6.0 * tempb; - else if (tempb < 0.5) b = temp2; - else if (tempb < 2.0 / 3.0) b = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - tempb) * 6.0; - else b = temp1; - } - - ColorRGBA ColorRGBA; - ColorRGBA.r = int(r * 255.0); - ColorRGBA.g = int(g * 255.0); - ColorRGBA.b = int(b * 255.0); - ColorRGBA.a = int(a * 255.0); - return ColorRGBA; - } - - //Converts an RGB color to HSV color - ColorHSV RGBtoHSV(const ColorRGBA& ColorRGBA) - { - double r, g, b, a, h = 0.0, s = 0.0, v; //this function works with floats between 0 and 1 - r = ColorRGBA.r / 256.0; - g = ColorRGBA.g / 256.0; - b = ColorRGBA.b / 256.0; - a = ColorRGBA.a / 256.0; - - const double maxColor = std::max(r, std::max(g, b)); - const double minColor = std::min(r, std::min(g, b)); - - v = maxColor; - - if (maxColor != 0.0) //avoid division by zero when the color is black - { - s = (maxColor - minColor) / maxColor; - } - - if (s == 0.0) - { - h = 0.0; //it doesn't matter what value it has - } - else - { - if (r == maxColor) h = (g - b) / (maxColor - minColor); - if (g == maxColor) h = 2.0 + (b - r) / (maxColor - minColor); - if (b == maxColor) h = 4.0 + (r - g) / (maxColor - minColor); - - h /= 6.0; //to bring it to a number between 0 and 1 - if (h < 0.0) h++; - } - - ColorHSV colorHSV; - colorHSV.h = int(h * 255.0); - colorHSV.s = int(s * 255.0); - colorHSV.v = int(v * 255.0); - colorHSV.a = int(a * 255.0); - return colorHSV; - } - - //Converts an HSV color to RGB color - ColorRGBA HSVtoRGB(const ColorHSV& colorHSV) - { - double r, g, b, a, h, s, v; //this function works with floats between 0 and 1 - h = colorHSV.h / 256.0; - s = colorHSV.s / 256.0; - v = colorHSV.v / 256.0; - a = colorHSV.a / 256.0; - - //if saturation is 0, the color is a shade of grey - if (s == 0.0) r = g = b = v; - //if saturation > 0, more complex calculations are needed - else - { - double f, p, q, t; - int i; - h *= 6.0; //to bring hue to a number between 0 and 6, better for the calculations - i = int(floor(h)); //e.g. 2.7 becomes 2 and 3.01 becomes 3 or 4.9999 becomes 4 - f = h - i;//the fractional part of h - - p = v * (1.0 - s); - q = v * (1.0 - (s * f)); - t = v * (1.0 - (s * (1.0 - f))); - - switch (i) - { - case 0: r = v; g = t; b = p; break; - case 1: r = q; g = v; b = p; break; - case 2: r = p; g = v; b = t; break; - case 3: r = p; g = q; b = v; break; - case 4: r = t; g = p; b = v; break; - case 5: r = v; g = p; b = q; break; - default: r = g = b = 0; break; - } - } - ColorRGBA ColorRGBA; - ColorRGBA.r = int(r * 255.0); - ColorRGBA.g = int(g * 255.0); - ColorRGBA.b = int(b * 255.0); - ColorRGBA.a = int(a * 255.0); - return ColorRGBA; - } - - Uint32 RGBtoINT(const ColorRGBA& ColorRGBA) - { - return (ColorRGBA.a | (ColorRGBA.b << 8) | (ColorRGBA.g << 16) | (ColorRGBA.r << 24)); - } - - ColorRGBA INTtoRGB(Uint32 colorINT) - { - ColorRGBA ColorRGBA; - ColorRGBA.r = (colorINT & 0xFF000000) >> 24; - ColorRGBA.g = (colorINT & 0x00FF0000) >> 16; - ColorRGBA.b = (colorINT & 0x0000FF00) >> 8; - ColorRGBA.a = (colorINT & 0x000000FF); - return ColorRGBA; - } - - //////////////////////////////////////////////////////////////////////////////// - //FILE FUNCTIONS//////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - void loadFile(std::vector& buffer, const std::string& filename) //designed for loading files from hard disk in an std::vector - { - std::ifstream file(filename.c_str(), std::ios::in | std::ios::binary | std::ios::ate); - - //get filesize - std::streamsize size = 0; - if (file.seekg(0, std::ios::end).good()) size = file.tellg(); - if (file.seekg(0, std::ios::beg).good()) size -= file.tellg(); - - //read contents of the file into the vector - buffer.resize(size_t(size)); - if (size > 0) file.read((char*)(&buffer[0]), size); - } - - //write given buffer to the file, overwriting the file, it doesn't append to it. - void saveFile(const std::vector& buffer, const std::string& filename) - { - std::ofstream file(filename.c_str(), std::ios::out | std::ios::binary); - file.write(buffer.size() ? (char*)&buffer[0] : 0, std::streamsize(buffer.size())); - } - - //////////////////////////////////////////////////////////////////////////////// - //IMAGE FUNCTIONS/////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - int loadImage(std::vector& out, unsigned long& w, unsigned long& h, const std::string& filename) - { - std::vector file, image; - loadFile(file, filename); - if (decodePNG(image, w, h, file)) return 1; - - out.resize(image.size() / 4); - - for (size_t i = 0; i < out.size(); i++) - { - out[i].r = image[i * 4 + 0]; - out[i].g = image[i * 4 + 1]; - out[i].b = image[i * 4 + 2]; - out[i].a = image[i * 4 + 3]; - } - - return 0; - } - - int loadImage(std::vector& out, unsigned long& w, unsigned long& h, const std::string& filename) - { - std::vector file, image; - loadFile(file, filename); - if (decodePNG(image, w, h, file)) return 1; - - out.resize(image.size() / 4); - - for (size_t i = 0; i < out.size(); i++) - { - out[i] = (Uint32)0x100000000 * image[i * 4 + 3] + 0x10000 * image[i * 4 + 0] + 0x100 * image[i * 4 + 1] + image[i * 4 + 2]; - } - - return 0; - } - - //////////////////////////////////////////////////////////////////////////////// - //TEXT FUNCTIONS//////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - //Draws character n at position x,y with color RGB and, if enabled, background color - //This function is used by the text printing functions below, and uses the font data - //defined below to draw the letter pixel by pixel - void drawLetter(unsigned char n, int x, int y, const ColorRGBA& color, bool bg, const ColorRGBA& color2) - { - int u, v; - - for (v = 0; v < 8; v++) - for (u = 0; u < 8; u++) - { - if (font[n][u][v]) pset(x + u, y + v, color); - else if (bg) pset(x + u, y + v, color2); - } - } - - //Draws a string of text - int printString(const std::string& text, int x, int y, const ColorRGBA& color, bool bg, const ColorRGBA& color2, int forceLength) - { - int amount = 0; - for (size_t i = 0; i < text.size(); i++) - { - amount++; - drawLetter(text[i], x, y, color, bg, color2); - x += 8; - if (x > w - 8) { x %= 8; y += 8; } - if (y > h - 8) { y %= 8; } - } - while (amount < forceLength) - { - amount++; - drawLetter(' ', x, y, color, bg, color2); - x += 8; - if (x > w - 8) { x %= 8; y += 8; } - if (y > h - 8) { y %= 8; } - } - return h * x + y; - } - - //////////////////////////////////////////////////////////////////////////////// - //TEXT INPUT FUNCTIONS////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - const int ASCII_ENTER = 13; - const int ASCII_BACKSPACE = 8; - const int ASCII_SPACE = 32; //smallest printable ascii char - - Uint8 getInputCharacter() - { - int ascii = 0; - static int previouschar = 0; - - if ((event.key.keysym.sym & 0xFF80) == 0) - { - if (event.type == SDL_KEYDOWN) - { - ascii = event.key.keysym.sym & 0x7F; - } - } - - if (ascii < ASCII_SPACE && ascii != ASCII_ENTER && ascii != ASCII_BACKSPACE) ascii = 0; //<32 ones, except enter and backspace - - if (ascii != previouschar) previouschar = ascii; - else ascii = 0; - - return ascii; - } - //returns a string, length is the maximum length of the given string array - void getInputString(std::string& text, const std::string& message, bool clear, int x, int y, const ColorRGBA& color, bool bg, const ColorRGBA& color2) - { - std::vector screenBuffer; - getScreenBuffer(screenBuffer); - - bool enter = 0; - bool change = 1; - text.clear(); - - while (enter == 0) - { - if (done()) end(); - Uint8 temp = getInputCharacter(); - if (temp >= ASCII_SPACE) - { - text.push_back(temp); - change = 1; - } - if (temp == ASCII_BACKSPACE && text.size() > 0) { text.resize(text.size() - 1); change = 1; } - - if (change) - { - drawBuffer(&screenBuffer[0]); - int pos = print(message, x, y, color, bg, color2); - int x2 = pos / h, y2 = pos % h; - print(text, x2, y2, color, bg, color2); - redraw(); - } - if (temp == ASCII_ENTER) { enter = 1; } - } - - //remove the input stuff from the screen again so there is room for possible next input - if (clear) - { - drawBuffer(&screenBuffer[0]); - redraw(); - } - } - - void encodeBase64(const std::vector& in, std::string& out) - { - const std::string characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - unsigned long bit24 = 0; - unsigned long sextet[4] = { 0, 0, 0, 0 }; - unsigned long octet[3] = { 0, 0, 0 }; - - out.clear(); - out.reserve((4 * in.size()) / 3); - - for (size_t pos = 0; pos < in.size(); pos += 3) - { - octet[0] = in[pos + 0]; - octet[1] = (pos + 1 < in.size()) ? in[pos + 1] : 0; - octet[2] = (pos + 2 < in.size()) ? in[pos + 2] : 0; - - bit24 = 256 * 256 * octet[0]; - bit24 += 256 * octet[1]; - bit24 += octet[2]; - - sextet[0] = (bit24 / (64 * 64 * 64)) % 64; - sextet[1] = (bit24 / (64 * 64)) % 64; - sextet[2] = (bit24 / (64)) % 64; - sextet[3] = (bit24 / (1)) % 64; - - for (size_t i = 0; i < 4; i++) - { - if (pos + i - 1 < in.size()) out.push_back(characters[sextet[i]]); - else out.push_back('='); - } - - if (pos % 57 == 0 && pos != 0) out.push_back(10); //newline char every 76 chars (57 = 3/4th of 76) - } - } - - void decodeBase64(std::vector& out, const std::string& in) - { - unsigned long bit24 = 0; - unsigned long sextet[4] = { 0, 0, 0, 0 }; - unsigned char octet[3] = { 0, 0, 0 }; - - out.clear(); - out.reserve((3 * in.size()) / 4); - - for (size_t pos = 0; pos < in.size() - 3; pos += 4) - { - for (size_t i = 0; i < 4; i++) - { - unsigned long c = in[pos + i]; - if (c >= 65 && c <= 90) sextet[i] = c - 65; - else if (c >= 97 && c <= 122) sextet[i] = c - 71; - else if (c >= 48 && c <= 57) sextet[i] = c + 4; - else if (c == '+') sextet[i] = 62; - else if (c == '/') sextet[i] = 63; - else if (c == '=') sextet[i] = 0; //value doesn't matter - else //unknown char, can be whitespace, newline, ... - { - pos++; - if (pos > in.size() - 3) return; - i--; - } - } - - bit24 = 64 * 64 * 64 * sextet[0]; - bit24 += 64 * 64 * sextet[1]; - bit24 += 64 * sextet[2]; - bit24 += sextet[3]; - - octet[0] = (bit24 / (256 * 256)) % 256; - octet[1] = (bit24 / (256)) % 256; - octet[2] = (bit24 / (1)) % 256; - - for (size_t i = 0; i < 3; i++) - { - if (in[pos + 1 + i] != '=') out.push_back(octet[i]); - } - } - } - - //////////////////////////////////////////////////////////////////////////////// - // PNG // - //////////////////////////////////////////////////////////////////////////////// - - int decodePNG(std::vector& out_image, unsigned long& image_width, unsigned long& image_height, const unsigned char* in_png, size_t in_size, bool convert_to_rgba32) - { - // picoPNG version 20101224 - // Copyright (c) 2005-2010 Lode Vandevenne - // - // This software is provided 'as-is', without any express or implied - // warranty. In no event will the authors be held liable for any damages - // arising from the use of this software. - // - // Permission is granted to anyone to use this software for any purpose, - // including commercial applications, and to alter it and redistribute it - // freely, subject to the following restrictions: - // - // 1. The origin of this software must not be misrepresented; you must not - // claim that you wrote the original software. If you use this software - // in a product, an acknowledgment in the product documentation would be - // appreciated but is not required. - // 2. Altered source versions must be plainly marked as such, and must not be - // misrepresented as being the original software. - // 3. This notice may not be removed or altered from any source distribution. - - static const unsigned long LENBASE[29] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 }; - static const unsigned long LENEXTRA[29] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; - static const unsigned long DISTBASE[30] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 }; - static const unsigned long DISTEXTRA[30] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; - static const unsigned long CLCL[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; //code length code lengths - struct Zlib //nested functions for zlib decompression - { - static unsigned long readBitFromStream(size_t& bitp, const unsigned char* bits) { unsigned long result = (bits[bitp >> 3] >> (bitp & 0x7)) & 1; bitp++; return result; } - static unsigned long readBitsFromStream(size_t& bitp, const unsigned char* bits, size_t nbits) - { - unsigned long result = 0; - for (size_t i = 0; i < nbits; i++) result += (readBitFromStream(bitp, bits)) << i; - return result; - } - struct HuffmanTree - { - int makeFromLengths(const std::vector& bitlen, unsigned long maxbitlen) - { //make tree given the lengths - unsigned long numcodes = (unsigned long)(bitlen.size()), treepos = 0, nodefilled = 0; - std::vector tree1d(numcodes), blcount(maxbitlen + 1, 0), nextcode(maxbitlen + 1, 0); - for (unsigned long bits = 0; bits < numcodes; bits++) blcount[bitlen[bits]]++; //count number of instances of each code length - for (unsigned long bits = 1; bits <= maxbitlen; bits++) nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1; - for (unsigned long n = 0; n < numcodes; n++) if (bitlen[n] != 0) tree1d[n] = nextcode[bitlen[n]]++; //generate all the codes - tree2d.clear(); tree2d.resize(numcodes * 2, 32767); //32767 here means the tree2d isn't filled there yet - for (unsigned long n = 0; n < numcodes; n++) //the codes - for (unsigned long i = 0; i < bitlen[n]; i++) //the bits for this code - { - unsigned long bit = (tree1d[n] >> (bitlen[n] - i - 1)) & 1; - if (treepos > numcodes - 2) return 55; - if (tree2d[2 * treepos + bit] == 32767) //not yet filled in - { - if (i + 1 == bitlen[n]) { tree2d[2 * treepos + bit] = n; treepos = 0; } //last bit - else { tree2d[2 * treepos + bit] = ++nodefilled + numcodes; treepos = nodefilled; } //addresses are encoded as values > numcodes - } - else treepos = tree2d[2 * treepos + bit] - numcodes; //subtract numcodes from address to get address value - } - return 0; - } - int decode(bool& decoded, unsigned long& result, size_t& treepos, unsigned long bit) const - { //Decodes a symbol from the tree - unsigned long numcodes = (unsigned long)tree2d.size() / 2; - if (treepos >= numcodes) return 11; //error: you appeared outside the codetree - result = tree2d[2 * treepos + bit]; - decoded = (result < numcodes); - treepos = decoded ? 0 : result - numcodes; - return 0; - } - std::vector tree2d; //2D representation of a huffman tree: The one dimension is "0" or "1", the other contains all nodes and leaves of the tree. - }; - struct Inflator - { - int error; - void inflate(std::vector& out, const std::vector& in, size_t inpos = 0) - { - size_t bp = 0, pos = 0; //bit pointer and byte pointer - error = 0; - unsigned long BFINAL = 0; - while (!BFINAL && !error) - { - if (bp >> 3 >= in.size()) { error = 52; return; } //error, bit pointer will jump past memory - BFINAL = readBitFromStream(bp, &in[inpos]); - unsigned long BTYPE = readBitFromStream(bp, &in[inpos]); BTYPE += 2 * readBitFromStream(bp, &in[inpos]); - if (BTYPE == 3) { error = 20; return; } //error: invalid BTYPE - else if (BTYPE == 0) inflateNoCompression(out, &in[inpos], bp, pos, in.size()); - else inflateHuffmanBlock(out, &in[inpos], bp, pos, in.size(), BTYPE); - } - if (!error) out.resize(pos); //Only now we know the true size of out, resize it to that - } - void generateFixedTrees(HuffmanTree& tree, HuffmanTree& treeD) //get the tree of a deflated block with fixed tree - { - std::vector bitlen(288, 8), bitlenD(32, 5);; - for (size_t i = 144; i <= 255; i++) bitlen[i] = 9; - for (size_t i = 256; i <= 279; i++) bitlen[i] = 7; - tree.makeFromLengths(bitlen, 15); - treeD.makeFromLengths(bitlenD, 15); - } - HuffmanTree codetree, codetreeD, codelengthcodetree; //the code tree for Huffman codes, dist codes, and code length codes - unsigned long huffmanDecodeSymbol(const unsigned char* in, size_t& bp, const HuffmanTree& codetree, size_t inlength) - { //decode a single symbol from given list of bits with given code tree. return value is the symbol - bool decoded; unsigned long ct; - for (size_t treepos = 0;;) - { - if ((bp & 0x07) == 0 && (bp >> 3) > inlength) { error = 10; return 0; } //error: end reached without endcode - error = codetree.decode(decoded, ct, treepos, readBitFromStream(bp, in)); if (error) return 0; //stop, an error happened - if (decoded) return ct; - } - } - void getTreeInflateDynamic(HuffmanTree& tree, HuffmanTree& treeD, const unsigned char* in, size_t& bp, size_t inlength) - { //get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree - std::vector bitlen(288, 0), bitlenD(32, 0); - if (bp >> 3 >= inlength - 2) { error = 49; return; } //the bit pointer is or will go past the memory - size_t HLIT = readBitsFromStream(bp, in, 5) + 257; //number of literal/length codes + 257 - size_t HDIST = readBitsFromStream(bp, in, 5) + 1; //number of dist codes + 1 - size_t HCLEN = readBitsFromStream(bp, in, 4) + 4; //number of code length codes + 4 - std::vector codelengthcode(19); //lengths of tree to decode the lengths of the dynamic tree - for (size_t i = 0; i < 19; i++) codelengthcode[CLCL[i]] = (i < HCLEN) ? readBitsFromStream(bp, in, 3) : 0; - error = codelengthcodetree.makeFromLengths(codelengthcode, 7); if (error) return; - size_t i = 0, replength; - while (i < HLIT + HDIST) - { - unsigned long code = huffmanDecodeSymbol(in, bp, codelengthcodetree, inlength); if (error) return; - if (code <= 15) { if (i < HLIT) bitlen[i++] = code; else bitlenD[i++ - HLIT] = code; } //a length code - else if (code == 16) //repeat previous - { - if (bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory - replength = 3 + readBitsFromStream(bp, in, 2); - unsigned long value; //set value to the previous code - if ((i - 1) < HLIT) value = bitlen[i - 1]; - else value = bitlenD[i - HLIT - 1]; - for (size_t n = 0; n < replength; n++) //repeat this value in the next lengths - { - if (i >= HLIT + HDIST) { error = 13; return; } //error: i is larger than the amount of codes - if (i < HLIT) bitlen[i++] = value; else bitlenD[i++ - HLIT] = value; - } - } - else if (code == 17) //repeat "0" 3-10 times - { - if (bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory - replength = 3 + readBitsFromStream(bp, in, 3); - for (size_t n = 0; n < replength; n++) //repeat this value in the next lengths - { - if (i >= HLIT + HDIST) { error = 14; return; } //error: i is larger than the amount of codes - if (i < HLIT) bitlen[i++] = 0; else bitlenD[i++ - HLIT] = 0; - } - } - else if (code == 18) //repeat "0" 11-138 times - { - if (bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory - replength = 11 + readBitsFromStream(bp, in, 7); - for (size_t n = 0; n < replength; n++) //repeat this value in the next lengths - { - if (i >= HLIT + HDIST) { error = 15; return; } //error: i is larger than the amount of codes - if (i < HLIT) bitlen[i++] = 0; else bitlenD[i++ - HLIT] = 0; - } - } - else { error = 16; return; } //error: somehow an unexisting code appeared. This can never happen. - } - if (bitlen[256] == 0) { error = 64; return; } //the length of the end code 256 must be larger than 0 - error = tree.makeFromLengths(bitlen, 15); if (error) return; //now we've finally got HLIT and HDIST, so generate the code trees, and the function is done - error = treeD.makeFromLengths(bitlenD, 15); if (error) return; - } - void inflateHuffmanBlock(std::vector& out, const unsigned char* in, size_t& bp, size_t& pos, size_t inlength, unsigned long btype) - { - if (btype == 1) { generateFixedTrees(codetree, codetreeD); } - else if (btype == 2) { getTreeInflateDynamic(codetree, codetreeD, in, bp, inlength); if (error) return; } - for (;;) - { - unsigned long code = huffmanDecodeSymbol(in, bp, codetree, inlength); if (error) return; - if (code == 256) return; //end code - else if (code <= 255) //literal symbol - { - if (pos >= out.size()) out.resize((pos + 1) * 2); //reserve more room - out[pos++] = (unsigned char)(code); - } - else if (code >= 257 && code <= 285) //length code - { - size_t length = LENBASE[code - 257], numextrabits = LENEXTRA[code - 257]; - if ((bp >> 3) >= inlength) { error = 51; return; } //error, bit pointer will jump past memory - length += readBitsFromStream(bp, in, numextrabits); - unsigned long codeD = huffmanDecodeSymbol(in, bp, codetreeD, inlength); if (error) return; - if (codeD > 29) { error = 18; return; } //error: invalid dist code (30-31 are never used) - unsigned long dist = DISTBASE[codeD], numextrabitsD = DISTEXTRA[codeD]; - if ((bp >> 3) >= inlength) { error = 51; return; } //error, bit pointer will jump past memory - dist += readBitsFromStream(bp, in, numextrabitsD); - size_t start = pos, back = start - dist; //backwards - if (pos + length >= out.size()) out.resize((pos + length) * 2); //reserve more room - for (size_t i = 0; i < length; i++) { out[pos++] = out[back++]; if (back >= start) back = start - dist; } - } - } - } - void inflateNoCompression(std::vector& out, const unsigned char* in, size_t& bp, size_t& pos, size_t inlength) - { - while ((bp & 0x7) != 0) bp++; //go to first boundary of byte - size_t p = bp / 8; - if (p >= inlength - 4) { error = 52; return; } //error, bit pointer will jump past memory - unsigned long LEN = in[p] + 256 * in[p + 1], NLEN = in[p + 2] + 256 * in[p + 3]; p += 4; - if (LEN + NLEN != 65535) { error = 21; return; } //error: NLEN is not one's complement of LEN - if (pos + LEN >= out.size()) out.resize(pos + LEN); - if (p + LEN > inlength) { error = 23; return; } //error: reading outside of in buffer - for (unsigned long n = 0; n < LEN; n++) out[pos++] = in[p++]; //read LEN bytes of literal data - bp = p * 8; - } - }; - int decompress(std::vector& out, const std::vector& in) //returns error value - { - Inflator inflator; - if (in.size() < 2) { return 53; } //error, size of zlib data too small - if ((in[0] * 256 + in[1]) % 31 != 0) { return 24; } //error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way - unsigned long CM = in[0] & 15, CINFO = (in[0] >> 4) & 15, FDICT = (in[1] >> 5) & 1; - if (CM != 8 || CINFO > 7) { return 25; } //error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec - if (FDICT != 0) { return 26; } //error: the specification of PNG says about the zlib stream: "The additional flags shall not specify a preset dictionary." - inflator.inflate(out, in, 2); - return inflator.error; //note: adler32 checksum was skipped and ignored - } - }; - struct PNG //nested functions for PNG decoding - { - struct Info - { - unsigned long width, height, colorType, bitDepth, compressionMethod, filterMethod, interlaceMethod, key_r, key_g, key_b, key_a; - bool key_defined; //is a transparent color key given? - std::vector palette; - } info; - int error; - void decode(std::vector& out, const unsigned char* in, size_t size, bool convert_to_rgba32) - { - error = 0; - if (size == 0 || in == 0) { error = 48; return; } //the given data is empty - readPngHeader(&in[0], size); if (error) return; - size_t pos = 33; //first byte of the first chunk after the header - std::vector idat; //the data from idat chunks - bool IEND = false, known_type = true; - info.key_defined = false; - while (!IEND) //loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer - { - if (pos + 8 >= size) { error = 30; return; } //error: size of the in buffer too small to contain next chunk - size_t chunkLength = read32bitInt(&in[pos]); pos += 4; - if (chunkLength > 2147483647) { error = 63; return; } - if (pos + chunkLength >= size) { error = 35; return; } //error: size of the in buffer too small to contain next chunk - if (in[pos + 0] == 'I' && in[pos + 1] == 'D' && in[pos + 2] == 'A' && in[pos + 3] == 'T') //IDAT chunk, containing compressed image data - { - idat.insert(idat.end(), &in[pos + 4], &in[pos + 4 + chunkLength]); - pos += (4 + chunkLength); - } - else if (in[pos + 0] == 'I' && in[pos + 1] == 'E' && in[pos + 2] == 'N' && in[pos + 3] == 'D') { pos += 4; IEND = true; } - else if (in[pos + 0] == 'P' && in[pos + 1] == 'L' && in[pos + 2] == 'T' && in[pos + 3] == 'E') //palette chunk (PLTE) - { - pos += 4; //go after the 4 letters - info.palette.resize(4 * (chunkLength / 3)); - if (info.palette.size() > (4 * 256)) { error = 38; return; } //error: palette too big - for (size_t i = 0; i < info.palette.size(); i += 4) - { - for (size_t j = 0; j < 4; j++) info.palette[i + j] = in[pos++]; //RGBA - } - } - else if (in[pos + 0] == 't' && in[pos + 1] == 'R' && in[pos + 2] == 'N' && in[pos + 3] == 'S') //palette transparency chunk (tRNS) - { - pos += 4; //go after the 4 letters - if (info.colorType == 3) - { - if (4 * chunkLength > info.palette.size()) { error = 39; return; } //error: more alpha values given than there are palette entries - for (size_t i = 0; i < chunkLength; i++) info.palette[4 * i + 3] = in[pos++]; - } - else if (info.colorType == 0) - { - if (chunkLength != 2) { error = 40; return; } //error: this chunk must be 2 bytes for greyscale image - info.key_defined = 1; info.key_r = info.key_g = info.key_b = 256 * in[pos] + in[pos + 1]; pos += 2; - } - else if (info.colorType == 2) - { - if (chunkLength != 6) { error = 41; return; } //error: this chunk must be 6 bytes for RGB image - info.key_defined = 1; - info.key_r = 256 * in[pos] + in[pos + 1]; pos += 2; - info.key_g = 256 * in[pos] + in[pos + 1]; pos += 2; - info.key_b = 256 * in[pos] + in[pos + 1]; pos += 2; - } - else { error = 42; return; } //error: tRNS chunk not allowed for other color models - } - else //it's not an implemented chunk type, so ignore it: skip over the data - { - if (!(in[pos + 0] & 32)) { error = 69; return; } //error: unknown critical chunk (5th bit of first byte of chunk type is 0) - pos += (chunkLength + 4); //skip 4 letters and uninterpreted data of unimplemented chunk - known_type = false; - } - pos += 4; //step over CRC (which is ignored) - } - unsigned long bpp = getBpp(info); - std::vector scanlines(((info.width * (info.height * bpp + 7)) / 8) + info.height); //now the out buffer will be filled - Zlib zlib; //decompress with the Zlib decompressor - error = zlib.decompress(scanlines, idat); if (error) return; //stop if the zlib decompressor returned an error - size_t bytewidth = (bpp + 7) / 8, outlength = (info.height * info.width * bpp + 7) / 8; - out.resize(outlength); //time to fill the out buffer - unsigned char* out_ = outlength ? &out[0] : 0; //use a regular pointer to the std::vector for faster code if compiled without optimization - if (info.interlaceMethod == 0) //no interlace, just filter - { - size_t linestart = 0, linelength = (info.width * bpp + 7) / 8; //length in bytes of a scanline, excluding the filtertype byte - if (bpp >= 8) //byte per byte - for (unsigned long y = 0; y < info.height; y++) - { - unsigned long filterType = scanlines[linestart]; - const unsigned char* prevline = (y == 0) ? 0 : &out_[(y - 1) * info.width * bytewidth]; - unFilterScanline(&out_[linestart - y], &scanlines[linestart + 1], prevline, bytewidth, filterType, linelength); if (error) return; - linestart += (1 + linelength); //go to start of next scanline - } - else //less than 8 bits per pixel, so fill it up bit per bit - { - std::vector templine((info.width * bpp + 7) >> 3); //only used if bpp < 8 - for (size_t y = 0, obp = 0; y < info.height; y++) - { - unsigned long filterType = scanlines[linestart]; - const unsigned char* prevline = (y == 0) ? 0 : &out_[(y - 1) * info.width * bytewidth]; - unFilterScanline(&templine[0], &scanlines[linestart + 1], prevline, bytewidth, filterType, linelength); if (error) return; - for (size_t bp = 0; bp < info.width * bpp;) setBitOfReversedStream(obp, out_, readBitFromReversedStream(bp, &templine[0])); - linestart += (1 + linelength); //go to start of next scanline - } - } - } - else //interlaceMethod is 1 (Adam7) - { - size_t passw[7] = { (info.width + 7) / 8, (info.width + 3) / 8, (info.width + 3) / 4, (info.width + 1) / 4, (info.width + 1) / 2, (info.width + 0) / 2, (info.width + 0) / 1 }; - size_t passh[7] = { (info.height + 7) / 8, (info.height + 7) / 8, (info.height + 3) / 8, (info.height + 3) / 4, (info.height + 1) / 4, (info.height + 1) / 2, (info.height + 0) / 2 }; - size_t passstart[7] = { 0 }; - size_t pattern[28] = { 0, 4, 0, 2, 0, 1, 0, 0, 0, 4, 0, 2, 0, 1, 8, 8, 4, 4, 2, 2, 1, 8, 8, 8, 4, 4, 2, 2 }; //values for the adam7 passes - for (int i = 0; i < 6; i++) passstart[i + 1] = passstart[i] + passh[i] * ((passw[i] ? 1 : 0) + (passw[i] * bpp + 7) / 8); - std::vector scanlineo((info.width * bpp + 7) / 8), scanlinen((info.width * bpp + 7) / 8); //"old" and "new" scanline - for (int i = 0; i < 7; i++) - adam7Pass(&out_[0], &scanlinen[0], &scanlineo[0], &scanlines[passstart[i]], info.width, pattern[i], pattern[i + 7], pattern[i + 14], pattern[i + 21], passw[i], passh[i], bpp); - } - if (convert_to_rgba32 && (info.colorType != 6 || info.bitDepth != 8)) //conversion needed - { - std::vector data = out; - error = convert(out, &data[0], info, info.width, info.height); - } - } - void readPngHeader(const unsigned char* in, size_t inlength) //read the information from the header and store it in the Info - { - if (inlength < 29) { error = 27; return; } //error: the data length is smaller than the length of the header - if (in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) { error = 28; return; } //no PNG signature - if (in[12] != 'I' || in[13] != 'H' || in[14] != 'D' || in[15] != 'R') { error = 29; return; } //error: it doesn't start with a IHDR chunk! - info.width = read32bitInt(&in[16]); info.height = read32bitInt(&in[20]); - info.bitDepth = in[24]; info.colorType = in[25]; - info.compressionMethod = in[26]; if (in[26] != 0) { error = 32; return; } //error: only compression method 0 is allowed in the specification - info.filterMethod = in[27]; if (in[27] != 0) { error = 33; return; } //error: only filter method 0 is allowed in the specification - info.interlaceMethod = in[28]; if (in[28] > 1) { error = 34; return; } //error: only interlace methods 0 and 1 exist in the specification - error = checkColorValidity(info.colorType, info.bitDepth); - } - void unFilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned long filterType, size_t length) - { - switch (filterType) - { - case 0: for (size_t i = 0; i < length; i++) recon[i] = scanline[i]; break; - case 1: - for (size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i]; - for (size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth]; - break; - case 2: - if (precon) for (size_t i = 0; i < length; i++) recon[i] = scanline[i] + precon[i]; - else for (size_t i = 0; i < length; i++) recon[i] = scanline[i]; - break; - case 3: - if (precon) - { - for (size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i] + precon[i] / 2; - for (size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2); - } - else - { - for (size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i]; - for (size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth] / 2; - } - break; - case 4: - if (precon) - { - for (size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i] + paethPredictor(0, precon[i], 0); - for (size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]); - } - else - { - for (size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i]; - for (size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + paethPredictor(recon[i - bytewidth], 0, 0); - } - break; - default: error = 36; return; //error: unexisting filter type given - } - } - void adam7Pass(unsigned char* out, unsigned char* linen, unsigned char* lineo, const unsigned char* in, unsigned long w, size_t passleft, size_t passtop, size_t spacex, size_t spacey, size_t passw, size_t passh, unsigned long bpp) - { //filter and reposition the pixels into the output when the image is Adam7 interlaced. This function can only do it after the full image is already decoded. The out buffer must have the correct allocated memory size already. - if (passw == 0) return; - size_t bytewidth = (bpp + 7) / 8, linelength = 1 + ((bpp * passw + 7) / 8); - for (unsigned long y = 0; y < passh; y++) - { - unsigned char filterType = in[y * linelength], *prevline = (y == 0) ? 0 : lineo; - unFilterScanline(linen, &in[y * linelength + 1], prevline, bytewidth, filterType, (w * bpp + 7) / 8); if (error) return; - if (bpp >= 8) for (size_t i = 0; i < passw; i++) for (size_t b = 0; b < bytewidth; b++) //b = current byte of this pixel - out[bytewidth * w * (passtop + spacey * y) + bytewidth * (passleft + spacex * i) + b] = linen[bytewidth * i + b]; - else for (size_t i = 0; i < passw; i++) - { - size_t obp = bpp * w * (passtop + spacey * y) + bpp * (passleft + spacex * i), bp = i * bpp; - for (size_t b = 0; b < bpp; b++) setBitOfReversedStream(obp, out, readBitFromReversedStream(bp, &linen[0])); - } - unsigned char* temp = linen; linen = lineo; lineo = temp; //swap the two buffer pointers "line old" and "line new" - } - } - static unsigned long readBitFromReversedStream(size_t& bitp, const unsigned char* bits) { unsigned long result = (bits[bitp >> 3] >> (7 - (bitp & 0x7))) & 1; bitp++; return result; } - static unsigned long readBitsFromReversedStream(size_t& bitp, const unsigned char* bits, unsigned long nbits) - { - unsigned long result = 0; - for (size_t i = nbits - 1; i < nbits; i--) result += ((readBitFromReversedStream(bitp, bits)) << i); - return result; - } - void setBitOfReversedStream(size_t& bitp, unsigned char* bits, unsigned long bit) { bits[bitp >> 3] |= (bit << (7 - (bitp & 0x7))); bitp++; } - unsigned long read32bitInt(const unsigned char* buffer) { return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; } - int checkColorValidity(unsigned long colorType, unsigned long bd) //return type is a LodePNG error code - { - if ((colorType == 2 || colorType == 4 || colorType == 6)) { if (!(bd == 8 || bd == 16)) return 37; else return 0; } - else if (colorType == 0) { if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; else return 0; } - else if (colorType == 3) { if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8)) return 37; else return 0; } - else return 31; //unexisting color type - } - unsigned long getBpp(const Info& info) - { - if (info.colorType == 2) return (3 * info.bitDepth); - else if (info.colorType >= 4) return (info.colorType - 2) * info.bitDepth; - else return info.bitDepth; - } - int convert(std::vector& out, const unsigned char* in, Info& infoIn, unsigned long w, unsigned long h) - { //converts from any color type to 32-bit. return value = LodePNG error code - size_t numpixels = w * h, bp = 0; - out.resize(numpixels * 4); - unsigned char* out_ = out.empty() ? 0 : &out[0]; //faster if compiled without optimization - if (infoIn.bitDepth == 8 && infoIn.colorType == 0) //greyscale - for (size_t i = 0; i < numpixels; i++) - { - out_[4 * i + 0] = out_[4 * i + 1] = out_[4 * i + 2] = in[i]; - out_[4 * i + 3] = (infoIn.key_defined && in[i] == infoIn.key_r) ? 0 : 255; - } - else if (infoIn.bitDepth == 8 && infoIn.colorType == 2) //RGB color - for (size_t i = 0; i < numpixels; i++) - { - for (size_t c = 0; c < 3; c++) out_[4 * i + c] = in[3 * i + c]; - out_[4 * i + 3] = (infoIn.key_defined == 1 && in[3 * i + 0] == infoIn.key_r && in[3 * i + 1] == infoIn.key_g && in[3 * i + 2] == infoIn.key_b) ? 0 : 255; - } - else if (infoIn.bitDepth == 8 && infoIn.colorType == 3) //indexed color (palette) - for (size_t i = 0; i < numpixels; i++) - { - if (4U * in[i] >= infoIn.palette.size()) return 46; - for (size_t c = 0; c < 4; c++) out_[4 * i + c] = infoIn.palette[4 * in[i] + c]; //get rgb colors from the palette - } - else if (infoIn.bitDepth == 8 && infoIn.colorType == 4) //greyscale with alpha - for (size_t i = 0; i < numpixels; i++) - { - out_[4 * i + 0] = out_[4 * i + 1] = out_[4 * i + 2] = in[2 * i + 0]; - out_[4 * i + 3] = in[2 * i + 1]; - } - else if (infoIn.bitDepth == 8 && infoIn.colorType == 6) for (size_t i = 0; i < numpixels; i++) for (size_t c = 0; c < 4; c++) out_[4 * i + c] = in[4 * i + c]; //RGB with alpha - else if (infoIn.bitDepth == 16 && infoIn.colorType == 0) //greyscale - for (size_t i = 0; i < numpixels; i++) - { - out_[4 * i + 0] = out_[4 * i + 1] = out_[4 * i + 2] = in[2 * i]; - out_[4 * i + 3] = (infoIn.key_defined && 256U * in[i] + in[i + 1] == infoIn.key_r) ? 0 : 255; - } - else if (infoIn.bitDepth == 16 && infoIn.colorType == 2) //RGB color - for (size_t i = 0; i < numpixels; i++) - { - for (size_t c = 0; c < 3; c++) out_[4 * i + c] = in[6 * i + 2 * c]; - out_[4 * i + 3] = (infoIn.key_defined && 256U * in[6 * i + 0] + in[6 * i + 1] == infoIn.key_r && 256U * in[6 * i + 2] + in[6 * i + 3] == infoIn.key_g && 256U * in[6 * i + 4] + in[6 * i + 5] == infoIn.key_b) ? 0 : 255; - } - else if (infoIn.bitDepth == 16 && infoIn.colorType == 4) //greyscale with alpha - for (size_t i = 0; i < numpixels; i++) - { - out_[4 * i + 0] = out_[4 * i + 1] = out_[4 * i + 2] = in[4 * i]; //most significant byte - out_[4 * i + 3] = in[4 * i + 2]; - } - else if (infoIn.bitDepth == 16 && infoIn.colorType == 6) for (size_t i = 0; i < numpixels; i++) for (size_t c = 0; c < 4; c++) out_[4 * i + c] = in[8 * i + 2 * c]; //RGB with alpha - else if (infoIn.bitDepth < 8 && infoIn.colorType == 0) //greyscale - for (size_t i = 0; i < numpixels; i++) - { - unsigned long value = (readBitsFromReversedStream(bp, in, infoIn.bitDepth) * 255) / ((1 << infoIn.bitDepth) - 1); //scale value from 0 to 255 - out_[4 * i + 0] = out_[4 * i + 1] = out_[4 * i + 2] = (unsigned char)(value); - out_[4 * i + 3] = (infoIn.key_defined && value && ((1U << infoIn.bitDepth) - 1U) == infoIn.key_r && ((1U << infoIn.bitDepth) - 1U)) ? 0 : 255; - } - else if (infoIn.bitDepth < 8 && infoIn.colorType == 3) //palette - for (size_t i = 0; i < numpixels; i++) - { - unsigned long value = readBitsFromReversedStream(bp, in, infoIn.bitDepth); - if (4 * value >= infoIn.palette.size()) return 47; - for (size_t c = 0; c < 4; c++) out_[4 * i + c] = infoIn.palette[4 * value + c]; //get rgb colors from the palette - } - return 0; - } - unsigned char paethPredictor(short a, short b, short c) //Paeth predicter, used by PNG filter type 4 - { - short p = a + b - c, pa = p > a ? (p - a) : (a - p), pb = p > b ? (p - b) : (b - p), pc = p > c ? (p - c) : (c - p); - return (unsigned char)((pa <= pb && pa <= pc) ? a : pb <= pc ? b : c); - } - }; - PNG decoder; decoder.decode(out_image, in_png, in_size, convert_to_rgba32); - image_width = decoder.info.width; image_height = decoder.info.height; - return decoder.error; - } - - int decodePNG(std::vector& out_image_32bit, unsigned long& image_width, unsigned long& image_height, const std::vector& in_png) - { - return decodePNG(out_image_32bit, image_width, image_height, in_png.size() ? &in_png[0] : 0, in_png.size()); - } - - - //////////////////////////////////////////////////////////////////////////////// - //DATA////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - - bool font[256][8][8]; - struct GenerateFont - { - GenerateFont() - { - /* - The full extended ASCII character set, in the form 256 bitmap symbols of 8x8 - pixels in one 128x128 PNG. - The background color is black, not transparent. - */ - - std::vector png, image; - loadFile(png, "charset.png"); - - unsigned long w, h; - decodePNG(image, w, h, &png[0], png.size()); - for (size_t c = 0; c < 256; c++) - for (size_t y = 0; y < 8; y++) - for (size_t x = 0; x < 8; x++) - { - font[c][x][y] = image[4 * 128 * (8 * (c / 16) + y) + 4 * (8 * (c % 16) + x)] != 0; - } - } - }; - GenerateFont generateFont; - - //////////////////////////////////////////////////////////////////////////////// - //Multithreading helper functions/////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - //SDL's C functions don't have destructors and such so therefore this here is needed - - //currently only needed for audio, therefor it's not in a different cpp file. - - //this creates SDL mutexes and makes sure that they're destroyed at the end. MutexFactory does the deletion! - struct MutexFactory - { - SDL_mutex* createMutex() - { - mutexes.push_back(SDL_CreateMutex()); - return mutexes.back(); - } - - ~MutexFactory() - { - for (size_t i = 0; i < mutexes.size(); i++) - SDL_DestroyMutex(mutexes[i]); - } - - private: - - std::vector mutexes; - }; - - MutexFactory mutexFactory; - - //this does SDL_mutexP in the ctor and SDL_mutexV in the dtor so no matter where you leave a function, SDL_mutexV is called - struct Mutex - { - SDL_mutex** m; - - Mutex(SDL_mutex*& mutex) - { - m = &mutex; - SDL_mutexP(*m); - } - - ~Mutex() - { - SDL_mutexV(*m); - } - }; - - //////////////////////////////////////////////////////////////////////////////// - //Soundcard functions/////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - size_t audio_min_samples = 4096; //safety buffer to avoid clicks - size_t audio_max_samples = 8192; //avoid too long queue - - double audio_volume = 1.0; - int audio_mode = 2; //0=off, 1=full (volume ignored), 2=volume-controlled - - void audioSetBufferSamplesRange(size_t min_samples, size_t max_samples) - { - audio_min_samples = min_samples; - audio_max_samples = max_samples; - } - - void audioSetMode(int mode) //0: silent, 1: full (no volume calculations ==> faster), 2: volume-controlled (= default value) - { - audio_mode = mode; - } - - void audioSetVolume(double volume) //multiplier used if mode is 2 (volume-controlled). Default value is 1.0. - { - audio_volume = volume; - } - - /* - Avoid the callback function and pushSamples function to be called at the same time, - or the std::vector can be invalid as two threads at the same time change it. - This SDL_mutex helps eliminate that problem. - */ - SDL_mutex* audio_lock = mutexFactory.createMutex(); - - std::vector audio_data(audio_min_samples, 0); - - SDL_AudioSpec audiospec_wanted, audiospec_obtained; - - size_t audioSamplesShortage() //returns value > 0 if the soundcard is consuming more samples than you're producing - { - if (audio_data.size() < audio_min_samples) return audio_min_samples - audio_data.size(); - else return 0; - } - - size_t audioSamplesOverflow() //returns value > 0 if you're producing more samples than the soundard is consuming - so take it easy a bit - { - if (audio_data.size() > audio_max_samples) return audio_data.size() - audio_max_samples; - else return 0; - } - - void audioCallback(void* /*userdata*/, Uint8* stream, int len) - { - Mutex mutex(audio_lock); - - size_t dataLengthLeft = audio_data.size(); - - //only play if we have data left - if (dataLengthLeft <= 0) return; - - int nsamples = len / 2; //always 16-bit, so always 2 bytes per sample, hence the amount of samples being len / 2 - size_t fill_len = (nsamples < dataLengthLeft ? nsamples : dataLengthLeft); - - for (int i = 0; i < nsamples; i++) - { - if (i < fill_len) - { - int s = int(audio_data[i] * 32768); - if (s < -32768) s = -32768; - if (s > 32767) s = 32767; - - stream[i * 2 + 0] = Uint8(s % 256); - stream[i * 2 + 1] = Uint8(s / 256); - } - else stream[i * 2 + 0] = stream[i * 2 + 1] = 0; - } - - audio_data.erase(audio_data.begin(), audio_data.begin() + fill_len); - } - - int audioOpen(int samplerate, int framesize) //always 16-bit mono sound for now - { - //set the audio format - audiospec_wanted.freq = samplerate; - audiospec_wanted.format = AUDIO_S16; - audiospec_wanted.channels = 1; //1 = mono, 2 = stereo - audiospec_wanted.samples = framesize; - audiospec_wanted.callback = audioCallback; - audiospec_wanted.userdata = NULL; - - /* - when using alsa and 44100 samples/second, then the framesize (samples) - will be 940 instead of 1024. Resampled to 48000Hz, this gives back 1024. - */ - - //open the audio device, forcing the wanted format - if (SDL_OpenAudio(&audiospec_wanted, &audiospec_obtained) < 0) - { - return 1; - } - - SDL_PauseAudio(0); - - return 0; - } - - void audioClose() - { - SDL_CloseAudio(); - } - - int audioReOpen() //closes and opens again with same parameters - { - SDL_PauseAudio(1); - SDL_CloseAudio(); - if (SDL_OpenAudio(&audiospec_wanted, &audiospec_obtained) < 0) - { - return 1; - } - SDL_PauseAudio(0); - - return 0; - } - - - //only works correct for 16 bit audio currently - void audioPushSamples(const std::vector& samples, size_t pos, size_t end) - { - if (audio_mode == 0) return; - - Mutex mutex(audio_lock); - - if (audio_mode == 1) - { - audio_data.insert(audio_data.end(), samples.begin() + pos, samples.begin() + end); - } - else if (audio_mode == 2) - { - size_t j = audio_data.size(); - audio_data.resize(j + samples.size()); - for (size_t i = 0; i < samples.size(); i++) - { - audio_data[j + i] = samples[i] * audio_volume; - } - } - } - - void audioPlay(const std::vector& samples) - { - if (audio_mode == 0) return; - - Mutex mutex(audio_lock); - - //the *current* time is at the first sample of audio_data, the rest has been played through soundcard already - - if (samples.size() > audio_data.size()) audio_data.resize(samples.size(), 0.0); - - if (audio_mode == 1) for (size_t i = 0; i < samples.size(); i++) audio_data[i] += samples[i]; - else if (audio_mode == 2) for (size_t i = 0; i < samples.size(); i++) audio_data[i] += samples[i] * audio_volume; - } - -} diff --git a/scratchpad/quickcg.h b/scratchpad/quickcg.h deleted file mode 100644 index 373507d..0000000 --- a/scratchpad/quickcg.h +++ /dev/null @@ -1,334 +0,0 @@ -/* -QuickCG SDL2 20190709 - -Copyright (c) 2004-2007, Lode Vandevenne - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -/* -QuickCG is an SDL 2.0 codebase that wraps some of the SDL 2.0 functionality. -It's used by Lode's Computer Graphics Tutorial to work with simple function calls -to demonstrate graphical programs. It may or may not be of industrial strength -for games, though I've actually used it for some. - -QuickCG can handle some things that standard C++ does not but that are useful, such as: --drawing graphics --a bitmap font --simplified saving and loading of files --reading keyboard and mouse input --playing sound --color models --loading images - -Contact info: -My email address is (puzzle the account and domain together with an @ symbol): -Domain: gmail dot com. -Account: lode dot vandevenne. -*/ - -#ifndef _quickcg_h_included -#define _quickcg_h_included - -#include - -#include -#include -#include -#include -#include //std::min and std::max - -namespace QuickCG -{ - -//////////////////////////////////////////////////////////////////////////////// -//useful templates////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -//don't know why, but the standard C++ abs sometimes gives cryptic errors? if so use this :D -template -const T template_abs(const T &a) -{ - return (a < 0) ? -a : a; -} - -//usage: std::string str = valtostr(25454.91654654f); -template -std::string valtostr(const T& val) -{ - std::ostringstream sstream; - sstream << val; - return sstream.str(); -} - -//usage: double val = strtoval("465498.654"); -template -T strtoval(const std::string& s) -{ - std::istringstream sstream(s); - T val; - sstream >> val; - return val; -} - -//length is decimal precision of the floating point number -template -std::string valtostr(const T& val, int length, bool fixed = true) -{ - std::ostringstream sstream; - if(fixed) sstream << std::fixed; - sstream << std::setprecision(length) << val; - return sstream.str(); -} - -//////////////////////////////////////////////////////////////////////////////// -//COLOR STRUCTS///////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -struct ColorRGBA8bit; -//a color with 4 components: r, g, b and a -struct ColorRGBA -{ - int r; - int g; - int b; - int a; - - ColorRGBA(Uint8 r, Uint8 g, Uint8 b, Uint8 a); - ColorRGBA(const ColorRGBA8bit& color); - ColorRGBA(); -}; - -ColorRGBA operator+(const ColorRGBA& color, const ColorRGBA& color2); -ColorRGBA operator-(const ColorRGBA& color, const ColorRGBA& color2); -ColorRGBA operator*(const ColorRGBA& color, int a); -ColorRGBA operator*(int a, const ColorRGBA& color); -ColorRGBA operator/(const ColorRGBA& color, int a); -ColorRGBA overlay(const ColorRGBA& color, const ColorRGBA& color2); -bool operator==(const ColorRGBA& color, const ColorRGBA& color2); -bool operator!=(const ColorRGBA& color, const ColorRGBA& color2); - -static const ColorRGBA RGB_Black ( 0, 0, 0, 255); -static const ColorRGBA RGB_Red (255, 0, 0, 255); -static const ColorRGBA RGB_Green ( 0, 255, 0, 255); -static const ColorRGBA RGB_Blue ( 0, 0, 255, 255); -static const ColorRGBA RGB_Cyan ( 0, 255, 255, 255); -static const ColorRGBA RGB_Magenta (255, 0, 255, 255); -static const ColorRGBA RGB_Yellow (255, 255, 0, 255); -static const ColorRGBA RGB_White (255, 255, 255, 255); -static const ColorRGBA RGB_Gray (128, 128, 128, 255); -static const ColorRGBA RGB_Grey (192, 192, 192, 255); -static const ColorRGBA RGB_Maroon (128, 0, 0, 255); -static const ColorRGBA RGB_Darkgreen( 0, 128, 0, 255); -static const ColorRGBA RGB_Navy ( 0, 0, 128, 255); -static const ColorRGBA RGB_Teal ( 0, 128, 128, 255); -static const ColorRGBA RGB_Purple (128, 0, 128, 255); -static const ColorRGBA RGB_Olive (128, 128, 0, 255); - -//a color with 4 components: r, g, b and a -struct ColorRGBA8bit -{ - Uint8 r; - Uint8 g; - Uint8 b; - Uint8 a; - - ColorRGBA8bit(Uint8 r, Uint8 g, Uint8 b, Uint8 a); - ColorRGBA8bit(const ColorRGBA& color); - ColorRGBA8bit(); -}; - -//a color with 3 components: h, s and l -struct ColorHSL -{ - int h; - int s; - int l; - int a; - - ColorHSL(Uint8 h, Uint8 s, Uint8 l, Uint8 a); - ColorHSL(); -}; - -//a color with 3 components: h, s and v -struct ColorHSV -{ - int h; - int s; - int v; - int a; - - ColorHSV(Uint8 h, Uint8 s, Uint8 v, Uint8 a); - ColorHSV(); -}; - -//////////////////////////////////////////////////////////////////////////////// -//GLOBAL VARIABLES////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -extern int w; -extern int h; - -//////////////////////////////////////////////////////////////////////////////// -//KEYBOARD FUNCTIONS//////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -bool keyDown(int key); //this checks if the key is held down, returns true all the time until the key is up -bool keyPressed(int key); //this checks if the key is *just* pressed, returns true only once until the key is up again - -//////////////////////////////////////////////////////////////////////////////// -//BASIC SCREEN FUNCTIONS//////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -void screen(int width = 640, int height = 400, bool fullscreen = 0, const std::string& text = " "); -void lock(); -void unlock(); -void redraw(); -void cls(const ColorRGBA& color = RGB_Black); -void pset(int x, int y, const ColorRGBA& color); -ColorRGBA pget(int x, int y); -void drawBuffer(Uint32* buffer); -bool onScreen(int x, int y); - -//////////////////////////////////////////////////////////////////////////////// -//NON GRAPHICAL FUNCTIONS/////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -void sleep(); -void sleep(double seconds); -void waitFrame(double oldTime, double frameDuration); //in seconds -bool done(bool quit_if_esc = true, bool delay = true); -void end(); -void readKeys(); -void getMouseState(int& mouseX, int& mouseY); -void getMouseState(int& mouseX, int& mouseY, bool& LMB, bool& RMB); -unsigned long getTicks(); //ticks in milliseconds -inline double getTime() { return getTicks() / 1000.0; } //time in seconds - -//////////////////////////////////////////////////////////////////////////////// -//2D SHAPES///////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -bool horLine(int y, int x1, int x2, const ColorRGBA& color); -bool verLine(int x, int y1, int y2, const ColorRGBA& color); -bool drawLine(int x1, int y1, int x2, int y2, const ColorRGBA& color); -bool drawCircle(int xc, int yc, int radius, const ColorRGBA& color); -bool drawDisk(int xc, int yc, int radius, const ColorRGBA& color); -bool drawRect(int x1, int y1, int x2, int y2, const ColorRGBA& color); -bool clipLine(int x1,int y1,int x2, int y2, int & x3, int & y3, int & x4, int & y4); - -//////////////////////////////////////////////////////////////////////////////// -//COLOR CONVERSIONS///////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -ColorHSL RGBtoHSL(const ColorRGBA& ColorRGBA); -ColorRGBA HSLtoRGB(const ColorHSL& colorHSL); -ColorHSV RGBtoHSV(const ColorRGBA& ColorRGBA); -ColorRGBA HSVtoRGB(const ColorHSV& colorHSV); -Uint32 RGBtoINT(const ColorRGBA& ColorRGBA); -ColorRGBA INTtoRGB(Uint32 colorINT); - -//////////////////////////////////////////////////////////////////////////////// -//FILE FUNCTIONS//////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -void loadFile(std::vector& buffer, const std::string& filename); -void saveFile(const std::vector& buffer, const std::string& filename); - -//////////////////////////////////////////////////////////////////////////////// -//IMAGE FUNCTIONS/////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -int loadImage(std::vector& out, unsigned long& w, unsigned long& h, const std::string& filename); -int loadImage(std::vector& out, unsigned long& w, unsigned long& h, const std::string& filename); -int decodePNG(std::vector& out_image, unsigned long& image_width, unsigned long& image_height, const unsigned char* in_png, size_t in_size, bool convert_to_rgba32 = true); -int decodePNG(std::vector& out_image_32bit, unsigned long& image_width, unsigned long& image_height, const std::vector& in_png); - -//////////////////////////////////////////////////////////////////////////////// -//TEXT FUNCTIONS//////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -extern bool font[256][8][8]; -void drawLetter(unsigned char n, int x, int y, const ColorRGBA& color = RGB_White, bool bg = 0, const ColorRGBA& color2 = RGB_Black); -int printString(const std::string& text, int x = 0, int y = 0, const ColorRGBA& color = RGB_White, bool bg = 0, const ColorRGBA& color2 = RGB_Black, int forceLength = 0); - -//print something (string, int, float, ...) -template -int print(const T& val, int x = 0, int y = 0, const ColorRGBA& color = RGB_White, bool bg = 0, const ColorRGBA& color2 = RGB_Black, int forceLength = 0) -{ - std::string text = valtostr(val); - return printString(text, x, y, color, bg, color2, forceLength); -} - -//print some floating point number, this one allows printing floating point numbers with limited length -template -int fprint(const T& val, int length, int x = 0, int y = 0, const ColorRGBA& color = RGB_White, bool bg = 0, const ColorRGBA& color2 = RGB_Black, int forceLength = 0) -{ - std::string text = valtostr(val, length, true); - return printString(text, x, y, color, bg, color2, forceLength); -} - -//////////////////////////////////////////////////////////////////////////////// -//TEXT INPUT FUNCTIONS////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -Uint8 getInputCharacter(); -void getInputString(std::string& text, const std::string& message = "", bool clear = false, int x = 0, int y = 0, const ColorRGBA& color = RGB_White, bool bg = 0, const ColorRGBA& color2 = RGB_Black); - -template -T getInput(const std::string& message = "", bool clear = false, int x = 0, int y = 0, const ColorRGBA& color = RGB_White, bool bg = 0, const ColorRGBA& color2 = RGB_Black) -{ - std::string text; - getInputString(text, message, clear, x, y, color, bg, color2); - return strtoval(text); -} - -//////////////////////////////////////////////////////////////////////////////// -//SOUNDCARD FUNCTIONS/////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -int audioOpen(int samplerate, int framesize); //always 16-bit mono sound for now; returns 0 if no error happened -void audioClose(); -int audioReOpen(); //closes and opens again with same parameters - -/* -push samples to the soundcard, making sure not to cause shortage or overflow -pos and end are the range in the samples vector that you want to push to the audio card -*/ -void audioPushSamples(const std::vector& samples, size_t pos, size_t end); - -size_t audioSamplesShortage(); //returns value > 0 if the soundcard is consuming more samples than you're producing -size_t audioSamplesOverflow(); //returns value > 0 if you're producing more samples than the soundard is consuming - so take it easy a bit -void audioSetBufferSamplesRange(size_t min_samples, size_t max_samples); //set shortage and overflow values. E.g. 4096 and 8192. - -/* -This plays the sound starting at this time, until it's done -The difference with audioPushSamples is: -audioPlay allows playing multiple sounds at the same time: it doesn't push at the end, -but elementwise-adds or pushes back samples if needed. -The duration depends on samplerate, make sure the samples in the vector have the correct samplerate. -*/ -void audioPlay(const std::vector& samples); - -void audioSetMode(int mode); //0: silent, 1: full (no volume calculations ==> faster), 2: volume-controlled (= default value) -void audioSetVolume(double volume); //multiplier used if mode is 2 (volume-controlled). Default value is 1.0. - -} //end of namespace QuickCG - -#endif - - diff --git a/scratchpad/raycaster.cpp b/scratchpad/raycaster.cpp deleted file mode 100644 index 8dda116..0000000 --- a/scratchpad/raycaster.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include -#include -#include -#include -#include "matrix.hpp" -#include - -using matrix::Matrix; -using namespace fmt; - -Matrix MAP{ - {1,1,1,1,1,1,1,1,1}, - {1,0,1,0,0,0,0,0,1}, - {1,0,1,0,0,1,1,0,1}, - {1,0,0,0,0,0,0,0,1}, - {1,1,0,0,0,0,0,0,1}, - {1,0,0,1,1,1,0,0,1}, - {1,0,0,0,1,0,0,0,1}, - {1,0,0,0,0,0,1,1,1}, - {1,1,1,1,1,1,1,1,1} -}; - -const int SCREEN_HEIGHT=480; -const int SCREEN_WIDTH=SCREEN_HEIGHT * 2; -const int MAP_SIZE=matrix::width(MAP); -const int TILE_SIZE=(SCREEN_WIDTH/2) / MAP_SIZE; -const float FOV = std::numbers::pi / 3.0; -const float HALF_FOV = FOV / 2; -const int CASTED_RAYS=30; -const float STEP_ANGLE = FOV / CASTED_RAYS; -const int MAX_DEPTH = MAP_SIZE * TILE_SIZE; -const float SCALE = (SCREEN_WIDTH / 2) / CASTED_RAYS; - -float player_x = SCREEN_WIDTH / 4; -float player_y = SCREEN_WIDTH / 4; -float player_angle = std::numbers::pi; - -void draw_rect(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, uint8_t color) { - sf::RectangleShape rect(size); - rect.setFillColor({color, color, color}); - rect.setPosition(pos); - window.draw(rect); -} - -void draw_map_rect(sf::RenderWindow &window, int x, int y, uint8_t color) { - draw_rect(window, - {float(x * TILE_SIZE), float(y * TILE_SIZE)}, - {float(TILE_SIZE-1), float(TILE_SIZE-1)}, - color); -} - -void draw_map(sf::RenderWindow &window, Matrix &map) { - uint8_t light_grey = 191; - uint8_t dark_grey = 65; - - for(size_t y = 0; y < matrix::height(map); y++) { - for(size_t x = 0; x < matrix::width(map); x++) { - draw_map_rect(window, x, y, map[y][x] == 1 ? light_grey : dark_grey); - } - } -} - -void draw_line(sf::RenderWindow &window, sf::Vector2f start, sf::Vector2f end) { - sf::Vertex line[] = { - sf::Vertex(start), - sf::Vertex(end) - }; - - window.draw(line, 2, sf::Lines); -} - -void draw_map_rays(sf::RenderWindow &window, int col, int row, sf::Vector2f target) { - draw_map_rect(window, col, row, 100); - draw_line(window, {player_x, player_y}, target); -} - -void draw_3d_view(sf::RenderWindow &window, int depth, float start_angle, int ray) { - uint8_t color = 255 / (1 + depth * depth * 0.0001); - - float fixed_depth = depth * std::cos(player_angle - start_angle); - - float wall_height = 21000 / fixed_depth; - - if(wall_height > SCREEN_HEIGHT){ - wall_height = SCREEN_HEIGHT; - } - - draw_rect(window, - {SCREEN_HEIGHT + ray * SCALE, (SCREEN_HEIGHT / 2) - wall_height / 2}, - {SCALE, wall_height}, - color); -} - -void ray_casting(sf::RenderWindow &window, Matrix& map) { - float start_angle = player_angle - HALF_FOV; - - for(int ray = 0; ray < CASTED_RAYS; ray++, start_angle += STEP_ANGLE) - { - for(int depth = 1; depth < MAX_DEPTH; depth++) { - float target_x = player_x - std::sin(start_angle) * depth; - float target_y = player_y + std::cos(start_angle) * depth; - - int col = int(target_x / TILE_SIZE); - int row = int(target_y / TILE_SIZE); - - if(map[row][col] == 1) { - draw_map_rays(window, col, row, {target_x, target_y}); - draw_3d_view(window, depth, start_angle, ray); - break; - } - } - } -} - -void draw_ceiling_floor(sf::RenderWindow &window) { - draw_rect(window, - {SCREEN_HEIGHT, SCREEN_HEIGHT /2}, - {SCREEN_HEIGHT, SCREEN_HEIGHT}, - 100); - draw_rect(window, - {SCREEN_HEIGHT, (SCREEN_HEIGHT * -1) / 2}, - {SCREEN_HEIGHT, SCREEN_HEIGHT}, - 200); -} - -void draw_everything(sf::RenderWindow &window) { - draw_map(window, MAP); - draw_ceiling_floor(window); - ray_casting(window, MAP); - window.display(); -} - -bool collision(float x, float y) { - int col = int(x / TILE_SIZE); - int row = int(y / TILE_SIZE); - - return MAP[row][col] == 1; -} - -int main() { - using KB = sf::Keyboard; - sf::RenderWindow window(sf::VideoMode(SCREEN_WIDTH, SCREEN_HEIGHT), "Raycaster"); - window.setVerticalSyncEnabled(true); - - while(window.isOpen()) { - draw_everything(window); - - float x = player_x; - float y = player_y; - - if(KB::isKeyPressed(KB::Q)) { - player_angle -= 0.1; - } else if(KB::isKeyPressed(KB::E)) { - player_angle += 0.1; - } - - if(KB::isKeyPressed(KB::W)) { - x += -1 * std::sin(player_angle) * 5; - y += std::cos(player_angle) * 5; - } else if(KB::isKeyPressed(KB::S)) { - x -= -1 * std::sin(player_angle) * 5; - y -= std::cos(player_angle) * 5; - } - - if(KB::isKeyPressed(KB::D)) { - x += -1 * std::sin(player_angle + std::numbers::pi * 0.5) * 5; - y += std::cos(player_angle + std::numbers::pi * 0.5) * 5; - } else if(KB::isKeyPressed(KB::A)) { - x -= -1 * std::sin(player_angle + std::numbers::pi * 0.5) * 5; - y -= std::cos(player_angle + std::numbers::pi * 0.5) * 5; - } - - if(!collision(x, y)) { - player_x = x; - player_y = y; - } - - sf::Event event; - while(window.pollEvent(event)) { - if(event.type == sf::Event::Closed) { - window.close(); - } - } - } - - return 0; -} diff --git a/scratchpad/raycaster_flat.cpp b/scratchpad/raycaster_flat.cpp deleted file mode 100644 index 06a72bb..0000000 --- a/scratchpad/raycaster_flat.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* -Copyright (c) 2004-2021, Lode Vandevenne - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include -#include -#include -#include - -#include "quickcg.h" -using namespace QuickCG; - -/* -g++ *.cpp -lSDL -O3 -W -Wall -ansi -pedantic -g++ *.cpp -lSDL -*/ - -//place the example code below here: - -#define screenWidth 640 -#define screenHeight 480 -#define mapWidth 24 -#define mapHeight 24 - -int worldMap[mapWidth][mapHeight]= -{ - {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, - {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,0,0,0,0,0,2,2,2,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1}, - {1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,3,0,0,0,3,0,0,0,1}, - {1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,0,0,0,0,0,2,2,0,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1}, - {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,4,0,0,0,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,4,0,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, - {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} -}; - -int main(int /*argc*/, char */*argv*/[]) -{ - double posX = 22, posY = 12; //x and y start position - double dirX = -1, dirY = 0; //initial direction vector - double planeX = 0, planeY = 0.66; //the 2d raycaster version of camera plane - - double time = 0; //time of current frame - double oldTime = 0; //time of previous frame - - screen(screenWidth, screenHeight, 0, "Raycaster"); - - while(!done()) - { - for(int x = 0; x < w; x++) - { - //calculate ray position and direction - double cameraX = 2 * x / (double)w - 1; //x-coordinate in camera space - double rayDirX = dirX + planeX * cameraX; - double rayDirY = dirY + planeY * cameraX; - //which box of the map we're in - int mapX = int(posX); - int mapY = int(posY); - - //length of ray from current position to next x or y-side - double sideDistX; - double sideDistY; - - //length of ray from one x or y-side to next x or y-side - //these are derived as: - //deltaDistX = sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX)) - //deltaDistY = sqrt(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY)) - //which can be simplified to abs(|rayDir| / rayDirX) and abs(|rayDir| / rayDirY) - //where |rayDir| is the length of the vector (rayDirX, rayDirY). Its length, - //unlike (dirX, dirY) is not 1, however this does not matter, only the - //ratio between deltaDistX and deltaDistY matters, due to the way the DDA - //stepping further below works. So the values can be computed as below. - // Division through zero is prevented, even though technically that's not - // needed in C++ with IEEE 754 floating point values. - double deltaDistX = (rayDirX == 0) ? 1e30 : std::abs(1 / rayDirX); - double deltaDistY = (rayDirY == 0) ? 1e30 : std::abs(1 / rayDirY); - - double perpWallDist; - - //what direction to step in x or y-direction (either +1 or -1) - int stepX; - int stepY; - - int hit = 0; //was there a wall hit? - int side; //was a NS or a EW wall hit? - //calculate step and initial sideDist - if(rayDirX < 0) - { - stepX = -1; - sideDistX = (posX - mapX) * deltaDistX; - } - else - { - stepX = 1; - sideDistX = (mapX + 1.0 - posX) * deltaDistX; - } - if(rayDirY < 0) - { - stepY = -1; - sideDistY = (posY - mapY) * deltaDistY; - } - else - { - stepY = 1; - sideDistY = (mapY + 1.0 - posY) * deltaDistY; - } - //perform DDA - while(hit == 0) - { - //jump to next map square, either in x-direction, or in y-direction - if(sideDistX < sideDistY) - { - sideDistX += deltaDistX; - mapX += stepX; - side = 0; - } - else - { - sideDistY += deltaDistY; - mapY += stepY; - side = 1; - } - //Check if ray has hit a wall - if(worldMap[mapX][mapY] > 0) hit = 1; - } - //Calculate distance projected on camera direction. This is the shortest distance from the point where the wall is - //hit to the camera plane. Euclidean to center camera point would give fisheye effect! - //This can be computed as (mapX - posX + (1 - stepX) / 2) / rayDirX for side == 0, or same formula with Y - //for size == 1, but can be simplified to the code below thanks to how sideDist and deltaDist are computed: - //because they were left scaled to |rayDir|. sideDist is the entire length of the ray above after the multiple - //steps, but we subtract deltaDist once because one step more into the wall was taken above. - if(side == 0) perpWallDist = (sideDistX - deltaDistX); - else perpWallDist = (sideDistY - deltaDistY); - - //Calculate height of line to draw on screen - int lineHeight = (int)(h / perpWallDist); - - //calculate lowest and highest pixel to fill in current stripe - int drawStart = -lineHeight / 2 + h / 2; - if(drawStart < 0) drawStart = 0; - int drawEnd = lineHeight / 2 + h / 2; - if(drawEnd >= h) drawEnd = h - 1; - - //choose wall color - ColorRGBA color; - switch(worldMap[mapX][mapY]) - { - case 1: color = RGB_Red; break; //red - case 2: color = RGB_Green; break; //green - case 3: color = RGB_Blue; break; //blue - case 4: color = RGB_White; break; //white - default: color = RGB_Yellow; break; //yellow - } - - //give x and y sides different brightness - if(side == 1) {color = color / 2;} - - //draw the pixels of the stripe as a vertical line - verLine(x, drawStart, drawEnd, color); - } - //timing for input and FPS counter - oldTime = time; - time = getTicks(); - double frameTime = (time - oldTime) / 1000.0; //frameTime is the time this frame has taken, in seconds - print(1.0 / frameTime); //FPS counter - redraw(); - - //speed modifiers - double moveSpeed = frameTime * 5.0; //the constant value is in squares/second - double rotSpeed = frameTime * 3.0; //the constant value is in radians/second - - SDL_Event event; - while(SDL_PollEvent(&event)) { - if(event.type != SDL_KEYDOWN) continue; - - cls(); - //move forward if no wall in front of you - if(event.key.keysym.sym == SDLK_UP) - { - if(worldMap[int(posX + dirX * moveSpeed)][int(posY)] == false) posX += dirX * moveSpeed; - if(worldMap[int(posX)][int(posY + dirY * moveSpeed)] == false) posY += dirY * moveSpeed; - } - //move backwards if no wall behind you - if(event.key.keysym.sym == SDLK_DOWN) - { - if(worldMap[int(posX - dirX * moveSpeed)][int(posY)] == false) posX -= dirX * moveSpeed; - if(worldMap[int(posX)][int(posY - dirY * moveSpeed)] == false) posY -= dirY * moveSpeed; - } - //rotate to the right - if(event.key.keysym.sym == SDLK_RIGHT) - { - //both camera direction and camera plane must be rotated - double oldDirX = dirX; - dirX = dirX * cos(-rotSpeed) - dirY * sin(-rotSpeed); - dirY = oldDirX * sin(-rotSpeed) + dirY * cos(-rotSpeed); - double oldPlaneX = planeX; - planeX = planeX * cos(-rotSpeed) - planeY * sin(-rotSpeed); - planeY = oldPlaneX * sin(-rotSpeed) + planeY * cos(-rotSpeed); - } - //rotate to the left - if(event.key.keysym.sym == SDLK_LEFT) - { - //both camera direction and camera plane must be rotated - double oldDirX = dirX; - dirX = dirX * cos(rotSpeed) - dirY * sin(rotSpeed); - dirY = oldDirX * sin(rotSpeed) + dirY * cos(rotSpeed); - double oldPlaneX = planeX; - planeX = planeX * cos(rotSpeed) - planeY * sin(rotSpeed); - planeY = oldPlaneX * sin(rotSpeed) + planeY * cos(rotSpeed); - } - } - } - - return 0; -} diff --git a/scratchpad/raycaster_floor.cpp b/scratchpad/raycaster_floor.cpp deleted file mode 100644 index 2e0ee83..0000000 --- a/scratchpad/raycaster_floor.cpp +++ /dev/null @@ -1,406 +0,0 @@ -/* -Copyright (c) 2004-2019, Lode Vandevenne - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include -#include -#include -#include - -#include "quickcg.h" -using namespace QuickCG; - -/* -g++ *.cpp -lSDL -O3 -W -Wall -ansi -pedantic -g++ *.cpp -lSDL -*/ - -// set to 1 to use the horizontal floor algorithm (contributed by Ádám Tóth in 2019), -// or to 0 to use the slower vertical floor algorithm. -#define FLOOR_HORIZONTAL 1 - -#define screenWidth 640 -#define screenHeight 480 -#define texWidth 64 // must be power of two -#define texHeight 64 // must be power of two -#define mapWidth 24 -#define mapHeight 24 - -int worldMap[mapWidth][mapHeight] = -{ - {8,8,8,8,8,8,8,8,8,8,8,4,4,6,4,4,6,4,6,4,4,4,6,4}, - {8,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,0,0,0,0,0,0,4}, - {8,0,3,3,0,0,0,0,0,8,8,4,0,0,0,0,0,0,0,0,0,0,0,6}, - {8,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6}, - {8,0,3,3,0,0,0,0,0,8,8,4,0,0,0,0,0,0,0,0,0,0,0,4}, - {8,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,6,6,6,0,6,4,6}, - {8,8,8,8,0,8,8,8,8,8,8,4,4,4,4,4,4,6,0,0,0,0,0,6}, - {7,7,7,7,0,7,7,7,7,0,8,0,8,0,8,0,8,4,0,4,0,6,0,6}, - {7,7,0,0,0,0,0,0,7,8,0,8,0,8,0,8,8,6,0,0,0,0,0,6}, - {7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,4}, - {7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,6,0,6,0,6}, - {7,7,0,0,0,0,0,0,7,8,0,8,0,8,0,8,8,6,4,6,0,6,6,6}, - {7,7,7,7,0,7,7,7,7,8,8,4,0,6,8,4,8,3,3,3,0,3,3,3}, - {2,2,2,2,0,2,2,2,2,4,6,4,0,0,6,0,6,3,0,0,0,0,0,3}, - {2,2,0,0,0,0,0,2,2,4,0,0,0,0,0,0,4,3,0,0,0,0,0,3}, - {2,0,0,0,0,0,0,0,2,4,0,0,0,0,0,0,4,3,0,0,0,0,0,3}, - {1,0,0,0,0,0,0,0,1,4,4,4,4,4,6,0,6,3,3,0,0,0,3,3}, - {2,0,0,0,0,0,0,0,2,2,2,1,2,2,2,6,6,0,0,5,0,5,0,5}, - {2,2,0,0,0,0,0,2,2,2,0,0,0,2,2,0,5,0,5,0,0,0,5,5}, - {2,0,0,0,0,0,0,0,2,0,0,0,0,0,2,5,0,5,0,5,0,5,0,5}, - {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5}, - {2,0,0,0,0,0,0,0,2,0,0,0,0,0,2,5,0,5,0,5,0,5,0,5}, - {2,2,0,0,0,0,0,2,2,2,0,0,0,2,2,0,5,0,5,0,0,0,5,5}, - {2,2,2,2,1,2,2,2,2,2,2,1,2,2,2,5,5,5,5,5,5,5,5,5} -}; - -Uint32 buffer[screenHeight][screenWidth]; // y-coordinate first because it works per scanline - -int main(int /*argc*/, char */*argv*/[]) -{ - double posX = 22.0, posY = 11.5; //x and y start position - double dirX = -1.0, dirY = 0.0; //initial direction vector - double planeX = 0.0, planeY = 0.66; //the 2d raycaster version of camera plane - - double time = 0; //time of current frame - double oldTime = 0; //time of previous frame - - std::vector texture[8]; - for(int i = 0; i < 8; i++) texture[i].resize(texWidth * texHeight); - - screen(screenWidth,screenHeight, 0, "Raycaster"); - - //load some textures - unsigned long tw, th, error = 0; - error |= loadImage(texture[0], tw, th, "pics/eagle.png"); - error |= loadImage(texture[1], tw, th, "pics/redbrick.png"); - error |= loadImage(texture[2], tw, th, "pics/purplestone.png"); - error |= loadImage(texture[3], tw, th, "pics/greystone.png"); - error |= loadImage(texture[4], tw, th, "pics/bluestone.png"); - error |= loadImage(texture[5], tw, th, "pics/mossy.png"); - error |= loadImage(texture[6], tw, th, "pics/wood.png"); - error |= loadImage(texture[7], tw, th, "pics/colorstone.png"); - if(error) { std::cout << "error loading images" << std::endl; return 1; } - - //start the main loop - while(!done()) - { -#if FLOOR_HORIZONTAL - //FLOOR CASTING - for(int y = screenHeight / 2 + 1; y < screenHeight; ++y) - { - // rayDir for leftmost ray (x = 0) and rightmost ray (x = w) - float rayDirX0 = dirX - planeX; - float rayDirY0 = dirY - planeY; - float rayDirX1 = dirX + planeX; - float rayDirY1 = dirY + planeY; - - // Current y position compared to the center of the screen (the horizon) - int p = y - screenHeight / 2; - - // Vertical position of the camera. - // NOTE: with 0.5, it's exactly in the center between floor and ceiling, - // matching also how the walls are being raycasted. For different values - // than 0.5, a separate loop must be done for ceiling and floor since - // they're no longer symmetrical. - float posZ = 0.5 * screenHeight; - - // Horizontal distance from the camera to the floor for the current row. - // 0.5 is the z position exactly in the middle between floor and ceiling. - // NOTE: this is affine texture mapping, which is not perspective correct - // except for perfectly horizontal and vertical surfaces like the floor. - // NOTE: this formula is explained as follows: The camera ray goes through - // the following two points: the camera itself, which is at a certain - // height (posZ), and a point in front of the camera (through an imagined - // vertical plane containing the screen pixels) with horizontal distance - // 1 from the camera, and vertical position p lower than posZ (posZ - p). When going - // through that point, the line has vertically traveled by p units and - // horizontally by 1 unit. To hit the floor, it instead needs to travel by - // posZ units. It will travel the same ratio horizontally. The ratio was - // 1 / p for going through the camera plane, so to go posZ times farther - // to reach the floor, we get that the total horizontal distance is posZ / p. - float rowDistance = posZ / p; - - // calculate the real world step vector we have to add for each x (parallel to camera plane) - // adding step by step avoids multiplications with a weight in the inner loop - float floorStepX = rowDistance * (rayDirX1 - rayDirX0) / screenWidth; - float floorStepY = rowDistance * (rayDirY1 - rayDirY0) / screenWidth; - - // real world coordinates of the leftmost column. This will be updated as we step to the right. - float floorX = posX + rowDistance * rayDirX0; - float floorY = posY + rowDistance * rayDirY0; - - for(int x = 0; x < screenWidth; ++x) - { - // the cell coord is simply got from the integer parts of floorX and floorY - int cellX = (int)(floorX); - int cellY = (int)(floorY); - - // get the texture coordinate from the fractional part - int tx = (int)(texWidth * (floorX - cellX)) & (texWidth - 1); - int ty = (int)(texHeight * (floorY - cellY)) & (texHeight - 1); - - floorX += floorStepX; - floorY += floorStepY; - - // choose texture and draw the pixel - int checkerBoardPattern = (int(cellX + cellY)) & 1; - int floorTexture; - if(checkerBoardPattern == 0) floorTexture = 3; - else floorTexture = 4; - int ceilingTexture = 6; - Uint32 color; - - // floor - color = texture[floorTexture][texWidth * ty + tx]; - color = (color >> 1) & 8355711; // make a bit darker - buffer[y][x] = color; - - //ceiling (symmetrical, at screenHeight - y - 1 instead of y) - color = texture[ceilingTexture][texWidth * ty + tx]; - color = (color >> 1) & 8355711; // make a bit darker - buffer[screenHeight - y - 1][x] = color; - } - } -#endif // FLOOR_HORIZONTAL - - // WALL CASTING - for(int x = 0; x < w; x++) - { - //calculate ray position and direction - double cameraX = 2 * x / double(w) - 1; //x-coordinate in camera space - double rayDirX = dirX + planeX * cameraX; - double rayDirY = dirY + planeY * cameraX; - - //which box of the map we're in - int mapX = int(posX); - int mapY = int(posY); - - //length of ray from current position to next x or y-side - double sideDistX; - double sideDistY; - - //length of ray from one x or y-side to next x or y-side - double deltaDistX = (rayDirX == 0) ? 1e30 : std::abs(1 / rayDirX); - double deltaDistY = (rayDirY == 0) ? 1e30 : std::abs(1 / rayDirY); - double perpWallDist; - - //what direction to step in x or y-direction (either +1 or -1) - int stepX; - int stepY; - - int hit = 0; //was there a wall hit? - int side; //was a NS or a EW wall hit? - - //calculate step and initial sideDist - if(rayDirX < 0) - { - stepX = -1; - sideDistX = (posX - mapX) * deltaDistX; - } - else - { - stepX = 1; - sideDistX = (mapX + 1.0 - posX) * deltaDistX; - } - if(rayDirY < 0) - { - stepY = -1; - sideDistY = (posY - mapY) * deltaDistY; - } - else - { - stepY = 1; - sideDistY = (mapY + 1.0 - posY) * deltaDistY; - } - //perform DDA - while (hit == 0) - { - //jump to next map square, either in x-direction, or in y-direction - if(sideDistX < sideDistY) - { - sideDistX += deltaDistX; - mapX += stepX; - side = 0; - } - else - { - sideDistY += deltaDistY; - mapY += stepY; - side = 1; - } - //Check if ray has hit a wall - if(worldMap[mapX][mapY] > 0) hit = 1; - } - - //Calculate distance of perpendicular ray (Euclidean distance would give fisheye effect!) - if(side == 0) perpWallDist = (sideDistX - deltaDistX); - else perpWallDist = (sideDistY - deltaDistY); - - //Calculate height of line to draw on screen - int lineHeight = (int)(h / perpWallDist); - - //calculate lowest and highest pixel to fill in current stripe - int drawStart = -lineHeight / 2 + h / 2; - if(drawStart < 0) drawStart = 0; - int drawEnd = lineHeight / 2 + h / 2; - if(drawEnd >= h) drawEnd = h - 1; - //texturing calculations - int texNum = worldMap[mapX][mapY] - 1; //1 subtracted from it so that texture 0 can be used! - - //calculate value of wallX - double wallX; //where exactly the wall was hit - if(side == 0) wallX = posY + perpWallDist * rayDirY; - else wallX = posX + perpWallDist * rayDirX; - wallX -= floor((wallX)); - - //x coordinate on the texture - int texX = int(wallX * double(texWidth)); - if(side == 0 && rayDirX > 0) texX = texWidth - texX - 1; - if(side == 1 && rayDirY < 0) texX = texWidth - texX - 1; - - // TODO: an integer-only bresenham or DDA like algorithm could make the texture coordinate stepping faster - // How much to increase the texture coordinate per screen pixel - double step = 1.0 * texHeight / lineHeight; - // Starting texture coordinate - double texPos = (drawStart - h / 2 + lineHeight / 2) * step; - for(int y = drawStart; y < drawEnd; y++) - { - // Cast the texture coordinate to integer, and mask with (texHeight - 1) in case of overflow - int texY = (int)texPos & (texHeight - 1); - texPos += step; - Uint32 color = texture[texNum][texHeight * texY + texX]; - //make color darker for y-sides: R, G and B byte each divided through two with a "shift" and an "and" - if(side == 1) color = (color >> 1) & 8355711; - buffer[y][x] = color; - } - -#if !FLOOR_HORIZONTAL - //FLOOR CASTING - double floorXWall, floorYWall; //x, y position of the floor texel at the bottom of the wall - - //4 different wall directions possible - if(side == 0 && rayDirX > 0) - { - floorXWall = mapX; - floorYWall = mapY + wallX; - } - else if(side == 0 && rayDirX < 0) - { - floorXWall = mapX + 1.0; - floorYWall = mapY + wallX; - } - else if(side == 1 && rayDirY > 0) - { - floorXWall = mapX + wallX; - floorYWall = mapY; - } - else - { - floorXWall = mapX + wallX; - floorYWall = mapY + 1.0; - } - - double distWall, distPlayer, currentDist; - - distWall = perpWallDist; - distPlayer = 0.0; - - if(drawEnd < 0) drawEnd = h; //becomes < 0 when the integer overflows - - //draw the floor from drawEnd to the bottom of the screen - for(int y = drawEnd + 1; y < h; y++) - { - currentDist = h / (2.0 * y - h); //you could make a small lookup table for this instead - - double weight = (currentDist - distPlayer) / (distWall - distPlayer); - - double currentFloorX = weight * floorXWall + (1.0 - weight) * posX; - double currentFloorY = weight * floorYWall + (1.0 - weight) * posY; - - int floorTexX, floorTexY; - floorTexX = int(currentFloorX * texWidth) & (texWidth - 1); - floorTexY = int(currentFloorY * texHeight) & (texHeight - 1); - - int checkerBoardPattern = ((int)currentFloorX + (int)currentFloorY) & 1; - int floorTexture; - if(checkerBoardPattern == 0) floorTexture = 3; - else floorTexture = 4; - - //floor - buffer[y][x] = (texture[floorTexture][texWidth * floorTexY + floorTexX] >> 1) & 8355711; - //ceiling (symmetrical) - buffer[h - y][x] = texture[6][texWidth * floorTexY + floorTexX]; - } -#endif // !FLOOR_HORIZONTAL - } - - drawBuffer(buffer[0]); - // No need to clear the screen here, since everything is overdrawn with floor and ceiling - - //timing for input and FPS counter - oldTime = time; - time = getTicks(); - double frameTime = (time - oldTime) / 1000.0; //frametime is the time this frame has taken, in seconds - print(1.0 / frameTime); //FPS counter - redraw(); - - //speed modifiers - double moveSpeed = frameTime * 3.0; //the constant value is in squares/second - double rotSpeed = frameTime * 2.0; //the constant value is in radians/second - readKeys(); - //move forward if no wall in front of you - if (keyDown(SDLK_UP)) - { - if(worldMap[int(posX + dirX * moveSpeed)][int(posY)] == false) posX += dirX * moveSpeed; - if(worldMap[int(posX)][int(posY + dirY * moveSpeed)] == false) posY += dirY * moveSpeed; - } - //move backwards if no wall behind you - if(keyDown(SDLK_DOWN)) - { - if(worldMap[int(posX - dirX * moveSpeed)][int(posY)] == false) posX -= dirX * moveSpeed; - if(worldMap[int(posX)][int(posY - dirY * moveSpeed)] == false) posY -= dirY * moveSpeed; - } - //rotate to the right - if(keyDown(SDLK_RIGHT)) - { - //both camera direction and camera plane must be rotated - double oldDirX = dirX; - dirX = dirX * cos(-rotSpeed) - dirY * sin(-rotSpeed); - dirY = oldDirX * sin(-rotSpeed) + dirY * cos(-rotSpeed); - double oldPlaneX = planeX; - planeX = planeX * cos(-rotSpeed) - planeY * sin(-rotSpeed); - planeY = oldPlaneX * sin(-rotSpeed) + planeY * cos(-rotSpeed); - } - //rotate to the left - if(keyDown(SDLK_LEFT)) - { - //both camera direction and camera plane must be rotated - double oldDirX = dirX; - dirX = dirX * cos(rotSpeed) - dirY * sin(rotSpeed); - dirY = oldDirX * sin(rotSpeed) + dirY * cos(rotSpeed); - double oldPlaneX = planeX; - planeX = planeX * cos(rotSpeed) - planeY * sin(rotSpeed); - planeY = oldPlaneX * sin(rotSpeed) + planeY * cos(rotSpeed); - } - } -} diff --git a/scratchpad/raycaster_pixels.cpp b/scratchpad/raycaster_pixels.cpp deleted file mode 100644 index cd3c5a7..0000000 --- a/scratchpad/raycaster_pixels.cpp +++ /dev/null @@ -1,228 +0,0 @@ -#include -#include -#include -#include -#include "matrix.hpp" -#include - -using matrix::Matrix; -using namespace fmt; - -Matrix MAP{ - {1,1,1,1,1,1,1,1,1}, - {1,0,1,0,0,0,0,0,1}, - {1,0,1,0,0,1,1,0,1}, - {1,0,0,0,0,0,0,0,1}, - {1,1,0,0,0,0,0,0,1}, - {1,0,0,1,1,1,0,0,1}, - {1,0,0,0,1,0,0,0,1}, - {1,0,0,0,0,0,1,1,1}, - {1,1,1,1,1,1,1,1,1} -}; - -const int SCREEN_HEIGHT=480; -const int THREED_VIEW_WIDTH=480; -const int THREED_VIEW_HEIGHT=480; -const int SCREEN_WIDTH=SCREEN_HEIGHT * 2; -const int MAP_SIZE=matrix::width(MAP); -const int TILE_SIZE=(SCREEN_WIDTH/2) / MAP_SIZE; -const float FOV = std::numbers::pi / 3.0; -const float HALF_FOV = FOV / 2; -const int CASTED_RAYS=120; -const float STEP_ANGLE = FOV / CASTED_RAYS; -const int MAX_DEPTH = MAP_SIZE * TILE_SIZE; -const float SCALE = (SCREEN_WIDTH / 2) / CASTED_RAYS; - -float player_x = SCREEN_WIDTH / 4; -float player_y = SCREEN_WIDTH / 4; -float player_angle = std::numbers::pi; - -struct RGBA { - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; -}; - -RGBA pixels[SCREEN_HEIGHT * SCREEN_HEIGHT] = {{0,0,0,0}}; -sf::Texture view_texture; -sf::Sprite view_sprite; - -void draw_rect(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, uint8_t color) { - sf::RectangleShape rect(size); - rect.setFillColor({color, color, color}); - rect.setPosition(pos); - window.draw(rect); -} - -void draw_pixel_buffer(sf::RenderWindow &window) { - view_texture.update((uint8_t *)pixels, SCREEN_HEIGHT, SCREEN_HEIGHT, 0, 0); - view_sprite.setTexture(view_texture); - view_sprite.setPosition(SCREEN_HEIGHT, 0); - window.draw(view_sprite); -} - -void draw_pixel_rect(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, uint8_t color) { - size_t x_start = size_t(pos.x - SCREEN_HEIGHT); - size_t y_start = size_t(pos.y); - size_t width = size_t(size.x); - size_t height = size_t(size.y); - - for(size_t y = y_start; y < y_start + height; y++) { - for(size_t x = x_start; x < x_start + width; x++) { - size_t pixel_index = (y * SCREEN_HEIGHT) + x; - pixels[pixel_index] = {color, color, color, 255}; - } - } -} - -void draw_map_rect(sf::RenderWindow &window, int x, int y, uint8_t color) { - draw_rect(window, - {float(x * TILE_SIZE), float(y * TILE_SIZE)}, - {float(TILE_SIZE-1), float(TILE_SIZE-1)}, - color); -} - -void draw_map(sf::RenderWindow &window, Matrix &map) { - uint8_t light_grey = 191; - uint8_t dark_grey = 65; - - for(size_t y = 0; y < matrix::height(map); y++) { - for(size_t x = 0; x < matrix::width(map); x++) { - draw_map_rect(window, x, y, map[y][x] == 1 ? light_grey : dark_grey); - } - } -} - -void draw_line(sf::RenderWindow &window, sf::Vector2f start, sf::Vector2f end) { - sf::Vertex line[] = { - sf::Vertex(start), - sf::Vertex(end) - }; - - window.draw(line, 2, sf::Lines); -} - -void draw_map_rays(sf::RenderWindow &window, int col, int row, sf::Vector2f target) { - draw_map_rect(window, col, row, 100); - draw_line(window, {player_x, player_y}, target); -} - -void draw_3d_view(sf::RenderWindow &window, int depth, float start_angle, int ray) { - uint8_t color = 255 / (1 + depth * depth * 0.0001); - - float fixed_depth = depth * std::cos(player_angle - start_angle); - - float wall_height = 21000 / fixed_depth; - - if(wall_height > SCREEN_HEIGHT){ - wall_height = SCREEN_HEIGHT; - } - - draw_pixel_rect(window, - {SCREEN_HEIGHT + ray * SCALE, (SCREEN_HEIGHT / 2) - wall_height / 2}, - {SCALE, wall_height}, - color); -} - -void clear_pixel_buffer() { - std::fill_n(pixels, SCREEN_HEIGHT * SCREEN_HEIGHT, RGBA{}); -} - -void ray_casting(sf::RenderWindow &window, Matrix& map) { - clear_pixel_buffer(); - float start_angle = player_angle - HALF_FOV; - - for(int ray = 0; ray < CASTED_RAYS; ray++, start_angle += STEP_ANGLE) - { - for(int depth = 1; depth < MAX_DEPTH; depth++) { - float target_x = player_x - std::sin(start_angle) * depth; - float target_y = player_y + std::cos(start_angle) * depth; - - int col = int(target_x / TILE_SIZE); - int row = int(target_y / TILE_SIZE); - - if(map[row][col] == 1) { - draw_map_rays(window, col, row, {target_x, target_y}); - draw_3d_view(window, depth, start_angle, ray); - break; - } - } - } -} - -void draw_ceiling_floor(sf::RenderWindow &window) { - draw_rect(window, - {SCREEN_HEIGHT, SCREEN_HEIGHT /2}, - {SCREEN_HEIGHT, SCREEN_HEIGHT}, - 100); - draw_rect(window, - {SCREEN_HEIGHT, (SCREEN_HEIGHT * -1) / 2}, - {SCREEN_HEIGHT, SCREEN_HEIGHT}, - 200); -} - -void draw_everything(sf::RenderWindow &window) { - draw_map(window, MAP); - draw_ceiling_floor(window); - ray_casting(window, MAP); - draw_pixel_buffer(window); - window.display(); -} - -bool collision(float x, float y) { - int col = int(x / TILE_SIZE); - int row = int(y / TILE_SIZE); - - return MAP[row][col] == 1; -} - -int main() { - using KB = sf::Keyboard; - sf::RenderWindow window(sf::VideoMode(SCREEN_WIDTH, SCREEN_HEIGHT), "Raycaster"); - window.setVerticalSyncEnabled(true); - view_texture.create(SCREEN_HEIGHT, SCREEN_HEIGHT); - - while(window.isOpen()) { - draw_everything(window); - - float x = player_x; - float y = player_y; - - if(KB::isKeyPressed(KB::Q)) { - player_angle -= 0.1; - } else if(KB::isKeyPressed(KB::E)) { - player_angle += 0.1; - } - - if(KB::isKeyPressed(KB::W)) { - x += -1 * std::sin(player_angle) * 5; - y += std::cos(player_angle) * 5; - } else if(KB::isKeyPressed(KB::S)) { - x -= -1 * std::sin(player_angle) * 5; - y -= std::cos(player_angle) * 5; - } - - if(KB::isKeyPressed(KB::D)) { - x += -1 * std::sin(player_angle + std::numbers::pi * 0.5) * 5; - y += std::cos(player_angle + std::numbers::pi * 0.5) * 5; - } else if(KB::isKeyPressed(KB::A)) { - x -= -1 * std::sin(player_angle + std::numbers::pi * 0.5) * 5; - y -= std::cos(player_angle + std::numbers::pi * 0.5) * 5; - } - - if(!collision(x, y)) { - player_x = x; - player_y = y; - } - - sf::Event event; - while(window.pollEvent(event)) { - if(event.type == sf::Event::Closed) { - window.close(); - } - } - } - - return 0; -} diff --git a/scratchpad/raycaster_sprites.cpp b/scratchpad/raycaster_sprites.cpp deleted file mode 100644 index 3e641ce..0000000 --- a/scratchpad/raycaster_sprites.cpp +++ /dev/null @@ -1,471 +0,0 @@ -/* -Copyright (c) 2004-2020, Lode Vandevenne - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include -#include -#include -#include - -#include "quickcg.h" -using namespace QuickCG; - -/* -g++ *.cpp -lSDL -O3 -W -Wall -ansi -pedantic -g++ *.cpp -lSDL -*/ - - -#define screenWidth 640 -#define screenHeight 480 -#define texWidth 64 // must be power of two -#define texHeight 64 // must be power of two -#define mapWidth 24 -#define mapHeight 24 - -int worldMap[mapWidth][mapHeight] = -{ - {8,8,8,8,8,8,8,8,8,8,8,4,4,6,4,4,6,4,6,4,4,4,6,4}, - {8,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,0,0,0,0,0,0,4}, - {8,0,3,3,0,0,0,0,0,8,8,4,0,0,0,0,0,0,0,0,0,0,0,6}, - {8,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6}, - {8,0,3,3,0,0,0,0,0,8,8,4,0,0,0,0,0,0,0,0,0,0,0,4}, - {8,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,6,6,6,0,6,4,6}, - {8,8,8,8,0,8,8,8,8,8,8,4,4,4,4,4,4,6,0,0,0,0,0,6}, - {7,7,7,7,0,7,7,7,7,0,8,0,8,0,8,0,8,4,0,4,0,6,0,6}, - {7,7,0,0,0,0,0,0,7,8,0,8,0,8,0,8,8,6,0,0,0,0,0,6}, - {7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,4}, - {7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,6,0,6,0,6}, - {7,7,0,0,0,0,0,0,7,8,0,8,0,8,0,8,8,6,4,6,0,6,6,6}, - {7,7,7,7,0,7,7,7,7,8,8,4,0,6,8,4,8,3,3,3,0,3,3,3}, - {2,2,2,2,0,2,2,2,2,4,6,4,0,0,6,0,6,3,0,0,0,0,0,3}, - {2,2,0,0,0,0,0,2,2,4,0,0,0,0,0,0,4,3,0,0,0,0,0,3}, - {2,0,0,0,0,0,0,0,2,4,0,0,0,0,0,0,4,3,0,0,0,0,0,3}, - {1,0,0,0,0,0,0,0,1,4,4,4,4,4,6,0,6,3,3,0,0,0,3,3}, - {2,0,0,0,0,0,0,0,2,2,2,1,2,2,2,6,6,0,0,5,0,5,0,5}, - {2,2,0,0,0,0,0,2,2,2,0,0,0,2,2,0,5,0,5,0,0,0,5,5}, - {2,0,0,0,0,0,0,0,2,0,0,0,0,0,2,5,0,5,0,5,0,5,0,5}, - {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5}, - {2,0,0,0,0,0,0,0,2,0,0,0,0,0,2,5,0,5,0,5,0,5,0,5}, - {2,2,0,0,0,0,0,2,2,2,0,0,0,2,2,0,5,0,5,0,0,0,5,5}, - {2,2,2,2,1,2,2,2,2,2,2,1,2,2,2,5,5,5,5,5,5,5,5,5} -}; - -struct Sprite -{ - double x; - double y; - int texture; -}; - -#define numSprites 19 - -Sprite sprite[numSprites] = -{ - {20.5, 11.5, 10}, //green light in front of playerstart - //green lights in every room - {18.5,4.5, 10}, - {10.0,4.5, 10}, - {10.0,12.5,10}, - {3.5, 6.5, 10}, - {3.5, 20.5,10}, - {3.5, 14.5,10}, - {14.5,20.5,10}, - - //row of pillars in front of wall: fisheye test - {18.5, 10.5, 9}, - {18.5, 11.5, 9}, - {18.5, 12.5, 9}, - - //some barrels around the map - {21.5, 1.5, 8}, - {15.5, 1.5, 8}, - {16.0, 1.8, 8}, - {16.2, 1.2, 8}, - {3.5, 2.5, 8}, - {9.5, 15.5, 8}, - {10.0, 15.1,8}, - {10.5, 15.8,8}, -}; - -Uint32 buffer[screenHeight][screenWidth]; // y-coordinate first because it works per scanline - -//1D Zbuffer -double ZBuffer[screenWidth]; - -//arrays used to sort the sprites -int spriteOrder[numSprites]; -double spriteDistance[numSprites]; - -//function used to sort the sprites -void sortSprites(int* order, double* dist, int amount); - -int main(int /*argc*/, char */*argv*/[]) -{ - double posX = 22.0, posY = 11.5; //x and y start position - double dirX = -1.0, dirY = 0.0; //initial direction vector - double planeX = 0.0, planeY = 0.66; //the 2d raycaster version of camera plane - - double time = 0; //time of current frame - double oldTime = 0; //time of previous frame - - std::vector texture[11]; - for(int i = 0; i < 11; i++) texture[i].resize(texWidth * texHeight); - - screen(screenWidth,screenHeight, 0, "Raycaster"); - - //load some textures - unsigned long tw, th, error = 0; - error |= loadImage(texture[0], tw, th, "pics/eagle.png"); - error |= loadImage(texture[1], tw, th, "pics/redbrick.png"); - error |= loadImage(texture[2], tw, th, "pics/purplestone.png"); - error |= loadImage(texture[3], tw, th, "pics/greystone.png"); - error |= loadImage(texture[4], tw, th, "pics/bluestone.png"); - error |= loadImage(texture[5], tw, th, "pics/mossy.png"); - error |= loadImage(texture[6], tw, th, "pics/wood.png"); - error |= loadImage(texture[7], tw, th, "pics/colorstone.png"); - if(error) { std::cout << "error loading images" << std::endl; return 1; } - - //load some sprite textures - error |= loadImage(texture[8], tw, th, "pics/barrel.png"); - error |= loadImage(texture[9], tw, th, "pics/pillar.png"); - error |= loadImage(texture[10], tw, th, "pics/greenlight.png"); - if(error) { std::cout << "error loading images" << std::endl; return 1; } - - //start the main loop - while(!done()) - { - //FLOOR CASTING - for(int y = screenHeight / 2 + 1; y < screenHeight; ++y) - { - // rayDir for leftmost ray (x = 0) and rightmost ray (x = w) - float rayDirX0 = dirX - planeX; - float rayDirY0 = dirY - planeY; - float rayDirX1 = dirX + planeX; - float rayDirY1 = dirY + planeY; - - // Current y position compared to the center of the screen (the horizon) - int p = y - screenHeight / 2; - - // Vertical position of the camera. - float posZ = 0.5 * screenHeight; - - // Horizontal distance from the camera to the floor for the current row. - // 0.5 is the z position exactly in the middle between floor and ceiling. - float rowDistance = posZ / p; - - // calculate the real world step vector we have to add for each x (parallel to camera plane) - // adding step by step avoids multiplications with a weight in the inner loop - float floorStepX = rowDistance * (rayDirX1 - rayDirX0) / screenWidth; - float floorStepY = rowDistance * (rayDirY1 - rayDirY0) / screenWidth; - - // real world coordinates of the leftmost column. This will be updated as we step to the right. - float floorX = posX + rowDistance * rayDirX0; - float floorY = posY + rowDistance * rayDirY0; - - for(int x = 0; x < screenWidth; ++x) - { - // the cell coord is simply got from the integer parts of floorX and floorY - int cellX = (int)(floorX); - int cellY = (int)(floorY); - - // get the texture coordinate from the fractional part - int tx = (int)(texWidth * (floorX - cellX)) & (texWidth - 1); - int ty = (int)(texHeight * (floorY - cellY)) & (texHeight - 1); - - floorX += floorStepX; - floorY += floorStepY; - - // choose texture and draw the pixel - int checkerBoardPattern = (int(cellX + cellY)) & 1; - int floorTexture; - if(checkerBoardPattern == 0) floorTexture = 3; - else floorTexture = 4; - int ceilingTexture = 6; - Uint32 color; - - // floor - color = texture[floorTexture][texWidth * ty + tx]; - color = (color >> 1) & 8355711; // make a bit darker - buffer[y][x] = color; - - //ceiling (symmetrical, at screenHeight - y - 1 instead of y) - color = texture[ceilingTexture][texWidth * ty + tx]; - color = (color >> 1) & 8355711; // make a bit darker - buffer[screenHeight - y - 1][x] = color; - } - } - - // WALL CASTING - for(int x = 0; x < w; x++) - { - //calculate ray position and direction - double cameraX = 2 * x / double(w) - 1; //x-coordinate in camera space - double rayDirX = dirX + planeX * cameraX; - double rayDirY = dirY + planeY * cameraX; - - //which box of the map we're in - int mapX = int(posX); - int mapY = int(posY); - - //length of ray from current position to next x or y-side - double sideDistX; - double sideDistY; - - //length of ray from one x or y-side to next x or y-side - double deltaDistX = (rayDirX == 0) ? 1e30 : std::abs(1 / rayDirX); - double deltaDistY = (rayDirY == 0) ? 1e30 : std::abs(1 / rayDirY); - double perpWallDist; - - //what direction to step in x or y-direction (either +1 or -1) - int stepX; - int stepY; - - int hit = 0; //was there a wall hit? - int side; //was a NS or a EW wall hit? - - //calculate step and initial sideDist - if(rayDirX < 0) - { - stepX = -1; - sideDistX = (posX - mapX) * deltaDistX; - } - else - { - stepX = 1; - sideDistX = (mapX + 1.0 - posX) * deltaDistX; - } - if(rayDirY < 0) - { - stepY = -1; - sideDistY = (posY - mapY) * deltaDistY; - } - else - { - stepY = 1; - sideDistY = (mapY + 1.0 - posY) * deltaDistY; - } - //perform DDA - while (hit == 0) - { - //jump to next map square, either in x-direction, or in y-direction - if(sideDistX < sideDistY) - { - sideDistX += deltaDistX; - mapX += stepX; - side = 0; - } - else - { - sideDistY += deltaDistY; - mapY += stepY; - side = 1; - } - //Check if ray has hit a wall - if(worldMap[mapX][mapY] > 0) hit = 1; - } - - //Calculate distance of perpendicular ray (Euclidean distance would give fisheye effect!) - if(side == 0) perpWallDist = (sideDistX - deltaDistX); - else perpWallDist = (sideDistY - deltaDistY); - - //Calculate height of line to draw on screen - int lineHeight = (int)(h / perpWallDist); - - //calculate lowest and highest pixel to fill in current stripe - int drawStart = -lineHeight / 2 + h / 2; - if(drawStart < 0) drawStart = 0; - int drawEnd = lineHeight / 2 + h / 2; - if(drawEnd >= h) drawEnd = h - 1; - //texturing calculations - int texNum = worldMap[mapX][mapY] - 1; //1 subtracted from it so that texture 0 can be used! - - //calculate value of wallX - double wallX; //where exactly the wall was hit - if (side == 0) wallX = posY + perpWallDist * rayDirY; - else wallX = posX + perpWallDist * rayDirX; - wallX -= floor((wallX)); - - //x coordinate on the texture - int texX = int(wallX * double(texWidth)); - if(side == 0 && rayDirX > 0) texX = texWidth - texX - 1; - if(side == 1 && rayDirY < 0) texX = texWidth - texX - 1; - - // TODO: an integer-only bresenham or DDA like algorithm could make the texture coordinate stepping faster - // How much to increase the texture coordinate per screen pixel - double step = 1.0 * texHeight / lineHeight; - // Starting texture coordinate - double texPos = (drawStart - h / 2 + lineHeight / 2) * step; - for(int y = drawStart; y < drawEnd; y++) - { - // Cast the texture coordinate to integer, and mask with (texHeight - 1) in case of overflow - int texY = (int)texPos & (texHeight - 1); - texPos += step; - Uint32 color = texture[texNum][texHeight * texY + texX]; - //make color darker for y-sides: R, G and B byte each divided through two with a "shift" and an "and" - if(side == 1) color = (color >> 1) & 8355711; - buffer[y][x] = color; - } - - //SET THE ZBUFFER FOR THE SPRITE CASTING - ZBuffer[x] = perpWallDist; //perpendicular distance is used - } - - //SPRITE CASTING - //sort sprites from far to close - for(int i = 0; i < numSprites; i++) - { - spriteOrder[i] = i; - spriteDistance[i] = ((posX - sprite[i].x) * (posX - sprite[i].x) + (posY - sprite[i].y) * (posY - sprite[i].y)); //sqrt not taken, unneeded - } - sortSprites(spriteOrder, spriteDistance, numSprites); - - //after sorting the sprites, do the projection and draw them - for(int i = 0; i < numSprites; i++) - { - //translate sprite position to relative to camera - double spriteX = sprite[spriteOrder[i]].x - posX; - double spriteY = sprite[spriteOrder[i]].y - posY; - - //transform sprite with the inverse camera matrix - // [ planeX dirX ] -1 [ dirY -dirX ] - // [ ] = 1/(planeX*dirY-dirX*planeY) * [ ] - // [ planeY dirY ] [ -planeY planeX ] - - double invDet = 1.0 / (planeX * dirY - dirX * planeY); //required for correct matrix multiplication - - double transformX = invDet * (dirY * spriteX - dirX * spriteY); - double transformY = invDet * (-planeY * spriteX + planeX * spriteY); //this is actually the depth inside the screen, that what Z is in 3D, the distance of sprite to player, matching sqrt(spriteDistance[i]) - - int spriteScreenX = int((w / 2) * (1 + transformX / transformY)); - - //parameters for scaling and moving the sprites - #define uDiv 1 - #define vDiv 1 - #define vMove 0.0 - int vMoveScreen = int(vMove / transformY); - - //calculate height of the sprite on screen - int spriteHeight = abs(int(h / (transformY))) / vDiv; //using "transformY" instead of the real distance prevents fisheye - //calculate lowest and highest pixel to fill in current stripe - int drawStartY = -spriteHeight / 2 + h / 2 + vMoveScreen; - if(drawStartY < 0) drawStartY = 0; - int drawEndY = spriteHeight / 2 + h / 2 + vMoveScreen; - if(drawEndY >= h) drawEndY = h - 1; - - //calculate width of the sprite - int spriteWidth = abs(int (h / (transformY))) / uDiv; // same as height of sprite, given that it's square - int drawStartX = -spriteWidth / 2 + spriteScreenX; - if(drawStartX < 0) drawStartX = 0; - int drawEndX = spriteWidth / 2 + spriteScreenX; - if(drawEndX > w) drawEndX = w; - - //loop through every vertical stripe of the sprite on screen - for(int stripe = drawStartX; stripe < drawEndX; stripe++) - { - int texX = int(256 * (stripe - (-spriteWidth / 2 + spriteScreenX)) * texWidth / spriteWidth) / 256; - //the conditions in the if are: - //1) it's in front of camera plane so you don't see things behind you - //2) ZBuffer, with perpendicular distance - if(transformY > 0 && transformY < ZBuffer[stripe]) - { - for(int y = drawStartY; y < drawEndY; y++) //for every pixel of the current stripe - { - int d = (y - vMoveScreen) * 256 - h * 128 + spriteHeight * 128; //256 and 128 factors to avoid floats - int texY = ((d * texHeight) / spriteHeight) / 256; - Uint32 color = texture[sprite[spriteOrder[i]].texture][texWidth * texY + texX]; //get current color from the texture - if((color & 0x00FFFFFF) != 0) buffer[y][stripe] = color; //paint pixel if it isn't black, black is the invisible color - } - } - } - } - - drawBuffer(buffer[0]); - // No need to clear the screen here, since everything is overdrawn with floor and ceiling - - //timing for input and FPS counter - oldTime = time; - time = getTicks(); - double frameTime = (time - oldTime) / 1000.0; //frametime is the time this frame has taken, in seconds - print(1.0 / frameTime); //FPS counter - redraw(); - - //speed modifiers - double moveSpeed = frameTime * 3.0; //the constant value is in squares/second - double rotSpeed = frameTime * 2.0; //the constant value is in radians/second - readKeys(); - //move forward if no wall in front of you - if (keyDown(SDLK_UP)) - { - if(worldMap[int(posX + dirX * moveSpeed)][int(posY)] == false) posX += dirX * moveSpeed; - if(worldMap[int(posX)][int(posY + dirY * moveSpeed)] == false) posY += dirY * moveSpeed; - } - //move backwards if no wall behind you - if(keyDown(SDLK_DOWN)) - { - if(worldMap[int(posX - dirX * moveSpeed)][int(posY)] == false) posX -= dirX * moveSpeed; - if(worldMap[int(posX)][int(posY - dirY * moveSpeed)] == false) posY -= dirY * moveSpeed; - } - //rotate to the right - if(keyDown(SDLK_RIGHT)) - { - //both camera direction and camera plane must be rotated - double oldDirX = dirX; - dirX = dirX * cos(-rotSpeed) - dirY * sin(-rotSpeed); - dirY = oldDirX * sin(-rotSpeed) + dirY * cos(-rotSpeed); - double oldPlaneX = planeX; - planeX = planeX * cos(-rotSpeed) - planeY * sin(-rotSpeed); - planeY = oldPlaneX * sin(-rotSpeed) + planeY * cos(-rotSpeed); - } - //rotate to the left - if(keyDown(SDLK_LEFT)) - { - //both camera direction and camera plane must be rotated - double oldDirX = dirX; - dirX = dirX * cos(rotSpeed) - dirY * sin(rotSpeed); - dirY = oldDirX * sin(rotSpeed) + dirY * cos(rotSpeed); - double oldPlaneX = planeX; - planeX = planeX * cos(rotSpeed) - planeY * sin(rotSpeed); - planeY = oldPlaneX * sin(rotSpeed) + planeY * cos(rotSpeed); - } - if(keyDown(SDLK_ESCAPE)) - { - break; - } - } -} - -//sort the sprites based on distance -void sortSprites(int* order, double* dist, int amount) -{ - std::vector> sprites(amount); - for(int i = 0; i < amount; i++) { - sprites[i].first = dist[i]; - sprites[i].second = order[i]; - } - std::sort(sprites.begin(), sprites.end()); - // restore in reverse order to go from farthest to nearest - for(int i = 0; i < amount; i++) { - dist[i] = sprites[amount - i - 1].first; - order[i] = sprites[amount - i - 1].second; - } -} diff --git a/scratchpad/raycaster_textured.cpp b/scratchpad/raycaster_textured.cpp deleted file mode 100644 index 3dca1b0..0000000 --- a/scratchpad/raycaster_textured.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/* -Copyright (c) 2004-2019, Lode Vandevenne - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include -#include -#include -#include - -#include "quickcg.h" -using namespace QuickCG; - -/* -g++ *.cpp -lSDL -O3 -W -Wall -ansi -pedantic -g++ *.cpp -lSDL -*/ - - -#define screenWidth 640 -#define screenHeight 480 -#define texWidth 64 -#define texHeight 64 -#define mapWidth 24 -#define mapHeight 24 - -int worldMap[mapWidth][mapHeight]= -{ - {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,7,7,7,7,7,7,7,7}, - {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,7}, - {4,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7}, - {4,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7}, - {4,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,7}, - {4,0,4,0,0,0,0,5,5,5,5,5,5,5,5,5,7,7,0,7,7,7,7,7}, - {4,0,5,0,0,0,0,5,0,5,0,5,0,5,0,5,7,0,0,0,7,7,7,1}, - {4,0,6,0,0,0,0,5,0,0,0,0,0,0,0,5,7,0,0,0,0,0,0,8}, - {4,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,1}, - {4,0,8,0,0,0,0,5,0,0,0,0,0,0,0,5,7,0,0,0,0,0,0,8}, - {4,0,0,0,0,0,0,5,0,0,0,0,0,0,0,5,7,0,0,0,7,7,7,1}, - {4,0,0,0,0,0,0,5,5,5,5,0,5,5,5,5,7,7,7,7,7,7,7,1}, - {6,6,6,6,6,6,6,6,6,6,6,0,6,6,6,6,6,6,6,6,6,6,6,6}, - {8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, - {6,6,6,6,6,6,0,6,6,6,6,0,6,6,6,6,6,6,6,6,6,6,6,6}, - {4,4,4,4,4,4,0,4,4,4,6,0,6,2,2,2,2,2,2,2,3,3,3,3}, - {4,0,0,0,0,0,0,0,0,4,6,0,6,2,0,0,0,0,0,2,0,0,0,2}, - {4,0,0,0,0,0,0,0,0,0,0,0,6,2,0,0,5,0,0,2,0,0,0,2}, - {4,0,0,0,0,0,0,0,0,4,6,0,6,2,0,0,0,0,0,2,2,0,2,2}, - {4,0,6,0,6,0,0,0,0,4,6,0,0,0,0,0,5,0,0,0,0,0,0,2}, - {4,0,0,5,0,0,0,0,0,4,6,0,6,2,0,0,0,0,0,2,2,0,2,2}, - {4,0,6,0,6,0,0,0,0,4,6,0,6,2,0,0,5,0,0,2,0,0,0,2}, - {4,0,0,0,0,0,0,0,0,4,6,0,6,2,0,0,0,0,0,2,0,0,0,2}, - {4,4,4,4,4,4,4,4,4,4,1,1,1,2,2,2,2,2,2,3,3,3,3,3} -}; - -Uint32 buffer[screenHeight][screenWidth]; - -int main(int /*argc*/, char */*argv*/[]) -{ - double posX = 22.0, posY = 11.5; //x and y start position - double dirX = -1.0, dirY = 0.0; //initial direction vector - double planeX = 0.0, planeY = 0.66; //the 2d raycaster version of camera plane - - double time = 0; //time of current frame - double oldTime = 0; //time of previous frame - - std::vector texture[8]; - for(int i = 0; i < 8; i++) texture[i].resize(texWidth * texHeight); - - screen(screenWidth, screenHeight, 0, "Raycaster"); - - //generate some textures -#if 1 - for(int x = 0; x < texWidth; x++) - for(int y = 0; y < texHeight; y++) - { - int xorcolor = (x * 256 / texWidth) ^ (y * 256 / texHeight); - //int xcolor = x * 256 / texWidth; - int ycolor = y * 256 / texHeight; - int xycolor = y * 128 / texHeight + x * 128 / texWidth; - texture[0][texWidth * y + x] = 65536 * 254 * (x != y && x != texWidth - y); //flat red texture with black cross - texture[1][texWidth * y + x] = xycolor + 256 * xycolor + 65536 * xycolor; //sloped greyscale - texture[2][texWidth * y + x] = 256 * xycolor + 65536 * xycolor; //sloped yellow gradient - texture[3][texWidth * y + x] = xorcolor + 256 * xorcolor + 65536 * xorcolor; //xor greyscale - texture[4][texWidth * y + x] = 256 * xorcolor; //xor green - texture[5][texWidth * y + x] = 65536 * 192 * (x % 16 && y % 16); //red bricks - texture[6][texWidth * y + x] = 65536 * ycolor; //red gradient - texture[7][texWidth * y + x] = 128 + 256 * 128 + 65536 * 128; //flat grey texture - } -#else - //generate some textures - unsigned long tw, th; - loadImage(texture[0], tw, th, "assets/eagle.png"); - loadImage(texture[1], tw, th, "assets/redbrick.png"); - loadImage(texture[2], tw, th, "assets/purplestone.png"); - loadImage(texture[3], tw, th, "assets/greystone.png"); - loadImage(texture[4], tw, th, "assets/bluestone.png"); - loadImage(texture[5], tw, th, "assets/mossy.png"); - loadImage(texture[6], tw, th, "assets/wood.png"); - loadImage(texture[7], tw, th, "assets/colorstone.png"); -#endif - - //start the main loop - while(!done()) - { - for(int x = 0; x < w; x++) - { - //calculate ray position and direction - double cameraX = 2 * x / (double)w - 1; //x-coordinate in camera space - double rayDirX = dirX + planeX*cameraX; - double rayDirY = dirY + planeY*cameraX; - - //which box of the map we're in - int mapX = int(posX); - int mapY = int(posY); - - //length of ray from current position to next x or y-side - double sideDistX; - double sideDistY; - - //length of ray from one x or y-side to next x or y-side - double deltaDistX = (rayDirX == 0) ? 1e30 : std::abs(1 / rayDirX); - double deltaDistY = (rayDirY == 0) ? 1e30 : std::abs(1 / rayDirY); - double perpWallDist; - - //what direction to step in x or y-direction (either +1 or -1) - int stepX; - int stepY; - - int hit = 0; //was there a wall hit? - int side; //was a NS or a EW wall hit? - - //calculate step and initial sideDist - if(rayDirX < 0) - { - stepX = -1; - sideDistX = (posX - mapX) * deltaDistX; - } - else - { - stepX = 1; - sideDistX = (mapX + 1.0 - posX) * deltaDistX; - } - if(rayDirY < 0) - { - stepY = -1; - sideDistY = (posY - mapY) * deltaDistY; - } - else - { - stepY = 1; - sideDistY = (mapY + 1.0 - posY) * deltaDistY; - } - //perform DDA - while (hit == 0) - { - //jump to next map square, either in x-direction, or in y-direction - if(sideDistX < sideDistY) - { - sideDistX += deltaDistX; - mapX += stepX; - side = 0; - } - else - { - sideDistY += deltaDistY; - mapY += stepY; - side = 1; - } - //Check if ray has hit a wall - if(worldMap[mapX][mapY] > 0) hit = 1; - } - - //Calculate distance of perpendicular ray (Euclidean distance would give fisheye effect!) - if(side == 0) perpWallDist = (sideDistX - deltaDistX); - else perpWallDist = (sideDistY - deltaDistY); - - //Calculate height of line to draw on screen - int lineHeight = (int)(h / perpWallDist); - - - int pitch = 100; - - //calculate lowest and highest pixel to fill in current stripe - int drawStart = -lineHeight / 2 + h / 2 + pitch; - if(drawStart < 0) drawStart = 0; - int drawEnd = lineHeight / 2 + h / 2 + pitch; - if(drawEnd >= h) drawEnd = h - 1; - - //texturing calculations - int texNum = worldMap[mapX][mapY] - 1; //1 subtracted from it so that texture 0 can be used! - - //calculate value of wallX - double wallX; //where exactly the wall was hit - if(side == 0) wallX = posY + perpWallDist * rayDirY; - else wallX = posX + perpWallDist * rayDirX; - wallX -= floor((wallX)); - - //x coordinate on the texture - int texX = int(wallX * double(texWidth)); - if(side == 0 && rayDirX > 0) texX = texWidth - texX - 1; - if(side == 1 && rayDirY < 0) texX = texWidth - texX - 1; - - // TODO: an integer-only bresenham or DDA like algorithm could make the texture coordinate stepping faster - // How much to increase the texture coordinate per screen pixel - double step = 1.0 * texHeight / lineHeight; - // Starting texture coordinate - double texPos = (drawStart - pitch - h / 2 + lineHeight / 2) * step; - for(int y = drawStart; y < drawEnd; y++) - { - // Cast the texture coordinate to integer, and mask with (texHeight - 1) in case of overflow - int texY = (int)texPos & (texHeight - 1); - texPos += step; - Uint32 color = texture[texNum][texHeight * texY + texX]; - //make color darker for y-sides: R, G and B byte each divided through two with a "shift" and an "and" - if(side == 1) color = (color >> 1) & 8355711; - buffer[y][x] = color; - } - } - - for(int y = 0; y < h; y++) for(int x = 0; x < w; x++) buffer[y][x] = 0; //clear the buffer instead of cls() - drawBuffer(buffer[0]); - - //timing for input and FPS counter - oldTime = time; - time = getTicks(); - double frameTime = (time - oldTime) / 1000.0; //frametime is the time this frame has taken, in seconds - print(1.0 / frameTime); //FPS counter - redraw(); - - //speed modifiers - double moveSpeed = frameTime * 5.0; //the constant value is in squares/second - double rotSpeed = frameTime * 3.0; //the constant value is in radians/second - - SDL_Event event; - while(SDL_PollEvent(&event)) { - if(event.type != SDL_KEYDOWN) continue; - //move forward if no wall in front of you - if(event.key.keysym.sym == SDLK_UP) - { - if(worldMap[int(posX + dirX * moveSpeed)][int(posY)] == false) posX += dirX * moveSpeed; - if(worldMap[int(posX)][int(posY + dirY * moveSpeed)] == false) posY += dirY * moveSpeed; - } - //move backwards if no wall behind you - if(event.key.keysym.sym == SDLK_DOWN) - { - if(worldMap[int(posX - dirX * moveSpeed)][int(posY)] == false) posX -= dirX * moveSpeed; - if(worldMap[int(posX)][int(posY - dirY * moveSpeed)] == false) posY -= dirY * moveSpeed; - } - //rotate to the right - if(event.key.keysym.sym == SDLK_RIGHT) - { - //both camera direction and camera plane must be rotated - double oldDirX = dirX; - dirX = dirX * cos(-rotSpeed) - dirY * sin(-rotSpeed); - dirY = oldDirX * sin(-rotSpeed) + dirY * cos(-rotSpeed); - double oldPlaneX = planeX; - planeX = planeX * cos(-rotSpeed) - planeY * sin(-rotSpeed); - planeY = oldPlaneX * sin(-rotSpeed) + planeY * cos(-rotSpeed); - } - //rotate to the left - if(event.key.keysym.sym == SDLK_LEFT) - { - //both camera direction and camera plane must be rotated - double oldDirX = dirX; - dirX = dirX * cos(rotSpeed) - dirY * sin(rotSpeed); - dirY = oldDirX * sin(rotSpeed) + dirY * cos(rotSpeed); - double oldPlaneX = planeX; - planeX = planeX * cos(rotSpeed) - planeY * sin(rotSpeed); - planeY = oldPlaneX * sin(rotSpeed) + planeY * cos(rotSpeed); - } - } - } - - return 0; -} diff --git a/scratchpad/sdlprog.cpp b/scratchpad/sdlprog.cpp deleted file mode 100644 index a641a0b..0000000 --- a/scratchpad/sdlprog.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "SDL.h" - -int main(int argc, char *argv[]) -{ - SDL_Window *window; - SDL_Renderer *renderer; - SDL_Surface *surface; - SDL_Event event; - - if (SDL_Init(SDL_INIT_VIDEO) < 0) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError()); - return 3; - } - - if (SDL_CreateWindowAndRenderer(320, 240, SDL_WINDOW_RESIZABLE, &window, &renderer)) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window and renderer: %s", SDL_GetError()); - return 3; - } - - while (1) { - SDL_PollEvent(&event); - if (event.type == SDL_QUIT) { - break; - } - SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00); - SDL_RenderClear(renderer); - SDL_RenderPresent(renderer); - } - - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - - SDL_Quit(); - - return 0; -} diff --git a/scratchpad/timcaster.cpp b/scratchpad/timcaster.cpp deleted file mode 100644 index aae265e..0000000 --- a/scratchpad/timcaster.cpp +++ /dev/null @@ -1,553 +0,0 @@ -#include -#include -#include -#include -#include - -#define ASSERT(_e, ...) if (!(_e)) { fprintf(stderr, __VA_ARGS__); exit(1); } - -typedef float f32; -typedef double f64; -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; -typedef int8_t i8; -typedef int16_t i16; -typedef int32_t i32; -typedef int64_t i64; -typedef size_t usize; -typedef ssize_t isize; - -#define SCREEN_SIZE_X 640 -#define SCREEN_SIZE_Y 360 - -#define TILE_WIDTH 1.0f -#define WALL_HEIGHT 1.2f - -typedef struct v2_s {f32 x, y;} v2; -typedef struct v2i_s { i32 x, y;} v2i; - -#define dot(v0, v1) \ - ({ const v2 _v0 = (v0), _v1 = (v1); (_v0.x * _v1.x) + (_v0.y * _v1.y); }) -#define length(v) ({ const v2 _v = (v); sqrtf(dot(_v, _v)); }) -#define normalize(u) ({ \ - const v2 _u = (u); \ - const f32 l = length(_u); \ - (v2) { _u.x/l, _u.y/l }; \ - }) -#define rotr(v) ({ const v2 _v = (v); (v2) { -_v.y, _v.x }; }) -#define min(a, b) ({ __typeof__(a) _a = (a), _b = (b); _a < _b ? _a : _b; }) -#define max(a, b) ({ __typeof__(a) _a = (a), _b = (b); _a > _b ? _a : _b; }) - -static u8 MAPDATA[8*13] = { - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 1, - 1, 0, 0, 3, 0, 0, 4, 1, - 1, 0, 0, 0, 0, 0, 0, 1, - 1, 0, 0, 0, 0, 0, 4, 1, - 1, 0, 2, 0, 0, 0, 0, 1, - 1, 0, 0, 0, 0, 0, 0, 1, - 1, 0, 0, 0, 0, 0, 0, 1, - 1, 0, 2, 2, 0, 0, 0, 1, - 1, 0, 2, 2, 0, 3, 0, 1, - 1, 0, 0, 0, 0, 3, 0, 1, - 1, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 1, 1, 1, 1, 1, -}; - -enum KeyboardKeyState { - KeyboardKeyState_Depressed = 0, // No recent event, key is still up - KeyboardKeyState_Released = 1, // Last event was a released event - KeyboardKeyState_Held = 2, // No recent event, key is still down - KeyboardKeyState_Pressed = 3, // Last event was a pressed event - KeyboardKeyState_COUNT = 4 -}; - -struct KeyBoardState { - enum KeyboardKeyState up; - enum KeyboardKeyState down; - enum KeyboardKeyState right; - enum KeyboardKeyState left; - enum KeyboardKeyState a; - enum KeyboardKeyState s; - enum KeyboardKeyState d; - enum KeyboardKeyState w; - enum KeyboardKeyState q; - enum KeyboardKeyState e; - - enum KeyboardKeyState one; - enum KeyboardKeyState two; - enum KeyboardKeyState three; - enum KeyboardKeyState four; - enum KeyboardKeyState five; - enum KeyboardKeyState six; - enum KeyboardKeyState seven; - enum KeyboardKeyState eight; -}; - -void clear_keyboard_state(struct KeyBoardState* kbs) { - kbs->up = KeyboardKeyState_Depressed; - kbs->down = KeyboardKeyState_Depressed; - kbs->right = KeyboardKeyState_Depressed; - kbs->left = KeyboardKeyState_Depressed; - kbs->a = KeyboardKeyState_Depressed; - kbs->s = KeyboardKeyState_Depressed; - kbs->d = KeyboardKeyState_Depressed; - kbs->w = KeyboardKeyState_Depressed; - kbs->q = KeyboardKeyState_Depressed; - kbs->e = KeyboardKeyState_Depressed; - - kbs->one = KeyboardKeyState_Depressed; - kbs->two = KeyboardKeyState_Depressed; - kbs->three = KeyboardKeyState_Depressed; - kbs->four = KeyboardKeyState_Depressed; - kbs->five = KeyboardKeyState_Depressed; - kbs->six = KeyboardKeyState_Depressed; - kbs->seven = KeyboardKeyState_Depressed; - kbs->eight = KeyboardKeyState_Depressed; -} - -void decay_keyboard_state(struct KeyBoardState* kbs) { - static enum KeyboardKeyState to_depressed_state[KeyboardKeyState_COUNT] = { - KeyboardKeyState_Depressed, - KeyboardKeyState_Depressed, - KeyboardKeyState_Held, - KeyboardKeyState_Held - }; - - kbs->up = to_depressed_state[kbs->up]; - kbs->down = to_depressed_state[kbs->down]; - kbs->right = to_depressed_state[kbs->right]; - kbs->left = to_depressed_state[kbs->left]; - kbs->a = to_depressed_state[kbs->a]; - kbs->s = to_depressed_state[kbs->s]; - kbs->d = to_depressed_state[kbs->d]; - kbs->w = to_depressed_state[kbs->w]; - kbs->q = to_depressed_state[kbs->q]; - kbs->e = to_depressed_state[kbs->e]; - - kbs->one = to_depressed_state[kbs->one]; - kbs->two = to_depressed_state[kbs->two]; - kbs->three = to_depressed_state[kbs->three]; - kbs->four = to_depressed_state[kbs->four]; - kbs->five = to_depressed_state[kbs->five]; - kbs->six = to_depressed_state[kbs->six]; - kbs->seven = to_depressed_state[kbs->seven]; - kbs->eight = to_depressed_state[kbs->eight]; -} - -bool is_pressed(enum KeyboardKeyState state) { - static bool lookup[KeyboardKeyState_COUNT] = {0, 0, 1, 1}; - return lookup[state]; -} - -// TODO: Could we store the pixels in column-major? We're always rendering -// in vertical lines, so I suspect that would be more efficient. -struct { - SDL_Window *window; - SDL_Texture *texture; - SDL_Renderer *renderer; - u32 pixels[SCREEN_SIZE_X * SCREEN_SIZE_Y]; - bool quit; - - v2 camera_pos; - v2 camera_dir; - v2 camera_dir_rotr; - f32 camera_width; - f32 camera_height; - f32 camera_z; - - v2 player_speed; - f32 player_omega; - - struct KeyBoardState keyboard_state; -} state; - - -static void tick(f32 dt) { - - v2 input_dir = {0.0, 0.0}; // In the body frame, which is right-handed, so y points left. - if (is_pressed(state.keyboard_state.w)) { - input_dir.x += 1.0; - } - if (is_pressed(state.keyboard_state.s)) { - input_dir.x -= 1.0; - } - if (is_pressed(state.keyboard_state.d)) { - input_dir.y -= 1.0; - } - if (is_pressed(state.keyboard_state.a)) { - input_dir.y += 1.0; - } - - int input_rot_dir = 0; // Right-hand rotation in plane (CCW) - if (is_pressed(state.keyboard_state.q)) { - input_rot_dir += 1; - } - if (is_pressed(state.keyboard_state.e)) { - input_rot_dir -= 1; - } - - if (is_pressed(state.keyboard_state.three)) { - state.camera_z *= 0.95; - printf("camera z: %.3f\n", state.camera_z); - } - if (is_pressed(state.keyboard_state.four)) { - state.camera_z /= 0.95; - printf("camera z: %.3f\n", state.camera_z); - } - if (is_pressed(state.keyboard_state.five)) { - state.camera_height *= 0.95; - printf("camera height: %.3f\n", state.camera_height); - } - if (is_pressed(state.keyboard_state.six)) { - state.camera_height /= 0.95; - printf("camera height: %.3f\n", state.camera_height); - } - if (is_pressed(state.keyboard_state.seven)) { - state.camera_width *= 0.95; - printf("camera width: %.3f\n", state.camera_width); - } - if (is_pressed(state.keyboard_state.eight)) { - state.camera_width /= 0.95; - printf("camera width: %.3f\n", state.camera_width); - } - - // Update the player's velocity - const f32 kPlayerInputAccel = 7.5; - const f32 kPlayerInputAngularAccel = 9.5; - const f32 kPlayerMaxSpeed = 7.0; - const f32 kPlayerMaxOmega = 7.0; - const f32 kAirFriction = 0.9; - const f32 kAirFrictionRot = 0.85; - - // Note: Speed is in the global frame - state.player_speed.x += (state.camera_dir.x*input_dir.x + state.camera_dir_rotr.x*input_dir.y) * kPlayerInputAccel * dt; - state.player_speed.y += (state.camera_dir.y*input_dir.x + state.camera_dir_rotr.y*input_dir.y) * kPlayerInputAccel * dt; - state.player_omega += input_rot_dir * kPlayerInputAngularAccel * dt; - - // Clamp the velocity to a maximum magnitude - f32 speed = length(state.player_speed); - if (speed > kPlayerMaxSpeed) { - state.player_speed.x *= kPlayerMaxSpeed / speed; - state.player_speed.y *= kPlayerMaxSpeed / speed; - } - if (state.player_omega > kPlayerMaxOmega) { - state.player_omega *= kPlayerMaxOmega / state.player_omega; - } else if (state.player_omega < -kPlayerMaxOmega) { - state.player_omega *= - kPlayerMaxOmega / state.player_omega; - } - - // Update the player's position - state.camera_pos.x += state.player_speed.x * dt; - state.camera_pos.y += state.player_speed.y * dt; - - // Update the player's rotational heading - float theta = atan2(state.camera_dir.y, state.camera_dir.x); - theta += state.player_omega * dt; - state.camera_dir = ((v2) {cos(theta), sin(theta)}); - state.camera_dir_rotr = rotr((state.camera_dir)); - - // Apply air friction - state.player_speed.x *= kAirFriction; - state.player_speed.y *= kAirFriction; - state.player_omega *= kAirFrictionRot; -} - - -// Fill all pixels in the vertical line at x between y0 and y1 with the given color. -static void draw_column(int x, int y0, int y1, u32 color) { - for (int y = y0; y <= y1; y++) { - state.pixels[(y * SCREEN_SIZE_X) + x] = color; - } -} - -static void render() { - static u32 color_wall[4] = { - 0xFFFF0000, - 0xFF00FF00, - 0xFF00FFFF, - 0xFF0000FF - }; - static u32 color_wall_light[4] = { - 0xFFFF3333, - 0xFF66FF66, - 0xFF88FFFF, - 0xFF3333FF - }; - const u32 color_floor = 0xFF666666; - const u32 color_ceil = 0xFF444444; - - // Get camera location's cell coordinates - int x_ind_cam = (int)(floorf(state.camera_pos.x / TILE_WIDTH)); - int y_ind_cam = (int)(floorf(state.camera_pos.y / TILE_WIDTH)); - f32 x_rem_cam = state.camera_pos.x - TILE_WIDTH*x_ind_cam; - f32 y_rem_cam = state.camera_pos.y - TILE_WIDTH*y_ind_cam; - - for (int x = 0; x < SCREEN_SIZE_X; x++) { - - // Camera to pixel column - const f32 dw = state.camera_width/2 - (state.camera_width*x)/SCREEN_SIZE_X; - const v2 cp = { - state.camera_dir.x + dw*state.camera_dir_rotr.x, - state.camera_dir.y + dw*state.camera_dir_rotr.y - }; - - // Distance from the camera to the column - const f32 cam_len = length( (cp) ); - - // Ray direction through this column - const v2 dir = {cp.x / cam_len, cp.y /cam_len}; - - // Start at the camera pos - int x_ind = x_ind_cam; - int y_ind = y_ind_cam; - f32 x_rem = x_rem_cam; - f32 y_rem = y_rem_cam; - - // We will be raycasting through cells of unit width. - // Our ray's position vs time is: - // x(t) = x_rem + dir.x * dt - // y(t) = y_rem + dir.y * dt - - // We cross x = 0 if dir.x < 0, at dt = -x_rem/dir.x - // We cross x = TILE_WIDTH if dir.x > 0, at dt = (TILE_WIDTH-x_rem)/dir.x - // We cross y = 0 if dir.y < 0, at dt = -y_rem/dir.y - // We cross y = TILE_WIDTH if dir.y > 0, at dt = (TILE_WIDTH-y_rem)/dir.y - - // We can generalize this to: - // dx_ind_dir = -1 if dir.x < 0, at dt = -1/dir.x * x_rem + 0.0 - // dx_ind_dir = 1 if dir.x > 0, at dt = -1/dir.x * x_rem + TILE_WIDTH/dir.x - // dx_ind_dir = 0 if dir.x = 0, at dt = 0 * x_rem + INFINITY - // dy_ind_dir = -1 if dir.y < 0, at dt = -1/dir.y * y_rem + 0.0 - // dy_ind_dir = 1 if dir.y > 0, at dt = -1/dir.y * y_rem + TILE_WIDTH/dir.y - // dy_ind_dir = 0 if dir.x = 0, at dt = 0 * y_rem + INFINITY - - int dx_ind_dir = 0; - f32 dx_a = 0.0; - f32 dx_b = INFINITY; - if (dir.x < 0) { - dx_ind_dir = -1; - dx_a = -1.0f/dir.x; - dx_b = 0.0; - } else if (dir.x > 0) { - dx_ind_dir = 1; - dx_a = -1.0f/dir.x; - dx_b = TILE_WIDTH/dir.x; - } - - int dy_ind_dir = 0; - f32 dy_a = 0.0; - f32 dy_b = INFINITY; - if (dir.y < 0) { - dy_ind_dir = -1; - dy_a = -1.0f/dir.y; - dy_b = 0.0; - } else if (dir.y > 0) { - dy_ind_dir = 1; - dy_a = -1.0f/dir.y; - dy_b = TILE_WIDTH/dir.y; - } - - // Step through cells until we hit an occupied cell - int n_steps = 0; - int dx_ind, dy_ind; - while (n_steps < 100) { - n_steps += 1; - - f32 dt_best = INFINITY; - dx_ind = 0; - dy_ind = 0; - - f32 dt_x = dx_a*x_rem + dx_b; - f32 dt_y = dy_a*y_rem + dy_b; - if (dt_x < dt_y) { - dt_best = dt_x; - dx_ind = dx_ind_dir; - dy_ind = 0; - } else { - dt_best = dt_y; - dx_ind = 0; - dy_ind = dy_ind_dir; - } - - // Move up to the next cell - x_ind += dx_ind; - y_ind += dy_ind; - x_rem += dir.x * dt_best - TILE_WIDTH*dx_ind; - y_rem += dir.y * dt_best - TILE_WIDTH*dy_ind; - - // Check to see if the new cell is solid - if (MAPDATA[y_ind*8 + x_ind] > 0) { - break; - } - } - - // Calculate the collision location - const v2 collision = { - TILE_WIDTH*x_ind + x_rem, - TILE_WIDTH*y_ind + y_rem - }; - - // Calculate the ray length - const f32 ray_len = length( ((v2) {collision.x - state.camera_pos.x, collision.y - state.camera_pos.y}) ); - - // Calculate the pixel bounds that we fill the wall in for - int y_lo = (int)(SCREEN_SIZE_Y/2.0f - cam_len*state.camera_z/ray_len * SCREEN_SIZE_Y / state.camera_height); - int y_hi = (int)(SCREEN_SIZE_Y/2.0f + cam_len*(WALL_HEIGHT - state.camera_z)/ray_len * SCREEN_SIZE_Y / state.camera_height); - y_lo = max(y_lo, 0); - y_hi = min(y_hi, SCREEN_SIZE_Y-1); - - u32 color_wall_to_render = (dx_ind == 0) ? color_wall[MAPDATA[y_ind*8 + x_ind]-1] : color_wall_light[MAPDATA[y_ind*8 + x_ind]-1]; - - draw_column(x, 0, y_lo-1, color_floor); - draw_column(x, y_lo, y_hi, color_wall_to_render); - draw_column(x, y_hi + 1, SCREEN_SIZE_Y-1, color_ceil); - } -} - -int main(int argc, char *argv[]) { - // Initialize SDL - ASSERT( - SDL_Init(SDL_INIT_VIDEO) == 0, - "SDL initialization failed: %s\n", - SDL_GetError() - ); - - // Create a window - state.window = SDL_CreateWindow( - "TOOM", - SDL_WINDOWPOS_CENTERED_DISPLAY(1), - SDL_WINDOWPOS_CENTERED_DISPLAY(1), - SCREEN_SIZE_X, - SCREEN_SIZE_Y, - SDL_WINDOW_ALLOW_HIGHDPI); - ASSERT(state.window, "Error creating SDL window: %s\n", SDL_GetError()); - - // Create a renderer - state.renderer = SDL_CreateRenderer(state.window, -1, SDL_RENDERER_PRESENTVSYNC); - ASSERT(state.renderer, "Error creating SDL renderer: %s\n", SDL_GetError()); - - // Create a texture - state.texture = SDL_CreateTexture( - state.renderer, - SDL_PIXELFORMAT_ABGR8888, - SDL_TEXTUREACCESS_STREAMING, - SCREEN_SIZE_X, - SCREEN_SIZE_Y); - ASSERT(state.texture, "Error creating SDL texture: %s\n", SDL_GetError()); - - // Init camera - state.camera_pos = (v2) { 5.0f, 5.0f }; - state.camera_dir = ((v2) {cos(0.0), sin(0.0)}); - state.camera_dir_rotr = rotr((state.camera_dir)); - state.camera_width = 1.5f; - state.camera_height = state.camera_width * SCREEN_SIZE_Y / SCREEN_SIZE_X; - state.camera_z = 0.4; - - // Init player state - state.player_speed = (v2) { 0.0f, 0.0f }; - state.player_omega = 0.0f; - - // Init keyboard - clear_keyboard_state(&state.keyboard_state); - - // Time structs - struct timeval timeval_start, timeval_end; - - // Main loop - u32 time_prev_tick = SDL_GetTicks(); - state.quit = 0; - while (state.quit == 0) { - const u32 time_start = SDL_GetTicks(); - gettimeofday(&timeval_start, NULL); - - SDL_Event event; - while (SDL_PollEvent(&event)) { - if (event.type == SDL_QUIT) { - state.quit = 1; - break; - } else if (event.type == SDL_KEYDOWN) { - switch (event.key.keysym.sym) { - case (SDLK_UP) : state.keyboard_state.up = KeyboardKeyState_Pressed; break; - case (SDLK_DOWN) : state.keyboard_state.down = KeyboardKeyState_Pressed; break; - case (SDLK_LEFT) : state.keyboard_state.left = KeyboardKeyState_Pressed; break; - case (SDLK_RIGHT) : state.keyboard_state.right = KeyboardKeyState_Pressed; break; - case (SDLK_a) : state.keyboard_state.a = KeyboardKeyState_Pressed; break; - case (SDLK_s) : state.keyboard_state.s = KeyboardKeyState_Pressed; break; - case (SDLK_d) : state.keyboard_state.d = KeyboardKeyState_Pressed; break; - case (SDLK_w) : state.keyboard_state.w = KeyboardKeyState_Pressed; break; - case (SDLK_q) : state.keyboard_state.q = KeyboardKeyState_Pressed; break; - case (SDLK_e) : state.keyboard_state.e = KeyboardKeyState_Pressed; break; - case (SDLK_1) : state.keyboard_state.one = KeyboardKeyState_Pressed; break; - case (SDLK_2) : state.keyboard_state.two = KeyboardKeyState_Pressed; break; - case (SDLK_3) : state.keyboard_state.three = KeyboardKeyState_Pressed; break; - case (SDLK_4) : state.keyboard_state.four = KeyboardKeyState_Pressed; break; - case (SDLK_5) : state.keyboard_state.five = KeyboardKeyState_Pressed; break; - case (SDLK_6) : state.keyboard_state.six = KeyboardKeyState_Pressed; break; - case (SDLK_7) : state.keyboard_state.seven = KeyboardKeyState_Pressed; break; - case (SDLK_8) : state.keyboard_state.eight = KeyboardKeyState_Pressed; break; - } - } else if (event.type == SDL_KEYUP) { - switch (event.key.keysym.sym) { - case (SDLK_UP) : state.keyboard_state.up = KeyboardKeyState_Released; break; - case (SDLK_DOWN) : state.keyboard_state.down = KeyboardKeyState_Released; break; - case (SDLK_LEFT) : state.keyboard_state.left = KeyboardKeyState_Released; break; - case (SDLK_RIGHT) : state.keyboard_state.right = KeyboardKeyState_Released; break; - case (SDLK_a) : state.keyboard_state.a = KeyboardKeyState_Released; break; - case (SDLK_s) : state.keyboard_state.s = KeyboardKeyState_Released; break; - case (SDLK_d) : state.keyboard_state.d = KeyboardKeyState_Released; break; - case (SDLK_w) : state.keyboard_state.w = KeyboardKeyState_Released; break; - case (SDLK_q) : state.keyboard_state.q = KeyboardKeyState_Released; break; - case (SDLK_e) : state.keyboard_state.e = KeyboardKeyState_Released; break; - case (SDLK_1) : state.keyboard_state.one = KeyboardKeyState_Released; break; - case (SDLK_2) : state.keyboard_state.two = KeyboardKeyState_Released; break; - case (SDLK_3) : state.keyboard_state.three = KeyboardKeyState_Released; break; - case (SDLK_4) : state.keyboard_state.four = KeyboardKeyState_Released; break; - case (SDLK_5) : state.keyboard_state.five = KeyboardKeyState_Released; break; - case (SDLK_6) : state.keyboard_state.six = KeyboardKeyState_Released; break; - case (SDLK_7) : state.keyboard_state.seven = KeyboardKeyState_Released; break; - case (SDLK_8) : state.keyboard_state.eight = KeyboardKeyState_Released; break; - } - } - } - - // TODO: Move to more accurate timing? - const u32 time_tick_start = SDL_GetTicks(); - const f32 dt = (time_tick_start - time_prev_tick) / 1000.0f; - tick(dt); - time_prev_tick = time_tick_start; - - render(); - - decay_keyboard_state(&state.keyboard_state); - - // Get timer end for all the non-SDL stuff - gettimeofday(&timeval_end, NULL); - f64 game_ms_elapsed = (timeval_end.tv_sec - timeval_start.tv_sec) * 1000.0; // sec to ms - game_ms_elapsed += (timeval_end.tv_usec - timeval_start.tv_usec) / 1000.0; // us to ms - // printf("Game: %.3f ms, %.1f fps\n", game_ms_elapsed, 1000.0f / max(1.0f, game_ms_elapsed)); - - SDL_UpdateTexture(state.texture, NULL, state.pixels, SCREEN_SIZE_X * 4); - SDL_RenderCopyEx( - state.renderer, - state.texture, - NULL, - NULL, - 0.0, - NULL, - SDL_FLIP_VERTICAL); - - // SDL_RENDERER_PRESENTVSYNC means this is syncronized with the monitor refresh rate. (30Hz) - SDL_RenderPresent(state.renderer); - - const u32 time_end = SDL_GetTicks(); - const u32 ms_elapsed = time_end - time_start; - const f32 fps = 1000.0f / max(1, ms_elapsed); - // printf("FPS: %.1f\n", fps); - } - - SDL_DestroyWindow(state.window); - return 0; -} diff --git a/scripts/animation_thing.ps1 b/scripts/animation_thing.ps1 new file mode 100644 index 0000000..db53d51 --- /dev/null +++ b/scripts/animation_thing.ps1 @@ -0,0 +1 @@ +magick montage -tile 3x1 -geometry +0+0 -background transparent .\assets\animations\torch_fixture_*.png assets/fixtures/torch_fixture.png