mirror of
https://github.com/ssloy/tinyraycaster.git
synced 2025-08-24 06:33:03 +02:00
a bit of refactoring
This commit is contained in:
46
gui.cpp
46
gui.cpp
@@ -1,5 +1,6 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
|
|
||||||
#include "map.h"
|
#include "map.h"
|
||||||
@@ -12,15 +13,18 @@
|
|||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
FrameBuffer fb{1024, 512, std::vector<uint32_t>(1024*512, pack_color(255, 255, 255))};
|
FrameBuffer fb{1024, 512, std::vector<uint32_t>(1024*512, pack_color(255, 255, 255))};
|
||||||
Player player{3.456, 2.345, 1.523, M_PI/3., 0, 0};
|
GameState gs{ Map(), // game map
|
||||||
Map map;
|
{3.456, 2.345, 1.523, M_PI/3., 0, 0}, // player
|
||||||
Texture tex_walls("../walltext.png");
|
{ {3.523, 3.812, 2, 0}, // monsters lists
|
||||||
Texture tex_monst("../monsters.png");
|
{1.834, 8.765, 0, 0},
|
||||||
if (!tex_walls.count || !tex_monst.count) {
|
{5.323, 5.365, 1, 0},
|
||||||
|
{4.123, 10.76, 1, 0} },
|
||||||
|
Texture("../walltext.png"), // textures for the walls
|
||||||
|
Texture("../monsters.png") }; // textures for the monsters
|
||||||
|
if (!gs.tex_walls.count || !gs.tex_monst.count) {
|
||||||
std::cerr << "Failed to load textures" << std::endl;
|
std::cerr << "Failed to load textures" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
std::vector<Sprite> sprites{ {3.523, 3.812, 2, 0}, {1.834, 8.765, 0, 0}, {5.323, 5.365, 1, 0}, {4.123, 10.765, 1, 0} };
|
|
||||||
|
|
||||||
SDL_Window *window = nullptr;
|
SDL_Window *window = nullptr;
|
||||||
SDL_Renderer *renderer = nullptr;
|
SDL_Renderer *renderer = nullptr;
|
||||||
@@ -42,27 +46,31 @@ int main() {
|
|||||||
if (SDL_PollEvent(&event)) {
|
if (SDL_PollEvent(&event)) {
|
||||||
if (SDL_QUIT==event.type || (SDL_KEYDOWN==event.type && SDLK_ESCAPE==event.key.keysym.sym)) break;
|
if (SDL_QUIT==event.type || (SDL_KEYDOWN==event.type && SDLK_ESCAPE==event.key.keysym.sym)) break;
|
||||||
if (SDL_KEYUP==event.type) {
|
if (SDL_KEYUP==event.type) {
|
||||||
if ('a'==event.key.keysym.sym || 'd'==event.key.keysym.sym) player.turn = 0;
|
if ('a'==event.key.keysym.sym || 'd'==event.key.keysym.sym) gs.player.turn = 0;
|
||||||
if ('w'==event.key.keysym.sym || 's'==event.key.keysym.sym) player.walk = 0;
|
if ('w'==event.key.keysym.sym || 's'==event.key.keysym.sym) gs.player.walk = 0;
|
||||||
}
|
}
|
||||||
if (SDL_KEYDOWN==event.type) {
|
if (SDL_KEYDOWN==event.type) {
|
||||||
if ('a'==event.key.keysym.sym) player.turn = -1;
|
if ('a'==event.key.keysym.sym) gs.player.turn = -1;
|
||||||
if ('d'==event.key.keysym.sym) player.turn = 1;
|
if ('d'==event.key.keysym.sym) gs.player.turn = 1;
|
||||||
if ('w'==event.key.keysym.sym) player.walk = 1;
|
if ('w'==event.key.keysym.sym) gs.player.walk = 1;
|
||||||
if ('s'==event.key.keysym.sym) player.walk = -1;
|
if ('s'==event.key.keysym.sym) gs.player.walk = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
player.a += float(player.turn)*.05;
|
gs.player.a += float(gs.player.turn)*.05;
|
||||||
float nx = player.x + player.walk*cos(player.a)*.1;
|
float nx = gs.player.x + gs.player.walk*cos(gs.player.a)*.1;
|
||||||
float ny = player.y + player.walk*sin(player.a)*.1;
|
float ny = gs.player.y + gs.player.walk*sin(gs.player.a)*.1;
|
||||||
|
|
||||||
if (int(nx)>=0 && int(nx)<int(map.w) && int(ny)>=0 && int(ny)<int(map.h) && map.is_empty(nx, ny)) {
|
if (int(nx)>=0 && int(nx)<int(gs.map.w) && int(ny)>=0 && int(ny)<int(gs.map.h) && gs.map.is_empty(nx, ny)) {
|
||||||
player.x = nx;
|
gs.player.x = nx;
|
||||||
player.y = ny;
|
gs.player.y = ny;
|
||||||
}
|
}
|
||||||
|
for (size_t i=0; i<gs.monsters.size(); i++) { // update the distances from the player to each sprite
|
||||||
|
gs.monsters[i].player_dist = std::sqrt(pow(gs.player.x - gs.monsters[i].x, 2) + pow(gs.player.y - gs.monsters[i].y, 2));
|
||||||
|
}
|
||||||
|
std::sort(gs.monsters.begin(), gs.monsters.end()); // sort it from farthest to closest
|
||||||
|
|
||||||
render(fb, map, player, sprites, tex_walls, tex_monst);
|
render(fb, gs);
|
||||||
SDL_UpdateTexture(framebuffer_texture, NULL, reinterpret_cast<void *>(fb.img.data()), fb.w*4);
|
SDL_UpdateTexture(framebuffer_texture, NULL, reinterpret_cast<void *>(fb.img.data()), fb.w*4);
|
||||||
|
|
||||||
SDL_RenderClear(renderer);
|
SDL_RenderClear(renderer);
|
||||||
|
4
map.cpp
4
map.cpp
@@ -24,12 +24,12 @@ Map::Map() : w(16), h(16) {
|
|||||||
assert(sizeof(map) == w*h+1); // +1 for the null terminated string
|
assert(sizeof(map) == w*h+1); // +1 for the null terminated string
|
||||||
}
|
}
|
||||||
|
|
||||||
int Map::get(const size_t i, const size_t j) {
|
int Map::get(const size_t i, const size_t j) const {
|
||||||
assert(i<w && j<h && sizeof(map) == w*h+1);
|
assert(i<w && j<h && sizeof(map) == w*h+1);
|
||||||
return map[i+j*w] - '0';
|
return map[i+j*w] - '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Map::is_empty(const size_t i, const size_t j) {
|
bool Map::is_empty(const size_t i, const size_t j) const {
|
||||||
assert(i<w && j<h && sizeof(map) == w*h+1);
|
assert(i<w && j<h && sizeof(map) == w*h+1);
|
||||||
return map[i+j*w] == ' ';
|
return map[i+j*w] == ' ';
|
||||||
}
|
}
|
||||||
|
4
map.h
4
map.h
@@ -6,8 +6,8 @@
|
|||||||
struct Map {
|
struct Map {
|
||||||
size_t w, h; // overall map dimensions
|
size_t w, h; // overall map dimensions
|
||||||
Map();
|
Map();
|
||||||
int get(const size_t i, const size_t j);
|
int get(const size_t i, const size_t j) const;
|
||||||
bool is_empty(const size_t i, const size_t j);
|
bool is_empty(const size_t i, const size_t j) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAP_H
|
#endif // MAP_H
|
||||||
|
@@ -45,12 +45,12 @@ Texture::Texture(const std::string filename) : img_w(0), img_h(0), count(0), siz
|
|||||||
stbi_image_free(pixmap);
|
stbi_image_free(pixmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t &Texture::get(const size_t i, const size_t j, const size_t idx) {
|
uint32_t Texture::get(const size_t i, const size_t j, const size_t idx) const {
|
||||||
assert(i<size && j<size && idx<count);
|
assert(i<size && j<size && idx<count);
|
||||||
return img[i+idx*size+j*img_w];
|
return img[i+idx*size+j*img_w];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint32_t> Texture::get_scaled_column(const size_t texture_id, const size_t tex_coord, const size_t column_height) {
|
std::vector<uint32_t> Texture::get_scaled_column(const size_t texture_id, const size_t tex_coord, const size_t column_height) const {
|
||||||
assert(tex_coord<size && texture_id<count);
|
assert(tex_coord<size && texture_id<count);
|
||||||
std::vector<uint32_t> column(column_height);
|
std::vector<uint32_t> column(column_height);
|
||||||
for (size_t y=0; y<column_height; y++) {
|
for (size_t y=0; y<column_height; y++) {
|
||||||
|
@@ -7,8 +7,8 @@ struct Texture {
|
|||||||
std::vector<uint32_t> img; // textures storage container
|
std::vector<uint32_t> img; // textures storage container
|
||||||
|
|
||||||
Texture(const std::string filename);
|
Texture(const std::string filename);
|
||||||
uint32_t &get(const size_t i, const size_t j, const size_t idx); // get the pixel (i,j) from the texture idx
|
uint32_t get(const size_t i, const size_t j, const size_t idx) const; // get the pixel (i,j) from the texture idx
|
||||||
std::vector<uint32_t> get_scaled_column(const size_t texture_id, const size_t tex_coord, const size_t column_height); // retrieve one column (tex_coord) from the texture texture_id and scale it to the destination size
|
std::vector<uint32_t> get_scaled_column(const size_t texture_id, const size_t tex_coord, const size_t column_height) const; // retrieve one column (tex_coord) from the texture texture_id and scale it to the destination size
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TEXTURES_H
|
#endif // TEXTURES_H
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
#define _USE_MATH_DEFINES
|
#define _USE_MATH_DEFINES
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
@@ -14,8 +13,9 @@
|
|||||||
#include "sprite.h"
|
#include "sprite.h"
|
||||||
#include "framebuffer.h"
|
#include "framebuffer.h"
|
||||||
#include "textures.h"
|
#include "textures.h"
|
||||||
|
#include "tinyraycaster.h"
|
||||||
|
|
||||||
int wall_x_texcoord(const float hitx, const float hity, Texture &tex_walls) {
|
int wall_x_texcoord(const float hitx, const float hity, const Texture &tex_walls) {
|
||||||
float x = hitx - floor(hitx+.5); // x and y contain (signed) fractional parts of hitx and hity,
|
float x = hitx - floor(hitx+.5); // x and y contain (signed) fractional parts of hitx and hity,
|
||||||
float y = hity - floor(hity+.5); // they vary between -0.5 and +0.5, and one of them is supposed to be very close to 0
|
float y = hity - floor(hity+.5); // they vary between -0.5 and +0.5, and one of them is supposed to be very close to 0
|
||||||
int tex = x*tex_walls.size;
|
int tex = x*tex_walls.size;
|
||||||
@@ -27,13 +27,23 @@ int wall_x_texcoord(const float hitx, const float hity, Texture &tex_walls) {
|
|||||||
return tex;
|
return tex;
|
||||||
}
|
}
|
||||||
|
|
||||||
void map_show_sprite(Sprite &sprite, FrameBuffer &fb, Map &map) {
|
void draw_map(FrameBuffer &fb, const std::vector<Sprite> &sprites, const Texture &tex_walls, const Map &map, const size_t cell_w, const size_t cell_h) {
|
||||||
const size_t rect_w = fb.w/(map.w*2); // size of one map cell on the screen
|
for (size_t j=0; j<map.h; j++) { // draw the map itself
|
||||||
const size_t rect_h = fb.h/map.h;
|
for (size_t i=0; i<map.w; i++) {
|
||||||
fb.draw_rectangle(sprite.x*rect_w-3, sprite.y*rect_h-3, 6, 6, pack_color(255, 0, 0));
|
if (map.is_empty(i, j)) continue; // skip empty spaces
|
||||||
|
size_t rect_x = i*cell_w;
|
||||||
|
size_t rect_y = j*cell_h;
|
||||||
|
size_t texid = map.get(i, j);
|
||||||
|
assert(texid<tex_walls.count);
|
||||||
|
fb.draw_rectangle(rect_x, rect_y, cell_w, cell_h, tex_walls.get(0, 0, texid)); // the color is taken from the upper left pixel of the texture #texid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (size_t i=0; i<sprites.size(); i++) { // show the monsters
|
||||||
|
fb.draw_rectangle(sprites[i].x*cell_w-3, sprites[i].y*cell_h-3, 6, 6, pack_color(255, 0, 0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw_sprite(Sprite &sprite, std::vector<float> &depth_buffer, FrameBuffer &fb, Player &player, Texture &tex_sprites) {
|
void draw_sprite(FrameBuffer &fb, const Sprite &sprite, const std::vector<float> &depth_buffer, const Player &player, const Texture &tex_sprites) {
|
||||||
// absolute direction from the player to the sprite (in radians)
|
// absolute direction from the player to the sprite (in radians)
|
||||||
float sprite_dir = atan2(sprite.y - player.y, sprite.x - player.x);
|
float sprite_dir = atan2(sprite.y - player.y, sprite.x - player.x);
|
||||||
while (sprite_dir - player.a > M_PI) sprite_dir -= 2*M_PI; // remove unncesessary periods from the relative direction
|
while (sprite_dir - player.a > M_PI) sprite_dir -= 2*M_PI; // remove unncesessary periods from the relative direction
|
||||||
@@ -57,35 +67,31 @@ void draw_sprite(Sprite &sprite, std::vector<float> &depth_buffer, FrameBuffer &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void render(FrameBuffer &fb, Map &map, Player &player, std::vector<Sprite> &sprites, Texture &tex_walls, Texture &tex_monst) {
|
void render(FrameBuffer &fb, const GameState &gs) {
|
||||||
|
const Map &map = gs.map;
|
||||||
|
const Player &player = gs.player;
|
||||||
|
const std::vector<Sprite> &sprites = gs.monsters;
|
||||||
|
const Texture &tex_walls = gs.tex_walls;
|
||||||
|
const Texture &tex_monst = gs.tex_monst;
|
||||||
|
|
||||||
fb.clear(pack_color(255, 255, 255)); // clear the screen
|
fb.clear(pack_color(255, 255, 255)); // clear the screen
|
||||||
|
|
||||||
const size_t rect_w = fb.w/(map.w*2); // size of one map cell on the screen
|
const size_t cell_w = fb.w/(map.w*2); // size of one map cell on the screen
|
||||||
const size_t rect_h = fb.h/map.h;
|
const size_t cell_h = fb.h/map.h;
|
||||||
for (size_t j=0; j<map.h; j++) { // draw the map
|
|
||||||
for (size_t i=0; i<map.w; i++) {
|
|
||||||
if (map.is_empty(i, j)) continue; // skip empty spaces
|
|
||||||
size_t rect_x = i*rect_w;
|
|
||||||
size_t rect_y = j*rect_h;
|
|
||||||
size_t texid = map.get(i, j);
|
|
||||||
assert(texid<tex_walls.count);
|
|
||||||
fb.draw_rectangle(rect_x, rect_y, rect_w, rect_h, tex_walls.get(0, 0, texid)); // the color is taken from the upper left pixel of the texture #texid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<float> depth_buffer(fb.w/2, 1e3);
|
std::vector<float> depth_buffer(fb.w/2, 1e3);
|
||||||
|
|
||||||
for (size_t i=0; i<fb.w/2; i++) { // draw the visibility cone AND the "3D" view
|
for (size_t i=0; i<fb.w/2; i++) { // draw the visibility cone AND the "3D" view
|
||||||
float angle = player.a-player.fov/2 + player.fov*i/float(fb.w/2);
|
float angle = player.a-player.fov/2 + player.fov*i/float(fb.w/2);
|
||||||
for (float t=0; t<20; t+=.01) { // ray marching loop
|
for (float t=0; t<20; t+=.01) { // ray marching loop
|
||||||
float x = player.x + t*cos(angle);
|
float x = player.x + t*cos(angle);
|
||||||
float y = player.y + t*sin(angle);
|
float y = player.y + t*sin(angle);
|
||||||
fb.set_pixel(x*rect_w, y*rect_h, pack_color(160, 160, 160)); // this draws the visibility cone
|
fb.set_pixel(x*cell_w, y*cell_h, pack_color(190, 190, 190)); // this draws the visibility cone
|
||||||
|
|
||||||
if (map.is_empty(x, y)) continue;
|
if (map.is_empty(x, y)) continue;
|
||||||
|
|
||||||
size_t texid = map.get(x, y); // our ray touches a wall, so draw the vertical column to create an illusion of 3D
|
size_t texid = map.get(x, y); // our ray touches a wall, so draw the vertical column to create an illusion of 3D
|
||||||
assert(texid<tex_walls.count);
|
assert(texid<tex_walls.count);
|
||||||
float dist = .2+t*cos(angle-player.a);
|
float dist = t*cos(angle-player.a);
|
||||||
depth_buffer[i] = dist;
|
depth_buffer[i] = dist;
|
||||||
size_t column_height = fb.h/dist;
|
size_t column_height = fb.h/dist;
|
||||||
int x_texcoord = wall_x_texcoord(x, y, tex_walls);
|
int x_texcoord = wall_x_texcoord(x, y, tex_walls);
|
||||||
@@ -101,21 +107,10 @@ void render(FrameBuffer &fb, Map &map, Player &player, std::vector<Sprite> &spri
|
|||||||
} // ray marching loop
|
} // ray marching loop
|
||||||
} // field of view ray sweeping
|
} // field of view ray sweeping
|
||||||
|
|
||||||
for (size_t i=0; i<sprites.size(); i++) { // update the distances from the player to each sprite
|
draw_map(fb, sprites, tex_walls, map, cell_w, cell_h);
|
||||||
sprites[i].player_dist = std::sqrt(pow(player.x - sprites[i].x, 2) + pow(player.y - sprites[i].y, 2));
|
|
||||||
}
|
|
||||||
std::sort(sprites.begin(), sprites.end()); // sort it from farthest to closest
|
|
||||||
|
|
||||||
for (size_t i=0; i<sprites.size(); i++) { // draw the sprites
|
for (size_t i=0; i<sprites.size(); i++) { // draw the sprites
|
||||||
map_show_sprite(sprites[i], fb, map);
|
draw_sprite(fb, sprites[i], depth_buffer, player, tex_monst);
|
||||||
draw_sprite(sprites[i], depth_buffer, fb, player, tex_monst);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
int main() {
|
|
||||||
drop_ppm_image("./out.ppm", fb.img, fb.w, fb.h);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
@@ -1,7 +1,15 @@
|
|||||||
#ifndef TINYRAYCASTER_H
|
#ifndef TINYRAYCASTER_H
|
||||||
#define TINYRAYCASTER_H
|
#define TINYRAYCASTER_H
|
||||||
|
|
||||||
void render(FrameBuffer &fb, Map &map, Player &player, std::vector<Sprite> &sprites, Texture &tex_walls, Texture &tex_monst);
|
struct GameState {
|
||||||
|
Map map;
|
||||||
|
Player player;
|
||||||
|
std::vector<Sprite> monsters;
|
||||||
|
Texture tex_walls;
|
||||||
|
Texture tex_monst;
|
||||||
|
};
|
||||||
|
|
||||||
|
void render(FrameBuffer &fb, const GameState &gs);
|
||||||
|
|
||||||
#endif // TINYRAYCASTER_H
|
#endif // TINYRAYCASTER_H
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user