mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 11:17:05 -04:00
Fix chunk meshes at lod borders.
This commit is contained in:
parent
219957681d
commit
26f4dd2ff2
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user