diff --git a/assets/cubyz/biomes/bush_lands.json b/assets/cubyz/biomes/bush_lands.json index ba50da55..b6abbc24 100644 --- a/assets/cubyz/biomes/bush_lands.json +++ b/assets/cubyz/biomes/bush_lands.json @@ -34,6 +34,13 @@ "variation" : 4, "depth" : 2, "smoothness" : 0.2 - } + }, + { + "id" : "cubyz:simple_vegetation", + "block" : "cubyz:grass_vegetation", + "chance" : 1, + "height" : 1, + "height_variation" : 0 + }, ] } diff --git a/assets/cubyz/blocks/grass_vegetation.json b/assets/cubyz/blocks/grass_vegetation.json new file mode 100644 index 00000000..666273b7 --- /dev/null +++ b/assets/cubyz/blocks/grass_vegetation.json @@ -0,0 +1,12 @@ +{ + "class" : "leaf", + "hardness" : 0.1, + "drops" : [ + "auto" + ], + "degradable" : true, + "viewThrough" : true, + "absorbedLight" : 0x121012, + "model" : "cross", + "texture" : "cubyz:grass_vegetation" +} diff --git a/assets/cubyz/blocks/textures/grass_vegetation.png b/assets/cubyz/blocks/textures/grass_vegetation.png new file mode 100644 index 00000000..49d1fbce Binary files /dev/null and b/assets/cubyz/blocks/textures/grass_vegetation.png differ diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vs b/assets/cubyz/shaders/chunks/chunk_vertex.vs index d43e619d..385f8a5e 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vs +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vs @@ -154,7 +154,7 @@ void main() { position *= voxelSize; ivec3 totalOffset = ivec3(16*voxelSize*quads[quadIndex].corners[vertexID]); totalOffset = ivec3(permutationMatrix*(vec3(equal(mirrorVector, vec3(1)))*totalOffset + vec3(equal(mirrorVector, vec3(-1)))*(1 - totalOffset))); - position += totalOffset - 16*voxelSize*ivec3(normal); + position += totalOffset; direction = position.xyz/16.0 + permutationMatrix*(mirrorVector*modelPosition); diff --git a/src/graphics.zig b/src/graphics.zig index 54a7a11b..13912eed 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -1847,7 +1847,7 @@ pub fn generateBlockTexture(blockType: u16) Texture { c.glDisable(c.GL_DEPTH_TEST); defer if(depthTest != 0) c.glEnable(c.GL_DEPTH_TEST); const cullFace = c.glIsEnabled(c.GL_CULL_FACE); - c.glDisable(c.GL_CULL_FACE); + c.glEnable(c.GL_CULL_FACE); defer if(cullFace != 0) c.glEnable(c.GL_CULL_FACE); frameBuffer.init(false, c.GL_NEAREST, c.GL_REPEAT); @@ -1874,31 +1874,29 @@ pub fn generateBlockTexture(blockType: u16) Texture { c.glBlendFunc(c.GL_ONE, c.GL_SRC1_COLOR); main.renderer.chunk_meshing.bindTransparentShaderAndUniforms(projMatrix, .{1, 1, 1}); } else { - if(block.mode().model(block).modelIndex == 0) { - main.renderer.chunk_meshing.bindShaderAndUniforms(projMatrix, .{1, 1, 1}); - } else { - std.log.err("TODO: Item textures for non-cube models.", .{}); - } + main.renderer.chunk_meshing.bindShaderAndUniforms(projMatrix, .{1, 1, 1}); } const uniforms = if(block.transparent()) &main.renderer.chunk_meshing.transparentUniforms else &main.renderer.chunk_meshing.uniforms; - var faceData: [6]main.renderer.chunk_meshing.FaceData = undefined; - var faces: u8 = 0; - faceData[faces + 0] = main.renderer.chunk_meshing.ChunkMesh.constructFaceData(block, main.chunk.Neighbors.dirPosX, 1+1, 1, 1, false); - faceData[faces + 1] = main.renderer.chunk_meshing.ChunkMesh.constructFaceData(block, main.chunk.Neighbors.dirPosY, 1, 1+1, 1, false); - faceData[faces + 2] = main.renderer.chunk_meshing.ChunkMesh.constructFaceData(block, main.chunk.Neighbors.dirUp, 1, 1, 1+1, false); - faces += 3; - if(block.hasBackFace()) { - faceData[faces+2] = main.renderer.chunk_meshing.ChunkMesh.constructFaceData(block, main.chunk.Neighbors.dirPosX, 1, 1, 1, true); - faceData[faces+1] = main.renderer.chunk_meshing.ChunkMesh.constructFaceData(block, main.chunk.Neighbors.dirPosY, 1, 1, 1, true); - faceData[faces+0] = main.renderer.chunk_meshing.ChunkMesh.constructFaceData(block, main.chunk.Neighbors.dirUp, 1, 1, 1, true); - faces += 3; + var faceData: main.ListUnmanaged(main.renderer.chunk_meshing.FaceData) = .{}; + defer faceData.deinit(main.stackAllocator); + const model = &main.models.models.items[main.blocks.meshes.model(block).modelIndex]; + model.appendInternalQuadsToList(&faceData, main.stackAllocator, block, 1, 1, 1, false); + for(main.chunk.Neighbors.iterable) |neighbor| { + model.appendNeighborFacingQuadsToList(&faceData, main.stackAllocator, block, neighbor, 1 + main.chunk.Neighbors.relX[neighbor], 1 + main.chunk.Neighbors.relY[neighbor], 1 + main.chunk.Neighbors.relZ[neighbor], false); } - for(faceData[0..faces]) |*face| { + if(block.hasBackFace()) { + model.appendInternalQuadsToList(&faceData, main.stackAllocator, block, 1, 1, 1, true); + for(main.chunk.Neighbors.iterable) |neighbor| { + model.appendNeighborFacingQuadsToList(&faceData, main.stackAllocator, block, neighbor, 1, 1, 1, true); + } + } + + for(faceData.items) |*face| { @memset(&face.light, ~@as(u32, 0)); } var allocation: SubAllocation = .{.start = 0, .len = 0}; - main.renderer.chunk_meshing.faceBuffer.uploadData(faceData[0..faces], &allocation); + main.renderer.chunk_meshing.faceBuffer.uploadData(faceData.items, &allocation); c.glUniform3f(uniforms.modelPosition, -65.5 - 1.5, -65.5 - 1.5, -92.631 - 1.5); c.glUniform1i(uniforms.visibilityMask, 0xff); @@ -1910,8 +1908,9 @@ pub fn generateBlockTexture(blockType: u16) Texture { c.glActiveTexture(c.GL_TEXTURE2); main.blocks.meshes.reflectivityAndAbsorptionTextureArray.bind(); block_texture.depthTexture.bindTo(5); - c.glDrawElementsBaseVertex(c.GL_TRIANGLES, 6*faces, c.GL_UNSIGNED_INT, null, allocation.start*4); + c.glDrawElementsBaseVertex(c.GL_TRIANGLES, @intCast(6*faceData.items.len), c.GL_UNSIGNED_INT, null, allocation.start*4); + c.glDisable(c.GL_CULL_FACE); var finalFrameBuffer: FrameBuffer = undefined; finalFrameBuffer.init(false, c.GL_NEAREST, c.GL_REPEAT); finalFrameBuffer.updateSize(textureSize, textureSize, c.GL_RGBA8); diff --git a/src/models.zig b/src/models.zig index 5242e887..6feefdf8 100644 --- a/src/models.zig +++ b/src/models.zig @@ -8,6 +8,8 @@ const vec = @import("vec.zig"); const Vec3i = vec.Vec3i; const Vec3f = vec.Vec3f; const Vec2f = vec.Vec2f; +const FaceData = main.renderer.chunk_meshing.FaceData; +const NeverFailingAllocator = main.utils.NeverFailingAllocator; var quadSSBO: graphics.SSBO = undefined; @@ -21,7 +23,81 @@ const QuadInfo = extern struct { const Model = struct { min: Vec3i, max: Vec3i, - quads: []u16, + internalQuads: []u16, + neighborFacingQuads: [6][]u16, + + fn getFaceNeighbor(quad: *const QuadInfo) ?u3 { + var allZero: @Vector(3, bool) = .{true, true, true}; + var allOne: @Vector(3, bool) = .{true, true, true}; + for(quad.corners) |corner| { + allZero = @select(bool, allZero, corner == Vec3f{0, 0, 0}, allZero); // vector and TODO: #14306 + allOne = @select(bool, allOne, corner == Vec3f{1, 1, 1}, allOne); // vector and TODO: #14306 + } + if(allZero[0]) return Neighbors.dirNegX; + if(allZero[1]) return Neighbors.dirNegY; + if(allZero[2]) return Neighbors.dirDown; + if(allOne[0]) return Neighbors.dirPosX; + if(allOne[1]) return Neighbors.dirPosY; + if(allOne[2]) return Neighbors.dirUp; + return null; + } + + fn init(self: *Model, allocator: NeverFailingAllocator, quadInfos: []const QuadInfo) void { + var amounts: [6]usize = .{0, 0, 0, 0, 0, 0}; + var internalAmount: usize = 0; + for(quadInfos) |*quad| { + if(getFaceNeighbor(quad)) |neighbor| { + amounts[neighbor] += 1; + } else { + internalAmount += 1; + } + } + + for(0..6) |i| { + self.neighborFacingQuads[i] = allocator.alloc(u16, amounts[i]); + } + self.internalQuads = allocator.alloc(u16, internalAmount); + + var indices: [6]usize = .{0, 0, 0, 0, 0, 0}; + var internalIndex: usize = 0; + for(quadInfos) |_quad| { + var quad = _quad; + if(getFaceNeighbor(&quad)) |neighbor| { + for(&quad.corners) |*corner| { + corner.* -= quad.normal; + } + const quadIndex = addQuad(quad); + self.neighborFacingQuads[neighbor][indices[neighbor]] = quadIndex; + indices[neighbor] += 1; + } else { + const quadIndex = addQuad(quad); + self.internalQuads[internalIndex] = quadIndex; + internalIndex += 1; + } + } + } + + fn deinit(self: *const Model, allocator: NeverFailingAllocator) void { + for(0..6) |i| { + allocator.free(self.neighborFacingQuads[i]); + } + allocator.free(self.internalQuads); + } + + fn appendQuadsToList(quadList: []const u16, list: *main.ListUnmanaged(FaceData), allocator: NeverFailingAllocator, block: main.blocks.Block, x: i32, y: i32, z: i32, comptime backFace: bool) void { + for(quadList) |quadIndex| { + const texture = main.blocks.meshes.textureIndex(block, quads.items[quadIndex].textureSlot); + list.append(allocator, FaceData.init(texture, quadIndex, x, y, z, backFace)); + } + } + + pub fn appendInternalQuadsToList(self: *const Model, list: *main.ListUnmanaged(FaceData), allocator: NeverFailingAllocator, block: main.blocks.Block, x: i32, y: i32, z: i32, comptime backFace: bool) void { + appendQuadsToList(self.internalQuads, list, allocator, block, x, y, z, backFace); + } + + pub fn appendNeighborFacingQuadsToList(self: *const Model, list: *main.ListUnmanaged(FaceData), allocator: NeverFailingAllocator, block: main.blocks.Block, neighbor: u3, x: i32, y: i32, z: i32, comptime backFace: bool) void { + appendQuadsToList(self.neighborFacingQuads[neighbor], list, allocator, block, x, y, z, backFace); + } }; var nameToIndex: std.StringHashMap(u16) = undefined; @@ -37,7 +113,7 @@ pub var quads: main.List(QuadInfo) = undefined; pub var models: main.List(Model) = undefined; pub var fullCube: u16 = undefined; -fn addQuad(info: QuadInfo) u16 { +fn addQuad(info: QuadInfo) u16 { // TODO: Merge duplicates const index: u16 = @intCast(quads.items.len); quads.append(info); return index; @@ -56,45 +132,78 @@ pub fn init() void { const cube = models.addOne(); cube.min = .{0, 0, 0}; cube.max = .{16, 16, 16}; - cube.quads = main.globalAllocator.alloc(u16, 6); - cube.quads[chunk.Neighbors.dirNegX] = addQuad(.{ - .normal = .{-1, 0, 0}, - .corners = .{.{0, 1, 0}, .{0, 1, 1}, .{0, 0, 0}, .{0, 0, 1}}, - .cornerUV = .{.{0, 0}, .{0, 1}, .{1, 0}, .{1, 1}}, - .textureSlot = chunk.Neighbors.dirNegX, - }); - cube.quads[chunk.Neighbors.dirPosX] = addQuad(.{ - .normal = .{1, 0, 0}, - .corners = .{.{1, 0, 0}, .{1, 0, 1}, .{1, 1, 0}, .{1, 1, 1}}, - .cornerUV = .{.{0, 0}, .{0, 1}, .{1, 0}, .{1, 1}}, - .textureSlot = chunk.Neighbors.dirPosX, - }); - cube.quads[chunk.Neighbors.dirNegY] = addQuad(.{ - .normal = .{0, -1, 0}, - .corners = .{.{0, 0, 0}, .{0, 0, 1}, .{1, 0, 0}, .{1, 0, 1}}, - .cornerUV = .{.{0, 0}, .{0, 1}, .{1, 0}, .{1, 1}}, - .textureSlot = chunk.Neighbors.dirNegY, - }); - cube.quads[chunk.Neighbors.dirPosY] = addQuad(.{ - .normal = .{0, 1, 0}, - .corners = .{.{1, 1, 0}, .{1, 1, 1}, .{0, 1, 0}, .{0, 1, 1}}, - .cornerUV = .{.{0, 0}, .{0, 1}, .{1, 0}, .{1, 1}}, - .textureSlot = chunk.Neighbors.dirPosY, - }); - cube.quads[chunk.Neighbors.dirDown] = addQuad(.{ - .normal = .{0, 0, -1}, - .corners = .{.{0, 1, 0}, .{0, 0, 0}, .{1, 1, 0}, .{1, 0, 0}}, - .cornerUV = .{.{0, 0}, .{0, 1}, .{1, 0}, .{1, 1}}, - .textureSlot = chunk.Neighbors.dirDown, - }); - cube.quads[chunk.Neighbors.dirUp] = addQuad(.{ - .normal = .{0, 0, 1}, - .corners = .{.{1, 1, 1}, .{1, 0, 1}, .{0, 1, 1}, .{0, 0, 1}}, - .cornerUV = .{.{0, 0}, .{0, 1}, .{1, 0}, .{1, 1}}, - .textureSlot = chunk.Neighbors.dirUp, + cube.init(main.globalAllocator, &.{ + .{ + .normal = .{-1, 0, 0}, + .corners = .{.{0, 1, 0}, .{0, 1, 1}, .{0, 0, 0}, .{0, 0, 1}}, + .cornerUV = .{.{0, 0}, .{0, 1}, .{1, 0}, .{1, 1}}, + .textureSlot = chunk.Neighbors.dirNegX, + }, + .{ + .normal = .{1, 0, 0}, + .corners = .{.{1, 0, 0}, .{1, 0, 1}, .{1, 1, 0}, .{1, 1, 1}}, + .cornerUV = .{.{0, 0}, .{0, 1}, .{1, 0}, .{1, 1}}, + .textureSlot = chunk.Neighbors.dirPosX, + }, + .{ + .normal = .{0, -1, 0}, + .corners = .{.{0, 0, 0}, .{0, 0, 1}, .{1, 0, 0}, .{1, 0, 1}}, + .cornerUV = .{.{0, 0}, .{0, 1}, .{1, 0}, .{1, 1}}, + .textureSlot = chunk.Neighbors.dirNegY, + }, + .{ + .normal = .{0, 1, 0}, + .corners = .{.{1, 1, 0}, .{1, 1, 1}, .{0, 1, 0}, .{0, 1, 1}}, + .cornerUV = .{.{0, 0}, .{0, 1}, .{1, 0}, .{1, 1}}, + .textureSlot = chunk.Neighbors.dirPosY, + }, + .{ + .normal = .{0, 0, -1}, + .corners = .{.{0, 1, 0}, .{0, 0, 0}, .{1, 1, 0}, .{1, 0, 0}}, + .cornerUV = .{.{0, 0}, .{0, 1}, .{1, 0}, .{1, 1}}, + .textureSlot = chunk.Neighbors.dirDown, + }, + .{ + .normal = .{0, 0, 1}, + .corners = .{.{1, 1, 1}, .{1, 0, 1}, .{0, 1, 1}, .{0, 0, 1}}, + .cornerUV = .{.{0, 0}, .{0, 1}, .{1, 0}, .{1, 1}}, + .textureSlot = chunk.Neighbors.dirUp, + }, }); fullCube = cubeIndex; + const crossModelIndex: u16 = @intCast(models.items.len); + nameToIndex.put("cross", crossModelIndex) catch unreachable; + const cross = models.addOne(); + cross.min = .{0, 0, 0}; + cross.max = .{16, 16, 16}; + cross.init(main.globalAllocator, &.{ + .{ + .normal = .{-std.math.sqrt1_2, std.math.sqrt1_2, 0}, + .corners = .{.{1, 1, 0}, .{1, 1, 1}, .{0, 0, 0}, .{0, 0, 1}}, + .cornerUV = .{.{0, 0}, .{0, 1}, .{1, 0}, .{1, 1}}, + .textureSlot = 0, + }, + .{ + .normal = .{std.math.sqrt1_2, -std.math.sqrt1_2, 0}, + .corners = .{.{0, 0, 0}, .{0, 0, 1}, .{1, 1, 0}, .{1, 1, 1}}, + .cornerUV = .{.{0, 0}, .{0, 1}, .{1, 0}, .{1, 1}}, + .textureSlot = 0, + }, + .{ + .normal = .{-std.math.sqrt1_2, -std.math.sqrt1_2, 0}, + .corners = .{.{0, 1, 0}, .{0, 1, 1}, .{1, 0, 0}, .{1, 0, 1}}, + .cornerUV = .{.{0, 0}, .{0, 1}, .{1, 0}, .{1, 1}}, + .textureSlot = 0, + }, + .{ + .normal = .{std.math.sqrt1_2, std.math.sqrt1_2, 0}, + .corners = .{.{1, 0, 0}, .{1, 0, 1}, .{0, 1, 0}, .{0, 1, 1}}, + .cornerUV = .{.{0, 0}, .{0, 1}, .{1, 0}, .{1, 1}}, + .textureSlot = 0, + }, + }); + quadSSBO = graphics.SSBO.initStatic(QuadInfo, quads.items); quadSSBO.bind(4); } @@ -103,7 +212,7 @@ pub fn deinit() void { quadSSBO.deinit(); nameToIndex.deinit(); for(models.items) |model| { - main.globalAllocator.free(model.quads); + model.deinit(main.globalAllocator); } models.deinit(); quads.deinit(); diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index 2de83d6b..a6747ab3 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -144,6 +144,13 @@ pub const FaceData = extern struct { quadIndex: u16, }, light: [4]u32 = .{0, 0, 0, 0}, + + pub inline fn init(texture: u16, quadIndex: u16, x: i32, y: i32, z: i32, comptime backFace: bool) FaceData { + return FaceData { + .position = .{.x = @intCast(x), .y = @intCast(y), .z = @intCast(z), .permutation = @bitCast(@as(u6, 0)), .isBackFace = backFace}, // TODO: Handle permutation during meshing + .blockAndQuad = .{.texture = texture, .quadIndex = quadIndex}, + }; + } }; const PrimitiveMesh = struct { @@ -181,15 +188,22 @@ const PrimitiveMesh = struct { } } - fn appendCore(self: *PrimitiveMesh, face: FaceData) void { - self.coreFaces.append(main.globalAllocator, face); + fn appendInternalQuadsToCore(self: *PrimitiveMesh, block: Block, x: i32, y: i32, z: i32, comptime backFace: bool) void { + const model = blocks.meshes.model(block); + models.models.items[model.modelIndex].appendInternalQuadsToList(&self.coreFaces, main.globalAllocator, block, x, y, z, backFace); } - fn appendNeighbor(self: *PrimitiveMesh, face: FaceData, neighbor: u3, comptime isLod: bool) void { + fn appendNeighborFacingQuadsToCore(self: *PrimitiveMesh, block: Block, neighbor: u3, x: i32, y: i32, z: i32, comptime backFace: bool) void { + const model = blocks.meshes.model(block); + models.models.items[model.modelIndex].appendNeighborFacingQuadsToList(&self.coreFaces, main.globalAllocator, block, neighbor, x, y, z, backFace); + } + + fn appendNeighborFacingQuadsToNeighbor(self: *PrimitiveMesh, block: Block, neighbor: u3, x: i32, y: i32, z: i32, comptime backFace: bool, comptime isLod: bool) void { + const model = blocks.meshes.model(block); if(isLod) { - self.neighborFacesHigherLod[neighbor].append(main.globalAllocator, face); + models.models.items[model.modelIndex].appendNeighborFacingQuadsToList(&self.neighborFacesHigherLod[neighbor ^ 1], main.globalAllocator, block, neighbor, x, y, z, backFace); } else { - self.neighborFacesSameLod[neighbor].append(main.globalAllocator, face); + models.models.items[model.modelIndex].appendNeighborFacingQuadsToList(&self.neighborFacesSameLod[neighbor ^ 1], main.globalAllocator, block, neighbor, x, y, z, backFace); } } @@ -269,7 +283,7 @@ const PrimitiveMesh = struct { const normal = models.quads.items[quadIndex].normal; for(0..4) |i| { const vertexPos = models.quads.items[quadIndex].corners[i]; - const lightPos = vertexPos + @as(Vec3f, @floatFromInt(blockPos)) - normal*@as(Vec3f, @splat(0.5)) - @as(Vec3f, @splat(0.5)); + const lightPos = vertexPos + @as(Vec3f, @floatFromInt(blockPos)) + normal*@as(Vec3f, @splat(0.5)) - @as(Vec3f, @splat(0.5)); const startPos: Vec3i = @intFromFloat(@floor(lightPos)); const interp = lightPos - @floor(lightPos); var val: [6]f32 = .{0, 0, 0, 0, 0, 0}; @@ -344,36 +358,6 @@ const PrimitiveMesh = struct { self.vertexCount = @intCast(6*fullBuffer.len); self.wasChanged = true; } - - fn addFace(self: *PrimitiveMesh, faceData: FaceData, fromNeighborChunk: ?u3) void { - if(fromNeighborChunk) |neighbor| { - self.neighborFacesSameLod[neighbor].append(main.globalAllocator, faceData); - } else { - self.coreFaces.append(main.globalAllocator, faceData); - } - } - - fn removeFace(self: *PrimitiveMesh, faceData: FaceData, fromNeighborChunk: ?u3) void { - if(fromNeighborChunk) |neighbor| { - var pos: usize = std.math.maxInt(usize); - for(self.neighborFacesSameLod[neighbor].items, 0..) |item, i| { - if(std.meta.eql(faceData, item)) { - pos = i; - break; - } - } - _ = self.neighborFacesSameLod[neighbor].swapRemove(pos); - } else { - var pos: usize = std.math.maxInt(usize); - for(self.coreFaces.items, 0..) |item, i| { - if(std.meta.eql(faceData, item)) { - pos = i; - break; - } - } - _ = self.coreFaces.swapRemove(pos); - } - } }; pub const ChunkMesh = struct { @@ -669,14 +653,19 @@ pub const ChunkMesh = struct { if(canBeSeenThroughOtherBlock(block, neighborBlock, i)) { if(block.transparent()) { if(block.hasBackFace()) { - self.transparentMesh.appendCore(constructFaceData(block, i ^ 1, x, y, z, true)); + self.transparentMesh.appendNeighborFacingQuadsToCore(block, i ^ 1, x, y, z, true); } - self.transparentMesh.appendCore(constructFaceData(block, i, x2, y2, z2, false)); + self.transparentMesh.appendNeighborFacingQuadsToCore(block, i, x2, y2, z2, false); } else { - self.opaqueMesh.appendCore(constructFaceData(block, i, x2, y2, z2, false)); // TODO: Create multiple faces for non-cube meshes. + self.opaqueMesh.appendNeighborFacingQuadsToCore(block, i, x2, y2, z2, false); } } } + if(block.transparent()) { + self.transparentMesh.appendInternalQuadsToCore(block, x, y, z, false); + } else { + self.opaqueMesh.appendInternalQuadsToCore(block, x, y, z, false); + } } } } @@ -698,28 +687,11 @@ pub const ChunkMesh = struct { self.finishNeighbors(); } - fn addFace(self: *ChunkMesh, faceData: FaceData, fromNeighborChunk: ?u3, transparent: bool) void { - if(transparent) { - self.transparentMesh.addFace(faceData, fromNeighborChunk); - } else { - self.opaqueMesh.addFace(faceData, fromNeighborChunk); - } - } - - fn removeFace(self: *ChunkMesh, faceData: FaceData, fromNeighborChunk: ?u3, transparent: bool) void { - if(transparent) { - self.transparentMesh.removeFace(faceData, fromNeighborChunk); - } else { - self.opaqueMesh.removeFace(faceData, fromNeighborChunk); - } - } - pub fn updateBlock(self: *ChunkMesh, _x: i32, _y: i32, _z: i32, newBlock: Block) void { self.mutex.lock(); const x = _x & chunk.chunkMask; const y = _y & chunk.chunkMask; const z = _z & chunk.chunkMask; - const oldBlock = self.chunk.blocks[chunk.getIndex(x, y, z)]; self.chunk.blocks[chunk.getIndex(x, y, z)] = newBlock; self.mutex.unlock(); for(self.lightingData[0..]) |lightingData| { @@ -732,118 +704,36 @@ pub const ChunkMesh = struct { } self.mutex.lock(); defer self.mutex.unlock(); - for(chunk.Neighbors.iterable) |neighbor| { - var neighborMesh = self; - var nx = x + chunk.Neighbors.relX[neighbor]; - var ny = y + chunk.Neighbors.relY[neighbor]; - var nz = z + chunk.Neighbors.relZ[neighbor]; - if(nx & chunk.chunkMask != nx or ny & chunk.chunkMask != ny or nz & chunk.chunkMask != nz) { // Outside this chunk. - neighborMesh = mesh_storage.getNeighborFromRenderThread(self.pos, self.pos.voxelSize, neighbor) orelse continue; - } - if(neighborMesh != self) { - self.mutex.unlock(); - deadlockFreeDoubleLock(&self.mutex, &neighborMesh.mutex); - } - defer if(neighborMesh != self) neighborMesh.mutex.unlock(); - nx &= chunk.chunkMask; - ny &= chunk.chunkMask; - nz &= chunk.chunkMask; - const neighborBlock = neighborMesh.chunk.blocks[chunk.getIndex(nx, ny, nz)]; - { // TODO: Batch all the changes and apply them in one go for more efficiency. - { // The face of the changed block - const newVisibility = canBeSeenThroughOtherBlock(newBlock, neighborBlock, neighbor); - const oldVisibility = canBeSeenThroughOtherBlock(oldBlock, neighborBlock, neighbor); - if(oldVisibility) { // Removing the face - const faceData = constructFaceData(oldBlock, neighbor, nx, ny, nz, false); - if(neighborMesh == self) { - self.removeFace(faceData, null, oldBlock.transparent()); - } else { - neighborMesh.removeFace(faceData, neighbor ^ 1, oldBlock.transparent()); - } - if(oldBlock.hasBackFace()) { - const backFaceData = constructFaceData(oldBlock, neighbor ^ 1, x, y, z, true); - if(neighborMesh == self) { - self.removeFace(backFaceData, null, true); - } else { - self.removeFace(backFaceData, neighbor, true); - } - } - } - if(newVisibility) { // Adding the face - const faceData = constructFaceData(newBlock, neighbor, nx, ny, nz, false); - if(neighborMesh == self) { - self.addFace(faceData, null, newBlock.transparent()); - } else { - neighborMesh.addFace(faceData, neighbor ^ 1, newBlock.transparent()); - } - if(newBlock.hasBackFace()) { - const backFaceData = constructFaceData(newBlock, neighbor ^ 1, x, y, z, true); - if(neighborMesh == self) { - self.addFace(backFaceData, null, true); - } else { - self.addFace(backFaceData, neighbor, true); - } - } - } - } - { // The face of the neighbor block - const newVisibility = canBeSeenThroughOtherBlock(neighborBlock, newBlock, neighbor ^ 1); - if(canBeSeenThroughOtherBlock(neighborBlock, oldBlock, neighbor ^ 1) != newVisibility) { - if(newVisibility) { // Adding the face - const faceData = constructFaceData(neighborBlock, neighbor ^ 1, x, y, z, false); - if(neighborMesh == self) { - self.addFace(faceData, null, neighborBlock.transparent()); - } else { - self.addFace(faceData, neighbor, neighborBlock.transparent()); - } - if(neighborBlock.hasBackFace()) { - const backFaceData = constructFaceData(neighborBlock, neighbor, nx, ny, nz, true); - if(neighborMesh == self) { - self.addFace(backFaceData, null, true); - } else { - neighborMesh.addFace(backFaceData, neighbor ^ 1, true); - } - } - } else { // Removing the face - const faceData = constructFaceData(neighborBlock, neighbor ^ 1, x, y, z, false); - if(neighborMesh == self) { - self.removeFace(faceData, null, neighborBlock.transparent()); - } else { - self.removeFace(faceData, neighbor, neighborBlock.transparent()); - } - if(neighborBlock.hasBackFace()) { - const backFaceData = constructFaceData(neighborBlock, neighbor, nx, ny, nz, true); - if(neighborMesh == self) { - self.removeFace(backFaceData, null, true); - } else { - neighborMesh.removeFace(backFaceData, neighbor ^ 1, true); - } - } - } - } - } - } - if(neighborMesh != self) { - _ = neighborMesh.needsLightRefresh.swap(false, .AcqRel); - neighborMesh.finishData(); - neighborMesh.uploadData(); - } + // Update neighbor chunks: + if(x == 0) { + self.lastNeighborsHigherLod[chunk.Neighbors.dirNegX] = null; + self.lastNeighborsSameLod[chunk.Neighbors.dirNegX] = null; + } else if(x == 31) { + self.lastNeighborsHigherLod[chunk.Neighbors.dirPosX] = null; + self.lastNeighborsSameLod[chunk.Neighbors.dirPosX] = null; } - _ = self.needsLightRefresh.swap(false, .AcqRel); - self.finishData(); + if(y == 0) { + self.lastNeighborsHigherLod[chunk.Neighbors.dirNegY] = null; + self.lastNeighborsSameLod[chunk.Neighbors.dirNegY] = null; + } else if(y == 31) { + self.lastNeighborsHigherLod[chunk.Neighbors.dirPosY] = null; + self.lastNeighborsSameLod[chunk.Neighbors.dirPosY] = null; + } + if(z == 0) { + self.lastNeighborsHigherLod[chunk.Neighbors.dirDown] = null; + self.lastNeighborsSameLod[chunk.Neighbors.dirDown] = null; + } else if(z == 31) { + self.lastNeighborsHigherLod[chunk.Neighbors.dirUp] = null; + self.lastNeighborsSameLod[chunk.Neighbors.dirUp] = null; + } + self.opaqueMesh.coreFaces.clearRetainingCapacity(); + self.transparentMesh.coreFaces.clearRetainingCapacity(); + self.mutex.unlock(); + self.generateMesh(); // TODO: Batch mesh updates instead of applying them for each block changes. + self.mutex.lock(); self.uploadData(); } - pub inline fn constructFaceData(block: Block, normal: u3, x: i32, y: i32, z: i32, comptime backFace: bool) FaceData { - const model = blocks.meshes.model(block); - const quadIndex = models.models.items[model.modelIndex].quads[normal]; - const texture = blocks.meshes.textureIndex(block, models.quads.items[quadIndex].textureSlot); - return FaceData { - .position = .{.x = @intCast(x), .y = @intCast(y), .z = @intCast(z), .permutation = model.permutation.toInt(), .isBackFace = backFace}, - .blockAndQuad = .{.texture = texture, .quadIndex = quadIndex}, // TODO: Expand the meshing algorithm to allow non-cuboid models. - }; - } - fn clearNeighbor(self: *ChunkMesh, neighbor: u3, comptime isLod: bool) void { self.opaqueMesh.clearNeighbor(neighbor, isLod); self.transparentMesh.clearNeighbor(neighbor, isLod); @@ -920,21 +810,21 @@ pub const ChunkMesh = struct { if(canBeSeenThroughOtherBlock(block, otherBlock, neighbor)) { if(block.transparent()) { if(block.hasBackFace()) { - self.transparentMesh.appendNeighbor(constructFaceData(block, neighbor ^ 1, x, y, z, true), neighbor, false); + self.transparentMesh.appendNeighborFacingQuadsToNeighbor(block, neighbor ^ 1, x, y, z, true, false); } - neighborMesh.transparentMesh.appendNeighbor(constructFaceData(block, neighbor, otherX, otherY, otherZ, false), neighbor ^ 1, false); + neighborMesh.transparentMesh.appendNeighborFacingQuadsToNeighbor(block, neighbor, otherX, otherY, otherZ, false, false); } else { - neighborMesh.opaqueMesh.appendNeighbor(constructFaceData(block, neighbor, otherX, otherY, otherZ, false), neighbor ^ 1, false); + neighborMesh.opaqueMesh.appendNeighborFacingQuadsToNeighbor(block, neighbor, otherX, otherY, otherZ, false, false); } } if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor ^ 1)) { if(otherBlock.transparent()) { if(otherBlock.hasBackFace()) { - neighborMesh.transparentMesh.appendNeighbor(constructFaceData(otherBlock, neighbor, otherX, otherY, otherZ, true), neighbor ^ 1, false); + neighborMesh.transparentMesh.appendNeighborFacingQuadsToNeighbor(otherBlock, neighbor, otherX, otherY, otherZ, true, false); } - self.transparentMesh.appendNeighbor(constructFaceData(otherBlock, neighbor ^ 1, x, y, z, false), neighbor, false); + self.transparentMesh.appendNeighborFacingQuadsToNeighbor(otherBlock, neighbor ^ 1, x, y, z, false, false); } else { - self.opaqueMesh.appendNeighbor(constructFaceData(otherBlock, neighbor ^ 1, x, y, z, false), neighbor, false); + self.opaqueMesh.appendNeighborFacingQuadsToNeighbor(otherBlock, neighbor ^ 1, x, y, z, false, false); } } } @@ -1000,14 +890,14 @@ pub const ChunkMesh = struct { const otherBlock = (&neighborMesh.chunk.blocks)[chunk.getIndex(otherX, otherY, otherZ)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed. if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor ^ 1)) { if(otherBlock.transparent()) { - self.transparentMesh.appendNeighbor(constructFaceData(otherBlock, neighbor ^ 1, x, y, z, false), neighbor, true); + self.transparentMesh.appendNeighborFacingQuadsToNeighbor(otherBlock, neighbor ^ 1, x, y, z, false, true); } else { - self.opaqueMesh.appendNeighbor(constructFaceData(otherBlock, neighbor ^ 1, x, y, z, false), neighbor, true); + self.opaqueMesh.appendNeighborFacingQuadsToNeighbor(otherBlock, neighbor ^ 1, x, y, z, false, true); } } if(block.hasBackFace()) { if(canBeSeenThroughOtherBlock(block, otherBlock, neighbor)) { - self.transparentMesh.appendNeighbor(constructFaceData(block, neighbor ^ 1, x, y, z, true), neighbor, true); + self.transparentMesh.appendNeighborFacingQuadsToNeighbor(block, neighbor ^ 1, x, y, z, true, true); } } }