mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 03:06:55 -04:00
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:
parent
76b9b118a1
commit
dbc416332a
@ -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) {
|
||||
|
253
src/chunk.zig
253
src/chunk.zig
@ -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 {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.vertexCount = @intCast(c_int, 6*(faces.items.len-1)/2);
|
||||
self.faceData.bufferData(u32, faces.items);
|
||||
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;
|
||||
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;
|
||||
}
|
||||
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(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
146
src/renderer.zig
146
src/renderer.zig
@ -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) {
|
||||
mesh.mesh.deinit();
|
||||
allocator.destroy(mesh);
|
||||
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 {
|
||||
mutex.lock();
|
||||
defer mutex.unlock();
|
||||
try updatableList.append(mesh);
|
||||
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();
|
||||
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);
|
||||
// }
|
||||
// }
|
||||
};
|
||||
|
@ -11,4 +11,7 @@ pub const highestLOD: u5 = 5;
|
||||
|
||||
pub var fov: f32 = 45;
|
||||
|
||||
pub var mouseSensitivity: f32 = 1;
|
||||
pub var mouseSensitivity: f32 = 1;
|
||||
|
||||
pub var renderDistance: i32 = 4;
|
||||
pub var LODFactor: f32 = 2.0;
|
@ -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;
|
||||
var parentIndex = (i - 1)/2;
|
||||
while(self.array[i].biggerThan(self.array[parentIndex]) and i > 0) {
|
||||
while(i > 0) {
|
||||
var parentIndex = (i - 1)/2;
|
||||
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,16 +282,18 @@ 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];
|
||||
if(!task.vtable.isStillNeeded(task.self)) {
|
||||
self.loadList.removeIndex(i);
|
||||
task.vtable.clean(task.self);
|
||||
} else {
|
||||
task.cachedPriority = task.vtable.getPriority(task.self);
|
||||
i += 1;
|
||||
{
|
||||
defer self.loadList.mutex.unlock();
|
||||
var i: u32 = 0;
|
||||
while(i < self.loadList.size) {
|
||||
var task = self.loadList.array[i];
|
||||
if(!task.vtable.isStillNeeded(task.self)) {
|
||||
self.loadList.removeIndex(i);
|
||||
task.vtable.clean(task.self);
|
||||
} else {
|
||||
task.cachedPriority = task.vtable.getPriority(task.self);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.loadList.updatePriority();
|
||||
@ -296,7 +302,7 @@ pub const ThreadPool = struct {
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user