aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/level.zig9
-rw-r--r--src/render.zig239
2 files changed, 154 insertions, 94 deletions
diff --git a/src/level.zig b/src/level.zig
index dce2a14..9f5217d 100644
--- a/src/level.zig
+++ b/src/level.zig
@@ -35,11 +35,12 @@ pub const Cell = struct {
floor_texture: u8 = 0,
ceiling_texture: u8 = 2,
- vertices: [][2]f32 = [][2]f32{
+ vertices: [5][2]f32 = [5][2]f32{
+ .{ 0, 0 },
+ .{ 0.5, 0 },
+ .{ 0.5, 0.5 },
+ .{ 0, 0.5 },
.{ 0, 0 },
- .{ 1, 0 },
- .{ 1, 1 },
- .{ 0, 1 },
},
pub const floor = Cell{};
diff --git a/src/render.zig b/src/render.zig
index 5879b31..0e68a93 100644
--- a/src/render.zig
+++ b/src/render.zig
@@ -72,29 +72,26 @@ fn fasterColourBlend(onto: Colour, from: Colour) Colour {
fn hitDistLocalCoords(
ray0: [2]f32,
ray1: [2]f32,
- vertices: [][2]f32,
+ vertices: []const [2]f32,
) ?f32 {
const rdy = ray1[1] - ray0[1];
const rdx = ray1[0] - ray0[0];
var vp = vertices[0];
var crossp: f32 = rdy * (vp[0] - ray0[0]) - rdx * (vp[1] - ray0[1]);
-
- // TODO: there is currently a bug in the logic, if vp->v is parallel to
- // ray0->ray1, then we'll return the distance ray0->vp (even if v is
- // closer). This is incorrect.
+ var return_val: ?f32 = null;
if (crossp == 0) {
// hit a vertex exactly
const dx = vp[0] - ray0[0];
const dy = vp[1] - ray0[1];
- return math.sqrt(dx * dx + dy * dy);
+ return std.math.sqrt(dx * dx + dy * dy);
}
var i: usize = 1;
var v = vertices[i];
var cross: f32 = rdy * (v[0] - ray0[0]) - rdx * (v[1] - ray0[1]);
- while (i < vertices.len) : ({
+ while (i + 1< vertices.len) : ({
i += 1;
vp = v;
v = vertices[i];
@@ -104,7 +101,12 @@ fn hitDistLocalCoords(
if (cross == 0) {
const dx = v[0] - ray0[0];
const dy = v[1] - ray0[1];
- return math.sqrt(dx * dx + dy * dy);
+ const new_distance = std.math.sqrt(dx * dx + dy * dy);
+ if (return_val) |local_distance| {
+ if (new_distance < local_distance) return_val = new_distance;
+ } else {
+ return_val = new_distance;
+ }
} else {
if (crossp * cross < 0) {
// If the segment from ray0->ray1 has vp and v on opposite sides
@@ -115,13 +117,21 @@ fn hitDistLocalCoords(
const vdx = v[0] - vp[0];
const vdy = v[1] - vp[1];
- const dx = rdx * cross; // this should really be negative, but we square it...
- const dy = rdy * (vdx * (ray0[1] - v[1]) - vdy * (ray0[0] - v[0]));
- return math.sqrt(dx * dx + dy * dy) / (rdx * vdy - vdx * rdy);
+ const t = (vdx * (ray0[1] - v[1]) - vdy * (ray0[0] - v[0])) / (rdx * vdy - vdx * rdy);
+ const new_distance = std.math.sqrt(rdx * rdx + rdy * rdy) * t;
+ // const s = cross / (rdx * vdy - vdx * rdy);
+ // const dx = v[0] + vdx * s - ray0[0];
+ // const dy = v[1] + vdy * s - ray0[1];
+ // const new_distance = std.math.sqrt(dx * dx + dy * dy);
+ if (return_val) |local_distance| {
+ if (new_distance < local_distance) return_val = new_distance;
+ } else {
+ return_val = new_distance;
+ }
}
}
}
- return null;
+ return return_val;
}
pub fn Renderer(PlaneWidth: f32, PlaneHeight: f32) type {
@@ -328,128 +338,177 @@ pub fn Renderer(PlaneWidth: f32, PlaneHeight: f32) type {
var top_of_floor: f32 = undefined;
var bottom_of_ceiling: f32 = undefined;
+
var distance: f32 = 0;
- var still_drawing = true;
+ var next_distance: f32 = 0;
+ var hit_horizontal: bool = undefined;
+ var next_hit_horizontal: bool = undefined;
+
+ if (dist_y < dist_x) {
+ hit_horizontal = false;
+ distance = dist_y;
+ dist_y += dy_for_x_step;
+ ipos_x += step_x;
+ } else {
+ hit_horizontal = true;
+ distance = dist_x;
+ dist_x += dx_for_y_step;
+ ipos_y += step_y;
+ }
+
+ if (dist_y < dist_x) {
+ next_hit_horizontal = false;
+ next_distance = dist_y;
+ } else {
+ next_hit_horizontal = true;
+ next_distance = dist_x;
+ }
+
var highest_drawn: f32 = 0;
var lowest_drawn: f32 = PlaneHeight - 1;
- var horizontal_hit: bool = undefined;
+ var still_drawing = true;
+
while (still_drawing and map.inBounds(ipos_x, ipos_y)) : ({
// Find the next cell on our path
if (dist_y < dist_x) {
- horizontal_hit = false;
+ hit_horizontal = false;
distance = dist_y;
dist_y += dy_for_x_step;
ipos_x += step_x;
} else {
- horizontal_hit = true;
+ hit_horizontal = true;
distance = dist_x;
dist_x += dx_for_y_step;
ipos_y += step_y;
}
+
+ if (dist_y < dist_x) {
+ next_distance = dist_y;
+ next_hit_horizontal = false;
+ } else {
+ next_distance = dist_x;
+ next_hit_horizontal = true;
+ }
}) {
const cell = map.lookup(ipos_x, ipos_y);
// Are we drawing vertical surfaces?
if (cell.floor_height > 0 or cell.draw_down) {
- if (hitDistLocalCoords(ray0, ray1, cell.vertices)) |local_distance| {
+ const ray0: [2]f32 = if (hit_horizontal)
+ [2]f32{
+ std.math.modf(distance * cosra + ppos_x).fpart,
+ (if (step_y > 0) 0 else 1),
+ }
+ else
+ [2]f32{
+ (if (step_x > 0) 0 else 1),
+ std.math.modf(distance * sinra + ppos_y).fpart,
+ };
- }
- // project the top of the bottom and the bottom of the top
- top_of_floor = PlaneHeight / 2 + PlaneDist * (cell.floor_height - pheight) / distance;
- bottom_of_ceiling = PlaneHeight / 2 + PlaneDist * (cell.ceiling_height - pheight) / distance;
+ const ray1: [2]f32 = if (next_hit_horizontal)
+ [2]f32{
+ std.math.modf(next_distance * cosra + ppos_x).fpart,
+ (if (step_y > 0) 1 else 0),
+ }
+ else
+ [2]f32{
+ (if (step_x > 0) 1 else 0),
+ std.math.modf(next_distance * sinra + ppos_y).fpart,
+ };
+
+ if (hitDistLocalCoords(ray0, ray1, &cell.vertices)) |local_distance| {
+ const adj_distance = distance + local_distance;
+
+ // project the top of the bottom and the bottom of the top
+ top_of_floor = PlaneHeight / 2 + PlaneDist * (cell.floor_height - pheight) / adj_distance;
+ bottom_of_ceiling = PlaneHeight / 2 + PlaneDist * (cell.ceiling_height - pheight) / adj_distance;
- const draw_lower = top_of_floor > highest_drawn;
- const draw_upper = cell.draw_down and bottom_of_ceiling < lowest_drawn;
+ const draw_lower = cell.floor_height > 0 and top_of_floor > highest_drawn;
+ const draw_upper = cell.draw_down and bottom_of_ceiling < lowest_drawn;
- // Are we able to see any vertical faces?
- if (draw_upper or draw_lower) {
- // we need the distance to calculate the fractional
- // part of the relevant coordinate for texture
- // mapping of the walls
- const hit_coordinate = if (horizontal_hit) distance * cosra + ppos_x else distance * sinra + ppos_y;
- var texfrac = std.math.modf(hit_coordinate).fpart;
+ // Are we able to see any vertical faces?
+ if (draw_upper or draw_lower) {
+ // we need the distance to calculate the fractional
+ // part of the relevant coordinate for texture
+ // mapping of the walls
+ var texfrac = if (hit_horizontal) ray0[0] else ray0[1];
- // we also want to be sure that we're consistently orienting
- // textures, in this case clockwise
- if ((horizontal_hit and sinra < 0) or (!horizontal_hit and cosra > 0)) texfrac = 1 - texfrac;
- const texstrip = @floatToInt(c_uint, constants.TextureDim * texfrac);
+ // we also want to be sure that we're consistently orienting
+ // textures, in this case clockwise
+ if ((hit_horizontal and sinra < 0) or (!hit_horizontal and cosra > 0)) texfrac = 1 - texfrac;
+ const texstrip = @floatToInt(c_uint, constants.TextureDim * texfrac);
- // height of a unit-height wall at this distance
- const nominal_length = PlaneDist / distance;
- // used for texel indexing
- const inv_nom_len = distance / PlaneDist;
+ // height of a unit-height wall at this distance
+ const nominal_length = PlaneDist / adj_distance;
+ // used for texel indexing
+ const inv_nom_len = adj_distance / PlaneDist;
- const td = @floatToInt(c_uint, constants.TextureDim);
- if (draw_lower) {
- // which texture index?
- const t_lower_off = cell.lower_texture * td;
- // Note the bizarre rounding we have to do to avoid artifacts
- const constrained_top = std.math.floor(std.math.min(top_of_floor, lowest_drawn));
- const proj_height = cell.floor_height * nominal_length;
- const constrained_bottom = std.math.max(highest_drawn, top_of_floor - proj_height);
- const stop = @floatToInt(usize, PlaneWidth * (PlaneHeight - constrained_bottom)) + col;
- const pix_y = @floatToInt(usize, std.math.ceil(std.math.max(PlaneHeight - constrained_top - 1, 0)));
- var pix_index = pix_y * @floatToInt(usize, PlaneWidth) + col;
- var texel_y: f32 = std.math.max((top_of_floor - constrained_top) / nominal_length, 0);
+ const td = @floatToInt(c_uint, constants.TextureDim);
+ if (draw_lower) {
+ // which texture index?
+ const t_lower_off = cell.lower_texture * td;
+ // Note the bizarre rounding we have to do to avoid artifacts
+ const constrained_top = std.math.floor(std.math.min(top_of_floor, lowest_drawn));
+ const proj_height = cell.floor_height * nominal_length;
+ const constrained_bottom = std.math.max(highest_drawn, top_of_floor - proj_height);
+ const stop = @floatToInt(usize, PlaneWidth * (PlaneHeight - constrained_bottom)) + col;
+ const pix_y = @floatToInt(usize, std.math.ceil(std.math.max(PlaneHeight - constrained_top - 1, 0)));
+ var pix_index = pix_y * @floatToInt(usize, PlaneWidth) + col;
+ var texel_y: f32 = std.math.max((top_of_floor - constrained_top) / nominal_length, 0);
- // now we have what we need to draw the face,
- // and update the z-buffer.
- while (pix_index < stop) : ({
- pix_index += @floatToInt(usize, PlaneWidth);
- texel_y += inv_nom_len;
- }) {
- const ty = @floatToInt(c_uint, std.math.modf(texel_y).fpart * constants.TextureDim);
- const texel = walls_image.getPixel(.{ .x = t_lower_off + texstrip, .y = ty });
+ // now we have what we need to draw the face,
+ // and update the z-buffer.
+ while (pix_index < stop) : ({
+ pix_index += @floatToInt(usize, PlaneWidth);
+ texel_y += inv_nom_len;
+ }) {
+ const ty = @floatToInt(c_uint, std.math.modf(texel_y).fpart * constants.TextureDim);
+ const texel = walls_image.getPixel(.{ .x = t_lower_off + texstrip, .y = ty });
- pixels[pix_index] = texel;
- self.z_buffer[pix_index] = distance;
+ pixels[pix_index] = texel;
+ self.z_buffer[pix_index] = adj_distance;
+ }
+ highest_drawn = constrained_top;
}
- highest_drawn = constrained_top;
- }
- if (draw_upper) {
- const proj_default_end = PlaneHeight / 2 + PlaneDist * (level.Cell.DEFAULT_HEIGHT - pheight) / distance;
- const constrained_top = std.math.min(lowest_drawn, proj_default_end);
- const constrained_bottom = std.math.ceil(std.math.max(bottom_of_ceiling, highest_drawn));
- const stop = @floatToInt(usize, PlaneWidth * (PlaneHeight - constrained_bottom)) + col;
- const t_upper_off = cell.upper_texture * td;
- const pix_y = @floatToInt(usize, std.math.ceil(std.math.max(PlaneHeight - constrained_top - 1, 0)));
- var pix_index = pix_y * @floatToInt(usize, PlaneWidth) + col;
- var texel_y: f32 = constants.TextureDim - (constrained_top - constrained_bottom) / nominal_length;
- while (pix_index < stop) : ({
- pix_index += @floatToInt(usize, PlaneWidth);
- texel_y += inv_nom_len;
- }) {
- const ty = @floatToInt(c_uint, std.math.modf(texel_y).fpart * constants.TextureDim);
- const texel = walls_image.getPixel(.{ .x = t_upper_off + texstrip, .y = ty });
+ if (draw_upper) {
+ const proj_default_end = PlaneHeight / 2 + PlaneDist * (level.Cell.DEFAULT_HEIGHT - pheight) / adj_distance;
+ const constrained_top = std.math.min(lowest_drawn, proj_default_end);
+ const constrained_bottom = std.math.ceil(std.math.max(bottom_of_ceiling, highest_drawn));
+ const stop = @floatToInt(usize, PlaneWidth * (PlaneHeight - constrained_bottom)) + col;
+ const t_upper_off = cell.upper_texture * td;
+ const pix_y = @floatToInt(usize, std.math.ceil(std.math.max(PlaneHeight - constrained_top - 1, 0)));
+ var pix_index = pix_y * @floatToInt(usize, PlaneWidth) + col;
+ var texel_y: f32 = constants.TextureDim - (constrained_top - constrained_bottom) / nominal_length;
+ while (pix_index < stop) : ({
+ pix_index += @floatToInt(usize, PlaneWidth);
+ texel_y += inv_nom_len;
+ }) {
+ const ty = @floatToInt(c_uint, std.math.modf(texel_y).fpart * constants.TextureDim);
+ const texel = walls_image.getPixel(.{ .x = t_upper_off + texstrip, .y = ty });
- pixels[pix_index] = texel;
- self.z_buffer[pix_index] = distance;
+ pixels[pix_index] = texel;
+ self.z_buffer[pix_index] = adj_distance;
+ }
+ lowest_drawn = constrained_bottom;
}
- lowest_drawn = constrained_bottom;
}
}
}
// do we potentially draw floor and or ceiling for this cell?
if (highest_drawn < PlaneHeight / 2 or (cell.draw_down and lowest_drawn > 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. Similarly so for next_bottom
- const next_top = PlaneHeight / 2 + PlaneDist * (cell.floor_height - pheight) / distance;
- const next_bottom = PlaneHeight / 2 + PlaneDist * (cell.ceiling_height - pheight) / distance;
+ const next_top = PlaneHeight / 2 + PlaneDist * (cell.floor_height - pheight) / next_distance;
+ const next_bottom = PlaneHeight / 2 + PlaneDist * (cell.ceiling_height - pheight) / next_distance;
// draw floor?
- if (next_top > highest_drawn) {
+ if (false and next_top > highest_drawn) {
const toff = cell.floor_texture * @floatToInt(c_uint, constants.TextureDim);
top_of_floor = std.math.ceil(std.math.min(std.math.min(next_top, lowest_drawn), PlaneHeight / 2 - 1));
@@ -478,7 +537,7 @@ pub fn Renderer(PlaneWidth: f32, PlaneHeight: f32) type {
}
// draw ceiling?
- if (cell.draw_down and next_bottom < lowest_drawn) {
+ if (false and cell.draw_down and next_bottom < lowest_drawn) {
const toff = cell.ceiling_texture * @floatToInt(c_uint, constants.TextureDim);
bottom_of_ceiling = std.math.ceil(lowest_drawn);