Brought in Amit's latest and will now merge in my fixing from last night into his to get them synced up.

master
Zed A. Shaw 11 months ago
parent adfb6367d7
commit c91e8fc543
  1. 8
      Makefile
  2. 25
      amt/main.cpp
  3. 65
      amt/matrix.hpp
  4. 23
      amt/pixel.hpp
  5. 48
      amt/raycaster.cpp
  6. 11
      amt/raycaster.hpp
  7. 5
      main.cpp
  8. 1
      meson.build

@ -9,6 +9,14 @@ reset:
build: build:
meson compile -j 10 -C builddir meson compile -j 10 -C builddir
release_build:
meson --wipe builddir --buildtype release
meson compile -j 10 -C builddir
debug_build:
meson --wipe builddir --buildtype debug
meson compile -j 10 -C builddir
test: build test: build
./builddir/runtests ./builddir/runtests

@ -1,4 +1,8 @@
#include "amt/raycaster.hpp" #include "amt/raycaster.hpp"
#include <iostream>
#include <chrono>
#include <numeric>
#include <functional>
#define RAY_VIEW_WIDTH 960 #define RAY_VIEW_WIDTH 960
#define RAY_VIEW_HEIGHT 720 #define RAY_VIEW_HEIGHT 720
@ -8,8 +12,6 @@
static const int SCREEN_HEIGHT=720; static const int SCREEN_HEIGHT=720;
static const int SCREEN_WIDTH=1280; static const int SCREEN_WIDTH=1280;
using Matrix = amt::Matrix<int>;
Matrix MAP{ Matrix MAP{
{8,8,8,8,8,8,8,8,8}, {8,8,8,8,8,8,8,8,8},
{8,0,2,0,0,0,0,0,8}, {8,0,2,0,0,0,0,0,8},
@ -28,8 +30,8 @@ int main() {
sf::RenderWindow window(sf::VideoMode(SCREEN_WIDTH, SCREEN_HEIGHT), "Zed's Ray Caster Game Thing"); sf::RenderWindow window(sf::VideoMode(SCREEN_WIDTH, SCREEN_HEIGHT), "Zed's Ray Caster Game Thing");
//ZED this should set with a function //ZED this should set with a function
float player_x = MAP.cols() / 2; float player_x = MAP.rows() / 2;
float player_y = MAP.rows() / 2; float player_y = MAP.cols() / 2;
Raycaster rayview(window, MAP, RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT); Raycaster rayview(window, MAP, RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT);
rayview.set_position(RAY_VIEW_X, RAY_VIEW_Y); rayview.set_position(RAY_VIEW_X, RAY_VIEW_Y);
@ -38,8 +40,23 @@ int main() {
double moveSpeed = 0.1; double moveSpeed = 0.1;
double rotSpeed = 0.1; double rotSpeed = 0.1;
std::size_t const max_count = 100;
std::vector<double> frames(max_count);
std::size_t it = 1;
while(window.isOpen()) { while(window.isOpen()) {
auto start = std::chrono::high_resolution_clock::now();
rayview.render(); rayview.render();
auto end = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration<double>(end - start);
auto frame = 1 / elapsed.count();
frames.push_back(frame);
if (it % max_count == 0) {
auto frame = std::accumulate(frames.begin(), frames.end(), 0., std::plus<>{}) / max_count;
std::cout << "Frame: " << frame << '\n';
frames.clear();
it = 1;
}
++it;
// DRAW GUI // DRAW GUI
window.display(); window.display();

@ -1,5 +1,4 @@
#ifndef AMT_MATRIX_HPP #pragma once
#define AMT_MATRIX_HPP
#include <cassert> #include <cassert>
#include <cstddef> #include <cstddef>
@ -10,6 +9,17 @@
namespace amt { 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 <typename T> template <typename T>
struct Matrix { struct Matrix {
using value_type = T; using value_type = T;
@ -28,16 +38,20 @@ namespace amt {
struct View { struct View {
using base_type = std::conditional_t<IsConst, const_pointer, pointer>; using base_type = std::conditional_t<IsConst, const_pointer, pointer>;
base_type data; base_type data;
size_type size; size_type r;
size_type rows;
constexpr reference operator[](size_type k) noexcept requires (!IsConst) { size_type cols;
assert(k < size && "Out of bound access");
return data[k]; 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 k) const noexcept { constexpr const_reference operator[](size_type c) const noexcept {
assert(k < size && "Out of bound access"); assert(c < cols && "Out of bound access");
return data[k]; auto const index = detail::cal_index(r, c, rows, cols);
return data[index];
} }
}; };
@ -83,7 +97,7 @@ namespace amt {
} }
Matrix(std::initializer_list<std::initializer_list<value_type>> li) Matrix(std::initializer_list<std::initializer_list<value_type>> li)
: m_row(li.size()) : m_row(li.size())
{ {
for (auto const& row: li) { for (auto const& row: li) {
m_col = std::max(m_col, row.size()); m_col = std::max(m_col, row.size());
@ -120,28 +134,26 @@ namespace amt {
constexpr const_reverse_iterator rbegin() const noexcept { return std::reverse_iterator(end()); } 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 const_reverse_iterator rend() const noexcept { return std::reverse_iterator(begin()); }
constexpr auto operator()(size_type r, size_type c) noexcept -> reference { constexpr auto operator()(size_type r, size_type c) noexcept -> reference {
auto const index = r + c * m_row; // row-major; auto const index = detail::cal_index(r, c, rows(), cols());
assert(index < size() && "Out of bound access"); assert(index < size() && "Out of bound access");
return m_data[index]; return m_data[index];
} }
constexpr auto operator()(size_type r, size_type c) const noexcept -> const_reference { constexpr auto operator()(size_type r, size_type c) const noexcept -> const_reference {
auto const index = r + c * m_row; // row-major; auto const index = detail::cal_index(r, c, rows(), cols());
assert(index < size() && "Out of bound access"); assert(index < size() && "Out of bound access");
return m_data[index]; return m_data[index];
} }
constexpr auto operator[](size_type c) noexcept -> View<false> { constexpr auto operator[](size_type r) noexcept -> View<false> {
auto const base = c * m_row; assert(r < rows() && "Out of bound access");
assert(c < cols() && "Out of bound access"); return { .data = m_data, .r = r, .rows = m_row, .cols = m_col };
return { .data = m_data + base, .size = m_row };
} }
constexpr auto operator[](size_type c) const noexcept -> View<true> { constexpr auto operator[](size_type r) const noexcept -> View<true> {
auto const base = c * m_row; assert(r < rows() && "Out of bound access");
assert(c < cols() && "Out of bound access"); return { .data = m_data, .r = r, .rows = m_row, .cols = m_col };
return { .data = m_data + base, .size = m_row };
} }
friend void swap(Matrix& lhs, Matrix& rhs) noexcept { friend void swap(Matrix& lhs, Matrix& rhs) noexcept {
@ -159,7 +171,7 @@ namespace amt {
} // namespace amt } // namespace amt
#if 0
#include <format> #include <format>
namespace std { namespace std {
template <typename T> template <typename T>
@ -167,7 +179,7 @@ namespace std {
constexpr auto parse(format_parse_context& ctx) { constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); return ctx.begin();
} }
auto format(amt::Matrix<T> const& m, auto& ctx) const { auto format(amt::Matrix<T> const& m, auto& ctx) const {
std::string s = "[\n"; std::string s = "[\n";
for (auto r = std::size_t{}; r < m.rows(); ++r) { for (auto r = std::size_t{}; r < m.rows(); ++r) {
@ -175,12 +187,11 @@ namespace std {
s += std::format("{}, ", m(r, c)); s += std::format("{}, ", m(r, c));
} }
s += '\n'; s += '\n';
} }
s += "]"; s += "]";
return format_to(ctx.out(), "{}", s); return format_to(ctx.out(), "{}", s);
} }
}; };
} // namespace std } // namespace std
#endif
#endif // AMT_MATRIX_HPP

@ -62,8 +62,20 @@ namespace amt {
constexpr RGBA() noexcept = default; constexpr RGBA() noexcept = default;
constexpr RGBA(RGBA const&) noexcept = default; constexpr RGBA(RGBA const&) noexcept = default;
constexpr RGBA(RGBA &&) noexcept = default; constexpr RGBA(RGBA &&) noexcept = default;
constexpr RGBA& operator=(RGBA const&) noexcept = default; RGBA& operator=(RGBA const& other) noexcept {
constexpr RGBA& operator=(RGBA &&) noexcept = default; // HACK: clang was unable to optimize the copy using a single move instruction.
auto& self = *reinterpret_cast<std::uint32_t*>(this);
auto color = *reinterpret_cast<std::uint32_t const*>(&other);
self = color;
return *this;
}
RGBA& operator=(RGBA && other) noexcept {
// HACK: clang was unable to optimize the copy using a single move instruction
auto& self = *reinterpret_cast<std::uint32_t*>(this);
auto color = *reinterpret_cast<std::uint32_t const*>(&other);
self = color;
return *this;
}
constexpr ~RGBA() noexcept = default; constexpr ~RGBA() noexcept = default;
constexpr RGBA(pixel_t r, pixel_t g, pixel_t b, pixel_t a = 0xff) noexcept constexpr RGBA(pixel_t r, pixel_t g, pixel_t b, pixel_t a = 0xff) noexcept
@ -452,6 +464,7 @@ namespace amt {
} }
} }
PixelBuf() noexcept = default;
PixelBuf(PixelBuf const&) = default; PixelBuf(PixelBuf const&) = default;
PixelBuf(PixelBuf &&) noexcept = default; PixelBuf(PixelBuf &&) noexcept = default;
PixelBuf& operator=(PixelBuf const&) = default; PixelBuf& operator=(PixelBuf const&) = default;
@ -476,8 +489,8 @@ namespace amt {
constexpr auto rbegin() const noexcept -> const_reverse_iterator { return m_data.rbegin(); } 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 auto rend() const noexcept -> const_reverse_iterator { return m_data.rend(); }
constexpr auto operator[](size_type r) noexcept { return m_data[r]; } constexpr decltype(auto) operator[](size_type r) noexcept { return m_data[r]; }
constexpr auto operator[](size_type r) const 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) 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 operator()(size_type r, size_type c) const noexcept -> const_reference { return m_data(r, c); }
@ -579,4 +592,4 @@ namespace std {
}; };
} // namespace std } // namespace std
#endif // AMT_PIXEL_HPP #endif // AMT_PIXEL_HPP

@ -1,4 +1,5 @@
#include "amt/raycaster.hpp" #include "amt/raycaster.hpp"
#include "pixel.hpp"
using namespace fmt; using namespace fmt;
using std::make_unique; using std::make_unique;
@ -7,16 +8,11 @@ using std::make_unique;
#define gray_color(c) rgba_color(c, c, c, 255) #define gray_color(c) rgba_color(c, c, c, 255)
std::vector<uint32_t> TexturePack::load_image(const char *filename) { amt::PixelBuf TexturePack::load_image(const char *filename) {
std::vector<uint32_t> texture(TEXTURE_WIDTH * TEXTURE_HEIGHT);
sf::Image img; sf::Image img;
bool good = img.loadFromFile(filename); bool good = img.loadFromFile(filename);
dbc::check(good, format("failed to load {}", filename)); dbc::check(good, format("failed to load {}", filename));
return amt::PixelBuf(img.getPixelsPtr(), TEXTURE_HEIGHT, TEXTURE_WIDTH);
uint32_t *pixbuf = (uint32_t *)img.getPixelsPtr();
std::copy_n(pixbuf, texture.size(), texture.begin());
return texture;
} }
void TexturePack::load_textures() { void TexturePack::load_textures() {
@ -31,7 +27,7 @@ void TexturePack::load_textures() {
images.emplace_back(load_image("assets/portal.png")); images.emplace_back(load_image("assets/portal.png"));
} }
std::vector<uint32_t>& TexturePack::get(size_t num) { amt::PixelBuf& TexturePack::get(size_t num) {
return images[num]; return images[num];
} }
@ -43,6 +39,7 @@ Sprite &TexturePack::get_sprite(size_t sprite_num) {
Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map, int width, int height) : Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map, int width, int height) :
$width(width), $height(height), $width(width), $height(height),
pixels(static_cast<size_t>(height), static_cast<std::size_t>(width)),
$window(window), $window(window),
$map(map), $map(map),
spriteOrder(textures.NUM_SPRITES), spriteOrder(textures.NUM_SPRITES),
@ -53,7 +50,6 @@ Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map, int width, int heigh
view_texture.create($width, $height); view_texture.create($width, $height);
view_sprite.setTexture(view_texture); view_sprite.setTexture(view_texture);
view_sprite.setPosition(0, 0); view_sprite.setPosition(0, 0);
pixels = make_unique<RGBA[]>($width * $height);
textures.load_textures(); textures.load_textures();
} }
@ -68,13 +64,13 @@ void Raycaster::position_camera(float player_x, float player_y) {
} }
void Raycaster::draw_pixel_buffer() { void Raycaster::draw_pixel_buffer() {
view_texture.update((uint8_t *)pixels.get(), $width, $height, 0, 0); view_texture.update(pixels.to_raw_buf(), $width, $height, 0, 0);
// BUG: can I do this once and just update it? // BUG: can I do this once and just update it?
$window.draw(view_sprite); $window.draw(view_sprite);
} }
void Raycaster::clear() { void Raycaster::clear() {
std::fill_n(pixels.get(), $width * $height, 0); pixels.fill({});
$window.clear(); $window.clear();
} }
@ -149,16 +145,15 @@ void Raycaster::sprite_casting() {
int texY = ((d * textureHeight) / spriteHeight) / 256; int texY = ((d * textureHeight) / spriteHeight) / 256;
//get current color from the texture //get current color from the texture
// BUG: this crashes sometimes when the math goes out of bounds // BUG: this crashes sometimes when the math goes out of bounds
uint32_t color = sprite_texture[textureWidth * texY + texX]; auto color = sprite_texture[texY][texX];
// poor person's transparency, get current color from the texture // poor person's transparency, get current color from the texture
if((color & 0x00FFFFFF) != 0) { if((color.to_hex() & 0xFFFFFF00) != 0) {
RGBA pixel = color; pixels[y][stripe] = color;
pixels[pixcoord(stripe, y)] = pixel;
} }
} }
} }
} }
} }
} }
void Raycaster::cast_rays() { void Raycaster::cast_rays() {
@ -217,7 +212,7 @@ void Raycaster::cast_rays() {
side = 1; side = 1;
} }
if($map[mapX][mapY] > 0) hit = 1; if($map[mapY][mapX] > 0) hit = 1;
} }
if(side == 0) { if(side == 0) {
@ -234,7 +229,7 @@ void Raycaster::cast_rays() {
int drawEnd = lineHeight / 2 + $height / 2 + PITCH; int drawEnd = lineHeight / 2 + $height / 2 + PITCH;
if(drawEnd >= $height) drawEnd = $height - 1; if(drawEnd >= $height) drawEnd = $height - 1;
auto &texture = textures.get($map[mapX][mapY] - 1); auto &texture = textures.get($map[mapY][mapX] - 1);
// calculate value of wallX // calculate value of wallX
double wallX; // where exactly the wall was hit double wallX; // where exactly the wall was hit
@ -260,8 +255,7 @@ void Raycaster::cast_rays() {
for(int y = drawStart; y < drawEnd; y++) { for(int y = drawStart; y < drawEnd; y++) {
int texY = (int)texPos & (textures.TEXTURE_HEIGHT - 1); int texY = (int)texPos & (textures.TEXTURE_HEIGHT - 1);
texPos += step; texPos += step;
RGBA pixel = texture[textures.TEXTURE_HEIGHT * texY + texX]; pixels[y][x] = texture[texY][texX];
pixels[pixcoord(x, y)] = pixel;
} }
// SET THE ZBUFFER FOR THE SPRITE CASTING // SET THE ZBUFFER FOR THE SPRITE CASTING
@ -270,8 +264,8 @@ void Raycaster::cast_rays() {
} }
void Raycaster::draw_ceiling_floor() { void Raycaster::draw_ceiling_floor() {
const int textureWidth = textures.TEXTURE_WIDTH; const size_t textureWidth = textures.TEXTURE_WIDTH;
const int textureHeight = textures.TEXTURE_HEIGHT; const size_t textureHeight = textures.TEXTURE_HEIGHT;
auto& floorTexture = textures.get(textures.floor); auto& floorTexture = textures.get(textures.floor);
auto& ceilingTexture = textures.get(textures.ceiling); auto& ceilingTexture = textures.get(textures.ceiling);
@ -322,19 +316,17 @@ void Raycaster::draw_ceiling_floor() {
floorY += floorStepY; floorY += floorStepY;
// now get the pixel from the texture // now get the pixel from the texture
uint32_t color;
// this uses the previous ty/tx fractional parts of // this uses the previous ty/tx fractional parts of
// floorX cellX to find the texture x/y. How? // floorX cellX to find the texture x/y. How?
// FLOOR // FLOOR
color = floorTexture[textureWidth * ty + tx]; pixels[y][x] = floorTexture[ty][tx];
pixels[pixcoord(x, y)] = color;
// CEILING // CEILING
color = ceilingTexture[textureWidth * ty + tx]; pixels[$height - y - 1][x] = ceilingTexture[ty][tx];
pixels[pixcoord(x, $height - y - 1)] = color;
} }
} }
} }
void Raycaster::render() { void Raycaster::render() {
@ -350,7 +342,7 @@ bool Raycaster::empty_space(int new_x, int new_y) {
dbc::check((size_t)new_y < $map.rows(), dbc::check((size_t)new_y < $map.rows(),
format("y={} too high={}", new_y, $map.rows())); format("y={} too high={}", new_y, $map.rows()));
return $map[new_x][new_y] == 0; return $map[new_y][new_x] == 0;
} }

@ -6,10 +6,11 @@
#include <numbers> #include <numbers>
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include "amt/matrix.hpp" #include "matrix.hpp"
#include <cstdlib> #include <cstdlib>
#include <array> #include <array>
#include "dbc.hpp" #include "dbc.hpp"
#include "pixel.hpp"
#include <memory> #include <memory>
using Matrix = amt::Matrix<int>; using Matrix = amt::Matrix<int>;
@ -32,15 +33,15 @@ struct TexturePack {
int TEXTURE_WIDTH=256; // must be power of two int TEXTURE_WIDTH=256; // must be power of two
int TEXTURE_HEIGHT=256; // must be power of two int TEXTURE_HEIGHT=256; // must be power of two
std::vector<std::vector<uint32_t>> images; std::vector<amt::PixelBuf> images;
std::vector<Sprite> SPRITE{{4.0, 3.55, 8}}; std::vector<Sprite> SPRITE{{4.0, 3.55, 8}};
const int floor = 3; const int floor = 3;
const int ceiling = 6; const int ceiling = 6;
void load_textures(); void load_textures();
std::vector<uint32_t> load_image(const char *filename); amt::PixelBuf load_image(const char *filename);
Sprite &get_sprite(size_t sprite_num); Sprite &get_sprite(size_t sprite_num);
std::vector<uint32_t>& get(size_t num); amt::PixelBuf& get(size_t num);
}; };
struct Raycaster { struct Raycaster {
@ -61,10 +62,10 @@ struct Raycaster {
sf::Sprite view_sprite; sf::Sprite view_sprite;
//ZED: USE smart pointer for this //ZED: USE smart pointer for this
std::unique_ptr<RGBA[]> pixels = nullptr;
int $width; int $width;
int $height; int $height;
amt::PixelBuf pixels;
sf::RenderWindow& $window; sf::RenderWindow& $window;
Matrix& $map; Matrix& $map;
std::vector<int> spriteOrder; std::vector<int> spriteOrder;

@ -1,4 +1,8 @@
#include "raycaster.hpp" #include "raycaster.hpp"
#include <iostream>
#include <chrono>
#include <numeric>
#include <functional>
#define RAY_VIEW_WIDTH 960 #define RAY_VIEW_WIDTH 960
#define RAY_VIEW_HEIGHT 720 #define RAY_VIEW_HEIGHT 720
@ -38,6 +42,7 @@ int main() {
while(window.isOpen()) { while(window.isOpen()) {
rayview.render(); rayview.render();
// DRAW GUI // DRAW GUI
window.display(); window.display();

@ -36,4 +36,5 @@ executable('amtcaster', [
'amt/raycaster.cpp', 'amt/raycaster.cpp',
'amt/main.cpp' 'amt/main.cpp'
], ],
cpp_args: ['-std=c++23'],
dependencies: dependencies) dependencies: dependencies)