mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 19:28:49 -04:00
Better occlusion and lighting for non-cube models.
Full faces of non-cube models now block light and occlude neighbor faces, whereas the others will let light through. This does make light propapagation significantly (~40%) slower though due to the extra logic involved. Closes #295
This commit is contained in:
parent
0bee580059
commit
4ab40cc85b
@ -30,6 +30,7 @@ pub const Model = struct {
|
||||
max: Vec3f,
|
||||
internalQuads: []u16,
|
||||
neighborFacingQuads: [6][]u16,
|
||||
isNeighborOccluded: [6]bool,
|
||||
|
||||
fn getFaceNeighbor(quad: *const QuadInfo) ?u3 {
|
||||
var allZero: @Vector(3, bool) = .{true, true, true};
|
||||
@ -47,6 +48,19 @@ pub const Model = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
fn fullyOccludesNeighbor(quad: *const QuadInfo) bool {
|
||||
var zeroes: @Vector(3, u32) = .{0, 0, 0};
|
||||
var ones: @Vector(3, u32) = .{0, 0, 0};
|
||||
for(quad.corners) |corner| {
|
||||
zeroes += @select(u32, approxEqAbs(corner, @splat(0), @splat(0.0001)), .{1, 1, 1}, .{0, 0, 0});
|
||||
ones += @select(u32, approxEqAbs(corner, @splat(1), @splat(0.0001)), .{1, 1, 1}, .{0, 0, 0});
|
||||
}
|
||||
// For full coverage there will 2 ones and 2 zeroes for two components, while the other one is constant.
|
||||
const hasTwoZeroes = zeroes == @Vector(3, u32){2, 2, 2};
|
||||
const hasTwoOnes = ones == @Vector(3, u32){2, 2, 2};
|
||||
return @popCount(@as(u3, @bitCast(hasTwoOnes))) == 2 and @popCount(@as(u3, @bitCast(hasTwoZeroes))) == 2;
|
||||
}
|
||||
|
||||
pub fn init(quadInfos: []const QuadInfo) u16 {
|
||||
const modelIndex: u16 = @intCast(models.items.len);
|
||||
const self = models.addOne();
|
||||
@ -54,6 +68,7 @@ pub const Model = struct {
|
||||
var internalAmount: usize = 0;
|
||||
self.min = .{1, 1, 1};
|
||||
self.max = .{0, 0, 0};
|
||||
self.isNeighborOccluded = .{false} ** 6;
|
||||
for(quadInfos) |*quad| {
|
||||
for(quad.corners) |corner| {
|
||||
self.min = @min(self.min, corner);
|
||||
@ -88,6 +103,13 @@ pub const Model = struct {
|
||||
internalIndex += 1;
|
||||
}
|
||||
}
|
||||
for(0..6) |neighbor| {
|
||||
for(self.neighborFacingQuads[neighbor]) |quad| {
|
||||
if(fullyOccludesNeighbor(&quads.items[quad])) {
|
||||
self.isNeighborOccluded[neighbor] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return modelIndex;
|
||||
}
|
||||
|
||||
|
@ -558,12 +558,11 @@ pub const ChunkMesh = struct {
|
||||
fn canBeSeenThroughOtherBlock(block: Block, other: Block, neighbor: u3) bool {
|
||||
const rotatedModel = blocks.meshes.model(block);
|
||||
const model = &models.models.items[rotatedModel];
|
||||
_ = neighbor;
|
||||
_ = model; // TODO: Check if the neighbor model occludes this one. (maybe not that relevant)
|
||||
return block.typ != 0 and (
|
||||
other.typ == 0
|
||||
or (!std.meta.eql(block, other) and other.viewThrough())
|
||||
or blocks.meshes.model(other) != 0
|
||||
or !models.models.items[blocks.meshes.model(other)].isNeighborOccluded[neighbor ^ 1]
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -74,6 +74,31 @@ pub const ChannelChunk = struct {
|
||||
return .{self.data[index][0].load(.Unordered), self.data[index][1].load(.Unordered), self.data[index][2].load(.Unordered)};
|
||||
}
|
||||
|
||||
fn calculateIncomingOcclusion(result: *[3]u8, block: blocks.Block, voxelSize: u31, neighbor: usize) void {
|
||||
if(main.models.models.items[blocks.meshes.model(block)].isNeighborOccluded[neighbor]) {
|
||||
var absorption: [3]u8 = extractColor(block.absorption());
|
||||
absorption[0] *|= @intCast(voxelSize);
|
||||
absorption[1] *|= @intCast(voxelSize);
|
||||
absorption[2] *|= @intCast(voxelSize);
|
||||
result[0] -|= absorption[0];
|
||||
result[1] -|= absorption[1];
|
||||
result[2] -|= absorption[2];
|
||||
}
|
||||
}
|
||||
|
||||
fn calculateOutgoingOcclusion(result: *[3]u8, block: blocks.Block, voxelSize: u31, neighbor: usize) void {
|
||||
const model = &main.models.models.items[blocks.meshes.model(block)];
|
||||
if(model.isNeighborOccluded[neighbor] and !model.isNeighborOccluded[neighbor ^ 1]) { // Avoid calculating the absorption twice.
|
||||
var absorption: [3]u8 = extractColor(block.absorption());
|
||||
absorption[0] *|= @intCast(voxelSize);
|
||||
absorption[1] *|= @intCast(voxelSize);
|
||||
absorption[2] *|= @intCast(voxelSize);
|
||||
result[0] -|= absorption[0];
|
||||
result[1] -|= absorption[1];
|
||||
result[2] -|= absorption[2];
|
||||
}
|
||||
}
|
||||
|
||||
fn propagateDirect(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry)) void {
|
||||
var neighborLists: [6]main.ListUnmanaged(Entry) = .{.{}} ** 6;
|
||||
defer {
|
||||
@ -110,19 +135,14 @@ pub const ChannelChunk = struct {
|
||||
result.value[1] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
|
||||
result.value[2] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
|
||||
}
|
||||
calculateOutgoingOcclusion(&result.value, self.ch.blocks[index], self.ch.pos.voxelSize, neighbor);
|
||||
if(result.value[0] == 0 and result.value[1] == 0 and result.value[2] == 0) continue;
|
||||
if(nx < 0 or nx >= chunk.chunkSize or ny < 0 or ny >= chunk.chunkSize or nz < 0 or nz >= chunk.chunkSize) {
|
||||
neighborLists[neighbor].append(main.stackAllocator, result);
|
||||
continue;
|
||||
}
|
||||
const neighborIndex = chunk.getIndex(nx, ny, nz);
|
||||
var absorption: [3]u8 = extractColor(self.ch.blocks[neighborIndex].absorption());
|
||||
absorption[0] *|= @intCast(self.ch.pos.voxelSize);
|
||||
absorption[1] *|= @intCast(self.ch.pos.voxelSize);
|
||||
absorption[2] *|= @intCast(self.ch.pos.voxelSize);
|
||||
result.value[0] -|= absorption[0];
|
||||
result.value[1] -|= absorption[1];
|
||||
result.value[2] -|= absorption[2];
|
||||
calculateIncomingOcclusion(&result.value, self.ch.blocks[neighborIndex], self.ch.pos.voxelSize, neighbor ^ 1);
|
||||
if(result.value[0] != 0 or result.value[1] != 0 or result.value[2] != 0) lightQueue.enqueue(result);
|
||||
}
|
||||
}
|
||||
@ -196,18 +216,13 @@ pub const ChannelChunk = struct {
|
||||
result.value[1] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
|
||||
result.value[2] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
|
||||
}
|
||||
calculateOutgoingOcclusion(&result.value, self.ch.blocks[index], self.ch.pos.voxelSize, neighbor);
|
||||
if(nx < 0 or nx >= chunk.chunkSize or ny < 0 or ny >= chunk.chunkSize or nz < 0 or nz >= chunk.chunkSize) {
|
||||
neighborLists[neighbor].append(main.stackAllocator, result);
|
||||
continue;
|
||||
}
|
||||
const neighborIndex = chunk.getIndex(nx, ny, nz);
|
||||
var absorption: [3]u8 = extractColor(self.ch.blocks[neighborIndex].absorption());
|
||||
absorption[0] *|= @intCast(self.ch.pos.voxelSize);
|
||||
absorption[1] *|= @intCast(self.ch.pos.voxelSize);
|
||||
absorption[2] *|= @intCast(self.ch.pos.voxelSize);
|
||||
result.value[0] -|= absorption[0];
|
||||
result.value[1] -|= absorption[1];
|
||||
result.value[2] -|= absorption[2];
|
||||
calculateIncomingOcclusion(&result.value, self.ch.blocks[neighborIndex], self.ch.pos.voxelSize, neighbor ^ 1);
|
||||
lightQueue.enqueue(result);
|
||||
}
|
||||
}
|
||||
@ -233,13 +248,7 @@ pub const ChannelChunk = struct {
|
||||
for(lights) |entry| {
|
||||
const index = chunk.getIndex(entry.x, entry.y, entry.z);
|
||||
var result = entry;
|
||||
var absorption: [3]u8 = extractColor(self.ch.blocks[index].absorption());
|
||||
absorption[0] *|= @intCast(self.ch.pos.voxelSize);
|
||||
absorption[1] *|= @intCast(self.ch.pos.voxelSize);
|
||||
absorption[2] *|= @intCast(self.ch.pos.voxelSize);
|
||||
result.value[0] -|= absorption[0];
|
||||
result.value[1] -|= absorption[1];
|
||||
result.value[2] -|= absorption[2];
|
||||
calculateIncomingOcclusion(&result.value, self.ch.blocks[index], self.ch.pos.voxelSize, entry.sourceDir);
|
||||
if(result.value[0] != 0 or result.value[1] != 0 or result.value[2] != 0) lightQueue.enqueue(result);
|
||||
}
|
||||
self.propagateDirect(lightQueue);
|
||||
@ -250,13 +259,7 @@ pub const ChannelChunk = struct {
|
||||
for(lights) |entry| {
|
||||
const index = chunk.getIndex(entry.x, entry.y, entry.z);
|
||||
var result = entry;
|
||||
var absorption: [3]u8 = extractColor(self.ch.blocks[index].absorption());
|
||||
absorption[0] *|= @intCast(self.ch.pos.voxelSize);
|
||||
absorption[1] *|= @intCast(self.ch.pos.voxelSize);
|
||||
absorption[2] *|= @intCast(self.ch.pos.voxelSize);
|
||||
result.value[0] -|= absorption[0];
|
||||
result.value[1] -|= absorption[1];
|
||||
result.value[2] -|= absorption[2];
|
||||
calculateIncomingOcclusion(&result.value, self.ch.blocks[index], self.ch.pos.voxelSize, entry.sourceDir);
|
||||
lightQueue.enqueue(result);
|
||||
}
|
||||
return self.propagateDestructive(lightQueue, constructiveEntries, false);
|
||||
@ -311,14 +314,9 @@ pub const ChannelChunk = struct {
|
||||
value[1] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
|
||||
value[2] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
|
||||
}
|
||||
calculateOutgoingOcclusion(&value, self.ch.blocks[neighborIndex], self.ch.pos.voxelSize, neighbor);
|
||||
if(value[0] == 0 and value[1] == 0 and value[2] == 0) continue;
|
||||
var absorption: [3]u8 = extractColor(self.ch.blocks[index].absorption());
|
||||
absorption[0] *|= @intCast(self.ch.pos.voxelSize);
|
||||
absorption[1] *|= @intCast(self.ch.pos.voxelSize);
|
||||
absorption[2] *|= @intCast(self.ch.pos.voxelSize);
|
||||
value[0] -|= absorption[0];
|
||||
value[1] -|= absorption[1];
|
||||
value[2] -|= absorption[2];
|
||||
calculateIncomingOcclusion(&value, self.ch.blocks[index], self.ch.pos.voxelSize, neighbor ^ 1);
|
||||
if(value[0] != 0 or value[1] != 0 or value[2] != 0) lightQueue.enqueue(.{.x = @intCast(x), .y = @intCast(y), .z = @intCast(z), .value = value, .sourceDir = @intCast(neighbor), .activeValue = 0b111});
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user