mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 03:06:55 -04:00
Improve how chunk meshes store their mesh data to avoid needless copies and needless allocations.
Instead of storing 14 different array lists, the parts of the mesh are now constructed on the stack and then directly inserted into the full list (which I abstracted into a generic data structure) fixes #1188 makes #182 less severe (it is now overshadowed by #1202)
This commit is contained in:
parent
32269f1f84
commit
e7ecd161ce
@ -31,6 +31,7 @@ pub const heap = @import("utils/heap.zig");
|
||||
|
||||
pub const List = @import("utils/list.zig").List;
|
||||
pub const ListUnmanaged = @import("utils/list.zig").ListUnmanaged;
|
||||
pub const MultiArray = @import("utils/list.zig").MultiArray;
|
||||
pub const VirtualList = @import("utils/list.zig").VirtualList;
|
||||
|
||||
const file_monitor = utils.file_monitor;
|
||||
|
@ -286,16 +286,32 @@ pub const IndirectData = extern struct {
|
||||
};
|
||||
|
||||
const PrimitiveMesh = struct { // MARK: PrimitiveMesh
|
||||
coreFaces: main.ListUnmanaged(FaceData) = .{},
|
||||
neighborFacesSameLod: [6]main.ListUnmanaged(FaceData) = @splat(.{}),
|
||||
neighborFacesHigherLod: [6]main.ListUnmanaged(FaceData) = @splat(.{}),
|
||||
optionalFaces: main.ListUnmanaged(FaceData) = .{},
|
||||
completeList: []FaceData = &.{},
|
||||
coreLen: u32 = 0,
|
||||
sameLodLens: [6]u32 = @splat(0),
|
||||
higherLodLens: [6]u32 = @splat(0),
|
||||
optionalLen: u32 = 0,
|
||||
mutex: std.Thread.Mutex = .{},
|
||||
const FaceGroups = enum(u32) {
|
||||
core,
|
||||
neighbor0,
|
||||
neighbor1,
|
||||
neighbor2,
|
||||
neighbor3,
|
||||
neighbor4,
|
||||
neighbor5,
|
||||
neighborLod0,
|
||||
neighborLod1,
|
||||
neighborLod2,
|
||||
neighborLod3,
|
||||
neighborLod4,
|
||||
neighborLod5,
|
||||
optional,
|
||||
|
||||
pub fn neighbor(n: main.chunk.Neighbor) FaceGroups {
|
||||
return @enumFromInt(@intFromEnum(FaceGroups.neighbor0) + @intFromEnum(n));
|
||||
}
|
||||
|
||||
pub fn neighborLod(n: main.chunk.Neighbor) FaceGroups {
|
||||
return @enumFromInt(@intFromEnum(FaceGroups.neighborLod0) + @intFromEnum(n));
|
||||
}
|
||||
};
|
||||
completeList: main.MultiArray(FaceData, FaceGroups) = .{},
|
||||
lock: main.utils.ReadWriteLock = .{},
|
||||
bufferAllocation: graphics.SubAllocation = .{.start = 0, .len = 0},
|
||||
vertexCount: u31 = 0,
|
||||
byNormalCount: [14]u32 = @splat(0),
|
||||
@ -304,86 +320,23 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh
|
||||
max: Vec3f = undefined,
|
||||
|
||||
fn deinit(self: *PrimitiveMesh) void {
|
||||
faceBuffer.free(self.bufferAllocation);
|
||||
self.coreFaces.deinit(main.globalAllocator);
|
||||
self.optionalFaces.deinit(main.globalAllocator);
|
||||
for(&self.neighborFacesSameLod) |*neighborFaces| {
|
||||
neighborFaces.deinit(main.globalAllocator);
|
||||
}
|
||||
for(&self.neighborFacesHigherLod) |*neighborFaces| {
|
||||
neighborFaces.deinit(main.globalAllocator);
|
||||
}
|
||||
main.globalAllocator.free(self.completeList);
|
||||
self.completeList.deinit(main.globalAllocator);
|
||||
}
|
||||
|
||||
fn reset(self: *PrimitiveMesh) void {
|
||||
self.coreFaces.clearRetainingCapacity();
|
||||
for(&self.neighborFacesSameLod) |*neighborFaces| {
|
||||
neighborFaces.clearRetainingCapacity();
|
||||
}
|
||||
for(&self.neighborFacesHigherLod) |*neighborFaces| {
|
||||
neighborFaces.clearRetainingCapacity();
|
||||
}
|
||||
self.optionalFaces.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
fn appendInternalQuadsToCore(self: *PrimitiveMesh, block: Block, x: i32, y: i32, z: i32, comptime backFace: bool, optional: bool) void {
|
||||
const model = blocks.meshes.model(block);
|
||||
models.models.items[model].appendInternalQuadsToList(if(optional) &self.optionalFaces else &self.coreFaces, main.globalAllocator, block, x, y, z, backFace);
|
||||
}
|
||||
|
||||
fn appendNeighborFacingQuadsToCore(self: *PrimitiveMesh, block: Block, neighbor: chunk.Neighbor, x: i32, y: i32, z: i32, comptime backFace: bool, optional: bool) void {
|
||||
const model = blocks.meshes.model(block);
|
||||
models.models.items[model].appendNeighborFacingQuadsToList(if(optional) &self.optionalFaces else &self.coreFaces, main.globalAllocator, block, neighbor, x, y, z, backFace);
|
||||
}
|
||||
|
||||
fn appendNeighborFacingQuadsToNeighbor(self: *PrimitiveMesh, block: Block, neighbor: chunk.Neighbor, x: i32, y: i32, z: i32, comptime backFace: bool, comptime isLod: bool) void {
|
||||
const model = blocks.meshes.model(block);
|
||||
if(isLod) {
|
||||
models.models.items[model].appendNeighborFacingQuadsToList(&self.neighborFacesHigherLod[neighbor.reverse().toInt()], main.globalAllocator, block, neighbor, x, y, z, backFace);
|
||||
} else {
|
||||
models.models.items[model].appendNeighborFacingQuadsToList(&self.neighborFacesSameLod[neighbor.reverse().toInt()], main.globalAllocator, block, neighbor, x, y, z, backFace);
|
||||
}
|
||||
}
|
||||
|
||||
fn clearNeighbor(self: *PrimitiveMesh, neighbor: chunk.Neighbor, comptime isLod: bool) void {
|
||||
if(isLod) {
|
||||
self.neighborFacesHigherLod[neighbor.toInt()].clearRetainingCapacity();
|
||||
} else {
|
||||
self.neighborFacesSameLod[neighbor.toInt()].clearRetainingCapacity();
|
||||
}
|
||||
fn replaceRange(self: *PrimitiveMesh, group: FaceGroups, items: []const FaceData) void {
|
||||
self.lock.lockWrite();
|
||||
self.completeList.replaceRange(main.globalAllocator, group, items);
|
||||
self.lock.unlockWrite();
|
||||
}
|
||||
|
||||
fn finish(self: *PrimitiveMesh, parent: *ChunkMesh, lightList: *main.List(u32), lightMap: *std.AutoHashMap([4]u32, u16)) void {
|
||||
var len: usize = self.coreFaces.items.len;
|
||||
for(self.neighborFacesSameLod) |neighborFaces| {
|
||||
len += neighborFaces.items.len;
|
||||
}
|
||||
for(self.neighborFacesHigherLod) |neighborFaces| {
|
||||
len += neighborFaces.items.len;
|
||||
}
|
||||
len += self.optionalFaces.items.len;
|
||||
const completeList = main.globalAllocator.alloc(FaceData, len);
|
||||
var i: usize = 0;
|
||||
@memcpy(completeList[i..][0..self.coreFaces.items.len], self.coreFaces.items);
|
||||
i += self.coreFaces.items.len;
|
||||
for(self.neighborFacesSameLod) |neighborFaces| {
|
||||
@memcpy(completeList[i..][0..neighborFaces.items.len], neighborFaces.items);
|
||||
i += neighborFaces.items.len;
|
||||
}
|
||||
for(self.neighborFacesHigherLod) |neighborFaces| {
|
||||
@memcpy(completeList[i..][0..neighborFaces.items.len], neighborFaces.items);
|
||||
i += neighborFaces.items.len;
|
||||
}
|
||||
@memcpy(completeList[i..][0..self.optionalFaces.items.len], self.optionalFaces.items);
|
||||
i += self.optionalFaces.items.len;
|
||||
|
||||
self.min = @splat(std.math.floatMax(f32));
|
||||
self.max = @splat(-std.math.floatMax(f32));
|
||||
|
||||
self.lock.lockRead();
|
||||
parent.lightingData[0].lock.lockRead();
|
||||
parent.lightingData[1].lock.lockRead();
|
||||
for(completeList) |*face| {
|
||||
for(self.completeList.getEverything()) |*face| {
|
||||
const light = getLight(parent, .{face.position.x, face.position.y, face.position.z}, face.blockAndQuad.texture, face.blockAndQuad.quadIndex);
|
||||
const result = lightMap.getOrPut(light) catch unreachable;
|
||||
if(!result.found_existing) {
|
||||
@ -403,20 +356,7 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh
|
||||
}
|
||||
parent.lightingData[0].lock.unlockRead();
|
||||
parent.lightingData[1].lock.unlockRead();
|
||||
|
||||
self.mutex.lock();
|
||||
const oldList = self.completeList;
|
||||
self.completeList = completeList;
|
||||
self.coreLen = @intCast(self.coreFaces.items.len);
|
||||
for(self.neighborFacesSameLod, 0..) |neighborFaces, j| {
|
||||
self.sameLodLens[j] = @intCast(neighborFaces.items.len);
|
||||
}
|
||||
for(self.neighborFacesHigherLod, 0..) |neighborFaces, j| {
|
||||
self.higherLodLens[j] = @intCast(neighborFaces.items.len);
|
||||
}
|
||||
self.optionalLen = @intCast(self.optionalFaces.items.len);
|
||||
self.mutex.unlock();
|
||||
main.globalAllocator.free(oldList);
|
||||
self.lock.unlockRead();
|
||||
}
|
||||
|
||||
fn getValues(mesh: *ChunkMesh, wx: i32, wy: i32, wz: i32) [6]u8 {
|
||||
@ -584,36 +524,28 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh
|
||||
}
|
||||
|
||||
fn uploadData(self: *PrimitiveMesh, isNeighborLod: [6]bool) void {
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
var len: u32 = self.coreLen;
|
||||
var offset: u32 = self.coreLen;
|
||||
self.lock.lockRead();
|
||||
defer self.lock.unlockRead();
|
||||
var len: usize = 0;
|
||||
const coreList = self.completeList.getRange(.core);
|
||||
len += coreList.len;
|
||||
const optionalList = self.completeList.getRange(.optional);
|
||||
len += optionalList.len;
|
||||
var list: [6][]FaceData = undefined;
|
||||
for(0..6) |i| {
|
||||
const neighborLen = self.sameLodLens[i];
|
||||
if(!isNeighborLod[i]) {
|
||||
list[i] = self.completeList[offset..][0..neighborLen];
|
||||
len += neighborLen;
|
||||
list[i] = self.completeList.getRange(.neighbor(@enumFromInt(i)));
|
||||
} else {
|
||||
list[i] = self.completeList.getRange(.neighborLod(@enumFromInt(i)));
|
||||
}
|
||||
offset += neighborLen;
|
||||
len += list[i].len;
|
||||
}
|
||||
for(0..6) |i| {
|
||||
const neighborLen = self.higherLodLens[i];
|
||||
if(isNeighborLod[i]) {
|
||||
list[i] = self.completeList[offset..][0..neighborLen];
|
||||
len += neighborLen;
|
||||
}
|
||||
offset += neighborLen;
|
||||
}
|
||||
const optionalList = self.completeList[offset..][0..self.optionalLen];
|
||||
offset += self.optionalLen;
|
||||
len += self.optionalLen;
|
||||
|
||||
const fullBuffer = faceBuffer.allocateAndMapRange(len, &self.bufferAllocation);
|
||||
defer faceBuffer.unmapRange(fullBuffer);
|
||||
// Sort the faces by normal to allow for backface culling on the GPU:
|
||||
var i: u32 = 0;
|
||||
var iStart = i;
|
||||
const coreList = self.completeList[0..self.coreLen];
|
||||
for(0..7) |normal| {
|
||||
for(coreList) |face| {
|
||||
if(main.models.extraQuadInfos.items[face.blockAndQuad.quadIndex].alignedNormalDirection) |normalDir| {
|
||||
@ -870,10 +802,6 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
}
|
||||
|
||||
pub fn generateLightingData(self: *ChunkMesh) error{AlreadyStored}!void {
|
||||
self.mutex.lock();
|
||||
self.opaqueMesh.reset();
|
||||
self.transparentMesh.reset();
|
||||
self.mutex.unlock();
|
||||
try mesh_storage.addMeshToStorage(self);
|
||||
|
||||
var lightRefreshList = main.List(*ChunkMesh).init(main.stackAllocator);
|
||||
@ -922,6 +850,16 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
}
|
||||
}
|
||||
|
||||
fn appendInternalQuads(block: Block, x: i32, y: i32, z: i32, comptime backFace: bool, list: *main.ListUnmanaged(FaceData), allocator: main.heap.NeverFailingAllocator) void {
|
||||
const model = blocks.meshes.model(block);
|
||||
models.models.items[model].appendInternalQuadsToList(list, allocator, block, x, y, z, backFace);
|
||||
}
|
||||
|
||||
fn appendNeighborFacingQuads(block: Block, neighbor: chunk.Neighbor, x: i32, y: i32, z: i32, comptime backFace: bool, list: *main.ListUnmanaged(FaceData), allocator: main.heap.NeverFailingAllocator) void {
|
||||
const model = blocks.meshes.model(block);
|
||||
models.models.items[model].appendNeighborFacingQuadsToList(list, allocator, block, neighbor, x, y, z, backFace);
|
||||
}
|
||||
|
||||
pub fn generateMesh(self: *ChunkMesh, lightRefreshList: *main.List(*ChunkMesh)) void {
|
||||
var alwaysViewThroughMask: [chunk.chunkSize][chunk.chunkSize]u32 = undefined;
|
||||
@memset(std.mem.asBytes(&alwaysViewThroughMask), 0);
|
||||
@ -933,6 +871,16 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
var hasFaces: [chunk.chunkSize][chunk.chunkSize]u32 = undefined;
|
||||
@memset(std.mem.asBytes(&hasFaces), 0);
|
||||
self.mutex.lock();
|
||||
|
||||
var transparentCore: main.ListUnmanaged(FaceData) = .{};
|
||||
defer transparentCore.deinit(main.stackAllocator);
|
||||
var opaqueCore: main.ListUnmanaged(FaceData) = .{};
|
||||
defer opaqueCore.deinit(main.stackAllocator);
|
||||
var transparentOptional: main.ListUnmanaged(FaceData) = .{};
|
||||
defer transparentOptional.deinit(main.stackAllocator);
|
||||
var opaqueOptional: main.ListUnmanaged(FaceData) = .{};
|
||||
defer opaqueOptional.deinit(main.stackAllocator);
|
||||
|
||||
const OcclusionInfo = packed struct {
|
||||
canSeeNeighbor: u6 = 0,
|
||||
canSeeAllNeighbors: bool = false,
|
||||
@ -1029,9 +977,9 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
if(occlusionInfo.hasInternalQuads) {
|
||||
const block = self.chunk.data.palette[paletteId];
|
||||
if(block.transparent()) {
|
||||
self.transparentMesh.appendInternalQuadsToCore(block, x, y, z, false, false);
|
||||
appendInternalQuads(block, x, y, z, false, &transparentCore, main.stackAllocator);
|
||||
} else {
|
||||
self.opaqueMesh.appendInternalQuadsToCore(block, x, y, z, false, false);
|
||||
appendInternalQuads(block, x, y, z, false, &opaqueCore, main.stackAllocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1055,11 +1003,11 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
}
|
||||
if(block.transparent()) {
|
||||
if(block.hasBackFace()) {
|
||||
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false);
|
||||
appendNeighborFacingQuads(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, &transparentCore, main.stackAllocator);
|
||||
}
|
||||
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x - 1), @intCast(y), z, false, false);
|
||||
appendNeighborFacingQuads(block, neighbor, @intCast(x - 1), @intCast(y), z, false, &transparentCore, main.stackAllocator);
|
||||
} else {
|
||||
self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x - 1), @intCast(y), z, false, initialAlwaysViewThroughMask[x - 1][y] & setBit != 0);
|
||||
appendNeighborFacingQuads(block, neighbor, @intCast(x - 1), @intCast(y), z, false, if(initialAlwaysViewThroughMask[x - 1][y] & setBit != 0) &opaqueOptional else &opaqueCore, main.stackAllocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1082,11 +1030,11 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
}
|
||||
if(block.transparent()) {
|
||||
if(block.hasBackFace()) {
|
||||
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false);
|
||||
appendNeighborFacingQuads(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, &transparentCore, main.stackAllocator);
|
||||
}
|
||||
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x + 1), @intCast(y), z, false, false);
|
||||
appendNeighborFacingQuads(block, neighbor, @intCast(x + 1), @intCast(y), z, false, &transparentCore, main.stackAllocator);
|
||||
} else {
|
||||
self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x + 1), @intCast(y), z, false, initialAlwaysViewThroughMask[x + 1][y] & setBit != 0);
|
||||
appendNeighborFacingQuads(block, neighbor, @intCast(x + 1), @intCast(y), z, false, if(initialAlwaysViewThroughMask[x + 1][y] & setBit != 0) &opaqueOptional else &opaqueCore, main.stackAllocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1109,11 +1057,11 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
}
|
||||
if(block.transparent()) {
|
||||
if(block.hasBackFace()) {
|
||||
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false);
|
||||
appendNeighborFacingQuads(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, &transparentCore, main.stackAllocator);
|
||||
}
|
||||
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y - 1), z, false, false);
|
||||
appendNeighborFacingQuads(block, neighbor, @intCast(x), @intCast(y - 1), z, false, &transparentCore, main.stackAllocator);
|
||||
} else {
|
||||
self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y - 1), z, false, initialAlwaysViewThroughMask[x][y - 1] & setBit != 0);
|
||||
appendNeighborFacingQuads(block, neighbor, @intCast(x), @intCast(y - 1), z, false, if(initialAlwaysViewThroughMask[x][y - 1] & setBit != 0) &opaqueOptional else &opaqueCore, main.stackAllocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1136,11 +1084,11 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
}
|
||||
if(block.transparent()) {
|
||||
if(block.hasBackFace()) {
|
||||
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false);
|
||||
appendNeighborFacingQuads(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, &transparentCore, main.stackAllocator);
|
||||
}
|
||||
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y + 1), z, false, false);
|
||||
appendNeighborFacingQuads(block, neighbor, @intCast(x), @intCast(y + 1), z, false, &transparentCore, main.stackAllocator);
|
||||
} else {
|
||||
self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y + 1), z, false, initialAlwaysViewThroughMask[x][y + 1] & setBit != 0);
|
||||
appendNeighborFacingQuads(block, neighbor, @intCast(x), @intCast(y + 1), z, false, if(initialAlwaysViewThroughMask[x][y + 1] & setBit != 0) &opaqueOptional else &opaqueCore, main.stackAllocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1163,11 +1111,11 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
}
|
||||
if(block.transparent()) {
|
||||
if(block.hasBackFace()) {
|
||||
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false);
|
||||
appendNeighborFacingQuads(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, &transparentCore, main.stackAllocator);
|
||||
}
|
||||
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z - 1, false, false);
|
||||
appendNeighborFacingQuads(block, neighbor, @intCast(x), @intCast(y), z - 1, false, &transparentCore, main.stackAllocator);
|
||||
} else {
|
||||
self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z - 1, false, initialAlwaysViewThroughMask[x][y] << 1 & setBit != 0);
|
||||
appendNeighborFacingQuads(block, neighbor, @intCast(x), @intCast(y), z - 1, false, if(initialAlwaysViewThroughMask[x][y] << 1 & setBit != 0) &opaqueOptional else &opaqueCore, main.stackAllocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1190,11 +1138,11 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
}
|
||||
if(block.transparent()) {
|
||||
if(block.hasBackFace()) {
|
||||
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false);
|
||||
appendNeighborFacingQuads(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, &transparentCore, main.stackAllocator);
|
||||
}
|
||||
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z + 1, false, false);
|
||||
appendNeighborFacingQuads(block, neighbor, @intCast(x), @intCast(y), z + 1, false, &transparentCore, main.stackAllocator);
|
||||
} else {
|
||||
self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z + 1, false, initialAlwaysViewThroughMask[x][y] >> 1 & setBit != 0);
|
||||
appendNeighborFacingQuads(block, neighbor, @intCast(x), @intCast(y), z + 1, false, if(initialAlwaysViewThroughMask[x][y] >> 1 & setBit != 0) &opaqueOptional else &opaqueCore, main.stackAllocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1203,6 +1151,12 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
|
||||
self.mutex.unlock();
|
||||
|
||||
self.opaqueMesh.replaceRange(.core, opaqueCore.items);
|
||||
self.opaqueMesh.replaceRange(.optional, opaqueOptional.items);
|
||||
|
||||
self.transparentMesh.replaceRange(.core, transparentCore.items);
|
||||
self.transparentMesh.replaceRange(.optional, transparentOptional.items);
|
||||
|
||||
self.finishNeighbors(lightRefreshList);
|
||||
}
|
||||
|
||||
@ -1243,10 +1197,6 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
if(neighborBlock.mode().dependsOnNeighbors) {
|
||||
if(neighborBlock.mode().updateData(&neighborBlock, neighbor.reverse(), newBlock)) {
|
||||
neighborChunkMesh.chunk.data.setValue(index, neighborBlock);
|
||||
neighborChunkMesh.opaqueMesh.coreFaces.clearRetainingCapacity();
|
||||
neighborChunkMesh.transparentMesh.coreFaces.clearRetainingCapacity();
|
||||
neighborChunkMesh.opaqueMesh.optionalFaces.clearRetainingCapacity();
|
||||
neighborChunkMesh.transparentMesh.optionalFaces.clearRetainingCapacity();
|
||||
neighborChunkMesh.mutex.unlock();
|
||||
neighborChunkMesh.updateBlockLight(@intCast(nx & chunk.chunkMask), @intCast(ny & chunk.chunkMask), @intCast(nz & chunk.chunkMask), neighborBlock, &lightRefreshList);
|
||||
neighborChunkMesh.generateMesh(&lightRefreshList);
|
||||
@ -1302,10 +1252,6 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
self.lastNeighborsHigherLod[chunk.Neighbor.dirUp.toInt()] = null;
|
||||
self.lastNeighborsSameLod[chunk.Neighbor.dirUp.toInt()] = null;
|
||||
}
|
||||
self.opaqueMesh.coreFaces.clearRetainingCapacity();
|
||||
self.transparentMesh.coreFaces.clearRetainingCapacity();
|
||||
self.opaqueMesh.optionalFaces.clearRetainingCapacity();
|
||||
self.transparentMesh.optionalFaces.clearRetainingCapacity();
|
||||
self.mutex.unlock();
|
||||
self.generateMesh(&lightRefreshList); // TODO: Batch mesh updates instead of applying them for each block changes.
|
||||
self.mutex.lock();
|
||||
@ -1319,7 +1265,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
self.uploadData();
|
||||
}
|
||||
|
||||
fn clearNeighbor(self: *ChunkMesh, neighbor: chunk.Neighbor, comptime isLod: bool) void {
|
||||
fn clearNeighborA(self: *ChunkMesh, neighbor: chunk.Neighbor, comptime isLod: bool) void {
|
||||
self.opaqueMesh.clearNeighbor(neighbor, isLod);
|
||||
self.transparentMesh.clearNeighbor(neighbor, isLod);
|
||||
}
|
||||
@ -1377,8 +1323,16 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
if(self.lastNeighborsSameLod[neighbor.toInt()] == neighborMesh) break :sameLodBlock;
|
||||
self.lastNeighborsSameLod[neighbor.toInt()] = neighborMesh;
|
||||
neighborMesh.lastNeighborsSameLod[neighbor.reverse().toInt()] = self;
|
||||
self.clearNeighbor(neighbor, false);
|
||||
neighborMesh.clearNeighbor(neighbor.reverse(), false);
|
||||
|
||||
var transparentSelf: main.ListUnmanaged(FaceData) = .{};
|
||||
defer transparentSelf.deinit(main.stackAllocator);
|
||||
var opaqueSelf: main.ListUnmanaged(FaceData) = .{};
|
||||
defer opaqueSelf.deinit(main.stackAllocator);
|
||||
var transparentNeighbor: main.ListUnmanaged(FaceData) = .{};
|
||||
defer transparentNeighbor.deinit(main.stackAllocator);
|
||||
var opaqueNeighbor: main.ListUnmanaged(FaceData) = .{};
|
||||
defer opaqueNeighbor.deinit(main.stackAllocator);
|
||||
|
||||
const x3: i32 = if(neighbor.isPositive()) chunk.chunkMask else 0;
|
||||
var x1: i32 = 0;
|
||||
while(x1 < chunk.chunkSize) : (x1 += 1) {
|
||||
@ -1410,25 +1364,30 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
if(canBeSeenThroughOtherBlock(block, otherBlock, neighbor)) {
|
||||
if(block.transparent()) {
|
||||
if(block.hasBackFace()) {
|
||||
self.transparentMesh.appendNeighborFacingQuadsToNeighbor(block, neighbor.reverse(), x, y, z, true, false);
|
||||
appendNeighborFacingQuads(block, neighbor.reverse(), x, y, z, true, &transparentSelf, main.stackAllocator);
|
||||
}
|
||||
neighborMesh.transparentMesh.appendNeighborFacingQuadsToNeighbor(block, neighbor, otherX, otherY, otherZ, false, false);
|
||||
appendNeighborFacingQuads(block, neighbor, otherX, otherY, otherZ, false, &transparentNeighbor, main.stackAllocator);
|
||||
} else {
|
||||
neighborMesh.opaqueMesh.appendNeighborFacingQuadsToNeighbor(block, neighbor, otherX, otherY, otherZ, false, false);
|
||||
appendNeighborFacingQuads(block, neighbor, otherX, otherY, otherZ, false, &opaqueNeighbor, main.stackAllocator);
|
||||
}
|
||||
}
|
||||
if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor.reverse())) {
|
||||
if(otherBlock.transparent()) {
|
||||
if(otherBlock.hasBackFace()) {
|
||||
neighborMesh.transparentMesh.appendNeighborFacingQuadsToNeighbor(otherBlock, neighbor, otherX, otherY, otherZ, true, false);
|
||||
appendNeighborFacingQuads(otherBlock, neighbor, otherX, otherY, otherZ, true, &transparentNeighbor, main.stackAllocator);
|
||||
}
|
||||
self.transparentMesh.appendNeighborFacingQuadsToNeighbor(otherBlock, neighbor.reverse(), x, y, z, false, false);
|
||||
appendNeighborFacingQuads(otherBlock, neighbor.reverse(), x, y, z, false, &transparentSelf, main.stackAllocator);
|
||||
} else {
|
||||
self.opaqueMesh.appendNeighborFacingQuadsToNeighbor(otherBlock, neighbor.reverse(), x, y, z, false, false);
|
||||
appendNeighborFacingQuads(otherBlock, neighbor.reverse(), x, y, z, false, &opaqueSelf, main.stackAllocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.opaqueMesh.replaceRange(.neighbor(neighbor), opaqueSelf.items);
|
||||
self.transparentMesh.replaceRange(.neighbor(neighbor), transparentSelf.items);
|
||||
neighborMesh.opaqueMesh.replaceRange(.neighbor(neighbor.reverse()), opaqueNeighbor.items);
|
||||
neighborMesh.transparentMesh.replaceRange(.neighbor(neighbor.reverse()), transparentNeighbor.items);
|
||||
|
||||
_ = neighborMesh.needsLightRefresh.store(true, .release);
|
||||
neighborMesh.increaseRefCount();
|
||||
lightRefreshList.append(neighborMesh);
|
||||
@ -1436,7 +1395,8 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
if(self.lastNeighborsSameLod[neighbor.toInt()] != null) {
|
||||
self.clearNeighbor(neighbor, false);
|
||||
self.opaqueMesh.replaceRange(.neighbor(neighbor), &.{});
|
||||
self.transparentMesh.replaceRange(.neighbor(neighbor), &.{});
|
||||
self.lastNeighborsSameLod[neighbor.toInt()] = null;
|
||||
}
|
||||
}
|
||||
@ -1446,7 +1406,8 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
if(self.lastNeighborsHigherLod[neighbor.toInt()] != null) {
|
||||
self.clearNeighbor(neighbor, true);
|
||||
self.opaqueMesh.replaceRange(.neighborLod(neighbor), &.{});
|
||||
self.transparentMesh.replaceRange(.neighborLod(neighbor), &.{});
|
||||
self.lastNeighborsHigherLod[neighbor.toInt()] = null;
|
||||
}
|
||||
continue;
|
||||
@ -1457,7 +1418,12 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
defer neighborMesh.mutex.unlock();
|
||||
if(self.lastNeighborsHigherLod[neighbor.toInt()] == neighborMesh) continue;
|
||||
self.lastNeighborsHigherLod[neighbor.toInt()] = neighborMesh;
|
||||
self.clearNeighbor(neighbor, true);
|
||||
|
||||
var transparentSelf: main.ListUnmanaged(FaceData) = .{};
|
||||
defer transparentSelf.deinit(main.stackAllocator);
|
||||
var opaqueSelf: main.ListUnmanaged(FaceData) = .{};
|
||||
defer opaqueSelf.deinit(main.stackAllocator);
|
||||
|
||||
const x3: i32 = if(neighbor.isPositive()) chunk.chunkMask else 0;
|
||||
const offsetX = @divExact(self.pos.wx, self.pos.voxelSize) & chunk.chunkSize;
|
||||
const offsetY = @divExact(self.pos.wy, self.pos.voxelSize) & chunk.chunkSize;
|
||||
@ -1491,18 +1457,20 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
if(settings.leavesQuality == 0) otherBlock.typ = otherBlock.opaqueVariant();
|
||||
if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor.reverse())) {
|
||||
if(otherBlock.transparent()) {
|
||||
self.transparentMesh.appendNeighborFacingQuadsToNeighbor(otherBlock, neighbor.reverse(), x, y, z, false, true);
|
||||
appendNeighborFacingQuads(otherBlock, neighbor.reverse(), x, y, z, false, &transparentSelf, main.stackAllocator);
|
||||
} else {
|
||||
self.opaqueMesh.appendNeighborFacingQuadsToNeighbor(otherBlock, neighbor.reverse(), x, y, z, false, true);
|
||||
appendNeighborFacingQuads(otherBlock, neighbor.reverse(), x, y, z, false, &opaqueSelf, main.stackAllocator);
|
||||
}
|
||||
}
|
||||
if(block.hasBackFace()) {
|
||||
if(canBeSeenThroughOtherBlock(block, otherBlock, neighbor)) {
|
||||
self.transparentMesh.appendNeighborFacingQuadsToNeighbor(block, neighbor.reverse(), x, y, z, true, true);
|
||||
appendNeighborFacingQuads(block, neighbor.reverse(), x, y, z, true, &transparentSelf, main.stackAllocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.opaqueMesh.replaceRange(.neighborLod(neighbor), opaqueSelf.items);
|
||||
self.transparentMesh.replaceRange(.neighborLod(neighbor), transparentSelf.items);
|
||||
}
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
@ -1541,33 +1509,26 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
var needsUpdate: bool = false;
|
||||
if(self.transparentMesh.wasChanged) {
|
||||
self.transparentMesh.wasChanged = false;
|
||||
self.transparentMesh.mutex.lock();
|
||||
defer self.transparentMesh.mutex.unlock();
|
||||
var len: usize = self.transparentMesh.coreLen;
|
||||
var offset: usize = self.transparentMesh.coreLen;
|
||||
self.transparentMesh.lock.lockRead();
|
||||
defer self.transparentMesh.lock.unlockRead();
|
||||
var len: usize = 0;
|
||||
const coreList = self.transparentMesh.completeList.getRange(.core);
|
||||
len += coreList.len;
|
||||
var list: [6][]FaceData = undefined;
|
||||
for(0..6) |i| {
|
||||
const neighborLen = self.transparentMesh.sameLodLens[i];
|
||||
if(!self.isNeighborLod[i]) {
|
||||
list[i] = self.transparentMesh.completeList[offset..][0..neighborLen];
|
||||
len += neighborLen;
|
||||
list[i] = self.transparentMesh.completeList.getRange(.neighbor(@enumFromInt(i)));
|
||||
} else {
|
||||
list[i] = self.transparentMesh.completeList.getRange(.neighborLod(@enumFromInt(i)));
|
||||
}
|
||||
offset += neighborLen;
|
||||
}
|
||||
for(0..6) |i| {
|
||||
const neighborLen = self.transparentMesh.higherLodLens[i];
|
||||
if(self.isNeighborLod[i]) {
|
||||
list[i] = self.transparentMesh.completeList[offset..][0..neighborLen];
|
||||
len += neighborLen;
|
||||
}
|
||||
offset += neighborLen;
|
||||
len += list[i].len;
|
||||
}
|
||||
self.currentSorting = main.globalAllocator.realloc(self.currentSorting, len);
|
||||
self.sortingOutputBuffer = main.globalAllocator.realloc(self.sortingOutputBuffer, len + self.blockBreakingFaces.items.len);
|
||||
for(0..self.transparentMesh.coreLen) |i| {
|
||||
self.currentSorting[i].face = self.transparentMesh.completeList[i];
|
||||
for(0..coreList.len) |i| {
|
||||
self.currentSorting[i].face = coreList[i];
|
||||
}
|
||||
offset = self.transparentMesh.coreLen;
|
||||
var offset = coreList.len;
|
||||
for(0..6) |n| {
|
||||
for(0..list[n].len) |i| {
|
||||
self.currentSorting[offset + i].face = list[n][i];
|
||||
|
@ -980,7 +980,9 @@ fn addBreakingAnimationFace(pos: Vec3i, quadIndex: u16, texture: u16, neighbor:
|
||||
defer mesh.mutex.unlock();
|
||||
const lightIndex = blk: {
|
||||
const meshData = if(isTransparent) &mesh.transparentMesh else &mesh.opaqueMesh;
|
||||
for(meshData.completeList) |face| {
|
||||
meshData.lock.lockRead();
|
||||
defer meshData.lock.unlockRead();
|
||||
for(meshData.completeList.getEverything()) |face| {
|
||||
if(face.position.x == relPos[0] and face.position.y == relPos[1] and face.position.z == relPos[2] and face.blockAndQuad.quadIndex == quadIndex) {
|
||||
break :blk face.position.lightIndex;
|
||||
}
|
||||
|
@ -433,6 +433,85 @@ pub fn ListUnmanaged(comptime T: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
/// Holds multiple arrays sequentially in memory.
|
||||
/// Allows addressing and remove each subarray individually, as well as iterating through all of them at once.
|
||||
pub fn MultiArray(T: type, Range: type) type {
|
||||
const size = @typeInfo(Range).@"enum".fields.len;
|
||||
std.debug.assert(@typeInfo(Range).@"enum".is_exhaustive);
|
||||
for(@typeInfo(Range).@"enum".fields) |field| {
|
||||
std.debug.assert(field.value < size);
|
||||
}
|
||||
return struct {
|
||||
offsets: [size + 1]usize = @splat(0),
|
||||
items: [*]T = undefined,
|
||||
capacity: usize = 0,
|
||||
|
||||
pub fn initCapacity(allocator: NeverFailingAllocator, capacity: usize) @This() {
|
||||
return .{
|
||||
.items = allocator.alloc(T, capacity)[0..0],
|
||||
.capacity = capacity,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: @This(), allocator: NeverFailingAllocator) void {
|
||||
if(self.capacity != 0) {
|
||||
allocator.free(self.items[0..self.capacity]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clearAndFree(self: *@This(), allocator: NeverFailingAllocator) void {
|
||||
self.deinit(allocator);
|
||||
self.* = .{};
|
||||
}
|
||||
|
||||
pub fn clearRetainingCapacity(self: *@This()) void {
|
||||
self.offsets = @splat(0);
|
||||
}
|
||||
|
||||
pub fn ensureCapacity(self: *@This(), allocator: NeverFailingAllocator, newCapacity: usize) void {
|
||||
if(newCapacity <= self.capacity) return;
|
||||
const newAllocation = allocator.realloc(self.items[0..self.capacity], newCapacity);
|
||||
self.items = newAllocation.ptr;
|
||||
self.capacity = newAllocation.len;
|
||||
}
|
||||
|
||||
pub fn addMany(self: *@This(), allocator: NeverFailingAllocator, n: usize) []T {
|
||||
self.ensureFreeCapacity(allocator, n);
|
||||
return self.addManyAssumeCapacity(n);
|
||||
}
|
||||
|
||||
pub fn replaceRange(self: *@This(), allocator: NeverFailingAllocator, range: Range, elems: []const T) void {
|
||||
const i: usize = @intFromEnum(range);
|
||||
const oldLen = self.offsets[i + 1] - self.offsets[i];
|
||||
self.ensureCapacity(allocator, self.offsets[size] - oldLen + elems.len);
|
||||
const startIndex = self.offsets[i + 1];
|
||||
const newStartIndex = self.offsets[i + 1] - oldLen + elems.len;
|
||||
const endIndex = self.offsets[size];
|
||||
const newEndIndex = self.offsets[size] - oldLen + elems.len;
|
||||
if(newStartIndex > startIndex) {
|
||||
std.mem.copyBackwards(T, self.items[newStartIndex..newEndIndex], self.items[startIndex..endIndex]);
|
||||
} else {
|
||||
std.mem.copyForwards(T, self.items[newStartIndex..newEndIndex], self.items[startIndex..endIndex]);
|
||||
}
|
||||
@memcpy(self.items[self.offsets[i]..][0..elems.len], elems);
|
||||
for(self.offsets[i + 1 ..]) |*offset| {
|
||||
offset.* = offset.* - oldLen + elems.len;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getRange(self: *@This(), range: Range) []T {
|
||||
const i: usize = @intFromEnum(range);
|
||||
const startIndex = self.offsets[i];
|
||||
const endIndex = self.offsets[i + 1];
|
||||
return self.items[startIndex..endIndex];
|
||||
}
|
||||
|
||||
pub fn getEverything(self: *@This()) []T {
|
||||
return self.items[0..self.offsets[size]];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const page_size_min = std.heap.page_size_min;
|
||||
const page_size_max = std.heap.page_size_max;
|
||||
const pageSize = std.heap.pageSize;
|
||||
|
Loading…
x
Reference in New Issue
Block a user