Rework meshing to support arbitrary quad models. Add some X-shaped grass to demonstrate that this system works.

This commit is contained in:
IntegratedQuantum 2024-03-19 16:28:11 +01:00
parent 21f2ab084a
commit 1f78bde24f
7 changed files with 251 additions and 234 deletions

View File

@ -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
},
]
}

View File

@ -0,0 +1,12 @@
{
"class" : "leaf",
"hardness" : 0.1,
"drops" : [
"auto"
],
"degradable" : true,
"viewThrough" : true,
"absorbedLight" : 0x121012,
"model" : "cross",
"texture" : "cubyz:grass_vegetation"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -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);

View File

@ -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);

View File

@ -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();

View File

@ -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);
}
}
}