aboutsummaryrefslogtreecommitdiff
path: root/src/raycast.zig
diff options
context:
space:
mode:
authortslil clingman <>2021-09-12 00:17:24 -0400
committertslil clingman <>2021-09-12 00:17:24 -0400
commit027b973d7550ac2e72e5de1e2155e26b35f16353 (patch)
treec83de5e5ed38dc77e8c07e0f2561963cc7919e51 /src/raycast.zig
parent842d498f81e4733452e03f01d1c570ab5bb02771 (diff)
More efficient surface rendering!
Now, outside of sprites, every pixel on the screen is draw at most once. Hoorah. Unfortunately things still seem to be a little slow, perhaps its the z_buffer access or the fact that vertical floor slice drawing is bad?
Diffstat (limited to 'src/raycast.zig')
-rw-r--r--src/raycast.zig135
1 files changed, 65 insertions, 70 deletions
diff --git a/src/raycast.zig b/src/raycast.zig
index 1b78983..2b55f49 100644
--- a/src/raycast.zig
+++ b/src/raycast.zig
@@ -114,29 +114,29 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type {
window: RenderWindow,
walls_sprite: Sprite,
objects_sprite: Sprite,
- floors_image: Image,
- rendered_floors_texture: Texture,
- rendered_floors_sprite: Sprite,
+ surfaces_image: Image,
+ rendered_surfaces_texture: Texture,
+ rendered_surfaces_sprite: Sprite,
map: level.Map,
) !void {
- // TODO: this is wasteful -- we render the floors and draw the
- // texture, draw over that for the walls, then change the floor
- // texture for the tops of things and blank everywhere else, then
- // redraw the floor texture.
-
- // First render the floors
- try self.renderFloorsToTexture(floors_image, rendered_floors_texture, map);
- window.draw(rendered_floors_sprite, null);
-
- // then reset the z_buffer
+ // Fist reset the z_buffer
var i: usize = 0;
while (i < self.z_buffer.len) : (i += 1) {
self.z_buffer.set(i, std.math.inf(f32));
}
- // then render all the walls and populate the z_buffer
- try self.renderCells(window, walls_sprite, floors_image, rendered_floors_texture, map);
- window.draw(rendered_floors_sprite, null);
+ var pixels = [_]Colour{Colour.fromRGBA(0, 0, 0, 0)} ** (PlaneWidth * PlaneHeight);
+
+ // then draw all the walls and populate the z_buffer, while also
+ // rendering the surfaces below the horizon to the pixel array
+ self.renderCells(window, walls_sprite, surfaces_image, map, &pixels);
+
+ // then render the ceilings to our pixel array
+ self.renderCeilingsToTexture(surfaces_image, map, &pixels);
+
+ // we're now ready to draw the surfaces
+ try rendered_surfaces_texture.updateFromPixels(&pixels, null);
+ window.draw(rendered_surfaces_sprite, null);
// use the z_buffer to render sprites
self.renderObjects(window, objects_sprite, map);
@@ -220,12 +220,10 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type {
self: *@This(),
window: RenderWindow,
walls_sprite: Sprite,
- floors_image : Image,
- rendered_floors_texture : Texture,
+ floors_image: Image,
map: level.Map,
- ) !void {
- var pixels = [_]Colour{Colour.fromRGBA(0, 0, 0, 0)} ** (PlaneWidth * PlaneHeight);
-
+ pixels: []Colour,
+ ) void {
// This is a TERRIBLE hack: for whatever reason *linearly*
// interpolating on the direction vectors gives
// perspective-correct-seeming walls!
@@ -279,6 +277,7 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type {
dist_x = (self.pos_y - @intToFloat(f32, ipos_y)) * dx_for_y_step;
}
+ var top: f32 = undefined;
var distance: f32 = 0;
var still_drawing = true;
var highest_point: f32 = 0;
@@ -302,7 +301,7 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type {
// Is there a wall?
if (cell.height > 0) {
// project the top of the wall
- var top = PlaneHeight / 2 + PlaneDist * (cell.height - self.height) / distance;
+ top = PlaneHeight / 2 + PlaneDist * (cell.height - self.height) / distance;
// Does the wall extend above what we've draw?
if (top > highest_point) {
@@ -340,61 +339,60 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type {
}
highest_point = top;
}
+ }
- // do we potentially draw the top of this cell?
- if (highest_point < PlaneHeight / 2) {
- if (dist_y < dist_x) {
- distance = dist_y;
- } else {
- distance = dist_x;
- }
+ // do we potentially draw the top of this cell?
+ if (highest_point < PlaneHeight / 2) {
+ if (dist_y < dist_x) {
+ distance = dist_y;
+ } else {
+ distance = dist_x;
+ }
- // Note: next_top can never exceed PlaneHeight / 2
- // in the body of the next block. If the wall is
- // taller than us the back edge is lower than the
- // front one so this check will fail as we just drew
- // it (or higher than it). If the wall is shorter
- // then the back edge is at most the horizon.
- const next_top = PlaneHeight / 2 + PlaneDist * (cell.height - self.height) / distance;
+ // Note: next_top can never exceed PlaneHeight / 2
+ // in the body of the next block. If the wall is
+ // taller than us the back edge is lower than the
+ // front one so this check will fail as we just drew
+ // it (or higher than it). If the wall is shorter
+ // then the back edge is at most the horizon.
+ const next_top = PlaneHeight / 2 + PlaneDist * (cell.height - self.height) / distance;
- // only if we can see some part of it
- if (next_top > highest_point) {
- top = highest_point;
- while (top <= next_top and top < PlaneHeight / 2) : (top += 1) {
- const row_dist = (self.height - cell.height) * PlaneDist / (PlaneHeight / 2 - top);
- const ptop = @floatToInt(usize, top + 1);
- const itop = @floatToInt(usize, PlaneHeight) - ptop;
+ // only if we can see some part of it
+ if (next_top > highest_point) {
+ top = highest_point;
+ while (top <= next_top and top < PlaneHeight / 2) : (top += 1) {
+ const row_dist = (self.height - cell.height) * PlaneDist / (PlaneHeight / 2 - top);
+ const ptop = @floatToInt(usize, top + 1);
+ const itop = @floatToInt(usize, PlaneHeight) - ptop;
- // draw the correct pixel
- const sx = std.math.modf(self.pos_x + row_dist * cosra);
- const sy = std.math.modf(self.pos_y + row_dist * sinra);
- const toff = cell.floor_texture * @floatToInt(c_uint, constants.TextureDim);
- const px = @floatToInt(c_uint, constants.TextureDim * std.math.fabs(sx.fpart));
- const py = @floatToInt(c_uint, constants.TextureDim * std.math.fabs(sy.fpart));
- const val = floors_image.getPixel(.{ .x = toff + px, .y = py });
- pixels[itop * @floatToInt(usize, PlaneWidth) + col] = val;
+ // draw the correct pixel
+ const sx = std.math.modf(self.pos_x + row_dist * cosra);
+ const sy = std.math.modf(self.pos_y + row_dist * sinra);
+ const toff = cell.floor_texture * @floatToInt(c_uint, constants.TextureDim);
+ const px = @floatToInt(c_uint, constants.TextureDim * std.math.fabs(sx.fpart));
+ const py = @floatToInt(c_uint, constants.TextureDim * std.math.fabs(sy.fpart));
+ const val = floors_image.getPixel(.{ .x = toff + px, .y = py });
+ pixels[itop * @floatToInt(usize, PlaneWidth) + col] = val;
- // record in the z_buffer
+ // record in the z_buffer only if we're above the floor!
+ if (cell.height > 0) {
const index = col * @floatToInt(usize, PlaneHeight) + ptop;
self.z_buffer.set(index, row_dist);
}
- highest_point = top;
}
+ highest_point = top;
}
}
}
}
- try rendered_floors_texture.updateFromPixels(&pixels, null);
}
- fn renderFloorsToTexture(
+ fn renderCeilingsToTexture(
self: @This(),
- floors_image: Image,
- rendered_floors_texture: Texture,
+ surfaces_image: Image,
map: level.Map,
- ) !void {
- var pixels = [_]Colour{Colour.fromRGBA(0, 0, 0, 0)} ** (PlaneWidth * PlaneHeight);
-
+ pixels: []Colour,
+ ) void {
// Again, another TERRIBLE hack: we do the same nasty linear
// interpolation trick and for whatever reason the floors look fine.
const cos_first = std.math.cos(self.ang + 0.5 * FOV);
@@ -403,11 +401,9 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type {
const sin_last = std.math.sin(self.ang - 0.5 * FOV);
var row: usize = 0;
- while (row < PlaneHeight) : (row += 1) {
- if (row == PlaneHeight / 2) continue;
- const frow = std.math.fabs(PlaneHeight / 2 - @intToFloat(f32, row));
- const scale = if (row < PlaneHeight / 2) constants.MAX_HEIGHT - self.height else self.height;
- const row_dist = scale * PlaneDist / frow;
+ while (row < PlaneHeight / 2) : (row += 1) {
+ const frow = (PlaneHeight / 2 - @intToFloat(f32, row));
+ const row_dist = (constants.MAX_HEIGHT - self.height) * PlaneDist / frow;
const dx_step = row_dist * (cos_last - cos_first) / PlaneWidth;
const dy_step = row_dist * (sin_last - sin_first) / PlaneWidth;
@@ -430,20 +426,19 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type {
const ix = @floatToInt(i32, sx.ipart);
const iy = @floatToInt(i32, sy.ipart);
- if (map.inBounds(ix, iy)) {
+ const index = col * @floatToInt(usize, PlaneHeight) + @floatToInt(usize, PlaneHeight - 1) - row;
+ if (map.inBounds(ix, iy) and row_dist < self.z_buffer.get(index)) {
const cell = map.lookup(ix, iy);
- const tex = if (row > PlaneHeight / 2) cell.floor_texture else cell.ceiling_texture;
- const toff = tex * @floatToInt(c_uint, constants.TextureDim);
+ const toff = cell.ceiling_texture * @floatToInt(c_uint, constants.TextureDim);
const px = @floatToInt(c_uint, constants.TextureDim * std.math.fabs(sx.fpart));
const py = @floatToInt(c_uint, constants.TextureDim * std.math.fabs(sy.fpart));
- const val = floors_image.getPixel(.{ .x = toff + px, .y = py });
+ const val = surfaces_image.getPixel(.{ .x = toff + px, .y = py });
pixels[row * @floatToInt(usize, PlaneWidth) + col] = val;
}
}
}
- try rendered_floors_texture.updateFromPixels(&pixels, null);
}
};
}