mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-09-08 03:29:48 -04:00
Optimize meshing using bitmasks to reduce the number of blocks that need to be sampled from the chunk.
Improves meshing time from 30s to just 8s at render distance 12
This commit is contained in:
parent
fffb94046d
commit
5f9d740843
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@ logs/
|
|||||||
saves/
|
saves/
|
||||||
zig-out/
|
zig-out/
|
||||||
zig-cache/
|
zig-cache/
|
||||||
|
.zig-cache/
|
||||||
serverAssets/
|
serverAssets/
|
||||||
settings.json
|
settings.json
|
||||||
gui_layout.json
|
gui_layout.json
|
||||||
|
@ -16,5 +16,5 @@
|
|||||||
|
|
||||||
|
|
||||||
"drops" : [],
|
"drops" : [],
|
||||||
"model" : ""
|
"model" : "none"
|
||||||
}
|
}
|
@ -42,6 +42,9 @@ pub const Model = struct {
|
|||||||
internalQuads: []u16,
|
internalQuads: []u16,
|
||||||
neighborFacingQuads: [6][]u16,
|
neighborFacingQuads: [6][]u16,
|
||||||
isNeighborOccluded: [6]bool,
|
isNeighborOccluded: [6]bool,
|
||||||
|
allNeighborsOccluded: bool,
|
||||||
|
noNeighborsOccluded: bool,
|
||||||
|
hasNeighborFacingQuads: 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};
|
||||||
@ -130,12 +133,18 @@ pub const Model = struct {
|
|||||||
internalIndex += 1;
|
internalIndex += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.hasNeighborFacingQuads = false;
|
||||||
|
self.allNeighborsOccluded = true;
|
||||||
|
self.noNeighborsOccluded = true;
|
||||||
for(0..6) |neighbor| {
|
for(0..6) |neighbor| {
|
||||||
for(self.neighborFacingQuads[neighbor]) |quad| {
|
for(self.neighborFacingQuads[neighbor]) |quad| {
|
||||||
if(fullyOccludesNeighbor(&quads.items[quad])) {
|
if(fullyOccludesNeighbor(&quads.items[quad])) {
|
||||||
self.isNeighborOccluded[neighbor] = true;
|
self.isNeighborOccluded[neighbor] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.hasNeighborFacingQuads = self.hasNeighborFacingQuads or self.neighborFacingQuads[neighbor].len != 0;
|
||||||
|
self.allNeighborsOccluded = self.allNeighborsOccluded and self.isNeighborOccluded[neighbor];
|
||||||
|
self.noNeighborsOccluded = self.noNeighborsOccluded and !self.isNeighborOccluded[neighbor];
|
||||||
}
|
}
|
||||||
return modelIndex;
|
return modelIndex;
|
||||||
}
|
}
|
||||||
@ -316,6 +325,8 @@ pub fn init() void {
|
|||||||
|
|
||||||
nameToIndex = std.StringHashMap(u16).init(main.globalAllocator.allocator);
|
nameToIndex = std.StringHashMap(u16).init(main.globalAllocator.allocator);
|
||||||
|
|
||||||
|
nameToIndex.put("none", Model.init(&.{})) catch unreachable;
|
||||||
|
|
||||||
const cube = Model.init(&box(.{0, 0, 0}, .{1, 1, 1}, .{0, 0}));
|
const cube = Model.init(&box(.{0, 0, 0}, .{1, 1, 1}, .{0, 0}));
|
||||||
nameToIndex.put("cube", cube) catch unreachable;
|
nameToIndex.put("cube", cube) catch unreachable;
|
||||||
fullCube = cube;
|
fullCube = cube;
|
||||||
|
@ -795,45 +795,229 @@ pub const ChunkMesh = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn generateMesh(self: *ChunkMesh) void {
|
pub fn generateMesh(self: *ChunkMesh) void {
|
||||||
|
var canSeeNeighbor: [6][chunk.chunkSize][chunk.chunkSize]u32 = undefined;
|
||||||
|
@memset(std.mem.asBytes(&canSeeNeighbor), 0);
|
||||||
|
var canSeeAllNeighbors: [chunk.chunkSize][chunk.chunkSize]u32 = undefined;
|
||||||
|
@memset(std.mem.asBytes(&canSeeAllNeighbors), 0);
|
||||||
|
var hasFaces: [chunk.chunkSize][chunk.chunkSize]u32 = undefined;
|
||||||
|
@memset(std.mem.asBytes(&hasFaces), 0);
|
||||||
self.mutex.lock();
|
self.mutex.lock();
|
||||||
var n: u32 = 0;
|
const OcclusionInfo = packed struct {
|
||||||
var x: u8 = 0;
|
canSeeNeighbor: u6 = 0,
|
||||||
while(x < chunk.chunkSize): (x += 1) {
|
canSeeAllNeighbors: bool = false,
|
||||||
var y: u8 = 0;
|
hasExternalQuads: bool = false,
|
||||||
while(y < chunk.chunkSize): (y += 1) {
|
hasInternalQuads: bool = false,
|
||||||
var z: u8 = 0;
|
};
|
||||||
while(z < chunk.chunkSize): (z += 1) {
|
var paletteCache = main.stackAllocator.alloc(OcclusionInfo, self.chunk.data.paletteLength);
|
||||||
const block = self.chunk.data.getValue(chunk.getIndex(x, y, z));
|
defer main.stackAllocator.free(paletteCache);
|
||||||
if(block.typ == 0) continue;
|
for(0..self.chunk.data.paletteLength) |i| {
|
||||||
// Check all neighbors:
|
const block = self.chunk.data.palette[i];
|
||||||
for(chunk.Neighbors.iterable) |i| {
|
const model = &models.models.items[blocks.meshes.model(block)];
|
||||||
n += 1;
|
var result: OcclusionInfo = .{};
|
||||||
const x2 = x + chunk.Neighbors.relX[i];
|
if(model.noNeighborsOccluded or block.viewThrough()) {
|
||||||
const y2 = y + chunk.Neighbors.relY[i];
|
result.canSeeAllNeighbors = true;
|
||||||
const z2 = z + chunk.Neighbors.relZ[i];
|
} else if(!model.allNeighborsOccluded) {
|
||||||
if(x2&chunk.chunkMask != x2 or y2&chunk.chunkMask != y2 or z2&chunk.chunkMask != z2) continue; // Neighbor is outside the chunk.
|
for(chunk.Neighbors.iterable) |neighbor| {
|
||||||
const neighborBlock = self.chunk.data.getValue(chunk.getIndex(x2, y2, z2));
|
if(!model.isNeighborOccluded[neighbor]) {
|
||||||
if(canBeSeenThroughOtherBlock(block, neighborBlock, i)) {
|
result.canSeeNeighbor |= @as(u6, 1) << neighbor;
|
||||||
if(block.transparent()) {
|
}
|
||||||
if(block.hasBackFace()) {
|
}
|
||||||
self.transparentMesh.appendNeighborFacingQuadsToCore(block, i ^ 1, x, y, z, true);
|
}
|
||||||
}
|
if(model.hasNeighborFacingQuads) {
|
||||||
self.transparentMesh.appendNeighborFacingQuadsToCore(block, i, x2, y2, z2, false);
|
result.hasExternalQuads = true;
|
||||||
} else {
|
}
|
||||||
self.opaqueMesh.appendNeighborFacingQuadsToCore(block, i, x2, y2, z2, false);
|
if(model.internalQuads.len != 0) {
|
||||||
|
result.hasInternalQuads = true;
|
||||||
|
}
|
||||||
|
paletteCache[i] = result;
|
||||||
|
}
|
||||||
|
// Generate the bitMasks:
|
||||||
|
for(0..chunk.chunkSize) |_x| {
|
||||||
|
const x: u5 = @intCast(_x);
|
||||||
|
for(0..chunk.chunkSize) |_y| {
|
||||||
|
const y: u5 = @intCast(_y);
|
||||||
|
for(0..chunk.chunkSize) |_z| {
|
||||||
|
const z: u5 = @intCast(_z);
|
||||||
|
const paletteId = self.chunk.data.data.getValue(chunk.getIndex(x, y, z));
|
||||||
|
const occlusionInfo = paletteCache[paletteId];
|
||||||
|
const setBit = @as(u32, 1) << z;
|
||||||
|
if(occlusionInfo.canSeeAllNeighbors) {
|
||||||
|
canSeeAllNeighbors[x][y] |= setBit;
|
||||||
|
} else if(occlusionInfo.canSeeNeighbor != 0) {
|
||||||
|
for(chunk.Neighbors.iterable) |neighbor| {
|
||||||
|
if(occlusionInfo.canSeeNeighbor & @as(u6, 1) << neighbor != 0) {
|
||||||
|
canSeeNeighbor[neighbor][x][y] |= setBit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(block.transparent()) {
|
if(occlusionInfo.hasExternalQuads) {
|
||||||
self.transparentMesh.appendInternalQuadsToCore(block, x, y, z, false);
|
hasFaces[x][y] |= setBit;
|
||||||
} else {
|
}
|
||||||
self.opaqueMesh.appendInternalQuadsToCore(block, x, y, z, false);
|
if(occlusionInfo.hasInternalQuads) {
|
||||||
|
const block = self.chunk.data.palette[paletteId];
|
||||||
|
if(block.transparent()) {
|
||||||
|
self.transparentMesh.appendInternalQuadsToCore(block, x, y, z, false);
|
||||||
|
} else {
|
||||||
|
self.opaqueMesh.appendInternalQuadsToCore(block, x, y, z, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Generate the meshes:
|
||||||
|
{
|
||||||
|
const neighbor = chunk.Neighbors.dirNegX;
|
||||||
|
for(1..chunk.chunkSize) |x| {
|
||||||
|
for(0..chunk.chunkSize) |y| {
|
||||||
|
var bitMask = hasFaces[x][y] & (canSeeNeighbor[neighbor ^ 1][x - 1][y] | canSeeAllNeighbors[x - 1][y]);
|
||||||
|
while(bitMask != 0) {
|
||||||
|
const z = @ctz(bitMask);
|
||||||
|
bitMask &= ~(@as(u32, 1) << @intCast(z));
|
||||||
|
const block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z));
|
||||||
|
if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block
|
||||||
|
const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x - 1), @intCast(y), z));
|
||||||
|
if(std.meta.eql(block, neighborBlock)) continue;
|
||||||
|
}
|
||||||
|
if(block.transparent()) {
|
||||||
|
if(block.hasBackFace()) {
|
||||||
|
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor ^ 1, @intCast(x), @intCast(y), z, true);
|
||||||
|
}
|
||||||
|
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x - 1), @intCast(y), z, false);
|
||||||
|
} else {
|
||||||
|
self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x - 1), @intCast(y), z, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const neighbor = chunk.Neighbors.dirPosX;
|
||||||
|
for(0..chunk.chunkSize-1) |x| {
|
||||||
|
for(0..chunk.chunkSize) |y| {
|
||||||
|
var bitMask = hasFaces[x][y] & (canSeeNeighbor[neighbor ^ 1][x + 1][y] | canSeeAllNeighbors[x + 1][y]);
|
||||||
|
while(bitMask != 0) {
|
||||||
|
const z = @ctz(bitMask);
|
||||||
|
bitMask &= ~(@as(u32, 1) << @intCast(z));
|
||||||
|
const block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z));
|
||||||
|
if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block
|
||||||
|
const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x + 1), @intCast(y), z));
|
||||||
|
if(std.meta.eql(block, neighborBlock)) continue;
|
||||||
|
}
|
||||||
|
if(block.transparent()) {
|
||||||
|
if(block.hasBackFace()) {
|
||||||
|
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor ^ 1, @intCast(x), @intCast(y), z, true);
|
||||||
|
}
|
||||||
|
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x + 1), @intCast(y), z, false);
|
||||||
|
} else {
|
||||||
|
self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x + 1), @intCast(y), z, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const neighbor = chunk.Neighbors.dirNegY;
|
||||||
|
for(0..chunk.chunkSize) |x| {
|
||||||
|
for(1..chunk.chunkSize) |y| {
|
||||||
|
var bitMask = hasFaces[x][y] & (canSeeNeighbor[neighbor ^ 1][x][y - 1] | canSeeAllNeighbors[x][y - 1]);
|
||||||
|
while(bitMask != 0) {
|
||||||
|
const z = @ctz(bitMask);
|
||||||
|
bitMask &= ~(@as(u32, 1) << @intCast(z));
|
||||||
|
const block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z));
|
||||||
|
if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block
|
||||||
|
const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y - 1), z));
|
||||||
|
if(std.meta.eql(block, neighborBlock)) continue;
|
||||||
|
}
|
||||||
|
if(block.transparent()) {
|
||||||
|
if(block.hasBackFace()) {
|
||||||
|
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor ^ 1, @intCast(x), @intCast(y), z, true);
|
||||||
|
}
|
||||||
|
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y - 1), z, false);
|
||||||
|
} else {
|
||||||
|
self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y - 1), z, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const neighbor = chunk.Neighbors.dirPosY;
|
||||||
|
for(0..chunk.chunkSize) |x| {
|
||||||
|
for(0..chunk.chunkSize-1) |y| {
|
||||||
|
var bitMask = hasFaces[x][y] & (canSeeNeighbor[neighbor ^ 1][x][y + 1] | canSeeAllNeighbors[x][y + 1]);
|
||||||
|
while(bitMask != 0) {
|
||||||
|
const z = @ctz(bitMask);
|
||||||
|
bitMask &= ~(@as(u32, 1) << @intCast(z));
|
||||||
|
const block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z));
|
||||||
|
if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block
|
||||||
|
const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y + 1), z));
|
||||||
|
if(std.meta.eql(block, neighborBlock)) continue;
|
||||||
|
}
|
||||||
|
if(block.transparent()) {
|
||||||
|
if(block.hasBackFace()) {
|
||||||
|
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor ^ 1, @intCast(x), @intCast(y), z, true);
|
||||||
|
}
|
||||||
|
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y + 1), z, false);
|
||||||
|
} else {
|
||||||
|
self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y + 1), z, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const neighbor = chunk.Neighbors.dirDown;
|
||||||
|
for(0..chunk.chunkSize) |x| {
|
||||||
|
for(0..chunk.chunkSize) |y| {
|
||||||
|
var bitMask = hasFaces[x][y] & (canSeeNeighbor[neighbor ^ 1][x][y] | canSeeAllNeighbors[x][y]) << 1;
|
||||||
|
while(bitMask != 0) {
|
||||||
|
const z = @ctz(bitMask);
|
||||||
|
bitMask &= ~(@as(u32, 1) << @intCast(z));
|
||||||
|
const block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z));
|
||||||
|
if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block
|
||||||
|
const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z - 1));
|
||||||
|
if(std.meta.eql(block, neighborBlock)) continue;
|
||||||
|
}
|
||||||
|
if(block.transparent()) {
|
||||||
|
if(block.hasBackFace()) {
|
||||||
|
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor ^ 1, @intCast(x), @intCast(y), z, true);
|
||||||
|
}
|
||||||
|
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z - 1, false);
|
||||||
|
} else {
|
||||||
|
self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z - 1, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const neighbor = chunk.Neighbors.dirUp;
|
||||||
|
for(0..chunk.chunkSize) |x| {
|
||||||
|
for(0..chunk.chunkSize) |y| {
|
||||||
|
var bitMask = hasFaces[x][y] & (canSeeNeighbor[neighbor ^ 1][x][y] | canSeeAllNeighbors[x][y]) >> 1;
|
||||||
|
while(bitMask != 0) {
|
||||||
|
const z = @ctz(bitMask);
|
||||||
|
bitMask &= ~(@as(u32, 1) << @intCast(z));
|
||||||
|
const block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z));
|
||||||
|
if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block
|
||||||
|
const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z + 1));
|
||||||
|
if(std.meta.eql(block, neighborBlock)) continue;
|
||||||
|
}
|
||||||
|
if(block.transparent()) {
|
||||||
|
if(block.hasBackFace()) {
|
||||||
|
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor ^ 1, @intCast(x), @intCast(y), z, true);
|
||||||
|
}
|
||||||
|
self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z + 1, false);
|
||||||
|
} else {
|
||||||
|
self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z + 1, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check out the borders:
|
// Check out the borders:
|
||||||
x = 0;
|
var x: u8 = 0;
|
||||||
while(x < chunk.chunkSize): (x += 1) {
|
while(x < chunk.chunkSize): (x += 1) {
|
||||||
var y: u8 = 0;
|
var y: u8 = 0;
|
||||||
while(y < chunk.chunkSize): (y += 1) {
|
while(y < chunk.chunkSize): (y += 1) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user