Add Mesh creation for voxelSize=1 chunks. My goal is to combine all meshes in one algorithm to simplify things from the java version.

This commit is contained in:
IntegratedQuantum 2022-09-29 12:15:47 +02:00
parent 76b9b118a1
commit dbc416332a
7 changed files with 322 additions and 184 deletions

View File

@ -12,7 +12,7 @@ pub fn build(b: *std.build.Builder) void {
const mode = b.standardReleaseOptions();
const exe = b.addExecutable("Cubyzig", "src/main.zig");
exe.addIncludeDir("include");
exe.addIncludePath("include");
exe.linkLibC();
{ // compile glfw from source:
if(target.getOsTag() == .windows) {

View File

@ -9,6 +9,7 @@ const c = graphics.c;
const Shader = graphics.Shader;
const SSBO = graphics.SSBO;
const main = @import("main.zig");
const renderer = @import("renderer.zig");
const vec = @import("vec.zig");
const Vec3f = vec.Vec3f;
const Vec3d = vec.Vec3d;
@ -40,11 +41,11 @@ pub const Neighbors = struct {
/// Directions Index
pub const dirNegZ: u32 = 5;
/// Index to relative position
pub const relX = [_]i32 {0, 0, 1, -1, 0, 0};
pub const relX = [_]ChunkCoordinate {0, 0, 1, -1, 0, 0};
/// Index to relative position
pub const relY = [_]i32 {1, -1, 0, 0, 0, 0};
pub const relY = [_]ChunkCoordinate {1, -1, 0, 0, 0, 0};
/// Index to relative position
pub const relZ = [_]i32 {0, 0, 0, 0, 1, -1};
pub const relZ = [_]ChunkCoordinate {0, 0, 0, 0, 1, -1};
/// Index to bitMask for bitmap direction data
pub const bitMask = [_]u6 {0x01, 0x02, 0x04, 0x08, 0x10, 0x20};
/// To iterate over all neighbors easily
@ -79,11 +80,6 @@ pub const ChunkPosition = struct {
// public int hashCode() {
// int shift = Math.min(Integer.numberOfTrailingZeros(wx), Math.min(Integer.numberOfTrailingZeros(wy), Integer.numberOfTrailingZeros(wz)));
// return (((wx >> shift) * 31 + (wy >> shift)) * 31 + (wz >> shift)) * 31 + voxelSize;
// }
// TODO:
// public float getPriority(Player source) {
// int halfWidth = voxelSize * Chunk.chunkSize / 2;
// return -(float) source.getPosition().distance(wx + halfWidth, wy + halfWidth, wz + halfWidth) / voxelSize;
// }
pub fn getMinDistanceSquared(self: ChunkPosition, playerPosition: Vec3d) f64 {
@ -113,17 +109,15 @@ pub const Chunk = struct {
widthShift: u5,
mutex: std.Thread.Mutex,
pub fn init(self: *Chunk, wx: ChunkCoordinate, wy: ChunkCoordinate, wz: ChunkCoordinate, voxelSize: UChunkCoordinate) void {
std.debug.assert((voxelSize - 1 & voxelSize) == 0);
std.debug.assert(@mod(wx, voxelSize) == 0 and @mod(wy, voxelSize) == 0 and @mod(wz, voxelSize) == 0);
const voxelSizeShift = @intCast(u5, std.math.log2_int(UChunkCoordinate, voxelSize));
pub fn init(self: *Chunk, pos: ChunkPosition) void {
std.debug.assert((pos.voxelSize - 1 & pos.voxelSize) == 0);
std.debug.assert(@mod(pos.wx, pos.voxelSize) == 0 and @mod(pos.wy, pos.voxelSize) == 0 and @mod(pos.wz, pos.voxelSize) == 0);
const voxelSizeShift = @intCast(u5, std.math.log2_int(UChunkCoordinate, pos.voxelSize));
self.* = Chunk {
.pos = ChunkPosition {
.wx = wx, .wy = wy, .wz = wz, .voxelSize = voxelSize
},
.width = voxelSize*chunkSize,
.pos = pos,
.width = pos.voxelSize*chunkSize,
.voxelSizeShift = voxelSizeShift,
.voxelSizeMask = voxelSize - 1,
.voxelSizeMask = pos.voxelSize - 1,
.widthShift = voxelSizeShift + chunkShift,
.mutex = std.Thread.Mutex{},
};
@ -208,10 +202,10 @@ pub const Chunk = struct {
/// Gets a block if it is inside this chunk.
/// Does not do any bound checks. They are expected to be done with the `liesInChunk` function.
pub fn getBlock(self: *const Chunk, x: ChunkCoordinate, y: ChunkCoordinate, z: ChunkCoordinate) Block {
x >>= self.voxelSizeShift;
y >>= self.voxelSizeShift;
z >>= self.voxelSizeShift;
pub fn getBlock(self: *const Chunk, _x: ChunkCoordinate, _y: ChunkCoordinate, _z: ChunkCoordinate) Block {
var x = _x >> self.voxelSizeShift;
var y = _y >> self.voxelSizeShift;
var z = _z >> self.voxelSizeShift;
var index = getIndex(x, y, z);
return self.blocks[index];
}
@ -535,8 +529,6 @@ pub const meshing = struct {
@"fog.activ": c_int,
@"fog.color": c_int,
@"fog.density": c_int,
lowerBounds: c_int,
upperBounds: c_int,
texture_sampler: c_int,
emissionSampler: c_int,
@"waterFog.activ": c_int,
@ -583,7 +575,7 @@ pub const meshing = struct {
c.glUniform3fv(uniforms.@"fog.color", 1, @ptrCast([*c]f32, &game.fog.color));
c.glUniform1f(uniforms.@"fog.density", game.fog.density);
c.glUniformMatrix4fv(uniforms.projectionMatrix, 1, c.GL_FALSE, @ptrCast([*c]f32, &projMatrix));
c.glUniformMatrix4fv(uniforms.projectionMatrix, 1, c.GL_FALSE, @ptrCast([*c]const f32, &projMatrix));
c.glUniform1i(uniforms.texture_sampler, 0);
c.glUniform1i(uniforms.emissionSampler, 1);
@ -594,117 +586,160 @@ pub const meshing = struct {
c.glUniform1i(uniforms.time, @bitCast(i32, time));
c.glUniform3f(uniforms.lowerBounds, -std.math.inf_f32, -std.math.inf_f32, -std.math.inf_f32);
c.glUniform3f(uniforms.upperBounds, std.math.inf_f32, std.math.inf_f32, std.math.inf_f32);
c.glBindVertexArray(vao);
}
pub fn bindShaderForReplacement() void {
shader.bind();
c.glBindVertexArray(vao);
}
pub const ChunkMesh = struct {
pos: ChunkPosition,
size: ChunkCoordinate,
replacement: ?*ChunkMesh,
chunk: ?*Chunk,
faces: std.ArrayList(u32),
faceData: SSBO,
vertexCount: c_int = 0,
coreCount: u31 = 0,
neighborStart: [7]u31 = [_]u31{0} ** 7,
vertexCount: u31 = 0,
generated: bool = false,
mutex: std.Thread.Mutex = std.Thread.Mutex{},
pub fn init(pos: ChunkPosition, replacement: ?*ChunkMesh) ChunkMesh {
pub fn init(allocator: Allocator, pos: ChunkPosition) ChunkMesh {
return ChunkMesh{
.pos = pos,
.size = chunkSize*pos.voxelSize,
.replacement = replacement,
.faces = std.ArrayList(u32).init(allocator),
.chunk = null,
.faceData = SSBO.init(),
};
}
pub fn deinit(self: ChunkMesh) void {
pub fn deinit(self: *ChunkMesh) void {
self.faceData.deinit();
self.faces.deinit();
if(self.chunk) |ch| {
renderer.RenderStructure.allocator.destroy(ch);
}
}
pub fn regenerateMesh(self: *ChunkMesh, visDat: *ChunkVisibilityData) !void {
pub fn regenerateMainMesh(self: *ChunkMesh, chunk: *Chunk) !void {
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
self.faces.clearRetainingCapacity();
try self.faces.append(chunk.pos.voxelSize);
var n: u32 = 0;
var x: u8 = 0;
while(x < chunkSize): (x += 1) {
var y: u8 = 0;
while(y < chunkSize): (y += 1) {
var z: u8 = 0;
while(z < chunkSize): (z += 1) {
const block = (&chunk.blocks)[getIndex(x, y, z)]; // a little hack that increases speed 100×. TODO: check if this is *that* compiler bug.
if(block.typ == 0) continue;
// Check all neighbors:
for(Neighbors.iterable) |i| {
n += 1;
const x2 = x + Neighbors.relX[i];
const y2 = y + Neighbors.relY[i];
const z2 = z + Neighbors.relZ[i];
if(x2&chunkMask != x2 or y2&chunkMask != y2 or z2&chunkMask != z2) continue; // Neighbor is outside the chunk.
const neighborBlock = (&chunk.blocks)[getIndex(x2, y2, z2)]; // a little hack that increases speed 100×. TODO: check if this is *that* compiler bug.
var isVisible = neighborBlock.typ == 0; // TODO: Transparency
if(isVisible) {
const normal: u32 = i;
const position: u32 = @as(u32, x) | @as(u32, y)<<5 | @as(u32, z)<<10;
const textureNormal = blocks.meshes.textureIndices(block)[i] | normal<<24;
try self.faces.append(position);
try self.faces.append(textureNormal);
}
}
}
}
}
if(self.chunk) |oldChunk| {
renderer.RenderStructure.allocator.destroy(oldChunk);
}
self.chunk = chunk;
self.coreCount = @intCast(u31, self.faces.items.len);
self.neighborStart = [_]u31{self.coreCount} ** 7;
}
pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void {
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
if(self.chunk == null) return; // In the mean-time the mesh was discarded and recreated and all the data was lost.
self.generated = true;
faces.clearRetainingCapacity();
try faces.append(visDat.pos.voxelSize);
for(visDat.visibles.items) |visible| {
const block = visible.block;
const x = visible.x;
const y = visible.y;
const z = visible.z;
if(visible.neighbors & Neighbors.bitMask[Neighbors.dirNegX] != 0) {
const normal: u32 = 0;
const position: u32 = @as(u32, x) | @as(u32, y) << 6 | @as(u32, z) << 12;
const textureNormal = blocks.meshes.textureIndices(block)[Neighbors.dirNegX] | (normal << 24);
try faces.append(position);
try faces.append(textureNormal);
self.faces.shrinkRetainingCapacity(self.coreCount);
for(Neighbors.iterable) |neighbor| {
self.neighborStart[neighbor] = @intCast(u31, self.faces.items.len);
var nullNeighborMesh = renderer.RenderStructure.getNeighbor(self.pos, neighbor);
if(nullNeighborMesh) |neighborMesh| {
std.debug.assert(neighborMesh != self);
neighborMesh.mutex.lock();
defer neighborMesh.mutex.unlock();
if(neighborMesh.generated) {
var additionalNeighborFaces = std.ArrayList(u32).init(main.threadAllocator);
defer additionalNeighborFaces.deinit();
var x3: u8 = if(neighbor & 1 == 0) @intCast(u8, chunkMask) else 0;
var x1: u8 = 0;
while(x1 < chunkSize): (x1 += 1) {
var x2: u8 = 0;
while(x2 < chunkSize): (x2 += 1) {
var x: u8 = undefined;
var y: u8 = undefined;
var z: u8 = undefined;
if(Neighbors.relX[neighbor] != 0) {
x = x3;
y = x1;
z = x2;
} else if(Neighbors.relY[neighbor] != 0) {
x = x1;
y = x3;
z = x2;
} else {
x = x2;
y = x1;
z = x3;
}
if(visible.neighbors & Neighbors.bitMask[Neighbors.dirPosX] != 0) {
const normal: u32 = 1;
const position: u32 = @as(u32, x+1) | @as(u32, y) << 6 | @as(u32, z) << 12;
const textureNormal = blocks.meshes.textureIndices(block)[Neighbors.dirPosX] | (normal << 24);
try faces.append(position);
try faces.append(textureNormal);
var otherX = @intCast(u8, x+%Neighbors.relX[neighbor] & chunkMask);
var otherY = @intCast(u8, y+%Neighbors.relY[neighbor] & chunkMask);
var otherZ = @intCast(u8, z+%Neighbors.relZ[neighbor] & chunkMask);
var block = self.chunk.?.blocks[getIndex(x, y, z)];
var otherBlock = neighborMesh.chunk.?.blocks[getIndex(otherX, otherY, otherZ)];
if(otherBlock.typ == 0 and block.typ != 0) { // TODO: Transparency
const normal: u32 = neighbor;
const position: u32 = @as(u32, x) | @as(u32, y)<<5 | @as(u32, z)<<10;
const textureNormal = blocks.meshes.textureIndices(block)[neighbor] | normal<<24;
try self.faces.append(position);
try self.faces.append(textureNormal);
}
if(visible.neighbors & Neighbors.bitMask[Neighbors.dirDown] != 0) {
const normal: u32 = 4;
const position: u32 = @as(u32, x) | @as(u32, y) << 6 | @as(u32, z) << 12;
const textureNormal = blocks.meshes.textureIndices(block)[Neighbors.dirDown] | (normal << 24);
try faces.append(position);
try faces.append(textureNormal);
}
if(visible.neighbors & Neighbors.bitMask[Neighbors.dirUp] != 0) {
const normal: u32 = 5;
const position: u32 = @as(u32, x) | @as(u32, y+1) << 6 | @as(u32, z) << 12;
const textureNormal = blocks.meshes.textureIndices(block)[Neighbors.dirUp] | (normal << 24);
try faces.append(position);
try faces.append(textureNormal);
}
if(visible.neighbors & Neighbors.bitMask[Neighbors.dirNegZ] != 0) {
const normal: u32 = 2;
const position: u32 = @as(u32, x) | @as(u32, y) << 6 | @as(u32, z) << 12;
const textureNormal = blocks.meshes.textureIndices(block)[Neighbors.dirNegZ] | (normal << 24);
try faces.append(position);
try faces.append(textureNormal);
}
if(visible.neighbors & Neighbors.bitMask[Neighbors.dirPosZ] != 0) {
const normal: u32 = 3;
const position: u32 = @as(u32, x) | @as(u32, y) << 6 | @as(u32, z+1) << 12;
const textureNormal = blocks.meshes.textureIndices(block)[Neighbors.dirPosZ] | (normal << 24);
try faces.append(position);
try faces.append(textureNormal);
if(block.typ == 0 and otherBlock.typ != 0) { // TODO: Transparency
const normal: u32 = neighbor ^ 1;
const position: u32 = @as(u32, otherX) | @as(u32, otherY)<<5 | @as(u32, otherZ)<<10;
const textureNormal = blocks.meshes.textureIndices(otherBlock)[neighbor] | normal<<24;
try additionalNeighborFaces.append(position);
try additionalNeighborFaces.append(textureNormal);
}
}
self.vertexCount = @intCast(c_int, 6*(faces.items.len-1)/2);
self.faceData.bufferData(u32, faces.items);
}
var rangeStart = neighborMesh.neighborStart[neighbor ^ 1];
var rangeEnd = neighborMesh.neighborStart[(neighbor ^ 1)+1];
try neighborMesh.faces.replaceRange(rangeStart, rangeEnd - rangeStart, additionalNeighborFaces.items);
for(neighborMesh.neighborStart[1+(neighbor ^ 1)..]) |*neighborStart| {
neighborStart.* = neighborStart.* - (rangeEnd - rangeStart) + @intCast(u31, additionalNeighborFaces.items.len);
}
neighborMesh.vertexCount = @intCast(u31, 6*(neighborMesh.faces.items.len-1)/2);
neighborMesh.faceData.bufferData(u32, neighborMesh.faces.items);
} else {
// TODO: Resolution boundary.
}
} else {
// TODO: Resolution boundary.
}
}
self.neighborStart[6] = @intCast(u31, self.faces.items.len);
self.vertexCount = @intCast(u31, 6*(self.faces.items.len-1)/2);
self.faceData.bufferData(u32, self.faces.items);
}
pub fn render(self: *ChunkMesh, playerPosition: Vec3d) void {
if(!self.generated) {
if(self.replacement == null) return;
c.glUniform3f(
uniforms.lowerBounds,
@floatCast(f32, @intToFloat(f64, self.pos.wx) - playerPosition.x - 0.001),
@floatCast(f32, @intToFloat(f64, self.pos.wy) - playerPosition.y - 0.001),
@floatCast(f32, @intToFloat(f64, self.pos.wz) - playerPosition.z - 0.001)
);
c.glUniform3f(
uniforms.upperBounds,
@floatCast(f32, @intToFloat(f64, self.pos.wx + self.size) - playerPosition.x + 0.001),
@floatCast(f32, @intToFloat(f64, self.pos.wy + self.size) - playerPosition.y + 0.001),
@floatCast(f32, @intToFloat(f64, self.pos.wz + self.size) - playerPosition.z + 0.001)
);
self.replacement.?.render(playerPosition);
c.glUniform3f(uniforms.lowerBounds, -std.math.inf_f32, -std.math.inf_f32, -std.math.inf_f32);
c.glUniform3f(uniforms.upperBounds, std.math.inf_f32, std.math.inf_f32, std.math.inf_f32);
return;
}
if(self.vertexCount == 0) return;

View File

@ -563,7 +563,7 @@ pub const TextureArray = struct {
}
};
pub const Color = packed struct(u32) {
pub const Color = extern struct {
r: u8,
g: u8,
b: u8,

View File

@ -688,37 +688,39 @@ pub const Protocols = blk: {
.wz = std.mem.readIntBig(chunk.ChunkCoordinate, data[8..12]),
.voxelSize = @intCast(chunk.UChunkCoordinate, std.mem.readIntBig(chunk.ChunkCoordinate, data[12..16])),
};
data = data[16..];
const _inflatedData = try utils.Compression.inflate(main.threadAllocator, data[16..]);
data = _inflatedData;
defer main.threadAllocator.free(_inflatedData);
if(pos.voxelSize == 1) {
// TODO:
// byte[] chunkData = ChunkIO.decompressChunk(data, offset, length);
// if(chunkData == null)
// return;
// VisibleChunk ch = new VisibleChunk(Cubyz.world, wx, wy, wz);
// ch.loadFromByteArray(chunkData, chunkData.length);
// ThreadPool.addTask(new ChunkLoadTask(ch));
} else {
data = try utils.Compression.inflate(main.threadAllocator, data);
defer main.threadAllocator.free(data);
var size = @divExact(data.len, 8);
var x = data[0..size];
var y = data[size..2*size];
var z = data[2*size..3*size];
var neighbors = data[3*size..4*size];
var visibleBlocks = data[4*size..];
var result = try renderer.RenderStructure.allocator.create(chunk.ChunkVisibilityData);
result.* = try chunk.ChunkVisibilityData.initEmpty(renderer.RenderStructure.allocator, pos, size);
for(x) |_, i| {
var block = result.visibles.addOneAssumeCapacity();
block.x = x[i];
block.y = y[i];
block.z = z[i];
block.neighbors = neighbors[i];
var blockTypeAndData = std.mem.readIntBig(u32, visibleBlocks[4*i..][0..4]);
block.block.typ = @intCast(u16, blockTypeAndData & 0xffff);
block.block.data = @intCast(u16, blockTypeAndData >> 16);
var ch = try renderer.RenderStructure.allocator.create(chunk.Chunk);
ch.init(pos);
for(ch.blocks) |*block| {
var blockTypeAndData = std.mem.readIntBig(u32, data[0..4]);
block.typ = @intCast(u16, blockTypeAndData & 0xffff);
block.data = @intCast(u16, blockTypeAndData >> 16);
data = data[4..];
}
try renderer.RenderStructure.updateChunkMesh(result);
try renderer.RenderStructure.updateChunkMesh(ch);
} else {
//var size = @divExact(data.len, 8);
//var x = data[0..size];
//var y = data[size..2*size];
//var z = data[2*size..3*size];
//var neighbors = data[3*size..4*size];
//var visibleBlocks = data[4*size..];
//var result = try renderer.RenderStructure.allocator.create(chunk.ChunkVisibilityData);
//result.* = try chunk.ChunkVisibilityData.initEmpty(renderer.RenderStructure.allocator, pos, size);
//for(x) |_, i| {
// var block = result.visibles.addOneAssumeCapacity();
// block.x = x[i];
// block.y = y[i];
// block.z = z[i];
// block.neighbors = neighbors[i];
// var blockTypeAndData = std.mem.readIntBig(u32, visibleBlocks[4*i..][0..4]);
// block.block.typ = @intCast(u16, blockTypeAndData & 0xffff);
// block.block.data = @intCast(u16, blockTypeAndData >> 16);
//}
// TODO: try renderer.RenderStructure.updateChunkMesh(result);
}
}
pub fn sendChunk(conn: *Connection, visData: chunk.ChunkVisibilityData) !void {

View File

@ -16,6 +16,7 @@ const chunk = @import("chunk.zig");
const main = @import("main.zig");
const network = @import("network.zig");
const settings = @import("settings.zig");
const utils = @import("utils.zig");
const Window = main.Window;
/// The number of milliseconds after which no more chunk meshes are created. This allows the game to run smoother on movement.
@ -211,7 +212,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
var frustum = Frustum.init(Vec3f{.x=0, .y=0, .z=0}, game.camera.viewMatrix, settings.fov, zFarLOD, main.Window.width, main.Window.height);
const time = @intCast(u32, std.time.milliTimestamp() & std.math.maxInt(u32));
const waterFog = Fog{.active=true, .color=.{.x=0.0, .y=0.1, .z=0.2}, .density=0.1};
var waterFog = Fog{.active=true, .color=.{.x=0.0, .y=0.1, .z=0.2}, .density=0.1};
// Update the uniforms. The uniforms are needed to render the replacement meshes.
chunk.meshing.bindShaderAndUniforms(game.lodProjectionMatrix, ambientLight, time);
@ -235,7 +236,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
var meshes = std.ArrayList(*chunk.meshing.ChunkMesh).init(main.threadAllocator);
defer meshes.deinit();
try RenderStructure.updateAndGetRenderChunks(game.world.?.conn, game.playerPos, 4, 2.0, frustum, &meshes);
try RenderStructure.updateAndGetRenderChunks(game.world.?.conn, game.playerPos, settings.renderDistance, settings.LODFactor, frustum, &meshes);
// for (ChunkMesh mesh : Cubyz.chunkTree.getRenderChunks(frustumInt, x0, y0, z0)) {
// if (mesh instanceof NormalChunkMesh) {
// visibleChunks.add((NormalChunkMesh)mesh);
@ -433,7 +434,8 @@ pub const RenderStructure = struct {
drawableChildren: u32, // How many children can be renderer. If this is 8 then there is no need to render this mesh.
};
var storageLists: [settings.highestLOD + 1][]?*ChunkMeshNode = undefined;
var updatableList: std.ArrayList(*chunk.ChunkVisibilityData) = undefined;
var updatableList: std.ArrayList(chunk.ChunkPosition) = undefined;
var clearList: std.ArrayList(*ChunkMeshNode) = undefined;
var lastRD: i32 = 0;
var lastFactor: f32 = 0;
var lastX: [settings.highestLOD + 1]chunk.ChunkCoordinate = [_]chunk.ChunkCoordinate{0} ** (settings.highestLOD + 1);
@ -448,7 +450,8 @@ pub const RenderStructure = struct {
lastFactor = 0;
gpa = std.heap.GeneralPurposeAllocator(.{}){};
allocator = gpa.allocator();
updatableList = std.ArrayList(*chunk.ChunkVisibilityData).init(allocator);
updatableList = std.ArrayList(chunk.ChunkPosition).init(allocator);
clearList = std.ArrayList(*ChunkMeshNode).init(allocator);
for(storageLists) |*storageList| {
storageList.* = try allocator.alloc(?*ChunkMeshNode, 0);
}
@ -464,11 +467,12 @@ pub const RenderStructure = struct {
}
allocator.free(storageList);
}
for(updatableList.items) |updatable| {
updatable.visibles.deinit();
allocator.destroy(updatable);
}
updatableList.deinit();
for(clearList.items) |chunkMesh| {
chunkMesh.mesh.deinit();
allocator.destroy(chunkMesh);
}
clearList.deinit();
game.world.?.blockPalette.deinit();
if(gpa.deinit()) {
@panic("Memory leak");
@ -489,6 +493,15 @@ pub const RenderStructure = struct {
return storageLists[lod][@intCast(usize, index)];
}
pub fn getNeighbor(_pos: chunk.ChunkPosition, neighbor: u3) ?*chunk.meshing.ChunkMesh {
var pos = _pos;
pos.wx += pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relX[neighbor];
pos.wy += pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relY[neighbor];
pos.wz += pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relZ[neighbor];
var node = _getNode(pos) orelse return null;
return &node.mesh;
}
pub fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: Vec3d, renderDistance: i32, LODFactor: f32, frustum: Frustum, meshes: *std.ArrayList(*chunk.meshing.ChunkMesh)) !void {
if(lastRD != renderDistance and lastFactor != LODFactor) {
// TODO:
@ -551,7 +564,7 @@ pub const RenderStructure = struct {
_node.shouldBeRemoved = false;
} else {
node = try allocator.create(ChunkMeshNode);
node.?.mesh = chunk.meshing.ChunkMesh.init(pos, null); // TODO: Replacement mesh?
node.?.mesh = chunk.meshing.ChunkMesh.init(allocator, pos);
node.?.shouldBeRemoved = true; // Might be removed in the next iteration.
try meshRequests.append(pos);
}
@ -590,8 +603,13 @@ pub const RenderStructure = struct {
for(oldList) |nullMesh| {
if(nullMesh) |mesh| {
if(mesh.shouldBeRemoved) {
if(mesh.mesh.mutex.tryLock()) { // Make sure there is no task currently running on the thing.
mesh.mesh.mutex.unlock();
mesh.mesh.deinit();
allocator.destroy(mesh);
} else {
try clearList.append(mesh);
}
} else {
mesh.shouldBeRemoved = true;
}
@ -600,6 +618,19 @@ pub const RenderStructure = struct {
allocator.free(oldList);
}
var i: usize = 0;
while(i < clearList.items.len) {
const mesh = clearList.items[i];
if(mesh.mesh.mutex.tryLock()) { // Make sure there is no task currently running on the thing.
mesh.mesh.mutex.unlock();
mesh.mesh.deinit();
allocator.destroy(mesh);
_ = clearList.swapRemove(i);
} else {
i += 1;
}
}
lastRD = renderDistance;
lastFactor = LODFactor;
// Make requests after updating the, to avoid concurrency issues and reduce the number of requests:
@ -610,27 +641,88 @@ pub const RenderStructure = struct {
mutex.lock();
defer mutex.unlock();
while(updatableList.items.len != 0) {
const mesh = updatableList.pop();
const nullNode = _getNode(mesh.pos);
const pos = updatableList.pop();
mutex.unlock();
defer mutex.lock();
const nullNode = _getNode(pos);
if(nullNode) |node| {
try node.mesh.regenerateMesh(mesh);
node.mesh.mutex.lock();
defer node.mesh.mutex.unlock();
try node.mesh.uploadDataAndFinishNeighbors();
}
mesh.visibles.deinit();
allocator.destroy(mesh);
if(std.time.milliTimestamp() >= targetTime) break; // Update at least one mesh.
}
}
pub fn updateChunkMesh(mesh: *chunk.ChunkVisibilityData) !void {
pub const MeshGenerationTask = struct {
mesh: *chunk.Chunk,
pub const vtable = utils.ThreadPool.VTable{
.getPriority = @ptrCast(*const fn(*anyopaque) f32, &getPriority),
.isStillNeeded = @ptrCast(*const fn(*anyopaque) bool, &isStillNeeded),
.run = @ptrCast(*const fn(*anyopaque) void, &run),
.clean = @ptrCast(*const fn(*anyopaque) void, &clean),
};
pub fn schedule(mesh: *chunk.Chunk) !void {
var task = try allocator.create(MeshGenerationTask);
task.* = MeshGenerationTask {
.mesh = mesh,
};
try main.threadPool.addTask(task, &vtable);
}
pub fn getPriority(self: *MeshGenerationTask) f32 {
return -@floatCast(f32, self.mesh.pos.getMinDistanceSquared(game.playerPos))/@intToFloat(f32, self.mesh.pos.voxelSize*self.mesh.pos.voxelSize);
}
pub fn isStillNeeded(self: *MeshGenerationTask) bool {
var distanceSqr = self.mesh.pos.getMinDistanceSquared(game.playerPos);
var maxRenderDistance = settings.renderDistance*chunk.chunkSize*self.mesh.pos.voxelSize;
if(self.mesh.pos.voxelSize != 1) maxRenderDistance = @floatToInt(i32, @ceil(@intToFloat(f32, maxRenderDistance)*settings.LODFactor));
maxRenderDistance += 2*self.mesh.pos.voxelSize*chunk.chunkSize;
return distanceSqr < @intToFloat(f64, maxRenderDistance*maxRenderDistance);
}
pub fn run(self: *MeshGenerationTask) void {
const pos = self.mesh.pos;
const nullNode = _getNode(pos);
if(nullNode) |node| {
{
node.mesh.mutex.lock();
defer node.mesh.mutex.unlock();
node.mesh.regenerateMainMesh(self.mesh) catch |err| {
std.log.err("Error while regenerating mesh: {s}", .{@errorName(err)});
if(@errorReturnTrace()) |trace| {
std.log.err("Trace: {}", .{trace});
}
allocator.destroy(self.mesh);
allocator.destroy(self);
return;
};
}
mutex.lock();
defer mutex.unlock();
try updatableList.append(mesh);
updatableList.append(pos) catch |err| {
std.log.err("Error while regenerating mesh: {s}", .{@errorName(err)});
if(@errorReturnTrace()) |trace| {
std.log.err("Trace: {}", .{trace});
}
allocator.destroy(self.mesh);
};
} else {
allocator.destroy(self.mesh);
}
allocator.destroy(self);
}
pub fn clean(self: *MeshGenerationTask) void {
allocator.destroy(self.mesh);
allocator.destroy(self);
}
};
pub fn updateChunkMesh(mesh: *chunk.Chunk) !void {
try MeshGenerationTask.schedule(mesh);
}
// TODO:
// public void updateChunkMesh(VisibleChunk mesh) {
// OctTreeNode node = findNode(mesh);
// if (node != null) {
// ((NormalChunkMesh)node.mesh).updateChunk(mesh);
// }
// }
};

View File

@ -12,3 +12,6 @@ pub const highestLOD: u5 = 5;
pub var fov: f32 = 45;
pub var mouseSensitivity: f32 = 1;
pub var renderDistance: i32 = 4;
pub var LODFactor: f32 = 2.0;

View File

@ -118,6 +118,7 @@ pub fn BlockingMaxHeap(comptime T: type) type {
/// Moves an element from a given index down the heap, such that all children are always smaller than their parents.
fn siftDown(self: *@This(), _i: usize) void {
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
var i = _i;
while(2*i + 2 < self.size) {
var biggest = if(self.array[2*i + 1].biggerThan(self.array[2*i + 2])) 2*i + 1 else 2*i + 2;
@ -134,14 +135,15 @@ pub fn BlockingMaxHeap(comptime T: type) type {
/// Moves an element from a given index up the heap, such that all children are always smaller than their parents.
fn siftUp(self: *@This(), _i: usize) void {
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
var i = _i;
while(i > 0) {
var parentIndex = (i - 1)/2;
while(self.array[i].biggerThan(self.array[parentIndex]) and i > 0) {
if(!self.array[i].biggerThan(self.array[parentIndex])) break;
var local = self.array[parentIndex];
self.array[parentIndex] = self.array[i];
self.array[i] = local;
i = parentIndex;
parentIndex = (i - 1)/2;
}
}
@ -156,6 +158,7 @@ pub fn BlockingMaxHeap(comptime T: type) type {
/// Returns the i-th element in the heap. Useless for most applications.
pub fn get(self: *@This(), i: usize) ?T {
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
if(i >= self.size) return null;
return self.array[i];
}
@ -176,6 +179,7 @@ pub fn BlockingMaxHeap(comptime T: type) type {
}
fn removeIndex(self: *@This(), i: usize) void {
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
self.size -= 1;
self.array[i] = self.array[self.size];
self.siftDown(i);
@ -204,7 +208,7 @@ pub fn BlockingMaxHeap(comptime T: type) type {
}
fn increaseCapacity(self: *@This(), newCapacity: usize) !void {
self.array = self.allocator.realloc(self.array, newCapacity);
self.array = try self.allocator.realloc(self.array, newCapacity);
}
};
}
@ -213,7 +217,7 @@ pub const ThreadPool = struct {
const Task = struct {
cachedPriority: f32,
self: *anyopaque,
vtable: *VTable,
vtable: *const VTable,
fn biggerThan(self: Task, other: Task) bool {
return self.cachedPriority > other.cachedPriority;
@ -278,10 +282,11 @@ pub const ThreadPool = struct {
if(std.time.milliTimestamp() -% lastUpdate > refreshTime) {
lastUpdate = std.time.milliTimestamp();
if(self.loadList.mutex.tryLock()) {
{
defer self.loadList.mutex.unlock();
var i: u32 = 0;
while(i < self.loadList.size) {
var task = &self.loadList.array[i];
var task = self.loadList.array[i];
if(!task.vtable.isStillNeeded(task.self)) {
self.loadList.removeIndex(i);
task.vtable.clean(task.self);
@ -290,13 +295,14 @@ pub const ThreadPool = struct {
i += 1;
}
}
}
self.loadList.updatePriority();
}
}
}
}
pub fn addTask(self: ThreadPool, task: *anyopaque, vtable: *VTable) !void {
pub fn addTask(self: ThreadPool, task: *anyopaque, vtable: *const VTable) !void {
try self.loadList.add(Task {
.cachedPriority = vtable.getPriority(task),
.vtable = vtable,