Preallocate texture VRAM, move files around
This commit is contained in:
263
src/Chunk.zig
Normal file
263
src/Chunk.zig
Normal file
@@ -0,0 +1,263 @@
|
||||
const Chunk = @This();
|
||||
const std = @import("std");
|
||||
|
||||
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 ObjectUniformsBuffer = GenericBuffer(void, shaders.ObjectUniforms);
|
||||
|
||||
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: vm.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: 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: ObjectUniformsBuffer = try .init(.{
|
||||
.usage = .storage,
|
||||
.target_queue = .graphics,
|
||||
.array_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.array_capacity < object_count) {
|
||||
const desired_capacity = std.math.ceilPowerOfTwoAssert(u32, object_count);
|
||||
const new_object_buffer: ObjectUniformsBuffer = try .init(.{
|
||||
.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();
|
||||
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(.{ .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, 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
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user