mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-09-08 19:50:23 -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,
|
max: Vec3f,
|
||||||
internalQuads: []u16,
|
internalQuads: []u16,
|
||||||
neighborFacingQuads: [6][]u16,
|
neighborFacingQuads: [6][]u16,
|
||||||
|
isNeighborOccluded: [6]bool,
|
||||||
|
|
||||||
fn getFaceNeighbor(quad: *const QuadInfo) ?u3 {
|
fn getFaceNeighbor(quad: *const QuadInfo) ?u3 {
|
||||||
var allZero: @Vector(3, bool) = .{true, true, true};
|
var allZero: @Vector(3, bool) = .{true, true, true};
|
||||||
@ -47,6 +48,19 @@ pub const Model = struct {
|
|||||||
return null;
|
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 {
|
pub fn init(quadInfos: []const QuadInfo) u16 {
|
||||||
const modelIndex: u16 = @intCast(models.items.len);
|
const modelIndex: u16 = @intCast(models.items.len);
|
||||||
const self = models.addOne();
|
const self = models.addOne();
|
||||||
@ -54,6 +68,7 @@ pub const Model = struct {
|
|||||||
var internalAmount: usize = 0;
|
var internalAmount: usize = 0;
|
||||||
self.min = .{1, 1, 1};
|
self.min = .{1, 1, 1};
|
||||||
self.max = .{0, 0, 0};
|
self.max = .{0, 0, 0};
|
||||||
|
self.isNeighborOccluded = .{false} ** 6;
|
||||||
for(quadInfos) |*quad| {
|
for(quadInfos) |*quad| {
|
||||||
for(quad.corners) |corner| {
|
for(quad.corners) |corner| {
|
||||||
self.min = @min(self.min, corner);
|
self.min = @min(self.min, corner);
|
||||||
@ -88,6 +103,13 @@ pub const Model = struct {
|
|||||||
internalIndex += 1;
|
internalIndex += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for(0..6) |neighbor| {
|
||||||
|
for(self.neighborFacingQuads[neighbor]) |quad| {
|
||||||
|
if(fullyOccludesNeighbor(&quads.items[quad])) {
|
||||||
|
self.isNeighborOccluded[neighbor] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return modelIndex;
|
return modelIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,12 +558,11 @@ pub const ChunkMesh = struct {
|
|||||||
fn canBeSeenThroughOtherBlock(block: Block, other: Block, neighbor: u3) bool {
|
fn canBeSeenThroughOtherBlock(block: Block, other: Block, neighbor: u3) bool {
|
||||||
const rotatedModel = blocks.meshes.model(block);
|
const rotatedModel = blocks.meshes.model(block);
|
||||||
const model = &models.models.items[rotatedModel];
|
const model = &models.models.items[rotatedModel];
|
||||||
_ = neighbor;
|
|
||||||
_ = model; // TODO: Check if the neighbor model occludes this one. (maybe not that relevant)
|
_ = model; // TODO: Check if the neighbor model occludes this one. (maybe not that relevant)
|
||||||
return block.typ != 0 and (
|
return block.typ != 0 and (
|
||||||
other.typ == 0
|
other.typ == 0
|
||||||
or (!std.meta.eql(block, other) and other.viewThrough())
|
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)};
|
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 {
|
fn propagateDirect(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry)) void {
|
||||||
var neighborLists: [6]main.ListUnmanaged(Entry) = .{.{}} ** 6;
|
var neighborLists: [6]main.ListUnmanaged(Entry) = .{.{}} ** 6;
|
||||||
defer {
|
defer {
|
||||||
@ -110,19 +135,14 @@ pub const ChannelChunk = struct {
|
|||||||
result.value[1] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
|
result.value[1] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
|
||||||
result.value[2] -|= 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(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) {
|
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);
|
neighborLists[neighbor].append(main.stackAllocator, result);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const neighborIndex = chunk.getIndex(nx, ny, nz);
|
const neighborIndex = chunk.getIndex(nx, ny, nz);
|
||||||
var absorption: [3]u8 = extractColor(self.ch.blocks[neighborIndex].absorption());
|
calculateIncomingOcclusion(&result.value, self.ch.blocks[neighborIndex], self.ch.pos.voxelSize, neighbor ^ 1);
|
||||||
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];
|
|
||||||
if(result.value[0] != 0 or result.value[1] != 0 or result.value[2] != 0) lightQueue.enqueue(result);
|
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[1] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
|
||||||
result.value[2] -|= 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) {
|
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);
|
neighborLists[neighbor].append(main.stackAllocator, result);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const neighborIndex = chunk.getIndex(nx, ny, nz);
|
const neighborIndex = chunk.getIndex(nx, ny, nz);
|
||||||
var absorption: [3]u8 = extractColor(self.ch.blocks[neighborIndex].absorption());
|
calculateIncomingOcclusion(&result.value, self.ch.blocks[neighborIndex], self.ch.pos.voxelSize, neighbor ^ 1);
|
||||||
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];
|
|
||||||
lightQueue.enqueue(result);
|
lightQueue.enqueue(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,13 +248,7 @@ pub const ChannelChunk = struct {
|
|||||||
for(lights) |entry| {
|
for(lights) |entry| {
|
||||||
const index = chunk.getIndex(entry.x, entry.y, entry.z);
|
const index = chunk.getIndex(entry.x, entry.y, entry.z);
|
||||||
var result = entry;
|
var result = entry;
|
||||||
var absorption: [3]u8 = extractColor(self.ch.blocks[index].absorption());
|
calculateIncomingOcclusion(&result.value, self.ch.blocks[index], self.ch.pos.voxelSize, entry.sourceDir);
|
||||||
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];
|
|
||||||
if(result.value[0] != 0 or result.value[1] != 0 or result.value[2] != 0) lightQueue.enqueue(result);
|
if(result.value[0] != 0 or result.value[1] != 0 or result.value[2] != 0) lightQueue.enqueue(result);
|
||||||
}
|
}
|
||||||
self.propagateDirect(lightQueue);
|
self.propagateDirect(lightQueue);
|
||||||
@ -250,13 +259,7 @@ pub const ChannelChunk = struct {
|
|||||||
for(lights) |entry| {
|
for(lights) |entry| {
|
||||||
const index = chunk.getIndex(entry.x, entry.y, entry.z);
|
const index = chunk.getIndex(entry.x, entry.y, entry.z);
|
||||||
var result = entry;
|
var result = entry;
|
||||||
var absorption: [3]u8 = extractColor(self.ch.blocks[index].absorption());
|
calculateIncomingOcclusion(&result.value, self.ch.blocks[index], self.ch.pos.voxelSize, entry.sourceDir);
|
||||||
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];
|
|
||||||
lightQueue.enqueue(result);
|
lightQueue.enqueue(result);
|
||||||
}
|
}
|
||||||
return self.propagateDestructive(lightQueue, constructiveEntries, false);
|
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[1] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
|
||||||
value[2] -|= 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;
|
if(value[0] == 0 and value[1] == 0 and value[2] == 0) continue;
|
||||||
var absorption: [3]u8 = extractColor(self.ch.blocks[index].absorption());
|
calculateIncomingOcclusion(&value, self.ch.blocks[index], self.ch.pos.voxelSize, neighbor ^ 1);
|
||||||
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];
|
|
||||||
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});
|
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