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 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…
Add table
Add a link
Reference in a new issue