Dynamically switch to higher lod chunks, when otherwise a high-to-low lod border would be drawn(I have no mesh data for these).

This required dynamically re-uploading some chunk meshes.
Fixes #130
This commit is contained in:
IntegratedQuantum 2023-11-25 16:20:05 +01:00
parent 0b8c4b455b
commit a6248dc5c2
2 changed files with 103 additions and 38 deletions

View File

@ -28,7 +28,7 @@ pub const chunkVolume: u31 = 1 << 3*chunkShift;
pub const chunkMask: i32 = chunkSize - 1;
/// Contains a bunch of constants used to describe neighboring blocks.
pub const Neighbors = struct {
pub const Neighbors = struct { // TODO: Should this be an enum?
/// How many neighbors there are.
pub const neighbors: u3 = 6;
/// Directions Index
@ -573,11 +573,11 @@ pub const meshing = struct {
}
}
fn finish(self: *PrimitiveMesh, parent: *ChunkMesh) !void {
fn finish(self: *PrimitiveMesh, parent: *ChunkMesh, isNeighborLod: [6]bool) !void {
var len: usize = self.coreFaces.items.len;
var neighborFaceLists: [6][]FaceData = undefined;
for(0..6) |i| {
if(parent.lastNeighborsSameLod[i] == null) {
if(isNeighborLod[i]) {
neighborFaceLists[i] = self.neighborFacesHigherLod[i].items;
} else {
neighborFaceLists[i] = self.neighborFacesSameLod[i].items;
@ -771,6 +771,7 @@ pub const meshing = struct {
transparentMesh: PrimitiveMesh,
lastNeighborsSameLod: [6]?*const ChunkMesh = [_]?*const ChunkMesh{null} ** 6,
lastNeighborsHigherLod: [6]?*const ChunkMesh = [_]?*const ChunkMesh{null} ** 6,
forceHigherLod: [6]bool = .{false} ** 6,
visibilityMask: u8 = 0xff,
currentSorting: []SortingData = &.{},
sortingOutputBuffer: []FaceData = &.{},
@ -1015,15 +1016,11 @@ pub const meshing = struct {
}
}
if(neighborMesh != self) {
try neighborMesh.opaqueMesh.finish(neighborMesh);
try neighborMesh.voxelMesh.finish(neighborMesh);
try neighborMesh.transparentMesh.finish(neighborMesh);
try neighborMesh.uploadData();
}
}
self.chunk.blocks[getIndex(x, y, z)] = newBlock;
try self.opaqueMesh.finish(self);
try self.voxelMesh.finish(self);
try self.transparentMesh.finish(self);
try self.uploadData();
}
pub inline fn constructFaceData(block: Block, normal: u3, x: i32, y: i32, z: i32, comptime backFace: bool) FaceData {
@ -1034,6 +1031,33 @@ pub const meshing = struct {
};
}
fn clearNeighbor(self: *ChunkMesh, neighbor: u3, comptime isLod: bool) void {
self.opaqueMesh.clearNeighbor(neighbor, isLod);
self.voxelMesh.clearNeighbor(neighbor, isLod);
self.transparentMesh.clearNeighbor(neighbor, isLod);
}
fn uploadData(self: *ChunkMesh) !void {
const isNeighborLod: [6]bool = .{
self.lastNeighborsSameLod[0] == null or self.forceHigherLod[0],
self.lastNeighborsSameLod[1] == null or self.forceHigherLod[1],
self.lastNeighborsSameLod[2] == null or self.forceHigherLod[2],
self.lastNeighborsSameLod[3] == null or self.forceHigherLod[3],
self.lastNeighborsSameLod[4] == null or self.forceHigherLod[4],
self.lastNeighborsSameLod[5] == null or self.forceHigherLod[5],
};
try self.opaqueMesh.finish(self, isNeighborLod);
try self.voxelMesh.finish(self, isNeighborLod);
try self.transparentMesh.finish(self, isNeighborLod);
}
pub fn changeLodBorders(self: *ChunkMesh, forceHigherLod: [6]bool) !void {
if(!std.meta.eql(forceHigherLod, self.forceHigherLod)) {
self.forceHigherLod = forceHigherLod;
try self.uploadData();
}
}
pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void {
for(Neighbors.iterable) |neighbor| {
const nullNeighborMesh = renderer.RenderStructure.getNeighborFromRenderThread(self.pos, self.pos.voxelSize, neighbor);
@ -1042,12 +1066,8 @@ pub const meshing = struct {
if(self.lastNeighborsSameLod[neighbor] == neighborMesh) continue;
self.lastNeighborsSameLod[neighbor] = neighborMesh;
neighborMesh.lastNeighborsSameLod[neighbor ^ 1] = self;
self.opaqueMesh.clearNeighbor(neighbor, false);
self.voxelMesh.clearNeighbor(neighbor, false);
self.transparentMesh.clearNeighbor(neighbor, false);
neighborMesh.opaqueMesh.clearNeighbor(neighbor ^ 1, false);
neighborMesh.voxelMesh.clearNeighbor(neighbor ^ 1, false);
neighborMesh.transparentMesh.clearNeighbor(neighbor ^ 1, false);
self.clearNeighbor(neighbor, false);
neighborMesh.clearNeighbor(neighbor ^ 1, false);
const x3: i32 = if(neighbor & 1 == 0) chunkMask else 0;
var x1: i32 = 0;
while(x1 < chunkSize): (x1 += 1) {
@ -1104,33 +1124,25 @@ pub const meshing = struct {
}
}
}
try neighborMesh.opaqueMesh.finish(neighborMesh);
try neighborMesh.voxelMesh.finish(neighborMesh);
try neighborMesh.transparentMesh.finish(neighborMesh);
try neighborMesh.uploadData();
} else {
if(self.lastNeighborsSameLod[neighbor] != null) {
self.opaqueMesh.clearNeighbor(neighbor, false);
self.voxelMesh.clearNeighbor(neighbor, false);
self.transparentMesh.clearNeighbor(neighbor, false);
}
self.clearNeighbor(neighbor, false);
self.lastNeighborsSameLod[neighbor] = null;
}
}
// lod border:
if(self.pos.voxelSize == 1 << settings.highestLOD) continue;
const neighborMesh = renderer.RenderStructure.getNeighborFromRenderThread(self.pos, 2*self.pos.voxelSize, neighbor) orelse {
if(self.lastNeighborsHigherLod[neighbor] != null) {
self.opaqueMesh.clearNeighbor(neighbor, true);
self.voxelMesh.clearNeighbor(neighbor, true);
self.transparentMesh.clearNeighbor(neighbor, true);
}
self.clearNeighbor(neighbor, true);
self.lastNeighborsHigherLod[neighbor] = null;
}
continue;
};
if(self.lastNeighborsHigherLod[neighbor] == neighborMesh) continue;
self.lastNeighborsHigherLod[neighbor] = neighborMesh;
self.opaqueMesh.clearNeighbor(neighbor, true);
self.voxelMesh.clearNeighbor(neighbor, true);
self.transparentMesh.clearNeighbor(neighbor, true);
self.clearNeighbor(neighbor, true);
const x3: i32 = if(neighbor & 1 == 0) chunkMask else 0;
const offsetX = @divExact(self.pos.wx, self.pos.voxelSize) & chunkSize;
const offsetY = @divExact(self.pos.wy, self.pos.voxelSize) & chunkSize;
@ -1179,9 +1191,7 @@ pub const meshing = struct {
}
}
}
try self.opaqueMesh.finish(self);
try self.voxelMesh.finish(self);
try self.transparentMesh.finish(self);
try self.uploadData();
}
pub fn render(self: *ChunkMesh, playerPosition: Vec3d) void {

View File

@ -874,6 +874,7 @@ pub const RenderStructure = struct {
min: Vec2f,
max: Vec2f,
active: bool,
rendered: bool,
};
const storageSize = 32;
const storageMask = storageSize - 1;
@ -903,6 +904,7 @@ pub const RenderStructure = struct {
storageList.* = try main.globalAllocator.create([storageSize*storageSize*storageSize]ChunkMeshNode);
for(storageList.*) |*val| {
val.mesh = null;
val.rendered = false;
}
}
}
@ -1223,6 +1225,7 @@ pub const RenderStructure = struct {
node.min = @splat(-1);
node.max = @splat(1);
node.active = true;
node.rendered = true;
try searchList.add(.{
.node = node,
.distance = 0,
@ -1235,14 +1238,17 @@ pub const RenderStructure = struct {
firstPos.voxelSize *= 2;
}
}
var nodeList = std.ArrayList(*ChunkMeshNode).init(main.globalAllocator);
defer nodeList.deinit();
const projRotMat = game.projectionMatrix.mul(game.camera.viewMatrix);
while(searchList.removeOrNull()) |data| {
try nodeList.append(data.node);
data.node.active = false;
const mesh = data.node.mesh.?;
mesh.visibilityMask = 0xff;
try meshList.append(mesh);
const relPos: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{mesh.pos.wx, mesh.pos.wy, mesh.pos.wz})) - playerPos;
const relPosFloat: Vec3f = @floatCast(relPos);
var forceNeighborsLod: [6]bool = .{false} ** 6;
for(chunk.Neighbors.iterable) |neighbor| continueNeighborLoop: {
const component = chunk.Neighbors.extractDirectionComponent(neighbor, relPos);
if(chunk.Neighbors.isPositive[neighbor] and component + @as(f64, @floatFromInt(chunk.chunkSize*mesh.pos.voxelSize)) <= 0) continue;
@ -1388,8 +1394,53 @@ pub const RenderStructure = struct {
};
var lod: u3 = data.node.lod;
while(lod <= settings.highestLOD) : (lod += 1) {
defer {
neighborPos.wx &= ~@as(i32, neighborPos.voxelSize*chunk.chunkSize);
neighborPos.wy &= ~@as(i32, neighborPos.voxelSize*chunk.chunkSize);
neighborPos.wz &= ~@as(i32, neighborPos.voxelSize*chunk.chunkSize);
neighborPos.voxelSize *= 2;
}
const node = getNodeFromRenderThread(neighborPos);
if(node.mesh != null) {
// Ensure that there are no high-to-low lod transitions, which would produce cracks.
if(lod == data.node.lod and lod != settings.highestLOD and !node.rendered) {
var isValid: bool = true;
const relPos2: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{neighborPos.wx, neighborPos.wy, neighborPos.wz})) - playerPos;
for(chunk.Neighbors.iterable) |neighbor2| {
const component2 = chunk.Neighbors.extractDirectionComponent(neighbor2, relPos2);
if(chunk.Neighbors.isPositive[neighbor2] and component2 + @as(f64, @floatFromInt(chunk.chunkSize*node.mesh.?.pos.voxelSize)) >= 0) continue;
if(!chunk.Neighbors.isPositive[neighbor2] and component2 <= 0) continue;
{ // Check the chunk of same lod:
const neighborPos2 = chunk.ChunkPosition{
.wx = neighborPos.wx + chunk.Neighbors.relX[neighbor2]*chunk.chunkSize*neighborPos.voxelSize,
.wy = neighborPos.wy + chunk.Neighbors.relY[neighbor2]*chunk.chunkSize*neighborPos.voxelSize,
.wz = neighborPos.wz + chunk.Neighbors.relZ[neighbor2]*chunk.chunkSize*neighborPos.voxelSize,
.voxelSize = neighborPos.voxelSize,
};
const node2 = getNodeFromRenderThread(neighborPos2);
if(node2.rendered) {
continue;
}
}
{ // Check the chunk of higher lod
const neighborPos2 = chunk.ChunkPosition{
.wx = neighborPos.wx + chunk.Neighbors.relX[neighbor2]*chunk.chunkSize*neighborPos.voxelSize,
.wy = neighborPos.wy + chunk.Neighbors.relY[neighbor2]*chunk.chunkSize*neighborPos.voxelSize,
.wz = neighborPos.wz + chunk.Neighbors.relZ[neighbor2]*chunk.chunkSize*neighborPos.voxelSize,
.voxelSize = neighborPos.voxelSize << 1,
};
const node2 = getNodeFromRenderThread(neighborPos2);
if(node2.rendered) {
isValid = false;
break;
}
}
}
if(!isValid) {
forceNeighborsLod[neighbor] = true;
continue;
}
}
if(node.active) {
node.min = @min(node.min, min);
node.max = @max(node.max, max);
@ -1402,17 +1453,17 @@ pub const RenderStructure = struct {
.node = node,
.distance = node.mesh.?.pos.getMaxDistanceSquared(playerPos),
});
node.rendered = true;
}
break :continueNeighborLoop;
}
neighborPos.wx &= ~@as(i32, neighborPos.voxelSize*chunk.chunkSize);
neighborPos.wy &= ~@as(i32, neighborPos.voxelSize*chunk.chunkSize);
neighborPos.wz &= ~@as(i32, neighborPos.voxelSize*chunk.chunkSize);
neighborPos.voxelSize *= 2;
}
}
try mesh.changeLodBorders(forceNeighborsLod);
}
for(meshList.items) |mesh| {
for(nodeList.items) |node| {
node.rendered = false;
const mesh = node.mesh.?;
if(mesh.pos.voxelSize != @as(u31, 1) << settings.highestLOD) {
const parent = getNodeFromRenderThread(.{.wx=mesh.pos.wx, .wy=mesh.pos.wy, .wz=mesh.pos.wz, .voxelSize=mesh.pos.voxelSize << 1});
if(parent.mesh) |parentMesh| {
@ -1425,6 +1476,10 @@ pub const RenderStructure = struct {
try mesh.uploadDataAndFinishNeighbors();
mesh.needsNeighborUpdate = false;
}
// Remove empty meshes.
if(mesh.opaqueMesh.vertexCount != 0 or mesh.voxelMesh.vertexCount != 0 or mesh.transparentMesh.vertexCount != 0) {
try meshList.append(mesh);
}
}
lastPx = px;