261 lines
9.8 KiB
Zig
261 lines
9.8 KiB
Zig
const Chunk = @This();
|
|
const std = @import("std");
|
|
|
|
const math = @import("../math.zig");
|
|
const shaders = @import("../shaders.zig");
|
|
const vk = @import("vulkan");
|
|
const voxels = @import("../voxels.zig");
|
|
|
|
const Blocks = @import("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("Materials.zig");
|
|
|
|
const Matrix4x4 = math.Matrix4x4;
|
|
const ObjectUniformsBuffer = GenericBuffer(void, shaders.ObjectUniforms);
|
|
const Vector3 = math.Vector3;
|
|
|
|
pub const chunk_size = 16;
|
|
|
|
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: Vector3,
|
|
descriptor_set: vk.DescriptorSet,
|
|
object_buffer: ObjectUniformsBuffer,
|
|
object_count: u32,
|
|
|
|
/// For debug purposes, a unique "name" per chunk instance.
|
|
chunk_id: u64,
|
|
|
|
pub const InitInfo = struct {
|
|
origin: Vector3,
|
|
descriptor_pool: vk.DescriptorPool,
|
|
per_batch_descriptor_set_layout: vk.DescriptorSetLayout,
|
|
};
|
|
|
|
pub const Neighbors = struct {
|
|
positive_x: ?*const Chunk,
|
|
negative_x: ?*const Chunk,
|
|
positive_y: ?*const Chunk,
|
|
negative_y: ?*const Chunk,
|
|
positive_z: ?*const Chunk,
|
|
negative_z: ?*const Chunk,
|
|
};
|
|
|
|
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,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
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, engine: *Engine, descriptor_pool: vk.DescriptorPool) void {
|
|
std.log.scoped(.deinit).debug("Deinitializing chunk {d} with {*} and {s}#{X}", .{ self.chunk_id, engine, @typeName(vk.DescriptorPool), @intFromEnum(descriptor_pool) });
|
|
|
|
self.object_buffer.deinit(engine);
|
|
engine.freeDescriptorSet(descriptor_pool, self.descriptor_set);
|
|
|
|
self.* = undefined;
|
|
}
|
|
|
|
pub fn refresh(self: *Chunk, engine: *Engine, blocks: *const Blocks, neighbors: Neighbors, temp_allocator: std.mem.Allocator) !void {
|
|
var uniforms: std.ArrayList(shaders.ObjectUniforms) = try .initCapacity(temp_allocator, initial_capacity);
|
|
defer uniforms.deinit(temp_allocator);
|
|
|
|
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 center = 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, center);
|
|
try uniforms.append(temp_allocator, .init(matrix, matrix, material));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const object_count: u32 = @intCast(uniforms.items.len);
|
|
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;
|
|
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.write(engine, .{ .elements = 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, center: Vector3) Matrix4x4 {
|
|
return switch (side) {
|
|
// zig fmt: off
|
|
.positive_x => .init(
|
|
0, 1, 0, 0,
|
|
0, 0, 1, 0,
|
|
1, 0, 0, 0,
|
|
center.getX() + 0.5, center.getY(), center.getZ(), 1,
|
|
),
|
|
.negative_x => .init(
|
|
0, -1, 0, 0,
|
|
0, 0, 1, 0,
|
|
-1, 0, 0, 0,
|
|
center.getX() - 0.5, center.getY(), center.getZ(), 1,
|
|
),
|
|
.positive_y => .init(
|
|
-1, 0, 0, 0,
|
|
0, 0, 1, 0,
|
|
0, 1, 0, 0,
|
|
center.getX(), center.getY() + 0.5, center.getZ(), 1,
|
|
),
|
|
.negative_y => .init(
|
|
1, 0, 0, 0,
|
|
0, 0, 1, 0,
|
|
0, -1, 0, 0,
|
|
center.getX(), center.getY() - 0.5, center.getZ(), 1,
|
|
),
|
|
.positive_z => .init(
|
|
1, 0, 0, 0,
|
|
0, 1, 0, 0,
|
|
0, 0, 1, 0,
|
|
center.getX(), center.getY(), center.getZ() + 0.5, 1,
|
|
),
|
|
.negative_z => .init(
|
|
1, 0, 0, 0,
|
|
0, -1, 0, 0,
|
|
0, 0, -1, 0,
|
|
center.getX(), center.getY(), center.getZ() - 0.5, 1,
|
|
),
|
|
// zig fmt: on
|
|
};
|
|
}
|