diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/constants.zig | 2 | ||||
| -rw-r--r-- | src/level.zig | 11 | ||||
| -rw-r--r-- | src/main.zig | 34 | ||||
| -rw-r--r-- | src/player.zig | 233 |
4 files changed, 152 insertions, 128 deletions
diff --git a/src/constants.zig b/src/constants.zig index d363af5..450155f 100644 --- a/src/constants.zig +++ b/src/constants.zig @@ -28,5 +28,3 @@ pub const BackTextureWidth: f32 = 1280; pub const BackTextureHeight: f32 = 240; pub const TextureDim: f32 = 32.0; - -pub const MAX_HEIGHT: f32 = 2; diff --git a/src/level.zig b/src/level.zig index 1d61a02..a5d7330 100644 --- a/src/level.zig +++ b/src/level.zig @@ -27,12 +27,17 @@ pub const Object = struct { }; pub const Cell = struct { - height: f32, - wall_texture: u8 = 0, + floor_height: f32 = 0, + ceiling_height : f32 = DEFAULT_HEIGHT, + draw_down: bool = false, + lower_texture: u8 = 0, + upper_texture: u8 = 0, floor_texture: u8 = 0, ceiling_texture: u8 = 2, - const floor = Cell{ .height = 0 }; + pub const floor = Cell{ }; + + pub const DEFAULT_HEIGHT : f32 = 4; }; pub const Map = struct { diff --git a/src/main.zig b/src/main.zig index 5754793..d76e424 100644 --- a/src/main.zig +++ b/src/main.zig @@ -48,18 +48,34 @@ pub fn main() !void { var y: u32 = 0; while (y < 16) : (y += 1) { - map.cells.items[y * 16 + 00] = level.Cell{ .height = constants.MAX_HEIGHT }; - map.cells.items[y * 16 + 15] = level.Cell{ .height = constants.MAX_HEIGHT, .wall_texture = 2 }; - map.cells.items[15 * 16 + y] = level.Cell{ .height = constants.MAX_HEIGHT }; - map.cells.items[00 * 16 + y] = level.Cell{ .height = constants.MAX_HEIGHT }; + map.cells.items[y * 16 + 00] = level.Cell{ .floor_height = level.Cell.DEFAULT_HEIGHT }; + map.cells.items[y * 16 + 15] = level.Cell{ .floor_height = level.Cell.DEFAULT_HEIGHT, .lower_texture = 2 }; + map.cells.items[15 * 16 + y] = level.Cell{ .floor_height = level.Cell.DEFAULT_HEIGHT }; + map.cells.items[00 * 16 + y] = level.Cell{ .floor_height = level.Cell.DEFAULT_HEIGHT }; + + if (y < 15) { + map.cells.items[y * 16 + 01] = level.Cell{ + .ceiling_height = 3, + .upper_texture = 3, + .draw_down = true, + }; + } } - 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 = 1 }; + map.cells.items[16 * 7 + 7] = level.Cell{ .floor_height = level.Cell.DEFAULT_HEIGHT, .lower_texture = 1 }; + map.cells.items[16 * 7 + 6] = level.Cell{ + .floor_height = 0.5, + .ceiling_height = 2, + .lower_texture = 1, + .upper_texture = 1, + .floor_texture = 1, + .ceiling_texture = 1, + .draw_down = true, + }; - 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 }); - try map.objects.append(level.Object{ .pos_x = 6.5, .pos_y = 8.5, .texture = 0, .height = 1, .width = 1 }); + try map.objects.append(level.Object{ .pos_x = 1.5, .pos_y = 7.5, .pos_z = 2, .texture = 2, .height = 1, .width = 1 }); + try map.objects.append(level.Object{ .pos_x = 4, .pos_y = 8.5, .texture = 0, .height = 1, .width = 1 }); + try map.objects.append(level.Object{ .pos_x = 6, .pos_y = 8.5, .texture = 1, .height = 1, .width = 1 }); // Initialise SFML //-------------------------------------------------------------------------- diff --git a/src/player.zig b/src/player.zig index f6b82b1..c8fc4df 100644 --- a/src/player.zig +++ b/src/player.zig @@ -115,13 +115,19 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type { const nix = @floatToInt(i32, std.math.floor(next_x + if (self.vel_x > 0) min_dist else -min_dist)); const niy = @floatToInt(i32, std.math.floor(next_y + if (self.vel_y > 0) min_dist else -min_dist)); - if (!map.inBounds(nix, iy) or map.lookup(nix, iy).height > 0) { + if (!map.inBounds(nix, iy) or + blk: { + const cell = map.lookup(nix, iy); + if (cell.floor_height > 0) break :blk true; + if (cell.ceiling_height < self.height) break :blk true; + break :blk false; + }) { next_x = fx + if (self.vel_x > 0) 1 - min_dist else min_dist; self.vel_x = 0; self.acc_x = 0; } - if (!map.inBounds(ix, niy) or map.lookup(ix, niy).height > 0) { + if (!map.inBounds(ix, niy) or map.lookup(ix, niy).floor_height > 0) { next_y = fy + if (self.vel_y > 0) 1 - min_dist else min_dist; self.vel_y = 0; self.acc_y = 0; @@ -167,13 +173,9 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type { var pixels = [_]Colour{Colour.Transparent} ** (PlaneWidth * PlaneHeight); - // then draw all the walls and populate the z_buffer, while also - // rendering the surfaces below the horizon to the pixel array + // Draw all vertical and horizontal surfaces, and populate the z-buffer self.renderCells(walls_image, surfaces_image, map, &pixels); - // then render the ceilings to our pixel array - self.renderCeilingsToTexture(surfaces_image, map, &pixels); - // use the z_buffer to render sprites self.renderObjects(objects_image, map, &pixels); @@ -244,7 +246,7 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type { tex_frac += tex_frac_step; }) { var bottom = std.math.min(top, PlaneHeight); - var pix_y = @floatToInt(usize, std.math.ceil(std.math.max(PlaneHeight - bottom, 0))); + var pix_y = @floatToInt(usize, std.math.ceil(std.math.max(PlaneHeight - bottom - 1, 0))); var texel_y = (top - bottom) / height; while (pix_y < PlaneHeight and bottom >= top - height) : ({ bottom -= 1; @@ -252,9 +254,7 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type { texel_y += inv_height; }) { const index = col * @floatToInt(usize, PlaneHeight) + @floatToInt(usize, bottom); - if (self.z_buffer[index] < scaled_perp_distance) { - break; - } else { + if (self.z_buffer[index] > scaled_perp_distance) { const tx = @floatToInt(c_uint, tex_frac * constants.TextureDim); const toff = obj.texture * @floatToInt(c_uint, constants.TextureDim); const ty = @floatToInt(c_uint, texel_y * constants.TextureDim); @@ -328,10 +328,12 @@ 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 top_of_floor: f32 = undefined; + var bottom_of_ceiling: f32 = undefined; var distance: f32 = 0; var still_drawing = true; - var highest_point: f32 = 0; + var highest_drawn: f32 = 0; + var lowest_drawn: f32 = PlaneHeight - 1; var horizontal_hit: bool = undefined; while (still_drawing and map.inBounds(ipos_x, ipos_y)) : ({ // Find the next cell on our path @@ -349,19 +351,17 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type { }) { const cell = map.lookup(ipos_x, ipos_y); - // Is there a wall? - if (cell.height > 0) { - // project the top of the wall - top = PlaneHeight / 2 + PlaneDist * (cell.height - self.height) / distance; + // Are we drawing vertical surfaces? + if (cell.floor_height > 0 or cell.draw_down) { + // project the top of the bottom and the bottom of the top + top_of_floor = PlaneHeight / 2 + PlaneDist * (cell.floor_height - self.height) / distance; + bottom_of_ceiling = PlaneHeight / 2 + PlaneDist * (cell.ceiling_height - self.height) / distance; - // Does the wall extend above what we've draw? - if (top > highest_point) { - - // If we reach the top we have to stop! - if (top > PlaneHeight) { - still_drawing = false; - } + const draw_lower = 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 @@ -374,60 +374,90 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type { if (texfrac == 1) texfrac = 0.9999; // i think this caused a crash at one point const texstrip = @floatToInt(c_uint, constants.TextureDim * texfrac); - // which texture index? - const toff = cell.wall_texture * @floatToInt(c_uint, constants.TextureDim); - // height of a unit-height wall at this distance const nominal_length = PlaneDist / distance; const inv_nom_len = distance / PlaneDist; + // used for texel indexing - // now we have what we need to draw the wall, and - // update the z-buffer - const constrained_top = std.math.min(top, PlaneHeight - 1); - var zb_y = @floatToInt(i32, constrained_top); - var pix_y = @floatToInt(usize, std.math.ceil(std.math.max(PlaneHeight - top, 0))); - var texel_y = (top - constrained_top) / nominal_length; - while (zb_y > @floatToInt(i32, highest_point)) : ({ - zb_y -= 1; - pix_y += 1; - texel_y += inv_nom_len; - }) { - const ty = @floatToInt(c_uint, std.math.modf(texel_y).fpart * constants.TextureDim); - const texel = walls_image.getPixel(.{ .x = toff + texstrip, .y = ty }); + const td = @floatToInt(c_uint, constants.TextureDim); + if (draw_lower) { + // which texture index? + const t_lower_off = cell.lower_texture * td; + // now we have what we need to draw the face, and + // update the z-buffer + const constrained_top = std.math.min(top_of_floor, lowest_drawn); + var zb_y = @floatToInt(i32, constrained_top); + var pix_y = @floatToInt(usize, std.math.ceil(std.math.max(PlaneHeight - constrained_top, 0))); + var texel_y = (top_of_floor - constrained_top) / nominal_length; + while (zb_y > @floatToInt(i32, highest_drawn)) : ({ + zb_y -= 1; + pix_y += 1; + texel_y += inv_nom_len; + }) { + const ty = @floatToInt(c_uint, std.math.modf(texel_y).fpart * (constants.TextureDim - 1)); + const texel = walls_image.getPixel(.{ .x = t_lower_off + texstrip, .y = ty }); - const pix_index = pix_y * @floatToInt(usize, PlaneWidth) + col; - pixels[pix_index] = texel; + const pix_index = pix_y * @floatToInt(usize, PlaneWidth) + col; + pixels[pix_index] = texel; - const index = @intCast(usize, col * @floatToInt(i32, PlaneHeight) + zb_y); - self.z_buffer[index] = distance; + const index = @intCast(usize, col * @floatToInt(i32, PlaneHeight) + zb_y); + self.z_buffer[index] = distance; + } + highest_drawn = top_of_floor; + } + + if (draw_upper) { + const proj_default_end = PlaneHeight / 2 + PlaneDist * (level.Cell.DEFAULT_HEIGHT - self.height) / distance; + const stop = @floatToInt(i32, std.math.min(lowest_drawn, proj_default_end)); + const t_upper_off = cell.upper_texture * td; + const constrained_bottom = std.math.max(bottom_of_ceiling, highest_drawn); + var zb_y = @floatToInt(i32, constrained_bottom); + var pix_y = @floatToInt(usize, std.math.ceil(std.math.max(PlaneHeight - constrained_bottom - 1, 0))); + var texel_y: f32 = 0; + while (zb_y < stop) : ({ + zb_y += 1; + pix_y -= 1; + texel_y += inv_nom_len; + }) { + const ty = @floatToInt(c_uint, (1 - std.math.modf(texel_y).fpart) * (constants.TextureDim - 1)); + const texel = walls_image.getPixel(.{ .x = t_upper_off + texstrip, .y = ty }); + + const pix_index = pix_y * @floatToInt(usize, PlaneWidth) + col; + pixels[pix_index] = texel; + + const index = @intCast(usize, col * @floatToInt(i32, PlaneHeight) + zb_y); + self.z_buffer[index] = distance; + } + lowest_drawn = bottom_of_ceiling; } - highest_point = top; } } - // do we potentially draw the top of this cell? - if (highest_point < PlaneHeight / 2) { + // do we potentially draw floor 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. - 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. Similarly so for next_bottom + const next_top = PlaneHeight / 2 + PlaneDist * (cell.floor_height - self.height) / distance; + const next_bottom = PlaneHeight / 2 + PlaneDist * (cell.ceiling_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 floor? + if (next_top > highest_drawn) { + top_of_floor = std.math.floor(highest_drawn); + const thresh = std.math.min(std.math.min(next_top, lowest_drawn), PlaneHeight / 2) - 1; + while (top_of_floor <= thresh) : (top_of_floor += 1) { + const row_dist = (self.height - cell.floor_height) * PlaneDist / (PlaneHeight / 2 - top_of_floor); + const ptop = @floatToInt(usize, top_of_floor); + const itop = @floatToInt(usize, PlaneHeight) - ptop - 1; // draw the correct pixel const sx = std.math.modf(self.pos_x + row_dist * cosra); @@ -439,69 +469,44 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type { pixels[itop * @floatToInt(usize, PlaneWidth) + col] = val; // record in the z_buffer only if we're above the floor! - if (cell.height > 0) { + if (cell.floor_height > 0) { const index = col * @floatToInt(usize, PlaneHeight) + ptop; self.z_buffer[index] = row_dist; } } - highest_point = next_top; + highest_drawn = next_top; } - } - } - } - } - - fn renderCeilingsToTexture( - self: @This(), - surfaces_image: Image, - map: level.Map, - 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); - const sin_first = std.math.sin(self.ang + 0.5 * FOV); - const cos_last = std.math.cos(self.ang - 0.5 * FOV); - const sin_last = std.math.sin(self.ang - 0.5 * FOV); - - var row: usize = 0; - 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; - - var col: usize = 0; - var dx = row_dist * cos_first; - var dy = row_dist * sin_first; - while (col < PlaneWidth) : ({ - col += 1; - dx += dx_step; - dy += dy_step; - }) { - const x = self.pos_x + dx; - const y = self.pos_y + dy; - - const sx = std.math.modf(x); - const sy = std.math.modf(y); - - const ix = @floatToInt(i32, sx.ipart); - const iy = @floatToInt(i32, sy.ipart); - - const index = col * @floatToInt(usize, PlaneHeight) + @floatToInt(usize, PlaneHeight - 1) - row; - if (map.inBounds(ix, iy) and row_dist < self.z_buffer[index]) { - const cell = map.lookup(ix, iy); - 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)); + // draw ceiling? + if (cell.draw_down and next_bottom < lowest_drawn) { + bottom_of_ceiling = std.math.ceil(lowest_drawn); + const thresh = std.math.max(std.math.max(next_bottom, highest_drawn), PlaneHeight / 2) - 1; + while (bottom_of_ceiling >= thresh) : (bottom_of_ceiling -= 1) { + const row_dist = (cell.ceiling_height - self.height) * PlaneDist / (bottom_of_ceiling - PlaneHeight / 2); + const ptop = @floatToInt(usize, bottom_of_ceiling); + const itop = @floatToInt(usize, PlaneHeight) - ptop; - const val = surfaces_image.getPixel(.{ .x = toff + px, .y = py }); + 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.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 = surfaces_image.getPixel(.{ .x = toff + px, .y = py }); + pixels[itop * @floatToInt(usize, PlaneWidth) + col] = val; - pixels[row * @floatToInt(usize, PlaneWidth) + col] = val; + if (cell.draw_down) { + const index = col * @floatToInt(usize, PlaneHeight) + ptop; + self.z_buffer[index] = row_dist; + } + } + lowest_drawn = next_bottom; + } } } + // Have we filled this column? + if (top_of_floor > lowest_drawn or bottom_of_ceiling < highest_drawn) { + still_drawing = false; + } } } }; |
