Reputation: 109
I am making a 2.3D environment with raycasting for the walls and scanline for the floors. Here is the function I am using to draw the floors and ceilings.
void cast_floor_and_ceiling(t_game *game, t_floor_ceiling *f, t_rendering_threads *thread)
{
int px, py, cell_x, cell_y;
int floor_tx, floor_ty, ceiling_tx, ceiling_ty;
float floor_x, floor_y, ceiling_x, ceiling_y;
float step_x, step_y, row_distance, pos_z;
int y, x, p;
Uint32 ceiling_color, floor_color;
// Set horizon position and texture pointers
f->horizon = (WIND_HEIGHT / 2) + CAM_SHIFT;
f->ceiling_pixels = game->textures.ceiling.pixels;
f->floor_pixels = game->textures.floor.pixels;
// Calculate ray directions for floor and ceiling casting
f->ray_dir_x_0 = PLAYER_DIR_X - PLAYER_CAM_X;
f->ray_dir_y_0 = PLAYER_DIR_Y - PLAYER_CAM_Y;
f->ray_dir_x_1 = PLAYER_DIR_X + PLAYER_CAM_X;
f->ray_dir_y_1 = PLAYER_DIR_Y + PLAYER_CAM_Y;
// Render Ceiling
pos_z = 0.5 * WIND_HEIGHT - (PLAYER_HEIGHT / 2);
for (y = 0; y < f->horizon; y++)
{
// Distance from the player to the current row
p = (WIND_HEIGHT / 2) - y + CAM_SHIFT;
row_distance = (pos_z / p) * 2;
// Calculate step size for texture sampling
step_x = row_distance * (f->ray_dir_x_1 - f->ray_dir_x_0) / WIND_WIDTH;
step_y = row_distance * (f->ray_dir_y_1 - f->ray_dir_y_0) / WIND_WIDTH;
// Compute the starting position for ceiling drawing
ceiling_x = PLAYER_X + row_distance * f->ray_dir_x_0 + thread->start * step_x;
ceiling_y = PLAYER_Y + row_distance * f->ray_dir_y_0 + thread->start * step_y;
// Iterate through the row
for (x = thread->start; x < thread->end; x++)
{
cell_x = (int)ceiling_x;
cell_y = (int)ceiling_y;
// Check if the ceiling tile is valid
if (cell_x >= 0 && cell_y >= 0 && cell_x < MAP_WIDTH && cell_y < MAP_HEIGHT
&& ((MAPS[LEVEL][cell_y][cell_x] == EMPTY || MAPS[LEVEL][cell_y][cell_x] == TRIGGER)
|| IS_HALF_BLOCK_UP(MAPS[LEVEL][cell_y][cell_x])))
{
if (f->ceiling_pixels)
{
// Compute texture coordinates
ceiling_tx = ((int)(game->textures.ceiling.width * (ceiling_x - cell_x)))
& (game->textures.ceiling.width - 1);
ceiling_ty = ((int)(game->textures.ceiling.height * (ceiling_y - cell_y)))
& (game->textures.ceiling.height - 1);
// Fetch ceiling pixel color
ceiling_color = f->ceiling_pixels[game->textures.ceiling.width * ceiling_ty + ceiling_tx];
// Set pixel if it's not occluded
px = x;
py = y;
if (check_z_buffer(game, py * WIND_WIDTH + px, row_distance))
f->pixels[py * WIND_WIDTH + px] = ceiling_color;
}
}
ceiling_x += step_x;
ceiling_y += step_y;
}
}
// Render Floor
pos_z = 0.5 * WIND_HEIGHT + (PLAYER_HEIGHT / 2);
for (y = f->horizon; y < WIND_HEIGHT; y++)
{
// Distance from the player to the current row
p = y - (WIND_HEIGHT / 2) - CAM_SHIFT;
row_distance = (pos_z / p) * 2;
// Calculate step size for texture sampling
step_x = row_distance * (f->ray_dir_x_1 - f->ray_dir_x_0) / WIND_WIDTH;
step_y = row_distance * (f->ray_dir_y_1 - f->ray_dir_y_0) / WIND_WIDTH;
// Compute the starting position for floor drawing
floor_x = PLAYER_X + row_distance * f->ray_dir_x_0 + thread->start * step_x;
floor_y = PLAYER_Y + row_distance * f->ray_dir_y_0 + thread->start * step_y;
// Iterate through the row
for (x = thread->start; x < thread->end; x++)
{
cell_x = (int)floor_x;
cell_y = (int)floor_y;
// Check if the floor tile is valid
if (cell_x >= 0 && cell_y >= 0 && cell_x < MAP_WIDTH && cell_y < MAP_HEIGHT
&& ((MAPS[LEVEL][cell_y][cell_x] == EMPTY || MAPS[LEVEL][cell_y][cell_x] == TRIGGER)
|| IS_HALF_BLOCK_DOWN(MAPS[LEVEL][cell_y][cell_x])))
{
if (f->floor_pixels)
{
// Compute texture coordinates
floor_tx = ((int)(game->textures.floor.width * (floor_x - cell_x)))
& (game->textures.floor.width - 1);
floor_ty = ((int)(game->textures.floor.height * (floor_y - cell_y)))
& (game->textures.floor.height - 1);
// Fetch floor pixel color
floor_color = f->floor_pixels[game->textures.floor.width * floor_ty + floor_tx];
// Set pixel if it's not occluded
px = x;
py = y;
if (check_z_buffer(game, py * WIND_WIDTH + px, row_distance))
{
set_z_buffer(game, row_distance, py * WIND_WIDTH + px);
f->pixels[py * WIND_WIDTH + px] = floor_color;
}
}
}
floor_x += step_x;
floor_y += step_y;
}
}
}
As you can see, I have different floor and ceiling heights, and the only way I’ve found to adjust their height is by modifying pos_z (the camera height).
The problem is that pos_z is set outside the loop, and since nearly everything else depends on it, I haven’t found a way to adjust floor or ceiling heights dynamically within the loop.
Right now, I render all standard floors and ceilings using the function above. Then, I call the function below, which scans the entire screen again to find floor tiles with an offset height. This function runs for every floor height that should be visible based on the player's height. However, since ceilings require a similar approach, the whole screen ends up being scanned about five times per frame, leading to a massive FPS drop.
I managed to improve performance by splitting the rendering across multiple threads, but I still feel like my approach is highly inefficient.
Is there a way to merge all these functions into one, so that it's called only once per frame and can correctly calculate the height of every floor pixel without redundant calculations?
// Draws a floor tile based on the given type
static void draw_floor_tile(t_game *game, t_floor_ceiling *f,
t_rendering_threads *thread, char type)
{
int px, cell_x, cell_y, floor_tx, floor_ty;
Uint32 floor_color;
float floor_x, floor_y, step_x, step_y, row_distance, pos_z;
int y, x, p, row_start;
double height;
// Determine floor height based on tile type
height = (type == WALL_0) * 0.402
+ (type == WALL_1) * 0.202
+ (type == WALL_2) * 0.0
+ (type == WALL_3) * -0.200;
// Calculate perspective height and set pixel data
pos_z = height * WIND_HEIGHT + (PLAYER_HEIGHT / 2);
f->floor_pixels = game->textures.floor_light.pixels;
// Iterate through each row below the horizon
y = f->horizon - 1;
while (++y < WIND_HEIGHT)
{
// Calculate row distance based on perspective
p = y - (WIND_HEIGHT / 2) - CAM_SHIFT;
row_distance = (pos_z / p) * 2;
// Compute step increments for texture sampling
step_x = row_distance * (f->ray_dir_x_1 - f->ray_dir_x_0) / WIND_WIDTH;
step_y = row_distance * (f->ray_dir_y_1 - f->ray_dir_y_0) / WIND_WIDTH;
// Initialize floor coordinates
floor_x = PLAYER_X + row_distance * f->ray_dir_x_0 + thread->start * step_x;
floor_y = PLAYER_Y + row_distance * f->ray_dir_y_0 + thread->start * step_y;
row_start = y * WIND_WIDTH;
x = thread->start - 1;
// Process each column in the current row
while (++x < thread->end)
{
cell_x = (int)floor_x;
cell_y = (int)floor_y;
// Check if the current tile matches the target type
if (cell_x >= 0 && cell_y >= 0
&& cell_x < MAP_WIDTH && cell_y < MAP_HEIGHT
&& MAPS[LEVEL][cell_y][cell_x] == type)
{
// Get texture coordinates
floor_tx = ((int)(game->textures.floor_light.width * (floor_x - cell_x)))
& (game->textures.floor_light.width - 1);
floor_ty = ((int)(game->textures.floor_light.height * (floor_y - cell_y)))
& (game->textures.floor_light.height - 1);
// Retrieve floor texture color
floor_color = f->floor_pixels[game->textures.floor_light.width * floor_ty + floor_tx] | 0x010101;
px = x;
// Store pixel if it passes the depth test
if (check_z_buffer(game, row_start + px, row_distance))
f->pixels[row_start + px] = floor_color;
}
// Move to the next floor tile
floor_x += step_x;
floor_y += step_y;
}
}
}
Upvotes: 0
Views: 71