From a77bddbdb240aa1d31a1410b24364062993a087c Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Sun, 21 Dec 2025 22:26:55 +0100 Subject: [PATCH] Separate out Chunks-related code, "fix" collisions to be less broken --- src/Chunks.zig | 286 +++++++++++++++++++++++++++++++++++++++++++++++ src/Game.zig | 13 +-- src/Player.zig | 295 ++----------------------------------------------- src/const.zig | 6 +- src/math.zig | 8 ++ 5 files changed, 313 insertions(+), 295 deletions(-) create mode 100644 src/Chunks.zig diff --git a/src/Chunks.zig b/src/Chunks.zig new file mode 100644 index 0000000..3ded3eb --- /dev/null +++ b/src/Chunks.zig @@ -0,0 +1,286 @@ +const Chunks = @This(); +const std = @import("std"); + +const c = @import("const.zig"); +const math = @import("math.zig"); +const vk = @import("vulkan"); + +const Blocks = @import("assets/Blocks.zig"); +const Chunk = @import("assets/Chunk.zig"); +const Engine = @import("engine/Engine.zig"); +const Iterator2 = math.Iterator2; +const Vector2Int = math.Vector2Int; +const Vector3Int = math.Vector3Int; + +chunks: std.AutoHashMapUnmanaged([3]i16, Chunk), + +const SweepHit = struct { + normal_frac: Vector3Int, + projected_distance_sv: i32, +}; + +pub fn deinit(self: *Chunks, engine: *Engine, descriptor_pool: vk.DescriptorPool, allocator: std.mem.Allocator) void { + var it = self.chunks.valueIterator(); + while (it.next()) |chunk| { + chunk.deinit(engine, descriptor_pool); + } + self.chunks.deinit(allocator); + self.* = undefined; +} + +pub fn getVoxelAt(self: *const Chunks, vx: Vector3Int) ?Blocks.Id { + const min_ck = Vector3Int.initScalar(std.math.minInt(i16)); + const max_ck = Vector3Int.initScalar(std.math.maxInt(i16)); + const ck = vx.divScalar(c.vx_per_ck); + if (@reduce(.Or, (ck.vector < min_ck.vector) | (ck.vector > max_ck.vector))) { + return null; + } + + if (self.chunks.get(.{ + @intCast(ck.getX()), + @intCast(ck.getY()), + @intCast(ck.getZ()), + })) |chunk| { + const ckvx = vx.modScalar(c.vx_per_ck); + return chunk.blocks[@intCast(ckvx.getZ())][@intCast(ckvx.getY())][@intCast(ckvx.getX())]; + } else { + return .air; + } +} + +pub fn isSolid(self: *const Chunks, vx: Vector3Int) bool { + const maybe_id = getVoxelAt(self, vx); + // NOTE `null` is considered solid, as it's out of bounds. + return maybe_id != .air; +} + +pub fn sweepCastDown(self: *const Chunks, min_sv: Vector3Int, max_sv: Vector3Int, distance_sv: i32) ?SweepHit { + const min_x_vx = c.subvoxelsToVoxels(.border_up, min_sv.getX()); + const min_y_vx = c.subvoxelsToVoxels(.border_up, min_sv.getY()); + const max_x_vx = c.subvoxelsToVoxels(.border_down, max_sv.getX()); + const max_y_vx = c.subvoxelsToVoxels(.border_down, max_sv.getY()); + + const start_z_vx = c.subvoxelsToVoxels(.border_up, min_sv.getZ()) - 1; + const end_z_vx = c.subvoxelsToVoxels(.border_up, min_sv.getZ() - distance_sv); + + var z_vx: i32 = start_z_vx; + while (z_vx >= end_z_vx) : (z_vx -= 1) { + const z_sv = c.voxelsToSubvoxels(z_vx + 1); + var it = Iterator2(i32).init(.{ + .min = .{ min_x_vx, min_y_vx }, + .max = .{ max_x_vx, max_y_vx }, + }); + while (it.next()) |xy_vx| { + const x_vx, const y_vx = xy_vx; + if (!self.isSolid(.init(x_vx, y_vx, z_vx))) continue; + + return .{ + .projected_distance_sv = distance_sv - (min_sv.getZ() - z_sv), + .normal_frac = .unit_z_frac, + }; + } + } + + return null; +} + +pub fn sweepCastUp(self: *const Chunks, min_sv: Vector3Int, max_sv: Vector3Int, distance_sv: i32) ?SweepHit { + const min_x_vx = c.subvoxelsToVoxels(.border_up, min_sv.getX()); + const min_y_vx = c.subvoxelsToVoxels(.border_up, min_sv.getY()); + const max_x_vx = c.subvoxelsToVoxels(.border_down, max_sv.getX()); + const max_y_vx = c.subvoxelsToVoxels(.border_down, max_sv.getY()); + + const start_z_vx = c.subvoxelsToVoxels(.border_down, max_sv.getZ()) + 1; + const end_z_vx = c.subvoxelsToVoxels(.border_down, max_sv.getZ() + distance_sv); + + var z_vx: i32 = start_z_vx; + while (z_vx <= end_z_vx) : (z_vx += 1) { + const z_sv = c.voxelsToSubvoxels(z_vx); + var it = Iterator2(i32).init(.{ + .min = .{ min_x_vx, min_y_vx }, + .max = .{ max_x_vx, max_y_vx }, + }); + while (it.next()) |xy_vx| { + const x_vx, const y_vx = xy_vx; + if (!self.isSolid(.init(x_vx, y_vx, z_vx))) continue; + + return .{ + .projected_distance_sv = distance_sv - (z_sv - max_sv.getZ()), + .normal_frac = .unit_nz_frac, + }; + } + } + + return null; +} + +pub fn sweepCastHorizontal(self: *const Chunks, min_sv: Vector3Int, max_sv: Vector3Int, ray_sv: Vector2Int) ?SweepHit { + const min_z_vx = c.subvoxelsToVoxels(.border_up, min_sv.getZ()); + const max_z_vx = c.subvoxelsToVoxels(.border_down, max_sv.getZ()); + + var hit: ?SweepHit = null; + var hit_distance_squared = std.math.inf(f32); + + const fdydx: f32 = @as(f32, @floatFromInt(ray_sv.getY())) / @as(f32, @floatFromInt(ray_sv.getX())); + const fdxdy: f32 = @as(f32, @floatFromInt(ray_sv.getX())) / @as(f32, @floatFromInt(ray_sv.getY())); + + // Positive X + if (ray_sv.getX() > 0) { + const x0_sv = max_sv.getX(); + const y0_sv = min_sv.getY(); + const y1_sv = max_sv.getY(); + + const start_x_vx = c.subvoxelsToVoxels(.border_down, x0_sv) + 1; + const end_x_vx = c.subvoxelsToVoxels(.border_down, x0_sv + ray_sv.getX()); + + var x_vx: i32 = start_x_vx; + px: while (x_vx <= end_x_vx) : (x_vx += 1) { + const x_sv = c.voxelsToSubvoxels(x_vx); + const min_y_vx = c.subvoxelsToVoxels(.border_up, math.wideMulDivFloor(x_sv - x0_sv, ray_sv.getY(), ray_sv.getX()) + y0_sv); + const max_y_vx = c.subvoxelsToVoxels(.border_down, math.wideMulDivCeil(x_sv - x0_sv, ray_sv.getY(), ray_sv.getX()) + y1_sv); + var it = Iterator2(i32).init(.{ + .min = .{ min_y_vx, min_z_vx }, + .max = .{ max_y_vx, max_z_vx }, + }); + while (it.next()) |yz_vx| { + const y_vx, const z_vx = yz_vx; + if (!self.isSolid(.init(x_vx, y_vx, z_vx))) continue; + + const dx = x_sv - x0_sv; + const fdx: f32 = @floatFromInt(dx); + const fdy: f32 = fdx * fdydx; + + hit = .{ + .projected_distance_sv = ray_sv.getX() - dx, + .normal_frac = .unit_nx_frac, + }; + hit_distance_squared = fdx * fdx + fdy * fdy; + // std.debug.print("HIT +X ({X}->{X}) at ({X}, {X}, {X}) [VX] | min_sv={X} max_sv={X} ray_sv={X} | proj_dist={X} \n", .{ start_x_vx, end_x_vx, x_vx, y_vx, z_vx, min_sv.vector, max_sv.vector, ray_sv.vector, hit.?.projected_distance_sv }); + + break :px; + } + } + } + + // Negative X + if (ray_sv.getX() < 0) { + const x0_sv = min_sv.getX(); + const y0_sv = min_sv.getY(); + const y1_sv = max_sv.getY(); + + const start_x_vx = c.subvoxelsToVoxels(.border_up, x0_sv) - 1; + const end_x_vx = c.subvoxelsToVoxels(.border_up, x0_sv + ray_sv.getX()); + + var x_vx: i32 = start_x_vx; + nx: while (x_vx >= end_x_vx) : (x_vx -= 1) { + const x_sv = c.voxelsToSubvoxels(x_vx + 1); + const min_y_vx = c.subvoxelsToVoxels(.border_up, math.wideMulDivFloor(x_sv - x0_sv, ray_sv.getY(), ray_sv.getX()) + y0_sv); + const max_y_vx = c.subvoxelsToVoxels(.border_down, math.wideMulDivCeil(x_sv - x0_sv, ray_sv.getY(), ray_sv.getX()) + y1_sv); + var it = Iterator2(i32).init(.{ + .min = .{ min_y_vx, min_z_vx }, + .max = .{ max_y_vx, max_z_vx }, + }); + while (it.next()) |yz_vx| { + const y_vx, const z_vx = yz_vx; + if (!self.isSolid(.init(x_vx, y_vx, z_vx))) continue; + + const dx = x_sv - x0_sv; + const fdx: f32 = @floatFromInt(dx); + const fdy: f32 = fdx * fdydx; + + hit = .{ + .projected_distance_sv = -(ray_sv.getX() - dx), + .normal_frac = .unit_x_frac, + }; + hit_distance_squared = fdx * fdx + fdy * fdy; + // std.debug.print("HIT -X ({X}->{X}) at ({X}, {X}, {X}) [VX] | min_sv={X} max_sv={X} ray_sv={X} | proj_dist={X} \n", .{ start_x_vx, end_x_vx, x_vx, y_vx, z_vx, min_sv.vector, max_sv.vector, ray_sv.vector, hit.?.projected_distance_sv }); + + break :nx; + } + } + } + + // Positive Y + if (ray_sv.getY() > 0) { + const y0_sv = max_sv.getY(); + const x0_sv = min_sv.getX(); + const x1_sv = max_sv.getX(); + + const start_y_vx = c.subvoxelsToVoxels(.border_down, y0_sv) + 1; + const end_y_vx = c.subvoxelsToVoxels(.border_down, y0_sv + ray_sv.getY()); + + var y_vx = start_y_vx; + py: while (y_vx <= end_y_vx) : (y_vx += 1) { + const y_sv = c.voxelsToSubvoxels(y_vx); + const min_x_vx = c.subvoxelsToVoxels(.border_up, math.wideMulDivFloor(y_sv - y0_sv, ray_sv.getX(), ray_sv.getY()) + x0_sv); + const max_x_vx = c.subvoxelsToVoxels(.border_down, math.wideMulDivCeil(y_sv - y0_sv, ray_sv.getX(), ray_sv.getY()) + x1_sv); + var it = Iterator2(i32).init(.{ + .min = .{ min_x_vx, min_z_vx }, + .max = .{ max_x_vx, max_z_vx }, + }); + while (it.next()) |xz_vx| { + const x_vx, const z_vx = xz_vx; + if (!self.isSolid(.init(x_vx, y_vx, z_vx))) continue; + + const dy = y_sv - y0_sv; + const fdy: f32 = @floatFromInt(dy); + const fdx: f32 = fdy * fdxdy; + + const this_hit_distance_squared = fdx * fdx + fdy * fdy; + if (this_hit_distance_squared < hit_distance_squared) { + hit = .{ + .projected_distance_sv = ray_sv.getY() - dy, + .normal_frac = .unit_ny_frac, + }; + hit_distance_squared = this_hit_distance_squared; + // std.debug.print("HIT +Y ({X}->{X}) at ({X}, {X}, {X}) [VX] | min_sv={X} max_sv={X} ray_sv={X} | proj_dist={X} \n", .{ start_y_vx, end_y_vx, x_vx, y_vx, z_vx, min_sv.vector, max_sv.vector, ray_sv.vector, hit.?.projected_distance_sv }); + } + + break :py; + } + } + } + + // Negative Y + if (ray_sv.getY() < 0.0) { + const y0_sv = min_sv.getY(); + const x0_sv = min_sv.getX(); + const x1_sv = max_sv.getX(); + + const start_y_vx = c.subvoxelsToVoxels(.border_up, y0_sv) - 1; + const end_y_vx = c.subvoxelsToVoxels(.border_up, y0_sv + ray_sv.getY()); + + var y_vx = start_y_vx; + ny: while (y_vx >= end_y_vx) : (y_vx -= 1) { + const y_sv = c.voxelsToSubvoxels(y_vx + 1); + const min_x_vx = c.subvoxelsToVoxels(.border_up, math.wideMulDivFloor(y_sv - y0_sv, ray_sv.getX(), ray_sv.getY()) + x0_sv); + const max_x_vx = c.subvoxelsToVoxels(.border_down, math.wideMulDivCeil(y_sv - y0_sv, ray_sv.getX(), ray_sv.getY()) + x1_sv); + var it = Iterator2(i32).init(.{ + .min = .{ min_x_vx, min_z_vx }, + .max = .{ max_x_vx, max_z_vx }, + }); + while (it.next()) |xz_vx| { + const x_vx, const z_vx = xz_vx; + if (!self.isSolid(.init(x_vx, y_vx, z_vx))) continue; + + const dy = y_sv - y0_sv; + const fdy: f32 = @floatFromInt(dy); + const fdx: f32 = fdy * fdxdy; + + const this_hit_distance_squared = fdx * fdx + fdy * fdy; + if (this_hit_distance_squared < hit_distance_squared) { + hit = .{ + .projected_distance_sv = -(ray_sv.getY() - dy), + .normal_frac = .unit_y_frac, + }; + hit_distance_squared = this_hit_distance_squared; + // std.debug.print("HIT -Y ({X}->{X}) at ({X}, {X}, {X}) [VX] | min_sv={X} max_sv={X} ray_sv={X} | proj_dist={X} \n", .{ start_y_vx, end_y_vx, x_vx, y_vx, z_vx, min_sv.vector, max_sv.vector, ray_sv.vector, hit.?.projected_distance_sv }); + } + + break :ny; + } + } + } + + return hit; +} diff --git a/src/Game.zig b/src/Game.zig index 223b494..73b43ca 100644 --- a/src/Game.zig +++ b/src/Game.zig @@ -10,6 +10,7 @@ const worldgen = @import("worldgen.zig"); const Blocks = @import("assets/Blocks.zig"); const Chunk = @import("assets/Chunk.zig"); +const Chunks = @import("Chunks.zig"); const CommandBuffer = @import("engine/CommandBuffer.zig"); const Engine = @import("engine/Engine.zig"); const Iterator2 = math.Iterator2; @@ -50,7 +51,7 @@ deferred_command_buffers: std.ArrayList(CommandBuffer), blocks: Blocks, materials: Materials, textures: Textures, -chunks: std.AutoHashMapUnmanaged([3]i16, Chunk), +chunks: Chunks, skybox: Skybox, player: Player, @@ -659,7 +660,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain .blocks = blocks, .materials = materials, .textures = textures, - .chunks = chunks, + .chunks = .{ .chunks = chunks }, .skybox = skybox, .player = .init(player_position_sv, 0, 0), @@ -677,11 +678,7 @@ pub fn deinit(self: *Game) void { self.vertex_buffer.deinit(self.engine); self.index_buffer.deinit(self.engine); - var it = self.chunks.valueIterator(); - while (it.next()) |chunk| { - chunk.deinit(self.engine, self.descriptor_pool); - } - self.chunks.deinit(self.allocator); + self.chunks.deinit(self.engine, self.descriptor_pool, self.allocator); self.skybox.deinit(self.engine); self.global_uniforms.deinit(self.engine); @@ -889,7 +886,7 @@ fn render(self: *Game) !void { command_buffer.bindIndexBuffer(self.index_buffer.buffer, 0, .uint16); command_buffer.bindDescriptorSet(.graphics, self.pipeline_layout, 0, self.global_descriptor_set, null); - var it = self.chunks.valueIterator(); + var it = self.chunks.chunks.valueIterator(); while (it.next()) |chunk| { chunk.draw(self.pipeline_layout, command_buffer); } diff --git a/src/Player.zig b/src/Player.zig index 2f728e2..bdce8c9 100644 --- a/src/Player.zig +++ b/src/Player.zig @@ -6,7 +6,7 @@ const glfw = @import("zglfw"); const math = @import("math.zig"); const Blocks = @import("assets/Blocks.zig"); -const Chunk = @import("assets/Chunk.zig"); +const Chunks = @import("Chunks.zig"); const Iterator2 = math.Iterator2; const Vector2 = math.Vector2; const Vector3 = math.Vector3; @@ -226,7 +226,7 @@ pub fn onMouseMove(self: *Player, dx: f32, dy: f32) void { self.yaw_rad = @mod(self.yaw_rad - dx * self.mouse_sensitivity, std.math.tau); } -pub fn update(self: *Player, dt: f32, chunks: *const std.AutoHashMapUnmanaged([3]i16, Chunk)) void { +pub fn update(self: *Player, dt: f32, chunks: *const Chunks) void { defer self.resetAllButtons(); // --- GATHER INPUTS ------------------------------------------------------- @@ -272,17 +272,21 @@ pub fn update(self: *Player, dt: f32, chunks: *const std.AutoHashMapUnmanaged([3 var vertical_displacement_sv = math.mulIntFloat(vertical_velocity_sv, dt); var position_sv = self.position_sv; + var min_sv = position_sv.add(.init(-collision_half_width_sv, -collision_half_width_sv, 0)); + var max_sv = position_sv.add(.init(collision_half_width_sv, collision_half_width_sv, collision_height_sv)); if (vertical_displacement_sv > 0) { - if (sweepCastUp(position_sv, vertical_displacement_sv, chunks)) |hit| { + if (chunks.sweepCastUp(min_sv, max_sv, vertical_displacement_sv)) |hit| { vertical_displacement_sv -= hit.projected_distance_sv; } position_sv = .add(position_sv, .init(0, 0, vertical_displacement_sv)); + min_sv = .add(min_sv, .init(0, 0, vertical_displacement_sv)); + max_sv = .add(max_sv, .init(0, 0, vertical_displacement_sv)); } var i: usize = 0; while (i < 2) : (i += 1) { - if (sweepCastHorizontal(position_sv, horizontal_displacement_sv, chunks)) |hit| { + if (chunks.sweepCastHorizontal(min_sv, max_sv, horizontal_displacement_sv)) |hit| { const adjustment = hit.normal_frac .asVector2Int() .mulScalarFrac(hit.projected_distance_sv); @@ -306,7 +310,9 @@ pub fn update(self: *Player, dt: f32, chunks: *const std.AutoHashMapUnmanaged([3 position_sv = .add(position_sv, horizontal_displacement_sv.asVector3Int(0)); if (vertical_displacement_sv < 0.0) { - if (sweepCastDown(position_sv, -vertical_displacement_sv, chunks)) |hit| { + min_sv = .add(min_sv, horizontal_displacement_sv.asVector3Int(0)); + max_sv = .add(max_sv, horizontal_displacement_sv.asVector3Int(0)); + if (chunks.sweepCastDown(min_sv, max_sv, -vertical_displacement_sv)) |hit| { vertical_displacement_sv += hit.projected_distance_sv; } position_sv = .add(position_sv, .init(0, 0, vertical_displacement_sv)); @@ -317,251 +323,6 @@ pub fn update(self: *Player, dt: f32, chunks: *const std.AutoHashMapUnmanaged([3 } } -const SweepHit = struct { - normal_frac: Vector3Int, - projected_distance_sv: i32, -}; - -fn sweepCastDown(origin_sv: Vector3Int, distance_sv: i32, chunks: *const std.AutoHashMapUnmanaged([3]i16, Chunk)) ?SweepHit { - const min_origin_sv = origin_sv.add(.init(-collision_half_width_sv, -collision_half_width_sv, 0)); - const max_origin_sv = origin_sv.add(.init(collision_half_width_sv, collision_half_width_sv, collision_height_sv)); - - const min_x_vx = c.subvoxelsToVoxels(.border_up, min_origin_sv.getX()); - const min_y_vx = c.subvoxelsToVoxels(.border_up, min_origin_sv.getY()); - const max_x_vx = c.subvoxelsToVoxels(.border_down, max_origin_sv.getX()); - const max_y_vx = c.subvoxelsToVoxels(.border_down, max_origin_sv.getY()); - - const start_z_vx = c.subvoxelsToVoxels(.border_up, min_origin_sv.getZ()) - 1; - const end_z_vx = c.subvoxelsToVoxels(.border_up, min_origin_sv.getZ() - distance_sv); - - var z_vx: i32 = start_z_vx; - while (z_vx >= end_z_vx) : (z_vx -= 1) { - const z_sv = c.voxelsToSubvoxels(z_vx + 1); - var it = Iterator2(i32).init(.{ - .min = .{ min_x_vx, min_y_vx }, - .max = .{ max_x_vx, max_y_vx }, - }); - while (it.next()) |xy_vx| { - const x_vx, const y_vx = xy_vx; - if (!isSolid(chunks, .init(x_vx, y_vx, z_vx))) continue; - - return .{ - .projected_distance_sv = distance_sv - (min_origin_sv.getZ() - z_sv), - .normal_frac = .unit_z_frac, - }; - } - } - - return null; -} - -fn sweepCastUp(origin_sv: Vector3Int, distance_sv: i32, chunks: *const std.AutoHashMapUnmanaged([3]i16, Chunk)) ?SweepHit { - const min_origin_sv = origin_sv.add(.init(-collision_half_width_sv, -collision_half_width_sv, 0)); - const max_origin_sv = origin_sv.add(.init(collision_half_width_sv, collision_half_width_sv, collision_height_sv)); - - const min_x_vx = c.subvoxelsToVoxels(.border_up, min_origin_sv.getX()); - const min_y_vx = c.subvoxelsToVoxels(.border_up, min_origin_sv.getY()); - const max_x_vx = c.subvoxelsToVoxels(.border_down, max_origin_sv.getX()); - const max_y_vx = c.subvoxelsToVoxels(.border_down, max_origin_sv.getY()); - - const start_z_vx = c.subvoxelsToVoxels(.border_down, max_origin_sv.getZ()) + 1; - const end_z_vx = c.subvoxelsToVoxels(.border_down, max_origin_sv.getZ() + distance_sv); - - var z_vx: i32 = start_z_vx; - while (z_vx <= end_z_vx) : (z_vx += 1) { - const z_sv = c.voxelsToSubvoxels(z_vx); - var it = Iterator2(i32).init(.{ - .min = .{ min_x_vx, min_y_vx }, - .max = .{ max_x_vx, max_y_vx }, - }); - while (it.next()) |xy_vx| { - const x_vx, const y_vx = xy_vx; - if (!isSolid(chunks, .init(x_vx, y_vx, z_vx))) continue; - - return .{ - .projected_distance_sv = distance_sv - (z_sv - max_origin_sv.getZ()), - .normal_frac = .unit_nz_frac, - }; - } - } - - return null; -} - -fn sweepCastHorizontal(origin_sv: Vector3Int, ray_sv: Vector2Int, chunks: *const std.AutoHashMapUnmanaged([3]i16, Chunk)) ?SweepHit { - const min_origin_sv = origin_sv.add(.init(-collision_half_width_sv, -collision_half_width_sv, 0)); - const max_origin_sv = origin_sv.add(.init(collision_half_width_sv, collision_half_width_sv, collision_height_sv)); - - const min_z_vx = c.subvoxelsToVoxels(.border_up, min_origin_sv.getZ()); - const max_z_vx = c.subvoxelsToVoxels(.border_down, max_origin_sv.getZ()); - - var hit: ?SweepHit = null; - var hit_distance_squared = std.math.inf(f32); - - const fdydx: f32 = @as(f32, @floatFromInt(ray_sv.getY())) / @as(f32, @floatFromInt(ray_sv.getX())); - const fdxdy: f32 = @as(f32, @floatFromInt(ray_sv.getX())) / @as(f32, @floatFromInt(ray_sv.getY())); - - // Positive X - if (ray_sv.getX() > 0) { - const x0_sv = max_origin_sv.getX(); - const y0_sv = min_origin_sv.getY(); - const y1_sv = max_origin_sv.getY(); - - const start_x_vx = c.subvoxelsToVoxels(.border_down, x0_sv) + 1; - const end_x_vx = c.subvoxelsToVoxels(.border_down, x0_sv + ray_sv.getX()); - - var x_vx: i32 = start_x_vx; - px: while (x_vx <= end_x_vx) : (x_vx += 1) { - const x_sv = c.voxelsToSubvoxels(x_vx); - const min_y_vx = c.subvoxelsToVoxels(.border_up, wideMulDivFloor(x_sv - x0_sv, ray_sv.getY(), ray_sv.getX()) + y0_sv); - const max_y_vx = c.subvoxelsToVoxels(.border_down, wideMulDivCeil(x_sv - x0_sv, ray_sv.getY(), ray_sv.getX()) + y1_sv); - var it = Iterator2(i32).init(.{ - .min = .{ min_y_vx, min_z_vx }, - .max = .{ max_y_vx, max_z_vx }, - }); - while (it.next()) |yz_vx| { - const y_vx, const z_vx = yz_vx; - if (!isSolid(chunks, .init(x_vx, y_vx, z_vx))) continue; - - const dx = x_sv - x0_sv; - const fdx: f32 = @floatFromInt(dx); - const fdy: f32 = fdx * fdydx; - - hit = .{ - .projected_distance_sv = ray_sv.getX() - dx, - .normal_frac = .unit_nx_frac, - }; - hit_distance_squared = fdx * fdx + fdy * fdy; - std.debug.print("+X fdx={d} fdy={d} dist2={d}\n", .{ fdx, fdy, hit_distance_squared }); - - break :px; - } - } - } - - // Negative X - if (ray_sv.getX() < 0) { - const x0_sv = min_origin_sv.getX(); - const y0_sv = min_origin_sv.getY(); - const y1_sv = max_origin_sv.getY(); - - const start_x_vx = c.subvoxelsToVoxels(.border_up, x0_sv) - 1; - const end_x_vx = c.subvoxelsToVoxels(.border_up, x0_sv + ray_sv.getX()); - - var x_vx: i32 = start_x_vx; - nx: while (x_vx >= end_x_vx) : (x_vx -= 1) { - const x_sv = c.voxelsToSubvoxels(x_vx + 1); - const min_y_vx = c.subvoxelsToVoxels(.border_up, wideMulDivFloor(x_sv - x0_sv, ray_sv.getY(), ray_sv.getX()) + y0_sv); - const max_y_vx = c.subvoxelsToVoxels(.border_down, wideMulDivCeil(x_sv - x0_sv, ray_sv.getY(), ray_sv.getX()) + y1_sv); - var it = Iterator2(i32).init(.{ - .min = .{ min_y_vx, min_z_vx }, - .max = .{ max_y_vx, max_z_vx }, - }); - while (it.next()) |yz_vx| { - const y_vx, const z_vx = yz_vx; - if (!isSolid(chunks, .init(x_vx, y_vx, z_vx))) continue; - - const dx = x_sv - x0_sv; - const fdx: f32 = @floatFromInt(dx); - const fdy: f32 = fdx * fdydx; - - hit = .{ - .projected_distance_sv = -(ray_sv.getX() - dx), - .normal_frac = .unit_x_frac, - }; - hit_distance_squared = fdx * fdx + fdy * fdy; - std.debug.print("-X fdx={d} fdy={d} dist2={d}\n", .{ fdx, fdy, hit_distance_squared }); - - break :nx; - } - } - } - - // Positive Y - if (ray_sv.getY() > 0) { - const y0_sv = max_origin_sv.getY(); - const x0_sv = min_origin_sv.getX(); - const x1_sv = max_origin_sv.getX(); - - const start_y_vx = c.subvoxelsToVoxels(.border_down, y0_sv) + 1; - const end_y_vx = c.subvoxelsToVoxels(.border_down, y0_sv + ray_sv.getY()); - - var y_vx = start_y_vx; - py: while (y_vx <= end_y_vx) : (y_vx += 1) { - const y_sv = c.voxelsToSubvoxels(y_vx); - const min_x_vx = c.subvoxelsToVoxels(.border_up, wideMulDivFloor(y_sv - y0_sv, ray_sv.getX(), ray_sv.getY()) + x0_sv); - const max_x_vx = c.subvoxelsToVoxels(.border_down, wideMulDivCeil(y_sv - y0_sv, ray_sv.getX(), ray_sv.getY()) + x1_sv); - var it = Iterator2(i32).init(.{ - .min = .{ min_x_vx, min_z_vx }, - .max = .{ max_x_vx, max_z_vx }, - }); - while (it.next()) |xz_vx| { - const x_vx, const z_vx = xz_vx; - if (!isSolid(chunks, .init(x_vx, y_vx, z_vx))) continue; - - const dy = y_sv - y0_sv; - const fdy: f32 = @floatFromInt(dy); - const fdx: f32 = fdy * fdxdy; - - const this_hit_distance_squared = fdx * fdx + fdy * fdy; - std.debug.print("+Y fdx={d} fdy={d} dist2={d}\n", .{ fdx, fdy, this_hit_distance_squared }); - if (this_hit_distance_squared < hit_distance_squared) { - hit = .{ - .projected_distance_sv = ray_sv.getY() - dy, - .normal_frac = .unit_ny_frac, - }; - hit_distance_squared = this_hit_distance_squared; - } - - break :py; - } - } - } - - // Negative Y - if (ray_sv.getY() < 0.0) { - const y0_sv = min_origin_sv.getY(); - const x0_sv = min_origin_sv.getX(); - const x1_sv = max_origin_sv.getX(); - - const start_y_vx = c.subvoxelsToVoxels(.border_up, y0_sv) - 1; - const end_y_vx = c.subvoxelsToVoxels(.border_up, y0_sv + ray_sv.getY()); - - var y_vx = start_y_vx; - ny: while (y_vx >= end_y_vx) : (y_vx -= 1) { - const y_sv = c.voxelsToSubvoxels(y_vx + 1); - const min_x_vx = c.subvoxelsToVoxels(.border_up, wideMulDivFloor(y_sv - y0_sv, ray_sv.getX(), ray_sv.getY()) + x0_sv); - const max_x_vx = c.subvoxelsToVoxels(.border_down, wideMulDivCeil(y_sv - y0_sv, ray_sv.getX(), ray_sv.getY()) + x1_sv); - var it = Iterator2(i32).init(.{ - .min = .{ min_x_vx, min_z_vx }, - .max = .{ max_x_vx, max_z_vx }, - }); - while (it.next()) |xz_vx| { - const x_vx, const z_vx = xz_vx; - if (!isSolid(chunks, .init(x_vx, y_vx, z_vx))) continue; - - const dy = y_sv - y0_sv; - const fdy: f32 = @floatFromInt(dy); - const fdx: f32 = fdy * fdxdy; - - const this_hit_distance_squared = fdx * fdx + fdy * fdy; - std.debug.print("-Y fdx={d} fdy={d} dist2={d}\n", .{ fdx, fdy, this_hit_distance_squared }); - if (this_hit_distance_squared < hit_distance_squared) { - hit = .{ - .projected_distance_sv = -(ray_sv.getY() - dy), - .normal_frac = .unit_y_frac, - }; - hit_distance_squared = this_hit_distance_squared; - } - - break :ny; - } - } - } - - return hit; -} - fn resetAllButtons(self: *Player) void { inline for (@typeInfo(Player).@"struct".fields) |field| { if (field.type == Button) { @@ -570,40 +331,6 @@ fn resetAllButtons(self: *Player) void { } } -fn getVoxelAt(chunks: *const std.AutoHashMapUnmanaged([3]i16, Chunk), vx: Vector3Int) ?Blocks.Id { - const min_ck = Vector3Int.initScalar(std.math.minInt(i16)); - const max_ck = Vector3Int.initScalar(std.math.maxInt(i16)); - const ck = vx.divScalar(c.vx_per_ck); - if (@reduce(.Or, (ck.vector < min_ck.vector) | (ck.vector > max_ck.vector))) { - return null; - } - - if (chunks.get(.{ - @intCast(ck.getX()), - @intCast(ck.getY()), - @intCast(ck.getZ()), - })) |chunk| { - const ckvx = vx.modScalar(c.vx_per_ck); - return chunk.blocks[@intCast(ckvx.getZ())][@intCast(ckvx.getY())][@intCast(ckvx.getX())]; - } else { - return .air; - } -} - -fn isSolid(chunks: *const std.AutoHashMapUnmanaged([3]i16, Chunk), vx: Vector3Int) bool { - const maybe_id = getVoxelAt(chunks, vx); - // NOTE `null` is considered solid, as it's out of bounds. - return maybe_id != .air; -} - -inline fn wideMulDivFloor(a: i32, mul: i32, div: i32) i32 { - return @intCast(@divFloor(@as(i64, a) * @as(i64, mul), div)); -} - -inline fn wideMulDivCeil(a: i32, mul: i32, div: i32) i32 { - return @intCast(@divFloor(@as(i64, a) * @as(i64, mul) + @as(i64, div) - 1, div)); -} - inline fn sv(comptime vx: comptime_float) comptime_int { return @intFromFloat(@round(vx * c.sv_per_vx)); } diff --git a/src/const.zig b/src/const.zig index 83cf2f6..083a662 100644 --- a/src/const.zig +++ b/src/const.zig @@ -46,7 +46,7 @@ pub const RoundingMode = enum { /// SV to VX pub inline fn subvoxelsToVoxels(comptime rounding_mode: RoundingMode, sv: i32) i32 { return switch (rounding_mode) { - .border_down => @divFloor(sv, sv_per_vx - 1), + .border_down => @divFloor(sv - 1, sv_per_vx), .border_up => @divFloor(sv, sv_per_vx), }; } @@ -54,7 +54,7 @@ pub inline fn subvoxelsToVoxels(comptime rounding_mode: RoundingMode, sv: i32) i /// SV to CK pub inline fn subvoxelsToChunks(comptime rounding_mode: RoundingMode, sv: i32) i32 { return switch (rounding_mode) { - .border_down => @divFloor(sv, sv_per_ck - 1), + .border_down => @divFloor(sv - 1, sv_per_ck), .border_up => @divFloor(sv, sv_per_ck), }; } @@ -77,7 +77,7 @@ pub inline fn voxelsToSubvoxels(vx: i32) i32 { /// VX to CK pub inline fn voxelsToChunks(comptime rounding_mode: RoundingMode, vx: i32) i32 { return switch (rounding_mode) { - .border_down => @divFloor(vx, vx_per_ck - 1), + .border_down => @divFloor(vx - 1, vx_per_ck), .border_up => @divFloor(vx, vx_per_ck), }; } diff --git a/src/math.zig b/src/math.zig index e0c6b65..4ba09a2 100644 --- a/src/math.zig +++ b/src/math.zig @@ -73,3 +73,11 @@ pub inline fn mulFracFrac(a: i32, b: i32) i32 { const b_wide: i64 = b; return .{ .vector = @intCast(@divFloor(a_wide * b_wide + rounding_bias, denominator)) }; } + +pub inline fn wideMulDivFloor(a: i32, mul: i32, div: i32) i32 { + return @intCast(@divFloor(@as(i64, a) * @as(i64, mul), div)); +} + +pub inline fn wideMulDivCeil(a: i32, mul: i32, div: i32) i32 { + return @intCast(@divFloor(@as(i64, a) * @as(i64, mul) + @as(i64, div) - 1, div)); +}