263 lines
9.6 KiB
Zig
263 lines
9.6 KiB
Zig
const Chunk = @This();
|
|
const std = @import("std");
|
|
|
|
const c = @import("const.zig");
|
|
const ctx = @import("AppContext.zig");
|
|
const math = @import("math.zig");
|
|
const shaders = @import("shaders.zig");
|
|
const vk = @import("vulkan");
|
|
const vm = @import("vecmath");
|
|
const voxels = @import("voxels.zig");
|
|
|
|
const Blocks = @import("assets/Blocks.zig");
|
|
const CommandBuffer = @import("engine/CommandBuffer.zig");
|
|
const Engine = @import("engine/Engine.zig");
|
|
const Game = @import("Game.zig");
|
|
const GenericBuffer = @import("engine/GenericBuffer.zig").GenericBuffer;
|
|
const Materials = @import("engine/Materials.zig");
|
|
|
|
const chunk_size = c.vx_per_ck;
|
|
|
|
const initial_capacity = 256;
|
|
var next_chunk_id: std.atomic.Value(u64) = .init(0);
|
|
|
|
/// To get the block at coordinates (x, y, z) relative to the chunk, read the
|
|
/// value at `blocks[z][y][x]`.
|
|
blocks: [chunk_size][chunk_size][chunk_size]Blocks.Id,
|
|
origin: vm.Vector3,
|
|
descriptor_set: vk.DescriptorSet,
|
|
object_buffer: shaders.ObjectUniformsBuffer,
|
|
object_count: u32,
|
|
|
|
/// For debug purposes, a unique "name" per chunk instance.
|
|
chunk_id: u64,
|
|
|
|
pub const InitInfo = struct {
|
|
origin: vm.Vector3,
|
|
descriptor_pool: vk.DescriptorPool,
|
|
per_batch_descriptor_set_layout: vk.DescriptorSetLayout,
|
|
};
|
|
|
|
pub const Neighbors = std.enums.EnumFieldStruct(voxels.Orientation, ?*const Chunk, null);
|
|
|
|
pub fn init(init_info: InitInfo) !Chunk {
|
|
const engine = ctx.engine;
|
|
|
|
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: shaders.ObjectUniformsBuffer = try .init(.{
|
|
.usage = .storage,
|
|
.target_queue = .graphics,
|
|
.capacity = initial_capacity,
|
|
});
|
|
errdefer object_buffer.deinit();
|
|
|
|
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,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const chunk_id = next_chunk_id.fetchAdd(1, .monotonic);
|
|
engine.setObjectName(descriptor_set, "DS Chunk[{d}]", .{chunk_id});
|
|
engine.setObjectName(object_buffer.buffer, "B Chunk[{d}]", .{chunk_id});
|
|
engine.setObjectName(object_buffer.device_memory, "DM Chunk[{d}]", .{chunk_id});
|
|
|
|
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,
|
|
.chunk_id = chunk_id,
|
|
};
|
|
}
|
|
|
|
pub fn deinit(self: *Chunk, descriptor_pool: vk.DescriptorPool) void {
|
|
const engine = ctx.engine;
|
|
|
|
std.log.scoped(.deinit).debug("Deinitializing chunk {d} with {s}#{X}", .{ self.chunk_id, @typeName(vk.DescriptorPool), @intFromEnum(descriptor_pool) });
|
|
|
|
self.object_buffer.deinit();
|
|
engine.freeDescriptorSet(descriptor_pool, self.descriptor_set);
|
|
|
|
self.* = undefined;
|
|
}
|
|
|
|
pub fn refresh(self: *Chunk, blocks: *const Blocks, neighbors: Neighbors) !void {
|
|
const allocator_frame = ctx.allocator_frame;
|
|
const engine = ctx.engine;
|
|
|
|
var uniforms: std.ArrayList(shaders.ObjectUniforms) = try .initCapacity(allocator_frame, initial_capacity);
|
|
|
|
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: *const Blocks.Definition = &blocks.array.items[id.toInt()];
|
|
const origin = vm.Vector3.add(self.origin, .init(fx, fy, fz));
|
|
|
|
inline for (@typeInfo(voxels.Orientation).@"enum".fields) |field| {
|
|
const side = @field(voxels.Orientation, field.name);
|
|
const material = @field(block.walls, field.name).material;
|
|
if (material != .empty and self.getOpposite(side, ix, iy, iz, blocks, neighbors) == .empty) {
|
|
const matrix = getMatrix(side, origin);
|
|
try uniforms.append(allocator_frame, .{
|
|
.matrixOStoWS = matrix,
|
|
.matrixOStoWSNormal = matrix,
|
|
.material = material,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const object_count: u32 = @intCast(uniforms.items.len);
|
|
if (self.object_buffer.capacity < object_count) {
|
|
const desired_capacity = std.math.ceilPowerOfTwoAssert(u32, object_count);
|
|
const new_object_buffer: shaders.ObjectUniformsBuffer = try .init(.{
|
|
.usage = .storage,
|
|
.target_queue = .graphics,
|
|
.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();
|
|
self.object_buffer = new_object_buffer;
|
|
engine.setObjectName(new_object_buffer.buffer, "B Chunk[{d}]", .{self.chunk_id});
|
|
engine.setObjectName(new_object_buffer.device_memory, "DM Chunk[{d}]", .{self.chunk_id});
|
|
}
|
|
|
|
try self.object_buffer.writeSlice(uniforms.items);
|
|
self.object_count = object_count;
|
|
}
|
|
|
|
pub fn draw(self: *const Chunk, layout: vk.PipelineLayout, command_buffer: CommandBuffer) void {
|
|
command_buffer.bindDescriptorSets(.graphics, layout, 1, &.{self.descriptor_set}, &.{});
|
|
command_buffer.drawIndexed(.{ .index_count = 6, .instance_count = self.object_count });
|
|
}
|
|
|
|
fn getOpposite(self: *const Chunk, comptime side: voxels.Orientation, x: usize, y: usize, z: usize, blocks: *const Blocks, neighbors: Neighbors) Materials.Id {
|
|
return switch (side) {
|
|
.positive_x => if (x + 1 < chunk_size)
|
|
blocks.array.items[self.blocks[z][y][x + 1].toInt()].walls.negative_x.material
|
|
else if (neighbors.positive_x) |neighbor|
|
|
blocks.array.items[neighbor.blocks[z][y][0].toInt()].walls.negative_x.material
|
|
else
|
|
.empty,
|
|
.negative_x => if (x > 0)
|
|
blocks.array.items[self.blocks[z][y][x - 1].toInt()].walls.positive_x.material
|
|
else if (neighbors.negative_x) |neighbor|
|
|
blocks.array.items[neighbor.blocks[z][y][chunk_size - 1].toInt()].walls.positive_x.material
|
|
else
|
|
.empty,
|
|
.positive_y => if (y + 1 < chunk_size)
|
|
blocks.array.items[self.blocks[z][y + 1][x].toInt()].walls.negative_y.material
|
|
else if (neighbors.positive_y) |neighbor|
|
|
blocks.array.items[neighbor.blocks[z][0][x].toInt()].walls.negative_y.material
|
|
else
|
|
.empty,
|
|
.negative_y => if (y > 0)
|
|
blocks.array.items[self.blocks[z][y - 1][x].toInt()].walls.positive_y.material
|
|
else if (neighbors.negative_y) |neighbor|
|
|
blocks.array.items[neighbor.blocks[z][chunk_size - 1][x].toInt()].walls.positive_y.material
|
|
else
|
|
.empty,
|
|
.positive_z => if (z + 1 < chunk_size)
|
|
blocks.array.items[self.blocks[z + 1][y][x].toInt()].walls.negative_z.material
|
|
else if (neighbors.positive_z) |neighbor|
|
|
blocks.array.items[neighbor.blocks[0][y][x].toInt()].walls.negative_z.material
|
|
else
|
|
.empty,
|
|
.negative_z => if (z > 0)
|
|
blocks.array.items[self.blocks[z - 1][y][x].toInt()].walls.positive_z.material
|
|
else if (neighbors.negative_z) |neighbor|
|
|
blocks.array.items[neighbor.blocks[chunk_size - 1][y][x].toInt()].walls.positive_z.material
|
|
else
|
|
.empty,
|
|
};
|
|
}
|
|
|
|
fn getMatrix(comptime side: voxels.Orientation, origin: vm.Vector3) vm.Matrix4x4 {
|
|
return switch (side) {
|
|
// zig fmt: off
|
|
.positive_x => .init(
|
|
0, 1, 0, 0,
|
|
0, 0, 1, 0,
|
|
1, 0, 0, 0,
|
|
origin.x + 1, origin.y, origin.z, 1,
|
|
),
|
|
.negative_x => .init(
|
|
0, -1, 0, 0,
|
|
0, 0, 1, 0,
|
|
-1, 0, 0, 0,
|
|
origin.x, origin.y + 1, origin.z, 1,
|
|
),
|
|
.positive_y => .init(
|
|
-1, 0, 0, 0,
|
|
0, 0, 1, 0,
|
|
0, 1, 0, 0,
|
|
origin.x + 1, origin.y + 1, origin.z, 1,
|
|
),
|
|
.negative_y => .init(
|
|
1, 0, 0, 0,
|
|
0, 0, 1, 0,
|
|
0, -1, 0, 0,
|
|
origin.x, origin.y, origin.z, 1,
|
|
),
|
|
.positive_z => .init(
|
|
1, 0, 0, 0,
|
|
0, 1, 0, 0,
|
|
0, 0, 1, 0,
|
|
origin.x, origin.y, origin.z + 1, 1,
|
|
),
|
|
.negative_z => .init(
|
|
1, 0, 0, 0,
|
|
0, -1, 0, 0,
|
|
0, 0, -1, 0,
|
|
origin.x, origin.y + 1, origin.z, 1,
|
|
),
|
|
// zig fmt: on
|
|
};
|
|
}
|