aboutsummaryrefslogtreecommitdiff
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
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?
-rw-r--r--floors.pngbin399 -> 0 bytes
-rw-r--r--src/main.zig26
-rw-r--r--src/raycast.zig135
-rw-r--r--surfaces.pngbin0 -> 637 bytes
4 files changed, 78 insertions, 83 deletions
diff --git a/floors.png b/floors.png
deleted file mode 100644
index 19254e7..0000000
--- a/floors.png
+++ /dev/null
Binary files differ
diff --git a/src/main.zig b/src/main.zig
index 218ba92..c44b5e6 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -55,7 +55,7 @@ pub fn main() !void {
}
map.cells.items[16 * 7 + 7] = level.Cell{ .height = constants.MAX_HEIGHT, .wall_texture = 1 };
- map.cells.items[16 * 7 + 6] = level.Cell{ .height = constants.MAX_HEIGHT / 4, .wall_texture = 1, .floor_texture = 0 };
+ map.cells.items[16 * 7 + 6] = level.Cell{ .height = constants.MAX_HEIGHT / 4, .wall_texture = 1, .floor_texture = 1 };
try map.objects.append(level.Object{ .pos_x = 5.5, .pos_y = 7.5, .pos_z = 1, .texture = 2, .height = 1, .width = 1 });
try map.objects.append(level.Object{ .pos_x = 4, .pos_y = 8.5, .texture = 1, .height = 1, .width = 1 });
@@ -103,18 +103,18 @@ pub fn main() !void {
defer objects_sprite.destroy();
// Load floor image
- var floors_image = try sf.Image.createFromFile("floors.png");
- defer floors_image.destroy();
+ var surfaces_image = try sf.Image.createFromFile("surfaces.png");
+ defer surfaces_image.destroy();
- var rendered_floors_texture = try sf.Texture.create(.{ .x = constants.PlaneWidth, .y = constants.PlaneHeight });
- defer rendered_floors_texture.destroy();
- rendered_floors_texture.setSmooth(false);
+ var rendered_surfaces_texture = try sf.Texture.create(.{ .x = constants.PlaneWidth, .y = constants.PlaneHeight });
+ defer rendered_surfaces_texture.destroy();
+ rendered_surfaces_texture.setSmooth(false);
- var rendered_floors_sprite = try sf.Sprite.createFromTexture(rendered_floors_texture);
- defer rendered_floors_sprite.destroy();
+ var rendered_surfaces_sprite = try sf.Sprite.createFromTexture(rendered_surfaces_texture);
+ defer rendered_surfaces_sprite.destroy();
- rendered_floors_sprite.setPosition(.{ .x = 0, .y = 0 });
- rendered_floors_sprite.setScale(.{ .x = constants.HFact, .y = constants.VFact });
+ rendered_surfaces_sprite.setPosition(.{ .x = 0, .y = 0 });
+ rendered_surfaces_sprite.setScale(.{ .x = constants.HFact, .y = constants.VFact });
// Start your engines!
//--------------------------------------------------------------------------
@@ -169,9 +169,9 @@ pub fn main() !void {
window,
walls_sprite,
objects_sprite,
- floors_image,
- rendered_floors_texture,
- rendered_floors_sprite,
+ surfaces_image,
+ rendered_surfaces_texture,
+ rendered_surfaces_sprite,
map,
);
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);
}
};
}
diff --git a/surfaces.png b/surfaces.png
new file mode 100644
index 0000000..f699b8b
--- /dev/null
+++ b/surfaces.png
Binary files differ