Block placing

This commit is contained in:
2026-01-08 13:27:35 +01:00
parent d96946d1a0
commit 133994d2ef
3 changed files with 198 additions and 89 deletions

View File

@@ -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) {

View File

@@ -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 => {},
}
}

View File

@@ -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;
}