Fix chunk meshes at lod borders.

This commit is contained in:
IntegratedQuantum 2022-10-01 16:46:58 +02:00
parent 219957681d
commit 26f4dd2ff2
3 changed files with 102 additions and 21 deletions

View File

@ -5,7 +5,7 @@ out vec2 outTexCoord;
flat out float textureIndex;
out float outNormalVariation;
uniform int visibilityMask;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform vec3 modelPosition;
@ -46,13 +46,13 @@ const vec3[6] normals = vec3[6](
vec3(0, 0, 1),
vec3(0, 0, -1)
);
const vec3[6] positionOffset = vec3[6](
vec3(0, 0, 0),
vec3(0, 1, 0),
vec3(0, 0, 0),
vec3(1, 0, 0),
vec3(0, 0, 0),
vec3(0, 0, 1)
const ivec3[6] positionOffset = ivec3[6](
ivec3(0, 0, 0),
ivec3(0, 1, 0),
ivec3(0, 0, 0),
ivec3(1, 0, 0),
ivec3(0, 0, 0),
ivec3(0, 0, 1)
);
const ivec3[6] textureX = ivec3[6](
ivec3(1, 0, 0),
@ -81,14 +81,20 @@ void main() {
textureIndex = texCoordz + time / animationTimes[texCoordz] % animationFrames[texCoordz];
outTexCoord = vec2(float(vertexID>>1 & 1)*voxelSize, float(vertexID & 1)*voxelSize);
vec3 position = vec3(
ivec3 position = ivec3(
encodedPosition & 31,
encodedPosition >> 5 & 31,
encodedPosition >> 10 & 31
);
int octantIndex = (position.x >> 4) | (position.y >> 4)<<1 | (position.z >> 4)<<2;
if((visibilityMask & 1<<octantIndex) == 0) { // discard face
gl_Position = vec4(-2, -2, -2, 1);
return;
}
position += positionOffset[normal];
position += vec3(equal(textureX[normal], ivec3(-1, -1, -1))) + (vertexID>>1 & 1)*textureX[normal];
position += vec3(equal(textureY[normal], ivec3(-1, -1, -1))) + (vertexID & 1)*textureY[normal];
position += ivec3(equal(textureX[normal], ivec3(-1, -1, -1))) + (vertexID>>1 & 1)*textureX[normal];
position += ivec3(equal(textureY[normal], ivec3(-1, -1, -1))) + (vertexID & 1)*textureY[normal];
vec3 globalPosition = position*voxelSize + modelPosition;

View File

@ -10,6 +10,7 @@ const Shader = graphics.Shader;
const SSBO = graphics.SSBO;
const main = @import("main.zig");
const renderer = @import("renderer.zig");
const settings = @import("settings.zig");
const vec = @import("vec.zig");
const Vec3f = vec.Vec3f;
const Vec3d = vec.Vec3d;
@ -535,6 +536,7 @@ pub const meshing = struct {
@"waterFog.color": c_int,
@"waterFog.density": c_int,
time: c_int,
visibilityMask: c_int,
} = undefined;
var vao: c_uint = undefined;
var vbo: c_uint = undefined;
@ -600,6 +602,7 @@ pub const meshing = struct {
vertexCount: u31 = 0,
generated: bool = false,
mutex: std.Thread.Mutex = std.Thread.Mutex{},
visibilityMask: u8 = 0xff,
pub fn init(allocator: Allocator, pos: ChunkPosition) ChunkMesh {
return ChunkMesh{
@ -664,11 +667,10 @@ pub const meshing = struct {
pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void {
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
if(self.chunk == null) return; // In the mean-time the mesh was discarded and recreated and all the data was lost.
self.generated = true;
self.faces.shrinkRetainingCapacity(self.coreCount);
for(Neighbors.iterable) |neighbor| {
self.neighborStart[neighbor] = @intCast(u31, self.faces.items.len);
var nullNeighborMesh = renderer.RenderStructure.getNeighbor(self.pos, neighbor);
var nullNeighborMesh = renderer.RenderStructure.getNeighbor(self.pos, self.pos.voxelSize, neighbor);
if(nullNeighborMesh) |neighborMesh| {
std.debug.assert(neighborMesh != self);
neighborMesh.mutex.lock();
@ -726,16 +728,61 @@ pub const meshing = struct {
}
neighborMesh.vertexCount = @intCast(u31, 6*(neighborMesh.faces.items.len-1)/2);
neighborMesh.faceData.bufferData(u32, neighborMesh.faces.items);
} else {
// TODO: Resolution boundary.
continue;
}
}
// lod border:
if(self.pos.voxelSize == 1 << settings.highestLOD) continue;
var neighborMesh = renderer.RenderStructure.getNeighbor(self.pos, 2*self.pos.voxelSize, neighbor) orelse return error.LODMissing;
neighborMesh.mutex.lock();
defer neighborMesh.mutex.unlock();
if(neighborMesh.generated) {
const x3: u8 = if(neighbor & 1 == 0) @intCast(u8, chunkMask) else 0;
const offsetX = @divExact(self.pos.wx, self.pos.voxelSize) & chunkSize;
const offsetY = @divExact(self.pos.wy, self.pos.voxelSize) & chunkSize;
const offsetZ = @divExact(self.pos.wz, self.pos.voxelSize) & chunkSize;
var x1: u8 = 0;
while(x1 < chunkSize): (x1 += 1) {
var x2: u8 = 0;
while(x2 < chunkSize): (x2 += 1) {
var x: u8 = undefined;
var y: u8 = undefined;
var z: u8 = undefined;
if(Neighbors.relX[neighbor] != 0) {
x = x3;
y = x1;
z = x2;
} else if(Neighbors.relY[neighbor] != 0) {
x = x1;
y = x3;
z = x2;
} else {
x = x2;
y = x1;
z = x3;
}
var otherX = @intCast(u8, (x+%Neighbors.relX[neighbor]+%offsetX >> 1) & chunkMask);
var otherY = @intCast(u8, (y+%Neighbors.relY[neighbor]+%offsetY >> 1) & chunkMask);
var otherZ = @intCast(u8, (z+%Neighbors.relZ[neighbor]+%offsetZ >> 1) & chunkMask);
var block = (&self.chunk.?.blocks)[getIndex(x, y, z)]; // a little hack that increases speed 100×. TODO: check if this is *that* compiler bug.
var otherBlock = (&neighborMesh.chunk.?.blocks)[getIndex(otherX, otherY, otherZ)]; // a little hack that increases speed 100×. TODO: check if this is *that* compiler bug.
if(block.typ == 0 and otherBlock.typ != 0) { // TODO: Transparency
const normal: u32 = neighbor ^ 1;
const position: u32 = @as(u32, x) | @as(u32, y)<<5 | @as(u32, z)<<10;
const textureNormal = blocks.meshes.textureIndices(otherBlock)[neighbor] | normal<<24;
try self.faces.append(position);
try self.faces.append(textureNormal);
}
}
}
} else {
// TODO: Resolution boundary.
return error.LODMissing;
}
}
self.neighborStart[6] = @intCast(u31, self.faces.items.len);
self.vertexCount = @intCast(u31, 6*(self.faces.items.len-1)/2);
self.faceData.bufferData(u32, self.faces.items);
self.generated = true;
}
pub fn render(self: *ChunkMesh, playerPosition: Vec3d) void {
@ -749,6 +796,7 @@ pub const meshing = struct {
@floatCast(f32, @intToFloat(f64, self.pos.wy) - playerPosition.y),
@floatCast(f32, @intToFloat(f64, self.pos.wz) - playerPosition.z)
);
c.glUniform1i(uniforms.visibilityMask, self.visibilityMask);
self.faceData.bind(3);
c.glDrawElements(c.GL_TRIANGLES, self.vertexCount, c.GL_UNSIGNED_INT, null);
}

View File

@ -493,11 +493,12 @@ pub const RenderStructure = struct {
return storageLists[lod][@intCast(usize, index)];
}
pub fn getNeighbor(_pos: chunk.ChunkPosition, neighbor: u3) ?*chunk.meshing.ChunkMesh {
pub fn getNeighbor(_pos: chunk.ChunkPosition, resolution: chunk.UChunkCoordinate, neighbor: u3) ?*chunk.meshing.ChunkMesh {
var pos = _pos;
pos.wx += pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relX[neighbor];
pos.wy += pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relY[neighbor];
pos.wz += pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relZ[neighbor];
pos.voxelSize = resolution;
var node = _getNode(pos) orelse return null;
return &node.mesh;
}
@ -518,6 +519,7 @@ pub const RenderStructure = struct {
const lod = @intCast(u5, _lod);
var maxRenderDistance = renderDistance*chunk.chunkSize << lod;
if(lod != 0) maxRenderDistance = @floatToInt(i32, @ceil(@intToFloat(f32, maxRenderDistance)*LODFactor));
var sizeShift = chunk.chunkShift + lod;
const size = @intCast(chunk.UChunkCoordinate, chunk.chunkSize << lod);
const mask: chunk.ChunkCoordinate = size - 1;
const invMask: chunk.ChunkCoordinate = ~mask;
@ -576,12 +578,13 @@ pub const RenderStructure = struct {
.x = @intToFloat(f32, size),
.y = @intToFloat(f32, size),
.z = @intToFloat(f32, size),
}) and node.?.drawableChildren < 8) { // TODO: Case where more than 0 and less than 8 exist.
}) and node.?.mesh.visibilityMask != 0) {
try meshes.append(&node.?.mesh);
}
if(lod+1 != storageLists.len and node.?.mesh.generated) {
if(_getNode(.{.wx=x, .wy=y, .wz=z, .voxelSize=@as(chunk.UChunkCoordinate, 1)<<(lod+1)})) |parent| {
parent.drawableChildren += 1;
const octantIndex = @intCast(u3, (x>>sizeShift & 1) | (y>>sizeShift & 1)<<1 | (z>>sizeShift & 1)<<2);
parent.mesh.visibilityMask &= ~(@as(u8, 1) << octantIndex);
}
}
node.?.drawableChildren = 0;
@ -603,6 +606,22 @@ pub const RenderStructure = struct {
for(oldList) |nullMesh| {
if(nullMesh) |mesh| {
if(mesh.shouldBeRemoved) {
if(mesh.mesh.pos.voxelSize != 1 << settings.highestLOD) {
if(_getNode(.{.wx=mesh.mesh.pos.wx, .wy=mesh.mesh.pos.wy, .wz=mesh.mesh.pos.wz, .voxelSize=2*mesh.mesh.pos.voxelSize})) |parent| {
const octantIndex = @intCast(u3, (mesh.mesh.pos.wx>>sizeShift & 1) | (mesh.mesh.pos.wy>>sizeShift & 1)<<1 | (mesh.mesh.pos.wz>>sizeShift & 1)<<2);
parent.mesh.visibilityMask |= @as(u8, 1) << octantIndex;
}
}
// Update the neighbors, so we don't get cracks when we look back:
for(chunk.Neighbors.iterable) |neighbor| {
if(getNeighbor(mesh.mesh.pos, mesh.mesh.pos.voxelSize, neighbor)) |neighborMesh| {
if(neighborMesh.generated) {
neighborMesh.mutex.lock();
defer neighborMesh.mutex.unlock();
try neighborMesh.uploadDataAndFinishNeighbors();
}
}
}
if(mesh.mesh.mutex.tryLock()) { // Make sure there is no task currently running on the thing.
mesh.mesh.mutex.unlock();
mesh.mesh.deinit();
@ -641,14 +660,22 @@ pub const RenderStructure = struct {
mutex.lock();
defer mutex.unlock();
while(updatableList.items.len != 0) {
const pos = updatableList.pop();
const pos = updatableList.orderedRemove(0);
mutex.unlock();
defer mutex.lock();
const nullNode = _getNode(pos);
if(nullNode) |node| {
node.mesh.mutex.lock();
defer node.mesh.mutex.unlock();
try node.mesh.uploadDataAndFinishNeighbors();
node.mesh.uploadDataAndFinishNeighbors() catch |err| {
if(err == error.LODMissing) {
mutex.lock();
defer mutex.unlock();
try updatableList.append(pos);
} else {
return err;
}
};
}
if(std.time.milliTimestamp() >= targetTime) break; // Update at least one mesh.
}