Sprite rendering cleanup started.
This commit is contained in:
parent
033d5cdfec
commit
c9d4b7ed1e
2 changed files with 96 additions and 93 deletions
179
raycaster.cpp
179
raycaster.cpp
|
@ -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 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)
|
#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) {
|
inline size_t pixcoord(int x, int y) {
|
||||||
return ((y) * RAY_VIEW_WIDTH) + (x);
|
return ((y) * RAY_VIEW_WIDTH) + (x);
|
||||||
|
@ -81,15 +78,93 @@ void Raycaster::clear() {
|
||||||
$window.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() {
|
void Raycaster::cast_rays() {
|
||||||
int w = RAY_VIEW_WIDTH;
|
|
||||||
int h = RAY_VIEW_HEIGHT;
|
|
||||||
double perpWallDist;
|
double perpWallDist;
|
||||||
|
|
||||||
// WALL CASTING
|
// WALL CASTING
|
||||||
for(int x = 0; x < w; x++) {
|
for(int x = 0; x < $width; x++) {
|
||||||
// calculate ray position and direction
|
// 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 rayDirX = dirX + planeX * cameraX;
|
||||||
double rayDirY = dirY + planeY * cameraX;
|
double rayDirY = dirY + planeY * cameraX;
|
||||||
|
|
||||||
|
@ -148,13 +223,13 @@ void Raycaster::cast_rays() {
|
||||||
perpWallDist = (sideDistY - deltaDistY);
|
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;
|
if(drawStart < 0) drawStart = 0;
|
||||||
|
|
||||||
int drawEnd = lineHeight / 2 + h / 2 + PITCH;
|
int drawEnd = lineHeight / 2 + $height / 2 + PITCH;
|
||||||
if(drawEnd >= h) drawEnd = h - 1;
|
if(drawEnd >= $height) drawEnd = $height - 1;
|
||||||
|
|
||||||
auto &texture = textures.get($map[mapY][mapX] - 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
|
// How much to increase the texture coordinate per screen pixel
|
||||||
double step = 1.0 * TEXTURE_HEIGHT / lineHeight;
|
double step = 1.0 * TEXTURE_HEIGHT / lineHeight;
|
||||||
// Starting texture coordinate
|
// 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++) {
|
for(int y = drawStart; y < drawEnd; y++) {
|
||||||
int texY = (int)texPos & (TEXTURE_HEIGHT - 1);
|
int texY = (int)texPos & (TEXTURE_HEIGHT - 1);
|
||||||
|
@ -189,85 +264,6 @@ void Raycaster::cast_rays() {
|
||||||
// SET THE ZBUFFER FOR THE SPRITE CASTING
|
// SET THE ZBUFFER FOR THE SPRITE CASTING
|
||||||
ZBuffer[x] = perpWallDist;
|
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() {
|
void Raycaster::draw_ceiling_floor() {
|
||||||
|
@ -340,6 +336,7 @@ void Raycaster::draw_ceiling_floor() {
|
||||||
void Raycaster::render() {
|
void Raycaster::render() {
|
||||||
draw_ceiling_floor();
|
draw_ceiling_floor();
|
||||||
cast_rays();
|
cast_rays();
|
||||||
|
sprite_casting();
|
||||||
draw_pixel_buffer();
|
draw_pixel_buffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,11 @@ using matrix::Matrix;
|
||||||
struct Sprite {
|
struct Sprite {
|
||||||
double x;
|
double x;
|
||||||
double y;
|
double y;
|
||||||
double elevation;
|
|
||||||
int texture;
|
int texture;
|
||||||
|
// ZED: this should be a separate transform parameter
|
||||||
|
double elevation=0;
|
||||||
|
int uDiv=1;
|
||||||
|
int vDiv=1;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define RAY_VIEW_WIDTH 960
|
#define RAY_VIEW_WIDTH 960
|
||||||
|
@ -36,7 +39,7 @@ using RGBA = uint32_t;
|
||||||
|
|
||||||
struct TexturePack {
|
struct TexturePack {
|
||||||
std::vector<uint32_t> texture[NUM_TEXTURES];
|
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 floor = 3;
|
||||||
const int ceiling = 6;
|
const int ceiling = 6;
|
||||||
|
|
||||||
|
@ -47,6 +50,8 @@ struct TexturePack {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Raycaster {
|
struct Raycaster {
|
||||||
|
int $width=RAY_VIEW_WIDTH;
|
||||||
|
int $height=RAY_VIEW_HEIGHT;
|
||||||
TexturePack textures;
|
TexturePack textures;
|
||||||
double posX = 0;
|
double posX = 0;
|
||||||
double posY = 0;
|
double posY = 0;
|
||||||
|
@ -80,6 +85,7 @@ struct Raycaster {
|
||||||
void draw_pixel_buffer();
|
void draw_pixel_buffer();
|
||||||
void clear();
|
void clear();
|
||||||
void cast_rays();
|
void cast_rays();
|
||||||
|
void sprite_casting();
|
||||||
void draw_ceiling_floor();
|
void draw_ceiling_floor();
|
||||||
void render();
|
void render();
|
||||||
bool empty_space(int new_x, int new_y);
|
bool empty_space(int new_x, int new_y);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue