From 5ca4ab6a9d92d9be1b79fde680b0b93915b0d797 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sun, 16 Jul 2023 14:09:18 +0200 Subject: [PATCH] Fake reflections and transparency (both absorptive and regular transparency are possible) --- assets/cubyz/shaders/block_fragment.fs | 48 --- .../cubyz/shaders/block_selection_fragment.fs | 3 - assets/cubyz/shaders/block_vertex.vs | 49 --- assets/cubyz/shaders/chunks/chunk_fragment.fs | 9 +- .../shaders/chunks/transparent_fragment.fs | 190 +++++++++ assets/cubyz/shaders/deferred_render_pass.fs | 1 - assets/cubyz/shaders/fog_fragment.fs | 3 +- assets/cubyz/shaders/item_drop.fs | 3 - assets/cubyz/shaders/transparent_fragment.fs | 66 --- assets/cubyz/shaders/transparent_vertex.vs | 49 --- src/chunk.zig | 397 +++++++++++++----- src/renderer.zig | 33 +- 12 files changed, 494 insertions(+), 357 deletions(-) delete mode 100644 assets/cubyz/shaders/block_fragment.fs delete mode 100644 assets/cubyz/shaders/block_vertex.vs create mode 100644 assets/cubyz/shaders/chunks/transparent_fragment.fs delete mode 100644 assets/cubyz/shaders/transparent_fragment.fs delete mode 100644 assets/cubyz/shaders/transparent_vertex.vs diff --git a/assets/cubyz/shaders/block_fragment.fs b/assets/cubyz/shaders/block_fragment.fs deleted file mode 100644 index f30aeaa1..00000000 --- a/assets/cubyz/shaders/block_fragment.fs +++ /dev/null @@ -1,48 +0,0 @@ -#version 430 - -in vec2 outTexCoord; -flat in float textureIndex; -in vec3 outColor; -in vec3 mvVertexPos; - -layout(location = 0) out vec4 fragColor; -layout(location = 1) out vec4 position; - -struct Fog { - bool activ; - vec3 color; - float density; -}; - -uniform sampler2DArray texture_sampler; -uniform sampler2DArray emissionSampler; -uniform Fog fog; -uniform int selectedIndex; - -vec4 ambientC; - -void setupColors(vec3 textCoord) { - ambientC = texture(texture_sampler, textCoord); -} - -vec4 calcFog(vec3 pos, vec4 color, Fog fog) { - float distance = length(pos); - float fogFactor = 1.0/exp((distance*fog.density)*(distance*fog.density)); - fogFactor = clamp(fogFactor, 0.0, 1.0); - vec3 resultColor = mix(fog.color, color.xyz, fogFactor); - return vec4(resultColor.xyz, color.w + 1 - fogFactor); -} - -void main() { - setupColors(vec3(outTexCoord, textureIndex)); - if (ambientC.a != 1) discard; - - fragColor = ambientC*vec4(outColor, 1); - fragColor.rgb += texture(emissionSampler, vec3(outTexCoord, textureIndex)).rgb; - - if (fog.activ) { - fragColor = calcFog(mvVertexPos, fragColor, fog); - } - fragColor.rgb /= 4; - position = vec4(mvVertexPos, 1); -} diff --git a/assets/cubyz/shaders/block_selection_fragment.fs b/assets/cubyz/shaders/block_selection_fragment.fs index 9c726eeb..508e298f 100644 --- a/assets/cubyz/shaders/block_selection_fragment.fs +++ b/assets/cubyz/shaders/block_selection_fragment.fs @@ -3,11 +3,8 @@ in vec3 mvVertexPos; layout(location = 0) out vec4 fragColor; -layout(location = 1) out vec4 position; void main() { fragColor = vec4(0, 0, 0, 1); fragColor.rgb /= 4; - - position = vec4(mvVertexPos, 1); } diff --git a/assets/cubyz/shaders/block_vertex.vs b/assets/cubyz/shaders/block_vertex.vs deleted file mode 100644 index 852bb4a7..00000000 --- a/assets/cubyz/shaders/block_vertex.vs +++ /dev/null @@ -1,49 +0,0 @@ -#version 430 - -layout (location=0) in vec3 position; -layout (location=1) in vec3 texCoord; -layout (location=2) in vec3 vertexNormal; -layout (location=3) in int easyLight; - -out vec2 outTexCoord; -flat out float textureIndex; -out vec3 mvVertexPos; -out vec3 outColor; - -uniform mat4 projectionMatrix; -uniform vec3 ambientLight; -uniform vec3 directionalLight; -uniform mat4 viewMatrix; -uniform vec3 modelPosition; - -layout(std430, binding = 0) buffer _animationTimes -{ - int animationTimes[]; -}; -layout(std430, binding = 1) buffer _animationFrames -{ - int animationFrames[]; -}; - -uniform int time; - -vec3 calcLight(int srgb) { - float s = (srgb >> 24) & 255; - float r = (srgb >> 16) & 255; - float g = (srgb >> 8) & 255; - float b = (srgb >> 0) & 255; - s = s*(1 - dot(directionalLight, vertexNormal)); - r = max(s*ambientLight.x, r); - g = max(s*ambientLight.y, g); - b = max(s*ambientLight.z, b); - return vec3(r, g, b); -} - -void main() { - outColor = calcLight(easyLight)*0.003890625; - vec4 mvPos = viewMatrix*vec4(position + modelPosition, 1); - gl_Position = projectionMatrix*mvPos; - outTexCoord = texCoord.xy; - textureIndex = texCoord.z + time / animationTimes[int(texCoord.z)] % animationFrames[int(texCoord.z)]; - mvVertexPos = mvPos.xyz; -} diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.fs b/assets/cubyz/shaders/chunks/chunk_fragment.fs index 2b38483d..1525d746 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.fs +++ b/assets/cubyz/shaders/chunks/chunk_fragment.fs @@ -14,7 +14,6 @@ uniform sampler2DArray texture_sampler; uniform sampler2DArray emissionSampler; layout(location = 0) out vec4 fragColor; -layout(location = 1) out vec4 position; struct Fog { bool activ; @@ -205,13 +204,8 @@ void main() { ivec2 textureCoords = getTextureCoords(result.voxelPosition, result.textureDir); fragColor = mipMapSample(texture_sampler, textureCoords, textureIndex, lod)*vec4(ambientLight*normalVariation, 1); - if (fragColor.a <= 0.1f) fragColor.a = 1; // TODO: Proper alpha handling. + if (fragColor.a < 1) discard; - if (fog.activ) { - - // Underwater fog in lod(assumes that the fog is maximal): - fragColor = vec4((1 - fragColor.a) * waterFog.color.xyz + fragColor.a * fragColor.xyz, 1); - } fragColor.rgb += mipMapSample(emissionSampler, textureCoords, textureIndex, lod).rgb; if (fog.activ) { @@ -219,7 +213,6 @@ void main() { } if(!renderedToItemTexture) { fragColor.rgb /= 4; - position = vec4(mvVertexPos, 1); } // TODO: Update the depth. } diff --git a/assets/cubyz/shaders/chunks/transparent_fragment.fs b/assets/cubyz/shaders/chunks/transparent_fragment.fs new file mode 100644 index 00000000..6e13390f --- /dev/null +++ b/assets/cubyz/shaders/chunks/transparent_fragment.fs @@ -0,0 +1,190 @@ +#version 430 + +in vec3 mvVertexPos; +flat in int blockType; +flat in int faceNormal; +flat in int modelIndex; +in vec3 startPosition; +in vec3 direction; + +uniform int time; +uniform vec3 ambientLight; +uniform sampler2DArray texture_sampler; +uniform sampler2DArray emissionSampler; + +layout (location = 0, index = 0) out vec4 fragColor; +layout (location = 0, index = 1) out vec4 blendColor; + +struct Fog { + bool activ; + vec3 color; + float density; +}; + +struct AnimationData { + int frames; + int time; +}; + +layout(std430, binding = 0) buffer _animation +{ + AnimationData animation[]; +}; +layout(std430, binding = 1) buffer _textureIndices +{ + int textureIndices[][6]; +}; + + +const float[6] normalVariations = float[6]( + 1.0, //vec3(0, 1, 0), + 0.84, //vec3(0, -1, 0), + 0.92, //vec3(1, 0, 0), + 0.92, //vec3(-1, 0, 0), + 0.96, //vec3(0, 0, 1), + 0.88 //vec3(0, 0, -1) +); +const vec3[6] normals = vec3[6]( + vec3(0, 1, 0), + vec3(0, -1, 0), + vec3(1, 0, 0), + vec3(-1, 0, 0), + vec3(0, 0, 1), + vec3(0, 0, -1) +); + + +uniform Fog fog; +uniform Fog waterFog; // TODO: Select fog from texture +uniform bool renderedToItemTexture; + +vec4 calcFog(vec3 pos, vec4 color, Fog fog) { + float distance = length(pos); + float fogFactor = 1.0/exp((distance*fog.density)*(distance*fog.density)); + fogFactor = clamp(fogFactor, 0.0, 1.0); + vec4 resultColor = mix(vec4(fog.color, 1), color, fogFactor); + return resultColor; +} + +ivec2 getTextureCoords(ivec3 voxelPosition, int textureDir) { + switch(textureDir) { + case 0: + return ivec2(15 - voxelPosition.x, voxelPosition.z); + case 1: + return ivec2(voxelPosition.x, voxelPosition.z); + case 2: + return ivec2(15 - voxelPosition.z, voxelPosition.y); + case 3: + return ivec2(voxelPosition.z, voxelPosition.y); + case 4: + return ivec2(voxelPosition.x, voxelPosition.y); + case 5: + return ivec2(15 - voxelPosition.x, voxelPosition.y); + } +} + +float getLod(ivec3 voxelPosition, int normal, vec3 direction, float variance) { + return max(0, min(4, log2(variance*length(direction)/abs(dot(vec3(normals[normal]), direction))))); +} + +float perpendicularFwidth(vec3 direction) { // Estimates how big fwidth would be if the fragment normal was perpendicular to the light direction. + vec3 variance = dFdx(direction); + variance += direction; + variance = variance*length(direction)/length(variance); + variance -= direction; + return 16*length(variance); +} + +vec4 mipMapSample(sampler2DArray texture, ivec2 textureCoords, int textureIndex, float lod) { // TODO: anisotropic filtering? + int lowerLod = int(floor(lod)); + int higherLod = lowerLod+1; + float interpolation = lod - lowerLod; + vec4 lower = texelFetch(texture, ivec3(textureCoords >> lowerLod, textureIndex), lowerLod); + vec4 higher = texelFetch(texture, ivec3(textureCoords >> higherLod, textureIndex), higherLod); + return higher*interpolation + (1 - interpolation)*lower; +} + + +ivec3 random3to3(ivec3 v) { + v &= 15; + ivec3 fac = ivec3(11248723, 105436839, 45399083); + int seed = v.x*fac.x ^ v.y*fac.y ^ v.z*fac.z; + v = seed*fac; + return v; +} + +float snoise(vec3 v){ // TODO: Maybe use a cubemap. + const vec2 C = vec2(1.0/6.0, 1.0/3.0); + + // First corner + vec3 i = floor(v + dot(v, C.yyy)); + vec3 x0 = v - i + dot(i, C.xxx); + + // Other corners + vec3 g = step(x0.yzx, x0.xyz); + vec3 l = 1.0 - g; + vec3 i1 = min(g.xyz, l.zxy); + vec3 i2 = max(g.xyz, l.zxy); + + // x0 = x0 - 0. + 0.0 * C + vec3 x1 = x0 - i1 + 1.0*C.xxx; + vec3 x2 = x0 - i2 + 2.0*C.xxx; + vec3 x3 = x0 - 1. + 3.0*C.xxx; + + // Get gradients: + ivec3 rand = random3to3(ivec3(i)); + vec3 p0 = vec3(rand); + + rand = random3to3((ivec3(i + i1))); + vec3 p1 = vec3(rand); + + rand = random3to3((ivec3(i + i2))); + vec3 p2 = vec3(rand); + + rand = random3to3((ivec3(i + 1))); + vec3 p3 = vec3(rand); + + // Mix final noise value + vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); + m = m*m; + return 42.0*dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)))/(1 << 31); +} + +void main() { + float variance = perpendicularFwidth(direction); + int textureIndex = textureIndices[blockType][faceNormal]; + textureIndex = textureIndex + time / animation[textureIndex].time % animation[textureIndex].frames; + float normalVariation = normalVariations[faceNormal]; + float lod = getLod(ivec3(startPosition), faceNormal, direction, variance); + ivec2 textureCoords = getTextureCoords(ivec3(startPosition), faceNormal); + fragColor = mipMapSample(texture_sampler, textureCoords, textureIndex, lod)*vec4(ambientLight*normalVariation, 1); + + if (fragColor.a == 1) discard; + + if (fog.activ) { + // TODO: Underwater fog if possible. + } + blendColor = fragColor; + fragColor.rgb = vec3(0); + + if(fragColor.a < 1) { + // Fake reflection: + // TODO: Make the amount configurable. + // TODO: Also allow this for opaque pixels. + // TODO: Change this when it rains. + // TODO: Normal mapping. + // TODO: Allow textures to contribute to this term. + fragColor.rgb += 0.1*vec3(snoise(normalize(reflect(direction, normals[faceNormal])))) + vec3(0.2); + fragColor.a = 1; + } else { + fragColor.rgb += mipMapSample(emissionSampler, textureCoords, textureIndex, lod).rgb; + } + + if (fog.activ) { + fragColor = calcFog(mvVertexPos, fragColor, fog); + } + if(!renderedToItemTexture) { + fragColor.rgb /= 4; + } + // TODO: Update the depth. +} diff --git a/assets/cubyz/shaders/deferred_render_pass.fs b/assets/cubyz/shaders/deferred_render_pass.fs index 87a1f663..3e185660 100644 --- a/assets/cubyz/shaders/deferred_render_pass.fs +++ b/assets/cubyz/shaders/deferred_render_pass.fs @@ -4,7 +4,6 @@ out vec4 fragColor; in vec2 texCoords; uniform sampler2D color; -uniform sampler2D position; void main() { fragColor = texture(color, texCoords); diff --git a/assets/cubyz/shaders/fog_fragment.fs b/assets/cubyz/shaders/fog_fragment.fs index fcad78a7..7f540a20 100644 --- a/assets/cubyz/shaders/fog_fragment.fs +++ b/assets/cubyz/shaders/fog_fragment.fs @@ -13,7 +13,6 @@ struct Fog { uniform Fog fog; uniform sampler2D color; -uniform sampler2D position; vec4 calcFog(vec3 pos, vec4 color, Fog fog) { float distance = length(pos); @@ -24,6 +23,6 @@ vec4 calcFog(vec3 pos, vec4 color, Fog fog) { } void main() { - fragColor = calcFog(texture(position, texCoords).xyz, texture(color, texCoords)*vec4(4, 4, 4, 1), fog); + // TODO: Reconstruct position from the depth value. fragColor = calcFog(texture(position, texCoords).xyz, texture(color, texCoords)*vec4(4, 4, 4, 1), fog); fragColor.rgb /= 4; } \ No newline at end of file diff --git a/assets/cubyz/shaders/item_drop.fs b/assets/cubyz/shaders/item_drop.fs index 9f5c38f2..10007ed7 100644 --- a/assets/cubyz/shaders/item_drop.fs +++ b/assets/cubyz/shaders/item_drop.fs @@ -10,7 +10,6 @@ flat in uvec3 lower; flat in uvec3 upper; layout(location = 0) out vec4 fragColor; -layout(location = 1) out vec4 position; struct Fog { bool activ; @@ -223,7 +222,6 @@ void mainBlockDrop() { fragColor = calcFog(startPosition, fragColor, fog); } fragColor.rgb /= 4; - position = vec4(startPosition, 1); } // itemDrops ------------------------------------------------------------------------------------------------------------------------- @@ -314,7 +312,6 @@ void mainItemDrop() { fragColor = calcFog(modifiedCameraSpacePos, color, fog); } fragColor.rgb /= 4; - position = vec4(modifiedCameraSpacePos, 1); } void main() { diff --git a/assets/cubyz/shaders/transparent_fragment.fs b/assets/cubyz/shaders/transparent_fragment.fs deleted file mode 100644 index 309dff43..00000000 --- a/assets/cubyz/shaders/transparent_fragment.fs +++ /dev/null @@ -1,66 +0,0 @@ -#version 430 - -in vec2 outTexCoord; -flat in float textureIndex; -in vec3 outColor; -in vec3 mvVertexPos; - -layout(location = 0) out vec4 fragColor; -layout(location = 1) out vec4 position; - -struct Fog { - bool activ; - vec3 color; - float density; -}; - -uniform sampler2DArray texture_sampler; -uniform sampler2DArray emissionSampler; -uniform sampler2D break_sampler; -uniform Fog fog; -uniform Fog waterFog; // TODO: Select fog from texture -uniform vec2 windowSize; -uniform vec3 ambientLight; - -uniform sampler2D positionBuffer; -uniform sampler2D colorBuffer; -uniform bool drawFrontFace; - -vec4 ambientC; - -void setupColors(vec3 textCoord) { - ambientC = texture(texture_sampler, textCoord); -} - -vec4 calcFog(vec3 pos, vec4 color, Fog fog) { - float distance = length(pos); - float fogFactor = 1.0/exp((distance*fog.density)*(distance*fog.density)); - fogFactor = clamp(fogFactor, 0.0, 1.0); - vec3 resultColor = mix(fog.color, color.xyz, fogFactor); - return vec4(resultColor.xyz, color.w + 1 - fogFactor); -} - -void main() { - setupColors(vec3(outTexCoord, textureIndex)); - if (ambientC.a == 1) discard; - - fragColor = ambientC*vec4(outColor, 1); - fragColor.rgb += texture(emissionSampler, vec3(outTexCoord, textureIndex)).rgb; - - if (fog.activ) { - fragColor = calcFog(mvVertexPos, fragColor, fog); - - // Underwater fog: - if (drawFrontFace) { // There is only fog betwen front and back face of the same volume. - vec2 frameBufferPos = gl_FragCoord.xy/windowSize; - vec4 oldColor = texture(colorBuffer, frameBufferPos); - oldColor.rgb *= 4; - vec3 oldPosition = texture(positionBuffer, frameBufferPos).xyz; - oldColor = calcFog(oldPosition - mvVertexPos, oldColor, waterFog); - fragColor = vec4((1 - fragColor.a) * oldColor.xyz + fragColor.a * fragColor.xyz, 1); - } - } - fragColor.rgb /= 4; - - position = vec4(mvVertexPos, 1); -} diff --git a/assets/cubyz/shaders/transparent_vertex.vs b/assets/cubyz/shaders/transparent_vertex.vs deleted file mode 100644 index 852bb4a7..00000000 --- a/assets/cubyz/shaders/transparent_vertex.vs +++ /dev/null @@ -1,49 +0,0 @@ -#version 430 - -layout (location=0) in vec3 position; -layout (location=1) in vec3 texCoord; -layout (location=2) in vec3 vertexNormal; -layout (location=3) in int easyLight; - -out vec2 outTexCoord; -flat out float textureIndex; -out vec3 mvVertexPos; -out vec3 outColor; - -uniform mat4 projectionMatrix; -uniform vec3 ambientLight; -uniform vec3 directionalLight; -uniform mat4 viewMatrix; -uniform vec3 modelPosition; - -layout(std430, binding = 0) buffer _animationTimes -{ - int animationTimes[]; -}; -layout(std430, binding = 1) buffer _animationFrames -{ - int animationFrames[]; -}; - -uniform int time; - -vec3 calcLight(int srgb) { - float s = (srgb >> 24) & 255; - float r = (srgb >> 16) & 255; - float g = (srgb >> 8) & 255; - float b = (srgb >> 0) & 255; - s = s*(1 - dot(directionalLight, vertexNormal)); - r = max(s*ambientLight.x, r); - g = max(s*ambientLight.y, g); - b = max(s*ambientLight.z, b); - return vec3(r, g, b); -} - -void main() { - outColor = calcLight(easyLight)*0.003890625; - vec4 mvPos = viewMatrix*vec4(position + modelPosition, 1); - gl_Position = projectionMatrix*mvPos; - outTexCoord = texCoord.xy; - textureIndex = texCoord.z + time / animationTimes[int(texCoord.z)] % animationFrames[int(texCoord.z)]; - mvVertexPos = mvPos.xyz; -} diff --git a/src/chunk.zig b/src/chunk.zig index 111016ba..c7f24226 100644 --- a/src/chunk.zig +++ b/src/chunk.zig @@ -368,6 +368,7 @@ pub const Chunk = struct { pub const meshing = struct { var shader: Shader = undefined; + var transparentShader: Shader = undefined; pub var uniforms: struct { projectionMatrix: c_int, viewMatrix: c_int, @@ -387,6 +388,25 @@ pub const meshing = struct { voxelSize: c_int, renderedToItemTexture: c_int, } = undefined; + pub var transparentUniforms: struct { + projectionMatrix: c_int, + viewMatrix: c_int, + modelPosition: c_int, + screenSize: c_int, + ambientLight: c_int, + @"fog.activ": c_int, + @"fog.color": c_int, + @"fog.density": c_int, + texture_sampler: c_int, + emissionSampler: c_int, + @"waterFog.activ": c_int, + @"waterFog.color": c_int, + @"waterFog.density": c_int, + time: c_int, + visibilityMask: c_int, + voxelSize: c_int, + renderedToItemTexture: c_int, + } = undefined; var vao: c_uint = undefined; var vbo: c_uint = undefined; var faces: std.ArrayList(u32) = undefined; @@ -394,6 +414,7 @@ pub const meshing = struct { pub fn init() !void { shader = try Shader.initAndGetUniforms("assets/cubyz/shaders/chunks/chunk_vertex.vs", "assets/cubyz/shaders/chunks/chunk_fragment.fs", &uniforms); + transparentShader = try Shader.initAndGetUniforms("assets/cubyz/shaders/chunks/chunk_vertex.vs", "assets/cubyz/shaders/chunks/transparent_fragment.fs", &transparentUniforms); var rawData: [6*3 << (3*chunkShift)]u32 = undefined; // 6 vertices per face, maximum 3 faces/block const lut = [_]u32{0, 1, 2, 2, 1, 3}; @@ -415,6 +436,7 @@ pub const meshing = struct { pub fn deinit() void { shader.deinit(); + transparentShader.deinit(); c.glDeleteVertexArrays(1, &vao); c.glDeleteBuffers(1, &vbo); faces.deinit(); @@ -444,6 +466,29 @@ pub const meshing = struct { c.glBindVertexArray(vao); } + pub fn bindTransparentShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, time: u32) void { + transparentShader.bind(); + + c.glUniform1i(transparentUniforms.@"fog.activ", if(game.fog.active) 1 else 0); + c.glUniform3fv(transparentUniforms.@"fog.color", 1, @ptrCast(&game.fog.color)); + c.glUniform1f(transparentUniforms.@"fog.density", game.fog.density); + + c.glUniformMatrix4fv(transparentUniforms.projectionMatrix, 1, c.GL_FALSE, @ptrCast(&projMatrix)); + + c.glUniform1i(transparentUniforms.texture_sampler, 0); + c.glUniform1i(transparentUniforms.emissionSampler, 1); + + c.glUniformMatrix4fv(transparentUniforms.viewMatrix, 1, c.GL_FALSE, @ptrCast(&game.camera.viewMatrix)); + + c.glUniform3f(transparentUniforms.ambientLight, ambient[0], ambient[1], ambient[2]); + + c.glUniform1i(transparentUniforms.time, @as(u31, @truncate(time))); + + c.glUniform1i(transparentUniforms.renderedToItemTexture, 0); + + c.glBindVertexArray(vao); + } + pub const FaceData = switch(@import("builtin").target.cpu.arch.endian()) { .Little => packed struct { position: u32, @@ -455,94 +500,61 @@ pub const meshing = struct { }, }; - pub const ChunkMesh = struct { - pos: ChunkPosition, - size: i32, - chunk: std.atomic.Atomic(?*Chunk), + const PrimitiveMesh = struct { faces: std.ArrayList(FaceData), bufferAllocation: graphics.LargeBuffer.Allocation = .{.start = 0, .len = 0}, coreCount: u31 = 0, neighborStart: [7]u31 = [_]u31{0} ** 7, vertexCount: u31 = 0, - generated: bool = false, - mutex: std.Thread.Mutex = std.Thread.Mutex{}, - visibilityMask: u8 = 0xff, - pub fn init(allocator: Allocator, pos: ChunkPosition) ChunkMesh { - return ChunkMesh{ - .pos = pos, - .size = chunkSize*pos.voxelSize, - .faces = std.ArrayList(FaceData).init(allocator), - .chunk = std.atomic.Atomic(?*Chunk).init(null), - }; - } - - pub fn deinit(self: *ChunkMesh) void { + fn deinit(self: *PrimitiveMesh) void { faceBuffer.free(self.bufferAllocation) catch unreachable; self.faces.deinit(); - if(self.chunk.load(.Monotonic)) |ch| { - main.globalAllocator.destroy(ch); - } } - fn canBeSeenThroughOtherBlock(block: Block, other: Block, neighbor: u3) bool { - const rotatedModel = blocks.meshes.model(block); - const model = &models.voxelModels.items[rotatedModel.modelIndex]; - const freestandingModel = rotatedModel.modelIndex != models.fullCube and switch(rotatedModel.permutation.permuteNeighborIndex(neighbor)) { - Neighbors.dirNegX => model.min[0] != 0, - Neighbors.dirPosX => model.max[0] != 16, - Neighbors.dirDown => model.min[1] != 0, - Neighbors.dirUp => model.max[1] != 16, - Neighbors.dirNegZ => model.min[2] != 0, - Neighbors.dirPosZ => model.max[2] != 16, - else => unreachable, - }; - return block.typ != 0 and ( - freestandingModel - or other.typ == 0 - or (!std.meta.eql(block, other) and other.viewThrough()) - or blocks.meshes.model(other).modelIndex != 0 // TODO: make this more strict to avoid overdraw. - ); - } - - pub fn regenerateMainMesh(self: *ChunkMesh, chunk: *Chunk) !void { - std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function. + fn reset(self: *PrimitiveMesh) void { self.faces.clearRetainingCapacity(); - var n: u32 = 0; - var x: u8 = 0; - while(x < chunkSize): (x += 1) { - var y: u8 = 0; - while(y < chunkSize): (y += 1) { - var z: u8 = 0; - while(z < chunkSize): (z += 1) { - const block = (&chunk.blocks)[getIndex(x, y, z)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed. - if(block.typ == 0) continue; - // Check all neighbors: - for(Neighbors.iterable) |i| { - n += 1; - const x2 = x + Neighbors.relX[i]; - const y2 = y + Neighbors.relY[i]; - const z2 = z + Neighbors.relZ[i]; - if(x2&chunkMask != x2 or y2&chunkMask != y2 or z2&chunkMask != z2) continue; // Neighbor is outside the chunk. - const neighborBlock = (&chunk.blocks)[getIndex(x2, y2, z2)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed. - if(canBeSeenThroughOtherBlock(block, neighborBlock, i)) { - try self.faces.append(constructFaceData(block, i, @intCast(x2), @intCast(y2), @intCast(z2))); - } - } - } - } - } + } - if(self.chunk.load(.Monotonic)) |oldChunk| { - main.globalAllocator.destroy(oldChunk); - } - self.chunk.store(chunk, .Monotonic); + fn resetToCore(self: *PrimitiveMesh) void { + self.faces.shrinkRetainingCapacity(self.coreCount); + } + + fn append(self: *PrimitiveMesh, face: FaceData) !void { + try self.faces.append(face); + } + + fn updateCore(self: *PrimitiveMesh) void { self.coreCount = @intCast(self.faces.items.len); self.neighborStart = [_]u31{self.coreCount} ** 7; } - fn addFace(self: *ChunkMesh, faceData: FaceData, fromNeighborChunk: ?u3) !void { - std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function. + fn startNeighbor(self: *PrimitiveMesh, neighbor: usize) void { + self.neighborStart[neighbor] = @intCast(self.faces.items.len); + } + + fn replaceNeighbors(self: *PrimitiveMesh, neighbor: usize, additionalNeighborFaces: []FaceData) !void { + var rangeStart = self.neighborStart[neighbor ^ 1]; + var rangeEnd = self.neighborStart[(neighbor ^ 1)+1]; + try self.faces.replaceRange(rangeStart, rangeEnd - rangeStart, additionalNeighborFaces); + for(self.neighborStart[1+(neighbor ^ 1)..]) |*neighborStart| { + neighborStart.* = neighborStart.* - (rangeEnd - rangeStart) + @as(u31, @intCast(additionalNeighborFaces.len)); + } + try self.uploadData(); + } + + fn finish(self: *PrimitiveMesh) !void { + self.neighborStart[6] = @intCast(self.faces.items.len); + try self.uploadData(); + } + + fn uploadData(self: *PrimitiveMesh) !void { + self.vertexCount = @intCast(6*self.faces.items.len); + try faceBuffer.realloc(&self.bufferAllocation, @intCast(8*self.faces.items.len)); + faceBuffer.bufferSubData(self.bufferAllocation.start, FaceData, self.faces.items); + } + + fn addFace(self: *PrimitiveMesh, faceData: FaceData, fromNeighborChunk: ?u3) !void { var insertionIndex: u31 = undefined; if(fromNeighborChunk) |neighbor| { insertionIndex = self.neighborStart[neighbor]; @@ -559,8 +571,7 @@ pub const meshing = struct { try self.faces.insert(insertionIndex, faceData); } - fn removeFace(self: *ChunkMesh, faceData: FaceData, fromNeighborChunk: ?u3) void { - std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function. + fn removeFace(self: *PrimitiveMesh, faceData: FaceData, fromNeighborChunk: ?u3) void { var searchStart: u32 = undefined; var searchEnd: u32 = undefined; if(fromNeighborChunk) |neighbor| { @@ -586,8 +597,7 @@ pub const meshing = struct { @panic("Couldn't find the face to remove. This case is not handled."); } - fn changeFace(self: *ChunkMesh, oldFaceData: FaceData, newFaceData: FaceData, fromNeighborChunk: ?u3) void { - std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function. + fn changeFace(self: *PrimitiveMesh, oldFaceData: FaceData, newFaceData: FaceData, fromNeighborChunk: ?u3) void { var searchRange: []FaceData = undefined; if(fromNeighborChunk) |neighbor| { searchRange = self.faces.items[self.neighborStart[neighbor]..self.neighborStart[neighbor+1]]; @@ -603,6 +613,140 @@ pub const meshing = struct { } std.log.err("Couldn't find the face to replace.", .{}); } + }; + + pub const ChunkMesh = struct { + pos: ChunkPosition, + size: i32, + chunk: std.atomic.Atomic(?*Chunk), + opaqueMesh: PrimitiveMesh, + transparentMesh: PrimitiveMesh, + generated: bool = false, + mutex: std.Thread.Mutex = std.Thread.Mutex{}, + visibilityMask: u8 = 0xff, + + pub fn init(allocator: Allocator, pos: ChunkPosition) ChunkMesh { + return ChunkMesh{ + .pos = pos, + .size = chunkSize*pos.voxelSize, + .opaqueMesh = .{ + .faces = std.ArrayList(FaceData).init(allocator) + }, + .transparentMesh = .{ + .faces = std.ArrayList(FaceData).init(allocator) + }, + .chunk = std.atomic.Atomic(?*Chunk).init(null), + }; + } + + pub fn deinit(self: *ChunkMesh) void { + self.opaqueMesh.deinit(); + self.transparentMesh.deinit(); + if(self.chunk.load(.Monotonic)) |ch| { + main.globalAllocator.destroy(ch); + } + } + + pub fn isEmpty(self: *const ChunkMesh) bool { + return self.opaqueMesh.vertexCount == 0 and self.transparentMesh.vertexCount == 0; + } + + fn canBeSeenThroughOtherBlock(block: Block, other: Block, neighbor: u3) bool { + const rotatedModel = blocks.meshes.model(block); + const model = &models.voxelModels.items[rotatedModel.modelIndex]; + const freestandingModel = rotatedModel.modelIndex != models.fullCube and switch(rotatedModel.permutation.permuteNeighborIndex(neighbor)) { + Neighbors.dirNegX => model.min[0] != 0, + Neighbors.dirPosX => model.max[0] != 16, + Neighbors.dirDown => model.min[1] != 0, + Neighbors.dirUp => model.max[1] != 16, + Neighbors.dirNegZ => model.min[2] != 0, + Neighbors.dirPosZ => model.max[2] != 16, + else => unreachable, + }; + return block.typ != 0 and ( + freestandingModel + or other.typ == 0 + or (!std.meta.eql(block, other) and other.viewThrough()) + or blocks.meshes.model(other).modelIndex != 0 // TODO: make this more strict to avoid overdraw. + ); + } + + pub fn regenerateMainMesh(self: *ChunkMesh, chunk: *Chunk) !void { + std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function. + self.opaqueMesh.reset(); + self.transparentMesh.reset(); + var n: u32 = 0; + var x: u8 = 0; + while(x < chunkSize): (x += 1) { + var y: u8 = 0; + while(y < chunkSize): (y += 1) { + var z: u8 = 0; + while(z < chunkSize): (z += 1) { + const block = (&chunk.blocks)[getIndex(x, y, z)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed. + if(block.typ == 0) continue; + // Check all neighbors: + for(Neighbors.iterable) |i| { + n += 1; + const x2 = x + Neighbors.relX[i]; + const y2 = y + Neighbors.relY[i]; + const z2 = z + Neighbors.relZ[i]; + if(x2&chunkMask != x2 or y2&chunkMask != y2 or z2&chunkMask != z2) continue; // Neighbor is outside the chunk. + const neighborBlock = (&chunk.blocks)[getIndex(x2, y2, z2)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed. + if(canBeSeenThroughOtherBlock(block, neighborBlock, i)) { + if(block.transparent()) { + try self.transparentMesh.append(constructFaceData(block, i, @intCast(x2), @intCast(y2), @intCast(z2))); + } else { + try self.opaqueMesh.append(constructFaceData(block, i, @intCast(x2), @intCast(y2), @intCast(z2))); + } + } + } + } + } + } + + if(self.chunk.swap(chunk, .Monotonic)) |oldChunk| { + main.globalAllocator.destroy(oldChunk); + } + self.transparentMesh.updateCore(); + self.opaqueMesh.updateCore(); + } + + fn addFace(self: *ChunkMesh, faceData: FaceData, fromNeighborChunk: ?u3, transparent: bool) !void { + std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function. + if(transparent) { + try self.transparentMesh.addFace(faceData, fromNeighborChunk); + } else { + try self.opaqueMesh.addFace(faceData, fromNeighborChunk); + } + } + + fn removeFace(self: *ChunkMesh, faceData: FaceData, fromNeighborChunk: ?u3, transparent: bool) void { + std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function. + if(transparent) { + self.transparentMesh.removeFace(faceData, fromNeighborChunk); + } else { + self.opaqueMesh.removeFace(faceData, fromNeighborChunk); + } + } + + fn changeFace(self: *ChunkMesh, oldFaceData: FaceData, newFaceData: FaceData, fromNeighborChunk: ?u3, oldTransparent: bool, newTransparent: bool) !void { + std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function. + if(oldTransparent) { + if(newTransparent) { + self.transparentMesh.changeFace(oldFaceData, newFaceData, fromNeighborChunk); + } else { + self.transparentMesh.removeFace(oldFaceData, fromNeighborChunk); + try self.opaqueMesh.addFace(newFaceData, fromNeighborChunk); + } + } else { + if(newTransparent) { + self.opaqueMesh.removeFace(oldFaceData, fromNeighborChunk); + try self.transparentMesh.addFace(newFaceData, fromNeighborChunk); + } else { + self.opaqueMesh.changeFace(oldFaceData, newFaceData, fromNeighborChunk); + } + } + } pub fn updateBlock(self: *ChunkMesh, _x: i32, _y: i32, _z: i32, newBlock: Block) !void { // TODO: Investigate bug when placing blocks. const x = _x & chunkMask; @@ -635,22 +779,22 @@ pub const meshing = struct { if(canBeSeenThroughOtherBlock(oldBlock, neighborBlock, neighbor) != newVisibility) { if(newVisibility) { // Adding the face if(neighborMesh == self) { - try self.addFace(newFaceData, null); + try self.addFace(newFaceData, null, newBlock.transparent()); } else { - try neighborMesh.addFace(newFaceData, neighbor); + try neighborMesh.addFace(newFaceData, neighbor, newBlock.transparent()); } } else { // Removing the face if(neighborMesh == self) { - self.removeFace(oldFaceData, null); + self.removeFace(oldFaceData, null, oldBlock.transparent()); } else { - neighborMesh.removeFace(oldFaceData, neighbor); + neighborMesh.removeFace(oldFaceData, neighbor, oldBlock.transparent()); } } } else if(newVisibility) { // Changing the face if(neighborMesh == self) { - self.changeFace(oldFaceData, newFaceData, null); + try self.changeFace(oldFaceData, newFaceData, null, oldBlock.transparent(), newBlock.transparent()); } else { - neighborMesh.changeFace(oldFaceData, newFaceData, neighbor); + try neighborMesh.changeFace(oldFaceData, newFaceData, neighbor, oldBlock.transparent(), newBlock.transparent()); } } } @@ -661,36 +805,34 @@ pub const meshing = struct { if(canBeSeenThroughOtherBlock(neighborBlock, oldBlock, neighbor ^ 1) != newVisibility) { if(newVisibility) { // Adding the face if(neighborMesh == self) { - try self.addFace(newFaceData, null); + try self.addFace(newFaceData, null, newBlock.transparent()); } else { - try self.addFace(newFaceData, neighbor); + try self.addFace(newFaceData, neighbor, newBlock.transparent()); } } else { // Removing the face if(neighborMesh == self) { - self.removeFace(oldFaceData, null); + self.removeFace(oldFaceData, null, oldBlock.transparent()); } else { - self.removeFace(oldFaceData, neighbor); + self.removeFace(oldFaceData, neighbor, oldBlock.transparent()); } } } else if(newVisibility) { // Changing the face if(neighborMesh == self) { - self.changeFace(oldFaceData, newFaceData, null); + try self.changeFace(oldFaceData, newFaceData, null, oldBlock.transparent(), newBlock.transparent()); } else { - self.changeFace(oldFaceData, newFaceData, neighbor); + try self.changeFace(oldFaceData, newFaceData, neighbor, oldBlock.transparent(), newBlock.transparent()); } } } } - if(neighborMesh != self) try neighborMesh.uploadData(); + if(neighborMesh != self) { + try neighborMesh.opaqueMesh.uploadData(); + try neighborMesh.transparentMesh.uploadData(); + } } self.chunk.load(.Monotonic).?.blocks[getIndex(x, y, z)] = newBlock; - try self.uploadData(); - } - - fn uploadData(self: *ChunkMesh) !void { - self.vertexCount = @intCast(6*self.faces.items.len); - try faceBuffer.realloc(&self.bufferAllocation, @intCast(8*self.faces.items.len)); - faceBuffer.bufferSubData(self.bufferAllocation.start, FaceData, self.faces.items); + try self.opaqueMesh.uploadData(); + try self.transparentMesh.uploadData(); } pub inline fn constructFaceData(block: Block, normal: u32, x: u32, y: u32, z: u32) FaceData { @@ -704,17 +846,21 @@ pub const meshing = struct { pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void { std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function. const chunk = self.chunk.load(.Monotonic) orelse return; // In the mean-time the mesh was discarded and recreated and all the data was lost. - self.faces.shrinkRetainingCapacity(self.coreCount); + self.opaqueMesh.resetToCore(); + self.transparentMesh.resetToCore(); for(Neighbors.iterable) |neighbor| { - self.neighborStart[neighbor] = @intCast(self.faces.items.len); + self.opaqueMesh.startNeighbor(neighbor); + self.transparentMesh.startNeighbor(neighbor); var nullNeighborMesh = renderer.RenderStructure.getNeighbor(self.pos, self.pos.voxelSize, neighbor); if(nullNeighborMesh) |neighborMesh| { std.debug.assert(neighborMesh != self); neighborMesh.mutex.lock(); defer neighborMesh.mutex.unlock(); if(neighborMesh.generated) { - var additionalNeighborFaces = std.ArrayList(FaceData).init(main.threadAllocator); - defer additionalNeighborFaces.deinit(); + var additionalNeighborFacesOpaque = std.ArrayList(FaceData).init(main.threadAllocator); + defer additionalNeighborFacesOpaque.deinit(); + var additionalNeighborFacesTransparent = std.ArrayList(FaceData).init(main.threadAllocator); + defer additionalNeighborFacesTransparent.deinit(); var x3: u8 = if(neighbor & 1 == 0) @intCast(chunkMask) else 0; var x1: u8 = 0; while(x1 < chunkSize): (x1 += 1) { @@ -742,20 +888,23 @@ pub const meshing = struct { var block = (&chunk.blocks)[getIndex(x, y, z)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed. var otherBlock = (&neighborMesh.chunk.load(.Monotonic).?.blocks)[getIndex(otherX, otherY, otherZ)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed. if(canBeSeenThroughOtherBlock(block, otherBlock, neighbor)) { - try additionalNeighborFaces.append(constructFaceData(block, neighbor, otherX, otherY, otherZ)); + if(block.transparent()) { + try additionalNeighborFacesTransparent.append(constructFaceData(block, neighbor, otherX, otherY, otherZ)); + } else { + try additionalNeighborFacesOpaque.append(constructFaceData(block, neighbor, otherX, otherY, otherZ)); + } } if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor ^ 1)) { - try self.faces.append(constructFaceData(otherBlock, neighbor ^ 1, x, y, z)); + if(otherBlock.transparent()) { + try self.transparentMesh.append(constructFaceData(otherBlock, neighbor ^ 1, x, y, z)); + } else { + try self.opaqueMesh.append(constructFaceData(otherBlock, neighbor ^ 1, x, y, z)); + } } } } - var rangeStart = neighborMesh.neighborStart[neighbor ^ 1]; - var rangeEnd = neighborMesh.neighborStart[(neighbor ^ 1)+1]; - try neighborMesh.faces.replaceRange(rangeStart, rangeEnd - rangeStart, additionalNeighborFaces.items); - for(neighborMesh.neighborStart[1+(neighbor ^ 1)..]) |*neighborStart| { - neighborStart.* = neighborStart.* - (rangeEnd - rangeStart) + @as(u31, @intCast(additionalNeighborFaces.items.len)); - } - try neighborMesh.uploadData(); + try neighborMesh.opaqueMesh.replaceNeighbors(neighbor, additionalNeighborFacesOpaque.items); + try neighborMesh.transparentMesh.replaceNeighbors(neighbor, additionalNeighborFacesTransparent.items); continue; } } @@ -795,7 +944,11 @@ pub const meshing = struct { var block = (&chunk.blocks)[getIndex(x, y, z)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed. var otherBlock = (&neighborMesh.chunk.load(.Monotonic).?.blocks)[getIndex(otherX, otherY, otherZ)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed. if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor ^ 1)) { - try self.faces.append(constructFaceData(otherBlock, neighbor ^ 1, x, y, z)); + if(otherBlock.transparent()) { + try self.transparentMesh.append(constructFaceData(otherBlock, neighbor ^ 1, x, y, z)); + } else { + try self.opaqueMesh.append(constructFaceData(otherBlock, neighbor ^ 1, x, y, z)); + } } } } @@ -803,8 +956,8 @@ pub const meshing = struct { return error.LODMissing; } } - self.neighborStart[6] = @intCast(self.faces.items.len); - try self.uploadData(); + try self.opaqueMesh.finish(); + try self.transparentMesh.finish(); self.generated = true; } @@ -812,7 +965,7 @@ pub const meshing = struct { if(!self.generated) { return; } - if(self.vertexCount == 0) return; + if(self.opaqueMesh.vertexCount == 0) return; c.glUniform3f( uniforms.modelPosition, @floatCast(@as(f64, @floatFromInt(self.pos.wx)) - playerPosition[0]), @@ -821,7 +974,23 @@ pub const meshing = struct { ); c.glUniform1i(uniforms.visibilityMask, self.visibilityMask); c.glUniform1i(uniforms.voxelSize, self.pos.voxelSize); - c.glDrawElementsBaseVertex(c.GL_TRIANGLES, self.vertexCount, c.GL_UNSIGNED_INT, null, self.bufferAllocation.start/8*4); + c.glDrawElementsBaseVertex(c.GL_TRIANGLES, self.opaqueMesh.vertexCount, c.GL_UNSIGNED_INT, null, self.opaqueMesh.bufferAllocation.start/8*4); + } + + pub fn renderTransparent(self: *ChunkMesh, playerPosition: Vec3d) void { // TODO: Transparency sorting + if(!self.generated) { + return; + } + if(self.transparentMesh.vertexCount == 0) return; + c.glUniform3f( + transparentUniforms.modelPosition, + @floatCast(@as(f64, @floatFromInt(self.pos.wx)) - playerPosition[0]), + @floatCast(@as(f64, @floatFromInt(self.pos.wy)) - playerPosition[1]), + @floatCast(@as(f64, @floatFromInt(self.pos.wz)) - playerPosition[2]) + ); + c.glUniform1i(transparentUniforms.visibilityMask, self.visibilityMask); + c.glUniform1i(transparentUniforms.voxelSize, self.pos.voxelSize); + c.glDrawElementsBaseVertex(c.GL_TRIANGLES, self.transparentMesh.vertexCount, c.GL_UNSIGNED_INT, null, self.transparentMesh.bufferAllocation.start/8*4); } }; }; \ No newline at end of file diff --git a/src/renderer.zig b/src/renderer.zig index d6aca5f9..30646843 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -34,12 +34,10 @@ var fogUniforms: struct { fog_activ: c_int, fog_color: c_int, fog_density: c_int, - position: c_int, color: c_int, } = undefined; var deferredRenderPassShader: graphics.Shader = undefined; var deferredUniforms: struct { - position: c_int, color: c_int, } = undefined; @@ -66,20 +64,17 @@ pub fn deinit() void { const buffers = struct { var buffer: c_uint = undefined; var colorTexture: c_uint = undefined; - var positionTexture: c_uint = undefined; var depthBuffer: c_uint = undefined; fn init() void { c.glGenFramebuffers(1, &buffer); c.glGenRenderbuffers(1, &depthBuffer); c.glGenTextures(1, &colorTexture); - c.glGenTextures(1, &positionTexture); updateBufferSize(Window.width, Window.height); c.glBindFramebuffer(c.GL_FRAMEBUFFER, buffer); c.glFramebufferTexture2D(c.GL_FRAMEBUFFER, c.GL_COLOR_ATTACHMENT0, c.GL_TEXTURE_2D, colorTexture, 0); - c.glFramebufferTexture2D(c.GL_FRAMEBUFFER, c.GL_COLOR_ATTACHMENT1, c.GL_TEXTURE_2D, positionTexture, 0); c.glFramebufferRenderbuffer(c.GL_FRAMEBUFFER, c.GL_DEPTH_STENCIL_ATTACHMENT, c.GL_RENDERBUFFER, depthBuffer); @@ -90,7 +85,6 @@ const buffers = struct { c.glDeleteFramebuffers(1, &buffer); c.glDeleteRenderbuffers(1, &depthBuffer); c.glDeleteTextures(1, &colorTexture); - c.glDeleteTextures(1, &positionTexture); } fn regenTexture(texture: c_uint, internalFormat: c_int, format: c_uint, width: u31, height: u31) void { @@ -105,13 +99,12 @@ const buffers = struct { c.glBindFramebuffer(c.GL_FRAMEBUFFER, buffer); regenTexture(colorTexture, c.GL_RGB10_A2, c.GL_RGB, width, height); - regenTexture(positionTexture, c.GL_RGB16F, c.GL_RGB, width, height); c.glBindRenderbuffer(c.GL_RENDERBUFFER, depthBuffer); c.glRenderbufferStorage(c.GL_RENDERBUFFER, c.GL_DEPTH24_STENCIL8, width, height); c.glBindRenderbuffer(c.GL_RENDERBUFFER, 0); - const attachments = [_]c_uint{c.GL_COLOR_ATTACHMENT0, c.GL_COLOR_ATTACHMENT1}; + const attachments = [_]c_uint{c.GL_COLOR_ATTACHMENT0}; c.glDrawBuffers(attachments.len, &attachments); c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0); @@ -120,8 +113,6 @@ const buffers = struct { fn bindTextures() void { c.glActiveTexture(c.GL_TEXTURE3); c.glBindTexture(c.GL_TEXTURE_2D, colorTexture); - c.glActiveTexture(c.GL_TEXTURE4); - c.glBindTexture(c.GL_TEXTURE_2D, positionTexture); } fn bind() void { @@ -270,7 +261,23 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo try itemdrop.ItemDropRenderer.renderItemDrops(game.projectionMatrix, ambientLight, playerPos, time); -// // Render transparent chunk meshes: + // Render transparent chunk meshes: + + chunk.meshing.bindTransparentShaderAndUniforms(game.projectionMatrix, ambientLight, time); + c.glUniform1i(chunk.meshing.transparentUniforms.@"waterFog.activ", if(waterFog.active) 1 else 0); + c.glUniform3fv(chunk.meshing.transparentUniforms.@"waterFog.color", 1, @ptrCast(&waterFog.color)); + c.glUniform1f(chunk.meshing.transparentUniforms.@"waterFog.density", waterFog.density); + + c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_SRC1_COLOR); + { + var i: usize = meshes.items.len; + while(true) { + if(i == 0) break; + i -= 1; + meshes.items[i].renderTransparent(playerPos); + } + } + c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA); // NormalChunkMesh.bindTransparentShader(ambientLight, directionalLight.getDirection(), time); buffers.bindTextures(); @@ -308,7 +315,6 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo // fogShader.setUniform(FogUniforms.loc_fog_color, waterFog.getColor()); // fogShader.setUniform(FogUniforms.loc_fog_density, waterFog.getDensity()); // glUniform1i(FogUniforms.loc_color, 3); -// glUniform1i(FogUniforms.loc_position, 4); // glBindVertexArray(Graphics.rectVAO); // glDisable(GL_DEPTH_TEST); @@ -323,7 +329,6 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo buffers.bindTextures(); deferredRenderPassShader.bind(); c.glUniform1i(deferredUniforms.color, 3); - c.glUniform1i(deferredUniforms.position, 4); c.glBindFramebuffer(c.GL_FRAMEBUFFER, activeFrameBuffer); @@ -1097,7 +1102,7 @@ pub const RenderStructure = struct { @floatFromInt(size), @floatFromInt(size), @floatFromInt(size), - }) and node.?.mesh.visibilityMask != 0 and node.?.mesh.vertexCount != 0) { + }) and node.?.mesh.visibilityMask != 0 and !node.?.mesh.isEmpty()) { try meshes.append(&node.?.mesh); } if(lod+1 != storageLists.len and node.?.mesh.generated) {