Blocks and chunks

This commit is contained in:
2025-11-28 23:24:22 +01:00
parent 2541dee18d
commit 81a56f393e
32 changed files with 714 additions and 113 deletions

238
src/assets/Chunk.zig Normal file
View File

@@ -0,0 +1,238 @@
const Chunk = @This();
const std = @import("std");
const vk = @import("vulkan");
const math = @import("../math.zig");
const Blocks = @import("Blocks.zig");
const CommandBuffer = @import("../engine/CommandBuffer.zig").CommandBuffer;
const Engine = @import("../engine/Engine.zig");
const Game = @import("../Game.zig");
const GenericBuffer = @import("../engine/GenericBuffer.zig").GenericBuffer;
const Matrix4x4 = math.Matrix4x4;
const ObjectUniformsBuffer = GenericBuffer(void, Game.ObjectUniforms);
const Vector3 = math.Vector3;
pub const chunk_size = 16;
const initial_capacity = 256;
blocks: [chunk_size][chunk_size][chunk_size]Blocks.Id,
origin: Vector3,
descriptor_set: vk.DescriptorSet,
object_buffer: ObjectUniformsBuffer,
object_count: u32,
pub const InitInfo = struct {
origin: Vector3,
descriptor_pool: vk.DescriptorPool,
per_batch_descriptor_set_layout: vk.DescriptorSetLayout,
};
pub fn init(engine: *Engine, init_info: InitInfo) !Chunk {
const descriptor_set = try engine.allocateDescriptorSet(.{
.descriptor_pool = init_info.descriptor_pool,
.set_layout = init_info.per_batch_descriptor_set_layout,
});
errdefer engine.freeDescriptorSet(init_info.descriptor_pool, descriptor_set);
var object_buffer: ObjectUniformsBuffer = try .init(engine, .{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = initial_capacity,
});
errdefer object_buffer.deinit(engine);
try engine.updateDescriptorSets(.{
.writes = &.{
.{
.dst_set = descriptor_set,
.dst_binding = 0,
.dst_array_element = 0,
.descriptor_type = .storage_buffer,
.descriptor_infos = .{
.buffer = &.{
.{
.buffer = object_buffer.buffer,
.offset = 0,
.range = vk.WHOLE_SIZE,
},
},
},
},
},
});
return .{
.blocks = .{.{[_]Blocks.Id{.air} ** chunk_size} ** chunk_size} ** chunk_size,
.origin = init_info.origin,
.descriptor_set = descriptor_set,
.object_buffer = object_buffer,
.object_count = 0,
};
}
pub fn deinit(self: *Chunk, engine: *Engine, descriptor_pool: vk.DescriptorPool) void {
self.object_buffer.deinit(engine);
engine.freeDescriptorSet(descriptor_pool, self.descriptor_set);
self.* = undefined;
}
pub fn refresh(self: *Chunk, engine: *Engine, blocks: *const Blocks, temp_allocator: std.mem.Allocator) !void {
var object_count: u32 = 0;
for (self.blocks) |plane| {
for (plane) |row| {
for (row) |id| {
const block = blocks.getBlock(id).?;
object_count += @intFromBool(block.positive_x != .empty);
object_count += @intFromBool(block.negative_x != .empty);
object_count += @intFromBool(block.positive_y != .empty);
object_count += @intFromBool(block.negative_y != .empty);
object_count += @intFromBool(block.positive_z != .empty);
object_count += @intFromBool(block.negative_z != .empty);
}
}
}
if (self.object_buffer.array_capacity < object_count) {
const desired_capacity = std.math.ceilPowerOfTwoAssert(u32, object_count);
const new_object_buffer: ObjectUniformsBuffer = try .init(engine, .{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = desired_capacity,
});
try engine.updateDescriptorSets(.{
.writes = &.{
.{
.dst_set = self.descriptor_set,
.dst_binding = 0,
.dst_array_element = 0,
.descriptor_type = .storage_buffer,
.descriptor_infos = .{
.buffer = &.{
.{
.buffer = new_object_buffer.buffer,
.offset = 0,
.range = vk.WHOLE_SIZE,
},
},
},
},
},
});
self.object_buffer.deinit(engine);
self.object_buffer = new_object_buffer;
}
const uniforms = try temp_allocator.alloc(Game.ObjectUniforms, object_count);
var object_i: usize = 0;
defer temp_allocator.free(uniforms);
for (self.blocks, 0..) |plane, iz| {
const fz: f32 = @floatFromInt(iz);
for (plane, 0..) |row, iy| {
const fy: f32 = @floatFromInt(iy);
for (row, 0..) |id, ix| {
const fx: f32 = @floatFromInt(ix);
const block = blocks.getBlock(id).?;
const center = Vector3.add(self.origin, .init(fx, fy, fz));
const cx, const cy, const cz = center.asArray();
if (block.positive_x != .empty) {
// zig fmt: off
const matrix: Matrix4x4 = .init(
0, 1, 0, 0,
0, 0, 1, 0,
1, 0, 0, 0,
cx + 0.5, cy, cz, 1,
);
// zig fmt: on
uniforms[object_i] = .init(matrix, matrix, block.positive_x);
object_i += 1;
}
if (block.negative_x != .empty) {
// zig fmt: off
const matrix: Matrix4x4 = .init(
0, -1, 0, 0,
0, 0, 1, 0,
-1, 0, 0, 0,
cx - 0.5, cy, cz, 1,
);
// zig fmt: on
uniforms[object_i] = .init(matrix, matrix, block.negative_x);
object_i += 1;
}
if (block.positive_y != .empty) {
// zig fmt: off
const matrix: Matrix4x4 = .init(
-1, 0, 0, 0,
0, 0, 1, 0,
0, 1, 0, 0,
cx, cy + 0.5, cz, 1,
);
// zig fmt: on
uniforms[object_i] = .init(matrix, matrix, block.positive_y);
object_i += 1;
}
if (block.negative_y != .empty) {
// zig fmt: off
const matrix: Matrix4x4 = .init(
1, 0, 0, 0,
0, 0, 1, 0,
0, -1, 0, 0,
cx, cy - 0.5, cz, 1,
);
// zig fmt: on
uniforms[object_i] = .init(matrix, matrix, block.negative_y);
object_i += 1;
}
if (block.positive_z != .empty) {
// zig fmt: off
const matrix: Matrix4x4 = .init(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
cx, cy, cz + 0.5, 1,
);
// zig fmt: on
uniforms[object_i] = .init(matrix, matrix, block.positive_z);
object_i += 1;
}
if (block.negative_z != .empty) {
// zig fmt: off
const matrix: Matrix4x4 = .init(
1, 0, 0, 0,
0, -1, 0, 0,
0, 0, -1, 0,
cx, cy, cz - 0.5, 1,
);
// zig fmt: on
uniforms[object_i] = .init(matrix, matrix, block.negative_z);
object_i += 1;
}
}
}
}
std.debug.assert(object_i == uniforms.len);
try self.object_buffer.write(engine, .{ .elements = uniforms });
self.object_count = object_count;
}
pub fn draw(self: *const Chunk, layout: vk.PipelineLayout, command_buffer: CommandBuffer(.graphics, .transient)) void {
command_buffer.bindDescriptorSets(.graphics, layout, 1, &.{self.descriptor_set}, &.{});
command_buffer.drawIndexed(.{ .index_count = 6, .instance_count = self.object_count });
}