diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.fs b/assets/cubyz/shaders/chunks/chunk_fragment.fs index 82aa7bce..45e27f4b 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.fs +++ b/assets/cubyz/shaders/chunks/chunk_fragment.fs @@ -4,7 +4,7 @@ in vec3 mvVertexPos; in vec3 direction; in vec2 uv; flat in vec3 normal; -flat in int textureIndex; +flat in uint textureIndexOffset; flat in int isBackFace; flat in int ditherSeed; flat in uint lightBufferIndex; @@ -41,6 +41,11 @@ layout(std430, binding = 10) buffer _lightData uint lightData[]; }; +layout(std430, binding = 11) buffer _textureData +{ + uint textureData[]; +}; + float lightVariation(vec3 normal) { const vec3 directionalPart = vec3(0, contrast/2, contrast); const float baseLighting = 1 - contrast; @@ -106,8 +111,16 @@ vec3 readLightValue() { return max(sunLight*ambientLight, blockLight)/31; } +uint readTextureIndex() { + uint x = clamp(uint(lightPosition.x), 0, lightArea.x - 2); + uint y = clamp(uint(lightPosition.y), 0, lightArea.y - 2); + uint index = textureIndexOffset + x*(lightArea.y - 1) + y; + return textureData[index >> 1] >> 16*(index & 1u) & 65535u; +} + void main() { vec3 light = readLightValue(); + uint textureIndex = readTextureIndex(); float animatedTextureIndex = animatedTexture[textureIndex]; float normalVariation = lightVariation(normal); vec3 textureCoords = vec3(uv, animatedTextureIndex); diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vs b/assets/cubyz/shaders/chunks/chunk_vertex.vs index 6c00e325..20805ce4 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vs +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vs @@ -4,7 +4,7 @@ out vec3 mvVertexPos; out vec3 direction; out vec2 uv; flat out vec3 normal; -flat out int textureIndex; +flat out uint textureIndexOffset; flat out int isBackFace; flat out int ditherSeed; flat out uint lightBufferIndex; @@ -51,9 +51,11 @@ struct ChunkData { int voxelSize; uint vertexStartOpaque; uint lightStartOpaque; + uint textureStartOpaque; uint faceCountsByNormalOpaque[7]; uint vertexStartTransparent; uint lightStartTransparent; + uint textureStartTransparent; uint vertexCountTransparent; uint visibilityState; uint oldVisibilityState; @@ -104,11 +106,11 @@ void main() { ); lightBufferIndex = (transparent ? chunks[chunkID].lightStartTransparent : chunks[chunkID].lightStartOpaque) + faceData[faceID].lightBufferIndex; lightArea = quadSize + uvec2(1, 1); - lightPosition = vec2(vertexID >> 1, vertexID & 1); + lightPosition = vec2(vertexID >> 1, vertexID & 1)*quadSize; isBackFace = encodedPosition>>31 & 1; ditherSeed = encodedPosition & 15; - textureIndex = textureAndQuad & 65535; + textureIndexOffset = (transparent ? chunks[chunkID].textureStartTransparent : chunks[chunkID].textureStartOpaque) + uint(textureAndQuad & 65535); int quadIndex = textureAndQuad >> 16; vec3 position = vec3( diff --git a/assets/cubyz/shaders/chunks/fillIndirectBuffer.glsl b/assets/cubyz/shaders/chunks/fillIndirectBuffer.glsl index dea6c167..1529c218 100644 --- a/assets/cubyz/shaders/chunks/fillIndirectBuffer.glsl +++ b/assets/cubyz/shaders/chunks/fillIndirectBuffer.glsl @@ -15,9 +15,11 @@ struct ChunkData { int voxelSize; uint vertexStartOpaque; uint lightStartOpaque; + uint textureStartOpaque; uint faceCountsByNormalOpaque[7]; uint vertexStartTransparent; uint lightStartTransparent; + uint textureStartTransparent; uint vertexCountTransparent; uint visibilityState; uint oldVisibilityState; diff --git a/assets/cubyz/shaders/chunks/occlusionTestFragment.fs b/assets/cubyz/shaders/chunks/occlusionTestFragment.fs index 0f647c44..957ee3d1 100644 --- a/assets/cubyz/shaders/chunks/occlusionTestFragment.fs +++ b/assets/cubyz/shaders/chunks/occlusionTestFragment.fs @@ -12,9 +12,11 @@ struct ChunkData { int voxelSize; uint vertexStartOpaque; uint lightStartOpaque; + uint textureStartOpaque; uint faceCountsByNormalOpaque[7]; uint vertexStartTransparent; uint lightStartTransparent; + uint textureStartTransparent; uint vertexCountTransparent; uint visibilityState; uint oldVisibilityState; diff --git a/assets/cubyz/shaders/chunks/occlusionTestVertex.vs b/assets/cubyz/shaders/chunks/occlusionTestVertex.vs index 3923f610..762296c7 100644 --- a/assets/cubyz/shaders/chunks/occlusionTestVertex.vs +++ b/assets/cubyz/shaders/chunks/occlusionTestVertex.vs @@ -10,9 +10,11 @@ struct ChunkData { int voxelSize; uint vertexStartOpaque; uint lightStartOpaque; + uint textureStartOpaque; uint faceCountsByNormalOpaque[7]; uint vertexStartTransparent; uint lightStartTransparent; + uint textureStartTransparent; uint vertexCountTransparent; uint visibilityState; uint oldVisibilityState; diff --git a/assets/cubyz/shaders/chunks/transparent_fragment.fs b/assets/cubyz/shaders/chunks/transparent_fragment.fs index a05b3d91..02326c7a 100644 --- a/assets/cubyz/shaders/chunks/transparent_fragment.fs +++ b/assets/cubyz/shaders/chunks/transparent_fragment.fs @@ -4,7 +4,7 @@ in vec3 mvVertexPos; in vec3 direction; in vec2 uv; flat in vec3 normal; -flat in int textureIndex; +flat in uint textureIndexOffset; flat in int isBackFace; flat in int ditherSeed; flat in uint lightBufferIndex; @@ -49,6 +49,11 @@ layout(std430, binding = 10) buffer _lightData uint lightData[]; }; +layout(std430, binding = 11) buffer _textureData +{ + uint textureData[]; +}; + float lightVariation(vec3 normal) { const vec3 directionalPart = vec3(0, contrast/2, contrast); const float baseLighting = 1 - contrast; @@ -142,8 +147,16 @@ vec3 readLightValue() { return max(sunLight*ambientLight, blockLight)/31; } +uint readTextureIndex() { + uint x = clamp(uint(lightPosition.x), 0, lightArea.x - 2); + uint y = clamp(uint(lightPosition.y), 0, lightArea.y - 2); + uint index = textureIndexOffset + x*(lightArea.y - 1) + y; + return textureData[index >> 1] >> 16*(index & 1u) & 65535u; +} + void main() { vec3 light = readLightValue(); + uint textureIndex = readTextureIndex(); float animatedTextureIndex = animatedTexture[textureIndex]; vec3 textureCoords = vec3(uv, animatedTextureIndex); float normalVariation = lightVariation(normal); diff --git a/src/chunk.zig b/src/chunk.zig index cbee8e8d..5d67934f 100644 --- a/src/chunk.zig +++ b/src/chunk.zig @@ -37,6 +37,15 @@ pub const Neighbors = struct { // TODO: Should this be an enum? pub const relY = [_]i32 {0, 0, 0, 0, 1, -1}; /// Index to relative position pub const relZ = [_]i32 {1, -1, 0, 0, 0, 0}; + /// Index to relative position vector + pub const relVec = [_]Vec3i { + .{0, 0, 1}, + .{0, 0, -1}, + .{1, 0, 0}, + .{-1, 0, 0}, + .{0, 1, 0}, + .{0, -1, 0}, + }; /// Index to bitMask for bitmap direction data pub const bitMask = [_]u6 {0x01, 0x02, 0x04, 0x08, 0x10, 0x20}; /// To iterate over all neighbors easily diff --git a/src/graphics.zig b/src/graphics.zig index 1350e652..389e44be 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -1951,6 +1951,7 @@ const block_texture = struct { }; pub fn generateBlockTexture(blockType: u16) Texture { + if(true) return undefined; // TODO const block = main.blocks.Block{.typ = blockType, .data = 0}; // TODO: Use natural standard data. const textureSize = block_texture.textureSize; c.glViewport(0, 0, textureSize, textureSize); diff --git a/src/models.zig b/src/models.zig index 11d2efff..274974a9 100644 --- a/src/models.zig +++ b/src/models.zig @@ -29,6 +29,7 @@ const ExtraQuadInfo = struct { greedyMeshable: bool, greedyMeshingXDir: ?u3, greedyMeshingYDir: ?u3, + offsetByNormal: bool, }; const gridSize = 4096; @@ -127,11 +128,11 @@ pub const Model = struct { for(&quad.corners) |*corner| { corner.* -= quad.normal; } - const quadIndex = addQuad(quad) catch continue; + const quadIndex = addQuad(quad, true) catch continue; self.neighborFacingQuads[neighbor][indices[neighbor]] = quadIndex; indices[neighbor] += 1; } else { - const quadIndex = addQuad(quad) catch continue; + const quadIndex = addQuad(quad, false) catch continue; self.internalQuads[internalIndex] = quadIndex; internalIndex += 1; } @@ -196,21 +197,6 @@ pub const Model = struct { } return Model.init(quadList.items); } - - 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; @@ -228,7 +214,7 @@ pub var models: main.List(Model) = undefined; pub var fullCube: u16 = undefined; pub const greedyMeshableQuads: u16 = 6; -var quadDeduplication: std.AutoHashMap([@sizeOf(QuadInfo)]u8, u16) = undefined; +var quadDeduplication: std.AutoHashMap([@sizeOf(QuadInfo) + 1]u8, u16) = undefined; fn getLineGreedyMeshingDir(corner0: Vec3f, corner1: Vec3f, corner0UV: Vec2f, corner1UV: Vec2f) ?u3 { // One component must wrap around, while the other 2 compoenents must be equal: @@ -258,8 +244,8 @@ fn getLineGreedyMeshingDir(corner0: Vec3f, corner1: Vec3f, corner0UV: Vec2f, cor return null; } -fn addQuad(info: QuadInfo) error{Degenerate}!u16 { - if(quadDeduplication.get(std.mem.toBytes(info))) |id| { +fn addQuad(info: QuadInfo, offsetByNormal: bool) error{Degenerate}!u16 { + if(quadDeduplication.get(std.mem.toBytes(info) ++ .{@intFromBool(offsetByNormal)})) |id| { return id; } // Check if it's degenerate: @@ -272,7 +258,7 @@ fn addQuad(info: QuadInfo) error{Degenerate}!u16 { if(cornerEqualities >= 2) return error.Degenerate; // One corner equality is fine, since then the quad degenerates to a triangle, which has a non-zero area. const index: u16 = @intCast(quads.items.len); quads.append(info); - quadDeduplication.put(std.mem.toBytes(info), index) catch unreachable; + quadDeduplication.put(std.mem.toBytes(info) ++ .{@intFromBool(offsetByNormal)}, index) catch unreachable; var extraQuadInfo: ExtraQuadInfo = undefined; extraQuadInfo.faceNeighbor = Model.getFaceNeighbor(&info); @@ -315,6 +301,7 @@ fn addQuad(info: QuadInfo) error{Degenerate}!u16 { } extraQuadInfo.greedyMeshable = extraQuadInfo.greedyMeshingXDir != null or extraQuadInfo.greedyMeshingYDir != null; } + extraQuadInfo.offsetByNormal = offsetByNormal; extraQuadInfos.append(extraQuadInfo); @@ -385,7 +372,7 @@ pub fn init() void { models = main.List(Model).init(main.globalAllocator); quads = main.List(QuadInfo).init(main.globalAllocator); extraQuadInfos = main.List(ExtraQuadInfo).init(main.globalAllocator); - quadDeduplication = std.AutoHashMap([@sizeOf(QuadInfo)]u8, u16).init(main.globalAllocator.allocator); + quadDeduplication = std.AutoHashMap([@sizeOf(QuadInfo) + 1]u8, u16).init(main.globalAllocator.allocator); nameToIndex = std.StringHashMap(u16).init(main.globalAllocator.allocator); diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index a7c4a879..fafac588 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -68,6 +68,7 @@ var vbo: c_uint = undefined; var faces: main.List(u32) = undefined; pub var faceBuffer: graphics.LargeBuffer(FaceData) = undefined; pub var lightBuffer: graphics.LargeBuffer(u32) = undefined; +pub var textureBuffer: graphics.LargeBuffer(u16) = undefined; pub var chunkBuffer: graphics.LargeBuffer(ChunkData) = undefined; pub var commandBuffer: graphics.LargeBuffer(IndirectData) = undefined; pub var chunkIDBuffer: graphics.LargeBuffer(u32) = undefined; @@ -97,6 +98,7 @@ pub fn init() void { faces = main.List(u32).initCapacity(main.globalAllocator, 65536); // TODO: What is this used for? faceBuffer.init(main.globalAllocator, 1 << 20, 3); lightBuffer.init(main.globalAllocator, 1 << 20, 10); + textureBuffer.init(main.globalAllocator, 1 << 20, 11); chunkBuffer.init(main.globalAllocator, 1 << 20, 6); commandBuffer.init(main.globalAllocator, 1 << 20, 8); chunkIDBuffer.init(main.globalAllocator, 1 << 20, 9); @@ -112,6 +114,7 @@ pub fn deinit() void { faces.deinit(); faceBuffer.deinit(); lightBuffer.deinit(); + textureBuffer.deinit(); chunkBuffer.deinit(); commandBuffer.deinit(); chunkIDBuffer.deinit(); @@ -120,6 +123,7 @@ pub fn deinit() void { pub fn beginRender() void { faceBuffer.beginRender(); lightBuffer.beginRender(); + textureBuffer.beginRender(); chunkBuffer.beginRender(); commandBuffer.beginRender(); chunkIDBuffer.beginRender(); @@ -128,6 +132,7 @@ pub fn beginRender() void { pub fn endRender() void { faceBuffer.endRender(); lightBuffer.endRender(); + textureBuffer.endRender(); chunkBuffer.endRender(); commandBuffer.endRender(); chunkIDBuffer.endRender(); @@ -250,15 +255,15 @@ pub const FaceData = extern struct { isBackFace: bool, }, blockAndQuad: packed struct(u32) { - texture: u16, + textureBufferIndex: u16, quadIndex: u16, }, lightBufferIndex: u32, - pub inline fn init(texture: u16, quadIndex: u16, x: i32, y: i32, z: i32, comptime backFace: bool) FaceData { + pub inline fn init(quadIndex: u16, x: i32, y: i32, z: i32, comptime backFace: bool) FaceData { return FaceData { .position = .{.x = @intCast(x), .y = @intCast(y), .z = @intCast(z), .xSizeMinusOne = 0, .ySizeMinusOne = 0, .isBackFace = backFace}, - .blockAndQuad = .{.texture = texture, .quadIndex = quadIndex}, + .blockAndQuad = .{.textureBufferIndex = undefined, .quadIndex = quadIndex}, .lightBufferIndex = undefined, }; } @@ -272,9 +277,11 @@ pub const ChunkData = extern struct { voxelSize: i32, vertexStartOpaque: u32, lightStartOpaque: u32, + textureStartOpaque: u32, faceCountsByNormalOpaque: [7]u32, vertexStartTransparent: u32, lightStartTransparent: u32, + textureStartTransparent: u32, vertexCountTransparent: u32, visibilityState: u32, oldVisibilityState: u32, @@ -289,26 +296,34 @@ pub const IndirectData = extern struct { }; const PrimitiveMesh = struct { - coreFaces: main.ListUnmanaged(FaceData) = .{}, - neighborFacesSameLod: [6]main.ListUnmanaged(FaceData) = [_]main.ListUnmanaged(FaceData){.{}} ** 6, - neighborFacesHigherLod: [6]main.ListUnmanaged(FaceData) = [_]main.ListUnmanaged(FaceData){.{}} ** 6, + const ExtendedFaceData = struct { + face: FaceData, + textures: []u16, + }; + coreFaces: main.ListUnmanaged(ExtendedFaceData) = .{}, + neighborFacesSameLod: [6]main.ListUnmanaged(ExtendedFaceData) = [_]main.ListUnmanaged(ExtendedFaceData){.{}} ** 6, + neighborFacesHigherLod: [6]main.ListUnmanaged(ExtendedFaceData) = [_]main.ListUnmanaged(ExtendedFaceData){.{}} ** 6, completeList: []FaceData = &.{}, completeLightList: []u32 = &.{}, + completeTextureList: []u16 = &.{}, coreLen: u32 = 0, sameLodLens: [6]u32 = .{0} ** 6, higherLodLens: [6]u32 = .{0} ** 6, mutex: std.Thread.Mutex = .{}, bufferAllocation: graphics.SubAllocation = .{.start = 0, .len = 0}, lightBufferAllocation: graphics.SubAllocation = .{.start = 0, .len = 0}, + textureBufferAllocation: graphics.SubAllocation = .{.start = 0, .len = 0}, vertexCount: u31 = 0, byNormalCount: [7]u32 = .{0} ** 7, wasChanged: bool = false, min: Vec3f = undefined, max: Vec3f = undefined, + textureArena: main.utils.NeverFailingArenaAllocator = main.utils.NeverFailingArenaAllocator.init(main.globalAllocator), fn deinit(self: *PrimitiveMesh) void { faceBuffer.free(self.bufferAllocation); lightBuffer.free(self.lightBufferAllocation); + textureBuffer.free(self.textureBufferAllocation); self.coreFaces.deinit(main.globalAllocator); for(&self.neighborFacesSameLod) |*neighborFaces| { neighborFaces.deinit(main.globalAllocator); @@ -316,8 +331,10 @@ const PrimitiveMesh = struct { for(&self.neighborFacesHigherLod) |*neighborFaces| { neighborFaces.deinit(main.globalAllocator); } + self.textureArena.deinit(); main.globalAllocator.free(self.completeList); main.globalAllocator.free(self.completeLightList); + main.globalAllocator.free(self.completeTextureList); } fn reset(self: *PrimitiveMesh) void { @@ -342,10 +359,15 @@ const PrimitiveMesh = struct { 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) { - models.models.items[model].appendNeighborFacingQuadsToList(&self.neighborFacesHigherLod[neighbor ^ 1], main.globalAllocator, block, neighbor, x, y, z, backFace); - } else { - models.models.items[model].appendNeighborFacingQuadsToList(&self.neighborFacesSameLod[neighbor ^ 1], main.globalAllocator, block, neighbor, x, y, z, backFace); + for(models.models.items[model].neighborFacingQuads[neighbor]) |quadIndex| { + const texture = main.blocks.meshes.textureIndex(block, main.models.quads.items[quadIndex].textureSlot); + const textures = self.textureArena.allocator().alloc(u16, 1); + textures[0] = texture; + if(isLod) { + self.neighborFacesHigherLod[neighbor ^ 1].append(main.globalAllocator, .{.face = FaceData.init(quadIndex, x, y, z, backFace), .textures = textures}); + } else { + self.neighborFacesSameLod[neighbor ^ 1].append(main.globalAllocator, .{.face = FaceData.init(quadIndex, x, y, z, backFace), .textures = textures}); + } } } @@ -366,15 +388,28 @@ const PrimitiveMesh = struct { len += neighborFaces.items.len; } const completeList = main.globalAllocator.alloc(FaceData, len); + var completeTextureList = main.List(u16).init(main.globalAllocator); var i: usize = 0; - @memcpy(completeList[i..][0..self.coreFaces.items.len], self.coreFaces.items); + for(completeList[i..][0..self.coreFaces.items.len], self.coreFaces.items) |*result, face| { + result.* = face.face; + result.blockAndQuad.textureBufferIndex = @intCast(completeTextureList.items.len); + completeTextureList.appendSlice(face.textures); + } i += self.coreFaces.items.len; for(self.neighborFacesSameLod) |neighborFaces| { - @memcpy(completeList[i..][0..neighborFaces.items.len], neighborFaces.items); + for(completeList[i..][0..neighborFaces.items.len], neighborFaces.items) |*result, face| { + result.* = face.face; + result.blockAndQuad.textureBufferIndex = @intCast(completeTextureList.items.len); + completeTextureList.appendSlice(face.textures); + } i += neighborFaces.items.len; } for(self.neighborFacesHigherLod) |neighborFaces| { - @memcpy(completeList[i..][0..neighborFaces.items.len], neighborFaces.items); + for(completeList[i..][0..neighborFaces.items.len], neighborFaces.items) |*result, face| { + result.* = face.face; + result.blockAndQuad.textureBufferIndex = @intCast(completeTextureList.items.len); + completeTextureList.appendSlice(face.textures); + } i += neighborFaces.items.len; } @@ -408,6 +443,8 @@ const PrimitiveMesh = struct { self.completeList = completeList; const oldLightList = self.completeLightList; self.completeLightList = completeLightList; + const oldTextureList = self.completeTextureList; + self.completeTextureList = completeTextureList.toOwnedSlice(); self.coreLen = @intCast(self.coreFaces.items.len); for(self.neighborFacesSameLod, 0..) |neighborFaces, j| { self.sameLodLens[j] = @intCast(neighborFaces.items.len); @@ -418,6 +455,7 @@ const PrimitiveMesh = struct { self.mutex.unlock(); main.globalAllocator.free(oldList); main.globalAllocator.free(oldLightList); + main.globalAllocator.free(oldTextureList); } fn getValues(mesh: *ChunkMesh, wx: i32, wy: i32, wz: i32) [6]u8 { @@ -600,6 +638,7 @@ const PrimitiveMesh = struct { const fullBuffer = faceBuffer.allocateAndMapRange(len, &self.bufferAllocation); defer faceBuffer.unmapRange(fullBuffer); lightBuffer.uploadData(self.completeLightList, &self.lightBufferAllocation); + textureBuffer.uploadData(self.completeTextureList, &self.textureBufferAllocation); // Sort the faces by normal to allow for backface culling on the GPU: var i: u32 = 0; var iStart = i; @@ -900,40 +939,63 @@ pub const ChunkMesh = struct { } } } - fn appendNeighborFacingQuadsToFaceList(list: *main.List(main.ListUnmanaged(FaceData)), quadToListIndex: []u16, block: Block, neighbor: u3, x: i32, y: i32, z: i32, comptime backFace: bool) void { + fn appendNeighborFacingQuadsToFaceList(mesh: *PrimitiveMesh, list: *main.List(main.ListUnmanaged(FaceData)), quadToListIndex: []u16, block: Block, neighbor: u3, x: i32, y: i32, z: i32, comptime backFace: bool) void { const model = blocks.meshes.model(block); for(models.models.items[model].neighborFacingQuads[neighbor]) |quadIndex| { - const texture = main.blocks.meshes.textureIndex(block, main.models.quads.items[quadIndex].textureSlot); const greedyMeshable = main.models.extraQuadInfos.items[quadIndex].greedyMeshable; - if(greedyMeshable) { + if(greedyMeshable and !block.transparent()) { // TODO: Greedy mesh transparent quads if(quadToListIndex[quadIndex] == 0) { quadToListIndex[quadIndex] = @intCast(list.items.len); list.append(.{}); } - list.items[quadToListIndex[quadIndex]].append(main.stackAllocator, FaceData.init(texture, quadIndex, x, y, z, backFace)); + list.items[quadToListIndex[quadIndex]].append(main.stackAllocator, FaceData.init(quadIndex, x, y, z, backFace)); } else { - list.items[0].append(main.stackAllocator, FaceData.init(texture, quadIndex, x, y, z, backFace)); + const texture = main.blocks.meshes.textureIndex(block, main.models.quads.items[quadIndex].textureSlot); + const textures = mesh.textureArena.allocator().alloc(u16, 1); + textures[0] = texture; + mesh.coreFaces.append(main.globalAllocator, .{.face = FaceData.init(quadIndex, x, y, z, backFace), .textures = textures}); } } } - fn appendInternalQuadsToFaceList(list: *main.List(main.ListUnmanaged(FaceData)), quadToListIndex: []u16, block: Block, x: i32, y: i32, z: i32, comptime backFace: bool) void { + fn appendInternalQuadsToFaceList(mesh: *PrimitiveMesh, list: *main.List(main.ListUnmanaged(FaceData)), quadToListIndex: []u16, block: Block, x: i32, y: i32, z: i32, comptime backFace: bool) void { const model = blocks.meshes.model(block); for(models.models.items[model].internalQuads) |quadIndex| { - const texture = main.blocks.meshes.textureIndex(block, main.models.quads.items[quadIndex].textureSlot); const greedyMeshable = main.models.extraQuadInfos.items[quadIndex].greedyMeshable; - if(greedyMeshable) { + if(greedyMeshable and !block.transparent()) { // TODO: Greedy mesh transparent quads if(quadToListIndex[quadIndex] == 0) { quadToListIndex[quadIndex] = @intCast(list.items.len); list.append(.{}); } - list.items[quadToListIndex[quadIndex]].append(main.stackAllocator, FaceData.init(texture, quadIndex, x, y, z, backFace)); + list.items[quadToListIndex[quadIndex]].append(main.stackAllocator, FaceData.init(quadIndex, x, y, z, backFace)); } else { - list.items[0].append(main.stackAllocator, FaceData.init(texture, quadIndex, x, y, z, backFace)); + const texture = main.blocks.meshes.textureIndex(block, main.models.quads.items[quadIndex].textureSlot); + const textures = mesh.textureArena.allocator().alloc(u16, 1); + textures[0] = texture; + mesh.coreFaces.append(main.globalAllocator, .{.face = FaceData.init(quadIndex, x, y, z, backFace), .textures = textures}); } } } - fn greedyMeshQuadType(mesh: *PrimitiveMesh, faceList: []const FaceData) void { + fn getTexturesOfGreedyMesh(self: *ChunkMesh, mesh: *PrimitiveMesh, facePos: Vec3i, quadIndex: u16, xDir: u3, yDir: u3, xLenMinusOne: usize, yLenMinusOne: usize) []u16 { + const extraQuadInfo = &models.extraQuadInfos.items[quadIndex]; + const textures = mesh.textureArena.allocator().alloc(u16, (xLenMinusOne + 1)*(yLenMinusOne + 1)); + for(0..xLenMinusOne + 1) |quadX| { + for(0..yLenMinusOne + 1) |quadY| { + var blockPos: Vec3i = facePos; + blockPos += chunk.Neighbors.relVec[xDir]*@as(Vec3i, @splat(@intCast(quadX))); + blockPos += chunk.Neighbors.relVec[yDir]*@as(Vec3i, @splat(@intCast(quadY))); + if(extraQuadInfo.offsetByNormal) { + blockPos -= chunk.Neighbors.relVec[extraQuadInfo.alignedNormalDirection.?]; + } + const block = self.chunk.data.getValue(chunk.getIndex(blockPos[0], blockPos[1], blockPos[2])); + const texture = main.blocks.meshes.textureIndex(block, main.models.quads.items[quadIndex].textureSlot); + textures[quadX*(yLenMinusOne + 1) + quadY] = texture; + } + } + return textures; + } + + fn greedyMeshQuadType(self: *ChunkMesh, mesh: *PrimitiveMesh, faceList: []const FaceData) void { const quadIndex = faceList[0].blockAndQuad.quadIndex; const extraQuadInfo = &main.models.extraQuadInfos.items[quadIndex]; if(extraQuadInfo.greedyMeshingXDir != null and extraQuadInfo.greedyMeshingYDir != null) { @@ -1055,20 +1117,24 @@ pub const ChunkMesh = struct { }, else => unreachable, } + const textures = self.getTexturesOfGreedyMesh(mesh, .{@intCast(faceX), @intCast(faceY), @intCast(faceZ)}, quadIndex, xDir, yDir, xLenMinusOne, yLenMinusOne); mesh.coreFaces.append(main.globalAllocator, .{ - .position = .{ - .x = @intCast(faceX), - .y = @intCast(faceY), - .z = @intCast(faceZ), - .xSizeMinusOne = xLenMinusOne, - .ySizeMinusOne = yLenMinusOne, - .isBackFace = false, + .face = .{ + .position = .{ + .x = @intCast(faceX), + .y = @intCast(faceY), + .z = @intCast(faceZ), + .xSizeMinusOne = xLenMinusOne, + .ySizeMinusOne = yLenMinusOne, + .isBackFace = false, + }, + .blockAndQuad = .{ + .textureBufferIndex = undefined, + .quadIndex = quadIndex, + }, + .lightBufferIndex = undefined, }, - .blockAndQuad = .{ - .texture = faceList[0].blockAndQuad.texture, - .quadIndex = quadIndex, - }, - .lightBufferIndex = undefined, + .textures = textures, }); } } @@ -1128,20 +1194,24 @@ pub const ChunkMesh = struct { }, else => unreachable, } + const textures = self.getTexturesOfGreedyMesh(mesh, .{@intCast(faceX), @intCast(faceY), @intCast(faceZ)}, quadIndex, xDir, 0, xLenMinusOne, 0); mesh.coreFaces.append(main.globalAllocator, .{ - .position = .{ - .x = @intCast(faceX), - .y = @intCast(faceY), - .z = @intCast(faceZ), - .xSizeMinusOne = xLenMinusOne, - .ySizeMinusOne = 0, - .isBackFace = false, + .face = .{ + .position = .{ + .x = @intCast(faceX), + .y = @intCast(faceY), + .z = @intCast(faceZ), + .xSizeMinusOne = xLenMinusOne, + .ySizeMinusOne = 0, + .isBackFace = false, + }, + .blockAndQuad = .{ + .textureBufferIndex = undefined, + .quadIndex = quadIndex, + }, + .lightBufferIndex = undefined, }, - .blockAndQuad = .{ - .texture = faceList[0].blockAndQuad.texture, - .quadIndex = quadIndex, - }, - .lightBufferIndex = undefined, + .textures = textures, }); } } @@ -1201,20 +1271,24 @@ pub const ChunkMesh = struct { }, else => unreachable, } + const textures = self.getTexturesOfGreedyMesh(mesh, .{@intCast(faceX), @intCast(faceY), @intCast(faceZ)}, quadIndex, 0, yDir, 0, yLenMinusOne); mesh.coreFaces.append(main.globalAllocator, .{ - .position = .{ - .x = @intCast(faceX), - .y = @intCast(faceY), - .z = @intCast(faceZ), - .xSizeMinusOne = 0, - .ySizeMinusOne = yLenMinusOne, - .isBackFace = false, + .face = .{ + .position = .{ + .x = @intCast(faceX), + .y = @intCast(faceY), + .z = @intCast(faceZ), + .xSizeMinusOne = 0, + .ySizeMinusOne = yLenMinusOne, + .isBackFace = false, + }, + .blockAndQuad = .{ + .textureBufferIndex = undefined, + .quadIndex = quadIndex, + }, + .lightBufferIndex = undefined, }, - .blockAndQuad = .{ - .texture = faceList[0].blockAndQuad.texture, - .quadIndex = quadIndex, - }, - .lightBufferIndex = undefined, + .textures = textures, }); } } @@ -1302,9 +1376,9 @@ pub const ChunkMesh = struct { if(occlusionInfo.hasInternalQuads) { const block = self.chunk.data.palette[paletteId]; if(block.transparent()) { - appendInternalQuadsToFaceList(&transparentFaceList, quadToListIndexTransparent, block, x, y, z, false); + appendInternalQuadsToFaceList(&self.transparentMesh, &transparentFaceList, quadToListIndexTransparent, block, x, y, z, false); } else { - appendInternalQuadsToFaceList(&opaqueFaceList, quadToListIndexOpaque, block, x, y, z, false); + appendInternalQuadsToFaceList(&self.opaqueMesh, &opaqueFaceList, quadToListIndexOpaque, block, x, y, z, false); } } } @@ -1326,11 +1400,11 @@ pub const ChunkMesh = struct { } if(block.transparent()) { if(block.hasBackFace()) { - appendNeighborFacingQuadsToFaceList(&transparentFaceList, quadToListIndexTransparent, block, neighbor ^ 1, @intCast(x), @intCast(y), z, true); + appendNeighborFacingQuadsToFaceList(&self.transparentMesh, &transparentFaceList, quadToListIndexTransparent, block, neighbor ^ 1, @intCast(x), @intCast(y), z, true); } - appendNeighborFacingQuadsToFaceList(&transparentFaceList, quadToListIndexTransparent, block, neighbor, @intCast(x - 1), @intCast(y), z, false); + appendNeighborFacingQuadsToFaceList(&self.transparentMesh, &transparentFaceList, quadToListIndexTransparent, block, neighbor, @intCast(x - 1), @intCast(y), z, false); } else { - appendNeighborFacingQuadsToFaceList(&opaqueFaceList, quadToListIndexOpaque, block, neighbor, @intCast(x - 1), @intCast(y), z, false); + appendNeighborFacingQuadsToFaceList(&self.opaqueMesh, &opaqueFaceList, quadToListIndexOpaque, block, neighbor, @intCast(x - 1), @intCast(y), z, false); } } } @@ -1351,11 +1425,11 @@ pub const ChunkMesh = struct { } if(block.transparent()) { if(block.hasBackFace()) { - appendNeighborFacingQuadsToFaceList(&transparentFaceList, quadToListIndexTransparent, block, neighbor ^ 1, @intCast(x), @intCast(y), z, true); + appendNeighborFacingQuadsToFaceList(&self.transparentMesh, &transparentFaceList, quadToListIndexTransparent, block, neighbor ^ 1, @intCast(x), @intCast(y), z, true); } - appendNeighborFacingQuadsToFaceList(&transparentFaceList, quadToListIndexTransparent, block, neighbor, @intCast(x + 1), @intCast(y), z, false); + appendNeighborFacingQuadsToFaceList(&self.transparentMesh, &transparentFaceList, quadToListIndexTransparent, block, neighbor, @intCast(x + 1), @intCast(y), z, false); } else { - appendNeighborFacingQuadsToFaceList(&opaqueFaceList, quadToListIndexOpaque, block, neighbor, @intCast(x + 1), @intCast(y), z, false); + appendNeighborFacingQuadsToFaceList(&self.opaqueMesh, &opaqueFaceList, quadToListIndexOpaque, block, neighbor, @intCast(x + 1), @intCast(y), z, false); } } } @@ -1376,11 +1450,11 @@ pub const ChunkMesh = struct { } if(block.transparent()) { if(block.hasBackFace()) { - appendNeighborFacingQuadsToFaceList(&transparentFaceList, quadToListIndexTransparent, block, neighbor ^ 1, @intCast(x), @intCast(y), z, true); + appendNeighborFacingQuadsToFaceList(&self.transparentMesh, &transparentFaceList, quadToListIndexTransparent, block, neighbor ^ 1, @intCast(x), @intCast(y), z, true); } - appendNeighborFacingQuadsToFaceList(&transparentFaceList, quadToListIndexTransparent, block, neighbor, @intCast(x), @intCast(y - 1), z, false); + appendNeighborFacingQuadsToFaceList(&self.transparentMesh, &transparentFaceList, quadToListIndexTransparent, block, neighbor, @intCast(x), @intCast(y - 1), z, false); } else { - appendNeighborFacingQuadsToFaceList(&opaqueFaceList, quadToListIndexOpaque, block, neighbor, @intCast(x), @intCast(y - 1), z, false); + appendNeighborFacingQuadsToFaceList(&self.opaqueMesh, &opaqueFaceList, quadToListIndexOpaque, block, neighbor, @intCast(x), @intCast(y - 1), z, false); } } } @@ -1401,11 +1475,11 @@ pub const ChunkMesh = struct { } if(block.transparent()) { if(block.hasBackFace()) { - appendNeighborFacingQuadsToFaceList(&transparentFaceList, quadToListIndexTransparent, block, neighbor ^ 1, @intCast(x), @intCast(y), z, true); + appendNeighborFacingQuadsToFaceList(&self.transparentMesh, &transparentFaceList, quadToListIndexTransparent, block, neighbor ^ 1, @intCast(x), @intCast(y), z, true); } - appendNeighborFacingQuadsToFaceList(&transparentFaceList, quadToListIndexTransparent, block, neighbor, @intCast(x), @intCast(y + 1), z, false); + appendNeighborFacingQuadsToFaceList(&self.transparentMesh, &transparentFaceList, quadToListIndexTransparent, block, neighbor, @intCast(x), @intCast(y + 1), z, false); } else { - appendNeighborFacingQuadsToFaceList(&opaqueFaceList, quadToListIndexOpaque, block, neighbor, @intCast(x), @intCast(y + 1), z, false); + appendNeighborFacingQuadsToFaceList(&self.opaqueMesh, &opaqueFaceList, quadToListIndexOpaque, block, neighbor, @intCast(x), @intCast(y + 1), z, false); } } } @@ -1426,11 +1500,11 @@ pub const ChunkMesh = struct { } if(block.transparent()) { if(block.hasBackFace()) { - appendNeighborFacingQuadsToFaceList(&transparentFaceList, quadToListIndexTransparent, block, neighbor ^ 1, @intCast(x), @intCast(y), z, true); + appendNeighborFacingQuadsToFaceList(&self.transparentMesh, &transparentFaceList, quadToListIndexTransparent, block, neighbor ^ 1, @intCast(x), @intCast(y), z, true); } - appendNeighborFacingQuadsToFaceList(&transparentFaceList, quadToListIndexTransparent, block, neighbor, @intCast(x), @intCast(y), z - 1, false); + appendNeighborFacingQuadsToFaceList(&self.transparentMesh, &transparentFaceList, quadToListIndexTransparent, block, neighbor, @intCast(x), @intCast(y), z - 1, false); } else { - appendNeighborFacingQuadsToFaceList(&opaqueFaceList, quadToListIndexOpaque, block, neighbor, @intCast(x), @intCast(y), z - 1, false); + appendNeighborFacingQuadsToFaceList(&self.opaqueMesh, &opaqueFaceList, quadToListIndexOpaque, block, neighbor, @intCast(x), @intCast(y), z - 1, false); } } } @@ -1451,33 +1525,21 @@ pub const ChunkMesh = struct { } if(block.transparent()) { if(block.hasBackFace()) { - appendNeighborFacingQuadsToFaceList(&transparentFaceList, quadToListIndexTransparent, block, neighbor ^ 1, @intCast(x), @intCast(y), z, true); + appendNeighborFacingQuadsToFaceList(&self.transparentMesh, &transparentFaceList, quadToListIndexTransparent, block, neighbor ^ 1, @intCast(x), @intCast(y), z, true); } - appendNeighborFacingQuadsToFaceList(&transparentFaceList, quadToListIndexTransparent, block, neighbor, @intCast(x), @intCast(y), z + 1, false); + appendNeighborFacingQuadsToFaceList(&self.transparentMesh, &transparentFaceList, quadToListIndexTransparent, block, neighbor, @intCast(x), @intCast(y), z + 1, false); } else { - appendNeighborFacingQuadsToFaceList(&opaqueFaceList, quadToListIndexOpaque, block, neighbor, @intCast(x), @intCast(y), z + 1, false); + appendNeighborFacingQuadsToFaceList(&self.opaqueMesh, &opaqueFaceList, quadToListIndexOpaque, block, neighbor, @intCast(x), @intCast(y), z + 1, false); } } } } } - for(transparentFaceList.items) |list| { // TODO: greedy mesh transparent quads as well. This requires making sure they still sort correctly. - for(list.items) |face| { - self.transparentMesh.coreFaces.append(main.globalAllocator, face); - } - } - for(opaqueFaceList.items[0].items) |face| { - self.opaqueMesh.coreFaces.append(main.globalAllocator, face); - } + std.debug.assert(transparentFaceList.items.len == 1 and transparentFaceList.items[0].items.len == 0); + std.debug.assert(opaqueFaceList.items[0].items.len == 0); for(opaqueFaceList.items[1..]) |list| { - if(list.items.len <= 1) { - for(list.items) |face| { - self.opaqueMesh.coreFaces.append(main.globalAllocator, face); - } - } else { - greedyMeshQuadType(&self.opaqueMesh, list.items); - } + greedyMeshQuadType(self, &self.opaqueMesh, list.items); } // Check out the borders: @@ -1792,9 +1854,11 @@ pub const ChunkMesh = struct { .visibilityMask = self.visibilityMask, .vertexStartOpaque = self.opaqueMesh.bufferAllocation.start*4, .lightStartOpaque = self.opaqueMesh.lightBufferAllocation.start, + .textureStartOpaque = self.opaqueMesh.textureBufferAllocation.start, .faceCountsByNormalOpaque = self.opaqueMesh.byNormalCount, .vertexStartTransparent = self.transparentMesh.bufferAllocation.start*4, .lightStartTransparent = self.transparentMesh.lightBufferAllocation.start, + .textureStartTransparent = self.transparentMesh.textureBufferAllocation.start, .vertexCountTransparent = self.transparentMesh.bufferAllocation.len*6, .min = self.min, .max = self.max,