From 6d77f95f6538ec3d1a83c42b614cb7adaf2b5c3c Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Mon, 25 May 2026 19:36:35 -0400 Subject: [PATCH 01/33] first test --- .../shaders/chunks/chunk_depth_fragment.frag | 20 ++++ .../shaders/chunks/chunk_depth_vertex.vert | 98 +++++++++++++++++++ .../cubyz/shaders/chunks/chunk_fragment.frag | 26 +++-- assets/cubyz/shaders/chunks/chunk_vertex.vert | 15 ++- .../shaders/chunks/transparent_fragment.frag | 17 ++-- src/block_entity.zig | 2 +- src/graphics.zig | 44 +++++---- src/gui/windows/gpu_performance_measuring.zig | 4 + src/renderer.zig | 76 +++++++++----- src/renderer/chunk_meshing.zig | 91 +++++++++++++---- src/renderer/mesh_storage.zig | 6 +- src/vec.zig | 11 +++ 12 files changed, 327 insertions(+), 83 deletions(-) create mode 100644 assets/cubyz/shaders/chunks/chunk_depth_fragment.frag create mode 100644 assets/cubyz/shaders/chunks/chunk_depth_vertex.vert diff --git a/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag b/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag new file mode 100644 index 0000000000..674f5fafed --- /dev/null +++ b/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag @@ -0,0 +1,20 @@ +#version 460 + +layout(location = 0) in vec3 mvVertexPos; +layout(location = 1) in vec3 direction; +layout(location = 2) in vec2 uv; +layout(location = 3) flat in vec3 normal; +layout(location = 4) flat in int isBackFace; +layout(location = 5) flat in float distanceForLodCheck; +layout(location = 6) flat in int opaqueInLod; + +layout(location = 5) uniform float lodDistance; + +layout(std430, binding = 1) buffer _animatedTexture +{ + float animatedTexture[]; +}; + +void main() { + gl_FragDepth = mvVertexPos.z; +} diff --git a/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert b/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert new file mode 100644 index 0000000000..2b7cc061e7 --- /dev/null +++ b/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert @@ -0,0 +1,98 @@ +#version 460 + +layout(location = 0) out vec3 mvVertexPos; +layout(location = 1) out vec3 direction; +layout(location = 2) out vec2 uv; +layout(location = 3) flat out vec3 normal; +layout(location = 4) flat out int isBackFace; +layout(location = 5) flat out float distanceForLodCheck; +layout(location = 6) flat out int opaqueInLod; + +layout(location = 1) uniform mat4 projectionMatrix; +layout(location = 2) uniform mat4 viewMatrix; +layout(location = 3) uniform ivec3 lightPositionInteger; +layout(location = 4) uniform vec3 lightPositionFraction; + +struct FaceData { + int encodedPositionAndLightIndex; + int textureAndQuad; +}; +layout(std430, binding = 3) buffer _faceData +{ + FaceData faceData[]; +}; + +struct QuadInfo { + vec3 normal; + float corners[4][3]; + vec2 cornerUV[4]; + uint textureSlot; + int opaqueInLod; +}; + +layout(std430, binding = 4) buffer _quads +{ + QuadInfo quads[]; +}; + +layout(std430, binding = 10) buffer _lightData +{ + uint lightData[]; +}; + +struct ChunkData { + ivec4 position; + vec4 minPos; + vec4 maxPos; + int voxelSize; + uint lightStart; + uint vertexStartOpaque; + uint faceCountsByNormalOpaque[14]; + uint vertexStartTransparent; + uint vertexCountTransparent; + uint visibilityState; + uint oldVisibilityState; +}; + +layout(std430, binding = 6) buffer _chunks +{ + ChunkData chunks[]; +}; + +vec3 square(vec3 x) { + return x*x; +} + +void main() { + int faceID = gl_VertexID >> 2; + int vertexID = gl_VertexID & 3; + int chunkID = gl_BaseInstance; + int voxelSize = chunks[chunkID].voxelSize; + int encodedPositionAndLightIndex = faceData[faceID].encodedPositionAndLightIndex; + int textureAndQuad = faceData[faceID].textureAndQuad; + isBackFace = encodedPositionAndLightIndex>>15 & 1; + + int quadIndex = textureAndQuad >> 16; + + vec3 position = vec3( + encodedPositionAndLightIndex & 31, + encodedPositionAndLightIndex >> 5 & 31, + encodedPositionAndLightIndex >> 10 & 31 + ); + + normal = quads[quadIndex].normal; + + position += vec3(quads[quadIndex].corners[vertexID][0], quads[quadIndex].corners[vertexID][1], quads[quadIndex].corners[vertexID][2]); + position *= voxelSize; + position += vec3(chunks[chunkID].position.xyz - lightPositionInteger); + position -= lightPositionFraction; + + direction = position; + + 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/chunk_fragment.frag b/assets/cubyz/shaders/chunks/chunk_fragment.frag index 8515a0bc84..ac1fe2e177 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_fragment.frag @@ -4,11 +4,12 @@ layout(location = 0) in vec3 mvVertexPos; layout(location = 1) in vec3 direction; layout(location = 2) in vec3 light; layout(location = 3) in vec2 uv; -layout(location = 4) flat in vec3 normal; -layout(location = 5) flat in int textureIndex; -layout(location = 6) flat in int isBackFace; -layout(location = 7) flat in float distanceForLodCheck; -layout(location = 8) flat in int opaqueInLod; +layout(location = 4) in vec4 mvVertexLightSpacePos; +layout(location = 5) flat in vec3 normal; +layout(location = 6) flat in int textureIndex; +layout(location = 7) flat in int isBackFace; +layout(location = 8) flat in float distanceForLodCheck; +layout(location = 9) flat in int opaqueInLod; layout(location = 0) out vec4 fragColor; @@ -17,10 +18,13 @@ layout(binding = 1) uniform sampler2DArray emissionSampler; layout(binding = 2) uniform sampler2DArray reflectivityAndAbsorptionSampler; layout(binding = 4) uniform samplerCube reflectionMap; layout(binding = 5) uniform sampler2D ditherTexture; +layout(binding = 6) uniform sampler2D shadowMap; layout(location = 5) uniform float reflectionMapSize; layout(location = 6) uniform float contrast; layout(location = 7) uniform float lodDistance; +layout(location = 8) uniform mat4 lightProjectionMatrix; +layout(location = 9) uniform mat4 lightViewMatrix; layout(std430, binding = 1) buffer _animatedTexture { @@ -51,6 +55,15 @@ vec4 fixedCubeMapLookup(vec3 v) { // Taken from http://the-witness.net/news/2012 return texture(reflectionMap, v); } +float shadowCalculation(vec4 fragPosLightSpace) { + vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; + projCoords = projCoords * 0.5 + 0.5; + float closestDepth = texture(shadowMap, projCoords.xy).r; + float currentDepth = projCoords.z; + float shadow = currentDepth > closestDepth ? 1.0 : 0.0; + return shadow; +} + void main() { float animatedTextureIndex = animatedTexture[textureIndex]; float normalVariation = lightVariation(normal); @@ -63,7 +76,8 @@ void main() { reflectivity = reflectivity*fixedCubeMapLookup(reflect(direction, normal)).x; reflectivity = reflectivity*(1 - fresnelReflection) + fresnelReflection; - vec3 pixelLight = max(light*normalVariation, texture(emissionSampler, textureCoords).r*4); + float shadow = shadowCalculation(mvVertexLightSpacePos); + vec3 pixelLight = max((1.0 - shadow*0.5)*light*normalVariation, texture(emissionSampler, textureCoords).r*4); fragColor = texture(textureSampler, textureCoords)*vec4(pixelLight, 1); fragColor.rgb += reflectivity*pixelLight; diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vert b/assets/cubyz/shaders/chunks/chunk_vertex.vert index ef481b6d9b..97ebd5b365 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vert +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vert @@ -4,17 +4,20 @@ layout(location = 0) out vec3 mvVertexPos; layout(location = 1) out vec3 direction; layout(location = 2) out vec3 light; layout(location = 3) out vec2 uv; -layout(location = 4) flat out vec3 normal; -layout(location = 5) flat out int textureIndex; -layout(location = 6) flat out int isBackFace; -layout(location = 7) flat out float distanceForLodCheck; -layout(location = 8) flat out int opaqueInLod; +layout(location = 4) out vec4 mvVertexLightSpacePos; +layout(location = 5) flat out vec3 normal; +layout(location = 6) flat out int textureIndex; +layout(location = 7) flat out int isBackFace; +layout(location = 8) flat out float distanceForLodCheck; +layout(location = 9) flat out int opaqueInLod; layout(location = 0) uniform vec3 ambientLight; layout(location = 1) uniform mat4 projectionMatrix; layout(location = 2) uniform mat4 viewMatrix; layout(location = 3) uniform ivec3 playerPositionInteger; layout(location = 4) uniform vec3 playerPositionFraction; +layout(location = 8) uniform mat4 lightProjectionMatrix; +layout(location = 9) uniform mat4 lightViewMatrix; #ifdef ENTITY layout(location = 14) uniform mat4 modelMatrix; @@ -120,4 +123,6 @@ void main() { distanceForLodCheck = length(mvPos.xyz) + voxelSize; uv = quads[quadIndex].cornerUV[vertexID]*voxelSize; opaqueInLod = quads[quadIndex].opaqueInLod; + + mvVertexLightSpacePos = lightViewMatrix*lightProjectionMatrix*vec4(position, 1); } diff --git a/assets/cubyz/shaders/chunks/transparent_fragment.frag b/assets/cubyz/shaders/chunks/transparent_fragment.frag index 51b47e6b20..2664f88ede 100644 --- a/assets/cubyz/shaders/chunks/transparent_fragment.frag +++ b/assets/cubyz/shaders/chunks/transparent_fragment.frag @@ -4,11 +4,12 @@ layout(location = 0) in vec3 mvVertexPos; layout(location = 1) in vec3 direction; layout(location = 2) in vec3 light; layout(location = 3) in vec2 uv; -layout(location = 4) flat in vec3 normal; -layout(location = 5) flat in int textureIndex; -layout(location = 6) flat in int isBackFace; -layout(location = 7) flat in float distanceForLodCheck; -layout(location = 8) flat in int opaqueInLod; +layout(location = 4) in vec4 mvVertexLightSpacePos; +layout(location = 5) flat in vec3 normal; +layout(location = 6) flat in int textureIndex; +layout(location = 7) flat in int isBackFace; +layout(location = 8) flat in float distanceForLodCheck; +layout(location = 9) flat in int opaqueInLod; layout(location = 0, index = 0) out vec4 fragColor; layout(location = 0, index = 1) out vec4 blendColor; @@ -25,8 +26,8 @@ layout(location = 4) uniform vec3 playerPositionFraction; layout(location = 5) uniform float reflectionMapSize; layout(location = 6) uniform float contrast; -layout(location = 8) uniform float zNear; -layout(location = 9) uniform float zFar; +layout(location = 10) uniform float zNear; +layout(location = 11) uniform float zFar; struct Fog { vec3 color; @@ -35,7 +36,7 @@ struct Fog { float fogHigher; }; -layout(location = 10) uniform Fog fog; +layout(location = 12) uniform Fog fog; layout(std430, binding = 1) buffer _animatedTexture { diff --git a/src/block_entity.zig b/src/block_entity.zig index 6693c60c1b..53c10d4e7a 100644 --- a/src/block_entity.zig +++ b/src/block_entity.zig @@ -495,7 +495,7 @@ pub const BlockEntityTypes = struct { // MARK: BlockEntityTypes defer c.glViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]); var finalFrameBuffer: graphics.FrameBuffer = undefined; - finalFrameBuffer.init(false, c.GL_NEAREST, c.GL_REPEAT); + finalFrameBuffer.init(true, false, c.GL_NEAREST, c.GL_REPEAT); finalFrameBuffer.updateSize(textureWidth, textureHeight, c.GL_RGBA8); finalFrameBuffer.bind(); finalFrameBuffer.clear(.{0, 0, 0, 0}); diff --git a/src/graphics.zig b/src/graphics.zig index f7517efdbd..dc48bb8b7b 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -1625,14 +1625,16 @@ pub fn LargeBuffer(comptime Entry: type) type { // MARK: LargerBuffer pub const FrameBuffer = struct { // MARK: FrameBuffer frameBuffer: c_uint, + hasTexture: bool, texture: c_uint, hasDepthTexture: bool, depthTexture: c_uint, - pub fn init(self: *FrameBuffer, hasDepthTexture: bool, textureFilter: c_int, textureWrap: c_int) void { + pub fn init(self: *FrameBuffer, hasTexture: bool, hasDepthTexture: bool, textureFilter: c_int, textureWrap: c_int) void { self.* = FrameBuffer{ .frameBuffer = undefined, .texture = undefined, + .hasTexture = hasTexture, .depthTexture = undefined, .hasDepthTexture = hasDepthTexture, }; @@ -1645,16 +1647,18 @@ pub const FrameBuffer = struct { // MARK: FrameBuffer c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, textureFilter); c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_S, textureWrap); c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_T, textureWrap); + c.glTexParameterfv(c.GL_TEXTURE_2D, c.GL_TEXTURE_BORDER_COLOR, @ptrCast(&[4]f32 {1.0, 1.0, 1.0, 1.0})); c.glFramebufferTexture2D(c.GL_FRAMEBUFFER, c.GL_DEPTH_ATTACHMENT, c.GL_TEXTURE_2D, self.depthTexture, 0); } - c.glGenTextures(1, &self.texture); - c.glBindTexture(c.GL_TEXTURE_2D, self.texture); - c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MIN_FILTER, textureFilter); - c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, textureFilter); - c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_S, textureWrap); - c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_T, textureWrap); - c.glFramebufferTexture2D(c.GL_FRAMEBUFFER, c.GL_COLOR_ATTACHMENT0, c.GL_TEXTURE_2D, self.texture, 0); - + if (hasTexture) { + c.glGenTextures(1, &self.texture); + c.glBindTexture(c.GL_TEXTURE_2D, self.texture); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MIN_FILTER, textureFilter); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, textureFilter); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_S, textureWrap); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_T, textureWrap); + c.glFramebufferTexture2D(c.GL_FRAMEBUFFER, c.GL_COLOR_ATTACHMENT0, c.GL_TEXTURE_2D, self.texture, 0); + } c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0); } @@ -1663,10 +1667,12 @@ pub const FrameBuffer = struct { // MARK: FrameBuffer if (self.hasDepthTexture) { c.glDeleteRenderbuffers(1, &self.depthTexture); } - c.glDeleteTextures(1, &self.texture); + if (self.hasTexture) { + c.glDeleteTextures(1, &self.texture); + } } - pub fn updateSize(self: *FrameBuffer, _width: u31, _height: u31, internalFormat: c_int) void { + pub fn updateSize(self: *FrameBuffer, _width: u31, _height: u31, internalFormat: ?c_int) void { const width = @max(_width, 1); const height = @max(_height, 1); c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer); @@ -1674,9 +1680,10 @@ pub const FrameBuffer = struct { // MARK: FrameBuffer c.glBindTexture(c.GL_TEXTURE_2D, self.depthTexture); c.glTexImage2D(c.GL_TEXTURE_2D, 0, c.GL_DEPTH_COMPONENT32F, width, height, 0, c.GL_DEPTH_COMPONENT, c.GL_FLOAT, null); } - - c.glBindTexture(c.GL_TEXTURE_2D, self.texture); - c.glTexImage2D(c.GL_TEXTURE_2D, 0, internalFormat, width, height, 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, null); + if (self.hasTexture) { + c.glBindTexture(c.GL_TEXTURE_2D, self.texture); + c.glTexImage2D(c.GL_TEXTURE_2D, 0, internalFormat.?, width, height, 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, null); + } } pub fn clear(_: FrameBuffer, clearColor: Vec4f) void { @@ -1698,6 +1705,7 @@ pub const FrameBuffer = struct { // MARK: FrameBuffer } pub fn bindTexture(self: *const FrameBuffer, target: c_uint) void { + std.debug.assert(self.hasTexture); c.glActiveTexture(target); c.glBindTexture(c.GL_TEXTURE_2D, self.texture); } @@ -2178,7 +2186,7 @@ pub fn generateBlockTexture(blockType: u16) Texture { var frameBuffer: FrameBuffer = undefined; - frameBuffer.init(false, c.GL_NEAREST, c.GL_REPEAT); + frameBuffer.init(true, false, c.GL_NEAREST, c.GL_REPEAT); defer frameBuffer.deinit(); frameBuffer.updateSize(textureSize, textureSize, c.GL_RGBA16F); frameBuffer.bind(); @@ -2245,9 +2253,9 @@ pub fn generateBlockTexture(blockType: u16) Texture { if (block.transparent()) { c.glBlendEquation(c.GL_FUNC_ADD); c.glBlendFunc(c.GL_ONE, c.GL_SRC1_COLOR); - main.renderer.chunk_meshing.bindTransparentShaderAndUniforms(projMatrix, .{1, 1, 1}, .{x, y, z}); + main.renderer.chunk_meshing.bindTransparentShaderAndUniforms(projMatrix, Mat4f.identity(), Mat4f.identity(), .{1, 1, 1}, .{x, y, z}); } else { - main.renderer.chunk_meshing.bindShaderAndUniforms(projMatrix, .{1, 1, 1}, .{x, y, z}); + main.renderer.chunk_meshing.bindShaderAndUniforms(projMatrix, Mat4f.identity(), Mat4f.identity(), .{1, 1, 1}, .{x, y, z}); } c.glUniform1f(uniforms.contrast, 0.25); c.glActiveTexture(c.GL_TEXTURE0); @@ -2262,7 +2270,7 @@ pub fn generateBlockTexture(blockType: u16) Texture { c.glDisable(c.GL_CULL_FACE); var finalFrameBuffer: FrameBuffer = undefined; - finalFrameBuffer.init(false, c.GL_NEAREST, c.GL_REPEAT); + finalFrameBuffer.init(true, false, c.GL_NEAREST, c.GL_REPEAT); finalFrameBuffer.updateSize(textureSize, textureSize, c.GL_RGBA8); finalFrameBuffer.bind(); const texture = Texture{.textureID = finalFrameBuffer.texture}; diff --git a/src/gui/windows/gpu_performance_measuring.zig b/src/gui/windows/gpu_performance_measuring.zig index 066a9145f1..b564cf71d9 100644 --- a/src/gui/windows/gpu_performance_measuring.zig +++ b/src/gui/windows/gpu_performance_measuring.zig @@ -14,6 +14,8 @@ const GuiComponent = gui.GuiComponent; pub const Samples = enum(u8) { screenbuffer_clear, + depth_framebuffer_clear, + depth_framebuffer_chunk_rendering, clear, skybox, animation, @@ -33,6 +35,8 @@ pub const Samples = enum(u8) { const names = [_][]const u8{ "Screenbuffer clear", + "Depth Framebuffer clear", + "Depth Framebuffer Chunk Rendering", "Clear", "Skybox", "Pre-processing Block Animations", diff --git a/src/renderer.zig b/src/renderer.zig index 071f5d5a97..f3d145374c 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -62,6 +62,10 @@ pub var activeFrameBuffer: c_uint = 0; pub const reflectionCubeMapSize = 64; var reflectionCubeMap: graphics.CubeMapTexture = undefined; +pub const shadowMapResolution = 512; +pub const shadowMapSize = 64.0; +var depthFrameBuffer: graphics.FrameBuffer = undefined; + pub fn init() void { deferredRenderPassPipeline = graphics.Pipeline.init( "assets/cubyz/shaders/deferred_render_pass.vert", @@ -85,7 +89,7 @@ pub fn init() void { .{.depthTest = false, .depthWrite = false}, .{.attachments = &.{.noBlending}}, ); - worldFrameBuffer.init(true, c.GL_NEAREST, c.GL_CLAMP_TO_EDGE); + worldFrameBuffer.init(true, true, c.GL_NEAREST, c.GL_CLAMP_TO_EDGE); worldFrameBuffer.updateSize(Window.width, Window.height, c.GL_RGB16F); Bloom.init(); MeshSelection.init(); @@ -96,6 +100,8 @@ pub fn init() void { reflectionCubeMap = .init(); reflectionCubeMap.generate(reflectionCubeMapSize, reflectionCubeMapSize); initReflectionCubeMap(); + depthFrameBuffer.init(false, true, c.GL_NEAREST, c.GL_CLAMP_TO_BORDER); + depthFrameBuffer.updateSize(shadowMapResolution, shadowMapResolution, null); } pub fn deinit() void { @@ -109,12 +115,13 @@ pub fn deinit() void { mesh_storage.deinit(); chunk_meshing.deinit(); reflectionCubeMap.deinit(); + depthFrameBuffer.deinit(); } fn initReflectionCubeMap() void { c.glViewport(0, 0, reflectionCubeMapSize, reflectionCubeMapSize); var framebuffer: graphics.FrameBuffer = undefined; - framebuffer.init(false, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE); + framebuffer.init(true, false, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE); defer framebuffer.deinit(); framebuffer.bind(); fakeReflectionPipeline.bind(null); @@ -187,15 +194,49 @@ pub fn crosshairDirection(rotationMatrix: Mat4f, fovY: f32, width: u31, height: } pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPos: Vec3d) void { // MARK: renderWorld() + depthFrameBuffer.bind(); + c.glViewport(0, 0, shadowMapResolution, shadowMapResolution); + gpu_performance_measuring.startQuery(.depth_framebuffer_clear); + c.glClear(c.GL_DEPTH_BUFFER_BIT); + gpu_performance_measuring.stopQuery(); + + // Uses FrustumCulling on the chunks. + const frustum = Frustum.init(Vec3f{0, 0, 0}, game.camera.viewMatrix, lastFov, lastWidth, lastHeight); + game.camera.updateViewMatrix(); + + chunk_meshing.quadsDrawn = 0; + chunk_meshing.transparentQuadsDrawn = 0; + const meshes = mesh_storage.updateAndGetRenderChunks(world.conn, &frustum, playerPos, settings.renderDistance); + + gpu_performance_measuring.startQuery(.chunk_rendering_preparation); + const direction = crosshairDirection(game.camera.viewMatrix, lastFov, lastWidth, lastHeight); + MeshSelection.select(playerPos, direction, game.Player.inventory.getItem(game.Player.selectedSlot)); + + chunk_meshing.beginRender(); + + + + var chunkLists: [main.settings.highestSupportedLod + 1]main.List(u32) = @splat(main.List(u32).init(main.stackAllocator)); + defer for (chunkLists) |list| list.deinit(); + for (meshes) |mesh| { + mesh.prepareRendering(&chunkLists); + } + gpu_performance_measuring.stopQuery(); + + const lightProjection: Mat4f = .orthogonal(-shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2); + const lightView: Mat4f = Mat4f.rotationX(std.math.pi); + gpu_performance_measuring.startQuery(.depth_framebuffer_chunk_rendering); + chunk_meshing.drawChunksIndirect(&chunkLists, lightProjection, lightProjection, lightView, ambientLight, playerPos, .depth); + gpu_performance_measuring.stopQuery(); + + chunk_meshing.endRender(); + worldFrameBuffer.bind(); c.glViewport(0, 0, lastWidth, lastHeight); gpu_performance_measuring.startQuery(.clear); worldFrameBuffer.clear(Vec4f{skyColor[0], skyColor[1], skyColor[2], 1}); gpu_performance_measuring.stopQuery(); - game.camera.updateViewMatrix(); - // Uses FrustumCulling on the chunks. - const frustum = Frustum.init(Vec3f{0, 0, 0}, game.camera.viewMatrix, lastFov, lastWidth, lastHeight); const time: u32 = @intCast(main.timestamp().toMilliseconds() & std.math.maxInt(u32)); @@ -208,7 +249,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo gpu_performance_measuring.stopQuery(); // Update the uniforms. The uniforms are needed to render the replacement meshes. - chunk_meshing.bindShaderAndUniforms(game.projectionMatrix, ambientLight, playerPos); + chunk_meshing.bindShaderAndUniforms(game.projectionMatrix, lightProjection, lightView, ambientLight, playerPos); c.glActiveTexture(c.GL_TEXTURE0); blocks.meshes.blockTextureArray.bind(); @@ -219,25 +260,14 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo c.glActiveTexture(c.GL_TEXTURE5); blocks.meshes.ditherTexture.bind(); reflectionCubeMap.bindTo(4); + depthFrameBuffer.bindDepthTexture(c.GL_TEXTURE6); chunk_meshing.quadsDrawn = 0; chunk_meshing.transparentQuadsDrawn = 0; - const meshes = mesh_storage.updateAndGetRenderChunks(world.conn, &frustum, playerPos, settings.renderDistance); - - gpu_performance_measuring.startQuery(.chunk_rendering_preparation); - const direction = crosshairDirection(game.camera.viewMatrix, lastFov, lastWidth, lastHeight); - MeshSelection.select(playerPos, direction, game.Player.inventory.getItem(game.Player.selectedSlot)); - chunk_meshing.beginRender(); - var chunkLists: [main.settings.highestSupportedLod + 1]main.List(u32) = @splat(main.List(u32).init(main.stackAllocator)); - defer for (chunkLists) |list| list.deinit(); - for (meshes) |mesh| { - mesh.prepareRendering(&chunkLists); - } - gpu_performance_measuring.stopQuery(); gpu_performance_measuring.startQuery(.chunk_rendering); - chunk_meshing.drawChunksIndirect(&chunkLists, game.projectionMatrix, ambientLight, playerPos, false); + chunk_meshing.drawChunksIndirect(&chunkLists, game.projectionMatrix, lightProjection, lightView, ambientLight, playerPos, .regular); gpu_performance_measuring.stopQuery(); gpu_performance_measuring.startQuery(.entity_rendering); @@ -278,7 +308,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo } gpu_performance_measuring.stopQuery(); gpu_performance_measuring.startQuery(.transparent_rendering); - chunk_meshing.drawChunksIndirect(&chunkLists, game.projectionMatrix, ambientLight, playerPos, true); + chunk_meshing.drawChunksIndirect(&chunkLists, game.projectionMatrix, lightProjection, lightView, ambientLight, playerPos, .transparent); gpu_performance_measuring.stopQuery(); } @@ -356,8 +386,8 @@ const Bloom = struct { // MARK: Bloom } = undefined; pub fn init() void { - buffer1.init(false, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE); - buffer2.init(false, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE); + buffer1.init(true, false, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE); + buffer2.init(true, false, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE); emptyBuffer = .init(); emptyBuffer.generate(graphics.Image.emptyImage); firstPassPipeline = graphics.Pipeline.init( @@ -633,7 +663,7 @@ pub const MenuBackGround = struct { defer updateViewport(Window.width, Window.height); var buffer: graphics.FrameBuffer = undefined; - buffer.init(true, c.GL_NEAREST, c.GL_REPEAT); + buffer.init(true, true, c.GL_NEAREST, c.GL_REPEAT); defer buffer.deinit(); buffer.updateSize(size, size, c.GL_RGBA8); diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index 9bfa776ef4..20ccfae9dc 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -28,6 +28,7 @@ const mesh_storage = @import("mesh_storage.zig"); var pipeline: graphics.Pipeline = undefined; var transparentPipeline: graphics.Pipeline = undefined; +var depthPipeline: graphics.Pipeline = undefined; const UniformStruct = struct { projectionMatrix: c_int, viewMatrix: c_int, @@ -44,9 +45,21 @@ const UniformStruct = struct { lodDistance: c_int, zNear: c_int, zFar: c_int, + lightProjectionMatrix: c_int, + lightViewMatrix: c_int, }; pub var uniforms: UniformStruct = undefined; pub var transparentUniforms: UniformStruct = undefined; +const DepthUniformStruct = struct { + projectionMatrix: c_int, + viewMatrix: c_int, + lightPositionInteger: c_int, + lightPositionFraction: c_int, + lodDistance: c_int, + zNear: c_int, + zFar: c_int, +}; +pub var depthUniforms: DepthUniformStruct = undefined; pub var commandPipeline: graphics.ComputePipeline = undefined; pub var commandUniforms: struct { chunkIDIndex: c_int, @@ -104,6 +117,17 @@ pub fn init() void { .alphaBlendOp = .add, }}}, ); + depthPipeline = graphics.Pipeline.init( + "assets/cubyz/shaders/chunks/chunk_depth_vertex.vert", + "assets/cubyz/shaders/chunks/chunk_depth_fragment.frag", + "", + &depthUniforms, + graphics.VertexArray.EmptyVertex, + &.{}, + .{}, + .{.depthTest = true, .depthWrite = true, .depthCompare = .lessOrEqual}, + .{.attachments = &.{.noBlending}}, + ); commandPipeline = graphics.ComputePipeline.init("assets/cubyz/shaders/chunks/fillIndirectBuffer.comp", "", &commandUniforms); occlusionTestPipeline = graphics.Pipeline.init( "assets/cubyz/shaders/chunks/occlusionTestVertex.vert", @@ -146,6 +170,7 @@ pub fn init() void { pub fn deinit() void { pipeline.deinit(); transparentPipeline.deinit(); + depthPipeline.deinit(); occlusionTestPipeline.deinit(); commandPipeline.deinit(); vao.deinit(); @@ -178,7 +203,7 @@ pub fn endRender() void { chunkIDBuffer.endRender(); } -fn bindCommonUniforms(locations: *UniformStruct, projMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d) void { +fn bindCommonUniforms(locations: *UniformStruct, projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d) void { c.glUniformMatrix4fv(locations.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projMatrix)); c.glUniform1f(locations.reflectionMapSize, renderer.reflectionCubeMapSize); @@ -196,17 +221,20 @@ fn bindCommonUniforms(locations: *UniformStruct, projMatrix: Mat4f, ambient: Vec c.glUniform3i(locations.playerPositionInteger, @floor(playerPos[0]), @floor(playerPos[1]), @floor(playerPos[2])); c.glUniform3f(locations.playerPositionFraction, @floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))); + + c.glUniformMatrix4fv(locations.lightProjectionMatrix, 1, c.GL_TRUE, @ptrCast(&lightProjMatrix)); + c.glUniformMatrix4fv(locations.lightViewMatrix, 1, c.GL_TRUE, @ptrCast(&lightViewMatrix)); } -pub fn bindShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d) void { +pub fn bindShaderAndUniforms(projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d) void { pipeline.bind(null); - bindCommonUniforms(&uniforms, projMatrix, ambient, playerPos); + bindCommonUniforms(&uniforms, projMatrix, lightProjMatrix, lightViewMatrix, ambient, playerPos); vao.bind(); } -pub fn bindTransparentShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d) void { +pub fn bindTransparentShaderAndUniforms(projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d) void { transparentPipeline.bind(null); c.glUniform3fv(transparentUniforms.@"fog.color", 1, @ptrCast(&game.fog.fogColor)); @@ -214,7 +242,25 @@ pub fn bindTransparentShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, playe c.glUniform1f(transparentUniforms.@"fog.fogLower", game.fog.fogLower); c.glUniform1f(transparentUniforms.@"fog.fogHigher", game.fog.fogHigher); - bindCommonUniforms(&transparentUniforms, projMatrix, ambient, playerPos); + bindCommonUniforms(&transparentUniforms, projMatrix, lightProjMatrix, lightViewMatrix, ambient, playerPos); + + vao.bind(); +} + +pub fn bindDepthShaderAndUniforms(projMatrix: Mat4f, viewMatrix: Mat4f, cameraPos: Vec3d) void { + depthPipeline.bind(null); + + c.glUniformMatrix4fv(depthUniforms.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projMatrix)); + + c.glUniform1f(depthUniforms.lodDistance, main.settings.@"lod0.5Distance"); + + c.glUniformMatrix4fv(depthUniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&viewMatrix)); + + c.glUniform1f(depthUniforms.zNear, renderer.zNear); + c.glUniform1f(depthUniforms.zFar, renderer.zFar); + + c.glUniform3i(depthUniforms.lightPositionInteger, @floor(cameraPos[0]), @floor(cameraPos[1]), @floor(cameraPos[2])); + c.glUniform3f(depthUniforms.lightPositionFraction, @floatCast(@mod(cameraPos[0], 1)), @floatCast(@mod(cameraPos[1], 1)), @floatCast(@mod(cameraPos[2], 1))); vao.bind(); } @@ -224,17 +270,24 @@ fn bindBuffers(lod: usize) void { lightBuffers[lod].ssbo.bind(lightBuffers[lod].binding); } -pub fn drawChunksIndirect(chunkIds: *const [main.settings.highestSupportedLod + 1]main.List(u32), projMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d, transparent: bool) void { +pub const DrawMode = enum { + regular, + transparent, + depth, +}; + +pub fn drawChunksIndirect(chunkIds: *const [main.settings.highestSupportedLod + 1]main.List(u32), projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d, mode: DrawMode) void { for (0..chunkIds.len) |i| { - const lod = if (transparent) main.settings.highestSupportedLod - i else i; + const lod = if (mode == .transparent) main.settings.highestSupportedLod - i else i; bindBuffers(lod); - drawChunksOfLod(chunkIds[lod].items, projMatrix, ambient, playerPos, transparent); + drawChunksOfLod(chunkIds[lod].items, projMatrix, lightProjMatrix, lightViewMatrix, ambient, playerPos, mode); } } -fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d, transparent: bool) void { + +fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d, mode: DrawMode) void { if (chunkIDs.len == 0) return; - const drawCallsEstimate: u31 = @intCast(if (transparent) chunkIDs.len else chunkIDs.len*8); + const drawCallsEstimate: u31 = @intCast(if (mode == .transparent) chunkIDs.len else chunkIDs.len*8); var chunkIDAllocation: main.graphics.SubAllocation = .{.start = 0, .len = 0}; chunkIDBuffer.uploadData(chunkIDs, &chunkIDAllocation); defer chunkIDBuffer.free(chunkIDAllocation); @@ -245,17 +298,17 @@ fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, ambient: Vec3f, pla c.glUniform1ui(commandUniforms.chunkIDIndex, chunkIDAllocation.start); c.glUniform1ui(commandUniforms.commandIndexStart, allocation.start); c.glUniform1ui(commandUniforms.size, @intCast(chunkIDs.len)); - c.glUniform1i(commandUniforms.isTransparent, @intFromBool(transparent)); + c.glUniform1i(commandUniforms.isTransparent, @intFromBool(mode == .transparent)); c.glUniform3i(commandUniforms.playerPositionInteger, @floor(playerPos[0]), @floor(playerPos[1]), @floor(playerPos[2])); - if (!transparent) { + if (mode != .transparent) { c.glUniform1i(commandUniforms.onlyDrawPreviouslyInvisible, 0); c.glDispatchCompute(@intCast(@divFloor(chunkIDs.len + 63, 64)), 1, 1); // TODO: Replace with @divCeil once available c.glMemoryBarrier(c.GL_SHADER_STORAGE_BARRIER_BIT | c.GL_COMMAND_BARRIER_BIT); - if (transparent) { - bindTransparentShaderAndUniforms(projMatrix, ambient, playerPos); + if (mode == .depth) { + bindDepthShaderAndUniforms(lightProjMatrix, lightViewMatrix, playerPos); } else { - bindShaderAndUniforms(projMatrix, ambient, playerPos); + bindShaderAndUniforms(projMatrix, lightProjMatrix, lightViewMatrix, ambient, playerPos); } c.glBindBuffer(c.GL_DRAW_INDIRECT_BUFFER, commandBuffer.ssbo.bufferID); c.glMultiDrawElementsIndirect(c.GL_TRIANGLES, c.GL_UNSIGNED_INT, @ptrFromInt(allocation.start*@sizeOf(IndirectData)), drawCallsEstimate, 0); @@ -277,10 +330,10 @@ fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, ambient: Vec3f, pla c.glDispatchCompute(@intCast(@divFloor(chunkIDs.len + 63, 64)), 1, 1); // TODO: Replace with @divCeil once available c.glMemoryBarrier(c.GL_SHADER_STORAGE_BARRIER_BIT | c.GL_COMMAND_BARRIER_BIT); - if (transparent) { - bindTransparentShaderAndUniforms(projMatrix, ambient, playerPos); - } else { - bindShaderAndUniforms(projMatrix, ambient, playerPos); + switch (mode) { + .regular => bindShaderAndUniforms(projMatrix, lightProjMatrix, lightViewMatrix, ambient, playerPos), + .transparent => bindTransparentShaderAndUniforms(projMatrix, lightProjMatrix, lightViewMatrix, ambient, playerPos), + .depth => bindDepthShaderAndUniforms(lightProjMatrix, lightViewMatrix, playerPos), } c.glBindBuffer(c.GL_DRAW_INDIRECT_BUFFER, commandBuffer.ssbo.bufferID); c.glMultiDrawElementsIndirect(c.GL_TRIANGLES, c.GL_UNSIGNED_INT, @ptrFromInt(allocation.start*@sizeOf(IndirectData)), drawCallsEstimate, 0); diff --git a/src/renderer/mesh_storage.zig b/src/renderer/mesh_storage.zig index 4baafd02fe..5684cc2047 100644 --- a/src/renderer/mesh_storage.zig +++ b/src/renderer/mesh_storage.zig @@ -544,7 +544,7 @@ fn createNewMeshes(olderPx: i32, olderPy: i32, olderPz: i32, olderRD: u16, meshR } } -pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: *const main.renderer.Frustum, playerPos: Vec3d, renderDistance: u16) []*chunk_meshing.ChunkMesh { // MARK: updateAndGetRenderChunks() +pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: ?*const main.renderer.Frustum, playerPos: Vec3d, renderDistance: u16) []*chunk_meshing.ChunkMesh { // MARK: updateAndGetRenderChunks() meshList.clearRetainingCapacity(); const playerPosInt: Vec3i = @floor(playerPos); @@ -622,7 +622,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: *co }; const node2 = getNodePointer(neighborPos); if (!node2.active and node2.finishedMeshing) { - if (!frustum.testAAB(relPosFloat + @as(Vec3f, @floatFromInt(Vec3i{neighbor.relX()*chunk.chunkSize*pos.voxelSize, neighbor.relY()*chunk.chunkSize*pos.voxelSize, neighbor.relZ()*chunk.chunkSize*pos.voxelSize})), @splat(@floatFromInt(chunk.chunkSize*pos.voxelSize)))) + if (frustum != null and !frustum.?.testAAB(relPosFloat + @as(Vec3f, @floatFromInt(Vec3i{neighbor.relX()*chunk.chunkSize*pos.voxelSize, neighbor.relY()*chunk.chunkSize*pos.voxelSize, neighbor.relZ()*chunk.chunkSize*pos.voxelSize})), @splat(@floatFromInt(chunk.chunkSize*pos.voxelSize)))) continue; node2.active = true; node2.rendered = true; @@ -649,7 +649,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: *co if (dz == 1) nextPos.wz ^= lowerLodBit; const node2 = getNodePointer(nextPos); const relNextPos: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{nextPos.wx, nextPos.wy, nextPos.wz})) - playerPos; - if (!frustum.testAAB(@floatCast(relNextPos), @splat(@floatFromInt(chunk.chunkSize*nextPos.voxelSize)))) + if (frustum != null and !frustum.?.testAAB(@floatCast(relNextPos), @splat(@floatFromInt(chunk.chunkSize*nextPos.voxelSize)))) continue; std.debug.assert(node2.finishedMeshing); node2.active = true; diff --git a/src/vec.zig b/src/vec.zig index ed1e124082..c092b5543f 100644 --- a/src/vec.zig +++ b/src/vec.zig @@ -259,6 +259,17 @@ pub const Mat4f = struct { // MARK: Mat4f Vec4f{0, 1, 0, 0}, }, }; + } + + pub fn orthogonal(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) Mat4f { + return Mat4f{ + .rows = [4]Vec4f { + Vec4f{2/(right - left), 1, 1, 1}, + Vec4f{1, 2/(top - bottom), 1, 1}, + Vec4f{1, 1, 2/(far - near), 1}, + Vec4f{-(right + left)/(right - left), -(top + bottom)/(top - bottom), -(far + near)/(far - near), 1}, + } + }; } // zig fmt: on pub fn transpose(self: Mat4f) Mat4f { From 6322a5a655f0204d421acb3e3d33986689df10a0 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Mon, 25 May 2026 20:42:46 -0400 Subject: [PATCH 02/33] more progress? --- assets/cubyz/shaders/chunks/chunk_vertex.vert | 2 +- src/renderer.zig | 6 +++--- src/vec.zig | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vert b/assets/cubyz/shaders/chunks/chunk_vertex.vert index 97ebd5b365..8670cde62d 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vert +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vert @@ -124,5 +124,5 @@ void main() { uv = quads[quadIndex].cornerUV[vertexID]*voxelSize; opaqueInLod = quads[quadIndex].opaqueInLod; - mvVertexLightSpacePos = lightViewMatrix*lightProjectionMatrix*vec4(position, 1); + mvVertexLightSpacePos = lightProjectionMatrix*lightViewMatrix*vec4(position, 1); } diff --git a/src/renderer.zig b/src/renderer.zig index f3d145374c..cc7f2b29b0 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -62,8 +62,8 @@ pub var activeFrameBuffer: c_uint = 0; pub const reflectionCubeMapSize = 64; var reflectionCubeMap: graphics.CubeMapTexture = undefined; -pub const shadowMapResolution = 512; -pub const shadowMapSize = 64.0; +pub const shadowMapResolution = 1024; +pub const shadowMapSize = 128.0; var depthFrameBuffer: graphics.FrameBuffer = undefined; pub fn init() void { @@ -224,7 +224,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo gpu_performance_measuring.stopQuery(); const lightProjection: Mat4f = .orthogonal(-shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2); - const lightView: Mat4f = Mat4f.rotationX(std.math.pi); + const lightView: Mat4f = Mat4f.identity().mul(Mat4f.rotationX(-std.math.pi/4.0)); gpu_performance_measuring.startQuery(.depth_framebuffer_chunk_rendering); chunk_meshing.drawChunksIndirect(&chunkLists, lightProjection, lightProjection, lightView, ambientLight, playerPos, .depth); gpu_performance_measuring.stopQuery(); diff --git a/src/vec.zig b/src/vec.zig index c092b5543f..723eae0121 100644 --- a/src/vec.zig +++ b/src/vec.zig @@ -262,14 +262,14 @@ pub const Mat4f = struct { // MARK: Mat4f } pub fn orthogonal(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) Mat4f { - return Mat4f{ + return (Mat4f{ .rows = [4]Vec4f { - Vec4f{2/(right - left), 1, 1, 1}, - Vec4f{1, 2/(top - bottom), 1, 1}, - Vec4f{1, 1, 2/(far - near), 1}, + Vec4f{2/(right - left), 0, 0, 0}, + Vec4f{0, 2/(top - bottom), 0, 0}, + Vec4f{0, 0, 2/(far - near), 0}, Vec4f{-(right + left)/(right - left), -(top + bottom)/(top - bottom), -(far + near)/(far - near), 1}, } - }; + }).transpose(); } // zig fmt: on pub fn transpose(self: Mat4f) Mat4f { From 2b28a0df2ed773aa2d114f907b253a1584e7709d Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Tue, 26 May 2026 12:41:52 -0400 Subject: [PATCH 03/33] Make it look better --- assets/cubyz/shaders/chunks/chunk_depth_fragment.frag | 1 - assets/cubyz/shaders/chunks/chunk_depth_vertex.vert | 8 ++++---- assets/cubyz/shaders/chunks/chunk_fragment.frag | 6 +++++- assets/cubyz/shaders/chunks/chunk_vertex.vert | 3 ++- src/graphics.zig | 3 +++ src/renderer.zig | 4 ++-- src/renderer/chunk_meshing.zig | 10 +++++----- 7 files changed, 21 insertions(+), 14 deletions(-) diff --git a/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag b/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag index 674f5fafed..4e10eb89da 100644 --- a/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag @@ -16,5 +16,4 @@ layout(std430, binding = 1) buffer _animatedTexture }; void main() { - gl_FragDepth = mvVertexPos.z; } diff --git a/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert b/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert index 2b7cc061e7..088ce9289b 100644 --- a/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert +++ b/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert @@ -10,8 +10,8 @@ layout(location = 6) flat out int opaqueInLod; layout(location = 1) uniform mat4 projectionMatrix; layout(location = 2) uniform mat4 viewMatrix; -layout(location = 3) uniform ivec3 lightPositionInteger; -layout(location = 4) uniform vec3 lightPositionFraction; +layout(location = 3) uniform ivec3 playerPositionInteger; +layout(location = 4) uniform vec3 playerPositionFraction; struct FaceData { int encodedPositionAndLightIndex; @@ -84,8 +84,8 @@ void main() { position += vec3(quads[quadIndex].corners[vertexID][0], quads[quadIndex].corners[vertexID][1], quads[quadIndex].corners[vertexID][2]); position *= voxelSize; - position += vec3(chunks[chunkID].position.xyz - lightPositionInteger); - position -= lightPositionFraction; + position += vec3(chunks[chunkID].position.xyz - playerPositionInteger); + position -= playerPositionFraction; direction = position; diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.frag b/assets/cubyz/shaders/chunks/chunk_fragment.frag index ac1fe2e177..18e77de26c 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_fragment.frag @@ -60,7 +60,11 @@ float shadowCalculation(vec4 fragPosLightSpace) { projCoords = projCoords * 0.5 + 0.5; float closestDepth = texture(shadowMap, projCoords.xy).r; float currentDepth = projCoords.z; - float shadow = currentDepth > closestDepth ? 1.0 : 0.0; + vec3 lightDir = -normalize(lightViewMatrix[2].xyz); + float bias = max(0.001 * (1.0 - dot(normal, lightDir)), 0.00001); + float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; + if(projCoords.z > 1.0) + shadow = 0.0; return shadow; } diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vert b/assets/cubyz/shaders/chunks/chunk_vertex.vert index 8670cde62d..a1983e64c6 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vert +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vert @@ -124,5 +124,6 @@ void main() { uv = quads[quadIndex].cornerUV[vertexID]*voxelSize; opaqueInLod = quads[quadIndex].opaqueInLod; - mvVertexLightSpacePos = lightProjectionMatrix*lightViewMatrix*vec4(position, 1); + vec3 shadowPos = position + normal * 0.02; + mvVertexLightSpacePos = lightProjectionMatrix*lightViewMatrix*vec4(shadowPos, 1); } diff --git a/src/graphics.zig b/src/graphics.zig index dc48bb8b7b..5b48b5ada7 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -1658,6 +1658,9 @@ pub const FrameBuffer = struct { // MARK: FrameBuffer c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_S, textureWrap); c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_T, textureWrap); c.glFramebufferTexture2D(c.GL_FRAMEBUFFER, c.GL_COLOR_ATTACHMENT0, c.GL_TEXTURE_2D, self.texture, 0); + } else { + c.glDrawBuffer(c.GL_NONE); + c.glReadBuffer(c.GL_NONE); } c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0); } diff --git a/src/renderer.zig b/src/renderer.zig index cc7f2b29b0..027d88b028 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -62,7 +62,7 @@ pub var activeFrameBuffer: c_uint = 0; pub const reflectionCubeMapSize = 64; var reflectionCubeMap: graphics.CubeMapTexture = undefined; -pub const shadowMapResolution = 1024; +pub const shadowMapResolution = 2048; pub const shadowMapSize = 128.0; var depthFrameBuffer: graphics.FrameBuffer = undefined; @@ -224,7 +224,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo gpu_performance_measuring.stopQuery(); const lightProjection: Mat4f = .orthogonal(-shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2); - const lightView: Mat4f = Mat4f.identity().mul(Mat4f.rotationX(-std.math.pi/4.0)); + const lightView: Mat4f = Mat4f.identity().mul(Mat4f.rotationX(std.math.pi*3.0/4.0)); gpu_performance_measuring.startQuery(.depth_framebuffer_chunk_rendering); chunk_meshing.drawChunksIndirect(&chunkLists, lightProjection, lightProjection, lightView, ambientLight, playerPos, .depth); gpu_performance_measuring.stopQuery(); diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index 20ccfae9dc..e9f690b98b 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -53,8 +53,8 @@ pub var transparentUniforms: UniformStruct = undefined; const DepthUniformStruct = struct { projectionMatrix: c_int, viewMatrix: c_int, - lightPositionInteger: c_int, - lightPositionFraction: c_int, + playerPositionInteger: c_int, + playerPositionFraction: c_int, lodDistance: c_int, zNear: c_int, zFar: c_int, @@ -124,7 +124,7 @@ pub fn init() void { &depthUniforms, graphics.VertexArray.EmptyVertex, &.{}, - .{}, + .{.cullMode = .none}, .{.depthTest = true, .depthWrite = true, .depthCompare = .lessOrEqual}, .{.attachments = &.{.noBlending}}, ); @@ -259,8 +259,8 @@ pub fn bindDepthShaderAndUniforms(projMatrix: Mat4f, viewMatrix: Mat4f, cameraPo c.glUniform1f(depthUniforms.zNear, renderer.zNear); c.glUniform1f(depthUniforms.zFar, renderer.zFar); - c.glUniform3i(depthUniforms.lightPositionInteger, @floor(cameraPos[0]), @floor(cameraPos[1]), @floor(cameraPos[2])); - c.glUniform3f(depthUniforms.lightPositionFraction, @floatCast(@mod(cameraPos[0], 1)), @floatCast(@mod(cameraPos[1], 1)), @floatCast(@mod(cameraPos[2], 1))); + c.glUniform3i(depthUniforms.playerPositionInteger, @floor(cameraPos[0]), @floor(cameraPos[1]), @floor(cameraPos[2])); + c.glUniform3f(depthUniforms.playerPositionFraction, @floatCast(@mod(cameraPos[0], 1)), @floatCast(@mod(cameraPos[1], 1)), @floatCast(@mod(cameraPos[2], 1))); vao.bind(); } From 0245cbba6f41720cb7aec238af026c0d3321a91d Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Tue, 26 May 2026 13:00:10 -0400 Subject: [PATCH 04/33] fix flickering --- assets/cubyz/shaders/chunks/chunk_fragment.frag | 2 +- src/renderer.zig | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.frag b/assets/cubyz/shaders/chunks/chunk_fragment.frag index 18e77de26c..095225a254 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_fragment.frag @@ -63,7 +63,7 @@ float shadowCalculation(vec4 fragPosLightSpace) { vec3 lightDir = -normalize(lightViewMatrix[2].xyz); float bias = max(0.001 * (1.0 - dot(normal, lightDir)), 0.00001); float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; - if(projCoords.z > 1.0) + if(projCoords.z > 1.0 || projCoords.z < 0.0) shadow = 0.0; return shadow; } diff --git a/src/renderer.zig b/src/renderer.zig index 027d88b028..5d2c00ee6d 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -62,7 +62,7 @@ pub var activeFrameBuffer: c_uint = 0; pub const reflectionCubeMapSize = 64; var reflectionCubeMap: graphics.CubeMapTexture = undefined; -pub const shadowMapResolution = 2048; +pub const shadowMapResolution = 1024; pub const shadowMapSize = 128.0; var depthFrameBuffer: graphics.FrameBuffer = undefined; @@ -223,8 +223,10 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo } gpu_performance_measuring.stopQuery(); + const lightOffset: Vec3f = Vec3f {@floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))}; + const lightProjection: Mat4f = .orthogonal(-shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2); - const lightView: Mat4f = Mat4f.identity().mul(Mat4f.rotationX(std.math.pi*3.0/4.0)); + const lightView: Mat4f = Mat4f.identity().mul(.rotationX(std.math.pi*3.0/4.0)).mul(.translation(lightOffset)); gpu_performance_measuring.startQuery(.depth_framebuffer_chunk_rendering); chunk_meshing.drawChunksIndirect(&chunkLists, lightProjection, lightProjection, lightView, ambientLight, playerPos, .depth); gpu_performance_measuring.stopQuery(); From 949eb7d4977a02265aa17bfb4ec386c12b0b4fff Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Tue, 26 May 2026 13:38:14 -0400 Subject: [PATCH 05/33] fix grass and angle sun --- .../shaders/chunks/chunk_depth_fragment.frag | 14 +++-- .../shaders/chunks/chunk_depth_vertex.vert | 8 +-- src/gui/windows/gpu_performance_measuring.zig | 2 + src/renderer.zig | 54 +++++++++++++------ 4 files changed, 57 insertions(+), 21 deletions(-) diff --git a/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag b/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag index 4e10eb89da..6cc565ff94 100644 --- a/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag @@ -4,16 +4,24 @@ layout(location = 0) in vec3 mvVertexPos; layout(location = 1) in vec3 direction; layout(location = 2) in vec2 uv; layout(location = 3) flat in vec3 normal; -layout(location = 4) flat in int isBackFace; -layout(location = 5) flat in float distanceForLodCheck; -layout(location = 6) flat in int opaqueInLod; +layout(location = 4) flat in int textureIndex; +layout(location = 5) flat in int isBackFace; +layout(location = 6) flat in float distanceForLodCheck; +layout(location = 7) flat in int opaqueInLod; layout(location = 5) uniform float lodDistance; +layout(binding = 0) uniform sampler2DArray textureSampler; +layout(binding = 5) uniform sampler2D ditherTexture; + layout(std430, binding = 1) buffer _animatedTexture { float animatedTexture[]; }; void main() { + float animatedTextureIndex = animatedTexture[textureIndex]; + vec3 textureCoords = vec3(uv, animatedTextureIndex); + vec4 color = texture(textureSampler, textureCoords); + if (color.a < 0.5) discard; } diff --git a/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert b/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert index 088ce9289b..729de0eb29 100644 --- a/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert +++ b/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert @@ -4,9 +4,10 @@ layout(location = 0) out vec3 mvVertexPos; layout(location = 1) out vec3 direction; layout(location = 2) out vec2 uv; layout(location = 3) flat out vec3 normal; -layout(location = 4) flat out int isBackFace; -layout(location = 5) flat out float distanceForLodCheck; -layout(location = 6) flat out int opaqueInLod; +layout(location = 4) flat out int textureIndex; +layout(location = 5) flat out int isBackFace; +layout(location = 6) flat out float distanceForLodCheck; +layout(location = 7) flat out int opaqueInLod; layout(location = 1) uniform mat4 projectionMatrix; layout(location = 2) uniform mat4 viewMatrix; @@ -72,6 +73,7 @@ void main() { int textureAndQuad = faceData[faceID].textureAndQuad; isBackFace = encodedPositionAndLightIndex>>15 & 1; + textureIndex = textureAndQuad & 65535; int quadIndex = textureAndQuad >> 16; vec3 position = vec3( diff --git a/src/gui/windows/gpu_performance_measuring.zig b/src/gui/windows/gpu_performance_measuring.zig index b564cf71d9..4a10a2d1bc 100644 --- a/src/gui/windows/gpu_performance_measuring.zig +++ b/src/gui/windows/gpu_performance_measuring.zig @@ -15,6 +15,7 @@ const GuiComponent = gui.GuiComponent; pub const Samples = enum(u8) { screenbuffer_clear, depth_framebuffer_clear, + depth_framebuffer_chunk_rendering_preparation, depth_framebuffer_chunk_rendering, clear, skybox, @@ -36,6 +37,7 @@ pub const Samples = enum(u8) { const names = [_][]const u8{ "Screenbuffer clear", "Depth Framebuffer clear", + "Depth Framebuffer Chunk Rendering Preparation", "Depth Framebuffer Chunk Rendering", "Clear", "Skybox", diff --git a/src/renderer.zig b/src/renderer.zig index 5d2c00ee6d..bd1acab1e6 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -200,35 +200,36 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo c.glClear(c.GL_DEPTH_BUFFER_BIT); gpu_performance_measuring.stopQuery(); - // Uses FrustumCulling on the chunks. - const frustum = Frustum.init(Vec3f{0, 0, 0}, game.camera.viewMatrix, lastFov, lastWidth, lastHeight); + + const lightOffset: Vec3f = Vec3f {@floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))}; + + const lightProjection: Mat4f = .orthogonal(-shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2); + const lightView: Mat4f = Mat4f.identity().mul(.rotationX(std.math.pi*0.6)).mul(.translation(lightOffset)); + game.camera.updateViewMatrix(); chunk_meshing.quadsDrawn = 0; chunk_meshing.transparentQuadsDrawn = 0; - const meshes = mesh_storage.updateAndGetRenderChunks(world.conn, &frustum, playerPos, settings.renderDistance); - - gpu_performance_measuring.startQuery(.chunk_rendering_preparation); - const direction = crosshairDirection(game.camera.viewMatrix, lastFov, lastWidth, lastHeight); - MeshSelection.select(playerPos, direction, game.Player.inventory.getItem(game.Player.selectedSlot)); + const depthMeshes = mesh_storage.updateAndGetRenderChunks(world.conn, null, playerPos, settings.renderDistance); + gpu_performance_measuring.startQuery(.depth_framebuffer_chunk_rendering_preparation); chunk_meshing.beginRender(); - var chunkLists: [main.settings.highestSupportedLod + 1]main.List(u32) = @splat(main.List(u32).init(main.stackAllocator)); - defer for (chunkLists) |list| list.deinit(); - for (meshes) |mesh| { - mesh.prepareRendering(&chunkLists); + var depthChunkLists: [main.settings.highestSupportedLod + 1]main.List(u32) = @splat(main.List(u32).init(main.stackAllocator)); + defer for (depthChunkLists) |list| list.deinit(); + for (depthMeshes) |mesh| { + mesh.prepareRendering(&depthChunkLists); } gpu_performance_measuring.stopQuery(); - const lightOffset: Vec3f = Vec3f {@floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))}; + // Rebind block textures back to their original slots + c.glActiveTexture(c.GL_TEXTURE0); + blocks.meshes.blockTextureArray.bind(); - const lightProjection: Mat4f = .orthogonal(-shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2); - const lightView: Mat4f = Mat4f.identity().mul(.rotationX(std.math.pi*3.0/4.0)).mul(.translation(lightOffset)); gpu_performance_measuring.startQuery(.depth_framebuffer_chunk_rendering); - chunk_meshing.drawChunksIndirect(&chunkLists, lightProjection, lightProjection, lightView, ambientLight, playerPos, .depth); + chunk_meshing.drawChunksIndirect(&depthChunkLists, lightProjection, lightProjection, lightView, ambientLight, playerPos, .depth); gpu_performance_measuring.stopQuery(); chunk_meshing.endRender(); @@ -266,6 +267,29 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo chunk_meshing.quadsDrawn = 0; chunk_meshing.transparentQuadsDrawn = 0; + + // Uses FrustumCulling on the chunks. + const frustum = Frustum.init(Vec3f{0, 0, 0}, game.camera.viewMatrix, lastFov, lastWidth, lastHeight); + game.camera.updateViewMatrix(); + + chunk_meshing.quadsDrawn = 0; + chunk_meshing.transparentQuadsDrawn = 0; + const meshes = mesh_storage.updateAndGetRenderChunks(world.conn, &frustum, playerPos, settings.renderDistance); + + gpu_performance_measuring.startQuery(.chunk_rendering_preparation); + const direction = crosshairDirection(game.camera.viewMatrix, lastFov, lastWidth, lastHeight); + MeshSelection.select(playerPos, direction, game.Player.inventory.getItem(game.Player.selectedSlot)); + + chunk_meshing.beginRender(); + + + + var chunkLists: [main.settings.highestSupportedLod + 1]main.List(u32) = @splat(main.List(u32).init(main.stackAllocator)); + defer for (chunkLists) |list| list.deinit(); + for (meshes) |mesh| { + mesh.prepareRendering(&chunkLists); + } + gpu_performance_measuring.stopQuery(); chunk_meshing.beginRender(); gpu_performance_measuring.startQuery(.chunk_rendering); From d76ee060107c602eb15d98612ef01ca04f656008 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Tue, 26 May 2026 16:37:00 -0400 Subject: [PATCH 06/33] Pixel aligned shadows (but still bias!!!) --- .../cubyz/shaders/chunks/chunk_fragment.frag | 37 ++++++++++++++++--- assets/cubyz/shaders/chunks/chunk_vertex.vert | 5 +-- .../shaders/chunks/transparent_fragment.frag | 2 +- src/renderer.zig | 6 +-- src/renderer/chunk_meshing.zig | 2 +- 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.frag b/assets/cubyz/shaders/chunks/chunk_fragment.frag index 095225a254..7829dea447 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_fragment.frag @@ -4,7 +4,7 @@ layout(location = 0) in vec3 mvVertexPos; layout(location = 1) in vec3 direction; layout(location = 2) in vec3 light; layout(location = 3) in vec2 uv; -layout(location = 4) in vec4 mvVertexLightSpacePos; +layout(location = 4) in vec3 shadowPos; layout(location = 5) flat in vec3 normal; layout(location = 6) flat in int textureIndex; layout(location = 7) flat in int isBackFace; @@ -55,14 +55,39 @@ vec4 fixedCubeMapLookup(vec3 v) { // Taken from http://the-witness.net/news/2012 return texture(reflectionMap, v); } -float shadowCalculation(vec4 fragPosLightSpace) { - vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; +float shadowCalculation() { + vec3 dx = dFdx(shadowPos); + vec3 dy = dFdy(shadowPos); + + vec2 duv_dx = dFdx(uv); + vec2 duv_dy = dFdy(uv); + + vec2 texSize = vec2(16.0); + vec2 texelFrac = fract(uv * texSize); + vec2 texelOffset = (0.5 - texelFrac) / texSize; + + mat2 uvGrad = mat2(duv_dx, duv_dy); + + mat2 invUvGrad = inverse(uvGrad); + + vec2 screenOffset = invUvGrad * texelOffset; + + vec3 offset = + dx * screenOffset.x + + dy * screenOffset.y; + + vec3 snappedShadowPos = shadowPos + offset; + + vec4 lightPos = + lightProjectionMatrix * + lightViewMatrix * + vec4(snappedShadowPos, 1.0); + vec3 projCoords = lightPos.xyz / lightPos.w; projCoords = projCoords * 0.5 + 0.5; float closestDepth = texture(shadowMap, projCoords.xy).r; float currentDepth = projCoords.z; vec3 lightDir = -normalize(lightViewMatrix[2].xyz); - float bias = max(0.001 * (1.0 - dot(normal, lightDir)), 0.00001); - float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; + float shadow = currentDepth > closestDepth ? 1.0 : 0.0; if(projCoords.z > 1.0 || projCoords.z < 0.0) shadow = 0.0; return shadow; @@ -80,7 +105,7 @@ void main() { reflectivity = reflectivity*fixedCubeMapLookup(reflect(direction, normal)).x; reflectivity = reflectivity*(1 - fresnelReflection) + fresnelReflection; - float shadow = shadowCalculation(mvVertexLightSpacePos); + float shadow = shadowCalculation(); vec3 pixelLight = max((1.0 - shadow*0.5)*light*normalVariation, texture(emissionSampler, textureCoords).r*4); fragColor = texture(textureSampler, textureCoords)*vec4(pixelLight, 1); fragColor.rgb += reflectivity*pixelLight; diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vert b/assets/cubyz/shaders/chunks/chunk_vertex.vert index a1983e64c6..2a0ff26926 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vert +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vert @@ -4,7 +4,7 @@ layout(location = 0) out vec3 mvVertexPos; layout(location = 1) out vec3 direction; layout(location = 2) out vec3 light; layout(location = 3) out vec2 uv; -layout(location = 4) out vec4 mvVertexLightSpacePos; +layout(location = 4) out vec3 shadowPos; layout(location = 5) flat out vec3 normal; layout(location = 6) flat out int textureIndex; layout(location = 7) flat out int isBackFace; @@ -124,6 +124,5 @@ void main() { uv = quads[quadIndex].cornerUV[vertexID]*voxelSize; opaqueInLod = quads[quadIndex].opaqueInLod; - vec3 shadowPos = position + normal * 0.02; - mvVertexLightSpacePos = lightProjectionMatrix*lightViewMatrix*vec4(shadowPos, 1); + shadowPos = position - normal * 0.001; } diff --git a/assets/cubyz/shaders/chunks/transparent_fragment.frag b/assets/cubyz/shaders/chunks/transparent_fragment.frag index 2664f88ede..c628d55518 100644 --- a/assets/cubyz/shaders/chunks/transparent_fragment.frag +++ b/assets/cubyz/shaders/chunks/transparent_fragment.frag @@ -4,7 +4,7 @@ layout(location = 0) in vec3 mvVertexPos; layout(location = 1) in vec3 direction; layout(location = 2) in vec3 light; layout(location = 3) in vec2 uv; -layout(location = 4) in vec4 mvVertexLightSpacePos; +layout(location = 4) in vec3 shadowPos; layout(location = 5) flat in vec3 normal; layout(location = 6) flat in int textureIndex; layout(location = 7) flat in int isBackFace; diff --git a/src/renderer.zig b/src/renderer.zig index bd1acab1e6..a84b541b55 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -62,7 +62,7 @@ pub var activeFrameBuffer: c_uint = 0; pub const reflectionCubeMapSize = 64; var reflectionCubeMap: graphics.CubeMapTexture = undefined; -pub const shadowMapResolution = 1024; +pub const shadowMapResolution = 4096; pub const shadowMapSize = 128.0; var depthFrameBuffer: graphics.FrameBuffer = undefined; @@ -203,8 +203,8 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo const lightOffset: Vec3f = Vec3f {@floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))}; - const lightProjection: Mat4f = .orthogonal(-shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2); - const lightView: Mat4f = Mat4f.identity().mul(.rotationX(std.math.pi*0.6)).mul(.translation(lightOffset)); + const lightProjection: Mat4f = .orthogonal(-shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2, -shadowMapSize, shadowMapSize); + const lightView: Mat4f = Mat4f.identity().mul(.rotationX(std.math.pi*0.8)).mul(.rotationZ(std.math.pi*0.2)).mul(.translation(lightOffset)); game.camera.updateViewMatrix(); diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index e9f690b98b..c98d1d3c4e 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -124,7 +124,7 @@ pub fn init() void { &depthUniforms, graphics.VertexArray.EmptyVertex, &.{}, - .{.cullMode = .none}, + .{.cullMode = .front}, .{.depthTest = true, .depthWrite = true, .depthCompare = .lessOrEqual}, .{.attachments = &.{.noBlending}}, ); From 01193d3ad4de93f12d3dab72be52c864df52b4b5 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Tue, 26 May 2026 17:29:36 -0400 Subject: [PATCH 07/33] painnnnn --- assets/cubyz/shaders/chunks/chunk_fragment.frag | 5 ++++- assets/cubyz/shaders/chunks/chunk_vertex.vert | 2 +- src/renderer.zig | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.frag b/assets/cubyz/shaders/chunks/chunk_fragment.frag index 7829dea447..cd6e252d62 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_fragment.frag @@ -87,7 +87,10 @@ float shadowCalculation() { float closestDepth = texture(shadowMap, projCoords.xy).r; float currentDepth = projCoords.z; vec3 lightDir = -normalize(lightViewMatrix[2].xyz); - float shadow = currentDepth > closestDepth ? 1.0 : 0.0; + float ndotl = max(dot(normal, lightDir), 0.0); + + float bias = max(0.0002 * (1.0 - ndotl), 0.00002); + float shadow = currentDepth + 1.0/textureSize(shadowMap, 0).x > closestDepth ? 1.0 : 0.0; if(projCoords.z > 1.0 || projCoords.z < 0.0) shadow = 0.0; return shadow; diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vert b/assets/cubyz/shaders/chunks/chunk_vertex.vert index 2a0ff26926..7e7f0aa407 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vert +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vert @@ -124,5 +124,5 @@ void main() { uv = quads[quadIndex].cornerUV[vertexID]*voxelSize; opaqueInLod = quads[quadIndex].opaqueInLod; - shadowPos = position - normal * 0.001; + shadowPos = position + normal * 0.05; } diff --git a/src/renderer.zig b/src/renderer.zig index a84b541b55..82b7fc9ac8 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -63,7 +63,7 @@ pub const reflectionCubeMapSize = 64; var reflectionCubeMap: graphics.CubeMapTexture = undefined; pub const shadowMapResolution = 4096; -pub const shadowMapSize = 128.0; +pub const shadowMapSize = 96.0; var depthFrameBuffer: graphics.FrameBuffer = undefined; pub fn init() void { From c47ff415ad00f31e9e6d4817e305de765b62baa1 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Tue, 26 May 2026 17:30:28 -0400 Subject: [PATCH 08/33] even more pain --- assets/cubyz/shaders/chunks/chunk_fragment.frag | 2 -- 1 file changed, 2 deletions(-) diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.frag b/assets/cubyz/shaders/chunks/chunk_fragment.frag index cd6e252d62..707d4a8ebf 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_fragment.frag @@ -88,8 +88,6 @@ float shadowCalculation() { float currentDepth = projCoords.z; vec3 lightDir = -normalize(lightViewMatrix[2].xyz); float ndotl = max(dot(normal, lightDir), 0.0); - - float bias = max(0.0002 * (1.0 - ndotl), 0.00002); float shadow = currentDepth + 1.0/textureSize(shadowMap, 0).x > closestDepth ? 1.0 : 0.0; if(projCoords.z > 1.0 || projCoords.z < 0.0) shadow = 0.0; From ac77d71e4ef823b31e5ab6e87a3c68fa6cf9ce2f Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Tue, 26 May 2026 17:32:46 -0400 Subject: [PATCH 09/33] painiest pain --- assets/cubyz/shaders/chunks/chunk_fragment.frag | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.frag b/assets/cubyz/shaders/chunks/chunk_fragment.frag index 707d4a8ebf..8b4ec90387 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_fragment.frag @@ -89,7 +89,9 @@ float shadowCalculation() { vec3 lightDir = -normalize(lightViewMatrix[2].xyz); float ndotl = max(dot(normal, lightDir), 0.0); float shadow = currentDepth + 1.0/textureSize(shadowMap, 0).x > closestDepth ? 1.0 : 0.0; - if(projCoords.z > 1.0 || projCoords.z < 0.0) + if(projCoords.x < 0.0 || projCoords.x > 1.0 || + projCoords.y < 0.0 || projCoords.y > 1.0 || + projCoords.z < 0.0 || projCoords.z > 1.0) shadow = 0.0; return shadow; } From ea5b91c947773aa36ef045033e1a8039d8b15e09 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Wed, 27 May 2026 06:31:14 -0400 Subject: [PATCH 10/33] Directional shading!!!!! --- assets/cubyz/shaders/chunks/chunk_fragment.frag | 14 ++++++++------ assets/cubyz/shaders/chunks/chunk_vertex.vert | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.frag b/assets/cubyz/shaders/chunks/chunk_fragment.frag index 8b4ec90387..716a55622d 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_fragment.frag @@ -56,6 +56,12 @@ vec4 fixedCubeMapLookup(vec3 v) { // Taken from http://the-witness.net/news/2012 } float shadowCalculation() { + vec3 lightDir = normalize(vec3( + lightViewMatrix[0][2], + lightViewMatrix[1][2], + lightViewMatrix[2][2] + )); + if (dot(lightDir, normal) > 0.0) return 1.0; vec3 dx = dFdx(shadowPos); vec3 dy = dFdy(shadowPos); @@ -86,12 +92,8 @@ float shadowCalculation() { projCoords = projCoords * 0.5 + 0.5; float closestDepth = texture(shadowMap, projCoords.xy).r; float currentDepth = projCoords.z; - vec3 lightDir = -normalize(lightViewMatrix[2].xyz); - float ndotl = max(dot(normal, lightDir), 0.0); - float shadow = currentDepth + 1.0/textureSize(shadowMap, 0).x > closestDepth ? 1.0 : 0.0; - if(projCoords.x < 0.0 || projCoords.x > 1.0 || - projCoords.y < 0.0 || projCoords.y > 1.0 || - projCoords.z < 0.0 || projCoords.z > 1.0) + float shadow = currentDepth > closestDepth ? 1.0 : 0.0; + if(projCoords.z > 1.0) shadow = 0.0; return shadow; } diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vert b/assets/cubyz/shaders/chunks/chunk_vertex.vert index 7e7f0aa407..a6ae732a04 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vert +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vert @@ -124,5 +124,5 @@ void main() { uv = quads[quadIndex].cornerUV[vertexID]*voxelSize; opaqueInLod = quads[quadIndex].opaqueInLod; - shadowPos = position + normal * 0.05; + shadowPos = position + normal * 0.01; } From 15292a5606611177a2d4f2e777c92fc4a9cfe6c4 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Wed, 27 May 2026 06:33:30 -0400 Subject: [PATCH 11/33] tweaky the numbers --- assets/cubyz/shaders/chunks/chunk_vertex.vert | 2 +- src/renderer.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vert b/assets/cubyz/shaders/chunks/chunk_vertex.vert index a6ae732a04..a289b6e9ee 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vert +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vert @@ -124,5 +124,5 @@ void main() { uv = quads[quadIndex].cornerUV[vertexID]*voxelSize; opaqueInLod = quads[quadIndex].opaqueInLod; - shadowPos = position + normal * 0.01; + shadowPos = position + normal * 0.1; } diff --git a/src/renderer.zig b/src/renderer.zig index 82b7fc9ac8..a84b541b55 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -63,7 +63,7 @@ pub const reflectionCubeMapSize = 64; var reflectionCubeMap: graphics.CubeMapTexture = undefined; pub const shadowMapResolution = 4096; -pub const shadowMapSize = 96.0; +pub const shadowMapSize = 128.0; var depthFrameBuffer: graphics.FrameBuffer = undefined; pub fn init() void { From 16aaf1b5b7e3ae9315fa8f6f2643b0ce2ed22a96 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Wed, 27 May 2026 06:36:56 -0400 Subject: [PATCH 12/33] lower resolution --- src/renderer.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer.zig b/src/renderer.zig index a84b541b55..841415d3a8 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -62,7 +62,7 @@ pub var activeFrameBuffer: c_uint = 0; pub const reflectionCubeMapSize = 64; var reflectionCubeMap: graphics.CubeMapTexture = undefined; -pub const shadowMapResolution = 4096; +pub const shadowMapResolution = 2048; pub const shadowMapSize = 128.0; var depthFrameBuffer: graphics.FrameBuffer = undefined; From 4360d44f59572131cc3f38d7b83e7421b8121985 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Fri, 29 May 2026 14:47:48 -0400 Subject: [PATCH 13/33] Fix a small bug --- src/renderer.zig | 2 +- src/renderer/chunk_meshing.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer.zig b/src/renderer.zig index 841415d3a8..215dd4e58d 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -63,7 +63,7 @@ pub const reflectionCubeMapSize = 64; var reflectionCubeMap: graphics.CubeMapTexture = undefined; pub const shadowMapResolution = 2048; -pub const shadowMapSize = 128.0; +pub const shadowMapSize = 96.0; var depthFrameBuffer: graphics.FrameBuffer = undefined; pub fn init() void { diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index c98d1d3c4e..dc24b9644b 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -319,7 +319,7 @@ fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, lightProjMatrix: Ma c.glUniform3i(occlusionTestUniforms.playerPositionInteger, @floor(playerPos[0]), @floor(playerPos[1]), @floor(playerPos[2])); c.glUniform3f(occlusionTestUniforms.playerPositionFraction, @floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))); c.glUniformMatrix4fv(occlusionTestUniforms.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projMatrix)); - c.glUniformMatrix4fv(occlusionTestUniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&game.camera.viewMatrix)); + c.glUniformMatrix4fv(occlusionTestUniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&if(mode == .depth) lightViewMatrix else game.camera.viewMatrix)); vao.bind(); c.glDrawElementsBaseVertex(c.GL_TRIANGLES, @intCast(6*6*chunkIDs.len), c.GL_UNSIGNED_INT, null, chunkIDAllocation.start*24); c.glMemoryBarrier(c.GL_SHADER_STORAGE_BARRIER_BIT); From aab9a84b5759048d3f4e297ad27800a75c04d653 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Sat, 30 May 2026 15:10:15 -0400 Subject: [PATCH 14/33] Fix the visibility state issues --- .../shaders/chunks/chunk_depth_vertex.vert | 2 + assets/cubyz/shaders/chunks/chunk_vertex.vert | 2 + .../shaders/chunks/fillIndirectBuffer.comp | 38 ++++++++++++++++--- .../shaders/chunks/occlusionTestFragment.frag | 10 ++++- .../shaders/chunks/occlusionTestVertex.vert | 9 ++++- src/graphics.zig | 2 + src/renderer/chunk_meshing.zig | 8 ++++ 7 files changed, 64 insertions(+), 7 deletions(-) diff --git a/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert b/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert index 729de0eb29..8934416b5b 100644 --- a/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert +++ b/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert @@ -53,6 +53,8 @@ struct ChunkData { uint vertexCountTransparent; uint visibilityState; uint oldVisibilityState; + uint visibilityStateDepth; + uint oldVisibilityStateDepth; }; layout(std430, binding = 6) buffer _chunks diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vert b/assets/cubyz/shaders/chunks/chunk_vertex.vert index a289b6e9ee..00080cbac7 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vert +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vert @@ -62,6 +62,8 @@ struct ChunkData { uint vertexCountTransparent; uint visibilityState; uint oldVisibilityState; + uint visibilityStateDepth; + uint oldVisibilityStateDepth; }; layout(std430, binding = 6) buffer _chunks diff --git a/assets/cubyz/shaders/chunks/fillIndirectBuffer.comp b/assets/cubyz/shaders/chunks/fillIndirectBuffer.comp index 824bc9a589..7fdf799b4b 100644 --- a/assets/cubyz/shaders/chunks/fillIndirectBuffer.comp +++ b/assets/cubyz/shaders/chunks/fillIndirectBuffer.comp @@ -19,6 +19,8 @@ struct ChunkData { uint vertexCountTransparent; uint visibilityState; uint oldVisibilityState; + uint visibilityStateDepth; + uint oldVisibilityStateDepth; }; layout(std430, binding = 6) buffer _chunks { @@ -49,6 +51,8 @@ layout(location = 5) uniform ivec3 playerPositionInteger; layout(location = 6) uniform float lodDistance; +layout(location = 7) uniform bool isDepth; + bool isVisible(int dir, ivec3 playerDist) { switch(dir) { case 0: // dirUp @@ -87,14 +91,31 @@ void main() { uint commandIndexEnd = commandIndex + 8; uint groupFaceOffset = 0; uint groupFaceCount = 0; - uint oldoldvisibilityState = chunks[chunkID].oldVisibilityState; + uint oldoldvisibilityState; + if(isDepth) { + oldoldvisibilityState = chunks[chunkID].oldVisibilityStateDepth; + } else { + 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)) { + uint oldVisibilityState; + if(isDepth) { + oldVisibilityState = chunks[chunkID].oldVisibilityStateDepth; + } else { + oldVisibilityState = chunks[chunkID].oldVisibilityState; + } + uint visibilityState; + if(isDepth) { + visibilityState = chunks[chunkID].visibilityStateDepth; + } else { + visibilityState = chunks[chunkID].visibilityState; + } + if((onlyDrawPreviouslyInvisible && oldVisibilityState == 0 && visibilityState != 0) || (oldVisibilityState != 0 && !onlyDrawPreviouslyInvisible)) { for(int i = 0; i < 14; i++) { if(playerDistSquare >= lodDistance*lodDistance && i == 7) break; uint faceCount = chunks[chunkID].faceCountsByNormalOpaque[i]; @@ -112,8 +133,15 @@ void main() { } } if(onlyDrawPreviouslyInvisible) { - chunks[chunkID].oldVisibilityState = chunks[chunkID].visibilityState; - chunks[chunkID].visibilityState = 0; + if(isDepth) { + chunks[chunkID].oldVisibilityStateDepth = chunks[chunkID].visibilityStateDepth; + chunks[chunkID].visibilityStateDepth = 0; + oldVisibilityState = chunks[chunkID].oldVisibilityStateDepth; + } else { + chunks[chunkID].oldVisibilityState = chunks[chunkID].visibilityState; + chunks[chunkID].visibilityState = 0; + oldVisibilityState = chunks[chunkID].oldVisibilityState; + } } if(groupFaceCount != 0) { commands[commandIndex] = addCommand(6*groupFaceCount, chunks[chunkID].vertexStartOpaque + 4*groupFaceOffset, chunkID); @@ -121,7 +149,7 @@ void main() { } for(; commandIndex < commandIndexEnd; commandIndex++) { - commands[commandIndex] = DrawElementsIndirectCommand(0, 0, 0, 0, oldoldvisibilityState << 1 | chunks[chunkID].oldVisibilityState); + commands[commandIndex] = DrawElementsIndirectCommand(0, 0, 0, 0, oldoldvisibilityState << 1 | oldVisibilityState); } } } diff --git a/assets/cubyz/shaders/chunks/occlusionTestFragment.frag b/assets/cubyz/shaders/chunks/occlusionTestFragment.frag index 106a89259d..61f00c93da 100644 --- a/assets/cubyz/shaders/chunks/occlusionTestFragment.frag +++ b/assets/cubyz/shaders/chunks/occlusionTestFragment.frag @@ -16,6 +16,8 @@ struct ChunkData { uint vertexCountTransparent; uint visibilityState; uint oldVisibilityState; + uint visibilityStateDepth; + uint oldVisibilityStateDepth; }; layout(std430, binding = 6) buffer _chunks @@ -23,6 +25,12 @@ layout(std430, binding = 6) buffer _chunks ChunkData chunks[]; }; +layout(location = 4) uniform bool isDepth; + void main() { - chunks[chunkID].visibilityState = 1; + if(isDepth) { + chunks[chunkID].visibilityStateDepth = 1; + } else { + chunks[chunkID].visibilityState = 1; + } } diff --git a/assets/cubyz/shaders/chunks/occlusionTestVertex.vert b/assets/cubyz/shaders/chunks/occlusionTestVertex.vert index 6eea1acb5f..0020c5f2a0 100644 --- a/assets/cubyz/shaders/chunks/occlusionTestVertex.vert +++ b/assets/cubyz/shaders/chunks/occlusionTestVertex.vert @@ -14,6 +14,8 @@ struct ChunkData { uint vertexCountTransparent; uint visibilityState; uint oldVisibilityState; + uint visibilityStateDepth; + uint oldVisibilityStateDepth; }; layout(std430, binding = 6) buffer _chunks @@ -61,6 +63,7 @@ layout(location = 0) uniform mat4 projectionMatrix; layout(location = 1) uniform mat4 viewMatrix; layout(location = 2) uniform ivec3 playerPositionInteger; layout(location = 3) uniform vec3 playerPositionFraction; +layout(location = 4) uniform bool isDepth; void main() { uint chunkIDID = uint(gl_VertexID)/24u; @@ -68,7 +71,11 @@ void main() { chunkID = chunkIDs[chunkIDID]; vec3 modelPosition = vec3(chunks[chunkID].position.xyz - playerPositionInteger) - playerPositionFraction; if(all(lessThan(modelPosition + chunks[chunkID].minPos.xyz*chunks[chunkID].voxelSize, vec3(0, 0, 0))) && all(greaterThan(modelPosition + chunks[chunkID].maxPos.xyz*chunks[chunkID].voxelSize, vec3(0, 0, 0)))) { - chunks[chunkID].visibilityState = 1; + if(isDepth) { + chunks[chunkID].visibilityStateDepth = 1; + } else { + chunks[chunkID].visibilityState = 1; + } gl_Position = vec4(-2, -2, -2, 1); return; } diff --git a/src/graphics.zig b/src/graphics.zig index c7f0853678..d46be9077e 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -2251,6 +2251,8 @@ pub fn generateBlockTexture(blockType: u16) Texture { .vertexCountTransparent = undefined, .visibilityState = 0, .oldVisibilityState = 0, + .visibilityStateDepth = 0, + .oldVisibilityStateDepth = 0, }}, &chunkAllocation); defer main.renderer.chunk_meshing.chunkBuffer.free(chunkAllocation); if (block.transparent()) { diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index f698d83ebf..97017f45c2 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -69,6 +69,7 @@ pub var commandUniforms: struct { playerPositionInteger: c_int, onlyDrawPreviouslyInvisible: c_int, lodDistance: c_int, + isDepth: c_int, } = undefined; pub var occlusionTestPipeline: graphics.Pipeline = undefined; pub var occlusionTestUniforms: struct { @@ -76,6 +77,7 @@ pub var occlusionTestUniforms: struct { viewMatrix: c_int, playerPositionInteger: c_int, playerPositionFraction: c_int, + isDepth: c_int, } = undefined; pub var vao: graphics.VertexArray = undefined; pub var faceBuffers: [settings.highestSupportedLod + 1]graphics.LargeBuffer(FaceData) = undefined; @@ -299,6 +301,7 @@ fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, lightProjMatrix: Ma c.glUniform1ui(commandUniforms.commandIndexStart, allocation.start); c.glUniform1ui(commandUniforms.size, @intCast(chunkIDs.len)); c.glUniform1i(commandUniforms.isTransparent, @intFromBool(mode == .transparent)); + c.glUniform1i(commandUniforms.isDepth, @intFromBool(mode == .depth)); c.glUniform3i(commandUniforms.playerPositionInteger, @floor(playerPos[0]), @floor(playerPos[1]), @floor(playerPos[2])); if (mode != .transparent) { c.glUniform1i(commandUniforms.onlyDrawPreviouslyInvisible, 0); @@ -320,6 +323,7 @@ fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, lightProjMatrix: Ma c.glUniform3f(occlusionTestUniforms.playerPositionFraction, @floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))); c.glUniformMatrix4fv(occlusionTestUniforms.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projMatrix)); c.glUniformMatrix4fv(occlusionTestUniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&if(mode == .depth) lightViewMatrix else game.camera.viewMatrix)); + c.glUniform1i(occlusionTestUniforms.isDepth, @intFromBool(mode == .depth)); vao.bind(); c.glDrawElementsBaseVertex(c.GL_TRIANGLES, @intCast(6*6*chunkIDs.len), c.GL_UNSIGNED_INT, null, chunkIDAllocation.start*24); c.glMemoryBarrier(c.GL_SHADER_STORAGE_BARRIER_BIT); @@ -372,6 +376,8 @@ pub const ChunkData = extern struct { vertexCountTransparent: u32, visibilityState: u32, oldVisibilityState: u32, + visibilityStateDepth: u32, + oldVisibilityStateDepth: u32, }; pub const IndirectData = extern struct { @@ -1505,6 +1511,8 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh .max = self.max, .visibilityState = 0, .oldVisibilityState = 0, + .visibilityStateDepth = 0, + .oldVisibilityStateDepth = 0, }}, &self.chunkAllocation); } From 7cbc7afce2b4a081e6c5419a4c001136ab23234b Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Sat, 30 May 2026 15:11:23 -0400 Subject: [PATCH 15/33] Formatting --- src/graphics.zig | 2 +- src/renderer.zig | 10 +++------- src/renderer/chunk_meshing.zig | 5 ++--- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/graphics.zig b/src/graphics.zig index d46be9077e..614575dc7d 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -1647,7 +1647,7 @@ pub const FrameBuffer = struct { // MARK: FrameBuffer c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, textureFilter); c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_S, textureWrap); c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_T, textureWrap); - c.glTexParameterfv(c.GL_TEXTURE_2D, c.GL_TEXTURE_BORDER_COLOR, @ptrCast(&[4]f32 {1.0, 1.0, 1.0, 1.0})); + c.glTexParameterfv(c.GL_TEXTURE_2D, c.GL_TEXTURE_BORDER_COLOR, @ptrCast(&[4]f32{1.0, 1.0, 1.0, 1.0})); c.glFramebufferTexture2D(c.GL_FRAMEBUFFER, c.GL_DEPTH_ATTACHMENT, c.GL_TEXTURE_2D, self.depthTexture, 0); } if (hasTexture) { diff --git a/src/renderer.zig b/src/renderer.zig index f4f6b96602..dcfeb42d38 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -200,8 +200,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo c.glClear(c.GL_DEPTH_BUFFER_BIT); gpu_performance_measuring.stopQuery(); - - const lightOffset: Vec3f = Vec3f {@floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))}; + const lightOffset: Vec3f = Vec3f{@floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))}; const lightProjection: Mat4f = .orthogonal(-shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2, -shadowMapSize, shadowMapSize); const lightView: Mat4f = Mat4f.identity().mul(.rotationX(std.math.pi*0.8)).mul(.rotationZ(std.math.pi*0.2)).mul(.translation(lightOffset)); @@ -215,8 +214,6 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo gpu_performance_measuring.startQuery(.depth_framebuffer_chunk_rendering_preparation); chunk_meshing.beginRender(); - - var depthChunkLists: [main.settings.highestSupportedLod + 1]main.List(u32) = @splat(main.List(u32).init(main.stackAllocator)); defer for (depthChunkLists) |list| list.deinit(); for (depthMeshes) |mesh| { @@ -231,7 +228,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo gpu_performance_measuring.startQuery(.depth_framebuffer_chunk_rendering); chunk_meshing.drawChunksIndirect(&depthChunkLists, lightProjection, lightProjection, lightView, ambientLight, playerPos, .depth); gpu_performance_measuring.stopQuery(); - + chunk_meshing.endRender(); worldFrameBuffer.bind(); @@ -240,7 +237,6 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo worldFrameBuffer.clear(Vec4f{skyColor[0], skyColor[1], skyColor[2], 1}); gpu_performance_measuring.stopQuery(); - const time: u32 = @intCast(main.timestamp().toMilliseconds() & std.math.maxInt(u32)); gpu_performance_measuring.startQuery(.skybox); @@ -267,7 +263,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo chunk_meshing.quadsDrawn = 0; chunk_meshing.transparentQuadsDrawn = 0; - + // Uses FrustumCulling on the chunks. const frustum = Frustum.init(Vec3f{0, 0, 0}, game.camera.viewMatrix, lastFov, lastWidth, lastHeight); game.camera.updateViewMatrix(); diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index 97017f45c2..40d2bacaa5 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -223,7 +223,7 @@ fn bindCommonUniforms(locations: *UniformStruct, projMatrix: Mat4f, lightProjMat c.glUniform3i(locations.playerPositionInteger, @floor(playerPos[0]), @floor(playerPos[1]), @floor(playerPos[2])); c.glUniform3f(locations.playerPositionFraction, @floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))); - + c.glUniformMatrix4fv(locations.lightProjectionMatrix, 1, c.GL_TRUE, @ptrCast(&lightProjMatrix)); c.glUniformMatrix4fv(locations.lightViewMatrix, 1, c.GL_TRUE, @ptrCast(&lightViewMatrix)); } @@ -286,7 +286,6 @@ pub fn drawChunksIndirect(chunkIds: *const [main.settings.highestSupportedLod + } } - fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d, mode: DrawMode) void { if (chunkIDs.len == 0) return; const drawCallsEstimate: u31 = @intCast(if (mode == .transparent) chunkIDs.len else chunkIDs.len*8); @@ -322,7 +321,7 @@ fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, lightProjMatrix: Ma c.glUniform3i(occlusionTestUniforms.playerPositionInteger, @floor(playerPos[0]), @floor(playerPos[1]), @floor(playerPos[2])); c.glUniform3f(occlusionTestUniforms.playerPositionFraction, @floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))); c.glUniformMatrix4fv(occlusionTestUniforms.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projMatrix)); - c.glUniformMatrix4fv(occlusionTestUniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&if(mode == .depth) lightViewMatrix else game.camera.viewMatrix)); + c.glUniformMatrix4fv(occlusionTestUniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&if (mode == .depth) lightViewMatrix else game.camera.viewMatrix)); c.glUniform1i(occlusionTestUniforms.isDepth, @intFromBool(mode == .depth)); vao.bind(); c.glDrawElementsBaseVertex(c.GL_TRIANGLES, @intCast(6*6*chunkIDs.len), c.GL_UNSIGNED_INT, null, chunkIDAllocation.start*24); From cab93f9f995fdd41eb2f05ce9b9d7067c9b3c9e1 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Sun, 31 May 2026 13:35:48 -0400 Subject: [PATCH 16/33] Mostly working version --- src/renderer.zig | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/renderer.zig b/src/renderer.zig index dcfeb42d38..f6c89db1ae 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -63,7 +63,7 @@ pub const reflectionCubeMapSize = 64; var reflectionCubeMap: graphics.CubeMapTexture = undefined; pub const shadowMapResolution = 2048; -pub const shadowMapSize = 96.0; +pub const shadowMapSize = 16.0; var depthFrameBuffer: graphics.FrameBuffer = undefined; pub fn init() void { @@ -202,8 +202,29 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo const lightOffset: Vec3f = Vec3f{@floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))}; - const lightProjection: Mat4f = .orthogonal(-shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2, -shadowMapSize, shadowMapSize); - const lightView: Mat4f = Mat4f.identity().mul(.rotationX(std.math.pi*0.8)).mul(.rotationZ(std.math.pi*0.2)).mul(.translation(lightOffset)); + + const xRot = std.math.pi*0.8; + const zRot = std.math.pi*0.2; + + const val = vec.rotateZ(vec.rotateX(Vec3f{0.0, 0.0, 1.0}, xRot), zRot); + const xR = val[0]; + const yR = val[1]; + const zR = val[2]; + + std.log.debug("{} {} {}", .{xR, yR, zR}); + + const far = shadowMapSize; + const near = -shadowMapSize; + const lightProjection = (Mat4f{ + .rows = [4]Vec4f { + Vec4f{1, 0, xR/zR, 0.0}, + Vec4f{0, 1, yR/zR, 0.0}, + Vec4f{0, 0, 1.0/(far - near), -near/(far - near)}, + Vec4f{0, 0, 0, 1}, + } + }).mul(.scale(.{1.0/shadowMapSize, 1.0/shadowMapSize, 1.0})).mul(.rotationZ(-zRot)).mul(.rotationZ(-xRot)); + + const lightView: Mat4f = Mat4f.identity().mul(.rotationX(xRot)).mul(.rotationZ(zRot)).mul(.translation(lightOffset)); game.camera.updateViewMatrix(); From e0d2da3e6cf0d262fd61514495b9b98ed0bcbc00 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Sun, 31 May 2026 14:31:44 -0400 Subject: [PATCH 17/33] Debug line --- src/renderer.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/renderer.zig b/src/renderer.zig index f6c89db1ae..069c636077 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -211,7 +211,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo const yR = val[1]; const zR = val[2]; - std.log.debug("{} {} {}", .{xR, yR, zR}); + //std.log.debug("{} {} {}", .{xR, yR, zR}); const far = shadowMapSize; const near = -shadowMapSize; @@ -226,6 +226,8 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo const lightView: Mat4f = Mat4f.identity().mul(.rotationX(xRot)).mul(.rotationZ(zRot)).mul(.translation(lightOffset)); + //std.log.debug("View matrix {}", .{lightView.rows}); + game.camera.updateViewMatrix(); chunk_meshing.quadsDrawn = 0; From ed23177b2a72ca86e77db267a32adbe9e66f4188 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Sun, 31 May 2026 15:17:39 -0400 Subject: [PATCH 18/33] Revert "Debug line" This reverts commit e0d2da3e6cf0d262fd61514495b9b98ed0bcbc00. --- src/renderer.zig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/renderer.zig b/src/renderer.zig index 069c636077..f6c89db1ae 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -211,7 +211,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo const yR = val[1]; const zR = val[2]; - //std.log.debug("{} {} {}", .{xR, yR, zR}); + std.log.debug("{} {} {}", .{xR, yR, zR}); const far = shadowMapSize; const near = -shadowMapSize; @@ -226,8 +226,6 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo const lightView: Mat4f = Mat4f.identity().mul(.rotationX(xRot)).mul(.rotationZ(zRot)).mul(.translation(lightOffset)); - //std.log.debug("View matrix {}", .{lightView.rows}); - game.camera.updateViewMatrix(); chunk_meshing.quadsDrawn = 0; From b625737c009565ef0d4ba63363f199db18acdd12 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Sun, 31 May 2026 15:17:46 -0400 Subject: [PATCH 19/33] Revert "Mostly working version" This reverts commit cab93f9f995fdd41eb2f05ce9b9d7067c9b3c9e1. --- src/renderer.zig | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/src/renderer.zig b/src/renderer.zig index f6c89db1ae..dcfeb42d38 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -63,7 +63,7 @@ pub const reflectionCubeMapSize = 64; var reflectionCubeMap: graphics.CubeMapTexture = undefined; pub const shadowMapResolution = 2048; -pub const shadowMapSize = 16.0; +pub const shadowMapSize = 96.0; var depthFrameBuffer: graphics.FrameBuffer = undefined; pub fn init() void { @@ -202,29 +202,8 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo const lightOffset: Vec3f = Vec3f{@floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))}; - - const xRot = std.math.pi*0.8; - const zRot = std.math.pi*0.2; - - const val = vec.rotateZ(vec.rotateX(Vec3f{0.0, 0.0, 1.0}, xRot), zRot); - const xR = val[0]; - const yR = val[1]; - const zR = val[2]; - - std.log.debug("{} {} {}", .{xR, yR, zR}); - - const far = shadowMapSize; - const near = -shadowMapSize; - const lightProjection = (Mat4f{ - .rows = [4]Vec4f { - Vec4f{1, 0, xR/zR, 0.0}, - Vec4f{0, 1, yR/zR, 0.0}, - Vec4f{0, 0, 1.0/(far - near), -near/(far - near)}, - Vec4f{0, 0, 0, 1}, - } - }).mul(.scale(.{1.0/shadowMapSize, 1.0/shadowMapSize, 1.0})).mul(.rotationZ(-zRot)).mul(.rotationZ(-xRot)); - - const lightView: Mat4f = Mat4f.identity().mul(.rotationX(xRot)).mul(.rotationZ(zRot)).mul(.translation(lightOffset)); + const lightProjection: Mat4f = .orthogonal(-shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2, -shadowMapSize, shadowMapSize); + const lightView: Mat4f = Mat4f.identity().mul(.rotationX(std.math.pi*0.8)).mul(.rotationZ(std.math.pi*0.2)).mul(.translation(lightOffset)); game.camera.updateViewMatrix(); From 175d035bae06a2e8390f2001f66e58eb982a1cb9 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Sun, 31 May 2026 16:03:19 -0400 Subject: [PATCH 20/33] Make light sources remove shadows --- .../cubyz/shaders/chunks/chunk_fragment.frag | 26 ++++++++++++------- assets/cubyz/shaders/chunks/chunk_vertex.vert | 24 ++++++++--------- .../shaders/chunks/transparent_fragment.frag | 22 ++++++++++------ 3 files changed, 43 insertions(+), 29 deletions(-) diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.frag b/assets/cubyz/shaders/chunks/chunk_fragment.frag index 716a55622d..b3d3eeb673 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_fragment.frag @@ -2,14 +2,15 @@ layout(location = 0) in vec3 mvVertexPos; layout(location = 1) in vec3 direction; -layout(location = 2) in vec3 light; -layout(location = 3) in vec2 uv; -layout(location = 4) in vec3 shadowPos; -layout(location = 5) flat in vec3 normal; -layout(location = 6) flat in int textureIndex; -layout(location = 7) flat in int isBackFace; -layout(location = 8) flat in float distanceForLodCheck; -layout(location = 9) flat in int opaqueInLod; +layout(location = 2) in vec3 sunLight; +layout(location = 3) in vec3 blockLight; +layout(location = 4) in vec2 uv; +layout(location = 5) in vec3 shadowPos; +layout(location = 6) flat in vec3 normal; +layout(location = 7) flat in int textureIndex; +layout(location = 8) flat in int isBackFace; +layout(location = 9) flat in float distanceForLodCheck; +layout(location = 10) flat in int opaqueInLod; layout(location = 0) out vec4 fragColor; @@ -31,6 +32,10 @@ layout(std430, binding = 1) buffer _animatedTexture float animatedTexture[]; }; +vec3 square(vec3 x) { + return x*x; +} + float lightVariation(vec3 normal) { const vec3 directionalPart = vec3(0, contrast/2, contrast); const float baseLighting = 1 - contrast; @@ -110,8 +115,11 @@ void main() { reflectivity = reflectivity*fixedCubeMapLookup(reflect(direction, normal)).x; reflectivity = reflectivity*(1 - fresnelReflection) + fresnelReflection; + float shadow = shadowCalculation(); - vec3 pixelLight = max((1.0 - shadow*0.5)*light*normalVariation, texture(emissionSampler, textureCoords).r*4); + vec3 light = min(sqrt(square((1.0 - shadow*0.5)*sunLight) + square(blockLight)), vec3(31))/31; + + vec3 pixelLight = max(light*normalVariation, texture(emissionSampler, textureCoords).r*4); fragColor = texture(textureSampler, textureCoords)*vec4(pixelLight, 1); fragColor.rgb += reflectivity*pixelLight; diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vert b/assets/cubyz/shaders/chunks/chunk_vertex.vert index 00080cbac7..f353d1cf57 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vert +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vert @@ -2,14 +2,15 @@ layout(location = 0) out vec3 mvVertexPos; layout(location = 1) out vec3 direction; -layout(location = 2) out vec3 light; -layout(location = 3) out vec2 uv; -layout(location = 4) out vec3 shadowPos; -layout(location = 5) flat out vec3 normal; -layout(location = 6) flat out int textureIndex; -layout(location = 7) flat out int isBackFace; -layout(location = 8) flat out float distanceForLodCheck; -layout(location = 9) flat out int opaqueInLod; +layout(location = 2) out vec3 sunLight; +layout(location = 3) out vec3 blockLight; +layout(location = 4) out vec2 uv; +layout(location = 5) out vec3 shadowPos; +layout(location = 6) flat out vec3 normal; +layout(location = 7) flat out int textureIndex; +layout(location = 8) flat out int isBackFace; +layout(location = 9) flat out float distanceForLodCheck; +layout(location = 10) flat out int opaqueInLod; layout(location = 0) uniform vec3 ambientLight; layout(location = 1) uniform mat4 projectionMatrix; @@ -84,17 +85,16 @@ void main() { int textureAndQuad = faceData[faceID].textureAndQuad; uint lightIndex = chunks[chunkID].lightStart + 4*(encodedPositionAndLightIndex >> 16); uint fullLight = lightData[lightIndex + vertexID]; - vec3 sunLight = vec3( + sunLight = vec3( fullLight >> 25 & 31u, fullLight >> 20 & 31u, fullLight >> 15 & 31u - ); - vec3 blockLight = vec3( + ) * ambientLight; + blockLight = vec3( fullLight >> 10 & 31u, fullLight >> 5 & 31u, fullLight >> 0 & 31u ); - light = min(sqrt(square(sunLight*ambientLight) + square(blockLight)), vec3(31))/31; isBackFace = encodedPositionAndLightIndex>>15 & 1; textureIndex = textureAndQuad & 65535; diff --git a/assets/cubyz/shaders/chunks/transparent_fragment.frag b/assets/cubyz/shaders/chunks/transparent_fragment.frag index c628d55518..a22292d19f 100644 --- a/assets/cubyz/shaders/chunks/transparent_fragment.frag +++ b/assets/cubyz/shaders/chunks/transparent_fragment.frag @@ -2,14 +2,15 @@ layout(location = 0) in vec3 mvVertexPos; layout(location = 1) in vec3 direction; -layout(location = 2) in vec3 light; -layout(location = 3) in vec2 uv; -layout(location = 4) in vec3 shadowPos; -layout(location = 5) flat in vec3 normal; -layout(location = 6) flat in int textureIndex; -layout(location = 7) flat in int isBackFace; -layout(location = 8) flat in float distanceForLodCheck; -layout(location = 9) flat in int opaqueInLod; +layout(location = 2) in vec3 sunLight; +layout(location = 3) in vec3 blockLight; +layout(location = 4) in vec2 uv; +layout(location = 5) in vec3 shadowPos; +layout(location = 6) flat in vec3 normal; +layout(location = 7) flat in int textureIndex; +layout(location = 8) flat in int isBackFace; +layout(location = 9) flat in float distanceForLodCheck; +layout(location = 10) flat in int opaqueInLod; layout(location = 0, index = 0) out vec4 fragColor; layout(location = 0, index = 1) out vec4 blendColor; @@ -53,6 +54,10 @@ layout(std430, binding = 7) buffer _fogData FogData fogData[]; }; +vec3 square(vec3 x) { + return x*x; +} + float lightVariation(vec3 normal) { const vec3 directionalPart = vec3(0, contrast/2, contrast); const float baseLighting = 1 - contrast; @@ -140,6 +145,7 @@ void main() { float fogDistance = calculateFogDistance(dist, densityAdjustment, playerPositionFraction.z, normalize(direction).z, fogData[int(animatedTextureIndex)].fogDensity, 1e10, 1e10); float airFogDistance = calculateFogDistance(dist, densityAdjustment, playerPositionFraction.z, normalize(direction).z, fog.density, fog.fogLower - playerPositionInteger.z, fog.fogHigher - playerPositionInteger.z); vec3 fogColor = unpackColor(fogData[int(animatedTextureIndex)].fogColor); + vec3 light = min(sqrt(square(sunLight) + square(blockLight)), vec3(31))/31; vec3 pixelLight = max(light*normalVariation, texture(emissionSampler, textureCoords).r*4); vec4 textureColor = texture(textureSampler, textureCoords)*vec4(pixelLight, 1); From 7129cc37cd2e9cc9de382f718d71cf603b232f9d Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Sun, 31 May 2026 17:11:38 -0400 Subject: [PATCH 21/33] bluer shadows --- assets/cubyz/shaders/chunks/chunk_fragment.frag | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.frag b/assets/cubyz/shaders/chunks/chunk_fragment.frag index b3d3eeb673..b99d513390 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_fragment.frag @@ -115,9 +115,10 @@ void main() { reflectivity = reflectivity*fixedCubeMapLookup(reflect(direction, normal)).x; reflectivity = reflectivity*(1 - fresnelReflection) + fresnelReflection; + vec3 shadowColor = vec3(0.5, 0.5, 0.35); float shadow = shadowCalculation(); - vec3 light = min(sqrt(square((1.0 - shadow*0.5)*sunLight) + square(blockLight)), vec3(31))/31; + vec3 light = min(sqrt(square((1.0 - shadow*shadowColor)*sunLight) + square(blockLight)), vec3(31))/31; vec3 pixelLight = max(light*normalVariation, texture(emissionSampler, textureCoords).r*4); fragColor = texture(textureSampler, textureCoords)*vec4(pixelLight, 1); From 2cad062e7a6b86237d600a17eb6c9fc476bc7751 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum <43880493+IntegratedQuantum@users.noreply.github.com> Date: Sun, 31 May 2026 23:16:38 +0200 Subject: [PATCH 22/33] Do the magic ObliqumProjectoloper --- .../shaders/chunks/chunk_depth_fragment.frag | 2 - .../shaders/chunks/chunk_depth_vertex.vert | 7 +--- .../cubyz/shaders/chunks/chunk_fragment.frag | 7 +--- assets/cubyz/shaders/chunks/chunk_vertex.vert | 2 +- src/graphics.zig | 4 +- src/renderer.zig | 38 +++++++++++++++---- src/renderer/chunk_meshing.zig | 32 ++++++++-------- src/renderer/mesh_storage.zig | 2 +- 8 files changed, 54 insertions(+), 40 deletions(-) diff --git a/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag b/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag index 6cc565ff94..f6935d5f18 100644 --- a/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag @@ -1,12 +1,10 @@ #version 460 -layout(location = 0) in vec3 mvVertexPos; layout(location = 1) in vec3 direction; layout(location = 2) in vec2 uv; layout(location = 3) flat in vec3 normal; layout(location = 4) flat in int textureIndex; layout(location = 5) flat in int isBackFace; -layout(location = 6) flat in float distanceForLodCheck; layout(location = 7) flat in int opaqueInLod; layout(location = 5) uniform float lodDistance; diff --git a/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert b/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert index 8934416b5b..ee0a159656 100644 --- a/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert +++ b/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert @@ -1,12 +1,10 @@ #version 460 -layout(location = 0) out vec3 mvVertexPos; layout(location = 1) out vec3 direction; layout(location = 2) out vec2 uv; layout(location = 3) flat out vec3 normal; layout(location = 4) flat out int textureIndex; layout(location = 5) flat out int isBackFace; -layout(location = 6) flat out float distanceForLodCheck; layout(location = 7) flat out int opaqueInLod; layout(location = 1) uniform mat4 projectionMatrix; @@ -93,10 +91,7 @@ void main() { direction = position; - vec4 mvPos = viewMatrix*vec4(position, 1); - gl_Position = projectionMatrix*mvPos; - mvVertexPos = mvPos.xyz; - distanceForLodCheck = length(mvPos.xyz) + voxelSize; + gl_Position = projectionMatrix*viewMatrix*vec4(position, 1); uv = quads[quadIndex].cornerUV[vertexID]*voxelSize; opaqueInLod = quads[quadIndex].opaqueInLod; } diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.frag b/assets/cubyz/shaders/chunks/chunk_fragment.frag index b99d513390..125f4f881c 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_fragment.frag @@ -26,6 +26,7 @@ layout(location = 6) uniform float contrast; layout(location = 7) uniform float lodDistance; layout(location = 8) uniform mat4 lightProjectionMatrix; layout(location = 9) uniform mat4 lightViewMatrix; +layout(location = 42) uniform vec3 lightDir; layout(std430, binding = 1) buffer _animatedTexture { @@ -61,11 +62,6 @@ vec4 fixedCubeMapLookup(vec3 v) { // Taken from http://the-witness.net/news/2012 } float shadowCalculation() { - vec3 lightDir = normalize(vec3( - lightViewMatrix[0][2], - lightViewMatrix[1][2], - lightViewMatrix[2][2] - )); if (dot(lightDir, normal) > 0.0) return 1.0; vec3 dx = dFdx(shadowPos); vec3 dy = dFdy(shadowPos); @@ -97,6 +93,7 @@ float shadowCalculation() { projCoords = projCoords * 0.5 + 0.5; float closestDepth = texture(shadowMap, projCoords.xy).r; float currentDepth = projCoords.z; + currentDepth += 0.00018; float shadow = currentDepth > closestDepth ? 1.0 : 0.0; if(projCoords.z > 1.0) shadow = 0.0; diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vert b/assets/cubyz/shaders/chunks/chunk_vertex.vert index f353d1cf57..d0ef6142ce 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vert +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vert @@ -126,5 +126,5 @@ void main() { uv = quads[quadIndex].cornerUV[vertexID]*voxelSize; opaqueInLod = quads[quadIndex].opaqueInLod; - shadowPos = position + normal * 0.1; + shadowPos = position + normal * 0.04; } diff --git a/src/graphics.zig b/src/graphics.zig index 614575dc7d..1d6167ef46 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -2258,9 +2258,9 @@ pub fn generateBlockTexture(blockType: u16) Texture { if (block.transparent()) { c.glBlendEquation(c.GL_FUNC_ADD); c.glBlendFunc(c.GL_ONE, c.GL_SRC1_COLOR); - main.renderer.chunk_meshing.bindTransparentShaderAndUniforms(projMatrix, Mat4f.identity(), Mat4f.identity(), .{1, 1, 1}, .{x, y, z}); + main.renderer.chunk_meshing.bindTransparentShaderAndUniforms(projMatrix, Mat4f.identity(), Mat4f.identity(), .{0, 0, 0}, .{1, 1, 1}, .{x, y, z}); } else { - main.renderer.chunk_meshing.bindShaderAndUniforms(projMatrix, Mat4f.identity(), Mat4f.identity(), .{1, 1, 1}, .{x, y, z}); + main.renderer.chunk_meshing.bindShaderAndUniforms(projMatrix, Mat4f.identity(), Mat4f.identity(), .{0, 0, 0}, .{1, 1, 1}, .{x, y, z}); } c.glUniform1f(uniforms.contrast, 0.25); c.glActiveTexture(c.GL_TEXTURE0); diff --git a/src/renderer.zig b/src/renderer.zig index dcfeb42d38..b5ee778493 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -62,8 +62,8 @@ pub var activeFrameBuffer: c_uint = 0; pub const reflectionCubeMapSize = 64; var reflectionCubeMap: graphics.CubeMapTexture = undefined; -pub const shadowMapResolution = 2048; -pub const shadowMapSize = 96.0; +pub const shadowMapResolution = 2048.0; +pub const shadowMapSize = 128.0; var depthFrameBuffer: graphics.FrameBuffer = undefined; pub fn init() void { @@ -202,8 +202,30 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo const lightOffset: Vec3f = Vec3f{@floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))}; - const lightProjection: Mat4f = .orthogonal(-shadowMapSize/2, shadowMapSize/2, -shadowMapSize/2, shadowMapSize/2, -shadowMapSize, shadowMapSize); - const lightView: Mat4f = Mat4f.identity().mul(.rotationX(std.math.pi*0.8)).mul(.rotationZ(std.math.pi*0.2)).mul(.translation(lightOffset)); + const xRot = std.math.pi*0.8; + const zRot = std.math.pi*0.2; + + const lightDir = vec.rotateZ(vec.rotateX(Vec3f{0.0, 0.0, 1.0}, xRot), zRot); + const xR = lightDir[0]; + const yR = lightDir[1]; + const zR = lightDir[2]; + + //std.log.debug("{} {} {}", .{xR, yR, zR}); + + const far = shadowMapSize; + const near = -shadowMapSize; + const lightProjection = Mat4f.scale(.{2.0/shadowMapSize, 2.0/shadowMapSize, 1.0}).mul(.{ + .rows = [4]Vec4f { + Vec4f{1, 0, xR/zR, 0.0}, + Vec4f{0, 1, yR/zR, 0.0}, + Vec4f{0, 0, 1.0/(far - near), -near/(far - near)}, + Vec4f{0, 0, 0, 1}, + } + }).mul(.scale(.{1, 1, -1})); + + const lightView: Mat4f = Mat4f.identity().mul(.translation(lightOffset)); + + //std.log.debug("View matrix {}", .{lightView.rows}); game.camera.updateViewMatrix(); @@ -226,7 +248,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo blocks.meshes.blockTextureArray.bind(); gpu_performance_measuring.startQuery(.depth_framebuffer_chunk_rendering); - chunk_meshing.drawChunksIndirect(&depthChunkLists, lightProjection, lightProjection, lightView, ambientLight, playerPos, .depth); + chunk_meshing.drawChunksIndirect(&depthChunkLists, lightProjection, lightProjection, lightView, lightDir, ambientLight, playerPos, .depth); gpu_performance_measuring.stopQuery(); chunk_meshing.endRender(); @@ -248,7 +270,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo gpu_performance_measuring.stopQuery(); // Update the uniforms. The uniforms are needed to render the replacement meshes. - chunk_meshing.bindShaderAndUniforms(game.projectionMatrix, lightProjection, lightView, ambientLight, playerPos); + chunk_meshing.bindShaderAndUniforms(game.projectionMatrix, lightProjection, lightView, lightDir, ambientLight, playerPos); c.glActiveTexture(c.GL_TEXTURE0); blocks.meshes.blockTextureArray.bind(); @@ -287,7 +309,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo chunk_meshing.beginRender(); gpu_performance_measuring.startQuery(.chunk_rendering); - chunk_meshing.drawChunksIndirect(&chunkLists, game.projectionMatrix, lightProjection, lightView, ambientLight, playerPos, .regular); + chunk_meshing.drawChunksIndirect(&chunkLists, game.projectionMatrix, lightProjection, lightView, lightDir, ambientLight, playerPos, .regular); gpu_performance_measuring.stopQuery(); gpu_performance_measuring.startQuery(.entity_rendering); @@ -328,7 +350,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo } gpu_performance_measuring.stopQuery(); gpu_performance_measuring.startQuery(.transparent_rendering); - chunk_meshing.drawChunksIndirect(&chunkLists, game.projectionMatrix, lightProjection, lightView, ambientLight, playerPos, .transparent); + chunk_meshing.drawChunksIndirect(&chunkLists, game.projectionMatrix, lightProjection, lightView, lightDir, ambientLight, playerPos, .transparent); gpu_performance_measuring.stopQuery(); } diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index 40d2bacaa5..5bcd7d8865 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -47,6 +47,7 @@ const UniformStruct = struct { zFar: c_int, lightProjectionMatrix: c_int, lightViewMatrix: c_int, + lightDir: c_int, }; pub var uniforms: UniformStruct = undefined; pub var transparentUniforms: UniformStruct = undefined; @@ -126,7 +127,7 @@ pub fn init() void { &depthUniforms, graphics.VertexArray.EmptyVertex, &.{}, - .{.cullMode = .front}, + .{.cullMode = .back}, .{.depthTest = true, .depthWrite = true, .depthCompare = .lessOrEqual}, .{.attachments = &.{.noBlending}}, ); @@ -205,7 +206,7 @@ pub fn endRender() void { chunkIDBuffer.endRender(); } -fn bindCommonUniforms(locations: *UniformStruct, projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d) void { +fn bindCommonUniforms(locations: *UniformStruct, projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, lightDir: Vec3f, ambient: Vec3f, playerPos: Vec3d) void { c.glUniformMatrix4fv(locations.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projMatrix)); c.glUniform1f(locations.reflectionMapSize, renderer.reflectionCubeMapSize); @@ -226,17 +227,18 @@ fn bindCommonUniforms(locations: *UniformStruct, projMatrix: Mat4f, lightProjMat c.glUniformMatrix4fv(locations.lightProjectionMatrix, 1, c.GL_TRUE, @ptrCast(&lightProjMatrix)); c.glUniformMatrix4fv(locations.lightViewMatrix, 1, c.GL_TRUE, @ptrCast(&lightViewMatrix)); + c.glUniform3fv(locations.lightDir, 1, @ptrCast(&lightDir)); } -pub fn bindShaderAndUniforms(projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d) void { +pub fn bindShaderAndUniforms(projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, lightDir: Vec3f, ambient: Vec3f, playerPos: Vec3d) void { pipeline.bind(null); - bindCommonUniforms(&uniforms, projMatrix, lightProjMatrix, lightViewMatrix, ambient, playerPos); + bindCommonUniforms(&uniforms, projMatrix, lightProjMatrix, lightViewMatrix, lightDir, ambient, playerPos); vao.bind(); } -pub fn bindTransparentShaderAndUniforms(projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d) void { +pub fn bindTransparentShaderAndUniforms(projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, lightDir: Vec3f, ambient: Vec3f, playerPos: Vec3d) void { transparentPipeline.bind(null); c.glUniform3fv(transparentUniforms.@"fog.color", 1, @ptrCast(&game.fog.fogColor)); @@ -244,12 +246,12 @@ pub fn bindTransparentShaderAndUniforms(projMatrix: Mat4f, lightProjMatrix: Mat4 c.glUniform1f(transparentUniforms.@"fog.fogLower", game.fog.fogLower); c.glUniform1f(transparentUniforms.@"fog.fogHigher", game.fog.fogHigher); - bindCommonUniforms(&transparentUniforms, projMatrix, lightProjMatrix, lightViewMatrix, ambient, playerPos); + bindCommonUniforms(&transparentUniforms, projMatrix, lightProjMatrix, lightViewMatrix, lightDir, ambient, playerPos); vao.bind(); } -pub fn bindDepthShaderAndUniforms(projMatrix: Mat4f, viewMatrix: Mat4f, cameraPos: Vec3d) void { +pub fn bindDepthShaderAndUniforms(projMatrix: Mat4f, viewMatrix: Mat4f, _: Vec3f, cameraPos: Vec3d) void { depthPipeline.bind(null); c.glUniformMatrix4fv(depthUniforms.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projMatrix)); @@ -278,15 +280,15 @@ pub const DrawMode = enum { depth, }; -pub fn drawChunksIndirect(chunkIds: *const [main.settings.highestSupportedLod + 1]main.ListManaged(u32), projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d, mode: DrawMode) void { +pub fn drawChunksIndirect(chunkIds: *const [main.settings.highestSupportedLod + 1]main.ListManaged(u32), projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, lightDir: Vec3f, ambient: Vec3f, playerPos: Vec3d, mode: DrawMode) void { for (0..chunkIds.len) |i| { const lod = if (mode == .transparent) main.settings.highestSupportedLod - i else i; bindBuffers(lod); - drawChunksOfLod(chunkIds[lod].items, projMatrix, lightProjMatrix, lightViewMatrix, ambient, playerPos, mode); + drawChunksOfLod(chunkIds[lod].items, projMatrix, lightProjMatrix, lightViewMatrix, lightDir, ambient, playerPos, mode); } } -fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d, mode: DrawMode) void { +fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, lightDir: Vec3f, ambient: Vec3f, playerPos: Vec3d, mode: DrawMode) void { if (chunkIDs.len == 0) return; const drawCallsEstimate: u31 = @intCast(if (mode == .transparent) chunkIDs.len else chunkIDs.len*8); var chunkIDAllocation: main.graphics.SubAllocation = .{.start = 0, .len = 0}; @@ -308,9 +310,9 @@ fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, lightProjMatrix: Ma c.glMemoryBarrier(c.GL_SHADER_STORAGE_BARRIER_BIT | c.GL_COMMAND_BARRIER_BIT); if (mode == .depth) { - bindDepthShaderAndUniforms(lightProjMatrix, lightViewMatrix, playerPos); + bindDepthShaderAndUniforms(lightProjMatrix, lightViewMatrix, lightDir, playerPos); } else { - bindShaderAndUniforms(projMatrix, lightProjMatrix, lightViewMatrix, ambient, playerPos); + bindShaderAndUniforms(projMatrix, lightProjMatrix, lightViewMatrix, lightDir, ambient, playerPos); } c.glBindBuffer(c.GL_DRAW_INDIRECT_BUFFER, commandBuffer.ssbo.bufferID); c.glMultiDrawElementsIndirect(c.GL_TRIANGLES, c.GL_UNSIGNED_INT, @ptrFromInt(allocation.start*@sizeOf(IndirectData)), drawCallsEstimate, 0); @@ -334,9 +336,9 @@ fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, lightProjMatrix: Ma c.glMemoryBarrier(c.GL_SHADER_STORAGE_BARRIER_BIT | c.GL_COMMAND_BARRIER_BIT); switch (mode) { - .regular => bindShaderAndUniforms(projMatrix, lightProjMatrix, lightViewMatrix, ambient, playerPos), - .transparent => bindTransparentShaderAndUniforms(projMatrix, lightProjMatrix, lightViewMatrix, ambient, playerPos), - .depth => bindDepthShaderAndUniforms(lightProjMatrix, lightViewMatrix, playerPos), + .regular => bindShaderAndUniforms(projMatrix, lightProjMatrix, lightViewMatrix, lightDir, ambient, playerPos), + .transparent => bindTransparentShaderAndUniforms(projMatrix, lightProjMatrix, lightViewMatrix, lightDir, ambient, playerPos), + .depth => bindDepthShaderAndUniforms(lightProjMatrix, lightViewMatrix, lightDir, playerPos), } c.glBindBuffer(c.GL_DRAW_INDIRECT_BUFFER, commandBuffer.ssbo.bufferID); c.glMultiDrawElementsIndirect(c.GL_TRIANGLES, c.GL_UNSIGNED_INT, @ptrFromInt(allocation.start*@sizeOf(IndirectData)), drawCallsEstimate, 0); diff --git a/src/renderer/mesh_storage.zig b/src/renderer/mesh_storage.zig index 5684cc2047..5759a07106 100644 --- a/src/renderer/mesh_storage.zig +++ b/src/renderer/mesh_storage.zig @@ -601,7 +601,6 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: ?*c while (searchList.popFront()) |node| { std.debug.assert(node.finishedMeshing); std.debug.assert(node.active); - if (!node.active) continue; node.active = false; const pos = node.pos; @@ -609,6 +608,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: ?*c const relPos: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{pos.wx, pos.wy, pos.wz})) - playerPos; const relPosFloat: Vec3f = @floatCast(relPos); + // TODO: This seems like a good place for a comment, could someone please add one? if (pos.voxelSize == @as(i32, 1) << settings.highestLod) { for (chunk.Neighbor.iterable) |neighbor| { const component = neighbor.extractDirectionComponent(relPosFloat); From dbb5af14d2cff5f4e4bd8fddca6661d67f4eda9e Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Sun, 31 May 2026 17:28:20 -0400 Subject: [PATCH 23/33] tweaks (waow)! --- assets/cubyz/shaders/chunks/chunk_fragment.frag | 2 +- src/renderer.zig | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.frag b/assets/cubyz/shaders/chunks/chunk_fragment.frag index 125f4f881c..a32b883943 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_fragment.frag @@ -89,7 +89,7 @@ float shadowCalculation() { lightProjectionMatrix * lightViewMatrix * vec4(snappedShadowPos, 1.0); - vec3 projCoords = lightPos.xyz / lightPos.w; + vec3 projCoords = lightPos.xyz; projCoords = projCoords * 0.5 + 0.5; float closestDepth = texture(shadowMap, projCoords.xy).r; float currentDepth = projCoords.z; diff --git a/src/renderer.zig b/src/renderer.zig index b5ee778493..aecd34ea7c 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -63,7 +63,6 @@ pub const reflectionCubeMapSize = 64; var reflectionCubeMap: graphics.CubeMapTexture = undefined; pub const shadowMapResolution = 2048.0; -pub const shadowMapSize = 128.0; var depthFrameBuffer: graphics.FrameBuffer = undefined; pub fn init() void { @@ -203,14 +202,14 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo const lightOffset: Vec3f = Vec3f{@floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))}; const xRot = std.math.pi*0.8; - const zRot = std.math.pi*0.2; + const zRot = std.math.pi*0.1; const lightDir = vec.rotateZ(vec.rotateX(Vec3f{0.0, 0.0, 1.0}, xRot), zRot); const xR = lightDir[0]; const yR = lightDir[1]; const zR = lightDir[2]; - //std.log.debug("{} {} {}", .{xR, yR, zR}); + const shadowMapSize = shadowMapResolution / 16.0; const far = shadowMapSize; const near = -shadowMapSize; @@ -225,8 +224,6 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo const lightView: Mat4f = Mat4f.identity().mul(.translation(lightOffset)); - //std.log.debug("View matrix {}", .{lightView.rows}); - game.camera.updateViewMatrix(); chunk_meshing.quadsDrawn = 0; From 7e2939c8ca83c981868a762dfd19b53fd5728a34 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Sun, 31 May 2026 17:28:48 -0400 Subject: [PATCH 24/33] Formatting --- src/renderer.zig | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/renderer.zig b/src/renderer.zig index aecd34ea7c..da7bbfa084 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -209,18 +209,16 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo const yR = lightDir[1]; const zR = lightDir[2]; - const shadowMapSize = shadowMapResolution / 16.0; + const shadowMapSize = shadowMapResolution/16.0; const far = shadowMapSize; const near = -shadowMapSize; - const lightProjection = Mat4f.scale(.{2.0/shadowMapSize, 2.0/shadowMapSize, 1.0}).mul(.{ - .rows = [4]Vec4f { - Vec4f{1, 0, xR/zR, 0.0}, - Vec4f{0, 1, yR/zR, 0.0}, - Vec4f{0, 0, 1.0/(far - near), -near/(far - near)}, - Vec4f{0, 0, 0, 1}, - } - }).mul(.scale(.{1, 1, -1})); + const lightProjection = Mat4f.scale(.{2.0/shadowMapSize, 2.0/shadowMapSize, 1.0}).mul(.{.rows = [4]Vec4f{ + Vec4f{1, 0, xR/zR, 0.0}, + Vec4f{0, 1, yR/zR, 0.0}, + Vec4f{0, 0, 1.0/(far - near), -near/(far - near)}, + Vec4f{0, 0, 0, 1}, + }}).mul(.scale(.{1, 1, -1})); const lightView: Mat4f = Mat4f.identity().mul(.translation(lightOffset)); From a8c2bae8f2cfd4a96f1220dd0a0e55e765eb8b77 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Sun, 31 May 2026 17:31:19 -0400 Subject: [PATCH 25/33] Formatting++! --- assets/cubyz/shaders/chunks/chunk_fragment.frag | 5 +++-- assets/cubyz/shaders/chunks/fillIndirectBuffer.comp | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.frag b/assets/cubyz/shaders/chunks/chunk_fragment.frag index a32b883943..15410cd7c4 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_fragment.frag @@ -95,8 +95,9 @@ float shadowCalculation() { float currentDepth = projCoords.z; currentDepth += 0.00018; float shadow = currentDepth > closestDepth ? 1.0 : 0.0; - if(projCoords.z > 1.0) - shadow = 0.0; + if(projCoords.z > 1.0) { + shadow = 0.0; + } return shadow; } diff --git a/assets/cubyz/shaders/chunks/fillIndirectBuffer.comp b/assets/cubyz/shaders/chunks/fillIndirectBuffer.comp index 7fdf799b4b..9191046317 100644 --- a/assets/cubyz/shaders/chunks/fillIndirectBuffer.comp +++ b/assets/cubyz/shaders/chunks/fillIndirectBuffer.comp @@ -107,13 +107,13 @@ void main() { if(isDepth) { oldVisibilityState = chunks[chunkID].oldVisibilityStateDepth; } else { - oldVisibilityState = chunks[chunkID].oldVisibilityState; + oldVisibilityState = chunks[chunkID].oldVisibilityState; } uint visibilityState; if(isDepth) { visibilityState = chunks[chunkID].visibilityStateDepth; } else { - visibilityState = chunks[chunkID].visibilityState; + visibilityState = chunks[chunkID].visibilityState; } if((onlyDrawPreviouslyInvisible && oldVisibilityState == 0 && visibilityState != 0) || (oldVisibilityState != 0 && !onlyDrawPreviouslyInvisible)) { for(int i = 0; i < 14; i++) { From b107904b760f7d2556f8e9829be17f7a56e8ff19 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Sun, 31 May 2026 17:39:05 -0400 Subject: [PATCH 26/33] Remove unused mat4f function --- src/vec.zig | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/vec.zig b/src/vec.zig index c39d6fd72e..9ed5344b0f 100644 --- a/src/vec.zig +++ b/src/vec.zig @@ -274,17 +274,6 @@ pub const Mat4f = struct { // MARK: Mat4f Vec4f{0, 1, 0, 0}, }, }; - } - - pub fn orthogonal(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) Mat4f { - return (Mat4f{ - .rows = [4]Vec4f { - Vec4f{2/(right - left), 0, 0, 0}, - Vec4f{0, 2/(top - bottom), 0, 0}, - Vec4f{0, 0, 2/(far - near), 0}, - Vec4f{-(right + left)/(right - left), -(top + bottom)/(top - bottom), -(far + near)/(far - near), 1}, - } - }).transpose(); } // zig fmt: on pub fn transpose(self: Mat4f) Mat4f { From 21ee80ae67a9b1e2ca10a54088d47c36ef92da25 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Sun, 31 May 2026 17:37:26 -0400 Subject: [PATCH 27/33] Fix compilation when merged with master --- src/renderer.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer.zig b/src/renderer.zig index da7bbfa084..fe82aafce5 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -231,7 +231,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo gpu_performance_measuring.startQuery(.depth_framebuffer_chunk_rendering_preparation); chunk_meshing.beginRender(); - var depthChunkLists: [main.settings.highestSupportedLod + 1]main.List(u32) = @splat(main.List(u32).init(main.stackAllocator)); + var depthChunkLists: [main.settings.highestSupportedLod + 1]main.ListManaged(u32) = @splat(main.ListManaged(u32).init(main.stackAllocator)); defer for (depthChunkLists) |list| list.deinit(); for (depthMeshes) |mesh| { mesh.prepareRendering(&depthChunkLists); @@ -295,7 +295,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo chunk_meshing.beginRender(); - var chunkLists: [main.settings.highestSupportedLod + 1]main.List(u32) = @splat(main.List(u32).init(main.stackAllocator)); + var chunkLists: [main.settings.highestSupportedLod + 1]main.ListManaged(u32) = @splat(main.ListManaged(u32).init(main.stackAllocator)); defer for (chunkLists) |list| list.deinit(); for (meshes) |mesh| { mesh.prepareRendering(&chunkLists); From 4344f3149a10dbcd8534eecc647f499e00814d0c Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Thu, 16 Jul 2026 12:45:48 -0400 Subject: [PATCH 28/33] Fix compilation --- src/renderer/mesh_storage.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/mesh_storage.zig b/src/renderer/mesh_storage.zig index aac8491421..3034a945a8 100644 --- a/src/renderer/mesh_storage.zig +++ b/src/renderer/mesh_storage.zig @@ -650,7 +650,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: ?*c if (dz == 1) nextPos.wz ^= lowerLodBit; const node2 = getNodePointer(nextPos); const relNextPos: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{ nextPos.wx, nextPos.wy, nextPos.wz })) - playerPos; - if (!frustum.testAAB(@floatCast(relNextPos), @splat(@floatFromInt(chunk.chunkSize * nextPos.voxelSize)))) + if (frustum != null and !frustum.?.testAAB(@floatCast(relNextPos), @splat(@floatFromInt(chunk.chunkSize * nextPos.voxelSize)))) continue; std.debug.assert(node2.finishedMeshing); node2.active = true; From 038cb4414f9cfa41eca677b63c25dbae9535bc12 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Thu, 16 Jul 2026 13:00:56 -0400 Subject: [PATCH 29/33] Grr stupid formatting --- src/renderer/mesh_storage.zig | 1522 ++++++++++++++++----------------- 1 file changed, 761 insertions(+), 761 deletions(-) diff --git a/src/renderer/mesh_storage.zig b/src/renderer/mesh_storage.zig index 3034a945a8..09a4e7e4c7 100644 --- a/src/renderer/mesh_storage.zig +++ b/src/renderer/mesh_storage.zig @@ -22,18 +22,18 @@ const chunk_meshing = @import("chunk_meshing.zig"); const ChunkMesh = chunk_meshing.ChunkMesh; const ChunkMeshNode = struct { - mesh: Atomic(?*chunk_meshing.ChunkMesh) = .init(null), - active: bool = false, - rendered: bool = false, - finishedMeshing: bool = false, // Must be synced with mesh.finishedMeshing - finishedMeshingHigherResolution: u8 = 0, // Must be synced with finishedMeshing of the 8 higher resolution chunks. - pos: chunk.ChunkPosition = undefined, - isNeighborLod: [6]bool = @splat(false), // Must be synced with mesh.isNeighborLod + mesh: Atomic(?*chunk_meshing.ChunkMesh) = .init(null), + active: bool = false, + rendered: bool = false, + finishedMeshing: bool = false, // Must be synced with mesh.finishedMeshing + finishedMeshingHigherResolution: u8 = 0, // Must be synced with finishedMeshing of the 8 higher resolution chunks. + pos: chunk.ChunkPosition = undefined, + isNeighborLod: [6]bool = @splat(false), // Must be synced with mesh.isNeighborLod }; const storageSize = 64; const storageMask = storageSize - 1; -var storageLists: [settings.highestSupportedLod + 1]*[storageSize * storageSize * storageSize]ChunkMeshNode = undefined; -var mapStorageLists: [settings.highestSupportedLod + 1]*[storageSize * storageSize]Atomic(?*LightMap.LightMapFragment) = undefined; +var storageLists: [settings.highestSupportedLod + 1]*[storageSize*storageSize*storageSize]ChunkMeshNode = undefined; +var mapStorageLists: [settings.highestSupportedLod + 1]*[storageSize*storageSize]Atomic(?*LightMap.LightMapFragment) = undefined; var meshList: main.List(*chunk_meshing.ChunkMesh) = .empty; var priorityMeshUpdateList: main.utils.ConcurrentQueue(chunk.ChunkPosition) = undefined; pub var updatableList: main.List(chunk.ChunkPosition) = .empty; @@ -45,853 +45,853 @@ var lastRD: u16 = 0; var mutex: main.utils.Mutex = .{}; pub const BlockUpdate = struct { - pos: Vec3i, - newBlock: blocks.Block, - blockEntityData: []const u8, - - pub fn init(pos: Vec3i, block: blocks.Block, blockEntityData: []const u8) BlockUpdate { - return .{ .pos = pos, .newBlock = block, .blockEntityData = blockEntityData }; - } - - pub fn initManaged(allocator: main.heap.NeverFailingAllocator, template: BlockUpdate) BlockUpdate { - return .{ - .pos = template.pos, - .newBlock = template.newBlock, - .blockEntityData = allocator.dupe(u8, template.blockEntityData), - }; - } - - pub fn deinitManaged(self: BlockUpdate, allocator: main.heap.NeverFailingAllocator) void { - allocator.free(self.blockEntityData); - } + pos: Vec3i, + newBlock: blocks.Block, + blockEntityData: []const u8, + + pub fn init(pos: Vec3i, block: blocks.Block, blockEntityData: []const u8) BlockUpdate { + return .{.pos = pos, .newBlock = block, .blockEntityData = blockEntityData}; + } + + pub fn initManaged(allocator: main.heap.NeverFailingAllocator, template: BlockUpdate) BlockUpdate { + return .{ + .pos = template.pos, + .newBlock = template.newBlock, + .blockEntityData = allocator.dupe(u8, template.blockEntityData), + }; + } + + pub fn deinitManaged(self: BlockUpdate, allocator: main.heap.NeverFailingAllocator) void { + allocator.free(self.blockEntityData); + } }; pub var meshMemoryPool: main.heap.MemoryPool(chunk_meshing.ChunkMesh) = .init(main.globalArena); pub fn init() void { // MARK: init() - lastRD = 0; - for (&storageLists) |*storageList| { - storageList.* = main.globalAllocator.create([storageSize * storageSize * storageSize]ChunkMeshNode); - for (storageList.*) |*val| { - val.* = .{}; - } - } - for (&mapStorageLists) |*mapStorageList| { - mapStorageList.* = main.globalAllocator.create([storageSize * storageSize]Atomic(?*LightMap.LightMapFragment)); - @memset(mapStorageList.*, .init(null)); - } - priorityMeshUpdateList = .init(main.globalAllocator, 16); - mapUpdatableList = .init(main.globalAllocator, 16); + lastRD = 0; + for (&storageLists) |*storageList| { + storageList.* = main.globalAllocator.create([storageSize*storageSize*storageSize]ChunkMeshNode); + for (storageList.*) |*val| { + val.* = .{}; + } + } + for (&mapStorageLists) |*mapStorageList| { + mapStorageList.* = main.globalAllocator.create([storageSize*storageSize]Atomic(?*LightMap.LightMapFragment)); + @memset(mapStorageList.*, .init(null)); + } + priorityMeshUpdateList = .init(main.globalAllocator, 16); + mapUpdatableList = .init(main.globalAllocator, 16); } pub fn deinit() void { - const olderPx = lastPx; - const olderPy = lastPy; - const olderPz = lastPz; - const olderRD = lastRD; - lastPx = 0; - lastPy = 0; - lastPz = 0; - lastRD = 0; - freeOldMeshes(olderPx, olderPy, olderPz, olderRD); - for (storageLists) |storageList| { - main.globalAllocator.destroy(storageList); - } - for (mapStorageLists) |mapStorageList| { - main.globalAllocator.destroy(mapStorageList); - } - - updatableList.clearAndFree(main.globalAllocator); - while (mapUpdatableList.popFront()) |map| { - map.deferredDeinit(); - } - mapUpdatableList.deinit(); - priorityMeshUpdateList.deinit(); - meshList.clearAndFree(main.globalAllocator); - main.heap.GarbageCollection.waitForFreeCompletion(); + const olderPx = lastPx; + const olderPy = lastPy; + const olderPz = lastPz; + const olderRD = lastRD; + lastPx = 0; + lastPy = 0; + lastPz = 0; + lastRD = 0; + freeOldMeshes(olderPx, olderPy, olderPz, olderRD); + for (storageLists) |storageList| { + main.globalAllocator.destroy(storageList); + } + for (mapStorageLists) |mapStorageList| { + main.globalAllocator.destroy(mapStorageList); + } + + updatableList.clearAndFree(main.globalAllocator); + while (mapUpdatableList.popFront()) |map| { + map.deferredDeinit(); + } + mapUpdatableList.deinit(); + priorityMeshUpdateList.deinit(); + meshList.clearAndFree(main.globalAllocator); + main.heap.GarbageCollection.waitForFreeCompletion(); } // MARK: getters fn getNodePointer(pos: chunk.ChunkPosition) *ChunkMeshNode { - const lod = std.math.log2_int(u31, pos.voxelSize); - var xIndex = pos.wx >> lod + chunk.chunkShift; - var yIndex = pos.wy >> lod + chunk.chunkShift; - var zIndex = pos.wz >> lod + chunk.chunkShift; - xIndex &= storageMask; - yIndex &= storageMask; - zIndex &= storageMask; - const index = (xIndex * storageSize + yIndex) * storageSize + zIndex; - return &storageLists[lod][@intCast(index)]; + const lod = std.math.log2_int(u31, pos.voxelSize); + var xIndex = pos.wx >> lod + chunk.chunkShift; + var yIndex = pos.wy >> lod + chunk.chunkShift; + var zIndex = pos.wz >> lod + chunk.chunkShift; + xIndex &= storageMask; + yIndex &= storageMask; + zIndex &= storageMask; + const index = (xIndex*storageSize + yIndex)*storageSize + zIndex; + return &storageLists[lod][@intCast(index)]; } fn finishedMeshingMask(x: bool, y: bool, z: bool) u8 { - return @as(u8, 1) << (@as(u3, @intFromBool(x)) * 4 + @as(u3, @intFromBool(y)) * 2 + @as(u3, @intFromBool(z))); + return @as(u8, 1) << (@as(u3, @intFromBool(x))*4 + @as(u3, @intFromBool(y))*2 + @as(u3, @intFromBool(z))); } fn updateHigherLodNodeFinishedMeshing(pos_: chunk.ChunkPosition, finishedMeshing: bool) void { - const lod = std.math.log2_int(u31, pos_.voxelSize); - if (lod == settings.highestLod) return; - var pos = pos_; - pos.wx &= ~@as(i32, pos.voxelSize * chunk.chunkSize); - pos.wy &= ~@as(i32, pos.voxelSize * chunk.chunkSize); - pos.wz &= ~@as(i32, pos.voxelSize * chunk.chunkSize); - pos.voxelSize *= 2; - const mask = finishedMeshingMask(pos.wx != pos_.wx, pos.wy != pos_.wy, pos.wz != pos_.wz); - const node = getNodePointer(pos); - if (finishedMeshing) { - node.finishedMeshingHigherResolution |= mask; - } else { - node.finishedMeshingHigherResolution &= ~mask; - } + const lod = std.math.log2_int(u31, pos_.voxelSize); + if (lod == settings.highestLod) return; + var pos = pos_; + pos.wx &= ~@as(i32, pos.voxelSize*chunk.chunkSize); + pos.wy &= ~@as(i32, pos.voxelSize*chunk.chunkSize); + pos.wz &= ~@as(i32, pos.voxelSize*chunk.chunkSize); + pos.voxelSize *= 2; + const mask = finishedMeshingMask(pos.wx != pos_.wx, pos.wy != pos_.wy, pos.wz != pos_.wz); + const node = getNodePointer(pos); + if (finishedMeshing) { + node.finishedMeshingHigherResolution |= mask; + } else { + node.finishedMeshingHigherResolution &= ~mask; + } } fn getMapPiecePointer(x: i32, y: i32, voxelSize: u31) *Atomic(?*LightMap.LightMapFragment) { - const lod = std.math.log2_int(u31, voxelSize); - var xIndex = x >> lod + LightMap.LightMapFragment.mapShift; - var yIndex = y >> lod + LightMap.LightMapFragment.mapShift; - xIndex &= storageMask; - yIndex &= storageMask; - const index = xIndex * storageSize + yIndex; - return &mapStorageLists[lod][@intCast(index)]; + const lod = std.math.log2_int(u31, voxelSize); + var xIndex = x >> lod + LightMap.LightMapFragment.mapShift; + var yIndex = y >> lod + LightMap.LightMapFragment.mapShift; + xIndex &= storageMask; + yIndex &= storageMask; + const index = xIndex*storageSize + yIndex; + return &mapStorageLists[lod][@intCast(index)]; } pub fn getLightMapPiece(x: i32, y: i32, voxelSize: u31) ?*LightMap.LightMapFragment { - return getMapPiecePointer(x, y, voxelSize).load(.acquire); + return getMapPiecePointer(x, y, voxelSize).load(.acquire); } pub fn getBlockFromRenderThread(x: i32, y: i32, z: i32) ?blocks.Block { - const node = getNodePointer(.{ .wx = x, .wy = y, .wz = z, .voxelSize = 1 }); - const mesh = node.mesh.load(.acquire) orelse return null; - const block = mesh.chunk.getBlock(x & chunk.chunkMask, y & chunk.chunkMask, z & chunk.chunkMask); - return block; + const node = getNodePointer(.{.wx = x, .wy = y, .wz = z, .voxelSize = 1}); + const mesh = node.mesh.load(.acquire) orelse return null; + const block = mesh.chunk.getBlock(x & chunk.chunkMask, y & chunk.chunkMask, z & chunk.chunkMask); + return block; } pub fn getLight(wx: i32, wy: i32, wz: i32) ?[6]u8 { - const node = getNodePointer(.{ .wx = wx, .wy = wy, .wz = wz, .voxelSize = 1 }); - const mesh = node.mesh.load(.acquire) orelse return null; - const pos: chunk.BlockPos = .fromWorldCoords(wx, wy, wz); - return mesh.lightingData[1].getValue(pos).toArray() ++ mesh.lightingData[0].getValue(pos).toArray(); + const node = getNodePointer(.{.wx = wx, .wy = wy, .wz = wz, .voxelSize = 1}); + const mesh = node.mesh.load(.acquire) orelse return null; + const pos: chunk.BlockPos = .fromWorldCoords(wx, wy, wz); + return mesh.lightingData[1].getValue(pos).toArray() ++ mesh.lightingData[0].getValue(pos).toArray(); } pub fn getBlockFromAnyLodFromRenderThread(x: i32, y: i32, z: i32) blocks.Block { - var lod: u5 = 0; - while (lod <= settings.highestLod) : (lod += 1) { - const node = getNodePointer(.{ .wx = x, .wy = y, .wz = z, .voxelSize = @as(u31, 1) << lod }); - const mesh = node.mesh.load(.acquire) orelse continue; - const block = mesh.chunk.getBlock(x & chunk.chunkMask << lod, y & chunk.chunkMask << lod, z & chunk.chunkMask << lod); - return block; - } - return blocks.Block{ .typ = 0, .data = 0 }; + var lod: u5 = 0; + while (lod <= settings.highestLod) : (lod += 1) { + const node = getNodePointer(.{.wx = x, .wy = y, .wz = z, .voxelSize = @as(u31, 1) << lod}); + const mesh = node.mesh.load(.acquire) orelse continue; + const block = mesh.chunk.getBlock(x & chunk.chunkMask << lod, y & chunk.chunkMask << lod, z & chunk.chunkMask << lod); + return block; + } + return blocks.Block{.typ = 0, .data = 0}; } pub fn getMesh(pos: chunk.ChunkPosition) ?*chunk_meshing.ChunkMesh { - const lod = std.math.log2_int(u31, pos.voxelSize); - const mask = ~((@as(i32, 1) << lod + chunk.chunkShift) - 1); - const node = getNodePointer(pos); - const mesh = node.mesh.load(.acquire) orelse return null; - if (pos.wx & mask != mesh.pos.wx or pos.wy & mask != mesh.pos.wy or pos.wz & mask != mesh.pos.wz) { - return null; - } - return mesh; + const lod = std.math.log2_int(u31, pos.voxelSize); + const mask = ~((@as(i32, 1) << lod + chunk.chunkShift) - 1); + const node = getNodePointer(pos); + const mesh = node.mesh.load(.acquire) orelse return null; + if (pos.wx & mask != mesh.pos.wx or pos.wy & mask != mesh.pos.wy or pos.wz & mask != mesh.pos.wz) { + return null; + } + return mesh; } pub fn getMeshFromAnyLod(wx: i32, wy: i32, wz: i32, voxelSize: u31) ?*chunk_meshing.ChunkMesh { - var lod: u5 = @ctz(voxelSize); - while (lod < settings.highestLod) : (lod += 1) { - const mesh = getMesh(.{ .wx = wx & ~chunk.chunkMask << lod, .wy = wy & ~chunk.chunkMask << lod, .wz = wz & ~chunk.chunkMask << lod, .voxelSize = @as(u31, 1) << lod }); - return mesh orelse continue; - } - return null; + var lod: u5 = @ctz(voxelSize); + while (lod < settings.highestLod) : (lod += 1) { + const mesh = getMesh(.{.wx = wx & ~chunk.chunkMask << lod, .wy = wy & ~chunk.chunkMask << lod, .wz = wz & ~chunk.chunkMask << lod, .voxelSize = @as(u31, 1) << lod}); + return mesh orelse continue; + } + return null; } pub fn getNeighbor(_pos: chunk.ChunkPosition, resolution: u31, neighbor: chunk.Neighbor) ?*chunk_meshing.ChunkMesh { - var pos = _pos; - pos.wx +%= pos.voxelSize * chunk.chunkSize * neighbor.relX(); - pos.wy +%= pos.voxelSize * chunk.chunkSize * neighbor.relY(); - pos.wz +%= pos.voxelSize * chunk.chunkSize * neighbor.relZ(); - pos.voxelSize = resolution; - return getMesh(pos); + var pos = _pos; + pos.wx +%= pos.voxelSize*chunk.chunkSize*neighbor.relX(); + pos.wy +%= pos.voxelSize*chunk.chunkSize*neighbor.relY(); + pos.wz +%= pos.voxelSize*chunk.chunkSize*neighbor.relZ(); + pos.voxelSize = resolution; + return getMesh(pos); } fn reduceRenderDistance(fullRenderDistance: i64, reduction: i64) i32 { - const reducedRenderDistanceSquare: f64 = @floatFromInt(fullRenderDistance * fullRenderDistance - reduction * reduction); - const reducedRenderDistance: i32 = @ceil(@as(f64, @sqrt(@max(0, reducedRenderDistanceSquare)))); - return reducedRenderDistance; + const reducedRenderDistanceSquare: f64 = @floatFromInt(fullRenderDistance*fullRenderDistance - reduction*reduction); + const reducedRenderDistance: i32 = @ceil(@as(f64, @sqrt(@max(0, reducedRenderDistanceSquare)))); + return reducedRenderDistance; } fn isInRenderDistance(pos: chunk.ChunkPosition) bool { // MARK: isInRenderDistance() - const maxRenderDistance = lastRD * chunk.chunkSize * pos.voxelSize; - const size: u31 = chunk.chunkSize * pos.voxelSize; - const mask: i32 = size - 1; - const invMask: i32 = ~mask; - - const minX = lastPx -% maxRenderDistance & invMask; - const maxX = lastPx +% maxRenderDistance +% size & invMask; - if (pos.wx -% minX < 0) return false; - if (pos.wx -% maxX >= 0) return false; - var deltaX: i64 = @abs(pos.wx +% size / 2 -% lastPx); - deltaX = @max(0, deltaX - size / 2); - - const maxYRenderDistance: i32 = reduceRenderDistance(maxRenderDistance, deltaX); - const minY = lastPy -% maxYRenderDistance & invMask; - const maxY = lastPy +% maxYRenderDistance +% size & invMask; - if (pos.wy -% minY < 0) return false; - if (pos.wy -% maxY >= 0) return false; - var deltaY: i64 = @abs(pos.wy +% size / 2 -% lastPy); - deltaY = @max(0, deltaY - size / 2); - - const maxZRenderDistance: i32 = reduceRenderDistance(maxYRenderDistance, deltaY); - if (maxZRenderDistance == 0) return false; - const minZ = lastPz -% maxZRenderDistance & invMask; - const maxZ = lastPz +% maxZRenderDistance +% size & invMask; - if (pos.wz -% minZ < 0) return false; - if (pos.wz -% maxZ >= 0) return false; - return true; + const maxRenderDistance = lastRD*chunk.chunkSize*pos.voxelSize; + const size: u31 = chunk.chunkSize*pos.voxelSize; + const mask: i32 = size - 1; + const invMask: i32 = ~mask; + + const minX = lastPx -% maxRenderDistance & invMask; + const maxX = lastPx +% maxRenderDistance +% size & invMask; + if (pos.wx -% minX < 0) return false; + if (pos.wx -% maxX >= 0) return false; + var deltaX: i64 = @abs(pos.wx +% size/2 -% lastPx); + deltaX = @max(0, deltaX - size/2); + + const maxYRenderDistance: i32 = reduceRenderDistance(maxRenderDistance, deltaX); + const minY = lastPy -% maxYRenderDistance & invMask; + const maxY = lastPy +% maxYRenderDistance +% size & invMask; + if (pos.wy -% minY < 0) return false; + if (pos.wy -% maxY >= 0) return false; + var deltaY: i64 = @abs(pos.wy +% size/2 -% lastPy); + deltaY = @max(0, deltaY - size/2); + + const maxZRenderDistance: i32 = reduceRenderDistance(maxYRenderDistance, deltaY); + if (maxZRenderDistance == 0) return false; + const minZ = lastPz -% maxZRenderDistance & invMask; + const maxZ = lastPz +% maxZRenderDistance +% size & invMask; + if (pos.wz -% minZ < 0) return false; + if (pos.wz -% maxZ >= 0) return false; + return true; } fn isMapInRenderDistance(pos: LightMap.MapFragmentPosition) bool { - const maxRenderDistance = lastRD * chunk.chunkSize * pos.voxelSize; - const size: u31 = @as(u31, LightMap.LightMapFragment.mapSize) * pos.voxelSize; - const mask: i32 = size - 1; - const invMask: i32 = ~mask; - - const minX = lastPx -% maxRenderDistance & invMask; - const maxX = lastPx +% maxRenderDistance +% size & invMask; - if (pos.wx -% minX < 0) return false; - if (pos.wx -% maxX >= 0) return false; - var deltaX: i64 = @abs(pos.wx +% size / 2 -% lastPx); - deltaX = @max(0, deltaX - size / 2); - - const maxYRenderDistance: i32 = reduceRenderDistance(maxRenderDistance, deltaX); - if (maxYRenderDistance == 0) return false; - const minY = lastPy -% maxYRenderDistance & invMask; - const maxY = lastPy +% maxYRenderDistance +% size & invMask; - if (pos.wy -% minY < 0) return false; - if (pos.wy -% maxY >= 0) return false; - return true; + const maxRenderDistance = lastRD*chunk.chunkSize*pos.voxelSize; + const size: u31 = @as(u31, LightMap.LightMapFragment.mapSize)*pos.voxelSize; + const mask: i32 = size - 1; + const invMask: i32 = ~mask; + + const minX = lastPx -% maxRenderDistance & invMask; + const maxX = lastPx +% maxRenderDistance +% size & invMask; + if (pos.wx -% minX < 0) return false; + if (pos.wx -% maxX >= 0) return false; + var deltaX: i64 = @abs(pos.wx +% size/2 -% lastPx); + deltaX = @max(0, deltaX - size/2); + + const maxYRenderDistance: i32 = reduceRenderDistance(maxRenderDistance, deltaX); + if (maxYRenderDistance == 0) return false; + const minY = lastPy -% maxYRenderDistance & invMask; + const maxY = lastPy +% maxYRenderDistance +% size & invMask; + if (pos.wy -% minY < 0) return false; + if (pos.wy -% maxY >= 0) return false; + return true; } fn freeOldMeshes(olderPx: i32, olderPy: i32, olderPz: i32, olderRD: u16) void { // MARK: freeOldMeshes() - for (0..settings.highestLod + 1) |_lod| { - const lod: u5 = @intCast(_lod); - const maxRenderDistanceNew = lastRD * chunk.chunkSize << lod; - const maxRenderDistanceOld = olderRD * chunk.chunkSize << lod; - const size: u31 = chunk.chunkSize << lod; - const mask: i32 = size - 1; - const invMask: i32 = ~mask; - - std.debug.assert(@divFloor(2 * maxRenderDistanceNew + size - 1, size) + 2 <= storageSize); - - const minX = olderPx -% maxRenderDistanceOld & invMask; - const maxX = olderPx +% maxRenderDistanceOld +% size & invMask; - var x = minX; - while (x != maxX) : (x +%= size) { - const xIndex = @divExact(x, size) & storageMask; - var deltaXNew: i64 = @abs(x +% size / 2 -% lastPx); - deltaXNew = @max(0, deltaXNew - size / 2); - var deltaXOld: i64 = @abs(x +% size / 2 -% olderPx); - deltaXOld = @max(0, deltaXOld - size / 2); - const maxYRenderDistanceNew: i32 = reduceRenderDistance(maxRenderDistanceNew, deltaXNew); - const maxYRenderDistanceOld: i32 = reduceRenderDistance(maxRenderDistanceOld, deltaXOld); - - const minY = olderPy -% maxYRenderDistanceOld & invMask; - const maxY = olderPy +% maxYRenderDistanceOld +% size & invMask; - var y = minY; - while (y != maxY) : (y +%= size) { - const yIndex = @divExact(y, size) & storageMask; - var deltaYOld: i64 = @abs(y +% size / 2 -% olderPy); - deltaYOld = @max(0, deltaYOld - size / 2); - var deltaYNew: i64 = @abs(y +% size / 2 -% lastPy); - deltaYNew = @max(0, deltaYNew - size / 2); - var maxZRenderDistanceOld: i32 = reduceRenderDistance(maxYRenderDistanceOld, deltaYOld); - if (maxZRenderDistanceOld == 0) maxZRenderDistanceOld -= size / 2; - var maxZRenderDistanceNew: i32 = reduceRenderDistance(maxYRenderDistanceNew, deltaYNew); - if (maxZRenderDistanceNew == 0) maxZRenderDistanceNew -= size / 2; - - const minZOld = olderPz -% maxZRenderDistanceOld & invMask; - const maxZOld = olderPz +% maxZRenderDistanceOld +% size & invMask; - const minZNew = lastPz -% maxZRenderDistanceNew & invMask; - const maxZNew = lastPz +% maxZRenderDistanceNew +% size & invMask; - - var zValues: [storageSize]i32 = undefined; - var zValuesLen: usize = 0; - if (minZNew -% minZOld > 0) { - var z = minZOld; - while (z != minZNew and z != maxZOld) : (z +%= size) { - zValues[zValuesLen] = z; - zValuesLen += 1; - } - } - if (maxZOld -% maxZNew > 0) { - var z = minZOld +% @max(0, maxZNew -% minZOld); - while (z != maxZOld) : (z +%= size) { - zValues[zValuesLen] = z; - zValuesLen += 1; - } - } - - for (zValues[0..zValuesLen]) |z| { - const zIndex = @divExact(z, size) & storageMask; - const index = (xIndex * storageSize + yIndex) * storageSize + zIndex; - - const node = &storageLists[_lod][@intCast(index)]; - const oldMesh = node.mesh.swap(null, .monotonic); - node.pos = undefined; - if (oldMesh) |mesh| { - node.finishedMeshing = false; - updateHigherLodNodeFinishedMeshing(mesh.pos, false); - mesh.deferredDeinit(); - } - node.isNeighborLod = @splat(false); - } - } - } - } - for (0..settings.highestLod + 1) |_lod| { - const lod: u5 = @intCast(_lod); - const maxRenderDistanceNew = lastRD * chunk.chunkSize << lod; - const maxRenderDistanceOld = olderRD * chunk.chunkSize << lod; - const size: u31 = @as(u31, LightMap.LightMapFragment.mapSize) << lod; - const mask: i32 = size - 1; - const invMask: i32 = ~mask; - - std.debug.assert(@divFloor(2 * maxRenderDistanceNew + size - 1, size) + 2 <= storageSize); - - const minX = olderPx -% maxRenderDistanceOld & invMask; - const maxX = olderPx +% maxRenderDistanceOld +% size & invMask; - var x = minX; - while (x != maxX) : (x +%= size) { - const xIndex = @divExact(x, size) & storageMask; - var deltaXNew: i64 = @abs(x +% size / 2 -% lastPx); - deltaXNew = @max(0, deltaXNew - size / 2); - var deltaXOld: i64 = @abs(x +% size / 2 -% olderPx); - deltaXOld = @max(0, deltaXOld - size / 2); - var maxYRenderDistanceNew: i32 = reduceRenderDistance(maxRenderDistanceNew, deltaXNew); - if (maxYRenderDistanceNew == 0) maxYRenderDistanceNew -= size / 2; - var maxYRenderDistanceOld: i32 = reduceRenderDistance(maxRenderDistanceOld, deltaXOld); - if (maxYRenderDistanceOld == 0) maxYRenderDistanceOld -= size / 2; - - const minYOld = olderPy -% maxYRenderDistanceOld & invMask; - const maxYOld = olderPy +% maxYRenderDistanceOld +% size & invMask; - const minYNew = lastPy -% maxYRenderDistanceNew & invMask; - const maxYNew = lastPy +% maxYRenderDistanceNew +% size & invMask; - - var yValues: [storageSize]i32 = undefined; - var yValuesLen: usize = 0; - if (minYNew -% minYOld > 0) { - var y = minYOld; - while (y != minYNew and y != maxYOld) : (y +%= size) { - yValues[yValuesLen] = y; - yValuesLen += 1; - } - } - if (maxYOld -% maxYNew > 0) { - var y = minYOld +% @max(0, maxYNew -% minYOld); - while (y != maxYOld) : (y +%= size) { - yValues[yValuesLen] = y; - yValuesLen += 1; - } - } - - for (yValues[0..yValuesLen]) |y| { - const yIndex = @divExact(y, size) & storageMask; - const index = xIndex * storageSize + yIndex; - - const oldMap = mapStorageLists[_lod][@intCast(index)].swap(null, .monotonic); - if (oldMap) |map| { - map.deferredDeinit(); - } - } - } - } + for (0..settings.highestLod + 1) |_lod| { + const lod: u5 = @intCast(_lod); + const maxRenderDistanceNew = lastRD*chunk.chunkSize << lod; + const maxRenderDistanceOld = olderRD*chunk.chunkSize << lod; + const size: u31 = chunk.chunkSize << lod; + const mask: i32 = size - 1; + const invMask: i32 = ~mask; + + std.debug.assert(@divFloor(2*maxRenderDistanceNew + size - 1, size) + 2 <= storageSize); + + const minX = olderPx -% maxRenderDistanceOld & invMask; + const maxX = olderPx +% maxRenderDistanceOld +% size & invMask; + var x = minX; + while (x != maxX) : (x +%= size) { + const xIndex = @divExact(x, size) & storageMask; + var deltaXNew: i64 = @abs(x +% size/2 -% lastPx); + deltaXNew = @max(0, deltaXNew - size/2); + var deltaXOld: i64 = @abs(x +% size/2 -% olderPx); + deltaXOld = @max(0, deltaXOld - size/2); + const maxYRenderDistanceNew: i32 = reduceRenderDistance(maxRenderDistanceNew, deltaXNew); + const maxYRenderDistanceOld: i32 = reduceRenderDistance(maxRenderDistanceOld, deltaXOld); + + const minY = olderPy -% maxYRenderDistanceOld & invMask; + const maxY = olderPy +% maxYRenderDistanceOld +% size & invMask; + var y = minY; + while (y != maxY) : (y +%= size) { + const yIndex = @divExact(y, size) & storageMask; + var deltaYOld: i64 = @abs(y +% size/2 -% olderPy); + deltaYOld = @max(0, deltaYOld - size/2); + var deltaYNew: i64 = @abs(y +% size/2 -% lastPy); + deltaYNew = @max(0, deltaYNew - size/2); + var maxZRenderDistanceOld: i32 = reduceRenderDistance(maxYRenderDistanceOld, deltaYOld); + if (maxZRenderDistanceOld == 0) maxZRenderDistanceOld -= size/2; + var maxZRenderDistanceNew: i32 = reduceRenderDistance(maxYRenderDistanceNew, deltaYNew); + if (maxZRenderDistanceNew == 0) maxZRenderDistanceNew -= size/2; + + const minZOld = olderPz -% maxZRenderDistanceOld & invMask; + const maxZOld = olderPz +% maxZRenderDistanceOld +% size & invMask; + const minZNew = lastPz -% maxZRenderDistanceNew & invMask; + const maxZNew = lastPz +% maxZRenderDistanceNew +% size & invMask; + + var zValues: [storageSize]i32 = undefined; + var zValuesLen: usize = 0; + if (minZNew -% minZOld > 0) { + var z = minZOld; + while (z != minZNew and z != maxZOld) : (z +%= size) { + zValues[zValuesLen] = z; + zValuesLen += 1; + } + } + if (maxZOld -% maxZNew > 0) { + var z = minZOld +% @max(0, maxZNew -% minZOld); + while (z != maxZOld) : (z +%= size) { + zValues[zValuesLen] = z; + zValuesLen += 1; + } + } + + for (zValues[0..zValuesLen]) |z| { + const zIndex = @divExact(z, size) & storageMask; + const index = (xIndex*storageSize + yIndex)*storageSize + zIndex; + + const node = &storageLists[_lod][@intCast(index)]; + const oldMesh = node.mesh.swap(null, .monotonic); + node.pos = undefined; + if (oldMesh) |mesh| { + node.finishedMeshing = false; + updateHigherLodNodeFinishedMeshing(mesh.pos, false); + mesh.deferredDeinit(); + } + node.isNeighborLod = @splat(false); + } + } + } + } + for (0..settings.highestLod + 1) |_lod| { + const lod: u5 = @intCast(_lod); + const maxRenderDistanceNew = lastRD*chunk.chunkSize << lod; + const maxRenderDistanceOld = olderRD*chunk.chunkSize << lod; + const size: u31 = @as(u31, LightMap.LightMapFragment.mapSize) << lod; + const mask: i32 = size - 1; + const invMask: i32 = ~mask; + + std.debug.assert(@divFloor(2*maxRenderDistanceNew + size - 1, size) + 2 <= storageSize); + + const minX = olderPx -% maxRenderDistanceOld & invMask; + const maxX = olderPx +% maxRenderDistanceOld +% size & invMask; + var x = minX; + while (x != maxX) : (x +%= size) { + const xIndex = @divExact(x, size) & storageMask; + var deltaXNew: i64 = @abs(x +% size/2 -% lastPx); + deltaXNew = @max(0, deltaXNew - size/2); + var deltaXOld: i64 = @abs(x +% size/2 -% olderPx); + deltaXOld = @max(0, deltaXOld - size/2); + var maxYRenderDistanceNew: i32 = reduceRenderDistance(maxRenderDistanceNew, deltaXNew); + if (maxYRenderDistanceNew == 0) maxYRenderDistanceNew -= size/2; + var maxYRenderDistanceOld: i32 = reduceRenderDistance(maxRenderDistanceOld, deltaXOld); + if (maxYRenderDistanceOld == 0) maxYRenderDistanceOld -= size/2; + + const minYOld = olderPy -% maxYRenderDistanceOld & invMask; + const maxYOld = olderPy +% maxYRenderDistanceOld +% size & invMask; + const minYNew = lastPy -% maxYRenderDistanceNew & invMask; + const maxYNew = lastPy +% maxYRenderDistanceNew +% size & invMask; + + var yValues: [storageSize]i32 = undefined; + var yValuesLen: usize = 0; + if (minYNew -% minYOld > 0) { + var y = minYOld; + while (y != minYNew and y != maxYOld) : (y +%= size) { + yValues[yValuesLen] = y; + yValuesLen += 1; + } + } + if (maxYOld -% maxYNew > 0) { + var y = minYOld +% @max(0, maxYNew -% minYOld); + while (y != maxYOld) : (y +%= size) { + yValues[yValuesLen] = y; + yValuesLen += 1; + } + } + + for (yValues[0..yValuesLen]) |y| { + const yIndex = @divExact(y, size) & storageMask; + const index = xIndex*storageSize + yIndex; + + const oldMap = mapStorageLists[_lod][@intCast(index)].swap(null, .monotonic); + if (oldMap) |map| { + map.deferredDeinit(); + } + } + } + } } fn createNewMeshes(olderPx: i32, olderPy: i32, olderPz: i32, olderRD: u16, meshRequests: *main.ListManaged(chunk.ChunkPosition), mapRequests: *main.ListManaged(LightMap.MapFragmentPosition)) void { // MARK: createNewMeshes() - for (0..settings.highestLod + 1) |_lod| { - const lod: u5 = @intCast(_lod); - const maxRenderDistanceNew = lastRD * chunk.chunkSize << lod; - const maxRenderDistanceOld = olderRD * chunk.chunkSize << lod; - const size: u31 = chunk.chunkSize << lod; - const mask: i32 = size - 1; - const invMask: i32 = ~mask; - - std.debug.assert(@divFloor(2 * maxRenderDistanceNew + size - 1, size) + 2 <= storageSize); - - const minX = lastPx -% maxRenderDistanceNew & invMask; - const maxX = lastPx +% maxRenderDistanceNew +% size & invMask; - var x = minX; - while (x != maxX) : (x +%= size) { - const xIndex = @divExact(x, size) & storageMask; - var deltaXNew: i64 = @abs(x +% size / 2 -% lastPx); - deltaXNew = @max(0, deltaXNew - size / 2); - var deltaXOld: i64 = @abs(x +% size / 2 -% olderPx); - deltaXOld = @max(0, deltaXOld - size / 2); - const maxYRenderDistanceNew: i32 = reduceRenderDistance(maxRenderDistanceNew, deltaXNew); - const maxYRenderDistanceOld: i32 = reduceRenderDistance(maxRenderDistanceOld, deltaXOld); - - const minY = lastPy -% maxYRenderDistanceNew & invMask; - const maxY = lastPy +% maxYRenderDistanceNew +% size & invMask; - var y = minY; - while (y != maxY) : (y +%= size) { - const yIndex = @divExact(y, size) & storageMask; - var deltaYOld: i64 = @abs(y +% size / 2 -% olderPy); - deltaYOld = @max(0, deltaYOld - size / 2); - var deltaYNew: i64 = @abs(y +% size / 2 -% lastPy); - deltaYNew = @max(0, deltaYNew - size / 2); - var maxZRenderDistanceNew: i32 = reduceRenderDistance(maxYRenderDistanceNew, deltaYNew); - if (maxZRenderDistanceNew == 0) maxZRenderDistanceNew -= size / 2; - var maxZRenderDistanceOld: i32 = reduceRenderDistance(maxYRenderDistanceOld, deltaYOld); - if (maxZRenderDistanceOld == 0) maxZRenderDistanceOld -= size / 2; - - const minZOld = olderPz -% maxZRenderDistanceOld & invMask; - const maxZOld = olderPz +% maxZRenderDistanceOld +% size & invMask; - const minZNew = lastPz -% maxZRenderDistanceNew & invMask; - const maxZNew = lastPz +% maxZRenderDistanceNew +% size & invMask; - - var zValues: [storageSize]i32 = undefined; - var zValuesLen: usize = 0; - if (minZOld -% minZNew > 0) { - var z = minZNew; - while (z != minZOld and z != maxZNew) : (z +%= size) { - zValues[zValuesLen] = z; - zValuesLen += 1; - } - } - if (maxZNew -% maxZOld > 0) { - var z = minZNew +% @max(0, maxZOld -% minZNew); - while (z != maxZNew) : (z +%= size) { - zValues[zValuesLen] = z; - zValuesLen += 1; - } - } - - for (zValues[0..zValuesLen]) |z| { - const zIndex = @divExact(z, size) & storageMask; - const index = (xIndex * storageSize + yIndex) * storageSize + zIndex; - const pos = chunk.ChunkPosition{ .wx = x, .wy = y, .wz = z, .voxelSize = @as(u31, 1) << lod }; - - const node = &storageLists[_lod][@intCast(index)]; - node.pos = pos; - if (node.mesh.load(.acquire)) |mesh| { - std.debug.assert(std.meta.eql(pos, mesh.pos)); - } else { - meshRequests.append(pos); - } - } - } - } - } - for (0..settings.highestLod + 1) |_lod| { - const lod: u5 = @intCast(_lod); - const maxRenderDistanceNew = lastRD * chunk.chunkSize << lod; - const maxRenderDistanceOld = olderRD * chunk.chunkSize << lod; - const size: u31 = @as(u31, LightMap.LightMapFragment.mapSize) << lod; - const mask: i32 = size - 1; - const invMask: i32 = ~mask; - - std.debug.assert(@divFloor(2 * maxRenderDistanceNew + size - 1, size) + 2 <= storageSize); - - const minX = lastPx -% maxRenderDistanceNew & invMask; - const maxX = lastPx +% maxRenderDistanceNew +% size & invMask; - var x = minX; - while (x != maxX) : (x +%= size) { - const xIndex = @divExact(x, size) & storageMask; - var deltaXNew: i64 = @abs(x +% size / 2 -% lastPx); - deltaXNew = @max(0, deltaXNew - size / 2); - var deltaXOld: i64 = @abs(x +% size / 2 -% olderPx); - deltaXOld = @max(0, deltaXOld - size / 2); - var maxYRenderDistanceNew: i32 = reduceRenderDistance(maxRenderDistanceNew, deltaXNew); - if (maxYRenderDistanceNew == 0) maxYRenderDistanceNew -= size / 2; - var maxYRenderDistanceOld: i32 = reduceRenderDistance(maxRenderDistanceOld, deltaXOld); - if (maxYRenderDistanceOld == 0) maxYRenderDistanceOld -= size / 2; - - const minYOld = olderPy -% maxYRenderDistanceOld & invMask; - const maxYOld = olderPy +% maxYRenderDistanceOld +% size & invMask; - const minYNew = lastPy -% maxYRenderDistanceNew & invMask; - const maxYNew = lastPy +% maxYRenderDistanceNew +% size & invMask; - - var yValues: [storageSize]i32 = undefined; - var yValuesLen: usize = 0; - if (minYOld -% minYNew > 0) { - var y = minYNew; - while (y != minYOld and y != maxYNew) : (y +%= size) { - yValues[yValuesLen] = y; - yValuesLen += 1; - } - } - if (maxYNew -% maxYOld > 0) { - var y = minYNew +% @max(0, maxYOld -% minYNew); - while (y != maxYNew) : (y +%= size) { - yValues[yValuesLen] = y; - yValuesLen += 1; - } - } - - for (yValues[0..yValuesLen]) |y| { - const yIndex = @divExact(y, size) & storageMask; - const index = xIndex * storageSize + yIndex; - const pos = LightMap.MapFragmentPosition{ .wx = x, .wy = y, .voxelSize = @as(u31, 1) << lod, .voxelSizeShift = lod }; - - const map = mapStorageLists[_lod][@intCast(index)].load(.monotonic); - if (map) |_map| { - std.debug.assert(std.meta.eql(pos, _map.pos)); - } else { - mapRequests.append(pos); - } - } - } - } + for (0..settings.highestLod + 1) |_lod| { + const lod: u5 = @intCast(_lod); + const maxRenderDistanceNew = lastRD*chunk.chunkSize << lod; + const maxRenderDistanceOld = olderRD*chunk.chunkSize << lod; + const size: u31 = chunk.chunkSize << lod; + const mask: i32 = size - 1; + const invMask: i32 = ~mask; + + std.debug.assert(@divFloor(2*maxRenderDistanceNew + size - 1, size) + 2 <= storageSize); + + const minX = lastPx -% maxRenderDistanceNew & invMask; + const maxX = lastPx +% maxRenderDistanceNew +% size & invMask; + var x = minX; + while (x != maxX) : (x +%= size) { + const xIndex = @divExact(x, size) & storageMask; + var deltaXNew: i64 = @abs(x +% size/2 -% lastPx); + deltaXNew = @max(0, deltaXNew - size/2); + var deltaXOld: i64 = @abs(x +% size/2 -% olderPx); + deltaXOld = @max(0, deltaXOld - size/2); + const maxYRenderDistanceNew: i32 = reduceRenderDistance(maxRenderDistanceNew, deltaXNew); + const maxYRenderDistanceOld: i32 = reduceRenderDistance(maxRenderDistanceOld, deltaXOld); + + const minY = lastPy -% maxYRenderDistanceNew & invMask; + const maxY = lastPy +% maxYRenderDistanceNew +% size & invMask; + var y = minY; + while (y != maxY) : (y +%= size) { + const yIndex = @divExact(y, size) & storageMask; + var deltaYOld: i64 = @abs(y +% size/2 -% olderPy); + deltaYOld = @max(0, deltaYOld - size/2); + var deltaYNew: i64 = @abs(y +% size/2 -% lastPy); + deltaYNew = @max(0, deltaYNew - size/2); + var maxZRenderDistanceNew: i32 = reduceRenderDistance(maxYRenderDistanceNew, deltaYNew); + if (maxZRenderDistanceNew == 0) maxZRenderDistanceNew -= size/2; + var maxZRenderDistanceOld: i32 = reduceRenderDistance(maxYRenderDistanceOld, deltaYOld); + if (maxZRenderDistanceOld == 0) maxZRenderDistanceOld -= size/2; + + const minZOld = olderPz -% maxZRenderDistanceOld & invMask; + const maxZOld = olderPz +% maxZRenderDistanceOld +% size & invMask; + const minZNew = lastPz -% maxZRenderDistanceNew & invMask; + const maxZNew = lastPz +% maxZRenderDistanceNew +% size & invMask; + + var zValues: [storageSize]i32 = undefined; + var zValuesLen: usize = 0; + if (minZOld -% minZNew > 0) { + var z = minZNew; + while (z != minZOld and z != maxZNew) : (z +%= size) { + zValues[zValuesLen] = z; + zValuesLen += 1; + } + } + if (maxZNew -% maxZOld > 0) { + var z = minZNew +% @max(0, maxZOld -% minZNew); + while (z != maxZNew) : (z +%= size) { + zValues[zValuesLen] = z; + zValuesLen += 1; + } + } + + for (zValues[0..zValuesLen]) |z| { + const zIndex = @divExact(z, size) & storageMask; + const index = (xIndex*storageSize + yIndex)*storageSize + zIndex; + const pos = chunk.ChunkPosition{.wx = x, .wy = y, .wz = z, .voxelSize = @as(u31, 1) << lod}; + + const node = &storageLists[_lod][@intCast(index)]; + node.pos = pos; + if (node.mesh.load(.acquire)) |mesh| { + std.debug.assert(std.meta.eql(pos, mesh.pos)); + } else { + meshRequests.append(pos); + } + } + } + } + } + for (0..settings.highestLod + 1) |_lod| { + const lod: u5 = @intCast(_lod); + const maxRenderDistanceNew = lastRD*chunk.chunkSize << lod; + const maxRenderDistanceOld = olderRD*chunk.chunkSize << lod; + const size: u31 = @as(u31, LightMap.LightMapFragment.mapSize) << lod; + const mask: i32 = size - 1; + const invMask: i32 = ~mask; + + std.debug.assert(@divFloor(2*maxRenderDistanceNew + size - 1, size) + 2 <= storageSize); + + const minX = lastPx -% maxRenderDistanceNew & invMask; + const maxX = lastPx +% maxRenderDistanceNew +% size & invMask; + var x = minX; + while (x != maxX) : (x +%= size) { + const xIndex = @divExact(x, size) & storageMask; + var deltaXNew: i64 = @abs(x +% size/2 -% lastPx); + deltaXNew = @max(0, deltaXNew - size/2); + var deltaXOld: i64 = @abs(x +% size/2 -% olderPx); + deltaXOld = @max(0, deltaXOld - size/2); + var maxYRenderDistanceNew: i32 = reduceRenderDistance(maxRenderDistanceNew, deltaXNew); + if (maxYRenderDistanceNew == 0) maxYRenderDistanceNew -= size/2; + var maxYRenderDistanceOld: i32 = reduceRenderDistance(maxRenderDistanceOld, deltaXOld); + if (maxYRenderDistanceOld == 0) maxYRenderDistanceOld -= size/2; + + const minYOld = olderPy -% maxYRenderDistanceOld & invMask; + const maxYOld = olderPy +% maxYRenderDistanceOld +% size & invMask; + const minYNew = lastPy -% maxYRenderDistanceNew & invMask; + const maxYNew = lastPy +% maxYRenderDistanceNew +% size & invMask; + + var yValues: [storageSize]i32 = undefined; + var yValuesLen: usize = 0; + if (minYOld -% minYNew > 0) { + var y = minYNew; + while (y != minYOld and y != maxYNew) : (y +%= size) { + yValues[yValuesLen] = y; + yValuesLen += 1; + } + } + if (maxYNew -% maxYOld > 0) { + var y = minYNew +% @max(0, maxYOld -% minYNew); + while (y != maxYNew) : (y +%= size) { + yValues[yValuesLen] = y; + yValuesLen += 1; + } + } + + for (yValues[0..yValuesLen]) |y| { + const yIndex = @divExact(y, size) & storageMask; + const index = xIndex*storageSize + yIndex; + const pos = LightMap.MapFragmentPosition{.wx = x, .wy = y, .voxelSize = @as(u31, 1) << lod, .voxelSizeShift = lod}; + + const map = mapStorageLists[_lod][@intCast(index)].load(.monotonic); + if (map) |_map| { + std.debug.assert(std.meta.eql(pos, _map.pos)); + } else { + mapRequests.append(pos); + } + } + } + } } pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: ?*const main.renderer.Frustum, playerPos: Vec3d, renderDistance: u16) []*chunk_meshing.ChunkMesh { // MARK: updateAndGetRenderChunks() - meshList.clearRetainingCapacity(); - - const playerPosInt: Vec3i = @floor(playerPos); - - var meshRequests: main.ListManaged(chunk.ChunkPosition) = .init(main.stackAllocator); - defer meshRequests.deinit(); - var mapRequests: main.ListManaged(LightMap.MapFragmentPosition) = .init(main.stackAllocator); - defer mapRequests.deinit(); - - const olderPx = lastPx; - const olderPy = lastPy; - const olderPz = lastPz; - const olderRD = lastRD; - mutex.lock(); - lastPx = @trunc(playerPos[0]); - lastPy = @trunc(playerPos[1]); - lastPz = @trunc(playerPos[2]); - lastRD = renderDistance; - mutex.unlock(); - freeOldMeshes(olderPx, olderPy, olderPz, olderRD); - - createNewMeshes(olderPx, olderPy, olderPz, olderRD, &meshRequests, &mapRequests); - - // Make requests as soon as possible to reduce latency: - network.protocols.lightMapRequest.sendRequest(conn, mapRequests.items); - network.protocols.chunkRequest.sendRequest(conn, meshRequests.items, .{ lastPx, lastPy, lastPz }, lastRD); - - // Finds all visible chunks and lod chunks using a breadth-first hierarchical search. - - var searchList = main.utils.CircularBufferQueue(*ChunkMeshNode).init(main.stackAllocator, 1024); - defer searchList.deinit(); - { - var firstPos = chunk.ChunkPosition{ - .wx = playerPosInt[0], - .wy = playerPosInt[1], - .wz = playerPosInt[2], - .voxelSize = 1, - }; - const lod: u3 = settings.highestLod; - firstPos.wx &= ~@as(i32, chunk.chunkMask << lod | (@as(i32, 1) << lod) - 1); - firstPos.wy &= ~@as(i32, chunk.chunkMask << lod | (@as(i32, 1) << lod) - 1); - firstPos.wz &= ~@as(i32, chunk.chunkMask << lod | (@as(i32, 1) << lod) - 1); - firstPos.voxelSize <<= lod; - const node = getNodePointer(firstPos); - const hasMesh = node.finishedMeshing; - if (hasMesh) { - node.active = true; - node.rendered = true; - searchList.pushBack(node); - } - } - var nodeList: main.ListManaged(*ChunkMeshNode) = .initCapacity(main.stackAllocator, 1024); - defer nodeList.deinit(); - while (searchList.popFront()) |node| { - std.debug.assert(node.finishedMeshing); - std.debug.assert(node.active); - node.active = false; - - const pos = node.pos; - - const relPos: Vec3i = Vec3i{ pos.wx, pos.wy, pos.wz } - playerPosInt; - - const chunkSizeVector: Vec3i = @splat(chunk.chunkSize * pos.voxelSize); - - // TODO: This seems like a good place for a comment, could someone please add one? - if (pos.voxelSize == @as(i32, 1) << settings.highestLod) { - for (chunk.Neighbor.iterable) |neighbor| { - const component = neighbor.extractDirectionComponent(relPos); - if (neighbor.isPositive() and component + chunk.chunkSize * pos.voxelSize <= 0) continue; - if (!neighbor.isPositive() and component > 0) continue; - const neighborPos = chunk.ChunkPosition{ - .wx = pos.wx +% neighbor.relX() * chunk.chunkSize * pos.voxelSize, - .wy = pos.wy +% neighbor.relY() * chunk.chunkSize * pos.voxelSize, - .wz = pos.wz +% neighbor.relZ() * chunk.chunkSize * pos.voxelSize, - .voxelSize = pos.voxelSize, - }; - const node2 = getNodePointer(neighborPos); - if (!node2.active and node2.finishedMeshing) { - const relPosFloat: Vec3f = @floatCast(@as(Vec3d, @floatFromInt(Vec3i{ pos.wx, pos.wy, pos.wz })) - playerPos); - if (frustum != null and !frustum.?.testAAB(relPosFloat + @as(Vec3f, @floatFromInt(neighbor.relPos() * chunkSizeVector)), @floatFromInt(chunkSizeVector))) continue; - node2.active = true; - node2.rendered = true; - searchList.pushBack(node2); - } - } - } - - if (node.finishedMeshingHigherResolution == 0xff) { - node.rendered = false; - const lowerLodBit: i32 = pos.voxelSize * chunk.chunkSize >> 1; - const startPos: chunk.ChunkPosition = .{ - .wx = pos.wx | if ((pos.wx | lowerLodBit) -% playerPosInt[0] > 0) lowerLodBit else 0, - .wy = pos.wy | if ((pos.wy | lowerLodBit) -% playerPosInt[1] > 0) lowerLodBit else 0, - .wz = pos.wz | if ((pos.wz | lowerLodBit) -% playerPosInt[2] > 0) lowerLodBit else 0, - .voxelSize = pos.voxelSize >> 1, - }; - for (0..2) |dx| { - for (0..2) |dy| { - for (0..2) |dz| { - var nextPos = startPos; - if (dx == 1) nextPos.wx ^= lowerLodBit; - if (dy == 1) nextPos.wy ^= lowerLodBit; - if (dz == 1) nextPos.wz ^= lowerLodBit; - const node2 = getNodePointer(nextPos); - const relNextPos: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{ nextPos.wx, nextPos.wy, nextPos.wz })) - playerPos; - if (frustum != null and !frustum.?.testAAB(@floatCast(relNextPos), @splat(@floatFromInt(chunk.chunkSize * nextPos.voxelSize)))) - continue; - std.debug.assert(node2.finishedMeshing); - node2.active = true; - node2.rendered = true; - searchList.pushFront(node2); - } - } - } - } else { - nodeList.append(node); - } - } - for (nodeList.items) |node| { - const pos = node.pos; - var isNeighborLod: [6]bool = @splat(false); - if (pos.voxelSize != @as(i32, 1) << settings.highestLod) { - for (chunk.Neighbor.iterable) |neighbor| { - var neighborPos = chunk.ChunkPosition{ - .wx = pos.wx +% neighbor.relX() * chunk.chunkSize * pos.voxelSize, - .wy = pos.wy +% neighbor.relY() * chunk.chunkSize * pos.voxelSize, - .wz = pos.wz +% neighbor.relZ() * chunk.chunkSize * pos.voxelSize, - .voxelSize = pos.voxelSize, - }; - 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 node2 = getNodePointer(neighborPos); - isNeighborLod[neighbor.toInt()] = node2.finishedMeshingHigherResolution != 0xff; - } - } - if (!std.meta.eql(node.isNeighborLod, isNeighborLod)) { - const mesh = node.mesh.load(.acquire).?; // no other thread is allowed to overwrite the mesh (unless it's null). - mesh.isNeighborLod = isNeighborLod; - node.isNeighborLod = isNeighborLod; - mesh.uploadData(); - } - } - for (nodeList.items) |node| { - node.rendered = false; - if (!node.finishedMeshing) continue; - - const mesh = node.mesh.load(.acquire).?; // no other thread is allowed to overwrite the mesh (unless it's null). - - if (mesh.needsMeshUpdate) { - mesh.needsMeshUpdate = false; - mesh.uploadData(); - } - // Remove empty meshes. - if (!mesh.isEmpty()) { - meshList.append(main.globalAllocator, mesh); - } - } - - return meshList.items; + meshList.clearRetainingCapacity(); + + const playerPosInt: Vec3i = @floor(playerPos); + + var meshRequests: main.ListManaged(chunk.ChunkPosition) = .init(main.stackAllocator); + defer meshRequests.deinit(); + var mapRequests: main.ListManaged(LightMap.MapFragmentPosition) = .init(main.stackAllocator); + defer mapRequests.deinit(); + + const olderPx = lastPx; + const olderPy = lastPy; + const olderPz = lastPz; + const olderRD = lastRD; + mutex.lock(); + lastPx = @trunc(playerPos[0]); + lastPy = @trunc(playerPos[1]); + lastPz = @trunc(playerPos[2]); + lastRD = renderDistance; + mutex.unlock(); + freeOldMeshes(olderPx, olderPy, olderPz, olderRD); + + createNewMeshes(olderPx, olderPy, olderPz, olderRD, &meshRequests, &mapRequests); + + // Make requests as soon as possible to reduce latency: + network.protocols.lightMapRequest.sendRequest(conn, mapRequests.items); + network.protocols.chunkRequest.sendRequest(conn, meshRequests.items, .{lastPx, lastPy, lastPz}, lastRD); + + // Finds all visible chunks and lod chunks using a breadth-first hierarchical search. + + var searchList = main.utils.CircularBufferQueue(*ChunkMeshNode).init(main.stackAllocator, 1024); + defer searchList.deinit(); + { + var firstPos = chunk.ChunkPosition{ + .wx = playerPosInt[0], + .wy = playerPosInt[1], + .wz = playerPosInt[2], + .voxelSize = 1, + }; + const lod: u3 = settings.highestLod; + firstPos.wx &= ~@as(i32, chunk.chunkMask << lod | (@as(i32, 1) << lod) - 1); + firstPos.wy &= ~@as(i32, chunk.chunkMask << lod | (@as(i32, 1) << lod) - 1); + firstPos.wz &= ~@as(i32, chunk.chunkMask << lod | (@as(i32, 1) << lod) - 1); + firstPos.voxelSize <<= lod; + const node = getNodePointer(firstPos); + const hasMesh = node.finishedMeshing; + if (hasMesh) { + node.active = true; + node.rendered = true; + searchList.pushBack(node); + } + } + var nodeList: main.ListManaged(*ChunkMeshNode) = .initCapacity(main.stackAllocator, 1024); + defer nodeList.deinit(); + while (searchList.popFront()) |node| { + std.debug.assert(node.finishedMeshing); + std.debug.assert(node.active); + node.active = false; + + const pos = node.pos; + + const relPos: Vec3i = Vec3i{pos.wx, pos.wy, pos.wz} - playerPosInt; + + const chunkSizeVector: Vec3i = @splat(chunk.chunkSize*pos.voxelSize); + + // TODO: This seems like a good place for a comment, could someone please add one? + if (pos.voxelSize == @as(i32, 1) << settings.highestLod) { + for (chunk.Neighbor.iterable) |neighbor| { + const component = neighbor.extractDirectionComponent(relPos); + if (neighbor.isPositive() and component + chunk.chunkSize*pos.voxelSize <= 0) continue; + if (!neighbor.isPositive() and component > 0) continue; + const neighborPos = chunk.ChunkPosition{ + .wx = pos.wx +% neighbor.relX()*chunk.chunkSize*pos.voxelSize, + .wy = pos.wy +% neighbor.relY()*chunk.chunkSize*pos.voxelSize, + .wz = pos.wz +% neighbor.relZ()*chunk.chunkSize*pos.voxelSize, + .voxelSize = pos.voxelSize, + }; + const node2 = getNodePointer(neighborPos); + if (!node2.active and node2.finishedMeshing) { + const relPosFloat: Vec3f = @floatCast(@as(Vec3d, @floatFromInt(Vec3i{pos.wx, pos.wy, pos.wz})) - playerPos); + if (frustum != null and !frustum.?.testAAB(relPosFloat + @as(Vec3f, @floatFromInt(neighbor.relPos()*chunkSizeVector)), @floatFromInt(chunkSizeVector))) continue; + node2.active = true; + node2.rendered = true; + searchList.pushBack(node2); + } + } + } + + if (node.finishedMeshingHigherResolution == 0xff) { + node.rendered = false; + const lowerLodBit: i32 = pos.voxelSize*chunk.chunkSize >> 1; + const startPos: chunk.ChunkPosition = .{ + .wx = pos.wx | if ((pos.wx | lowerLodBit) -% playerPosInt[0] > 0) lowerLodBit else 0, + .wy = pos.wy | if ((pos.wy | lowerLodBit) -% playerPosInt[1] > 0) lowerLodBit else 0, + .wz = pos.wz | if ((pos.wz | lowerLodBit) -% playerPosInt[2] > 0) lowerLodBit else 0, + .voxelSize = pos.voxelSize >> 1, + }; + for (0..2) |dx| { + for (0..2) |dy| { + for (0..2) |dz| { + var nextPos = startPos; + if (dx == 1) nextPos.wx ^= lowerLodBit; + if (dy == 1) nextPos.wy ^= lowerLodBit; + if (dz == 1) nextPos.wz ^= lowerLodBit; + const node2 = getNodePointer(nextPos); + const relNextPos: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{nextPos.wx, nextPos.wy, nextPos.wz})) - playerPos; + if (frustum != null and !frustum.?.testAAB(@floatCast(relNextPos), @splat(@floatFromInt(chunk.chunkSize*nextPos.voxelSize)))) + continue; + std.debug.assert(node2.finishedMeshing); + node2.active = true; + node2.rendered = true; + searchList.pushFront(node2); + } + } + } + } else { + nodeList.append(node); + } + } + for (nodeList.items) |node| { + const pos = node.pos; + var isNeighborLod: [6]bool = @splat(false); + if (pos.voxelSize != @as(i32, 1) << settings.highestLod) { + for (chunk.Neighbor.iterable) |neighbor| { + var neighborPos = chunk.ChunkPosition{ + .wx = pos.wx +% neighbor.relX()*chunk.chunkSize*pos.voxelSize, + .wy = pos.wy +% neighbor.relY()*chunk.chunkSize*pos.voxelSize, + .wz = pos.wz +% neighbor.relZ()*chunk.chunkSize*pos.voxelSize, + .voxelSize = pos.voxelSize, + }; + 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 node2 = getNodePointer(neighborPos); + isNeighborLod[neighbor.toInt()] = node2.finishedMeshingHigherResolution != 0xff; + } + } + if (!std.meta.eql(node.isNeighborLod, isNeighborLod)) { + const mesh = node.mesh.load(.acquire).?; // no other thread is allowed to overwrite the mesh (unless it's null). + mesh.isNeighborLod = isNeighborLod; + node.isNeighborLod = isNeighborLod; + mesh.uploadData(); + } + } + for (nodeList.items) |node| { + node.rendered = false; + if (!node.finishedMeshing) continue; + + const mesh = node.mesh.load(.acquire).?; // no other thread is allowed to overwrite the mesh (unless it's null). + + if (mesh.needsMeshUpdate) { + mesh.needsMeshUpdate = false; + mesh.uploadData(); + } + // Remove empty meshes. + if (!mesh.isEmpty()) { + meshList.append(main.globalAllocator, mesh); + } + } + + return meshList.items; } pub fn updateMeshes(targetTime: std.Io.Timestamp) void { // MARK: updateMeshes() - mutex.lock(); - defer mutex.unlock(); - while (priorityMeshUpdateList.popFront()) |pos| { - const mesh = getMesh(pos) orelse continue; - if (!mesh.needsMeshUpdate) { - continue; - } - mesh.needsMeshUpdate = false; - mutex.unlock(); - defer mutex.lock(); - mesh.uploadData(); - if (targetTime.durationTo(main.timestamp()).nanoseconds >= 0) break; // Update at least one mesh. - } - while (mapUpdatableList.popFront()) |map| { - if (!isMapInRenderDistance(map.pos)) { - map.deferredDeinit(); - } else { - const mapPointer = getMapPiecePointer(map.pos.wx, map.pos.wy, map.pos.voxelSize).swap(map, .release); - if (mapPointer) |old| { - old.deferredDeinit(); - } - } - } - while (updatableList.items.len != 0) { - // TODO: Find a faster solution than going through the entire list every frame. - var closestPriority: f32 = -std.math.floatMax(f32); - var closestIndex: usize = 0; - const playerPos = game.Player.getEyePosBlocking(); - { - var i: usize = 0; - while (i < updatableList.items.len) { - const pos = updatableList.items[i]; - if (!isInRenderDistance(pos)) { - _ = updatableList.swapRemove(i); - mutex.unlock(); - defer mutex.lock(); - continue; - } - const priority = pos.getPriority(playerPos); - if (priority > closestPriority) { - closestPriority = priority; - closestIndex = i; - } - i += 1; - } - if (updatableList.items.len == 0) break; - } - const pos = updatableList.swapRemove(closestIndex); - mutex.unlock(); - defer mutex.lock(); - if (isInRenderDistance(pos)) { - const node = getNodePointer(pos); - if (node.finishedMeshing) continue; - const mesh = getMesh(pos) orelse continue; - node.finishedMeshing = true; - mesh.finishedMeshing = true; - updateHigherLodNodeFinishedMeshing(pos, true); - mesh.uploadData(); - } - if (targetTime.durationTo(main.timestamp()).nanoseconds >= 0) break; // Update at least one mesh. - } + mutex.lock(); + defer mutex.unlock(); + while (priorityMeshUpdateList.popFront()) |pos| { + const mesh = getMesh(pos) orelse continue; + if (!mesh.needsMeshUpdate) { + continue; + } + mesh.needsMeshUpdate = false; + mutex.unlock(); + defer mutex.lock(); + mesh.uploadData(); + if (targetTime.durationTo(main.timestamp()).nanoseconds >= 0) break; // Update at least one mesh. + } + while (mapUpdatableList.popFront()) |map| { + if (!isMapInRenderDistance(map.pos)) { + map.deferredDeinit(); + } else { + const mapPointer = getMapPiecePointer(map.pos.wx, map.pos.wy, map.pos.voxelSize).swap(map, .release); + if (mapPointer) |old| { + old.deferredDeinit(); + } + } + } + while (updatableList.items.len != 0) { + // TODO: Find a faster solution than going through the entire list every frame. + var closestPriority: f32 = -std.math.floatMax(f32); + var closestIndex: usize = 0; + const playerPos = game.Player.getEyePosBlocking(); + { + var i: usize = 0; + while (i < updatableList.items.len) { + const pos = updatableList.items[i]; + if (!isInRenderDistance(pos)) { + _ = updatableList.swapRemove(i); + mutex.unlock(); + defer mutex.lock(); + continue; + } + const priority = pos.getPriority(playerPos); + if (priority > closestPriority) { + closestPriority = priority; + closestIndex = i; + } + i += 1; + } + if (updatableList.items.len == 0) break; + } + const pos = updatableList.swapRemove(closestIndex); + mutex.unlock(); + defer mutex.lock(); + if (isInRenderDistance(pos)) { + const node = getNodePointer(pos); + if (node.finishedMeshing) continue; + const mesh = getMesh(pos) orelse continue; + node.finishedMeshing = true; + mesh.finishedMeshing = true; + updateHigherLodNodeFinishedMeshing(pos, true); + mesh.uploadData(); + } + if (targetTime.durationTo(main.timestamp()).nanoseconds >= 0) break; // Update at least one mesh. + } } // MARK: adders pub fn addToUpdateList(mesh: *chunk_meshing.ChunkMesh) void { - mutex.lock(); - defer mutex.unlock(); - if (mesh.finishedMeshing) { - priorityMeshUpdateList.pushBack(mesh.pos); - mesh.needsMeshUpdate = true; - } + mutex.lock(); + defer mutex.unlock(); + if (mesh.finishedMeshing) { + priorityMeshUpdateList.pushBack(mesh.pos); + mesh.needsMeshUpdate = true; + } } pub fn addMeshToStorage(mesh: *chunk_meshing.ChunkMesh) error{ AlreadyStored, NoLongerNeeded }!void { - mutex.lock(); - defer mutex.unlock(); - if (!isInRenderDistance(mesh.pos)) { - return error.NoLongerNeeded; - } - const node = getNodePointer(mesh.pos); - if (node.mesh.cmpxchgStrong(null, mesh, .release, .monotonic) != null) { - return error.AlreadyStored; - } - node.finishedMeshing = mesh.finishedMeshing; - updateHigherLodNodeFinishedMeshing(mesh.pos, mesh.finishedMeshing); + mutex.lock(); + defer mutex.unlock(); + if (!isInRenderDistance(mesh.pos)) { + return error.NoLongerNeeded; + } + const node = getNodePointer(mesh.pos); + if (node.mesh.cmpxchgStrong(null, mesh, .release, .monotonic) != null) { + return error.AlreadyStored; + } + node.finishedMeshing = mesh.finishedMeshing; + updateHigherLodNodeFinishedMeshing(mesh.pos, mesh.finishedMeshing); } pub fn finishMesh(pos: chunk.ChunkPosition) void { - mutex.lock(); - defer mutex.unlock(); - updatableList.append(main.globalAllocator, pos); + mutex.lock(); + defer mutex.unlock(); + updatableList.append(main.globalAllocator, pos); } // MARK: updaters pub fn updateBlock(blockUpdate: BlockUpdate) void { - const pos = chunk.ChunkPosition{ .wx = blockUpdate.pos[0], .wy = blockUpdate.pos[1], .wz = blockUpdate.pos[2], .voxelSize = 1 }; - if (getMesh(pos)) |mesh| { - mesh.updateBlock(blockUpdate); - } // TODO: It seems like we simply ignore the block update if we don't have the mesh yet. + const pos = chunk.ChunkPosition{.wx = blockUpdate.pos[0], .wy = blockUpdate.pos[1], .wz = blockUpdate.pos[2], .voxelSize = 1}; + if (getMesh(pos)) |mesh| { + mesh.updateBlock(blockUpdate); + } // TODO: It seems like we simply ignore the block update if we don't have the mesh yet. } pub fn updateLightMap(map: *LightMap.LightMapFragment) void { - mapUpdatableList.pushBack(map); + mapUpdatableList.pushBack(map); } // MARK: Block breaking animation pub fn addBreakingAnimation(pos: Vec3i, breakingProgress: f32) void { - const animationFrame: usize = @trunc(breakingProgress * @as(f32, @floatFromInt(main.blocks.meshes.blockBreakingTextures.items.len))); - const texture = main.blocks.meshes.blockBreakingTextures.items[animationFrame]; - - const block = getBlockFromRenderThread(pos[0], pos[1], pos[2]) orelse return; - const model = main.blocks.meshes.model(block).model(); - - for (model.internalQuads) |quadIndex| { - addBreakingAnimationFace(pos, quadIndex, texture, null, block.transparent()); - } - for (&model.neighborFacingQuads, 0..) |quads, n| { - for (quads) |quadIndex| { - addBreakingAnimationFace(pos, quadIndex, texture, @enumFromInt(n), block.transparent()); - } - } + const animationFrame: usize = @trunc(breakingProgress*@as(f32, @floatFromInt(main.blocks.meshes.blockBreakingTextures.items.len))); + const texture = main.blocks.meshes.blockBreakingTextures.items[animationFrame]; + + const block = getBlockFromRenderThread(pos[0], pos[1], pos[2]) orelse return; + const model = main.blocks.meshes.model(block).model(); + + for (model.internalQuads) |quadIndex| { + addBreakingAnimationFace(pos, quadIndex, texture, null, block.transparent()); + } + for (&model.neighborFacingQuads, 0..) |quads, n| { + for (quads) |quadIndex| { + addBreakingAnimationFace(pos, quadIndex, texture, @enumFromInt(n), block.transparent()); + } + } } fn addBreakingAnimationFace(pos: Vec3i, quadIndex: main.models.QuadIndex, texture: u16, neighbor: ?chunk.Neighbor, isTransparent: bool) void { - const worldPos = pos +% if (neighbor) |n| n.relPos() else Vec3i{ 0, 0, 0 }; - const relPos = worldPos & @as(Vec3i, @splat(main.chunk.chunkMask)); - const mesh = getMesh(.{ .wx = worldPos[0], .wy = worldPos[1], .wz = worldPos[2], .voxelSize = 1 }) orelse return; - mesh.mutex.lock(); - defer mesh.mutex.unlock(); - const lightIndex = blk: { - mesh.meshUploadMutex.lock(); - defer mesh.meshUploadMutex.unlock(); - const meshData = if (isTransparent) &mesh.transparentMesh else &mesh.opaqueMesh; - for (meshData.completeList.getEverything()) |face| { - if (face.position.x == relPos[0] and face.position.y == relPos[1] and face.position.z == relPos[2] and face.blockAndQuad.quadIndex == quadIndex) { - break :blk face.position.lightIndex; - } - } - // The face doesn't exist. - return; - }; - mesh.blockBreakingFacesChanged = true; - mesh.blockBreakingFaces.append(.{ - .position = .{ - .x = @intCast(relPos[0]), - .y = @intCast(relPos[1]), - .z = @intCast(relPos[2]), - .isBackFace = false, - .lightIndex = lightIndex, - }, - .blockAndQuad = .{ - .texture = texture, - .quadIndex = quadIndex, - }, - }); + const worldPos = pos +% if (neighbor) |n| n.relPos() else Vec3i{0, 0, 0}; + const relPos = worldPos & @as(Vec3i, @splat(main.chunk.chunkMask)); + const mesh = getMesh(.{.wx = worldPos[0], .wy = worldPos[1], .wz = worldPos[2], .voxelSize = 1}) orelse return; + mesh.mutex.lock(); + defer mesh.mutex.unlock(); + const lightIndex = blk: { + mesh.meshUploadMutex.lock(); + defer mesh.meshUploadMutex.unlock(); + const meshData = if (isTransparent) &mesh.transparentMesh else &mesh.opaqueMesh; + for (meshData.completeList.getEverything()) |face| { + if (face.position.x == relPos[0] and face.position.y == relPos[1] and face.position.z == relPos[2] and face.blockAndQuad.quadIndex == quadIndex) { + break :blk face.position.lightIndex; + } + } + // The face doesn't exist. + return; + }; + mesh.blockBreakingFacesChanged = true; + mesh.blockBreakingFaces.append(.{ + .position = .{ + .x = @intCast(relPos[0]), + .y = @intCast(relPos[1]), + .z = @intCast(relPos[2]), + .isBackFace = false, + .lightIndex = lightIndex, + }, + .blockAndQuad = .{ + .texture = texture, + .quadIndex = quadIndex, + }, + }); } fn removeBreakingAnimationFace(pos: Vec3i, quadIndex: main.models.QuadIndex, neighbor: ?chunk.Neighbor) void { - const worldPos = pos +% if (neighbor) |n| n.relPos() else Vec3i{ 0, 0, 0 }; - const relPos = worldPos & @as(Vec3i, @splat(main.chunk.chunkMask)); - const mesh = getMesh(.{ .wx = worldPos[0], .wy = worldPos[1], .wz = worldPos[2], .voxelSize = 1 }) orelse return; - for (mesh.blockBreakingFaces.items, 0..) |face, i| { - if (face.position.x == relPos[0] and face.position.y == relPos[1] and face.position.z == relPos[2] and face.blockAndQuad.quadIndex == quadIndex) { - _ = mesh.blockBreakingFaces.swapRemove(i); - mesh.blockBreakingFacesChanged = true; - break; - } - } + const worldPos = pos +% if (neighbor) |n| n.relPos() else Vec3i{0, 0, 0}; + const relPos = worldPos & @as(Vec3i, @splat(main.chunk.chunkMask)); + const mesh = getMesh(.{.wx = worldPos[0], .wy = worldPos[1], .wz = worldPos[2], .voxelSize = 1}) orelse return; + for (mesh.blockBreakingFaces.items, 0..) |face, i| { + if (face.position.x == relPos[0] and face.position.y == relPos[1] and face.position.z == relPos[2] and face.blockAndQuad.quadIndex == quadIndex) { + _ = mesh.blockBreakingFaces.swapRemove(i); + mesh.blockBreakingFacesChanged = true; + break; + } + } } pub fn removeBreakingAnimation(pos: Vec3i) void { - const block = getBlockFromRenderThread(pos[0], pos[1], pos[2]) orelse return; - const model = main.blocks.meshes.model(block).model(); - - for (model.internalQuads) |quadIndex| { - removeBreakingAnimationFace(pos, quadIndex, null); - } - for (&model.neighborFacingQuads, 0..) |quads, n| { - for (quads) |quadIndex| { - removeBreakingAnimationFace(pos, quadIndex, @enumFromInt(n)); - } - } + const block = getBlockFromRenderThread(pos[0], pos[1], pos[2]) orelse return; + const model = main.blocks.meshes.model(block).model(); + + for (model.internalQuads) |quadIndex| { + removeBreakingAnimationFace(pos, quadIndex, null); + } + for (&model.neighborFacingQuads, 0..) |quads, n| { + for (quads) |quadIndex| { + removeBreakingAnimationFace(pos, quadIndex, @enumFromInt(n)); + } + } } From 26c72114f0cbd6626895a3a20a6738ff624274ec Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Fri, 17 Jul 2026 17:52:55 -0400 Subject: [PATCH 30/33] Properly implement frustum culling and fix the culling problems (yay!) --- src/renderer.zig | 26 +++++++- src/renderer/chunk_meshing.zig | 2 +- src/renderer/mesh_storage.zig | 6 +- src/vec.zig | 107 ++++++++++++++++++++++++++++++++- 4 files changed, 135 insertions(+), 6 deletions(-) diff --git a/src/renderer.zig b/src/renderer.zig index 87b47d893e..4dac82317e 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -222,11 +222,13 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo const lightView: Mat4f = Mat4f.identity().mul(.translation(lightOffset)); + const depthFrustum = Frustum.initFromView(lightProjection.mul(lightView), playerPos); + game.camera.updateViewMatrix(); chunk_meshing.quadsDrawn = 0; chunk_meshing.transparentQuadsDrawn = 0; - const depthMeshes = mesh_storage.updateAndGetRenderChunks(world.conn, null, playerPos, settings.renderDistance); + const depthMeshes = mesh_storage.updateAndGetRenderChunks(world.conn, &depthFrustum, playerPos, settings.renderDistance); gpu_performance_measuring.startQuery(.depth_framebuffer_chunk_rendering_preparation); chunk_meshing.beginRender(); @@ -907,6 +909,7 @@ pub const Frustum = struct { // MARK: Frustum norm: Vec3f, }; planes: [4]Plane, // Who cares about the near/far plane anyways? + isPerspective: bool, pub fn init(cameraPos: Vec3f, rotationMatrix: Mat4f, fovY: f32, width: u31, height: u31) Frustum { const invRotationMatrix = rotationMatrix.transpose(); @@ -922,6 +925,27 @@ pub const Frustum = struct { // MARK: Frustum self.planes[1] = Plane{.pos = cameraPos, .norm = vec.cross(cameraDir - cameraRight*@as(Vec3f, @splat(halfHSide)), cameraUp)}; // left self.planes[2] = Plane{.pos = cameraPos, .norm = vec.cross(cameraRight, cameraDir - cameraUp*@as(Vec3f, @splat(halfVSide)))}; // top self.planes[3] = Plane{.pos = cameraPos, .norm = vec.cross(cameraDir + cameraUp*@as(Vec3f, @splat(halfVSide)), cameraRight)}; // bottom + self.isPerspective = true; + return self; + } + + pub fn initFromView(view: Mat4f, playerPos: Vec3d) Frustum { + const invView = view.inverse(); + var planes: [4]Plane = undefined; + planes[0] = Plane{.pos = .{0, 0, 0}, .norm = .{1, 0, 0}}; + planes[1] = Plane{.pos = .{0, 0, 0}, .norm = .{0, 1, 0}}; + planes[2] = Plane{.pos = .{1, 0, 0}, .norm = .{-1, 0, 0}}; + planes[3] = Plane{.pos = .{0, 1, 0}, .norm = .{0, -1, 0}}; + + var self: Frustum = undefined; + inline for (0.., planes) |i, plane| { + const newPos = vec.xyz(invView.mulVec(vec.combine(plane.pos, 1.0))); + const normPos = plane.pos + plane.norm; + const newNormPos = vec.xyz(invView.mulVec(vec.combine(normPos, 1.0))); + const newNorm = vec.normalize(newNormPos - newPos); + self.planes[i] = Plane{.pos = @as(Vec3f, @floatCast(playerPos)) + newPos, .norm = newNorm}; + } + self.isPerspective = false; return self; } diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index a8b3e85725..36f1665546 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -127,7 +127,7 @@ pub fn init() void { &depthUniforms, graphics.VertexArray.EmptyVertex, &.{}, - .{.cullMode = .back}, + .{.cullMode = .none}, .{.depthTest = true, .depthWrite = true, .depthCompare = .lessOrEqual}, .{.attachments = &.{.noBlending}}, ); diff --git a/src/renderer/mesh_storage.zig b/src/renderer/mesh_storage.zig index 09a4e7e4c7..2a5d91ba29 100644 --- a/src/renderer/mesh_storage.zig +++ b/src/renderer/mesh_storage.zig @@ -544,7 +544,7 @@ fn createNewMeshes(olderPx: i32, olderPy: i32, olderPz: i32, olderRD: u16, meshR } } -pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: ?*const main.renderer.Frustum, playerPos: Vec3d, renderDistance: u16) []*chunk_meshing.ChunkMesh { // MARK: updateAndGetRenderChunks() +pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: *const main.renderer.Frustum, playerPos: Vec3d, renderDistance: u16) []*chunk_meshing.ChunkMesh { // MARK: updateAndGetRenderChunks() meshList.clearRetainingCapacity(); const playerPosInt: Vec3i = @floor(playerPos); @@ -624,7 +624,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: ?*c const node2 = getNodePointer(neighborPos); if (!node2.active and node2.finishedMeshing) { const relPosFloat: Vec3f = @floatCast(@as(Vec3d, @floatFromInt(Vec3i{pos.wx, pos.wy, pos.wz})) - playerPos); - if (frustum != null and !frustum.?.testAAB(relPosFloat + @as(Vec3f, @floatFromInt(neighbor.relPos()*chunkSizeVector)), @floatFromInt(chunkSizeVector))) continue; + if (!frustum.testAAB(relPosFloat + @as(Vec3f, @floatFromInt(neighbor.relPos()*chunkSizeVector)), @floatFromInt(chunkSizeVector))) continue; node2.active = true; node2.rendered = true; searchList.pushBack(node2); @@ -650,7 +650,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: ?*c if (dz == 1) nextPos.wz ^= lowerLodBit; const node2 = getNodePointer(nextPos); const relNextPos: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{nextPos.wx, nextPos.wy, nextPos.wz})) - playerPos; - if (frustum != null and !frustum.?.testAAB(@floatCast(relNextPos), @splat(@floatFromInt(chunk.chunkSize*nextPos.voxelSize)))) + if (frustum.isPerspective and !frustum.testAAB(@floatCast(relNextPos), @splat(@floatFromInt(chunk.chunkSize*nextPos.voxelSize)))) continue; std.debug.assert(node2.finishedMeshing); node2.active = true; diff --git a/src/vec.zig b/src/vec.zig index a62bc52fc7..c14fc31ee9 100644 --- a/src/vec.zig +++ b/src/vec.zig @@ -244,7 +244,7 @@ pub const Mat4f = struct { // MARK: Mat4f return @as(T, @bitCast(v0u & v1u)); // andps } - // copied from zmath library (MIT Liscence) : https://github.com/zig-gamedev/zmath/blob/3a5955b2b72cd081563fbb084eff05bffd1e3fbb/src/root.zig#L2726 + // copied from zmath library (MIT License) : https://github.com/zig-gamedev/zmath/blob/3a5955b2b72cd081563fbb084eff05bffd1e3fbb/src/root.zig#L2726 pub fn rotationQuat(quat: Quat) Mat4f { const f32x4_mask3: Vec4f = Vec4f{ @as(f32, @bitCast(@as(u32, 0xffff_ffff))), @@ -296,6 +296,111 @@ pub const Mat4f = struct { // MARK: Mat4f return m; } + // copied from zmath library (MIT License) : https://github.com/zig-gamedev/zmath/blob/3a5955b2b72cd081563fbb084eff05bffd1e3fbb/src/root.zig#L2536 + pub fn inverse(m: Mat4f) Mat4f { + const mt = transpose(m); + var v0: [4]Vec4f = undefined; + var v1: [4]Vec4f = undefined; + + v0[0] = swizzle(mt.rows[2], .x, .x, .y, .y); + v1[0] = swizzle(mt.rows[3], .z, .w, .z, .w); + v0[1] = swizzle(mt.rows[0], .x, .x, .y, .y); + v1[1] = swizzle(mt.rows[1], .z, .w, .z, .w); + v0[2] = @shuffle(f32, mt.rows[2], mt.rows[0], [4]i32{ 0, 2, ~@as(i32, 0), ~@as(i32, 2) }); + v1[2] = @shuffle(f32, mt.rows[3], mt.rows[1], [4]i32{ 1, 3, ~@as(i32, 1), ~@as(i32, 3) }); + + var d0 = v0[0] * v1[0]; + var d1 = v0[1] * v1[1]; + var d2 = v0[2] * v1[2]; + + v0[0] = swizzle(mt.rows[2], .z, .w, .z, .w); + v1[0] = swizzle(mt.rows[3], .x, .x, .y, .y); + v0[1] = swizzle(mt.rows[0], .z, .w, .z, .w); + v1[1] = swizzle(mt.rows[1], .x, .x, .y, .y); + v0[2] = @shuffle(f32, mt.rows[2], mt.rows[0], [4]i32{ 1, 3, ~@as(i32, 1), ~@as(i32, 3) }); + v1[2] = @shuffle(f32, mt.rows[3], mt.rows[1], [4]i32{ 0, 2, ~@as(i32, 0), ~@as(i32, 2) }); + + d0 = -v0[0] * v1[0] + d0; + d1 = -v0[1] * v1[1] + d1; + d2 = -v0[2] * v1[2] + d2; + + v0[0] = swizzle(mt.rows[1], .y, .z, .x, .y); + v1[0] = @shuffle(f32, d0, d2, [4]i32{ ~@as(i32, 1), 1, 3, 0 }); + v0[1] = swizzle(mt.rows[0], .z, .x, .y, .x); + v1[1] = @shuffle(f32, d0, d2, [4]i32{ 3, ~@as(i32, 1), 1, 2 }); + v0[2] = swizzle(mt.rows[3], .y, .z, .x, .y); + v1[2] = @shuffle(f32, d1, d2, [4]i32{ ~@as(i32, 3), 1, 3, 0 }); + v0[3] = swizzle(mt.rows[2], .z, .x, .y, .x); + v1[3] = @shuffle(f32, d1, d2, [4]i32{ 3, ~@as(i32, 3), 1, 2 }); + + var c0 = v0[0] * v1[0]; + var c2 = v0[1] * v1[1]; + var c4 = v0[2] * v1[2]; + var c6 = v0[3] * v1[3]; + + v0[0] = swizzle(mt.rows[1], .z, .w, .y, .z); + v1[0] = @shuffle(f32, d0, d2, [4]i32{ 3, 0, 1, ~@as(i32, 0) }); + v0[1] = swizzle(mt.rows[0], .w, .z, .w, .y); + v1[1] = @shuffle(f32, d0, d2, [4]i32{ 2, 1, ~@as(i32, 0), 0 }); + v0[2] = swizzle(mt.rows[3], .z, .w, .y, .z); + v1[2] = @shuffle(f32, d1, d2, [4]i32{ 3, 0, 1, ~@as(i32, 2) }); + v0[3] = swizzle(mt.rows[2], .w, .z, .w, .y); + v1[3] = @shuffle(f32, d1, d2, [4]i32{ 2, 1, ~@as(i32, 2), 0 }); + + c0 = -v0[0] * v1[0] + c0; + c2 = -v0[1] * v1[1] + c2; + c4 = -v0[2] * v1[2] + c4; + c6 = -v0[3] * v1[3] + c6; + + v0[0] = swizzle(mt.rows[1], .w, .x, .w, .x); + v1[0] = @shuffle(f32, d0, d2, [4]i32{ 2, ~@as(i32, 1), ~@as(i32, 0), 2 }); + v0[1] = swizzle(mt.rows[0], .y, .w, .x, .z); + v1[1] = @shuffle(f32, d0, d2, [4]i32{ ~@as(i32, 1), 0, 3, ~@as(i32, 0) }); + v0[2] = swizzle(mt.rows[3], .w, .x, .w, .x); + v1[2] = @shuffle(f32, d1, d2, [4]i32{ 2, ~@as(i32, 3), ~@as(i32, 2), 2 }); + v0[3] = swizzle(mt.rows[2], .y, .w, .x, .z); + v1[3] = @shuffle(f32, d1, d2, [4]i32{ ~@as(i32, 3), 0, 3, ~@as(i32, 2) }); + + const c1 = -v0[0] * v1[0] + c0; + const c3 = v0[1] * v1[1] + c2; + const c5 = -v0[2] * v1[2] + c4; + const c7 = v0[3] * v1[3] + c6; + + c0 = v0[0] * v1[0] + c0; + c2 = -v0[1] * v1[1] + c2; + c4 = v0[2] * v1[2] + c4; + c6 = -v0[3] * v1[3] + c6; + + var mr = Mat4f{ + .rows = [4]Vec4f{ + Vec4f{c0[0], c1[1], c0[2], c1[3]}, + Vec4f{c2[0], c3[1], c2[2], c3[3]}, + Vec4f{c4[0], c5[1], c4[2], c5[3]}, + Vec4f{c6[0], c7[1], c6[2], c7[3]}, + } + }; + + const det = dot(mr.rows[0], mt.rows[0]); + + if (std.math.approxEqAbs(f64, det, 0.0, std.math.floatEps(f64))) { + return Mat4f{ + .rows = [4]Vec4f{ + Vec4f{0.0, 0.0, 0.0, 0.0}, + Vec4f{0.0, 0.0, 0.0, 0.0}, + Vec4f{0.0, 0.0, 0.0, 0.0}, + Vec4f{0.0, 0.0, 0.0, 0.0}, + } + }; + } + + const scaleFactor = @as(Vec4f, @splat(1.0 / det)); + mr.rows[0] *= scaleFactor; + mr.rows[1] *= scaleFactor; + mr.rows[2] *= scaleFactor; + mr.rows[3] *= scaleFactor; + return mr; + } + pub fn perspective(fovY: f32, aspect: f32, near: f32, far: f32) Mat4f { // zig fmt: off const tanY = std.math.tan(fovY*0.5); const tanX = aspect*tanY; From 30b78f92ae71147dcbe2d4090d2bec2c5e6a32a2 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Fri, 17 Jul 2026 17:55:04 -0400 Subject: [PATCH 31/33] Add visibilityStateDepth to chunk_data.glsl --- assets/cubyz/shaders/include/chunk_data.glsl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assets/cubyz/shaders/include/chunk_data.glsl b/assets/cubyz/shaders/include/chunk_data.glsl index 996e30403f..ae01791ab1 100644 --- a/assets/cubyz/shaders/include/chunk_data.glsl +++ b/assets/cubyz/shaders/include/chunk_data.glsl @@ -10,6 +10,8 @@ struct ChunkData { uint vertexCountTransparent; uint visibilityState; uint oldVisibilityState; + uint visibilityStateDepth; + uint oldVisibilityStateDepth; }; layout(std430, binding = 6) buffer _chunks From c934147380bf2d32c53af682e0809c74330d9954 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Fri, 17 Jul 2026 17:55:59 -0400 Subject: [PATCH 32/33] Add back isDepth --- assets/cubyz/shaders/chunks/occlusionTestFragment.frag | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assets/cubyz/shaders/chunks/occlusionTestFragment.frag b/assets/cubyz/shaders/chunks/occlusionTestFragment.frag index 00aa81baf2..97b0fdcf59 100644 --- a/assets/cubyz/shaders/chunks/occlusionTestFragment.frag +++ b/assets/cubyz/shaders/chunks/occlusionTestFragment.frag @@ -6,6 +6,8 @@ layout(location = 0) flat in uint chunkID; #include "chunk_data.glsl" +layout(location = 4) uniform bool isDepth; + void main() { if(isDepth) { chunks[chunkID].visibilityStateDepth = 1; From 19c974f86d2a0b2c06ec9aff9448a0da3b546920 Mon Sep 17 00:00:00 2001 From: codemob-dev <69110900+codemob-dev@users.noreply.github.com> Date: Fri, 17 Jul 2026 20:37:34 -0400 Subject: [PATCH 33/33] Redo the pixel perfect system --- .../cubyz/shaders/chunks/chunk_fragment.frag | 32 ++++--------------- assets/cubyz/shaders/chunks/chunk_vertex.vert | 32 ++++++++++++++++++- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.frag b/assets/cubyz/shaders/chunks/chunk_fragment.frag index 15410cd7c4..c105967436 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_fragment.frag @@ -11,6 +11,8 @@ layout(location = 7) flat in int textureIndex; layout(location = 8) flat in int isBackFace; layout(location = 9) flat in float distanceForLodCheck; layout(location = 10) flat in int opaqueInLod; +layout(location = 11) flat in mat4 worldToQuad; +layout(location = 15) flat in mat3 uvTransform; layout(location = 0) out vec4 fragColor; @@ -63,37 +65,17 @@ vec4 fixedCubeMapLookup(vec3 v) { // Taken from http://the-witness.net/news/2012 float shadowCalculation() { if (dot(lightDir, normal) > 0.0) return 1.0; - vec3 dx = dFdx(shadowPos); - vec3 dy = dFdy(shadowPos); - vec2 duv_dx = dFdx(uv); - vec2 duv_dy = dFdy(uv); + vec3 shadowPosUV = uvTransform * (worldToQuad * lightViewMatrix * vec4(shadowPos, 1.0)).xyz; + shadowPosUV.xy = ceil(shadowPosUV.xy * 16.0) / 16.0; + vec4 shadowPosSnapped = inverse(worldToQuad) * vec4(inverse(uvTransform) * shadowPosUV, 1.0); - vec2 texSize = vec2(16.0); - vec2 texelFrac = fract(uv * texSize); - vec2 texelOffset = (0.5 - texelFrac) / texSize; - - mat2 uvGrad = mat2(duv_dx, duv_dy); - - mat2 invUvGrad = inverse(uvGrad); - - vec2 screenOffset = invUvGrad * texelOffset; - - vec3 offset = - dx * screenOffset.x + - dy * screenOffset.y; - - vec3 snappedShadowPos = shadowPos + offset; - - vec4 lightPos = - lightProjectionMatrix * - lightViewMatrix * - vec4(snappedShadowPos, 1.0); + vec4 lightPos = lightProjectionMatrix * shadowPosSnapped; vec3 projCoords = lightPos.xyz; projCoords = projCoords * 0.5 + 0.5; float closestDepth = texture(shadowMap, projCoords.xy).r; float currentDepth = projCoords.z; - currentDepth += 0.00018; + currentDepth += 0.0001; float shadow = currentDepth > closestDepth ? 1.0 : 0.0; if(projCoords.z > 1.0) { shadow = 0.0; diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vert b/assets/cubyz/shaders/chunks/chunk_vertex.vert index 402a7c8ad6..a55a4fc86a 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vert +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vert @@ -11,6 +11,8 @@ layout(location = 7) flat out int textureIndex; layout(location = 8) flat out int isBackFace; layout(location = 9) flat out float distanceForLodCheck; layout(location = 10) flat out int opaqueInLod; +layout(location = 11) flat out mat4 worldToQuad; +layout(location = 15) flat out mat3 uvTransform; layout(location = 0) uniform vec3 ambientLight; layout(location = 1) uniform mat4 projectionMatrix; @@ -107,5 +109,33 @@ void main() { uv = quads[quadIndex].cornerUV[vertexID]*voxelSize; opaqueInLod = quads[quadIndex].opaqueInLod; - shadowPos = position + normal * 0.04; + shadowPos = position + normal * 0.03; + + vec3 p0 = vec3(quads[quadIndex].corners[0][0], quads[quadIndex].corners[0][1], quads[quadIndex].corners[0][2]); + vec3 p1 = vec3(quads[quadIndex].corners[1][0], quads[quadIndex].corners[1][1], quads[quadIndex].corners[1][2]); + vec3 p3 = vec3(quads[quadIndex].corners[3][0], quads[quadIndex].corners[3][1], quads[quadIndex].corners[3][2]); + + vec2 uv0 = vec2(quads[quadIndex].cornerUV[0][0], quads[quadIndex].cornerUV[0][1]); + vec2 uv1 = vec2(quads[quadIndex].cornerUV[1][0], quads[quadIndex].cornerUV[1][1]); + vec2 uv3 = vec2(quads[quadIndex].cornerUV[3][0], quads[quadIndex].cornerUV[3][1]); + + vec3 u = p1 - p0; + vec3 v = p3 - p0; + mat3 invBasis = inverse(mat3(u, v, cross(u,v))); + + vec2 du = uv1 - uv0; + vec2 dv = uv3 - uv0; + + uvTransform = mat3( + vec3(du, 0.0), + vec3(dv, 0.0), + vec3(uv0, 1.0) + ); + + worldToQuad = mat4( + vec4(invBasis[0], 0), + vec4(invBasis[1], 0), + vec4(invBasis[2], 0), + vec4(-(invBasis * p0),1) + ); }