diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.fs b/assets/cubyz/shaders/chunks/chunk_fragment.fs index 7d2ae223..6f77b752 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.fs +++ b/assets/cubyz/shaders/chunks/chunk_fragment.fs @@ -8,6 +8,8 @@ flat in vec3 normal; flat in int textureIndex; flat in int isBackFace; flat in int ditherSeed; +flat in float distanceForLodCheck; +flat in int opaqueInLod; uniform sampler2DArray texture_sampler; uniform sampler2DArray emissionSampler; @@ -15,6 +17,7 @@ uniform sampler2DArray reflectivityAndAbsorptionSampler; uniform samplerCube reflectionMap; uniform float reflectionMapSize; uniform float contrast; +uniform float lodDistance; layout(location = 0) out vec4 fragColor; @@ -53,6 +56,11 @@ ivec2 random1to2(int v) { } bool passDitherTest(float alpha) { + if(opaqueInLod != 0) { + if(distanceForLodCheck > lodDistance) return true; + float factor = max(0, distanceForLodCheck - (lodDistance - 32.0))/32.0; + alpha = alpha*(1 - factor) + factor; + } ivec2 screenPos = ivec2(gl_FragCoord.xy); screenPos += random1to2(ditherSeed); screenPos &= 3; diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vs b/assets/cubyz/shaders/chunks/chunk_vertex.vs index dae52cb3..0e36244e 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vs +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vs @@ -8,6 +8,8 @@ flat out vec3 normal; flat out int textureIndex; flat out int isBackFace; flat out int ditherSeed; +flat out float distanceForLodCheck; +flat out int opaqueInLod; uniform vec3 ambientLight; uniform mat4 projectionMatrix; @@ -29,6 +31,7 @@ struct QuadInfo { vec3 corners[4]; vec2 cornerUV[4]; uint textureSlot; + int opaqueInLod; }; layout(std430, binding = 4) buffer _quads @@ -47,7 +50,7 @@ struct ChunkData { vec4 maxPos; int voxelSize; uint vertexStartOpaque; - uint faceCountsByNormalOpaque[7]; + uint faceCountsByNormalOpaque[14]; uint lightStartOpaque; uint vertexStartTransparent; uint vertexCountTransparent; @@ -109,5 +112,7 @@ void main() { vec4 mvPos = viewMatrix*vec4(position, 1); gl_Position = projectionMatrix*mvPos; mvVertexPos = mvPos.xyz; + distanceForLodCheck = length(mvPos.xyz) + voxelSize; uv = quads[quadIndex].cornerUV[vertexID]*voxelSize; + opaqueInLod = quads[quadIndex].opaqueInLod; } diff --git a/assets/cubyz/shaders/chunks/fillIndirectBuffer.glsl b/assets/cubyz/shaders/chunks/fillIndirectBuffer.glsl index 45b31f79..096f5658 100644 --- a/assets/cubyz/shaders/chunks/fillIndirectBuffer.glsl +++ b/assets/cubyz/shaders/chunks/fillIndirectBuffer.glsl @@ -13,7 +13,7 @@ struct ChunkData { vec4 maxPos; int voxelSize; uint vertexStartOpaque; - uint faceCountsByNormalOpaque[7]; + uint faceCountsByNormalOpaque[14]; uint lightStartOpaque; uint vertexStartTransparent; uint vertexCountTransparent; @@ -48,20 +48,22 @@ uniform bool isTransparent; uniform bool onlyDrawPreviouslyInvisible; uniform ivec3 playerPositionInteger; -bool isVisible(int dir, ivec3 relativePlayerPos, int voxelSize) { +uniform float lodDistance; + +bool isVisible(int dir, ivec3 playerDist) { switch(dir) { case 0: // dirUp - return relativePlayerPos.z >= 0; + return playerDist.z >= 0; case 1: // dirDown - return relativePlayerPos.z < 32*voxelSize; + return playerDist.z <= 0; case 2: // dirPosX - return relativePlayerPos.x >= 0; + return playerDist.x >= 0; case 3: // dirNegX - return relativePlayerPos.x < 32*voxelSize; + return playerDist.x <= 0; case 4: // dirPosY - return relativePlayerPos.y >= 0; + return playerDist.y >= 0; case 5: // dirNegY - return relativePlayerPos.y < 32*voxelSize; + return playerDist.y <= 0; } return true; } @@ -82,15 +84,22 @@ void main() { } chunks[chunkID].visibilityState = 0; } else { - uint commandIndex = commandIndexStart + gl_GlobalInvocationID.x*4; - uint commandIndexEnd = commandIndex + 4; + uint commandIndex = commandIndexStart + gl_GlobalInvocationID.x*8; + uint commandIndexEnd = commandIndex + 8; uint groupFaceOffset = 0; uint groupFaceCount = 0; uint oldoldvisibilityState = chunks[chunkID].oldVisibilityState; + ivec3 playerDist = playerPositionInteger - chunks[chunkID].position.xyz; + if(playerDist.x > 0) playerDist.x = max(0, playerDist.x - 32*chunks[chunkID].voxelSize); + if(playerDist.y > 0) playerDist.y = max(0, playerDist.y - 32*chunks[chunkID].voxelSize); + if(playerDist.z > 0) playerDist.z = max(0, playerDist.z - 32*chunks[chunkID].voxelSize); + float playerDistSquare = dot(playerDist, playerDist); + if((onlyDrawPreviouslyInvisible && chunks[chunkID].oldVisibilityState == 0 && chunks[chunkID].visibilityState != 0) || (chunks[chunkID].oldVisibilityState != 0 && !onlyDrawPreviouslyInvisible)) { - for(int i = 0; i < 7; i++) { + for(int i = 0; i < 14; i++) { + if(playerDistSquare >= lodDistance*lodDistance && i == 7) break; uint faceCount = chunks[chunkID].faceCountsByNormalOpaque[i]; - if(isVisible(i, playerPositionInteger - chunks[chunkID].position.xyz, chunks[chunkID].voxelSize) || faceCount == 0) { + if(isVisible(i%7, playerDist) || faceCount == 0) { groupFaceCount += faceCount; } else { if(groupFaceCount != 0) { diff --git a/assets/cubyz/shaders/chunks/occlusionTestFragment.fs b/assets/cubyz/shaders/chunks/occlusionTestFragment.fs index 8a8369ba..436e43a2 100644 --- a/assets/cubyz/shaders/chunks/occlusionTestFragment.fs +++ b/assets/cubyz/shaders/chunks/occlusionTestFragment.fs @@ -10,7 +10,7 @@ struct ChunkData { vec4 maxPos; int voxelSize; uint vertexStartOpaque; - uint faceCountsByNormalOpaque[7]; + uint faceCountsByNormalOpaque[14]; uint lightStartOpaque; uint vertexStartTransparent; uint vertexCountTransparent; diff --git a/assets/cubyz/shaders/chunks/occlusionTestVertex.vs b/assets/cubyz/shaders/chunks/occlusionTestVertex.vs index 2687a58d..aa5a049b 100644 --- a/assets/cubyz/shaders/chunks/occlusionTestVertex.vs +++ b/assets/cubyz/shaders/chunks/occlusionTestVertex.vs @@ -8,7 +8,7 @@ struct ChunkData { vec4 maxPos; int voxelSize; uint vertexStartOpaque; - uint faceCountsByNormalOpaque[7]; + uint faceCountsByNormalOpaque[14]; uint lightStartOpaque; uint vertexStartTransparent; uint vertexCountTransparent; diff --git a/assets/cubyz/shaders/chunks/transparent_fragment.fs b/assets/cubyz/shaders/chunks/transparent_fragment.fs index d398b74b..5ae364bc 100644 --- a/assets/cubyz/shaders/chunks/transparent_fragment.fs +++ b/assets/cubyz/shaders/chunks/transparent_fragment.fs @@ -8,6 +8,8 @@ flat in vec3 normal; flat in int textureIndex; flat in int isBackFace; flat in int ditherSeed; +flat in float distanceForLodCheck; +flat in int opaqueInLod; uniform sampler2DArray texture_sampler; uniform sampler2DArray emissionSampler; diff --git a/assets/cubyz/shaders/entity_vertex.vs b/assets/cubyz/shaders/entity_vertex.vs index 4ccd8d5a..6ce1eeae 100644 --- a/assets/cubyz/shaders/entity_vertex.vs +++ b/assets/cubyz/shaders/entity_vertex.vs @@ -16,6 +16,7 @@ struct QuadInfo { vec3 corners[4]; vec2 cornerUV[4]; uint textureSlot; + int opaqueInLod; }; layout(std430, binding = 11) buffer _quads diff --git a/assets/cubyz/shaders/item_drop.vs b/assets/cubyz/shaders/item_drop.vs index 3d4e3bf5..30080be1 100644 --- a/assets/cubyz/shaders/item_drop.vs +++ b/assets/cubyz/shaders/item_drop.vs @@ -27,6 +27,7 @@ struct QuadInfo { vec3 corners[4]; vec2 cornerUV[4]; uint textureSlot; + int opaqueInLod; }; layout(std430, binding = 4) buffer _quads diff --git a/src/graphics.zig b/src/graphics.zig index 2f6846ad..492f864d 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -1674,7 +1674,7 @@ pub const TextureArray = struct { // MARK: TextureArray } // Calculate the mipmap levels: - for(lodBuffer, 0..) |_, _lod| { + for(0..lodBuffer.len) |_lod| { const lod: u5 = @intCast(_lod); const curWidth = maxWidth >> lod; const curHeight = maxHeight >> lod; @@ -1693,6 +1693,29 @@ pub const TextureArray = struct { // MARK: TextureArray } } } + } + // Give the correct color to alpha 0 pixels, to avoid dark pixels: + for(1..lodBuffer.len) |_lod| { + const lod: u5 = @intCast(lodBuffer.len - 1 - _lod); + const curWidth = maxWidth >> lod; + const curHeight = maxHeight >> lod; + for(0..curWidth) |x| { + for(0..curHeight) |y| { + const index = x + y*curWidth; + const index2 = x/2 + y/2*curWidth/2; + if(lodBuffer[lod][index].a == 0) { + lodBuffer[lod][index].r = lodBuffer[lod + 1][index2].r; + lodBuffer[lod][index].g = lodBuffer[lod + 1][index2].g; + lodBuffer[lod][index].b = lodBuffer[lod + 1][index2].b; + } + } + } + } + // Upload: + for(0..lodBuffer.len) |_lod| { + const lod: u5 = @intCast(lodBuffer.len - 1 - _lod); + const curWidth = maxWidth >> lod; + const curHeight = maxHeight >> lod; c.glTexSubImage3D(c.GL_TEXTURE_2D_ARRAY, lod, 0, 0, @intCast(i), curWidth, curHeight, 1, c.GL_RGBA, c.GL_UNSIGNED_BYTE, lodBuffer[lod].ptr); } } diff --git a/src/gui/windows/graphics.zig b/src/gui/windows/graphics.zig index 04c6ff06..351ab874 100644 --- a/src/gui/windows/graphics.zig +++ b/src/gui/windows/graphics.zig @@ -68,8 +68,12 @@ fn fovFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const return std.fmt.allocPrint(allocator.allocator, "#ffffffField Of View: {d:.0}°", .{value}) catch unreachable; } -fn LODFactorCallback(newValue: u16) void { - settings.LODFactor = @as(f32, @floatFromInt(newValue + 1))/2; +fn lodDistanceFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 { + return std.fmt.allocPrint(allocator.allocator, "#ffffffOpaque leaves distance: {d:.0}", .{@round(value)}) catch unreachable; +} + +fn lodDistanceCallback(newValue: f32) void { + settings.@"lod0.5Distance" = @round(newValue); settings.save(); } @@ -103,6 +107,7 @@ pub fn onOpen() void { list.add(ContinuousSlider.init(.{0, 0}, 128, 10.0, 154.0, @floatFromInt(settings.fpsCap orelse 154), &fpsCapCallback, &fpsCapFormatter)); list.add(DiscreteSlider.init(.{0, 0}, 128, "#ffffffRender Distance: ", "{}", &renderDistances, @min(@max(settings.renderDistance, renderDistances[0]) - renderDistances[0], renderDistances.len - 1), &renderDistanceCallback)); list.add(DiscreteSlider.init(.{0, 0}, 128, "#ffffffLeaves Quality (TODO: requires reload): ", "{}", &leavesQualities, settings.leavesQuality - leavesQualities[0], &leavesQualityCallback)); + list.add(ContinuousSlider.init(.{0, 0}, 128, 50.0, 400.0, settings.@"lod0.5Distance", &lodDistanceCallback, &lodDistanceFormatter)); list.add(ContinuousSlider.init(.{0, 0}, 128, 40.0, 120.0, settings.fov, &fovCallback, &fovFormatter)); list.add(CheckBox.init(.{0, 0}, 128, "Bloom", settings.bloom, &bloomCallback)); list.add(CheckBox.init(.{0, 0}, 128, "Vertical Synchronization", settings.vsync, &vsyncCallback)); diff --git a/src/models.zig b/src/models.zig index 6f597cbc..dd7acd7e 100644 --- a/src/models.zig +++ b/src/models.zig @@ -19,6 +19,7 @@ pub const QuadInfo = extern struct { corners: [4]Vec3f, cornerUV: [4]Vec2f, textureSlot: u32, + opaqueInLod: u32 = 0, }; const ExtraQuadInfo = struct { @@ -415,7 +416,8 @@ pub var models: main.List(Model) = undefined; var quadDeduplication: std.AutoHashMap([@sizeOf(QuadInfo)]u8, u16) = undefined; -fn addQuad(info: QuadInfo) error{Degenerate}!u16 { +fn addQuad(info_: QuadInfo) error{Degenerate}!u16 { + var info = info_; if(quadDeduplication.get(std.mem.toBytes(info))) |id| { return id; } @@ -428,6 +430,7 @@ fn addQuad(info: QuadInfo) error{Degenerate}!u16 { } if(cornerEqualities >= 2) return error.Degenerate; // One corner equality is fine, since then the quad degenerates to a triangle, which has a non-zero area. const index: u16 = @intCast(quads.items.len); + info.opaqueInLod = @intFromBool(Model.getFaceNeighbor(&info) != null); quads.append(info); quadDeduplication.put(std.mem.toBytes(info), index) catch unreachable; diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index 2e212984..21fa0b20 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -41,6 +41,7 @@ const UniformStruct = struct { reflectivityAndAbsorptionSampler: c_int, reflectionMap: c_int, reflectionMapSize: c_int, + lodDistance: c_int, zNear: c_int, zFar: c_int, }; @@ -54,6 +55,7 @@ pub var commandUniforms: struct { isTransparent: c_int, playerPositionInteger: c_int, onlyDrawPreviouslyInvisible: c_int, + lodDistance: c_int, } = undefined; pub var occlusionTestShader: Shader = undefined; pub var occlusionTestUniforms: struct { @@ -140,6 +142,8 @@ fn bindCommonUniforms(locations: *UniformStruct, projMatrix: Mat4f, ambient: Vec c.glUniform1f(locations.contrast, 0.12); + c.glUniform1f(locations.lodDistance, main.settings.@"lod0.5Distance"); + c.glUniformMatrix4fv(locations.viewMatrix, 1, c.GL_TRUE, @ptrCast(&game.camera.viewMatrix)); c.glUniform3f(locations.ambientLight, ambient[0], ambient[1], ambient[2]); @@ -171,13 +175,14 @@ pub fn bindTransparentShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, playe } pub fn drawChunksIndirect(chunkIDs: []const u32, projMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d, transparent: bool) void { - const drawCallsEstimate: u31 = @intCast(if(transparent) chunkIDs.len else chunkIDs.len*4); + const drawCallsEstimate: u31 = @intCast(if(transparent) chunkIDs.len else chunkIDs.len*8); var chunkIDAllocation: main.graphics.SubAllocation = .{.start = 0, .len = 0}; chunkIDBuffer.uploadData(chunkIDs, &chunkIDAllocation); defer chunkIDBuffer.free(chunkIDAllocation); const allocation = commandBuffer.rawAlloc(drawCallsEstimate); defer commandBuffer.free(allocation); commandShader.bind(); + c.glUniform1f(commandUniforms.lodDistance, main.settings.@"lod0.5Distance"); c.glUniform1ui(commandUniforms.chunkIDIndex, chunkIDAllocation.start); c.glUniform1ui(commandUniforms.commandIndexStart, allocation.start); c.glUniform1ui(commandUniforms.size, @intCast(chunkIDs.len)); @@ -260,7 +265,7 @@ pub const ChunkData = extern struct { max: Vec3f align(16), voxelSize: i32, vertexStartOpaque: u32, - faceCountsByNormalOpaque: [7]u32, + faceCountsByNormalOpaque: [14]u32, lightStartOpaque: u32, vertexStartTransparent: u32, vertexCountTransparent: u32, @@ -281,17 +286,19 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh coreFaces: main.ListUnmanaged(FaceData) = .{}, neighborFacesSameLod: [6]main.ListUnmanaged(FaceData) = [_]main.ListUnmanaged(FaceData){.{}} ** 6, neighborFacesHigherLod: [6]main.ListUnmanaged(FaceData) = [_]main.ListUnmanaged(FaceData){.{}} ** 6, + optionalFaces: main.ListUnmanaged(FaceData) = .{}, completeList: []FaceData = &.{}, lightList: []u32 = &.{}, lightListNeedsUpload: bool = false, coreLen: u32 = 0, sameLodLens: [6]u32 = .{0} ** 6, higherLodLens: [6]u32 = .{0} ** 6, + optionalLen: u32 = 0, mutex: std.Thread.Mutex = .{}, bufferAllocation: graphics.SubAllocation = .{.start = 0, .len = 0}, lightAllocation: graphics.SubAllocation = .{.start = 0, .len = 0}, vertexCount: u31 = 0, - byNormalCount: [7]u32 = .{0} ** 7, + byNormalCount: [14]u32 = .{0} ** 14, wasChanged: bool = false, min: Vec3f = undefined, max: Vec3f = undefined, @@ -300,6 +307,7 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh faceBuffer.free(self.bufferAllocation); lightBuffer.free(self.lightAllocation); self.coreFaces.deinit(main.globalAllocator); + self.optionalFaces.deinit(main.globalAllocator); for(&self.neighborFacesSameLod) |*neighborFaces| { neighborFaces.deinit(main.globalAllocator); } @@ -318,16 +326,17 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh for(&self.neighborFacesHigherLod) |*neighborFaces| { neighborFaces.clearRetainingCapacity(); } + self.optionalFaces.clearRetainingCapacity(); } - fn appendInternalQuadsToCore(self: *PrimitiveMesh, block: Block, x: i32, y: i32, z: i32, comptime backFace: bool) void { + fn appendInternalQuadsToCore(self: *PrimitiveMesh, block: Block, x: i32, y: i32, z: i32, comptime backFace: bool, optional: bool) void { const model = blocks.meshes.model(block); - models.models.items[model].appendInternalQuadsToList(&self.coreFaces, main.globalAllocator, block, x, y, z, backFace); + models.models.items[model].appendInternalQuadsToList(if(optional) &self.optionalFaces else &self.coreFaces, main.globalAllocator, block, x, y, z, backFace); } - fn appendNeighborFacingQuadsToCore(self: *PrimitiveMesh, block: Block, neighbor: chunk.Neighbor, x: i32, y: i32, z: i32, comptime backFace: bool) void { + fn appendNeighborFacingQuadsToCore(self: *PrimitiveMesh, block: Block, neighbor: chunk.Neighbor, x: i32, y: i32, z: i32, comptime backFace: bool, optional: bool) void { const model = blocks.meshes.model(block); - models.models.items[model].appendNeighborFacingQuadsToList(&self.coreFaces, main.globalAllocator, block, neighbor, x, y, z, backFace); + models.models.items[model].appendNeighborFacingQuadsToList(if(optional) &self.optionalFaces else &self.coreFaces, main.globalAllocator, block, neighbor, x, y, z, backFace); } fn appendNeighborFacingQuadsToNeighbor(self: *PrimitiveMesh, block: Block, neighbor: chunk.Neighbor, x: i32, y: i32, z: i32, comptime backFace: bool, comptime isLod: bool) void { @@ -355,6 +364,7 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh for(self.neighborFacesHigherLod) |neighborFaces| { len += neighborFaces.items.len; } + len += self.optionalFaces.items.len; const completeList = main.globalAllocator.alloc(FaceData, len); var i: usize = 0; @memcpy(completeList[i..][0..self.coreFaces.items.len], self.coreFaces.items); @@ -367,6 +377,8 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh @memcpy(completeList[i..][0..neighborFaces.items.len], neighborFaces.items); i += neighborFaces.items.len; } + @memcpy(completeList[i..][0..self.optionalFaces.items.len], self.optionalFaces.items); + i += self.optionalFaces.items.len; var lightList = main.List(u32).init(main.stackAllocator); defer lightList.deinit(); var lightMap = std.AutoHashMap([4]u32, u16).init(main.stackAllocator.allocator); @@ -415,6 +427,7 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh for(self.neighborFacesHigherLod, 0..) |neighborFaces, j| { self.higherLodLens[j] = @intCast(neighborFaces.items.len); } + self.optionalLen = @intCast(self.optionalFaces.items.len); self.mutex.unlock(); main.globalAllocator.free(oldList); main.globalAllocator.free(oldLightList); @@ -597,6 +610,9 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh } offset += neighborLen; } + const optionalList = self.completeList[offset..][0..self.optionalLen]; + offset += self.optionalLen; + len += self.optionalLen; const fullBuffer = faceBuffer.allocateAndMapRange(len, &self.bufferAllocation); defer faceBuffer.unmapRange(fullBuffer); // Sort the faces by normal to allow for backface culling on the GPU: @@ -623,6 +639,21 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh self.byNormalCount[normal] = i - iStart; iStart = i; } + for(0..7) |normal| { + for(optionalList) |face| { + if(main.models.extraQuadInfos.items[face.blockAndQuad.quadIndex].alignedNormalDirection) |normalDir| { + if(normalDir.toInt() == normal) { + fullBuffer[i] = face; + i += 1; + } + } else if(normal == 6) { + fullBuffer[i] = face; + i += 1; + } + } + self.byNormalCount[normal + 7] = i - iStart; + iStart = i; + } std.debug.assert(i == fullBuffer.len); self.vertexCount = @intCast(6*fullBuffer.len); self.wasChanged = true; @@ -949,6 +980,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh } } } + const initialAlwaysViewThroughMask = alwaysViewThroughMask; const depthFilteredViewThroughMask = blk: { var a = &alwaysViewThroughMask; var b = &alwaysViewThroughMask2; @@ -1003,9 +1035,9 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh if(occlusionInfo.hasInternalQuads) { const block = self.chunk.data.palette[paletteId]; if(block.transparent()) { - self.transparentMesh.appendInternalQuadsToCore(block, x, y, z, false); + self.transparentMesh.appendInternalQuadsToCore(block, x, y, z, false, false); } else { - self.opaqueMesh.appendInternalQuadsToCore(block, x, y, z, false); + self.opaqueMesh.appendInternalQuadsToCore(block, x, y, z, false, false); } } } @@ -1019,20 +1051,21 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh var bitMask = hasFaces[x][y] & (canSeeNeighbor[comptime neighbor.reverse().toInt()][x - 1][y] | canSeeAllNeighbors[x - 1][y]); while(bitMask != 0) { const z = @ctz(bitMask); - bitMask &= ~(@as(u32, 1) << @intCast(z)); + const setBit = @as(u32, 1) << @intCast(z); + bitMask &= ~setBit; var block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z)); - if(depthFilteredViewThroughMask[x][y] & @as(u32, 1) << @intCast(z) != 0) block.typ = block.opaqueVariant(); + if(depthFilteredViewThroughMask[x][y] & setBit != 0) block.typ = block.opaqueVariant(); if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x - 1), @intCast(y), z)); if(std.meta.eql(block, neighborBlock)) continue; } if(block.transparent()) { if(block.hasBackFace()) { - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true); + self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false); } - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x - 1), @intCast(y), z, false); + self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x - 1), @intCast(y), z, false, false); } else { - self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x - 1), @intCast(y), z, false); + self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x - 1), @intCast(y), z, false, initialAlwaysViewThroughMask[x - 1][y] & setBit != 0); } } } @@ -1045,20 +1078,21 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh var bitMask = hasFaces[x][y] & (canSeeNeighbor[comptime neighbor.reverse().toInt()][x + 1][y] | canSeeAllNeighbors[x + 1][y]); while(bitMask != 0) { const z = @ctz(bitMask); - bitMask &= ~(@as(u32, 1) << @intCast(z)); + const setBit = @as(u32, 1) << @intCast(z); + bitMask &= ~setBit; var block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z)); - if(depthFilteredViewThroughMask[x][y] & @as(u32, 1) << @intCast(z) != 0) block.typ = block.opaqueVariant(); + if(depthFilteredViewThroughMask[x][y] & setBit != 0) block.typ = block.opaqueVariant(); if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x + 1), @intCast(y), z)); if(std.meta.eql(block, neighborBlock)) continue; } if(block.transparent()) { if(block.hasBackFace()) { - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true); + self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false); } - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x + 1), @intCast(y), z, false); + self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x + 1), @intCast(y), z, false, false); } else { - self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x + 1), @intCast(y), z, false); + self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x + 1), @intCast(y), z, false, initialAlwaysViewThroughMask[x + 1][y] & setBit != 0); } } } @@ -1071,20 +1105,21 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh var bitMask = hasFaces[x][y] & (canSeeNeighbor[comptime neighbor.reverse().toInt()][x][y - 1] | canSeeAllNeighbors[x][y - 1]); while(bitMask != 0) { const z = @ctz(bitMask); - bitMask &= ~(@as(u32, 1) << @intCast(z)); + const setBit = @as(u32, 1) << @intCast(z); + bitMask &= ~setBit; var block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z)); - if(depthFilteredViewThroughMask[x][y] & @as(u32, 1) << @intCast(z) != 0) block.typ = block.opaqueVariant(); + if(depthFilteredViewThroughMask[x][y] & setBit != 0) block.typ = block.opaqueVariant(); if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y - 1), z)); if(std.meta.eql(block, neighborBlock)) continue; } if(block.transparent()) { if(block.hasBackFace()) { - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true); + self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false); } - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y - 1), z, false); + self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y - 1), z, false, false); } else { - self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y - 1), z, false); + self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y - 1), z, false, initialAlwaysViewThroughMask[x][y - 1] & setBit != 0); } } } @@ -1097,20 +1132,21 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh var bitMask = hasFaces[x][y] & (canSeeNeighbor[comptime neighbor.reverse().toInt()][x][y + 1] | canSeeAllNeighbors[x][y + 1]); while(bitMask != 0) { const z = @ctz(bitMask); - bitMask &= ~(@as(u32, 1) << @intCast(z)); + const setBit = @as(u32, 1) << @intCast(z); + bitMask &= ~setBit; var block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z)); - if(depthFilteredViewThroughMask[x][y] & @as(u32, 1) << @intCast(z) != 0) block.typ = block.opaqueVariant(); + if(depthFilteredViewThroughMask[x][y] & setBit != 0) block.typ = block.opaqueVariant(); if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y + 1), z)); if(std.meta.eql(block, neighborBlock)) continue; } if(block.transparent()) { if(block.hasBackFace()) { - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true); + self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false); } - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y + 1), z, false); + self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y + 1), z, false, false); } else { - self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y + 1), z, false); + self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y + 1), z, false, initialAlwaysViewThroughMask[x][y + 1] & setBit != 0); } } } @@ -1123,20 +1159,21 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh var bitMask = hasFaces[x][y] & (canSeeNeighbor[comptime neighbor.reverse().toInt()][x][y] | canSeeAllNeighbors[x][y]) << 1; while(bitMask != 0) { const z = @ctz(bitMask); - bitMask &= ~(@as(u32, 1) << @intCast(z)); + const setBit = @as(u32, 1) << @intCast(z); + bitMask &= ~setBit; var block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z)); - if(depthFilteredViewThroughMask[x][y] & @as(u32, 1) << @intCast(z) != 0) block.typ = block.opaqueVariant(); + if(depthFilteredViewThroughMask[x][y] & setBit != 0) block.typ = block.opaqueVariant(); if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z - 1)); if(std.meta.eql(block, neighborBlock)) continue; } if(block.transparent()) { if(block.hasBackFace()) { - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true); + self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false); } - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z - 1, false); + self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z - 1, false, false); } else { - self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z - 1, false); + self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z - 1, false, initialAlwaysViewThroughMask[x][y] << 1 & setBit != 0); } } } @@ -1149,20 +1186,21 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh var bitMask = hasFaces[x][y] & (canSeeNeighbor[comptime neighbor.reverse().toInt()][x][y] | canSeeAllNeighbors[x][y]) >> 1; while(bitMask != 0) { const z = @ctz(bitMask); - bitMask &= ~(@as(u32, 1) << @intCast(z)); + const setBit = @as(u32, 1) << @intCast(z); + bitMask &= ~setBit; var block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z)); - if(depthFilteredViewThroughMask[x][y] & @as(u32, 1) << @intCast(z) != 0) block.typ = block.opaqueVariant(); + if(depthFilteredViewThroughMask[x][y] & setBit != 0) block.typ = block.opaqueVariant(); if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z + 1)); if(std.meta.eql(block, neighborBlock)) continue; } if(block.transparent()) { if(block.hasBackFace()) { - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true); + self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false); } - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z + 1, false); + self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z + 1, false, false); } else { - self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z + 1, false); + self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z + 1, false, initialAlwaysViewThroughMask[x][y] >> 1 & setBit != 0); } } } @@ -1213,6 +1251,8 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh neighborChunkMesh.chunk.data.setValue(index, neighborBlock); neighborChunkMesh.opaqueMesh.coreFaces.clearRetainingCapacity(); neighborChunkMesh.transparentMesh.coreFaces.clearRetainingCapacity(); + neighborChunkMesh.opaqueMesh.optionalFaces.clearRetainingCapacity(); + neighborChunkMesh.transparentMesh.optionalFaces.clearRetainingCapacity(); neighborChunkMesh.mutex.unlock(); neighborChunkMesh.updateBlockLight(@intCast(nx & chunk.chunkMask), @intCast(ny & chunk.chunkMask), @intCast(nz & chunk.chunkMask), neighborBlock, &lightRefreshList); neighborChunkMesh.generateMesh(&lightRefreshList); @@ -1270,6 +1310,8 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh } self.opaqueMesh.coreFaces.clearRetainingCapacity(); self.transparentMesh.coreFaces.clearRetainingCapacity(); + self.opaqueMesh.optionalFaces.clearRetainingCapacity(); + self.transparentMesh.optionalFaces.clearRetainingCapacity(); self.mutex.unlock(); self.generateMesh(&lightRefreshList); // TODO: Batch mesh updates instead of applying them for each block changes. self.mutex.lock(); diff --git a/src/settings.zig b/src/settings.zig index c389bc76..c2467920 100644 --- a/src/settings.zig +++ b/src/settings.zig @@ -45,6 +45,8 @@ pub var musicVolume: f32 = 1; pub var leavesQuality: u16 = 2; +pub var @"lod0.5Distance": f32 = 200; + pub var storageTime: i64 = 5000;