mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 11:17:05 -04:00
Improve CPU-side light interpolation code, reducing the number of samples needed in the average case of axis aligned blocks.
Fixes #296 Helps with #277
This commit is contained in:
parent
ad1e79a4c1
commit
d450fa5f62
@ -21,6 +21,13 @@ pub const QuadInfo = extern struct {
|
||||
textureSlot: u32,
|
||||
};
|
||||
|
||||
const ExtraQuadInfo = struct {
|
||||
faceNeighbor: ?u3,
|
||||
isFullQuad: bool,
|
||||
hasOnlyCornerVertices: bool,
|
||||
alignedNormalDirection: ?u3,
|
||||
};
|
||||
|
||||
const gridSize = 4096;
|
||||
|
||||
fn snapToGrid(x: anytype) @TypeOf(x) {
|
||||
@ -200,18 +207,44 @@ pub fn getModelIndex(string: []const u8) u16 {
|
||||
}
|
||||
|
||||
pub var quads: main.List(QuadInfo) = undefined;
|
||||
pub var extraQuadInfos: main.List(ExtraQuadInfo) = undefined;
|
||||
pub var models: main.List(Model) = undefined;
|
||||
pub var fullCube: u16 = undefined;
|
||||
|
||||
var quadDeduplication: std.AutoHashMap([@sizeOf(QuadInfo)]u8, u16) = undefined;
|
||||
|
||||
fn addQuad(info: QuadInfo) u16 { // TODO: Merge duplicates
|
||||
fn addQuad(info: QuadInfo) u16 {
|
||||
if(quadDeduplication.get(std.mem.toBytes(info))) |id| {
|
||||
return id;
|
||||
}
|
||||
const index: u16 = @intCast(quads.items.len);
|
||||
quads.append(info);
|
||||
quadDeduplication.put(std.mem.toBytes(info), index) catch unreachable;
|
||||
|
||||
var extraQuadInfo: ExtraQuadInfo = undefined;
|
||||
extraQuadInfo.faceNeighbor = Model.getFaceNeighbor(&info);
|
||||
extraQuadInfo.isFullQuad = Model.fullyOccludesNeighbor(&info);
|
||||
{
|
||||
var zeroes: @Vector(3, u32) = .{0, 0, 0};
|
||||
var ones: @Vector(3, u32) = .{0, 0, 0};
|
||||
for(info.corners) |corner| {
|
||||
zeroes += @select(u32, corner == @as(Vec3f, @splat(0)), .{1, 1, 1}, .{0, 0, 0});
|
||||
ones += @select(u32, corner == @as(Vec3f, @splat(1)), .{1, 1, 1}, .{0, 0, 0});
|
||||
}
|
||||
const cornerValues = @reduce(.Add, zeroes) + @reduce(.Add, ones);
|
||||
extraQuadInfo.hasOnlyCornerVertices = cornerValues == 4*3;
|
||||
}
|
||||
{
|
||||
extraQuadInfo.alignedNormalDirection = null;
|
||||
if(@reduce(.And, info.normal == Vec3f{-1, 0, 0})) extraQuadInfo.alignedNormalDirection = chunk.Neighbors.dirNegX;
|
||||
if(@reduce(.And, info.normal == Vec3f{1, 0, 0})) extraQuadInfo.alignedNormalDirection = chunk.Neighbors.dirPosX;
|
||||
if(@reduce(.And, info.normal == Vec3f{0, -1, 0})) extraQuadInfo.alignedNormalDirection = chunk.Neighbors.dirNegY;
|
||||
if(@reduce(.And, info.normal == Vec3f{0, 1, 0})) extraQuadInfo.alignedNormalDirection = chunk.Neighbors.dirPosY;
|
||||
if(@reduce(.And, info.normal == Vec3f{0, 0, -1})) extraQuadInfo.alignedNormalDirection = chunk.Neighbors.dirDown;
|
||||
if(@reduce(.And, info.normal == Vec3f{0, 0, 1})) extraQuadInfo.alignedNormalDirection = chunk.Neighbors.dirUp;
|
||||
}
|
||||
extraQuadInfos.append(extraQuadInfo);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
@ -278,6 +311,7 @@ fn openBox(min: Vec3f, max: Vec3f, uvOffset: Vec2f, openSide: enum{x, y, z}) [4]
|
||||
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);
|
||||
|
||||
nameToIndex = std.StringHashMap(u16).init(main.globalAllocator.allocator);
|
||||
@ -361,5 +395,6 @@ pub fn deinit() void {
|
||||
}
|
||||
models.deinit();
|
||||
quads.deinit();
|
||||
extraQuadInfos.deinit();
|
||||
quadDeduplication.deinit();
|
||||
}
|
@ -303,9 +303,61 @@ const PrimitiveMesh = struct {
|
||||
return result;
|
||||
}
|
||||
|
||||
fn getCornerLightAligned(parent: *ChunkMesh, pos: Vec3i, direction: u3) [6]u8 { // Fast path for algined normals, leading to 4 instead of 8 light samples.
|
||||
const normal: Vec3f = @floatFromInt(Vec3i{chunk.Neighbors.relX[direction], chunk.Neighbors.relY[direction], chunk.Neighbors.relZ[direction]});
|
||||
const lightPos = @as(Vec3f, @floatFromInt(pos)) + normal*@as(Vec3f, @splat(0.5)) - @as(Vec3f, @splat(0.5));
|
||||
const startPos: Vec3i = @intFromFloat(@floor(lightPos));
|
||||
var val: [6]f32 = .{0, 0, 0, 0, 0, 0};
|
||||
var dx: i32 = 0;
|
||||
while(dx <= 1): (dx += 1) {
|
||||
var dy: i32 = 0;
|
||||
while(dy <= 1): (dy += 1) {
|
||||
const weight: f32 = 1.0/4.0;
|
||||
const finalPos = startPos +% @as(Vec3i, @intCast(@abs(chunk.Neighbors.textureX[direction])))*@as(Vec3i, @splat(dx)) +% @as(Vec3i, @intCast(@abs(chunk.Neighbors.textureY[direction]*@as(Vec3i, @splat(dy)))));
|
||||
const lightVal: [6]u8 = getLightAt(parent, finalPos[0], finalPos[1], finalPos[2]);
|
||||
for(0..6) |i| {
|
||||
val[i] += @as(f32, @floatFromInt(lightVal[i]))*weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
var result: [6]u8 = undefined;
|
||||
for(0..6) |i| {
|
||||
result[i] = std.math.lossyCast(u8, val[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn packLightValues(rawVals: [4][6]u5) [4]u32 {
|
||||
var result: [4]u32 = undefined;
|
||||
for(0..4) |i| {
|
||||
result[i] = (
|
||||
@as(u32, rawVals[i][0]) << 25 |
|
||||
@as(u32, rawVals[i][1]) << 20 |
|
||||
@as(u32, rawVals[i][2]) << 15 |
|
||||
@as(u32, rawVals[i][3]) << 10 |
|
||||
@as(u32, rawVals[i][4]) << 5 |
|
||||
@as(u32, rawVals[i][5]) << 0
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn getLight(parent: *ChunkMesh, blockPos: Vec3i, quadIndex: u16) [4]u32 {
|
||||
// TODO: This is doing 12 interpolations of 8 values each. For full cube models only 4 interpolations or 4 values each would be needed.
|
||||
const normal = models.quads.items[quadIndex].normal;
|
||||
if(models.extraQuadInfos.items[quadIndex].hasOnlyCornerVertices) { // Fast path for simple quads.
|
||||
var rawVals: [4][6]u5 = undefined;
|
||||
for(0..4) |i| {
|
||||
const vertexPos = models.quads.items[quadIndex].corners[i];
|
||||
const fullPos = blockPos +% @as(Vec3i, @intFromFloat(vertexPos));
|
||||
const fullValues = if(models.extraQuadInfos.items[quadIndex].alignedNormalDirection) |dir|
|
||||
getCornerLightAligned(parent, fullPos, dir)
|
||||
else getCornerLight(parent, fullPos, normal);
|
||||
for(0..6) |j| {
|
||||
rawVals[i][j] = std.math.lossyCast(u5, fullValues[j]/8);
|
||||
}
|
||||
}
|
||||
return packLightValues(rawVals);
|
||||
}
|
||||
var cornerVals: [2][2][2][6]u8 = undefined;
|
||||
{
|
||||
var dx: u31 = 0;
|
||||
@ -314,7 +366,9 @@ const PrimitiveMesh = struct {
|
||||
while(dy <= 1) : (dy += 1) {
|
||||
var dz: u31 = 0;
|
||||
while(dz <= 1) : (dz += 1) {
|
||||
cornerVals[dx][dy][dz] = getCornerLight(parent, blockPos +% Vec3i{dx, dy, dz}, normal);
|
||||
cornerVals[dx][dy][dz] = if(models.extraQuadInfos.items[quadIndex].alignedNormalDirection) |dir|
|
||||
getCornerLightAligned(parent, blockPos +% Vec3i{dx, dy, dz}, dir)
|
||||
else getCornerLight(parent, blockPos +% Vec3i{dx, dy, dz}, normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -346,18 +400,7 @@ const PrimitiveMesh = struct {
|
||||
rawVals[i][j] = std.math.lossyCast(u5, val[j]/8);
|
||||
}
|
||||
}
|
||||
var result: [4]u32 = undefined;
|
||||
for(0..4) |i| {
|
||||
result[i] = (
|
||||
@as(u32, rawVals[i][0]) << 25 |
|
||||
@as(u32, rawVals[i][1]) << 20 |
|
||||
@as(u32, rawVals[i][2]) << 15 |
|
||||
@as(u32, rawVals[i][3]) << 10 |
|
||||
@as(u32, rawVals[i][4]) << 5 |
|
||||
@as(u32, rawVals[i][5]) << 0
|
||||
);
|
||||
}
|
||||
return result;
|
||||
return packLightValues(rawVals);
|
||||
}
|
||||
|
||||
fn uploadData(self: *PrimitiveMesh, isNeighborLod: [6]bool) void {
|
||||
|
Loading…
x
Reference in New Issue
Block a user