aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortslil clingman <>2021-09-11 23:43:51 -0400
committertslil clingman <>2021-09-11 23:43:51 -0400
commit842d498f81e4733452e03f01d1c570ab5bb02771 (patch)
tree73dfd262b2057003b195e35ec90f47826ca4bed2 /src
parenta0c7e2508df1cb0a094b9311ce90d4c25c6c31fd (diff)
Render tops of low walls!
But it's very inefficient right now
Diffstat (limited to 'src')
-rw-r--r--src/main.zig2
-rw-r--r--src/raycast.zig140
2 files changed, 96 insertions, 46 deletions
diff --git a/src/main.zig b/src/main.zig
index f51c3a3..218ba92 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 / 3, .wall_texture = 1, .floor_texture = 1 };
+ map.cells.items[16 * 7 + 6] = level.Cell{ .height = constants.MAX_HEIGHT / 4, .wall_texture = 1, .floor_texture = 0 };
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 });
diff --git a/src/raycast.zig b/src/raycast.zig
index a5bb731..1b78983 100644
--- a/src/raycast.zig
+++ b/src/raycast.zig
@@ -26,6 +26,41 @@ const Colour = @import("sfml").graphics.Color;
const level = @import("level.zig");
const constants = @import("constants.zig");
+fn renderSlice(
+ window: RenderWindow,
+ sprite: Sprite,
+ col: i32, // which column
+ top: f32, // top of wall
+ height: f32, // how tall
+ draw_frac: f32, // how much to draw, as a fraction; > 1 means repeat texture
+ texfrac: f32, // how far along the texture
+ texture: u8, // which texture index
+) void {
+ // we need ceil here so that we draw always to or past the edge of the screen
+ const draw_height = @floatToInt(c_int, std.math.ceil(draw_frac * constants.TextureDim));
+ const total_height = height * constants.VFact;
+
+ const xpos = @intToFloat(f32, col) * constants.HFact;
+ const ypos = constants.ScreenHeight / 2 + (constants.PlaneHeight / 2 - top) * constants.VFact;
+
+ const tind = texture * @floatToInt(c_int, constants.TextureDim);
+ const left = tind + @floatToInt(c_int, texfrac * constants.TextureDim);
+
+ sprite.setPosition(.{ .x = xpos, .y = ypos });
+ sprite.setScale(.{ .x = constants.HFact, .y = total_height / constants.TextureDim });
+ sprite.setTextureRect(.{ .top = 0, .left = left, .width = 1, .height = draw_height });
+ window.draw(sprite, null);
+}
+
+fn playerDistComp(pos: [2]f32, lhs: level.Object, rhs: level.Object) bool {
+ const lx = lhs.pos_x - pos[0];
+ const ly = lhs.pos_y - pos[1];
+ const rx = rhs.pos_x - pos[0];
+ const ry = rhs.pos_y - pos[1];
+
+ return (lx * lx + ly * ly > rx * rx + ry * ry);
+}
+
pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type {
const FOV: f32 = std.math.pi / 3.0;
const PlanePixels = PlaneWidth * PlaneHeight;
@@ -84,6 +119,11 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type {
rendered_floors_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);
@@ -95,21 +135,13 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type {
}
// then render all the walls and populate the z_buffer
- self.renderWalls(window, walls_sprite, map);
+ try self.renderCells(window, walls_sprite, floors_image, rendered_floors_texture, map);
+ window.draw(rendered_floors_sprite, null);
// use the z_buffer to render sprites
self.renderObjects(window, objects_sprite, map);
}
- fn playerDistComp(pos: [2]f32, lhs: level.Object, rhs: level.Object) bool {
- const lx = lhs.pos_x - pos[0];
- const ly = lhs.pos_y - pos[1];
- const rx = rhs.pos_x - pos[0];
- const ry = rhs.pos_y - pos[1];
-
- return (lx * lx + ly * ly > rx * rx + ry * ry);
- }
-
fn renderObjects(
self: @This(),
window: RenderWindow,
@@ -184,12 +216,16 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type {
}
}
- fn renderWalls(
+ fn renderCells(
self: *@This(),
window: RenderWindow,
walls_sprite: Sprite,
+ floors_image : Image,
+ rendered_floors_texture : Texture,
map: level.Map,
- ) void {
+ ) !void {
+ var pixels = [_]Colour{Colour.fromRGBA(0, 0, 0, 0)} ** (PlaneWidth * PlaneHeight);
+
// This is a TERRIBLE hack: for whatever reason *linearly*
// interpolating on the direction vectors gives
// perspective-correct-seeming walls!
@@ -201,7 +237,7 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type {
const cos_step = (cos_last - cos_first) / PlaneWidth;
const sin_step = (sin_last - sin_first) / PlaneWidth;
- var col: i32 = 0;
+ var col: u16 = 0;
var cosra = cos_first;
var sinra = sin_first;
while (col < PlaneWidth) : ({
@@ -267,10 +303,11 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type {
if (cell.height > 0) {
// project the top of the wall
var top = PlaneHeight / 2 + PlaneDist * (cell.height - self.height) / distance;
- // We have a wall to draw if it protrudes above what we have so far drawn
+
+ // Does the wall extend above what we've draw?
if (top > highest_point) {
- // did we extend beyond the top of the plane?
+ // If we reach the top we have to stop!
if (top > PlaneHeight) {
still_drawing = false;
}
@@ -301,12 +338,53 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type {
const index = @intCast(usize, col * @floatToInt(i32, PlaneHeight) + y);
self.z_buffer.set(index, distance);
}
-
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;
+ }
+
+ // 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;
+
+ // 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
+ const index = col * @floatToInt(usize, PlaneHeight) + ptop;
+ self.z_buffer.set(index, row_dist);
+ }
+ highest_point = top;
+ }
+ }
}
}
}
+ try rendered_floors_texture.updateFromPixels(&pixels, null);
}
fn renderFloorsToTexture(
@@ -354,7 +432,7 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type {
if (map.inBounds(ix, iy)) {
const cell = map.lookup(ix, iy);
- const tex = @as(c_uint, if (row > PlaneHeight / 2) cell.floor_texture else cell.ceiling_texture);
+ const tex = if (row > PlaneHeight / 2) cell.floor_texture else cell.ceiling_texture;
const toff = tex * @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));
@@ -367,33 +445,5 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type {
}
try rendered_floors_texture.updateFromPixels(&pixels, null);
}
-
- // TODO: is there anyway i can say fn renderSlice : RenderSliceFunction?
- fn renderSlice(
- window: RenderWindow,
- sprite: Sprite,
- col: i32, // which column
- top: f32, // top of wall
- total_height: f32, // how tall
- draw_frac: f32,
- texfrac: f32, // how far along the texture
- texture: u8, // which texture index
- ) void {
-
- // we need ceil here so that we draw always to or past the edge of the screen
- const draw_height = @floatToInt(c_int, std.math.ceil(draw_frac * constants.TextureDim));
- const total_length = total_height * constants.VFact;
-
- const xpos = @intToFloat(f32, col) * constants.HFact;
- const ypos = constants.ScreenHeight / 2 + (constants.PlaneHeight / 2 - top) * constants.VFact;
-
- const tind = @as(c_int, texture) * @floatToInt(c_int, constants.TextureDim);
- const left = tind + @floatToInt(c_int, texfrac * constants.TextureDim);
-
- sprite.setPosition(.{ .x = xpos, .y = ypos });
- sprite.setScale(.{ .x = constants.HFact, .y = total_length / constants.TextureDim });
- sprite.setTextureRect(.{ .top = 0, .left = left, .width = 1, .height = draw_height });
- window.draw(sprite, null);
- }
};
}