From d96946d1a06cf4c1b544578e5689c3241147ee41 Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Wed, 7 Jan 2026 14:19:49 +0100 Subject: [PATCH] BLOK DISTROJ --- castle | 2 +- src/Chunks.zig | 502 +++++++++++++++++++++++++++++++++++++------------ src/Game.zig | 3 +- src/Player.zig | 41 +++- 4 files changed, 414 insertions(+), 134 deletions(-) diff --git a/castle b/castle index 6c9a786..31651dc 160000 --- a/castle +++ b/castle @@ -1 +1 @@ -Subproject commit 6c9a786926ce0cebf94961bf70245c26e37f6ff7 +Subproject commit 31651dc96a9721db11099aba34dae18d98be46d1 diff --git a/src/Chunks.zig b/src/Chunks.zig index d6b62d0..7726117 100644 --- a/src/Chunks.zig +++ b/src/Chunks.zig @@ -13,11 +13,15 @@ const Iterator2 = math.Iterator2; chunks: std.AutoHashMapUnmanaged([3]i16, Chunk), -const RaycastHit = struct { +pub const SweepHit = struct { normal_frac: vm.Vector3Int, projected_distance_sv: i32, }; +pub const RaycastHit = struct { + voxel: vm.Vector3Int, +}; + 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| { @@ -48,13 +52,116 @@ pub fn getVoxelAt(self: *const Chunks, vx: vm.Vector3Int) ?Blocks.Id { } } +pub fn destroyVoxelAt(self: *const Chunks, vx: vm.Vector3Int, engine: *Engine, blocks: *const Blocks, allocator: std.mem.Allocator) !void { + const min_ck = vm.Vector3Int.initScalar(std.math.minInt(i16)); + const max_ck = vm.Vector3Int.initScalar(std.math.maxInt(i16)); + const ck = vx.divScalar(c.vx_per_ck); + if ((ck.x < min_ck.x) | (ck.y < min_ck.y) | (ck.z < min_ck.z) | (ck.x > max_ck.x) | (ck.y > max_ck.y) | (ck.z > max_ck.z)) { + @branchHint(.unlikely); + return; + } + + const x: i16 = @intCast(ck.x); + const y: i16 = @intCast(ck.y); + const z: i16 = @intCast(ck.z); + + if (self.chunks.getPtr(.{ x, y, z })) |chunk| { + const ckvx = vx.modScalar(c.vx_per_ck); + const block = &chunk.blocks[@intCast(ckvx.z)][@intCast(ckvx.y)][@intCast(ckvx.x)]; + if (block.* != .air) { + block.* = .air; + + try chunk.refresh(engine, blocks, .{ + .positive_x = self.chunks.getPtr(.{ x + 1, y, z }), + .negative_x = self.chunks.getPtr(.{ x - 1, y, z }), + .positive_y = self.chunks.getPtr(.{ x, y + 1, z }), + .negative_y = self.chunks.getPtr(.{ x, y - 1, z }), + .positive_z = self.chunks.getPtr(.{ x, y, z + 1 }), + .negative_z = self.chunks.getPtr(.{ x, y, z - 1 }), + }, allocator); + + if (ckvx.x == 0) { + if (self.chunks.getPtr(.{ x - 1, y, z })) |neighbor| { + try neighbor.refresh(engine, blocks, .{ + .positive_x = self.chunks.getPtr(.{ x, y, z }), + .negative_x = self.chunks.getPtr(.{ x - 2, y, z }), + .positive_y = self.chunks.getPtr(.{ x - 1, y + 1, z }), + .negative_y = self.chunks.getPtr(.{ x - 1, y - 1, z }), + .positive_z = self.chunks.getPtr(.{ x - 1, y, z + 1 }), + .negative_z = self.chunks.getPtr(.{ x - 1, y, z - 1 }), + }, allocator); + } + } else if (ckvx.x == c.vx_per_ck - 1) { + if (self.chunks.getPtr(.{ x + 1, y, z })) |neighbor| { + try neighbor.refresh(engine, blocks, .{ + .positive_x = self.chunks.getPtr(.{ x + 2, y, z }), + .negative_x = self.chunks.getPtr(.{ x, y, z }), + .positive_y = self.chunks.getPtr(.{ x + 1, y + 1, z }), + .negative_y = self.chunks.getPtr(.{ x + 1, y - 1, z }), + .positive_z = self.chunks.getPtr(.{ x + 1, y, z + 1 }), + .negative_z = self.chunks.getPtr(.{ x + 1, y, z - 1 }), + }, allocator); + } + } + + if (ckvx.y == 0) { + if (self.chunks.getPtr(.{ x, y - 1, z })) |neighbor| { + try neighbor.refresh(engine, blocks, .{ + .positive_x = self.chunks.getPtr(.{ x + 1, y - 1, z }), + .negative_x = self.chunks.getPtr(.{ x - 1, y - 1, z }), + .positive_y = self.chunks.getPtr(.{ x, y, z }), + .negative_y = self.chunks.getPtr(.{ x, y - 2, z }), + .positive_z = self.chunks.getPtr(.{ x, y - 1, z + 1 }), + .negative_z = self.chunks.getPtr(.{ x, y - 1, z - 1 }), + }, allocator); + } + } else if (ckvx.y == c.vx_per_ck - 1) { + if (self.chunks.getPtr(.{ x, y + 1, z })) |neighbor| { + try neighbor.refresh(engine, blocks, .{ + .positive_x = self.chunks.getPtr(.{ x + 1, y + 1, z }), + .negative_x = self.chunks.getPtr(.{ x - 1, y + 1, z }), + .positive_y = self.chunks.getPtr(.{ x, y + 2, z }), + .negative_y = self.chunks.getPtr(.{ x, y, z }), + .positive_z = self.chunks.getPtr(.{ x, y + 1, z + 1 }), + .negative_z = self.chunks.getPtr(.{ x, y + 1, z - 1 }), + }, allocator); + } + } + + if (ckvx.z == 0) { + if (self.chunks.getPtr(.{ x, y, z - 1 })) |neighbor| { + try neighbor.refresh(engine, blocks, .{ + .positive_x = self.chunks.getPtr(.{ x + 1, y, z - 1 }), + .negative_x = self.chunks.getPtr(.{ x - 1, y, z - 1 }), + .positive_y = self.chunks.getPtr(.{ x, y + 1, z - 1 }), + .negative_y = self.chunks.getPtr(.{ x, y - 1, z - 1 }), + .positive_z = self.chunks.getPtr(.{ x, y, z }), + .negative_z = self.chunks.getPtr(.{ x, y, z - 2 }), + }, allocator); + } + } else if (ckvx.z == c.vx_per_ck - 1) { + if (self.chunks.getPtr(.{ x, y, z + 1 })) |neighbor| { + try neighbor.refresh(engine, blocks, .{ + .positive_x = self.chunks.getPtr(.{ x + 1, y, z + 1 }), + .negative_x = self.chunks.getPtr(.{ x - 1, y, z + 1 }), + .positive_y = self.chunks.getPtr(.{ x, y + 1, z + 1 }), + .negative_y = self.chunks.getPtr(.{ x, y - 1, z + 1 }), + .positive_z = self.chunks.getPtr(.{ x, y, z + 2 }), + .negative_z = self.chunks.getPtr(.{ x, y, z }), + }, allocator); + } + } + } + } +} + pub fn isSolid(self: *const Chunks, vx: vm.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: vm.Vector3Int, max_sv: vm.Vector3Int, distance_sv: i32) ?RaycastHit { +pub fn sweepCastDown(self: *const Chunks, min_sv: vm.Vector3Int, max_sv: vm.Vector3Int, distance_sv: i32) ?SweepHit { const min_x_vx = c.subvoxelsToVoxels(.border_up, min_sv.x); const min_y_vx = c.subvoxelsToVoxels(.border_up, min_sv.y); const max_x_vx = c.subvoxelsToVoxels(.border_down, max_sv.x); @@ -84,7 +191,7 @@ pub fn sweepCastDown(self: *const Chunks, min_sv: vm.Vector3Int, max_sv: vm.Vect return null; } -pub fn sweepCastUp(self: *const Chunks, min_sv: vm.Vector3Int, max_sv: vm.Vector3Int, distance_sv: i32) ?RaycastHit { +pub fn sweepCastUp(self: *const Chunks, min_sv: vm.Vector3Int, max_sv: vm.Vector3Int, distance_sv: i32) ?SweepHit { const min_x_vx = c.subvoxelsToVoxels(.border_up, min_sv.x); const min_y_vx = c.subvoxelsToVoxels(.border_up, min_sv.y); const max_x_vx = c.subvoxelsToVoxels(.border_down, max_sv.x); @@ -114,11 +221,11 @@ pub fn sweepCastUp(self: *const Chunks, min_sv: vm.Vector3Int, max_sv: vm.Vector return null; } -pub fn sweepCastHorizontal(self: *const Chunks, min_sv: vm.Vector3Int, max_sv: vm.Vector3Int, ray_sv: vm.Vector2Int) ?RaycastHit { +pub fn sweepCastHorizontal(self: *const Chunks, min_sv: vm.Vector3Int, max_sv: vm.Vector3Int, ray_sv: vm.Vector2Int) ?SweepHit { const min_z_vx = c.subvoxelsToVoxels(.border_up, min_sv.z); const max_z_vx = c.subvoxelsToVoxels(.border_down, max_sv.z); - var hit: ?RaycastHit = null; + var hit: ?SweepHit = null; var hit_distance_squared = std.math.inf(f32); const fdydx: f32 = @as(f32, @floatFromInt(ray_sv.y)) / @as(f32, @floatFromInt(ray_sv.x)); @@ -288,20 +395,20 @@ pub fn sweepCastHorizontal(self: *const Chunks, min_sv: vm.Vector3Int, max_sv: v pub fn raycast(self: *const Chunks, origin_sv: vm.Vector3Int, ray_sv: vm.Vector3Int) ?RaycastHit { const end_sv = origin_sv.add(ray_sv); - std.debug.print("Raycast from ({d}:{X}, {d}:{X}, {d}:{X}) to ({d}:{X}, {d}:{X}, {d}:{X})\n", .{ - c.subvoxelsToVoxels(.border_up, origin_sv.x), - c.subvoxelsToVoxelSubvoxels(origin_sv.x), - c.subvoxelsToVoxels(.border_up, origin_sv.y), - c.subvoxelsToVoxelSubvoxels(origin_sv.y), - c.subvoxelsToVoxels(.border_up, origin_sv.z), - c.subvoxelsToVoxelSubvoxels(origin_sv.z), - c.subvoxelsToVoxels(.border_up, end_sv.x), - c.subvoxelsToVoxelSubvoxels(end_sv.x), - c.subvoxelsToVoxels(.border_up, end_sv.y), - c.subvoxelsToVoxelSubvoxels(end_sv.y), - c.subvoxelsToVoxels(.border_up, end_sv.z), - c.subvoxelsToVoxelSubvoxels(end_sv.z), - }); + //std.debug.print("Raycast from ({d}:{X}, {d}:{X}, {d}:{X}) to ({d}:{X}, {d}:{X}, {d}:{X})\n", .{ + // c.subvoxelsToVoxels(.border_up, origin_sv.x), + // c.subvoxelsToVoxelSubvoxels(origin_sv.x), + // c.subvoxelsToVoxels(.border_up, origin_sv.y), + // c.subvoxelsToVoxelSubvoxels(origin_sv.y), + // c.subvoxelsToVoxels(.border_up, origin_sv.z), + // c.subvoxelsToVoxelSubvoxels(origin_sv.z), + // c.subvoxelsToVoxels(.border_up, end_sv.x), + // c.subvoxelsToVoxelSubvoxels(end_sv.x), + // c.subvoxelsToVoxels(.border_up, end_sv.y), + // c.subvoxelsToVoxelSubvoxels(end_sv.y), + // c.subvoxelsToVoxels(.border_up, end_sv.z), + // c.subvoxelsToVoxelSubvoxels(end_sv.z), + //}); const ray_positive = vm.Vector3Int.init( @intFromBool(ray_sv.x > 0), @@ -319,37 +426,38 @@ pub fn raycast(self: *const Chunks, origin_sv: vm.Vector3Int, ray_sv: vm.Vector3 const end_vx = end_sv.sub(ray_positive).divScalar(c.sv_per_vx); const abs_ray_sv = ray_sv.abs(); - std.debug.print("Start [VX]: ({d}, {d}, {d}) | End [VX]: ({d}, {d}, {d})\n", .{ - start_vx.x, - start_vx.y, - start_vx.z, - end_vx.x, - end_vx.y, - end_vx.z, - }); + //std.debug.print("Start [VX]: ({d}, {d}, {d}) | End [VX]: ({d}, {d}, {d})\n", .{ + // start_vx.x, + // start_vx.y, + // start_vx.z, + // end_vx.x, + // end_vx.y, + // end_vx.z, + //}); - var i_index: usize = undefined; - var j_index: usize = undefined; - var k_index: usize = undefined; + const i_index: usize, const j_index: usize, const k_index: usize = blk: { + if (abs_ray_sv.x > abs_ray_sv.y and abs_ray_sv.x > abs_ray_sv.z) { + // Main axis: ±X + //std.debug.print("Order is (X, Y, Z)\n", .{}); + break :blk .{ 0, 1, 2 }; + } else if (abs_ray_sv.y > abs_ray_sv.z) { + // Main axis: ±Y + //std.debug.print("Order is (Y, Z, X)\n", .{}); + break :blk .{ 1, 2, 0 }; + } else if (abs_ray_sv.z > 0) { + // Main axis: ±Z + //std.debug.print("Order is (Z, X, Y)\n", .{}); + break :blk .{ 2, 0, 1 }; + } else { + std.debug.assert(abs_ray_sv.x == 0); + std.debug.assert(abs_ray_sv.y == 0); + std.debug.assert(abs_ray_sv.z == 0); + return null; + } + }; - if (abs_ray_sv.x > abs_ray_sv.y and abs_ray_sv.x > abs_ray_sv.z) { - // Main axis: ±X - i_index, j_index, k_index = .{ 0, 1, 2 }; - std.debug.print("Order is (X, Y, Z)\n", .{}); - } else if (abs_ray_sv.y > abs_ray_sv.z) { - // Main axis: ±Y - i_index, j_index, k_index = .{ 1, 2, 0 }; - std.debug.print("Order is (Y, Z, X)\n", .{}); - } else if (abs_ray_sv.z > 0) { - // Main axis: ±Z - i_index, j_index, k_index = .{ 2, 0, 1 }; - std.debug.print("Order is (Z, X, Y)\n", .{}); - } else { - std.debug.assert(abs_ray_sv.x == 0); - std.debug.assert(abs_ray_sv.y == 0); - std.debug.assert(abs_ray_sv.z == 0); - return null; - } + var ind_buf: [3]usize = undefined; + var current_vx: [3]i32 = undefined; const oi = origin_sv.asArray()[i_index]; const oj = origin_sv.asArray()[j_index]; @@ -359,42 +467,70 @@ pub fn raycast(self: *const Chunks, origin_sv: vm.Vector3Int, ray_sv: vm.Vector3 const dj = ray_sv.asArray()[j_index]; const dk = ray_sv.asArray()[k_index]; + const rpi = ray_positive.asArray()[i_index]; + const rpj = ray_positive.asArray()[j_index]; + const rpk = ray_positive.asArray()[k_index]; + + const rni = ray_negative.asArray()[i_index]; + const rnj = ray_negative.asArray()[j_index]; + const rnk = ray_negative.asArray()[k_index]; + var i_vx = start_vx.asArray()[i_index]; var enter_i_sv = oi; var enter_di_sv = enter_i_sv - oi; - var enter_jdi_sv = oj * di + enter_di_sv * dj; - var enter_kdi_sv = ok * di + enter_di_sv * dk; + var enter_jdi_sv = @as(i64, oj) * @as(i64, di) + @as(i64, enter_di_sv) * @as(i64, dj); + var enter_kdi_sv = @as(i64, ok) * @as(i64, di) + @as(i64, enter_di_sv) * @as(i64, dk); - var enter_i_vx = @divFloor(enter_i_sv - ray_positive.asArray()[i_index], c.sv_per_vx); - var enter_j_vx = @divFloor(enter_jdi_sv - ray_positive.asArray()[j_index], c.sv_per_vx * di); - var enter_k_vx = @divFloor(enter_kdi_sv - ray_positive.asArray()[k_index], c.sv_per_vx * di); + var enter_i_vx: i32 = @divFloor(enter_i_sv - rpi, c.sv_per_vx); + var enter_j_vx: i32 = @intCast(@divFloor(enter_jdi_sv - rpj, c.sv_per_vx * @as(i64, di))); + var enter_k_vx: i32 = @intCast(@divFloor(enter_kdi_sv - rpk, c.sv_per_vx * @as(i64, di))); - var exit_i_sv = c.voxelsToSubvoxels(i_vx + ray_negative.asArray()[i_index]); + current_vx[i_index] = enter_i_vx; + current_vx[j_index] = enter_j_vx; + current_vx[k_index] = enter_k_vx; + + var exit_i_sv = c.voxelsToSubvoxels(i_vx + rni); var exit_di_sv = exit_i_sv - oi; - var exit_jdi_sv = oj * di + exit_di_sv * dj; - var exit_kdi_sv = ok * di + exit_di_sv * dk; + var exit_jdi_sv = @as(i64, oj) * @as(i64, di) + @as(i64, exit_di_sv) * @as(i64, dj); + var exit_kdi_sv = @as(i64, ok) * @as(i64, di) + @as(i64, exit_di_sv) * @as(i64, dk); - var exit_i_vx = @divFloor(exit_i_sv - ray_positive.asArray()[i_index], c.sv_per_vx); - var exit_j_vx = @divFloor(exit_jdi_sv - ray_positive.asArray()[j_index], c.sv_per_vx * di); - var exit_k_vx = @divFloor(exit_kdi_sv - ray_positive.asArray()[k_index], c.sv_per_vx * di); + var exit_i_vx: i32 = @divFloor(exit_i_sv - rpi, c.sv_per_vx); + var exit_j_vx: i32 = @intCast(@divFloor(exit_jdi_sv - rpj, c.sv_per_vx * @as(i64, di))); + var exit_k_vx: i32 = @intCast(@divFloor(exit_kdi_sv - rpk, c.sv_per_vx * @as(i64, di))); - std.debug.print("({d}:{X}, {d:.3}, {d:.3}) → ({d}:{X}, {d:.3}, {d:.3}) | ({d}, {d}, {d}) → ({d}, {d}, {d})\n", .{ - c.subvoxelsToVoxels(.border_up, enter_i_sv), - c.subvoxelsToVoxelSubvoxels(enter_i_sv), - @as(f64, @floatFromInt(enter_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), - @as(f64, @floatFromInt(enter_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), - c.subvoxelsToVoxels(.border_up, exit_i_sv), - c.subvoxelsToVoxelSubvoxels(exit_i_sv), - @as(f64, @floatFromInt(exit_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), - @as(f64, @floatFromInt(exit_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), - enter_i_vx, - enter_j_vx, - enter_k_vx, - exit_i_vx, - exit_j_vx, - exit_k_vx, - }); + var cross_i = vm.unlerpInt(enter_i_sv, exit_i_sv, c.voxelsToSubvoxels(exit_i_vx + rni)); + var cross_j = vm.unlerpInt64(enter_jdi_sv, exit_jdi_sv, c.voxelsToSubvoxels(exit_j_vx + rnj) * @as(i64, di)); + var cross_k = vm.unlerpInt64(enter_kdi_sv, exit_kdi_sv, c.voxelsToSubvoxels(exit_k_vx + rnk) * @as(i64, di)); + + //std.debug.print("({d}:{X:0>3}, {d:.3}, {d:.3}) → ({d}:{X:0>3}, {d:.3}, {d:.3}) | ({d}, {d}, {d}) → ({d}, {d}, {d}) | × ({d:.3}, {d:.3}, {d:.3})\n", .{ + // c.subvoxelsToVoxels(.border_up, enter_i_sv), + // c.subvoxelsToVoxelSubvoxels(enter_i_sv), + // @as(f64, @floatFromInt(enter_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + // @as(f64, @floatFromInt(enter_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + // c.subvoxelsToVoxels(.border_up, exit_i_sv), + // c.subvoxelsToVoxelSubvoxels(exit_i_sv), + // @as(f64, @floatFromInt(exit_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + // @as(f64, @floatFromInt(exit_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + // enter_i_vx, + // enter_j_vx, + // enter_k_vx, + // exit_i_vx, + // exit_j_vx, + // exit_k_vx, + // cross_i, + // cross_j, + // cross_k, + //}); + + var cross_indices = order3(i_index, j_index, k_index, cross_i, cross_j, cross_k, &ind_buf); + for (cross_indices) |cross_index| { + current_vx[cross_index] += ray_sign.asArray()[cross_index]; + const voxel = vm.Vector3Int.initArray(current_vx); + if (self.isSolid(voxel)) { + return .{ .voxel = voxel }; + } + } while (i_vx != end_vx.asArray()[i_index]) : (i_vx += ray_sign.asArray()[i_index]) { enter_i_sv = exit_i_sv; @@ -406,31 +542,47 @@ pub fn raycast(self: *const Chunks, origin_sv: vm.Vector3Int, ray_sv: vm.Vector3 enter_j_vx = exit_j_vx; enter_k_vx = exit_k_vx; - exit_i_sv = c.voxelsToSubvoxels(i_vx + ray_positive.asArray()[i_index]); + exit_i_sv = c.voxelsToSubvoxels(i_vx + rpi); exit_di_sv = exit_i_sv - oi; - exit_jdi_sv = oj * di + exit_di_sv * dj; - exit_kdi_sv = ok * di + exit_di_sv * dk; + exit_jdi_sv = @as(i64, oj) * @as(i64, di) + @as(i64, exit_di_sv) * @as(i64, dj); + exit_kdi_sv = @as(i64, ok) * @as(i64, di) + @as(i64, exit_di_sv) * @as(i64, dk); - exit_i_vx = @divFloor(exit_i_sv - ray_positive.asArray()[i_index], c.sv_per_vx); - exit_j_vx = @divFloor(exit_jdi_sv - ray_positive.asArray()[j_index], c.sv_per_vx * di); - exit_k_vx = @divFloor(exit_kdi_sv - ray_positive.asArray()[k_index], c.sv_per_vx * di); + exit_i_vx = @divFloor(exit_i_sv - rpi, c.sv_per_vx); + exit_j_vx = @intCast(@divFloor(exit_jdi_sv - rpj, c.sv_per_vx * @as(i64, di))); + exit_k_vx = @intCast(@divFloor(exit_kdi_sv - rpk, c.sv_per_vx * @as(i64, di))); - std.debug.print("({d}:{X}, {d:.3}, {d:.3}) → ({d}:{X}, {d:.3}, {d:.3}) | ({d}, {d}, {d}) → ({d}, {d}, {d})\n", .{ - c.subvoxelsToVoxels(.border_up, enter_i_sv), - c.subvoxelsToVoxelSubvoxels(enter_i_sv), - @as(f64, @floatFromInt(enter_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), - @as(f64, @floatFromInt(enter_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), - c.subvoxelsToVoxels(.border_up, exit_i_sv), - c.subvoxelsToVoxelSubvoxels(exit_i_sv), - @as(f64, @floatFromInt(exit_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), - @as(f64, @floatFromInt(exit_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), - enter_i_vx, - enter_j_vx, - enter_k_vx, - exit_i_vx, - exit_j_vx, - exit_k_vx, - }); + cross_i = vm.unlerpInt(enter_i_sv, exit_i_sv, c.voxelsToSubvoxels(exit_i_vx + rni)); + cross_j = vm.unlerpInt64(enter_jdi_sv, exit_jdi_sv, c.voxelsToSubvoxels(exit_j_vx + rnj) * @as(i64, di)); + cross_k = vm.unlerpInt64(enter_kdi_sv, exit_kdi_sv, c.voxelsToSubvoxels(exit_k_vx + rnk) * @as(i64, di)); + + //std.debug.print("({d}:{X:0>3}, {d:.3}, {d:.3}) → ({d}:{X:0>3}, {d:.3}, {d:.3}) | ({d}, {d}, {d}) → ({d}, {d}, {d}) | × ({d:.3}, {d:.3}, {d:.3})\n", .{ + // c.subvoxelsToVoxels(.border_up, enter_i_sv), + // c.subvoxelsToVoxelSubvoxels(enter_i_sv), + // @as(f64, @floatFromInt(enter_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + // @as(f64, @floatFromInt(enter_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + // c.subvoxelsToVoxels(.border_up, exit_i_sv), + // c.subvoxelsToVoxelSubvoxels(exit_i_sv), + // @as(f64, @floatFromInt(exit_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + // @as(f64, @floatFromInt(exit_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + // enter_i_vx, + // enter_j_vx, + // enter_k_vx, + // exit_i_vx, + // exit_j_vx, + // exit_k_vx, + // cross_i, + // cross_j, + // cross_k, + //}); + + cross_indices = order3(i_index, j_index, k_index, cross_i, cross_j, cross_k, &ind_buf); + for (cross_indices) |cross_index| { + current_vx[cross_index] += ray_sign.asArray()[cross_index]; + const voxel = vm.Vector3Int.initArray(current_vx); + if (self.isSolid(voxel)) { + return .{ .voxel = voxel }; + } + } } enter_i_sv = exit_i_sv; @@ -444,33 +596,141 @@ pub fn raycast(self: *const Chunks, origin_sv: vm.Vector3Int, ray_sv: vm.Vector3 exit_i_sv = oi + di; exit_di_sv = exit_i_sv - oi; - exit_jdi_sv = oj * di + exit_di_sv * dj; - exit_kdi_sv = ok * di + exit_di_sv * dk; + exit_jdi_sv = @as(i64, oj) * @as(i64, di) + @as(i64, exit_di_sv) * @as(i64, dj); + exit_kdi_sv = @as(i64, ok) * @as(i64, di) + @as(i64, exit_di_sv) * @as(i64, dk); - exit_i_vx = @divFloor(exit_i_sv - ray_positive.asArray()[i_index], c.sv_per_vx); - exit_j_vx = @divFloor(exit_jdi_sv - ray_positive.asArray()[j_index], c.sv_per_vx * di); - exit_k_vx = @divFloor(exit_kdi_sv - ray_positive.asArray()[k_index], c.sv_per_vx * di); + exit_i_vx = @divFloor(exit_i_sv - rpi, c.sv_per_vx); + exit_j_vx = @intCast(@divFloor(exit_jdi_sv - rpj, c.sv_per_vx * @as(i64, di))); + exit_k_vx = @intCast(@divFloor(exit_kdi_sv - rpk, c.sv_per_vx * @as(i64, di))); - std.debug.print("({d}:{X}, {d:.3}, {d:.3}) → ({d}:{X}, {d:.3}, {d:.3}) | ({d}, {d}, {d}) → ({d}, {d}, {d})\n", .{ - c.subvoxelsToVoxels(.border_up, enter_i_sv), - c.subvoxelsToVoxelSubvoxels(enter_i_sv), - @as(f64, @floatFromInt(enter_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), - @as(f64, @floatFromInt(enter_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), - c.subvoxelsToVoxels(.border_up, exit_i_sv), - c.subvoxelsToVoxelSubvoxels(exit_i_sv), - @as(f64, @floatFromInt(exit_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), - @as(f64, @floatFromInt(exit_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), - enter_i_vx, - enter_j_vx, - enter_k_vx, - exit_i_vx, - exit_j_vx, - exit_k_vx, - }); + cross_i = vm.unlerpInt(enter_i_sv, exit_i_sv, c.voxelsToSubvoxels(exit_i_vx + rni)); + cross_j = vm.unlerpInt64(enter_jdi_sv, exit_jdi_sv, c.voxelsToSubvoxels(exit_j_vx + rnj) * @as(i64, di)); + cross_k = vm.unlerpInt64(enter_kdi_sv, exit_kdi_sv, c.voxelsToSubvoxels(exit_k_vx + rnk) * @as(i64, di)); - // XXX - std.debug.print("\n", .{}); + //std.debug.print("({d}:{X:0>3}, {d:.3}, {d:.3}) → ({d}:{X:0>3}, {d:.3}, {d:.3}) | ({d}, {d}, {d}) → ({d}, {d}, {d}) | × ({d:.3}, {d:.3}, {d:.3})\n", .{ + // c.subvoxelsToVoxels(.border_up, enter_i_sv), + // c.subvoxelsToVoxelSubvoxels(enter_i_sv), + // @as(f64, @floatFromInt(enter_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + // @as(f64, @floatFromInt(enter_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + // c.subvoxelsToVoxels(.border_up, exit_i_sv), + // c.subvoxelsToVoxelSubvoxels(exit_i_sv), + // @as(f64, @floatFromInt(exit_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + // @as(f64, @floatFromInt(exit_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + // enter_i_vx, + // enter_j_vx, + // enter_k_vx, + // exit_i_vx, + // exit_j_vx, + // exit_k_vx, + // cross_i, + // cross_j, + // cross_k, + //}); + + cross_indices = order3(i_index, j_index, k_index, cross_i, cross_j, cross_k, &ind_buf); + for (cross_indices) |cross_index| { + current_vx[cross_index] += ray_sign.asArray()[cross_index]; + const voxel = vm.Vector3Int.initArray(current_vx); + if (self.isSolid(voxel)) { + return .{ .voxel = voxel }; + } + } + + //std.debug.print("\n", .{}); - _ = self; return null; } + +fn order3(i: usize, j: usize, k: usize, cross_i: f32, cross_j: f32, cross_k: f32, buf: *[3]usize) []usize { + if (cross_i >= 0 and cross_i < 1) { + if (cross_j >= 0 and cross_j < 1) { + if (cross_k >= 0 and cross_k < 1) { + if (cross_i < cross_j and cross_i < cross_k) { + if (cross_j < cross_k) { + // Crossed I, J, K + buf.* = .{ i, j, k }; + return buf[0..3]; + } else { + // Crossed I, K, J + buf.* = .{ i, k, j }; + return buf[0..3]; + } + } else if (cross_j < cross_k) { + if (cross_i < cross_k) { + // Crossed J, I, K + buf.* = .{ j, i, k }; + return buf[0..3]; + } else { + // Crossed J, K, I + buf.* = .{ j, k, i }; + return buf[0..3]; + } + } else { + if (cross_i < cross_j) { + // Crossed K, I, J + buf.* = .{ k, i, j }; + return buf[0..3]; + } else { + // Crossed K, J, I + buf.* = .{ k, j, i }; + return buf[0..3]; + } + } + } else { + if (cross_i < cross_j) { + // Crossed I, J + buf.* = .{ i, j, undefined }; + return buf[0..2]; + } else { + // Crossed J, I + buf.* = .{ j, i, undefined }; + return buf[0..2]; + } + } + } else { + if (cross_k >= 0 and cross_k < 1) { + if (cross_i < cross_k) { + // Crossed I, K + buf.* = .{ i, k, undefined }; + return buf[0..2]; + } else { + // Crossed K, I + buf.* = .{ k, i, undefined }; + return buf[0..2]; + } + } else { + // Crossed I + buf.* = .{ i, undefined, undefined }; + return buf[0..1]; + } + } + } else { + if (cross_j >= 0 and cross_j < 1) { + if (cross_k >= 0 and cross_k < 1) { + if (cross_j < cross_k) { + // Crossed J, K + buf.* = .{ j, k, undefined }; + return buf[0..2]; + } else { + // Crossed K, J + buf.* = .{ k, j, undefined }; + return buf[0..2]; + } + } else { + // Crossed J + buf.* = .{ j, undefined, undefined }; + return buf[0..1]; + } + } else { + if (cross_k >= 0 and cross_k < 1) { + // Crossed K + buf.* = .{ k, undefined, undefined }; + return buf[0..1]; + } else { + // Crossed nothing + buf.* = undefined; + return buf[0..0]; + } + } + } +} diff --git a/src/Game.zig b/src/Game.zig index 277c4cc..11a3076 100644 --- a/src/Game.zig +++ b/src/Game.zig @@ -721,8 +721,7 @@ pub fn onMouseMove(self: *Game, dx: f32, dy: f32) void { } pub fn onMouseDown(self: *Game, button: glfw.MouseButton) void { - _ = self; - _ = button; + self.player.onMouseDown(button, self); } pub fn onMouseUp(self: *Game, button: glfw.MouseButton) void { diff --git a/src/Player.zig b/src/Player.zig index 1736001..3519300 100644 --- a/src/Player.zig +++ b/src/Player.zig @@ -8,6 +8,7 @@ const vm = @import("vecmath"); const Blocks = @import("assets/Blocks.zig"); const Chunks = @import("Chunks.zig"); +const Game = @import("Game.zig"); const Iterator2 = math.Iterator2; const AxisState = enum { @@ -175,6 +176,7 @@ mouse_sensitivity: f32 = 0.0003, vertical_fov_deg: f32 = 80, movement_state: MovementState, +maybe_raycast_hit: ?Chunks.RaycastHit = null, pub const camera_height_vx = 1.62; @@ -225,19 +227,38 @@ pub fn onMouseMove(self: *Player, dx: f32, dy: f32) void { self.yaw_turns = @mod(self.yaw_turns - dx * self.mouse_sensitivity, 1); } +pub fn onMouseDown(self: *Player, button: glfw.MouseButton, game: *Game) void { + if (self.maybe_raycast_hit) |raycast_hit| { + switch (button) { + .left => { + game.chunks.destroyVoxelAt( + raycast_hit.voxel, + game.engine, + &game.blocks, + game.allocator, + ) catch |err| { + std.log.err("Error while destroying voxel {f}: {}", .{ raycast_hit.voxel, err }); + }; + }, + else => {}, + } + } +} + pub fn update(self: *Player, dt: f32, chunks: *const Chunks) void { defer self.resetAllButtons(); - //const raycast_origin_sv = self.position_sv - // .add(.init(0, 0, @intFromFloat(@round(c.sv_per_vx * camera_height_vx)))); - //const raycast_ray_sv = vm.Vector3 - // .init(0, raycast_length_sv, 0) - // .rotate(.mulQuaternion( - // .initRotation(.XY, self.yaw_turns), - // .initRotation(.YZ, self.pitch_turns), - // )) - // .asVector3Int(); - //const raycast_hit = chunks.raycast(raycast_origin_sv, raycast_ray_sv); + const raycast_origin_sv = self.position_sv + .add(.init(0, 0, @intFromFloat(@round(c.sv_per_vx * camera_height_vx)))); + const raycast_ray_sv = vm.Vector3 + .init(0, raycast_length_sv, 0) + .rotate(.mulQuaternion( + .initRotation(.XY, self.yaw_turns), + .initRotation(.YZ, self.pitch_turns), + )) + .round() + .asInt(); + self.maybe_raycast_hit = chunks.raycast(raycast_origin_sv, raycast_ray_sv); // --- GATHER INPUTS -------------------------------------------------------