From 133994d2efea27478eb5defa986d4a47f37cf7c0 Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Thu, 8 Jan 2026 13:27:35 +0100 Subject: [PATCH] Block placing --- src/Chunks.zig | 234 +++++++++++++++++++++++++++++++------------------ src/Player.zig | 42 ++++++++- src/voxels.zig | 11 ++- 3 files changed, 198 insertions(+), 89 deletions(-) diff --git a/src/Chunks.zig b/src/Chunks.zig index 7726117..e922cff 100644 --- a/src/Chunks.zig +++ b/src/Chunks.zig @@ -5,6 +5,7 @@ const c = @import("const.zig"); const math = @import("math.zig"); const vk = @import("vulkan"); const vm = @import("vecmath"); +const voxels = @import("voxels.zig"); const Blocks = @import("assets/Blocks.zig"); const Chunk = @import("assets/Chunk.zig"); @@ -20,6 +21,7 @@ pub const SweepHit = struct { pub const RaycastHit = struct { voxel: vm.Vector3Int, + side: voxels.Orientation, }; pub fn deinit(self: *Chunks, engine: *Engine, descriptor_pool: vk.DescriptorPool, allocator: std.mem.Allocator) void { @@ -52,7 +54,16 @@ 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 { +pub fn setVoxelAt( + self: *Chunks, + vx: vm.Vector3Int, + id: Blocks.Id, + engine: *Engine, + blocks: *const Blocks, + descriptor_pool: vk.DescriptorPool, + per_batch_descriptor_set_layout: vk.DescriptorSetLayout, + 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); @@ -65,91 +76,91 @@ pub fn destroyVoxelAt(self: *const Chunks, vx: vm.Vector3Int, engine: *Engine, b 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; + const chunk = try self.getOrCreateChunk(ck, engine, descriptor_pool, per_batch_descriptor_set_layout, allocator); - 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); + const ckvx = vx.modScalar(c.vx_per_ck); + const block = &chunk.blocks[@intCast(ckvx.z)][@intCast(ckvx.y)][@intCast(ckvx.x)]; + if (block.* != id) { + block.* = id; - 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); - } + 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); } - - 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); - } + } 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.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); - } + 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); } } } @@ -525,10 +536,14 @@ pub fn raycast(self: *const Chunks, origin_sv: vm.Vector3Int, ray_sv: vm.Vector3 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 rs = ray_sign.asArray()[cross_index]; + current_vx[cross_index] += rs; const voxel = vm.Vector3Int.initArray(current_vx); if (self.isSolid(voxel)) { - return .{ .voxel = voxel }; + return .{ + .voxel = voxel, + .side = .initSignIndex(@intCast(-rs), @intCast(cross_index)), + }; } } @@ -577,10 +592,14 @@ pub fn raycast(self: *const Chunks, origin_sv: vm.Vector3Int, ray_sv: vm.Vector3 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 rs = ray_sign.asArray()[cross_index]; + current_vx[cross_index] += rs; const voxel = vm.Vector3Int.initArray(current_vx); if (self.isSolid(voxel)) { - return .{ .voxel = voxel }; + return .{ + .voxel = voxel, + .side = .initSignIndex(@intCast(-rs), @intCast(cross_index)), + }; } } } @@ -629,10 +648,14 @@ pub fn raycast(self: *const Chunks, origin_sv: vm.Vector3Int, ray_sv: vm.Vector3 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 rs = ray_sign.asArray()[cross_index]; + current_vx[cross_index] += rs; const voxel = vm.Vector3Int.initArray(current_vx); if (self.isSolid(voxel)) { - return .{ .voxel = voxel }; + return .{ + .voxel = voxel, + .side = .initSignIndex(@intCast(-rs), @intCast(cross_index)), + }; } } @@ -641,6 +664,43 @@ pub fn raycast(self: *const Chunks, origin_sv: vm.Vector3Int, ray_sv: vm.Vector3 return null; } +fn getOrCreateChunk( + self: *Chunks, + ck: vm.Vector3Int, + engine: *Engine, + descriptor_pool: vk.DescriptorPool, + per_batch_descriptor_set_layout: vk.DescriptorSetLayout, + allocator: std.mem.Allocator, +) !*Chunk { + const min_ck = vm.Vector3Int.initScalar(std.math.minInt(i16)); + const max_ck = vm.Vector3Int.initScalar(std.math.maxInt(i16)); + std.debug.assert((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)); + + const key = [_]i16{ + @intCast(ck.x), + @intCast(ck.y), + @intCast(ck.z), + }; + + const entry = try self.chunks.getOrPut(allocator, key); + + if (entry.found_existing) { + return entry.value_ptr; + } else { + errdefer _ = self.chunks.remove(key); + + const origin = ck.asFloat().mulScalar(c.vx_per_ck); + const chunk = try Chunk.init(engine, .{ + .origin = origin, + .descriptor_pool = descriptor_pool, + .per_batch_descriptor_set_layout = per_batch_descriptor_set_layout, + }); + + entry.value_ptr.* = chunk; + return entry.value_ptr; + } +} + 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) { diff --git a/src/Player.zig b/src/Player.zig index 3519300..74e19fb 100644 --- a/src/Player.zig +++ b/src/Player.zig @@ -177,6 +177,19 @@ vertical_fov_deg: f32 = 80, movement_state: MovementState, maybe_raycast_hit: ?Chunks.RaycastHit = null, +block_index: usize = 0, + +pub const blocks = [9][:0]const u8{ + "Bricks.json", + "ChiseledStoneBricks.json", + "Cobblestone.json", + "DiamondBlock.json", + "GoldBlock.json", + "IronBlock.json", + "OakPlanks.json", + "SmoothStone.json", + "StoneBricks.json", +}; pub const camera_height_vx = 1.62; @@ -214,6 +227,12 @@ pub fn onKeyDown(self: *Player, key: glfw.Key) void { self.x_input.onKeyDown(key); self.y_input.onKeyDown(key); self.z_input.onKeyDown(key); + + const key_code = @intFromEnum(key); + if (key_code >= @intFromEnum(glfw.Key.one) and key_code <= @intFromEnum(glfw.Key.nine)) { + self.block_index = @intCast(key_code - @intFromEnum(glfw.Key.one)); + std.log.info("Picked block {s}", .{blocks[self.block_index]}); + } } pub fn onKeyUp(self: *Player, key: glfw.Key) void { @@ -231,15 +250,36 @@ pub fn onMouseDown(self: *Player, button: glfw.MouseButton, game: *Game) void { if (self.maybe_raycast_hit) |raycast_hit| { switch (button) { .left => { - game.chunks.destroyVoxelAt( + game.chunks.setVoxelAt( raycast_hit.voxel, + .air, game.engine, &game.blocks, + game.descriptor_pool, + game.per_batch_descriptor_set_layout, game.allocator, ) catch |err| { std.log.err("Error while destroying voxel {f}: {}", .{ raycast_hit.voxel, err }); }; }, + .right => blk: { + const target_vx = raycast_hit.voxel.add(raycast_hit.side.getSignVector()); + const id = game.blocks.getOrLoad(game.engine, &game.materials, &game.textures, blocks[self.block_index], game.allocator) catch |err| { + std.log.err("Error while placing voxel at {f}: {}", .{ target_vx, err }); + break :blk; + }; + game.chunks.setVoxelAt( + target_vx, + id, + game.engine, + &game.blocks, + game.descriptor_pool, + game.per_batch_descriptor_set_layout, + game.allocator, + ) catch |err| { + std.log.err("Error while placing voxel at {f}: {}", .{ target_vx, err }); + }; + }, else => {}, } } diff --git a/src/voxels.zig b/src/voxels.zig index 7b853f6..92f7cfc 100644 --- a/src/voxels.zig +++ b/src/voxels.zig @@ -1,6 +1,7 @@ const std = @import("std"); const c = @import("const.zig"); +const vm = @import("vecmath"); const Materials = @import("assets/Materials.zig"); @@ -15,7 +16,7 @@ pub const Orientation = enum(u3) { pub fn initSignIndex(sign: i2, index: u2) Orientation { std.debug.assert(sign == -1 or sign == 1); std.debug.assert(index < 3); - const value: u3 = (if (sign > 0) @as(u3, 0b100) else @as(u3, 0b000)) | index; + const value: u3 = if (sign > 0) @as(u3, index) else ~@as(u3, index); return @enumFromInt(value); } @@ -32,6 +33,14 @@ pub const Orientation = enum(u3) { return if (@intFromEnum(self) & 0b100 != 0) -1 else 1; } + /// +1 for positive + /// -1 for negative + pub fn getSignVector(self: Orientation) vm.Vector3Int { + var array: [3]i32 = .{ 0, 0, 0 }; + array[self.getIndex()] = self.getSign(); + return .initArray(array); + } + pub fn getOffsetVx(self: Orientation) i32 { return if (@intFromEnum(self) & 0b100 != 0) 1 else 0; }