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. 59
      amt/matrix.hpp
  4. 21
      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:
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
./builddir/runtests

@ -1,4 +1,8 @@
#include "amt/raycaster.hpp"
#include <iostream>
#include <chrono>
#include <numeric>
#include <functional>
#define RAY_VIEW_WIDTH 960
#define RAY_VIEW_HEIGHT 720
@ -8,8 +12,6 @@
static const int SCREEN_HEIGHT=720;
static const int SCREEN_WIDTH=1280;
using Matrix = amt::Matrix<int>;
Matrix MAP{
{8,8,8,8,8,8,8,8,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");
//ZED this should set with a function
float player_x = MAP.cols() / 2;
float player_y = MAP.rows() / 2;
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);
@ -38,8 +40,23 @@ int main() {
double moveSpeed = 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()) {
auto start = std::chrono::high_resolution_clock::now();
rayview.render();
auto end = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration<double>(end - start);
auto frame = 1 / elapsed.count();
frames.push_back(frame);
if (it % max_count == 0) {
auto frame = std::accumulate(frames.begin(), frames.end(), 0., std::plus<>{}) / max_count;
std::cout << "Frame: " << frame << '\n';
frames.clear();
it = 1;
}
++it;
// DRAW GUI
window.display();

@ -1,5 +1,4 @@
#ifndef AMT_MATRIX_HPP
#define AMT_MATRIX_HPP
#pragma once
#include <cassert>
#include <cstddef>
@ -10,6 +9,17 @@
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>
struct Matrix {
using value_type = T;
@ -28,16 +38,20 @@ namespace amt {
struct View {
using base_type = std::conditional_t<IsConst, const_pointer, pointer>;
base_type data;
size_type size;
constexpr reference operator[](size_type k) noexcept requires (!IsConst) {
assert(k < size && "Out of bound access");
return data[k];
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 k) const noexcept {
assert(k < size && "Out of bound access");
return data[k];
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];
}
};
@ -120,28 +134,26 @@ namespace amt {
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 = r + c * m_row; // row-major;
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 = r + c * m_row; // row-major;
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 c) noexcept -> View<false> {
auto const base = c * m_row;
assert(c < cols() && "Out of bound access");
return { .data = m_data + base, .size = m_row };
constexpr auto operator[](size_type r) noexcept -> View<false> {
assert(r < rows() && "Out of bound access");
return { .data = m_data, .r = r, .rows = m_row, .cols = m_col };
}
constexpr auto operator[](size_type c) const noexcept -> View<true> {
auto const base = c * m_row;
assert(c < cols() && "Out of bound access");
return { .data = m_data + base, .size = m_row };
constexpr auto operator[](size_type r) const noexcept -> View<true> {
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 {
@ -159,7 +171,7 @@ namespace amt {
} // namespace amt
#if 0
#include <format>
namespace std {
template <typename T>
@ -182,5 +194,4 @@ namespace std {
};
} // namespace std
#endif // AMT_MATRIX_HPP
#endif

@ -62,8 +62,20 @@ namespace amt {
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;
RGBA& operator=(RGBA const& other) noexcept {
// HACK: clang was unable to optimize the copy using a single move instruction.
auto& self = *reinterpret_cast<std::uint32_t*>(this);
auto color = *reinterpret_cast<std::uint32_t const*>(&other);
self = color;
return *this;
}
RGBA& operator=(RGBA && other) noexcept {
// HACK: clang was unable to optimize the copy using a single move instruction
auto& self = *reinterpret_cast<std::uint32_t*>(this);
auto color = *reinterpret_cast<std::uint32_t const*>(&other);
self = color;
return *this;
}
constexpr ~RGBA() noexcept = default;
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 &&) noexcept = 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 rend() const noexcept -> const_reverse_iterator { return m_data.rend(); }
constexpr 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) 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); }

@ -1,4 +1,5 @@
#include "amt/raycaster.hpp"
#include "pixel.hpp"
using namespace fmt;
using std::make_unique;
@ -7,16 +8,11 @@ using std::make_unique;
#define gray_color(c) rgba_color(c, c, c, 255)
std::vector<uint32_t> TexturePack::load_image(const char *filename) {
std::vector<uint32_t> texture(TEXTURE_WIDTH * TEXTURE_HEIGHT);
amt::PixelBuf TexturePack::load_image(const char *filename) {
sf::Image img;
bool good = img.loadFromFile(filename);
dbc::check(good, format("failed to load {}", filename));
uint32_t *pixbuf = (uint32_t *)img.getPixelsPtr();
std::copy_n(pixbuf, texture.size(), texture.begin());
return texture;
return amt::PixelBuf(img.getPixelsPtr(), TEXTURE_HEIGHT, TEXTURE_WIDTH);
}
void TexturePack::load_textures() {
@ -31,7 +27,7 @@ void TexturePack::load_textures() {
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];
}
@ -43,6 +39,7 @@ Sprite &TexturePack::get_sprite(size_t sprite_num) {
Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map, int width, int height) :
$width(width), $height(height),
pixels(static_cast<size_t>(height), static_cast<std::size_t>(width)),
$window(window),
$map(map),
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_sprite.setTexture(view_texture);
view_sprite.setPosition(0, 0);
pixels = make_unique<RGBA[]>($width * $height);
textures.load_textures();
}
@ -68,13 +64,13 @@ void Raycaster::position_camera(float player_x, float player_y) {
}
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?
$window.draw(view_sprite);
}
void Raycaster::clear() {
std::fill_n(pixels.get(), $width * $height, 0);
pixels.fill({});
$window.clear();
}
@ -149,16 +145,15 @@ void Raycaster::sprite_casting() {
int texY = ((d * textureHeight) / spriteHeight) / 256;
//get current color from the texture
// 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
if((color & 0x00FFFFFF) != 0) {
RGBA pixel = color;
pixels[pixcoord(stripe, y)] = pixel;
if((color.to_hex() & 0xFFFFFF00) != 0) {
pixels[y][stripe] = color;
}
}
}
}
}
}
}
void Raycaster::cast_rays() {
@ -217,7 +212,7 @@ void Raycaster::cast_rays() {
side = 1;
}
if($map[mapX][mapY] > 0) hit = 1;
if($map[mapY][mapX] > 0) hit = 1;
}
if(side == 0) {
@ -234,7 +229,7 @@ void Raycaster::cast_rays() {
int drawEnd = lineHeight / 2 + $height / 2 + PITCH;
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
double wallX; // where exactly the wall was hit
@ -260,8 +255,7 @@ void Raycaster::cast_rays() {
for(int y = drawStart; y < drawEnd; y++) {
int texY = (int)texPos & (textures.TEXTURE_HEIGHT - 1);
texPos += step;
RGBA pixel = texture[textures.TEXTURE_HEIGHT * texY + texX];
pixels[pixcoord(x, y)] = pixel;
pixels[y][x] = texture[texY][texX];
}
// SET THE ZBUFFER FOR THE SPRITE CASTING
@ -270,8 +264,8 @@ void Raycaster::cast_rays() {
}
void Raycaster::draw_ceiling_floor() {
const int textureWidth = textures.TEXTURE_WIDTH;
const int textureHeight = textures.TEXTURE_HEIGHT;
const size_t textureWidth = textures.TEXTURE_WIDTH;
const size_t textureHeight = textures.TEXTURE_HEIGHT;
auto& floorTexture = textures.get(textures.floor);
auto& ceilingTexture = textures.get(textures.ceiling);
@ -322,19 +316,17 @@ void Raycaster::draw_ceiling_floor() {
floorY += floorStepY;
// now get the pixel from the texture
uint32_t color;
// this uses the previous ty/tx fractional parts of
// floorX cellX to find the texture x/y. How?
// FLOOR
color = floorTexture[textureWidth * ty + tx];
pixels[pixcoord(x, y)] = color;
pixels[y][x] = floorTexture[ty][tx];
// CEILING
color = ceilingTexture[textureWidth * ty + tx];
pixels[pixcoord(x, $height - y - 1)] = color;
pixels[$height - y - 1][x] = ceilingTexture[ty][tx];
}
}
}
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(),
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 <algorithm>
#include <cmath>
#include "amt/matrix.hpp"
#include "matrix.hpp"
#include <cstdlib>
#include <array>
#include "dbc.hpp"
#include "pixel.hpp"
#include <memory>
using Matrix = amt::Matrix<int>;
@ -32,15 +33,15 @@ struct TexturePack {
int TEXTURE_WIDTH=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}};
const int floor = 3;
const int ceiling = 6;
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);
std::vector<uint32_t>& get(size_t num);
amt::PixelBuf& get(size_t num);
};
struct Raycaster {
@ -61,10 +62,10 @@ struct Raycaster {
sf::Sprite view_sprite;
//ZED: USE smart pointer for this
std::unique_ptr<RGBA[]> pixels = nullptr;
int $width;
int $height;
amt::PixelBuf pixels;
sf::RenderWindow& $window;
Matrix& $map;
std::vector<int> spriteOrder;

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

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