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 math = @import("math.zig");
const vk = @import("vulkan"); const vk = @import("vulkan");
const vm = @import("vecmath"); const vm = @import("vecmath");
const voxels = @import("voxels.zig");
const Blocks = @import("assets/Blocks.zig"); const Blocks = @import("assets/Blocks.zig");
const Chunk = @import("assets/Chunk.zig"); const Chunk = @import("assets/Chunk.zig");
@@ -20,6 +21,7 @@ pub const SweepHit = struct {
pub const RaycastHit = struct { pub const RaycastHit = struct {
voxel: vm.Vector3Int, voxel: vm.Vector3Int,
side: voxels.Orientation,
}; };
pub fn deinit(self: *Chunks, engine: *Engine, descriptor_pool: vk.DescriptorPool, allocator: std.mem.Allocator) void { 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 min_ck = vm.Vector3Int.initScalar(std.math.minInt(i16));
const max_ck = vm.Vector3Int.initScalar(std.math.maxInt(i16)); const max_ck = vm.Vector3Int.initScalar(std.math.maxInt(i16));
const ck = vx.divScalar(c.vx_per_ck); const ck = vx.divScalar(c.vx_per_ck);
@@ -65,11 +76,12 @@ pub fn destroyVoxelAt(self: *const Chunks, vx: vm.Vector3Int, engine: *Engine, b
const y: i16 = @intCast(ck.y); const y: i16 = @intCast(ck.y);
const z: i16 = @intCast(ck.z); const z: i16 = @intCast(ck.z);
if (self.chunks.getPtr(.{ x, y, z })) |chunk| { const chunk = try self.getOrCreateChunk(ck, engine, descriptor_pool, per_batch_descriptor_set_layout, allocator);
const ckvx = vx.modScalar(c.vx_per_ck); const ckvx = vx.modScalar(c.vx_per_ck);
const block = &chunk.blocks[@intCast(ckvx.z)][@intCast(ckvx.y)][@intCast(ckvx.x)]; const block = &chunk.blocks[@intCast(ckvx.z)][@intCast(ckvx.y)][@intCast(ckvx.x)];
if (block.* != .air) { if (block.* != id) {
block.* = .air; block.* = id;
try chunk.refresh(engine, blocks, .{ try chunk.refresh(engine, blocks, .{
.positive_x = self.chunks.getPtr(.{ x + 1, y, z }), .positive_x = self.chunks.getPtr(.{ x + 1, y, z }),
@@ -152,7 +164,6 @@ pub fn destroyVoxelAt(self: *const Chunks, vx: vm.Vector3Int, engine: *Engine, b
} }
} }
} }
}
} }
pub fn isSolid(self: *const Chunks, vx: vm.Vector3Int) bool { pub fn isSolid(self: *const Chunks, vx: vm.Vector3Int) bool {
@@ -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); var cross_indices = order3(i_index, j_index, k_index, cross_i, cross_j, cross_k, &ind_buf);
for (cross_indices) |cross_index| { 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); const voxel = vm.Vector3Int.initArray(current_vx);
if (self.isSolid(voxel)) { 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); cross_indices = order3(i_index, j_index, k_index, cross_i, cross_j, cross_k, &ind_buf);
for (cross_indices) |cross_index| { 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); const voxel = vm.Vector3Int.initArray(current_vx);
if (self.isSolid(voxel)) { 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); cross_indices = order3(i_index, j_index, k_index, cross_i, cross_j, cross_k, &ind_buf);
for (cross_indices) |cross_index| { 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); const voxel = vm.Vector3Int.initArray(current_vx);
if (self.isSolid(voxel)) { 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; 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 { 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_i >= 0 and cross_i < 1) {
if (cross_j >= 0 and cross_j < 1) { if (cross_j >= 0 and cross_j < 1) {

View File

@@ -177,6 +177,19 @@ vertical_fov_deg: f32 = 80,
movement_state: MovementState, movement_state: MovementState,
maybe_raycast_hit: ?Chunks.RaycastHit = null, 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; 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.x_input.onKeyDown(key);
self.y_input.onKeyDown(key); self.y_input.onKeyDown(key);
self.z_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 { 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| { if (self.maybe_raycast_hit) |raycast_hit| {
switch (button) { switch (button) {
.left => { .left => {
game.chunks.destroyVoxelAt( game.chunks.setVoxelAt(
raycast_hit.voxel, raycast_hit.voxel,
.air,
game.engine, game.engine,
&game.blocks, &game.blocks,
game.descriptor_pool,
game.per_batch_descriptor_set_layout,
game.allocator, game.allocator,
) catch |err| { ) catch |err| {
std.log.err("Error while destroying voxel {f}: {}", .{ raycast_hit.voxel, 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 => {}, else => {},
} }
} }

View File

@@ -1,6 +1,7 @@
const std = @import("std"); const std = @import("std");
const c = @import("const.zig"); const c = @import("const.zig");
const vm = @import("vecmath");
const Materials = @import("assets/Materials.zig"); const Materials = @import("assets/Materials.zig");
@@ -15,7 +16,7 @@ pub const Orientation = enum(u3) {
pub fn initSignIndex(sign: i2, index: u2) Orientation { pub fn initSignIndex(sign: i2, index: u2) Orientation {
std.debug.assert(sign == -1 or sign == 1); std.debug.assert(sign == -1 or sign == 1);
std.debug.assert(index < 3); 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); return @enumFromInt(value);
} }
@@ -32,6 +33,14 @@ pub const Orientation = enum(u3) {
return if (@intFromEnum(self) & 0b100 != 0) -1 else 1; 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 { pub fn getOffsetVx(self: Orientation) i32 {
return if (@intFromEnum(self) & 0b100 != 0) 1 else 0; return if (@intFromEnum(self) & 0b100 != 0) 1 else 0;
} }