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 2 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:
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];
}
};
@ -83,7 +97,7 @@ namespace amt {
}
Matrix(std::initializer_list<std::initializer_list<value_type>> li)
: m_row(li.size())
: m_row(li.size())
{
for (auto const& row: li) {
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 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>
@ -167,7 +179,7 @@ namespace std {
constexpr auto parse(format_parse_context& ctx) {
return ctx.begin();
}
auto format(amt::Matrix<T> const& m, auto& ctx) const {
std::string s = "[\n";
for (auto r = std::size_t{}; r < m.rows(); ++r) {
@ -175,12 +187,11 @@ namespace std {
s += std::format("{}, ", m(r, c));
}
s += '\n';
}
}
s += "]";
return format_to(ctx.out(), "{}", s);
}
};
} // 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); }
@ -579,4 +592,4 @@ namespace std {
};
} // namespace std
#endif // AMT_PIXEL_HPP
#endif // AMT_PIXEL_HPP

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

Loading…
Cancel
Save