Sprite rendering cleanup started.

master
Zed A. Shaw 2 months ago
parent 033d5cdfec
commit c9d4b7ed1e
  1. 179
      raycaster.cpp
  2. 10
      raycaster.hpp

@ -6,9 +6,6 @@ using std::make_unique;
#define rgba_color(r,g,b,a) (r<<(0*8))|(g<<(1*8))|(b<<(2*8))|(a<<(3*8))
#define gray_color(c) rgba_color(c, c, c, 255)
//parameters for scaling and moving the sprites
#define uDiv 1
#define vDiv 1
inline size_t pixcoord(int x, int y) {
return ((y) * RAY_VIEW_WIDTH) + (x);
@ -81,15 +78,93 @@ void Raycaster::clear() {
$window.clear();
}
void Raycaster::sprite_casting() {
// sort sprites from far to close
for(int i = 0; i < NUM_SPRITES; i++) {
spriteOrder[i] = i;
// this is just the distance calculation
spriteDistance[i] = ((posX - textures.SPRITE[i].x) *
(posX - textures.SPRITE[i].x) +
(posY - textures.SPRITE[i].y) *
(posY - textures.SPRITE[i].y));
}
sort_sprites(spriteOrder, spriteDistance, NUM_SPRITES);
// after sorting the sprites, do the projection
for(int i = 0; i < NUM_SPRITES; i++) {
int sprite_index = spriteOrder[i];
Sprite& sprite_rec = textures.get_sprite(sprite_index);
double spriteX = sprite_rec.x - posX;
double spriteY = sprite_rec.y - posY;
auto& sprite_texture = textures.get(sprite_rec.texture);
//transform sprite with the inverse camera matrix
// [ planeX dirX ] -1 [ dirY -dirX ]
// [ ] = 1/(planeX*dirY-dirX*planeY) * [ ]
// [ planeY dirY ] [ -planeY planeX ]
double invDet = 1.0 / (planeX * dirY - dirX * planeY); // required for correct matrix multiplication
double transformX = invDet * (dirY * spriteX - dirX * spriteY);
//this is actually the depth inside the screen, that what Z is in 3D, the distance of sprite to player, matching sqrt(spriteDistance[i])
double transformY = invDet * (-planeY * spriteX + planeX * spriteY);
int spriteScreenX = int(($width / 2) * (1 + transformX / transformY));
int vMoveScreen = int(sprite_rec.elevation * -1 / transformY);
// calculate the height of the sprite on screen
//using "transformY" instead of the real distance prevents fisheye
int spriteHeight = abs(int($height / transformY)) / sprite_rec.vDiv;
//calculate lowest and highest pixel to fill in current stripe
int drawStartY = -spriteHeight / 2 + $height / 2 + vMoveScreen;
if(drawStartY < 0) drawStartY = 0;
int drawEndY = spriteHeight / 2 + $height / 2 + vMoveScreen;
if(drawEndY >= $height) drawEndY = $height - 1;
// calculate width the the sprite
// same as height of sprite, given that it's square
int spriteWidth = abs(int($height / transformY)) / sprite_rec.uDiv;
int drawStartX = -spriteWidth / 2 + spriteScreenX;
if(drawStartX < 0) drawStartX = 0;
int drawEndX = spriteWidth / 2 + spriteScreenX;
if(drawEndX > $width) drawEndX = $width;
//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;
// 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
if(transformY > 0 && transformY < ZBuffer[stripe]) {
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;
//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];
// poor person's transparency, get current color from the texture
if((color & 0x00FFFFFF) != 0) {
RGBA pixel = color;
pixels[pixcoord(stripe, y)] = pixel;
}
}
}
}
}
}
void Raycaster::cast_rays() {
int w = RAY_VIEW_WIDTH;
int h = RAY_VIEW_HEIGHT;
double perpWallDist;
// WALL CASTING
for(int x = 0; x < w; x++) {
for(int x = 0; x < $width; x++) {
// calculate ray position and direction
double cameraX = 2 * x / double(w) - 1; // x-coord in camera space
double cameraX = 2 * x / double($width) - 1; // x-coord in camera space
double rayDirX = dirX + planeX * cameraX;
double rayDirY = dirY + planeY * cameraX;
@ -148,13 +223,13 @@ void Raycaster::cast_rays() {
perpWallDist = (sideDistY - deltaDistY);
}
int lineHeight = int(h / perpWallDist);
int lineHeight = int($height / perpWallDist);
int drawStart = -lineHeight / 2 + h / 2 + PITCH;
int drawStart = -lineHeight / 2 + $height / 2 + PITCH;
if(drawStart < 0) drawStart = 0;
int drawEnd = lineHeight / 2 + h / 2 + PITCH;
if(drawEnd >= h) drawEnd = h - 1;
int drawEnd = lineHeight / 2 + $height / 2 + PITCH;
if(drawEnd >= $height) drawEnd = $height - 1;
auto &texture = textures.get($map[mapY][mapX] - 1);
@ -177,7 +252,7 @@ void Raycaster::cast_rays() {
// How much to increase the texture coordinate per screen pixel
double step = 1.0 * TEXTURE_HEIGHT / lineHeight;
// Starting texture coordinate
double texPos = (drawStart - PITCH - h / 2 + lineHeight / 2) * step;
double texPos = (drawStart - PITCH - $height / 2 + lineHeight / 2) * step;
for(int y = drawStart; y < drawEnd; y++) {
int texY = (int)texPos & (TEXTURE_HEIGHT - 1);
@ -189,85 +264,6 @@ void Raycaster::cast_rays() {
// SET THE ZBUFFER FOR THE SPRITE CASTING
ZBuffer[x] = perpWallDist;
}
// SPRITE CASTING
// sort sprites from far to close
for(int i = 0; i < NUM_SPRITES; i++) {
spriteOrder[i] = i;
// this is just the distance calculation
spriteDistance[i] = ((posX - textures.SPRITE[i].x) *
(posX - textures.SPRITE[i].x) +
(posY - textures.SPRITE[i].y) *
(posY - textures.SPRITE[i].y));
}
sort_sprites(spriteOrder, spriteDistance, NUM_SPRITES);
// after sorting the sprites, do the projection
for(int i = 0; i < NUM_SPRITES; i++) {
int sprite_index = spriteOrder[i];
Sprite& sprite_rec = textures.get_sprite(sprite_index);
double spriteX = sprite_rec.x - posX;
double spriteY = sprite_rec.y - posY;
auto& sprite_texture = textures.get(sprite_rec.texture);
//transform sprite with the inverse camera matrix
// [ planeX dirX ] -1 [ dirY -dirX ]
// [ ] = 1/(planeX*dirY-dirX*planeY) * [ ]
// [ planeY dirY ] [ -planeY planeX ]
double invDet = 1.0 / (planeX * dirY - dirX * planeY); // required for correct matrix multiplication
double transformX = invDet * (dirY * spriteX - dirX * spriteY);
//this is actually the depth inside the screen, that what Z is in 3D, the distance of sprite to player, matching sqrt(spriteDistance[i])
double transformY = invDet * (-planeY * spriteX + planeX * spriteY);
int spriteScreenX = int((w / 2) * (1 + transformX / transformY));
int vMoveScreen = int(sprite_rec.elevation * -1 / transformY);
// calculate the height of the sprite on screen
//using "transformY" instead of the real distance prevents fisheye
int spriteHeight = abs(int(h / transformY)) / vDiv;
//calculate lowest and highest pixel to fill in current stripe
int drawStartY = -spriteHeight / 2 + h / 2 + vMoveScreen;
if(drawStartY < 0) drawStartY = 0;
int drawEndY = spriteHeight / 2 + h / 2 + vMoveScreen;
if(drawEndY >= h) drawEndY = h - 1;
// calculate width the the sprite
// same as height of sprite, given that it's square
int spriteWidth = abs(int(h / transformY)) / uDiv;
int drawStartX = -spriteWidth / 2 + spriteScreenX;
if(drawStartX < 0) drawStartX = 0;
int drawEndX = spriteWidth / 2 + spriteScreenX;
if(drawEndX > w) drawEndX = w;
//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;
// 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
if(transformY > 0 && transformY < ZBuffer[stripe]) {
for(int y = drawStartY; y < drawEndY; y++) {
//256 and 128 factors to avoid floats
int d = (y - vMoveScreen) * 256 - h * 128 + spriteHeight * 128;
int texY = ((d * TEXTURE_HEIGHT) / 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];
// poor person's transparency, get current color from the texture
if((color & 0x00FFFFFF) != 0) {
RGBA pixel = color;
pixels[pixcoord(stripe, y)] = pixel;
}
}
}
}
}
}
void Raycaster::draw_ceiling_floor() {
@ -340,6 +336,7 @@ void Raycaster::draw_ceiling_floor() {
void Raycaster::render() {
draw_ceiling_floor();
cast_rays();
sprite_casting();
draw_pixel_buffer();
}

@ -23,8 +23,11 @@ using matrix::Matrix;
struct Sprite {
double x;
double y;
double elevation;
int texture;
// ZED: this should be a separate transform parameter
double elevation=0;
int uDiv=1;
int vDiv=1;
};
#define RAY_VIEW_WIDTH 960
@ -36,7 +39,7 @@ using RGBA = uint32_t;
struct TexturePack {
std::vector<uint32_t> texture[NUM_TEXTURES];
std::vector<Sprite> SPRITE{{4.0, 3.55, 0, 8}};
std::vector<Sprite> SPRITE{{4.0, 3.55, 8}};
const int floor = 3;
const int ceiling = 6;
@ -47,6 +50,8 @@ struct TexturePack {
};
struct Raycaster {
int $width=RAY_VIEW_WIDTH;
int $height=RAY_VIEW_HEIGHT;
TexturePack textures;
double posX = 0;
double posY = 0;
@ -80,6 +85,7 @@ struct Raycaster {
void draw_pixel_buffer();
void clear();
void cast_rays();
void sprite_casting();
void draw_ceiling_floor();
void render();
bool empty_space(int new_x, int new_y);

Loading…
Cancel
Save