mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 19:28:49 -04:00
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:
parent
0b8c4b455b
commit
a6248dc5c2
@ -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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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 {
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user