|
|
|
@ -7,40 +7,32 @@ using std::make_unique; |
|
|
|
|
#define gray_color(c) rgba_color(c, c, c, 255) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline size_t pixcoord(int x, int y) { |
|
|
|
|
return ((y) * RAY_VIEW_WIDTH) + (x); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void TexturePack::load_textures() { |
|
|
|
|
// ZED: this needs to determine sprite vs. other textures
|
|
|
|
|
// so it can put the texture into the sprite rec immediately
|
|
|
|
|
// or....maybe just use SFML's sprite stuff?
|
|
|
|
|
for(int i = 0; i < NUM_TEXTURES; i++) { |
|
|
|
|
texture[i].resize(TEXTURE_WIDTH * TEXTURE_HEIGHT); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
load_image(texture[0], "assets/tile16.png"); |
|
|
|
|
load_image(texture[1], "assets/tile02.png"); |
|
|
|
|
load_image(texture[2], "assets/tile03.png"); |
|
|
|
|
load_image(texture[3], "assets/tile32.png"); |
|
|
|
|
load_image(texture[4], "assets/tile05.png"); |
|
|
|
|
load_image(texture[5], "assets/tile17.png"); |
|
|
|
|
load_image(texture[6], "assets/tile10.png"); |
|
|
|
|
load_image(texture[7], "assets/tile01.png"); |
|
|
|
|
load_image(texture[8], "assets/portal.png"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::vector<uint32_t>& TexturePack::get(size_t num) { |
|
|
|
|
return texture[num]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void TexturePack::load_image(std::vector<uint32_t>& texture, const char *filename) { |
|
|
|
|
std::vector<uint32_t> TexturePack::load_image(const char *filename) { |
|
|
|
|
std::vector<uint32_t> texture(TEXTURE_WIDTH * TEXTURE_HEIGHT); |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void TexturePack::load_textures() { |
|
|
|
|
images.emplace_back(load_image("assets/tile16.png")); |
|
|
|
|
images.emplace_back(load_image("assets/tile02.png")); |
|
|
|
|
images.emplace_back(load_image("assets/tile03.png")); |
|
|
|
|
images.emplace_back(load_image("assets/tile32.png")); |
|
|
|
|
images.emplace_back(load_image("assets/tile05.png")); |
|
|
|
|
images.emplace_back(load_image("assets/tile17.png")); |
|
|
|
|
images.emplace_back(load_image("assets/tile10.png")); |
|
|
|
|
images.emplace_back(load_image("assets/tile01.png")); |
|
|
|
|
images.emplace_back(load_image("assets/portal.png")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::vector<uint32_t>& TexturePack::get(size_t num) { |
|
|
|
|
return images[num]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Sprite &TexturePack::get_sprite(size_t sprite_num) { |
|
|
|
@ -49,18 +41,26 @@ Sprite &TexturePack::get_sprite(size_t sprite_num) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map) : |
|
|
|
|
Raycaster::Raycaster(sf::RenderWindow& window, Matrix &map, int width, int height) : |
|
|
|
|
$width(width), $height(height), |
|
|
|
|
$window(window), |
|
|
|
|
$map(map) |
|
|
|
|
$map(map), |
|
|
|
|
spriteOrder(textures.NUM_SPRITES), |
|
|
|
|
spriteDistance(textures.NUM_SPRITES), |
|
|
|
|
ZBuffer(width) |
|
|
|
|
{ |
|
|
|
|
$window.setVerticalSyncEnabled(true); |
|
|
|
|
view_texture.create(RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT); |
|
|
|
|
view_texture.create($width, $height); |
|
|
|
|
view_sprite.setTexture(view_texture); |
|
|
|
|
view_sprite.setPosition(RAY_VIEW_X, 0); |
|
|
|
|
pixels = make_unique<RGBA[]>(RAY_VIEW_WIDTH * RAY_VIEW_HEIGHT); |
|
|
|
|
view_sprite.setPosition(0, 0); |
|
|
|
|
pixels = make_unique<RGBA[]>($width * $height); |
|
|
|
|
textures.load_textures(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Raycaster::set_position(int x, int y) { |
|
|
|
|
view_sprite.setPosition(x, y); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Raycaster::position_camera(float player_x, float player_y) { |
|
|
|
|
// x and y start position
|
|
|
|
|
posX = player_x; |
|
|
|
@ -68,19 +68,22 @@ void Raycaster::position_camera(float player_x, float player_y) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Raycaster::draw_pixel_buffer() { |
|
|
|
|
view_texture.update((uint8_t *)pixels.get(), RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT, 0, 0); |
|
|
|
|
view_texture.update((uint8_t *)pixels.get(), $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(), RAY_VIEW_WIDTH * RAY_VIEW_HEIGHT, 0); |
|
|
|
|
std::fill_n(pixels.get(), $width * $height, 0); |
|
|
|
|
$window.clear(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Raycaster::sprite_casting() { |
|
|
|
|
const int textureWidth = textures.TEXTURE_WIDTH; |
|
|
|
|
const int textureHeight = textures.TEXTURE_HEIGHT; |
|
|
|
|
|
|
|
|
|
// sort sprites from far to close
|
|
|
|
|
for(int i = 0; i < NUM_SPRITES; i++) { |
|
|
|
|
for(int i = 0; i < textures.NUM_SPRITES; i++) { |
|
|
|
|
spriteOrder[i] = i; |
|
|
|
|
// this is just the distance calculation
|
|
|
|
|
spriteDistance[i] = ((posX - textures.SPRITE[i].x) * |
|
|
|
@ -89,10 +92,10 @@ void Raycaster::sprite_casting() { |
|
|
|
|
(posY - textures.SPRITE[i].y)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sort_sprites(spriteOrder, spriteDistance, NUM_SPRITES); |
|
|
|
|
sort_sprites(spriteOrder, spriteDistance, textures.NUM_SPRITES); |
|
|
|
|
|
|
|
|
|
// after sorting the sprites, do the projection
|
|
|
|
|
for(int i = 0; i < NUM_SPRITES; i++) { |
|
|
|
|
for(int i = 0; i < textures.NUM_SPRITES; i++) { |
|
|
|
|
int sprite_index = spriteOrder[i]; |
|
|
|
|
Sprite& sprite_rec = textures.get_sprite(sprite_index); |
|
|
|
|
double spriteX = sprite_rec.x - posX; |
|
|
|
@ -135,7 +138,7 @@ void Raycaster::sprite_casting() { |
|
|
|
|
|
|
|
|
|
//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)) * TEXTURE_WIDTH / spriteWidth) / 256; |
|
|
|
|
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
|
|
|
|
@ -143,10 +146,10 @@ void Raycaster::sprite_casting() { |
|
|
|
|
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 * TEXTURE_HEIGHT) / spriteHeight) / 256; |
|
|
|
|
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[TEXTURE_WIDTH * texY + texX]; |
|
|
|
|
uint32_t color = sprite_texture[textureWidth * texY + texX]; |
|
|
|
|
// poor person's transparency, get current color from the texture
|
|
|
|
|
if((color & 0x00FFFFFF) != 0) { |
|
|
|
|
RGBA pixel = color; |
|
|
|
@ -243,21 +246,21 @@ void Raycaster::cast_rays() { |
|
|
|
|
wallX -= floor((wallX)); |
|
|
|
|
|
|
|
|
|
// x coorindate on the texture
|
|
|
|
|
int texX = int(wallX * double(TEXTURE_WIDTH)); |
|
|
|
|
if(side == 0 && rayDirX > 0) texX = TEXTURE_WIDTH - texX - 1; |
|
|
|
|
if(side == 1 && rayDirY < 0) texX = TEXTURE_WIDTH - texX - 1; |
|
|
|
|
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 * TEXTURE_HEIGHT / lineHeight; |
|
|
|
|
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 & (TEXTURE_HEIGHT - 1); |
|
|
|
|
int texY = (int)texPos & (textures.TEXTURE_HEIGHT - 1); |
|
|
|
|
texPos += step; |
|
|
|
|
RGBA pixel = texture[TEXTURE_HEIGHT * texY + texX]; |
|
|
|
|
RGBA pixel = texture[textures.TEXTURE_HEIGHT * texY + texX]; |
|
|
|
|
pixels[pixcoord(x, y)] = pixel; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -267,12 +270,13 @@ void Raycaster::cast_rays() { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Raycaster::draw_ceiling_floor() { |
|
|
|
|
int screenHeight = RAY_VIEW_HEIGHT; |
|
|
|
|
int screenWidth = RAY_VIEW_WIDTH; |
|
|
|
|
const int textureWidth = textures.TEXTURE_WIDTH; |
|
|
|
|
const int textureHeight = textures.TEXTURE_HEIGHT; |
|
|
|
|
|
|
|
|
|
auto& floorTexture = textures.get(textures.floor); |
|
|
|
|
auto& ceilingTexture = textures.get(textures.ceiling); |
|
|
|
|
|
|
|
|
|
for(int y = screenHeight / 2 + 1; y < screenHeight; ++y) { |
|
|
|
|
for(int y = $height / 2 + 1; y < $height; ++y) { |
|
|
|
|
// rayDir for leftmost ray (x=0) and rightmost (x = w)
|
|
|
|
|
float rayDirX0 = dirX - planeX; |
|
|
|
|
float rayDirY0 = dirY - planeY; |
|
|
|
@ -280,13 +284,13 @@ void Raycaster::draw_ceiling_floor() { |
|
|
|
|
float rayDirY1 = dirY + planeY; |
|
|
|
|
|
|
|
|
|
// current y position compared to the horizon
|
|
|
|
|
int p = y - screenHeight / 2; |
|
|
|
|
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 * screenHeight; |
|
|
|
|
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
|
|
|
|
@ -295,8 +299,8 @@ void Raycaster::draw_ceiling_floor() { |
|
|
|
|
|
|
|
|
|
// 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) / screenWidth; |
|
|
|
|
float floorStepY = rowDistance * (rayDirY1 - rayDirY0) / screenWidth; |
|
|
|
|
float floorStepX = rowDistance * (rayDirX1 - rayDirX0) / $width; |
|
|
|
|
float floorStepY = rowDistance * (rayDirY1 - rayDirY0) / $width; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// real world coordinates of the leftmost column.
|
|
|
|
@ -304,15 +308,15 @@ void Raycaster::draw_ceiling_floor() { |
|
|
|
|
float floorX = posX + rowDistance * rayDirX0; |
|
|
|
|
float floorY = posY + rowDistance * rayDirY0; |
|
|
|
|
|
|
|
|
|
for(int x = 0; x < screenWidth; ++x) { |
|
|
|
|
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(TEXTURE_WIDTH * (floorX - cellX)) & (TEXTURE_WIDTH - 1); |
|
|
|
|
int ty = int(TEXTURE_WIDTH * (floorY - cellY)) & (TEXTURE_HEIGHT - 1); |
|
|
|
|
int tx = int(textureWidth * (floorX - cellX)) & (textureWidth - 1); |
|
|
|
|
int ty = int(textureWidth * (floorY - cellY)) & (textureHeight - 1); |
|
|
|
|
|
|
|
|
|
floorX += floorStepX; |
|
|
|
|
floorY += floorStepY; |
|
|
|
@ -323,12 +327,12 @@ void Raycaster::draw_ceiling_floor() { |
|
|
|
|
// floorX cellX to find the texture x/y. How?
|
|
|
|
|
|
|
|
|
|
// FLOOR
|
|
|
|
|
color = floorTexture[TEXTURE_WIDTH * ty + tx]; |
|
|
|
|
color = floorTexture[textureWidth * ty + tx]; |
|
|
|
|
pixels[pixcoord(x, y)] = color; |
|
|
|
|
|
|
|
|
|
// CEILING
|
|
|
|
|
color = ceilingTexture[TEXTURE_WIDTH * ty + tx]; |
|
|
|
|
pixels[pixcoord(x, screenHeight - y - 1)] = color; |
|
|
|
|
color = ceilingTexture[textureWidth * ty + tx]; |
|
|
|
|
pixels[pixcoord(x, $height - y - 1)] = color; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -350,7 +354,7 @@ bool Raycaster::empty_space(int new_x, int new_y) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Raycaster::sort_sprites(int* order, double* dist, int amount) |
|
|
|
|
void Raycaster::sort_sprites(std::vector<int>& order, std::vector<double>& dist, int amount) |
|
|
|
|
{ |
|
|
|
|
std::vector<std::pair<double, int>> sprites(amount); |
|
|
|
|
|
|
|
|
|