Don't use vector types in the GPU buffer.

I decided to pack the corner positions, everything else would have made it more ugly.

fixes the most relevant part of #1634
This commit is contained in:
IntegratedQuantum 2025-06-19 22:04:26 +02:00
parent 9c33f8184d
commit a8b36f3ca4
6 changed files with 47 additions and 36 deletions

View File

@ -28,7 +28,7 @@ layout(std430, binding = 3) buffer _faceData
struct QuadInfo { struct QuadInfo {
vec3 normal; vec3 normal;
vec3 corners[4]; float corners[4][3];
vec2 cornerUV[4]; vec2 cornerUV[4];
uint textureSlot; uint textureSlot;
int opaqueInLod; int opaqueInLod;
@ -97,7 +97,7 @@ void main() {
normal = quads[quadIndex].normal; normal = quads[quadIndex].normal;
position += quads[quadIndex].corners[vertexID]; position += vec3(quads[quadIndex].corners[vertexID][0], quads[quadIndex].corners[vertexID][1], quads[quadIndex].corners[vertexID][2]);
position *= voxelSize; position *= voxelSize;
position += vec3(chunks[chunkID].position.xyz - playerPositionInteger); position += vec3(chunks[chunkID].position.xyz - playerPositionInteger);
position -= playerPositionFraction; position -= playerPositionFraction;

View File

@ -118,7 +118,7 @@ fn rotateQuad(originalCorners: [4]Vec2f, pattern: Pattern, min: f32, max: f32, s
corners3d[3] + offset, corners3d[3] + offset,
}, },
.cornerUV = originalCorners, .cornerUV = originalCorners,
.normal = @floatFromInt(side.relPos()), .normal = @as(Vec3f, @floatFromInt(side.relPos())),
.textureSlot = textureSlotOffset + @intFromEnum(pattern), .textureSlot = textureSlotOffset + @intFromEnum(pattern),
}; };

View File

@ -118,7 +118,7 @@ fn rotateQuad(pattern: Pattern, side: Neighbor) main.models.QuadInfo {
corners3d[3] + offset, corners3d[3] + offset,
}, },
.cornerUV = corners, .cornerUV = corners,
.normal = @floatFromInt(side.relPos()), .normal = @as(Vec3f, @floatFromInt(side.relPos())),
.textureSlot = @intFromEnum(pattern), .textureSlot = @intFromEnum(pattern),
}; };

View File

@ -162,9 +162,9 @@ pub const collision = struct {
for(model.neighborFacingQuads) |quads| { for(model.neighborFacingQuads) |quads| {
for(quads) |quadIndex| { for(quads) |quadIndex| {
const quad = quadIndex.quadInfo(); const quad = quadIndex.quadInfo();
if(triangleAABB(.{quad.corners[0] + quad.normal + pos, quad.corners[2] + quad.normal + pos, quad.corners[1] + quad.normal + pos}, entityPosition, entityBoundingBoxExtent)) { if(triangleAABB(.{quad.cornerVec(0) + quad.normalVec() + pos, quad.cornerVec(2) + quad.normalVec() + pos, quad.cornerVec(1) + quad.normalVec() + pos}, entityPosition, entityBoundingBoxExtent)) {
const min = @min(@min(quad.corners[0], quad.corners[1]), @min(quad.corners[2], quad.corners[3])) + quad.normal + pos; const min = @min(@min(quad.cornerVec(0), quad.cornerVec(1)), @min(quad.cornerVec(2), quad.cornerVec(3))) + quad.normalVec() + pos;
const max = @max(@max(quad.corners[0], quad.corners[1]), @max(quad.corners[2], quad.corners[3])) + quad.normal + pos; const max = @max(@max(quad.cornerVec(0), quad.cornerVec(1)), @max(quad.cornerVec(2), quad.cornerVec(3))) + quad.normalVec() + pos;
const dist = @min(vec.dot(directionVector, min), vec.dot(directionVector, max)); const dist = @min(vec.dot(directionVector, min), vec.dot(directionVector, max));
if(dist < minDistance) { if(dist < minDistance) {
resultBox = .{.min = min, .max = max}; resultBox = .{.min = min, .max = max};
@ -174,9 +174,9 @@ pub const collision = struct {
resultBox.?.max = @min(resultBox.?.max, max); resultBox.?.max = @min(resultBox.?.max, max);
} }
} }
if(triangleAABB(.{quad.corners[1] + quad.normal + pos, quad.corners[2] + quad.normal + pos, quad.corners[3] + quad.normal + pos}, entityPosition, entityBoundingBoxExtent)) { if(triangleAABB(.{quad.cornerVec(1) + quad.normalVec() + pos, quad.cornerVec(2) + quad.normalVec() + pos, quad.cornerVec(3) + quad.normalVec() + pos}, entityPosition, entityBoundingBoxExtent)) {
const min = @min(@min(quad.corners[0], quad.corners[1]), @min(quad.corners[2], quad.corners[3])) + quad.normal + pos; const min = @min(@min(quad.cornerVec(0), quad.cornerVec(1)), @min(quad.cornerVec(2), quad.cornerVec(3))) + quad.normalVec() + pos;
const max = @max(@max(quad.corners[0], quad.corners[1]), @max(quad.corners[2], quad.corners[3])) + quad.normal + pos; const max = @max(@max(quad.cornerVec(0), quad.cornerVec(1)), @max(quad.cornerVec(2), quad.cornerVec(3))) + quad.normalVec() + pos;
const dist = @min(vec.dot(directionVector, min), vec.dot(directionVector, max)); const dist = @min(vec.dot(directionVector, min), vec.dot(directionVector, max));
if(dist < minDistance) { if(dist < minDistance) {
resultBox = .{.min = min, .max = max}; resultBox = .{.min = min, .max = max};
@ -191,9 +191,9 @@ pub const collision = struct {
for(model.internalQuads) |quadIndex| { for(model.internalQuads) |quadIndex| {
const quad = quadIndex.quadInfo(); const quad = quadIndex.quadInfo();
if(triangleAABB(.{quad.corners[0] + pos, quad.corners[2] + pos, quad.corners[1] + pos}, entityPosition, entityBoundingBoxExtent)) { if(triangleAABB(.{quad.cornerVec(0) + pos, quad.cornerVec(2) + pos, quad.cornerVec(1) + pos}, entityPosition, entityBoundingBoxExtent)) {
const min = @min(@min(quad.corners[0], quad.corners[1]), @min(quad.corners[2], quad.corners[3])) + pos; const min = @min(@min(quad.cornerVec(0), quad.cornerVec(1)), @min(quad.cornerVec(2), quad.cornerVec(3))) + pos;
const max = @max(@max(quad.corners[0], quad.corners[1]), @max(quad.corners[2], quad.corners[3])) + pos; const max = @max(@max(quad.cornerVec(0), quad.cornerVec(1)), @max(quad.cornerVec(2), quad.cornerVec(3))) + pos;
const dist = @min(vec.dot(directionVector, min), vec.dot(directionVector, max)); const dist = @min(vec.dot(directionVector, min), vec.dot(directionVector, max));
if(dist < minDistance) { if(dist < minDistance) {
resultBox = .{.min = min, .max = max}; resultBox = .{.min = min, .max = max};
@ -203,9 +203,9 @@ pub const collision = struct {
resultBox.?.max = @min(resultBox.?.max, max); resultBox.?.max = @min(resultBox.?.max, max);
} }
} }
if(triangleAABB(.{quad.corners[1] + pos, quad.corners[2] + pos, quad.corners[3] + pos}, entityPosition, entityBoundingBoxExtent)) { if(triangleAABB(.{quad.cornerVec(1) + pos, quad.cornerVec(2) + pos, quad.cornerVec(3) + pos}, entityPosition, entityBoundingBoxExtent)) {
const min = @min(@min(quad.corners[0], quad.corners[1]), @min(quad.corners[2], quad.corners[3])) + pos; const min = @min(@min(quad.cornerVec(0), quad.cornerVec(1)), @min(quad.cornerVec(2), quad.cornerVec(3))) + pos;
const max = @max(@max(quad.corners[0], quad.corners[1]), @max(quad.corners[2], quad.corners[3])) + pos; const max = @max(@max(quad.cornerVec(0), quad.cornerVec(1)), @max(quad.cornerVec(2), quad.cornerVec(3))) + pos;
const dist = @min(vec.dot(directionVector, min), vec.dot(directionVector, max)); const dist = @min(vec.dot(directionVector, min), vec.dot(directionVector, max));
if(dist < minDistance) { if(dist < minDistance) {
resultBox = .{.min = min, .max = max}; resultBox = .{.min = min, .max = max};
@ -371,14 +371,14 @@ pub const collision = struct {
for(model.neighborFacingQuads) |quads| { for(model.neighborFacingQuads) |quads| {
for(quads) |quadIndex| { for(quads) |quadIndex| {
const quad = quadIndex.quadInfo(); const quad = quadIndex.quadInfo();
if(triangleAABB(.{quad.corners[0] + quad.normal + position, quad.corners[2] + quad.normal + position, quad.corners[1] + quad.normal + position}, center, extent) or if(triangleAABB(.{quad.cornerVec(0) + quad.normalVec() + position, quad.cornerVec(2) + quad.normalVec() + position, quad.cornerVec(1) + quad.normalVec() + position}, center, extent) or
triangleAABB(.{quad.corners[1] + quad.normal + position, quad.corners[2] + quad.normal + position, quad.corners[3] + quad.normal + position}, center, extent)) return true; triangleAABB(.{quad.cornerVec(1) + quad.normalVec() + position, quad.cornerVec(2) + quad.normalVec() + position, quad.cornerVec(3) + quad.normalVec() + position}, center, extent)) return true;
} }
} }
for(model.internalQuads) |quadIndex| { for(model.internalQuads) |quadIndex| {
const quad = quadIndex.quadInfo(); const quad = quadIndex.quadInfo();
if(triangleAABB(.{quad.corners[0] + position, quad.corners[2] + position, quad.corners[1] + position}, center, extent) or if(triangleAABB(.{quad.cornerVec(0) + position, quad.cornerVec(2) + position, quad.cornerVec(1) + position}, center, extent) or
triangleAABB(.{quad.corners[1] + position, quad.corners[2] + position, quad.corners[3] + position}, center, extent)) return true; triangleAABB(.{quad.cornerVec(1) + position, quad.cornerVec(2) + position, quad.cornerVec(3) + position}, center, extent)) return true;
} }
return false; return false;
} }

View File

@ -15,11 +15,21 @@ const NeverFailingAllocator = main.heap.NeverFailingAllocator;
var quadSSBO: graphics.SSBO = undefined; var quadSSBO: graphics.SSBO = undefined;
pub const QuadInfo = extern struct { pub const QuadInfo = extern struct {
normal: Vec3f, normal: [3]f32 align(16),
corners: [4]Vec3f, corners: [4][3]f32,
cornerUV: [4]Vec2f, cornerUV: [4][2]f32 align(8),
textureSlot: u32, textureSlot: u32,
opaqueInLod: u32 = 0, opaqueInLod: u32 = 0,
pub fn normalVec(self: QuadInfo) Vec3f {
return self.normal;
}
pub fn cornerVec(self: QuadInfo, i: usize) Vec3f {
return self.corners[i];
}
pub fn cornerUvVec(self: QuadInfo, i: usize) Vec2f {
return self.cornerUV[i];
}
}; };
const ExtraQuadInfo = struct { const ExtraQuadInfo = struct {
@ -33,8 +43,9 @@ const gridSize = 4096;
fn snapToGrid(x: anytype) @TypeOf(x) { fn snapToGrid(x: anytype) @TypeOf(x) {
const T = @TypeOf(x); const T = @TypeOf(x);
const int = @as(@Vector(@typeInfo(T).vector.len, i32), @intFromFloat(std.math.round(x*@as(T, @splat(gridSize))))); const Vec = @Vector(x.len, std.meta.Child(T));
return @as(T, @floatFromInt(int))/@as(T, @splat(gridSize)); const int = @as(@Vector(x.len, i32), @intFromFloat(std.math.round(@as(Vec, x)*@as(Vec, @splat(gridSize)))));
return @as(Vec, @floatFromInt(int))/@as(Vec, @splat(gridSize));
} }
const Triangle = struct { const Triangle = struct {
@ -132,8 +143,8 @@ pub const Model = struct {
self.isNeighborOccluded = @splat(false); self.isNeighborOccluded = @splat(false);
for(adjustedQuads) |*quad| { for(adjustedQuads) |*quad| {
for(quad.corners) |corner| { for(quad.corners) |corner| {
self.min = @min(self.min, corner); self.min = @min(self.min, @as(Vec3f, corner));
self.max = @max(self.max, corner); self.max = @max(self.max, @as(Vec3f, corner));
} }
if(getFaceNeighbor(quad)) |neighbor| { if(getFaceNeighbor(quad)) |neighbor| {
amounts[neighbor.toInt()] += 1; amounts[neighbor.toInt()] += 1;
@ -153,7 +164,7 @@ pub const Model = struct {
var quad = _quad; var quad = _quad;
if(getFaceNeighbor(&quad)) |neighbor| { if(getFaceNeighbor(&quad)) |neighbor| {
for(&quad.corners) |*corner| { for(&quad.corners) |*corner| {
corner.* -= quad.normal; corner.* = @as(Vec3f, corner.*) - @as(Vec3f, quad.normal);
} }
const quadIndex = addQuad(quad) catch continue; const quadIndex = addQuad(quad) catch continue;
self.neighborFacingQuads[neighbor.toInt()][indices[neighbor.toInt()]] = quadIndex; self.neighborFacingQuads[neighbor.toInt()][indices[neighbor.toInt()]] = quadIndex;
@ -202,8 +213,8 @@ pub const Model = struct {
for(quadInfos) |*quad| { for(quadInfos) |*quad| {
var minUv: Vec2f = @splat(std.math.inf(f32)); var minUv: Vec2f = @splat(std.math.inf(f32));
for(0..4) |i| { for(0..4) |i| {
quad.cornerUV[i] *= @splat(4); quad.cornerUV[i] = @as(Vec2f, quad.cornerUV[i])*@as(Vec2f, @splat(4));
minUv = @min(minUv, quad.cornerUV[i]); minUv = @min(minUv, @as(Vec2f, quad.cornerUV[i]));
} }
minUv = @floor(minUv); minUv = @floor(minUv);
quad.textureSlot = @as(u32, @intFromFloat(minUv[1]))*4 + @as(u32, @intFromFloat(minUv[0])); quad.textureSlot = @as(u32, @intFromFloat(minUv[1]))*4 + @as(u32, @intFromFloat(minUv[0]));
@ -213,7 +224,7 @@ pub const Model = struct {
} }
for(0..4) |i| { for(0..4) |i| {
quad.cornerUV[i] -= minUv; quad.cornerUV[i] = @as(Vec2f, quad.cornerUV[i]) - minUv;
} }
} }
return Model.init(quadInfos); return Model.init(quadInfos);
@ -382,7 +393,7 @@ pub const Model = struct {
for(model.neighborFacingQuads[neighbor]) |quadIndex| { for(model.neighborFacingQuads[neighbor]) |quadIndex| {
var quad = quadIndex.quadInfo().*; var quad = quadIndex.quadInfo().*;
for(&quad.corners) |*corner| { for(&quad.corners) |*corner| {
corner.* += quad.normal; corner.* = @as(Vec3f, corner.*) + @as(Vec3f, quad.normal);
} }
quadList.append(quad); quadList.append(quad);
} }
@ -448,7 +459,7 @@ fn addQuad(info_: QuadInfo) error{Degenerate}!QuadIndex {
var cornerEqualities: u32 = 0; var cornerEqualities: u32 = 0;
for(0..4) |i| { for(0..4) |i| {
for(i + 1..4) |j| { for(i + 1..4) |j| {
if(@reduce(.And, info.corners[i] == info.corners[j])) cornerEqualities += 1; if(@reduce(.And, @as(Vec3f, info.corners[i]) == @as(Vec3f, info.corners[j]))) cornerEqualities += 1;
} }
} }
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. 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.

View File

@ -517,7 +517,7 @@ pub const PrimitiveMesh = struct { // MARK: PrimitiveMesh
if(extraQuadInfo.hasOnlyCornerVertices) { // Fast path for simple quads. if(extraQuadInfo.hasOnlyCornerVertices) { // Fast path for simple quads.
var rawVals: [4][6]u5 = undefined; var rawVals: [4][6]u5 = undefined;
for(0..4) |i| { for(0..4) |i| {
const vertexPos = quadInfo.corners[i]; const vertexPos: Vec3f = quadInfo.corners[i];
const fullPos = blockPos +% @as(Vec3i, @intFromFloat(vertexPos)); const fullPos = blockPos +% @as(Vec3i, @intFromFloat(vertexPos));
const fullValues = if(extraQuadInfo.alignedNormalDirection) |dir| const fullValues = if(extraQuadInfo.alignedNormalDirection) |dir|
getCornerLightAligned(parent, fullPos, dir) getCornerLightAligned(parent, fullPos, dir)
@ -547,7 +547,7 @@ pub const PrimitiveMesh = struct { // MARK: PrimitiveMesh
} }
var rawVals: [4][6]u5 = undefined; var rawVals: [4][6]u5 = undefined;
for(0..4) |i| { for(0..4) |i| {
const vertexPos = quadInfo.corners[i]; const vertexPos: Vec3f = quadInfo.corners[i];
const lightPos = vertexPos + @as(Vec3f, @floatFromInt(blockPos)); const lightPos = vertexPos + @as(Vec3f, @floatFromInt(blockPos));
const interp = lightPos - @as(Vec3f, @floatFromInt(blockPos)); const interp = lightPos - @as(Vec3f, @floatFromInt(blockPos));
var val: [6]f32 = .{0, 0, 0, 0, 0, 0}; var val: [6]f32 = .{0, 0, 0, 0, 0, 0};
@ -652,7 +652,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
const dz = z + chunkDz; const dz = z + chunkDz;
self.isBackFace = self.face.position.isBackFace; self.isBackFace = self.face.position.isBackFace;
const quadIndex = self.face.blockAndQuad.quadIndex; const quadIndex = self.face.blockAndQuad.quadIndex;
const normalVector = quadIndex.quadInfo().normal; const normalVector: Vec3f = quadIndex.quadInfo().normal;
self.shouldBeCulled = vec.dot(normalVector, @floatFromInt(Vec3i{dx, dy, dz})) > 0; // TODO: Adjust for arbitrary voxel models. self.shouldBeCulled = vec.dot(normalVector, @floatFromInt(Vec3i{dx, dy, dz})) > 0; // TODO: Adjust for arbitrary voxel models.
const fullDx = dx - @as(i32, @intFromFloat(normalVector[0])); // TODO: This calculation should only be done for border faces. const fullDx = dx - @as(i32, @intFromFloat(normalVector[0])); // TODO: This calculation should only be done for border faces.
const fullDy = dy - @as(i32, @intFromFloat(normalVector[1])); const fullDy = dy - @as(i32, @intFromFloat(normalVector[1]));