Eliminate opposing walls

This commit is contained in:
2025-11-29 14:49:28 +01:00
parent 8d07dcd594
commit 5dfbc64676
4 changed files with 226 additions and 130 deletions

View File

@@ -15,7 +15,7 @@ const Engine = @import("engine/Engine.zig");
const GenericBuffer = @import("engine/GenericBuffer.zig").GenericBuffer;
const StagingBuffer = @import("engine/StagingBuffer.zig");
const Swapchain = @import("engine/Swapchain.zig");
const Iterator3 = math.Iterator3;
const Interator3 = math.Interator3;
const Matrix4x4 = math.Matrix4x4;
const Quaternion = math.Quaternion;
const Vector2 = math.Vector2;
@@ -117,7 +117,7 @@ index_buffer: IndexBuffer,
global_uniforms: GlobalUniformsBuffer,
global_uniforms_staging_buffer: StagingBuffer,
global_uniforms_transfer_command_buffer: CommandBuffer(.graphics, .persistent),
global_uniforms_transfer_command_buffer: CommandBuffer(.transfer, .persistent),
global_uniforms_transfer_semaphores: []vk.Semaphore,
point_lights: PointLightBuffer,
directional_lights: DirectionalLightBuffer,
@@ -126,7 +126,7 @@ sampler: vk.Sampler,
blocks: Blocks,
materials: Materials,
textures: Textures,
chunks: std.ArrayList(Chunk),
chunks: std.AutoHashMapUnmanaged([3]i16, Chunk),
camera_position: Vector3 = .init(0, 0, 1.62),
camera_pitch: f32 = 0,
@@ -142,7 +142,7 @@ input_down: bool = false,
const max_textures = 1024;
const max_point_lights = 1024;
const max_directional_lights = 4;
const max_objects = 1024;
const chunk_descriptor_pool = 512;
const camera_near_plane = 0.1;
const camera_vertical_fov_deg = 80.0;
@@ -318,7 +318,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
});
errdefer global_uniforms_staging_buffer.deinit(engine);
var global_uniforms_transfer_command_buffer: CommandBuffer(.graphics, .persistent) = try .init(engine);
var global_uniforms_transfer_command_buffer: CommandBuffer(.transfer, .persistent) = try .init(engine);
errdefer global_uniforms_transfer_command_buffer.deinit(engine);
try global_uniforms_transfer_command_buffer.beginCommandBuffer(.{});
@@ -491,7 +491,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
engine.setObjectName(pipeline, "P", .{});
const descriptor_pool = try engine.createDescriptorPool(.{
.max_sets = 1 + 256,
.max_sets = 1 + chunk_descriptor_pool,
.pool_sizes = &.{
.{
.type = .sampler,
@@ -507,7 +507,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
},
.{
.type = .storage_buffer,
.descriptor_count = 3 + 256,
.descriptor_count = 3 + chunk_descriptor_pool,
},
},
});
@@ -603,33 +603,51 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
},
});
var chunks: std.ArrayList(Chunk) = .empty;
var chunks: std.AutoHashMapUnmanaged([3]i16, Chunk) = .empty;
errdefer {
for (chunks.items) |*chunk| {
var it = chunks.valueIterator();
while (it.next()) |chunk| {
chunk.deinit(engine, descriptor_pool);
}
chunks.deinit(allocator);
}
var it: Iterator3 = .init(.init(-64, -64, 0), .init(64, 64, 0), .init(16, 16, 16));
while (it.next()) |origin| {
var it = Interator3(i16).init(-10, -10, 0, 9, 9, 0);
while (it.next()) |chunk_coords| {
const origin = Vector3.init(
@floatFromInt(chunk_coords[0]),
@floatFromInt(chunk_coords[1]),
@floatFromInt(chunk_coords[2]),
).mulScalar(16);
try chunks.ensureUnusedCapacity(allocator, 1);
chunks.appendAssumeCapacity(try Chunk.init(engine, .{
chunks.putAssumeCapacityNoClobber(chunk_coords, try Chunk.init(engine, .{
.origin = origin,
.descriptor_pool = descriptor_pool,
.per_batch_descriptor_set_layout = per_batch_descriptor_set_layout,
}));
const chunk = chunks.getPtr(chunk_coords).?;
const chunk = &chunks.items[chunks.items.len - 1];
var it2 = Iterator3.init(.init(0, 0, 0), .init(1, 1, 0), .{ .vector = @splat(1.0 / 16.0) });
while (it2.next()) |fpos| {
const x, const y, const z = fpos.asArrayNorm(u4);
var it2 = Interator3(usize).init(0, 0, 0, 15, 15, 0);
while (it2.next()) |pos| {
const x, const y, const z = pos;
const block: Blocks.Id = @enumFromInt(engine.random.intRangeLessThan(u12, 1, @intCast(blocks.blocks.items.len)));
chunk.blocks[z][y][x] = block;
}
}
try chunk.refresh(engine, &blocks, allocator);
var chunk_it = chunks.iterator();
while (chunk_it.next()) |entry| {
const x, const y, const z = entry.key_ptr.*;
const chunk = entry.value_ptr;
try chunk.refresh(engine, &blocks, .{
.positive_x = chunks.getPtr(.{ x + 1, y, z }),
.negative_x = chunks.getPtr(.{ x - 1, y, z }),
.positive_y = chunks.getPtr(.{ x, y + 1, z }),
.negative_y = chunks.getPtr(.{ x, y - 1, z }),
.positive_z = chunks.getPtr(.{ x, y, z + 1 }),
.negative_z = chunks.getPtr(.{ x, y, z - 1 }),
}, allocator);
}
const point_lights_data: []const PointLight = &.{};
@@ -683,7 +701,9 @@ pub fn deinit(self: *Game) void {
self.vertex_buffer.deinit(self.engine);
self.index_buffer.deinit(self.engine);
for (self.chunks.items) |*chunk| {
var it = self.chunks.valueIterator();
while (it.next()) |chunk| {
chunk.deinit(self.engine, self.descriptor_pool);
}
self.chunks.deinit(self.allocator);
@@ -916,7 +936,8 @@ fn render(self: *Game) !void {
command_buffer.bindIndexBuffer(self.index_buffer.buffer, 0, .uint16);
command_buffer.bindDescriptorSet(.graphics, self.pipeline_layout, 0, self.global_descriptor_set, null);
for (self.chunks.items) |chunk| {
var it = self.chunks.valueIterator();
while (it.next()) |chunk| {
chunk.draw(self.pipeline_layout, command_buffer);
}
}

View File

@@ -10,6 +10,8 @@ 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 Materials = @import("Materials.zig");
const Orientation = @import("../voxel.zig").Orientation;
const Matrix4x4 = math.Matrix4x4;
const ObjectUniformsBuffer = GenericBuffer(void, Game.ObjectUniforms);
@@ -20,11 +22,15 @@ 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 {
@@ -33,6 +39,15 @@ pub const InitInfo = struct {
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,
@@ -89,23 +104,97 @@ pub fn deinit(self: *Chunk, engine: *Engine, descriptor_pool: vk.DescriptorPool)
self.* = undefined;
}
pub fn refresh(self: *Chunk, engine: *Engine, blocks: *const Blocks, temp_allocator: std.mem.Allocator) !void {
var object_count: u32 = 0;
pub fn refresh(self: *Chunk, engine: *Engine, blocks: *const Blocks, neighbors: Neighbors, temp_allocator: std.mem.Allocator) !void {
var uniforms: std.ArrayList(Game.ObjectUniforms) = try .initCapacity(temp_allocator, initial_capacity);
defer uniforms.deinit(temp_allocator);
for (self.blocks) |plane| {
for (plane) |row| {
for (row) |id| {
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).?;
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);
const center = Vector3.add(self.origin, .init(fx, fy, fz));
const cx, const cy, const cz = center.asArray();
if (block.positive_x != .empty and self.getOpposite(.positive_x, ix, iy, iz, blocks, neighbors) == .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
try uniforms.append(temp_allocator, .init(matrix, matrix, block.positive_x));
}
if (block.negative_x != .empty and self.getOpposite(.negative_x, ix, iy, iz, blocks, neighbors) == .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
try uniforms.append(temp_allocator, .init(matrix, matrix, block.negative_x));
}
if (block.positive_y != .empty and self.getOpposite(.positive_y, ix, iy, iz, blocks, neighbors) == .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
try uniforms.append(temp_allocator, .init(matrix, matrix, block.positive_y));
}
if (block.negative_y != .empty and self.getOpposite(.negative_y, ix, iy, iz, blocks, neighbors) == .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
try uniforms.append(temp_allocator, .init(matrix, matrix, block.negative_y));
}
if (block.positive_z != .empty and self.getOpposite(.positive_z, ix, iy, iz, blocks, neighbors) == .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
try uniforms.append(temp_allocator, .init(matrix, matrix, block.positive_z));
}
if (block.negative_z != .empty and self.getOpposite(.negative_z, ix, iy, iz, blocks, neighbors) == .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
try uniforms.append(temp_allocator, .init(matrix, matrix, block.negative_z));
}
}
}
}
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, .{
@@ -140,105 +229,7 @@ pub fn refresh(self: *Chunk, engine: *Engine, blocks: *const Blocks, temp_alloca
engine.setObjectName(new_object_buffer.device_memory, "DM Chunk[{d}]", .{self.chunk_id});
}
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 });
try self.object_buffer.write(engine, .{ .elements = uniforms.items });
self.object_count = object_count;
}
@@ -246,3 +237,44 @@ pub fn draw(self: *const Chunk, layout: vk.PipelineLayout, command_buffer: Comma
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: 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.getBlock(self.blocks[z][y][x + 1]).?.negative_x
else if (neighbors.positive_x) |neighbor|
blocks.getBlock(neighbor.blocks[z][y][0]).?.negative_x
else
.empty,
.negative_x => if (x > 0)
blocks.getBlock(self.blocks[z][y][x - 1]).?.positive_x
else if (neighbors.negative_x) |neighbor|
blocks.getBlock(neighbor.blocks[z][y][chunk_size - 1]).?.positive_x
else
.empty,
.positive_y => if (y + 1 < chunk_size)
blocks.getBlock(self.blocks[z][y + 1][x]).?.negative_y
else if (neighbors.positive_y) |neighbor|
blocks.getBlock(neighbor.blocks[z][0][x]).?.negative_y
else
.empty,
.negative_y => if (y > 0)
blocks.getBlock(self.blocks[z][y - 1][x]).?.positive_y
else if (neighbors.negative_y) |neighbor|
blocks.getBlock(neighbor.blocks[z][chunk_size - 1][x]).?.positive_y
else
.empty,
.positive_z => if (z + 1 < chunk_size)
blocks.getBlock(self.blocks[z + 1][y][x]).?.negative_z
else if (neighbors.positive_z) |neighbor|
blocks.getBlock(neighbor.blocks[0][y][x]).?.negative_z
else
.empty,
.negative_z => if (z > 0)
blocks.getBlock(self.blocks[z - 1][y][x]).?.positive_z
else if (neighbors.negative_z) |neighbor|
blocks.getBlock(neighbor.blocks[chunk_size - 1][y][x]).?.positive_z
else
.empty,
};
}

View File

@@ -1,3 +1,4 @@
pub const Interator3 = @import("math/Interator3.zig").Interator3;
pub const Iterator3 = @import("math/Iterator3.zig");
pub const Matrix4x4 = @import("math/Matrix4x4.zig").Matrix4x4;
pub const Quaternion = @import("math/Quaternion.zig").Quaternion;

42
src/math/Interator3.zig Normal file
View File

@@ -0,0 +1,42 @@
pub fn Interator3(comptime T: type) type {
return struct {
const Self = @This();
const Vector = @Vector(3, T);
min: Vector,
max: Vector,
current: ?Vector,
pub fn init(min_x: T, min_y: T, min_z: T, max_x: T, max_y: T, max_z: T) Self {
const min: Vector = .{ min_x, min_y, min_z };
const max: Vector = .{ max_x, max_y, max_z };
return .{
.min = min,
.max = max,
.current = min,
};
}
pub fn next(self: *Self) ?[3]T {
const current = self.current orelse return null;
var next_current = current;
next_current[0] = next_current[0] + 1;
if (next_current[0] > self.max[0]) {
next_current[0] = self.min[0];
next_current[1] = next_current[1] + 1;
if (next_current[1] > self.max[1]) {
next_current[1] = self.min[1];
next_current[2] = next_current[2] + 1;
if (next_current[2] > self.max[2]) {
self.current = null;
return current;
}
}
}
self.current = next_current;
return current;
}
};
}