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;
|
pub const chunkMask: i32 = chunkSize - 1;
|
||||||
|
|
||||||
/// Contains a bunch of constants used to describe neighboring blocks.
|
/// 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.
|
/// How many neighbors there are.
|
||||||
pub const neighbors: u3 = 6;
|
pub const neighbors: u3 = 6;
|
||||||
/// Directions → Index
|
/// 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 len: usize = self.coreFaces.items.len;
|
||||||
var neighborFaceLists: [6][]FaceData = undefined;
|
var neighborFaceLists: [6][]FaceData = undefined;
|
||||||
for(0..6) |i| {
|
for(0..6) |i| {
|
||||||
if(parent.lastNeighborsSameLod[i] == null) {
|
if(isNeighborLod[i]) {
|
||||||
neighborFaceLists[i] = self.neighborFacesHigherLod[i].items;
|
neighborFaceLists[i] = self.neighborFacesHigherLod[i].items;
|
||||||
} else {
|
} else {
|
||||||
neighborFaceLists[i] = self.neighborFacesSameLod[i].items;
|
neighborFaceLists[i] = self.neighborFacesSameLod[i].items;
|
||||||
@ -771,6 +771,7 @@ pub const meshing = struct {
|
|||||||
transparentMesh: PrimitiveMesh,
|
transparentMesh: PrimitiveMesh,
|
||||||
lastNeighborsSameLod: [6]?*const ChunkMesh = [_]?*const ChunkMesh{null} ** 6,
|
lastNeighborsSameLod: [6]?*const ChunkMesh = [_]?*const ChunkMesh{null} ** 6,
|
||||||
lastNeighborsHigherLod: [6]?*const ChunkMesh = [_]?*const ChunkMesh{null} ** 6,
|
lastNeighborsHigherLod: [6]?*const ChunkMesh = [_]?*const ChunkMesh{null} ** 6,
|
||||||
|
forceHigherLod: [6]bool = .{false} ** 6,
|
||||||
visibilityMask: u8 = 0xff,
|
visibilityMask: u8 = 0xff,
|
||||||
currentSorting: []SortingData = &.{},
|
currentSorting: []SortingData = &.{},
|
||||||
sortingOutputBuffer: []FaceData = &.{},
|
sortingOutputBuffer: []FaceData = &.{},
|
||||||
@ -1015,15 +1016,11 @@ pub const meshing = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(neighborMesh != self) {
|
if(neighborMesh != self) {
|
||||||
try neighborMesh.opaqueMesh.finish(neighborMesh);
|
try neighborMesh.uploadData();
|
||||||
try neighborMesh.voxelMesh.finish(neighborMesh);
|
|
||||||
try neighborMesh.transparentMesh.finish(neighborMesh);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.chunk.blocks[getIndex(x, y, z)] = newBlock;
|
self.chunk.blocks[getIndex(x, y, z)] = newBlock;
|
||||||
try self.opaqueMesh.finish(self);
|
try self.uploadData();
|
||||||
try self.voxelMesh.finish(self);
|
|
||||||
try self.transparentMesh.finish(self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn constructFaceData(block: Block, normal: u3, x: i32, y: i32, z: i32, comptime backFace: bool) FaceData {
|
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 {
|
pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void {
|
||||||
for(Neighbors.iterable) |neighbor| {
|
for(Neighbors.iterable) |neighbor| {
|
||||||
const nullNeighborMesh = renderer.RenderStructure.getNeighborFromRenderThread(self.pos, self.pos.voxelSize, 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;
|
if(self.lastNeighborsSameLod[neighbor] == neighborMesh) continue;
|
||||||
self.lastNeighborsSameLod[neighbor] = neighborMesh;
|
self.lastNeighborsSameLod[neighbor] = neighborMesh;
|
||||||
neighborMesh.lastNeighborsSameLod[neighbor ^ 1] = self;
|
neighborMesh.lastNeighborsSameLod[neighbor ^ 1] = self;
|
||||||
self.opaqueMesh.clearNeighbor(neighbor, false);
|
self.clearNeighbor(neighbor, false);
|
||||||
self.voxelMesh.clearNeighbor(neighbor, false);
|
neighborMesh.clearNeighbor(neighbor ^ 1, false);
|
||||||
self.transparentMesh.clearNeighbor(neighbor, false);
|
|
||||||
neighborMesh.opaqueMesh.clearNeighbor(neighbor ^ 1, false);
|
|
||||||
neighborMesh.voxelMesh.clearNeighbor(neighbor ^ 1, false);
|
|
||||||
neighborMesh.transparentMesh.clearNeighbor(neighbor ^ 1, false);
|
|
||||||
const x3: i32 = if(neighbor & 1 == 0) chunkMask else 0;
|
const x3: i32 = if(neighbor & 1 == 0) chunkMask else 0;
|
||||||
var x1: i32 = 0;
|
var x1: i32 = 0;
|
||||||
while(x1 < chunkSize): (x1 += 1) {
|
while(x1 < chunkSize): (x1 += 1) {
|
||||||
@ -1104,33 +1124,25 @@ pub const meshing = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try neighborMesh.opaqueMesh.finish(neighborMesh);
|
try neighborMesh.uploadData();
|
||||||
try neighborMesh.voxelMesh.finish(neighborMesh);
|
|
||||||
try neighborMesh.transparentMesh.finish(neighborMesh);
|
|
||||||
} else {
|
} else {
|
||||||
if(self.lastNeighborsSameLod[neighbor] != null) {
|
if(self.lastNeighborsSameLod[neighbor] != null) {
|
||||||
self.opaqueMesh.clearNeighbor(neighbor, false);
|
self.clearNeighbor(neighbor, false);
|
||||||
self.voxelMesh.clearNeighbor(neighbor, false);
|
self.lastNeighborsSameLod[neighbor] = null;
|
||||||
self.transparentMesh.clearNeighbor(neighbor, false);
|
|
||||||
}
|
}
|
||||||
self.lastNeighborsSameLod[neighbor] = null;
|
|
||||||
}
|
}
|
||||||
// lod border:
|
// lod border:
|
||||||
if(self.pos.voxelSize == 1 << settings.highestLOD) continue;
|
if(self.pos.voxelSize == 1 << settings.highestLOD) continue;
|
||||||
const neighborMesh = renderer.RenderStructure.getNeighborFromRenderThread(self.pos, 2*self.pos.voxelSize, neighbor) orelse {
|
const neighborMesh = renderer.RenderStructure.getNeighborFromRenderThread(self.pos, 2*self.pos.voxelSize, neighbor) orelse {
|
||||||
if(self.lastNeighborsHigherLod[neighbor] != null) {
|
if(self.lastNeighborsHigherLod[neighbor] != null) {
|
||||||
self.opaqueMesh.clearNeighbor(neighbor, true);
|
self.clearNeighbor(neighbor, true);
|
||||||
self.voxelMesh.clearNeighbor(neighbor, true);
|
self.lastNeighborsHigherLod[neighbor] = null;
|
||||||
self.transparentMesh.clearNeighbor(neighbor, true);
|
|
||||||
}
|
}
|
||||||
self.lastNeighborsHigherLod[neighbor] = null;
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if(self.lastNeighborsHigherLod[neighbor] == neighborMesh) continue;
|
if(self.lastNeighborsHigherLod[neighbor] == neighborMesh) continue;
|
||||||
self.lastNeighborsHigherLod[neighbor] = neighborMesh;
|
self.lastNeighborsHigherLod[neighbor] = neighborMesh;
|
||||||
self.opaqueMesh.clearNeighbor(neighbor, true);
|
self.clearNeighbor(neighbor, true);
|
||||||
self.voxelMesh.clearNeighbor(neighbor, true);
|
|
||||||
self.transparentMesh.clearNeighbor(neighbor, true);
|
|
||||||
const x3: i32 = if(neighbor & 1 == 0) chunkMask else 0;
|
const x3: i32 = if(neighbor & 1 == 0) chunkMask else 0;
|
||||||
const offsetX = @divExact(self.pos.wx, self.pos.voxelSize) & chunkSize;
|
const offsetX = @divExact(self.pos.wx, self.pos.voxelSize) & chunkSize;
|
||||||
const offsetY = @divExact(self.pos.wy, 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.uploadData();
|
||||||
try self.voxelMesh.finish(self);
|
|
||||||
try self.transparentMesh.finish(self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(self: *ChunkMesh, playerPosition: Vec3d) void {
|
pub fn render(self: *ChunkMesh, playerPosition: Vec3d) void {
|
||||||
|
@ -874,6 +874,7 @@ pub const RenderStructure = struct {
|
|||||||
min: Vec2f,
|
min: Vec2f,
|
||||||
max: Vec2f,
|
max: Vec2f,
|
||||||
active: bool,
|
active: bool,
|
||||||
|
rendered: bool,
|
||||||
};
|
};
|
||||||
const storageSize = 32;
|
const storageSize = 32;
|
||||||
const storageMask = storageSize - 1;
|
const storageMask = storageSize - 1;
|
||||||
@ -903,6 +904,7 @@ pub const RenderStructure = struct {
|
|||||||
storageList.* = try main.globalAllocator.create([storageSize*storageSize*storageSize]ChunkMeshNode);
|
storageList.* = try main.globalAllocator.create([storageSize*storageSize*storageSize]ChunkMeshNode);
|
||||||
for(storageList.*) |*val| {
|
for(storageList.*) |*val| {
|
||||||
val.mesh = null;
|
val.mesh = null;
|
||||||
|
val.rendered = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1223,6 +1225,7 @@ pub const RenderStructure = struct {
|
|||||||
node.min = @splat(-1);
|
node.min = @splat(-1);
|
||||||
node.max = @splat(1);
|
node.max = @splat(1);
|
||||||
node.active = true;
|
node.active = true;
|
||||||
|
node.rendered = true;
|
||||||
try searchList.add(.{
|
try searchList.add(.{
|
||||||
.node = node,
|
.node = node,
|
||||||
.distance = 0,
|
.distance = 0,
|
||||||
@ -1235,14 +1238,17 @@ pub const RenderStructure = struct {
|
|||||||
firstPos.voxelSize *= 2;
|
firstPos.voxelSize *= 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var nodeList = std.ArrayList(*ChunkMeshNode).init(main.globalAllocator);
|
||||||
|
defer nodeList.deinit();
|
||||||
const projRotMat = game.projectionMatrix.mul(game.camera.viewMatrix);
|
const projRotMat = game.projectionMatrix.mul(game.camera.viewMatrix);
|
||||||
while(searchList.removeOrNull()) |data| {
|
while(searchList.removeOrNull()) |data| {
|
||||||
|
try nodeList.append(data.node);
|
||||||
data.node.active = false;
|
data.node.active = false;
|
||||||
const mesh = data.node.mesh.?;
|
const mesh = data.node.mesh.?;
|
||||||
mesh.visibilityMask = 0xff;
|
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 relPos: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{mesh.pos.wx, mesh.pos.wy, mesh.pos.wz})) - playerPos;
|
||||||
const relPosFloat: Vec3f = @floatCast(relPos);
|
const relPosFloat: Vec3f = @floatCast(relPos);
|
||||||
|
var forceNeighborsLod: [6]bool = .{false} ** 6;
|
||||||
for(chunk.Neighbors.iterable) |neighbor| continueNeighborLoop: {
|
for(chunk.Neighbors.iterable) |neighbor| continueNeighborLoop: {
|
||||||
const component = chunk.Neighbors.extractDirectionComponent(neighbor, relPos);
|
const component = chunk.Neighbors.extractDirectionComponent(neighbor, relPos);
|
||||||
if(chunk.Neighbors.isPositive[neighbor] and component + @as(f64, @floatFromInt(chunk.chunkSize*mesh.pos.voxelSize)) <= 0) continue;
|
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;
|
var lod: u3 = data.node.lod;
|
||||||
while(lod <= settings.highestLOD) : (lod += 1) {
|
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);
|
const node = getNodeFromRenderThread(neighborPos);
|
||||||
if(node.mesh != null) {
|
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) {
|
if(node.active) {
|
||||||
node.min = @min(node.min, min);
|
node.min = @min(node.min, min);
|
||||||
node.max = @max(node.max, max);
|
node.max = @max(node.max, max);
|
||||||
@ -1402,17 +1453,17 @@ pub const RenderStructure = struct {
|
|||||||
.node = node,
|
.node = node,
|
||||||
.distance = node.mesh.?.pos.getMaxDistanceSquared(playerPos),
|
.distance = node.mesh.?.pos.getMaxDistanceSquared(playerPos),
|
||||||
});
|
});
|
||||||
|
node.rendered = true;
|
||||||
}
|
}
|
||||||
break :continueNeighborLoop;
|
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) {
|
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});
|
const parent = getNodeFromRenderThread(.{.wx=mesh.pos.wx, .wy=mesh.pos.wy, .wz=mesh.pos.wz, .voxelSize=mesh.pos.voxelSize << 1});
|
||||||
if(parent.mesh) |parentMesh| {
|
if(parent.mesh) |parentMesh| {
|
||||||
@ -1425,6 +1476,10 @@ pub const RenderStructure = struct {
|
|||||||
try mesh.uploadDataAndFinishNeighbors();
|
try mesh.uploadDataAndFinishNeighbors();
|
||||||
mesh.needsNeighborUpdate = false;
|
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;
|
lastPx = px;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user