mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-09-13 06:16:45 -04:00
Store chunks in an array instead of an octree.
This commit is contained in:
parent
95eb578719
commit
76b9b118a1
@ -8,15 +8,19 @@ const graphics = @import("graphics.zig");
|
|||||||
const c = graphics.c;
|
const c = graphics.c;
|
||||||
const Shader = graphics.Shader;
|
const Shader = graphics.Shader;
|
||||||
const SSBO = graphics.SSBO;
|
const SSBO = graphics.SSBO;
|
||||||
const Vec3f = @import("vec.zig").Vec3f;
|
const main = @import("main.zig");
|
||||||
const Vec3d = @import("vec.zig").Vec3d;
|
const vec = @import("vec.zig");
|
||||||
const Mat4f = @import("vec.zig").Mat4f;
|
const Vec3f = vec.Vec3f;
|
||||||
|
const Vec3d = vec.Vec3d;
|
||||||
|
const Mat4f = vec.Mat4f;
|
||||||
|
|
||||||
pub const ChunkCoordinate = i32;
|
pub const ChunkCoordinate = i32;
|
||||||
pub const UChunkCoordinate = u31;
|
pub const UChunkCoordinate = u31;
|
||||||
pub const chunkShift: u5 = 5;
|
pub const chunkShift: u5 = 5;
|
||||||
pub const chunkShift2: u5 = chunkShift*2;
|
pub const chunkShift2: u5 = chunkShift*2;
|
||||||
pub const chunkSize: ChunkCoordinate = 1 << chunkShift;
|
pub const chunkSize: ChunkCoordinate = 1 << chunkShift;
|
||||||
|
pub const chunkSizeIterator: [chunkSize]u0 = undefined;
|
||||||
|
pub const chunkVolume: UChunkCoordinate = 1 << 3*chunkShift;
|
||||||
pub const chunkMask: ChunkCoordinate = chunkSize - 1;
|
pub const chunkMask: ChunkCoordinate = chunkSize - 1;
|
||||||
|
|
||||||
/// Contains a bunch of constants used to describe neighboring blocks.
|
/// Contains a bunch of constants used to describe neighboring blocks.
|
||||||
@ -52,6 +56,18 @@ fn getIndex(x: ChunkCoordinate, y: ChunkCoordinate, z: ChunkCoordinate) u32 {
|
|||||||
std.debug.assert((x & chunkMask) == x and (y & chunkMask) == y and (z & chunkMask) == z);
|
std.debug.assert((x & chunkMask) == x and (y & chunkMask) == y and (z & chunkMask) == z);
|
||||||
return (@intCast(u32, x) << chunkShift) | (@intCast(u32, y) << chunkShift2) | @intCast(u32, z);
|
return (@intCast(u32, x) << chunkShift) | (@intCast(u32, y) << chunkShift2) | @intCast(u32, z);
|
||||||
}
|
}
|
||||||
|
/// Gets the x coordinate from a given index inside this chunk.
|
||||||
|
fn extractXFromIndex(index: usize) ChunkCoordinate {
|
||||||
|
return @intCast(ChunkCoordinate, index >> chunkShift & chunkMask);
|
||||||
|
}
|
||||||
|
/// Gets the y coordinate from a given index inside this chunk.
|
||||||
|
fn extractYFromIndex(index: usize) ChunkCoordinate {
|
||||||
|
return @intCast(ChunkCoordinate, index >> chunkShift2 & chunkMask);
|
||||||
|
}
|
||||||
|
/// Gets the z coordinate from a given index inside this chunk.
|
||||||
|
fn extractZFromIndex(index: usize) ChunkCoordinate {
|
||||||
|
return @intCast(ChunkCoordinate, index & chunkMask);
|
||||||
|
}
|
||||||
|
|
||||||
pub const ChunkPosition = struct {
|
pub const ChunkPosition = struct {
|
||||||
wx: ChunkCoordinate,
|
wx: ChunkCoordinate,
|
||||||
@ -84,7 +100,7 @@ pub const ChunkPosition = struct {
|
|||||||
|
|
||||||
pub const Chunk = struct {
|
pub const Chunk = struct {
|
||||||
pos: ChunkPosition,
|
pos: ChunkPosition,
|
||||||
blocks: [chunkSize*chunkSize*chunkSize]Block = undefined,
|
blocks: [chunkVolume]Block = undefined,
|
||||||
|
|
||||||
wasChanged: bool = false,
|
wasChanged: bool = false,
|
||||||
/// When a chunk is cleaned, it won't be saved by the ChunkManager anymore, so following changes need to be saved directly.
|
/// When a chunk is cleaned, it won't be saved by the ChunkManager anymore, so following changes need to be saved directly.
|
||||||
@ -97,11 +113,11 @@ pub const Chunk = struct {
|
|||||||
widthShift: u5,
|
widthShift: u5,
|
||||||
mutex: std.Thread.Mutex,
|
mutex: std.Thread.Mutex,
|
||||||
|
|
||||||
pub fn init(wx: ChunkCoordinate, wy: ChunkCoordinate, wz: ChunkCoordinate, voxelSize: UChunkCoordinate) Chunk {
|
pub fn init(self: *Chunk, wx: ChunkCoordinate, wy: ChunkCoordinate, wz: ChunkCoordinate, voxelSize: UChunkCoordinate) void {
|
||||||
std.debug.assert((voxelSize - 1 & voxelSize) == 0);
|
std.debug.assert((voxelSize - 1 & voxelSize) == 0);
|
||||||
std.debug.assert(@mod(wx, voxelSize) == 0 and @mod(wy, voxelSize) == 0 and @mod(wz, 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));
|
const voxelSizeShift = @intCast(u5, std.math.log2_int(UChunkCoordinate, voxelSize));
|
||||||
return Chunk {
|
self.* = Chunk {
|
||||||
.pos = ChunkPosition {
|
.pos = ChunkPosition {
|
||||||
.wx = wx, .wy = wy, .wz = wz, .voxelSize = voxelSize
|
.wx = wx, .wy = wy, .wz = wz, .voxelSize = voxelSize
|
||||||
},
|
},
|
||||||
@ -113,7 +129,7 @@ pub const Chunk = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setChanged(self: *const Chunk) void {
|
pub fn setChanged(self: *Chunk) void {
|
||||||
self.wasChanged = true;
|
self.wasChanged = true;
|
||||||
{
|
{
|
||||||
self.mutex.lock();
|
self.mutex.lock();
|
||||||
@ -124,7 +140,7 @@ pub const Chunk = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clean(self: *const Chunk) void {
|
pub fn clean(self: *Chunk) void {
|
||||||
{
|
{
|
||||||
self.mutex.lock();
|
self.mutex.lock();
|
||||||
self.wasCleaned = true;
|
self.wasCleaned = true;
|
||||||
@ -133,7 +149,7 @@ pub const Chunk = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unclean(self: *const Chunk) void {
|
pub fn unclean(self: *Chunk) void {
|
||||||
{
|
{
|
||||||
self.mutex.lock();
|
self.mutex.lock();
|
||||||
self.wasCleaned = false;
|
self.wasCleaned = false;
|
||||||
@ -160,7 +176,7 @@ pub const Chunk = struct {
|
|||||||
|
|
||||||
/// Updates a block if current value is air or the current block is degradable.
|
/// Updates a block if current value is air or the current block is degradable.
|
||||||
/// Does not do any bound checks. They are expected to be done with the `liesInChunk` function.
|
/// Does not do any bound checks. They are expected to be done with the `liesInChunk` function.
|
||||||
pub fn updateBlockIfDegradable(self: *const Chunk, x: ChunkCoordinate, y: ChunkCoordinate, z: ChunkCoordinate, newBlock: Block) void {
|
pub fn updateBlockIfDegradable(self: *Chunk, x: ChunkCoordinate, y: ChunkCoordinate, z: ChunkCoordinate, newBlock: Block) void {
|
||||||
x >>= self.voxelSizeShift;
|
x >>= self.voxelSizeShift;
|
||||||
y >>= self.voxelSizeShift;
|
y >>= self.voxelSizeShift;
|
||||||
z >>= self.voxelSizeShift;
|
z >>= self.voxelSizeShift;
|
||||||
@ -172,7 +188,7 @@ pub const Chunk = struct {
|
|||||||
|
|
||||||
/// Updates a block if it is inside this chunk.
|
/// Updates a block if it is inside this chunk.
|
||||||
/// Does not do any bound checks. They are expected to be done with the `liesInChunk` function.
|
/// Does not do any bound checks. They are expected to be done with the `liesInChunk` function.
|
||||||
pub fn updateBlock(self: *const Chunk, x: ChunkCoordinate, y: ChunkCoordinate, z: ChunkCoordinate, newBlock: Block) void {
|
pub fn updateBlock(self: *Chunk, x: ChunkCoordinate, y: ChunkCoordinate, z: ChunkCoordinate, newBlock: Block) void {
|
||||||
x >>= self.voxelSizeShift;
|
x >>= self.voxelSizeShift;
|
||||||
y >>= self.voxelSizeShift;
|
y >>= self.voxelSizeShift;
|
||||||
z >>= self.voxelSizeShift;
|
z >>= self.voxelSizeShift;
|
||||||
@ -182,7 +198,7 @@ pub const Chunk = struct {
|
|||||||
|
|
||||||
/// Updates a block if it is inside this chunk. Should be used in generation to prevent accidently storing these as changes.
|
/// Updates a block if it is inside this chunk. Should be used in generation to prevent accidently storing these as changes.
|
||||||
/// Does not do any bound checks. They are expected to be done with the `liesInChunk` function.
|
/// Does not do any bound checks. They are expected to be done with the `liesInChunk` function.
|
||||||
pub fn updateBlockInGeneration(self: *const Chunk, x: ChunkCoordinate, y: ChunkCoordinate, z: ChunkCoordinate, newBlock: Block) void {
|
pub fn updateBlockInGeneration(self: *Chunk, x: ChunkCoordinate, y: ChunkCoordinate, z: ChunkCoordinate, newBlock: Block) void {
|
||||||
x >>= self.voxelSizeShift;
|
x >>= self.voxelSizeShift;
|
||||||
y >>= self.voxelSizeShift;
|
y >>= self.voxelSizeShift;
|
||||||
z >>= self.voxelSizeShift;
|
z >>= self.voxelSizeShift;
|
||||||
@ -200,7 +216,30 @@ pub const Chunk = struct {
|
|||||||
return self.blocks[index];
|
return self.blocks[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn updateFromLowerResolution(self: *const Chunk, other: *const Chunk) void {
|
pub fn getNeighbors(self: *const Chunk, x: ChunkCoordinate, y: ChunkCoordinate, z: ChunkCoordinate, neighborsArray: *[6]Block) void {
|
||||||
|
std.debug.assert(neighborsArray.length == 6);
|
||||||
|
x &= chunkMask;
|
||||||
|
y &= chunkMask;
|
||||||
|
z &= chunkMask;
|
||||||
|
for(Neighbors.relX) |_, i| {
|
||||||
|
var xi = x + Neighbors.relX[i];
|
||||||
|
var yi = y + Neighbors.relY[i];
|
||||||
|
var zi = z + Neighbors.relZ[i];
|
||||||
|
if (xi == (xi & chunkMask) and yi == (yi & chunkMask) and zi == (zi & chunkMask)) { // Simple double-bound test for coordinates.
|
||||||
|
neighborsArray[i] = self.getBlock(xi, yi, zi);
|
||||||
|
} else {
|
||||||
|
// TODO: What about other chunks?
|
||||||
|
// NormalChunk ch = world.getChunk(xi + wx, yi + wy, zi + wz);
|
||||||
|
// if (ch != null) {
|
||||||
|
// neighborsArray[i] = ch.getBlock(xi & chunkMask, yi & chunkMask, zi & chunkMask);
|
||||||
|
// } else {
|
||||||
|
// neighborsArray[i] = 1; // Some solid replacement, in case the chunk isn't loaded. TODO: Properly choose a solid block.
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updateFromLowerResolution(self: *Chunk, other: *const Chunk) void {
|
||||||
const xOffset = if(other.wx != self.wx) chunkSize/2 else 0; // Offsets of the lower resolution chunk in this chunk.
|
const xOffset = if(other.wx != self.wx) chunkSize/2 else 0; // Offsets of the lower resolution chunk in this chunk.
|
||||||
const yOffset = if(other.wy != self.wy) chunkSize/2 else 0;
|
const yOffset = if(other.wy != self.wy) chunkSize/2 else 0;
|
||||||
const zOffset = if(other.wz != self.wz) chunkSize/2 else 0;
|
const zOffset = if(other.wz != self.wz) chunkSize/2 else 0;
|
||||||
@ -279,7 +318,7 @@ pub const Chunk = struct {
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
setChanged();
|
self.setChanged();
|
||||||
}
|
}
|
||||||
// TODO: Move this outside.
|
// TODO: Move this outside.
|
||||||
// /**
|
// /**
|
||||||
@ -555,6 +594,9 @@ pub const meshing = struct {
|
|||||||
|
|
||||||
c.glUniform1i(uniforms.time, @bitCast(i32, time));
|
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);
|
c.glBindVertexArray(vao);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ pub const World = struct {
|
|||||||
|
|
||||||
pub fn finishHandshake(self: *World, jsonObject: JsonElement) !void {
|
pub fn finishHandshake(self: *World, jsonObject: JsonElement) !void {
|
||||||
// TODO: Consider using a per-world allocator.
|
// TODO: Consider using a per-world allocator.
|
||||||
self.blockPalette = try assets.BlockPalette.init(renderer.RenderOctree.allocator, jsonObject.getChild("blockPalette"));
|
self.blockPalette = try assets.BlockPalette.init(renderer.RenderStructure.allocator, jsonObject.getChild("blockPalette"));
|
||||||
var jsonSpawn = jsonObject.getChild("spawn");
|
var jsonSpawn = jsonObject.getChild("spawn");
|
||||||
self.spawn.x = jsonSpawn.get(f32, "x", 0);
|
self.spawn.x = jsonSpawn.get(f32, "x", 0);
|
||||||
self.spawn.y = jsonSpawn.get(f32, "y", 0);
|
self.spawn.y = jsonSpawn.get(f32, "y", 0);
|
||||||
|
@ -247,8 +247,8 @@ pub fn main() !void {
|
|||||||
|
|
||||||
network.init();
|
network.init();
|
||||||
|
|
||||||
try renderer.RenderOctree.init();
|
try renderer.RenderStructure.init();
|
||||||
defer renderer.RenderOctree.deinit();
|
defer renderer.RenderStructure.deinit();
|
||||||
|
|
||||||
var manager = try network.ConnectionManager.init(12347, true);
|
var manager = try network.ConnectionManager.init(12347, true);
|
||||||
defer manager.deinit();
|
defer manager.deinit();
|
||||||
@ -279,7 +279,6 @@ pub fn main() !void {
|
|||||||
var deltaTime = @intToFloat(f64, newTime -% lastTime)/1000.0;
|
var deltaTime = @intToFloat(f64, newTime -% lastTime)/1000.0;
|
||||||
lastTime = newTime;
|
lastTime = newTime;
|
||||||
try game.update(deltaTime);
|
try game.update(deltaTime);
|
||||||
try renderer.RenderOctree.update(game.world.?.conn, game.playerPos, 4, 2.0);
|
|
||||||
{ // Render the game
|
{ // Render the game
|
||||||
c.glEnable(c.GL_CULL_FACE);
|
c.glEnable(c.GL_CULL_FACE);
|
||||||
c.glEnable(c.GL_DEPTH_TEST);
|
c.glEnable(c.GL_DEPTH_TEST);
|
||||||
|
@ -706,8 +706,8 @@ pub const Protocols = blk: {
|
|||||||
var z = data[2*size..3*size];
|
var z = data[2*size..3*size];
|
||||||
var neighbors = data[3*size..4*size];
|
var neighbors = data[3*size..4*size];
|
||||||
var visibleBlocks = data[4*size..];
|
var visibleBlocks = data[4*size..];
|
||||||
var result = try renderer.RenderOctree.allocator.create(chunk.ChunkVisibilityData);
|
var result = try renderer.RenderStructure.allocator.create(chunk.ChunkVisibilityData);
|
||||||
result.* = try chunk.ChunkVisibilityData.initEmpty(renderer.RenderOctree.allocator, pos, size);
|
result.* = try chunk.ChunkVisibilityData.initEmpty(renderer.RenderStructure.allocator, pos, size);
|
||||||
for(x) |_, i| {
|
for(x) |_, i| {
|
||||||
var block = result.visibles.addOneAssumeCapacity();
|
var block = result.visibles.addOneAssumeCapacity();
|
||||||
block.x = x[i];
|
block.x = x[i];
|
||||||
@ -718,7 +718,7 @@ pub const Protocols = blk: {
|
|||||||
block.block.typ = @intCast(u16, blockTypeAndData & 0xffff);
|
block.block.typ = @intCast(u16, blockTypeAndData & 0xffff);
|
||||||
block.block.data = @intCast(u16, blockTypeAndData >> 16);
|
block.block.data = @intCast(u16, blockTypeAndData >> 16);
|
||||||
}
|
}
|
||||||
try renderer.RenderOctree.updateChunkMesh(result);
|
try renderer.RenderStructure.updateChunkMesh(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn sendChunk(conn: *Connection, visData: chunk.ChunkVisibilityData) !void {
|
pub fn sendChunk(conn: *Connection, visData: chunk.ChunkVisibilityData) !void {
|
||||||
|
376
src/renderer.zig
376
src/renderer.zig
@ -185,7 +185,7 @@ pub fn render(playerPosition: Vec3d) !void {
|
|||||||
skyColor.mulEqualScalar(0.25);
|
skyColor.mulEqualScalar(0.25);
|
||||||
|
|
||||||
try renderWorld(world, ambient, skyColor, playerPosition);
|
try renderWorld(world, ambient, skyColor, playerPosition);
|
||||||
try RenderOctree.updateMeshes(startTime + maximumMeshTime);
|
try RenderStructure.updateMeshes(startTime + maximumMeshTime);
|
||||||
} else {
|
} else {
|
||||||
// TODO:
|
// TODO:
|
||||||
// clearColor.y = clearColor.z = 0.7f;
|
// clearColor.y = clearColor.z = 0.7f;
|
||||||
@ -234,7 +234,8 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
|
|||||||
// SimpleList<ReducedChunkMesh> visibleReduced = new SimpleList<ReducedChunkMesh>(new ReducedChunkMesh[64]);
|
// SimpleList<ReducedChunkMesh> visibleReduced = new SimpleList<ReducedChunkMesh>(new ReducedChunkMesh[64]);
|
||||||
var meshes = std.ArrayList(*chunk.meshing.ChunkMesh).init(main.threadAllocator);
|
var meshes = std.ArrayList(*chunk.meshing.ChunkMesh).init(main.threadAllocator);
|
||||||
defer meshes.deinit();
|
defer meshes.deinit();
|
||||||
try RenderOctree.getRenderChunks(playerPos, frustum, &meshes);
|
|
||||||
|
try RenderStructure.updateAndGetRenderChunks(game.world.?.conn, game.playerPos, 4, 2.0, frustum, &meshes);
|
||||||
// for (ChunkMesh mesh : Cubyz.chunkTree.getRenderChunks(frustumInt, x0, y0, z0)) {
|
// for (ChunkMesh mesh : Cubyz.chunkTree.getRenderChunks(frustumInt, x0, y0, z0)) {
|
||||||
// if (mesh instanceof NormalChunkMesh) {
|
// if (mesh instanceof NormalChunkMesh) {
|
||||||
// visibleChunks.add((NormalChunkMesh)mesh);
|
// visibleChunks.add((NormalChunkMesh)mesh);
|
||||||
@ -422,146 +423,24 @@ pub const Frustum = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const RenderOctree = struct {
|
pub const RenderStructure = struct {
|
||||||
pub var allocator: std.mem.Allocator = undefined;
|
pub var allocator: std.mem.Allocator = undefined;
|
||||||
var gpa: std.heap.GeneralPurposeAllocator(.{}) = undefined;
|
var gpa: std.heap.GeneralPurposeAllocator(.{}) = undefined;
|
||||||
pub const Node = struct {
|
|
||||||
shouldBeRemoved: bool = false,
|
const ChunkMeshNode = struct {
|
||||||
children: ?[]*Node = null,
|
|
||||||
size: chunk.ChunkCoordinate,
|
|
||||||
mesh: chunk.meshing.ChunkMesh,
|
mesh: chunk.meshing.ChunkMesh,
|
||||||
mutex: std.Thread.Mutex = std.Thread.Mutex{},
|
shouldBeRemoved: bool, // Internal use.
|
||||||
|
drawableChildren: u32, // How many children can be renderer. If this is 8 then there is no need to render this mesh.
|
||||||
fn init(replacement: ?*chunk.meshing.ChunkMesh, pos: chunk.ChunkPosition, size: chunk.ChunkCoordinate, meshRequests: *std.ArrayList(chunk.ChunkPosition)) !*Node {
|
|
||||||
var self = try allocator.create(Node);
|
|
||||||
self.* = Node {
|
|
||||||
.size = size,
|
|
||||||
.mesh = chunk.meshing.ChunkMesh.init(pos, replacement),
|
|
||||||
};
|
};
|
||||||
try meshRequests.append(pos);
|
var storageLists: [settings.highestLOD + 1][]?*ChunkMeshNode = undefined;
|
||||||
std.debug.assert(pos.voxelSize & pos.voxelSize-1 == 0);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deinit(self: *Node) void {
|
|
||||||
if(self.children) |children| {
|
|
||||||
for(children) |child| {
|
|
||||||
child.deinit();
|
|
||||||
}
|
|
||||||
allocator.free(children);
|
|
||||||
}
|
|
||||||
self.mesh.deinit();
|
|
||||||
allocator.destroy(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(self: *Node, playerPos: Vec3d, renderDistance: i32, maxRD: i32, minHeight: i32, maxHeight: i32, nearRenderDistance: i32, meshRequests: *std.ArrayList(chunk.ChunkPosition)) !void {
|
|
||||||
self.mutex.lock();
|
|
||||||
defer self.mutex.unlock();
|
|
||||||
// Calculate the minimum distance between this chunk and the player:
|
|
||||||
var minDist = self.mesh.pos.getMinDistanceSquared(playerPos);
|
|
||||||
// Check if this chunk is outside the nearRenderDistance or outside the height limits:
|
|
||||||
// if (wy + size <= Cubyz.world.chunkManager.getOrGenerateMapFragment(x, z, 32).getMinHeight() || wy > Cubyz.world.chunkManager.getOrGenerateMapFragment(x, z, 32).getMaxHeight()) {
|
|
||||||
if(self.mesh.pos.wy + self.size <= 0 or self.mesh.pos.wy > 1024) {
|
|
||||||
if(minDist > @intToFloat(f64, nearRenderDistance*nearRenderDistance)) {
|
|
||||||
if(self.children) |children| {
|
|
||||||
for(children) |child| {
|
|
||||||
child.deinit();
|
|
||||||
}
|
|
||||||
allocator.free(children);
|
|
||||||
self.children = null;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check if parts of this OctTree require using normal chunks:
|
|
||||||
if(self.size == chunk.chunkSize*2 and minDist < @intToFloat(f64, renderDistance*renderDistance)) {
|
|
||||||
if(self.children == null) {
|
|
||||||
self.children = try allocator.alloc(*Node, 8);
|
|
||||||
for(self.children.?) |*child, i| {
|
|
||||||
child.* = try Node.init(&self.mesh, chunk.ChunkPosition{
|
|
||||||
.wx = self.mesh.pos.wx + (if(i & 1 == 0) 0 else @divFloor(self.size, 2)),
|
|
||||||
.wy = self.mesh.pos.wy + (if(i & 2 == 0) 0 else @divFloor(self.size, 2)),
|
|
||||||
.wz = self.mesh.pos.wz + (if(i & 4 == 0) 0 else @divFloor(self.size, 2)),
|
|
||||||
.voxelSize = @divFloor(self.mesh.pos.voxelSize, 2),
|
|
||||||
}, @divFloor(self.size, 2), meshRequests);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(self.children.?) |child| {
|
|
||||||
try child.update(playerPos, renderDistance, @divFloor(maxRD, 2), minHeight, maxHeight, nearRenderDistance, meshRequests);
|
|
||||||
}
|
|
||||||
// Check if parts of this OctTree require a higher resolution:
|
|
||||||
} else if(minDist < @intToFloat(f64, maxRD*maxRD)/4 and self.size > chunk.chunkSize*2) {
|
|
||||||
if(self.children == null) {
|
|
||||||
self.children = try allocator.alloc(*Node, 8);
|
|
||||||
for(self.children.?) |*child, i| {
|
|
||||||
child.* = try Node.init(&self.mesh, chunk.ChunkPosition{
|
|
||||||
.wx = self.mesh.pos.wx + (if(i & 1 == 0) 0 else @divFloor(self.size, 2)),
|
|
||||||
.wy = self.mesh.pos.wy + (if(i & 2 == 0) 0 else @divFloor(self.size, 2)),
|
|
||||||
.wz = self.mesh.pos.wz + (if(i & 4 == 0) 0 else @divFloor(self.size, 2)),
|
|
||||||
.voxelSize = @divFloor(self.mesh.pos.voxelSize, 2),
|
|
||||||
}, @divFloor(self.size, 2), meshRequests);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(self.children.?) |child| {
|
|
||||||
try child.update(playerPos, renderDistance, @divFloor(maxRD, 2), minHeight, maxHeight, nearRenderDistance, meshRequests);
|
|
||||||
}
|
|
||||||
// This OctTree doesn't require higher resolution:
|
|
||||||
} else {
|
|
||||||
if(self.children) |children| {
|
|
||||||
for(children) |child| {
|
|
||||||
child.deinit();
|
|
||||||
}
|
|
||||||
allocator.free(children);
|
|
||||||
self.children = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn getChunks(self: *Node, playerPos: Vec3d, frustum: Frustum, meshes: *std.ArrayList(*chunk.meshing.ChunkMesh)) !void {
|
|
||||||
self.mutex.lock();
|
|
||||||
defer self.mutex.unlock();
|
|
||||||
if(self.children) |children| {
|
|
||||||
for(children) |child| {
|
|
||||||
try child.getChunks(playerPos, frustum, meshes);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(frustum.testAAB(Vec3f{
|
|
||||||
.x = @floatCast(f32, @intToFloat(f64, self.mesh.pos.wx) - playerPos.x),
|
|
||||||
.y = @floatCast(f32, @intToFloat(f64, self.mesh.pos.wy) - playerPos.y),
|
|
||||||
.z = @floatCast(f32, @intToFloat(f64, self.mesh.pos.wz) - playerPos.z),
|
|
||||||
}, Vec3f{
|
|
||||||
.x = @intToFloat(f32, self.size),
|
|
||||||
.y = @intToFloat(f32, self.size),
|
|
||||||
.z = @intToFloat(f32, self.size),
|
|
||||||
})) {
|
|
||||||
try meshes.append(&self.mesh);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO:
|
|
||||||
// public boolean testFrustum(FrustumIntersection frustumInt, double x0, double y0, double z0) {
|
|
||||||
// return frustumInt.testAab((float)(wx - x0), (float)(wy - y0), (float)(wz - z0), (float)(wx + size - x0), (float)(wy + size - y0), (float)(wz + size - z0));
|
|
||||||
// }
|
|
||||||
};
|
|
||||||
|
|
||||||
const HashMapKey3D = struct {
|
|
||||||
x: chunk.ChunkCoordinate,
|
|
||||||
y: chunk.ChunkCoordinate,
|
|
||||||
z: chunk.ChunkCoordinate,
|
|
||||||
|
|
||||||
pub fn hash(_: anytype, key: HashMapKey3D) u64 {
|
|
||||||
return @bitCast(u32, ((key.x << 13) | (key.x >> 19)) ^ ((key.y << 7) | (key.y >> 25)) ^ ((key.z << 23) | (key.z >> 9))); // This should be a good hash for now. TODO: Test how good it really is and find a better one.
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn eql(_: anytype, key: HashMapKey3D, other: HashMapKey3D) bool {
|
|
||||||
return key.x == other.x and key.y == other.y and key.z == other.z;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var roots: std.HashMap(HashMapKey3D, *Node, HashMapKey3D, 80) = undefined;
|
|
||||||
var updatableList: std.ArrayList(*chunk.ChunkVisibilityData) = undefined;
|
var updatableList: std.ArrayList(*chunk.ChunkVisibilityData) = undefined;
|
||||||
var lastRD: i32 = 0;
|
var lastRD: i32 = 0;
|
||||||
var lastFactor: f32 = 0;
|
var lastFactor: f32 = 0;
|
||||||
|
var lastX: [settings.highestLOD + 1]chunk.ChunkCoordinate = [_]chunk.ChunkCoordinate{0} ** (settings.highestLOD + 1);
|
||||||
|
var lastY: [settings.highestLOD + 1]chunk.ChunkCoordinate = [_]chunk.ChunkCoordinate{0} ** (settings.highestLOD + 1);
|
||||||
|
var lastZ: [settings.highestLOD + 1]chunk.ChunkCoordinate = [_]chunk.ChunkCoordinate{0} ** (settings.highestLOD + 1);
|
||||||
|
var lastSize: [settings.highestLOD + 1]chunk.ChunkCoordinate = [_]chunk.ChunkCoordinate{0} ** (settings.highestLOD + 1);
|
||||||
|
var lodMutex: [settings.highestLOD + 1]std.Thread.Mutex = [_]std.Thread.Mutex{std.Thread.Mutex{}} ** (settings.highestLOD + 1);
|
||||||
var mutex = std.Thread.Mutex{};
|
var mutex = std.Thread.Mutex{};
|
||||||
|
|
||||||
pub fn init() !void {
|
pub fn init() !void {
|
||||||
@ -569,16 +448,22 @@ pub const RenderOctree = struct {
|
|||||||
lastFactor = 0;
|
lastFactor = 0;
|
||||||
gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
allocator = gpa.allocator();
|
allocator = gpa.allocator();
|
||||||
roots = std.HashMap(HashMapKey3D, *Node, HashMapKey3D, 80).initContext(allocator, undefined);
|
|
||||||
updatableList = std.ArrayList(*chunk.ChunkVisibilityData).init(allocator);
|
updatableList = std.ArrayList(*chunk.ChunkVisibilityData).init(allocator);
|
||||||
|
for(storageLists) |*storageList| {
|
||||||
|
storageList.* = try allocator.alloc(?*ChunkMeshNode, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit() void {
|
pub fn deinit() void {
|
||||||
var iterator = roots.valueIterator();
|
for(storageLists) |storageList| {
|
||||||
while(iterator.next()) |value| {
|
for(storageList) |nullChunkMesh| {
|
||||||
value.*.deinit();
|
if(nullChunkMesh) |chunkMesh| {
|
||||||
|
chunkMesh.mesh.deinit();
|
||||||
|
allocator.destroy(chunkMesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allocator.free(storageList);
|
||||||
}
|
}
|
||||||
roots.deinit();
|
|
||||||
for(updatableList.items) |updatable| {
|
for(updatableList.items) |updatable| {
|
||||||
updatable.visibles.deinit();
|
updatable.visibles.deinit();
|
||||||
allocator.destroy(updatable);
|
allocator.destroy(updatable);
|
||||||
@ -590,128 +475,144 @@ pub const RenderOctree = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(conn: *network.Connection, playerPos: Vec3d, renderDistance: i32, LODFactor: f32) !void {
|
fn _getNode(pos: chunk.ChunkPosition) ?*ChunkMeshNode {
|
||||||
|
var lod = std.math.log2_int(chunk.UChunkCoordinate, pos.voxelSize);
|
||||||
|
lodMutex[lod].lock();
|
||||||
|
defer lodMutex[lod].unlock();
|
||||||
|
var xIndex = pos.wx-%lastX[lod] >> lod+chunk.chunkShift;
|
||||||
|
var yIndex = pos.wy-%lastY[lod] >> lod+chunk.chunkShift;
|
||||||
|
var zIndex = pos.wz-%lastZ[lod] >> lod+chunk.chunkShift;
|
||||||
|
if(xIndex < 0 or xIndex >= lastSize[lod]) return null;
|
||||||
|
if(yIndex < 0 or yIndex >= lastSize[lod]) return null;
|
||||||
|
if(zIndex < 0 or zIndex >= lastSize[lod]) return null;
|
||||||
|
var index = (xIndex*lastSize[lod] + yIndex)*lastSize[lod] + zIndex;
|
||||||
|
return storageLists[lod][@intCast(usize, index)];
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
if(lastRD != renderDistance and lastFactor != LODFactor) {
|
||||||
// TODO:
|
// TODO:
|
||||||
// Protocols.GENERIC_UPDATE.sendRenderDistance(Cubyz.world.serverConnection, renderDistance, LODFactor);
|
// Protocols.GENERIC_UPDATE.sendRenderDistance(Cubyz.world.serverConnection, renderDistance, LODFactor);
|
||||||
}
|
}
|
||||||
var px = @floatToInt(chunk.ChunkCoordinate, playerPos.x);
|
const px = @floatToInt(chunk.ChunkCoordinate, playerPos.x);
|
||||||
var py = @floatToInt(chunk.ChunkCoordinate, playerPos.y);
|
const py = @floatToInt(chunk.ChunkCoordinate, playerPos.y);
|
||||||
var pz = @floatToInt(chunk.ChunkCoordinate, playerPos.z);
|
const pz = @floatToInt(chunk.ChunkCoordinate, playerPos.z);
|
||||||
var maxRenderDistance = @floatToInt(i32, @ceil(@intToFloat(f32, renderDistance*chunk.chunkSize << settings.highestLOD)*LODFactor));
|
|
||||||
var nearRenderDistance = renderDistance*chunk.chunkSize;
|
|
||||||
var LODShift = settings.highestLOD + chunk.chunkShift;
|
|
||||||
var LODSize = chunk.chunkSize << settings.highestLOD;
|
|
||||||
var LODMask = LODSize - 1;
|
|
||||||
var minX = (px - maxRenderDistance - LODMask) & ~LODMask;
|
|
||||||
var maxX = (px + maxRenderDistance + LODMask) & ~LODMask;
|
|
||||||
// The LOD chunks are offset from grid to make generation easier.
|
|
||||||
minX += @divExact(LODSize, 2) - chunk.chunkSize;
|
|
||||||
maxX += @divExact(LODSize, 2) - chunk.chunkSize;
|
|
||||||
var newMap = std.HashMap(HashMapKey3D, *Node, HashMapKey3D, 80).initContext(allocator, undefined);
|
|
||||||
var meshRequests = std.ArrayList(chunk.ChunkPosition).init(main.threadAllocator);
|
var meshRequests = std.ArrayList(chunk.ChunkPosition).init(main.threadAllocator);
|
||||||
defer meshRequests.deinit();
|
defer meshRequests.deinit();
|
||||||
var x = minX;
|
|
||||||
while(x <= maxX): (x += LODSize) {
|
|
||||||
var maxYRenderDistanceSquare = @intToFloat(f32, maxRenderDistance)*@intToFloat(f32, maxRenderDistance) - @intToFloat(f32, (x - px))*@intToFloat(f32, (x - px));
|
|
||||||
if(maxYRenderDistanceSquare < 0) continue;
|
|
||||||
var maxYRenderDistance = @floatToInt(i32, @ceil(@sqrt(maxYRenderDistanceSquare)));
|
|
||||||
var minY = (py - maxYRenderDistance - LODMask) & ~LODMask;
|
|
||||||
var maxY = (py + maxYRenderDistance + LODMask) & ~LODMask;
|
|
||||||
// The LOD chunks are offset from grid to make generation easier.
|
|
||||||
minY += @divFloor(LODSize, 2) - chunk.chunkSize;
|
|
||||||
maxY += @divFloor(LODSize, 2) - chunk.chunkSize;
|
|
||||||
var y = minY;
|
|
||||||
while(y <= maxY): (y += LODSize) {
|
|
||||||
var maxZRenderDistanceSquare = @intToFloat(f32, maxYRenderDistance)*@intToFloat(f32, maxYRenderDistance) - @intToFloat(f32, (y - py))*@intToFloat(f32, (y - py));
|
|
||||||
if(maxZRenderDistanceSquare < 0) continue;
|
|
||||||
var maxZRenderDistance = @floatToInt(i32, @ceil(@sqrt(maxZRenderDistanceSquare)));
|
|
||||||
var minZ = (pz - maxZRenderDistance - LODMask) & ~LODMask;
|
|
||||||
var maxZ = (pz + maxZRenderDistance + LODMask) & ~LODMask;
|
|
||||||
// The LOD chunks are offset from grid to make generation easier.
|
|
||||||
minZ += @divFloor(LODSize, 2) - chunk.chunkSize;
|
|
||||||
maxZ += @divFloor(LODSize, 2) - chunk.chunkSize;
|
|
||||||
var z = minZ;
|
|
||||||
while(z <= maxZ): (z += LODSize) {
|
|
||||||
if(y + LODSize <= 0 or y > 1024) {
|
|
||||||
var dx = @maximum(0, try std.math.absInt(x + @divFloor(LODSize, 2) - px) - @divFloor(LODSize, 2));
|
|
||||||
var dy = @maximum(0, try std.math.absInt(y + @divFloor(LODSize, 2) - py) - @divFloor(LODSize, 2));
|
|
||||||
var dz = @maximum(0, try std.math.absInt(z + @divFloor(LODSize, 2) - pz) - @divFloor(LODSize, 2));
|
|
||||||
if(dx*dx + dy*dy + dz*dz > nearRenderDistance*nearRenderDistance) continue;
|
|
||||||
}
|
|
||||||
var rootX = x >> LODShift;
|
|
||||||
var rootY = y >> LODShift;
|
|
||||||
var rootZ = z >> LODShift;
|
|
||||||
|
|
||||||
var key = HashMapKey3D{.x = rootX, .y = rootY, .z = rootZ};
|
for(storageLists) |_, _lod| {
|
||||||
var node = roots.get(key);
|
const lod = @intCast(u5, _lod);
|
||||||
|
var maxRenderDistance = renderDistance*chunk.chunkSize << lod;
|
||||||
|
if(lod != 0) maxRenderDistance = @floatToInt(i32, @ceil(@intToFloat(f32, maxRenderDistance)*LODFactor));
|
||||||
|
const size = @intCast(chunk.UChunkCoordinate, chunk.chunkSize << lod);
|
||||||
|
const mask: chunk.ChunkCoordinate = size - 1;
|
||||||
|
const invMask: chunk.ChunkCoordinate = ~mask;
|
||||||
|
|
||||||
|
const maxSideLength = @intCast(chunk.UChunkCoordinate, @divFloor(2*maxRenderDistance + size-1, size) + 2);
|
||||||
|
var newList = try allocator.alloc(?*ChunkMeshNode, maxSideLength*maxSideLength*maxSideLength);
|
||||||
|
std.mem.set(?*ChunkMeshNode, newList, null);
|
||||||
|
|
||||||
|
const startX = size*(@divFloor(px, size) -% maxSideLength/2);
|
||||||
|
const startY = size*(@divFloor(py, size) -% maxSideLength/2);
|
||||||
|
const startZ = size*(@divFloor(pz, size) -% maxSideLength/2);
|
||||||
|
|
||||||
|
const minX = px-%maxRenderDistance-%size & invMask;
|
||||||
|
const maxX = px+%maxRenderDistance & invMask;
|
||||||
|
var x = minX;
|
||||||
|
while(x != maxX): (x +%= size) {
|
||||||
|
const xIndex = @divExact(x -% startX, size);
|
||||||
|
var deltaX: f32 = @fabs(@intToFloat(f32, x + size/2 - px));
|
||||||
|
deltaX = @maximum(0, deltaX - @intToFloat(f32, size/2));
|
||||||
|
const maxYRenderDistanceSquare = @intToFloat(f32, maxRenderDistance)*@intToFloat(f32, maxRenderDistance) - deltaX*deltaX;
|
||||||
|
if(maxYRenderDistanceSquare < 0) continue;
|
||||||
|
const maxYRenderDistance = @floatToInt(i32, @ceil(@sqrt(maxYRenderDistanceSquare)));
|
||||||
|
|
||||||
|
const minY = py-maxYRenderDistance-size & invMask;
|
||||||
|
const maxY = py+maxYRenderDistance & invMask;
|
||||||
|
var y = minY;
|
||||||
|
while(y != maxY): (y +%= size) {
|
||||||
|
const yIndex = @divExact(y -% startY, size);
|
||||||
|
var deltaY: f32 = @fabs(@intToFloat(f32, y + size/2 - py));
|
||||||
|
deltaY = @maximum(0, deltaY - @intToFloat(f32, size/2));
|
||||||
|
const maxZRenderDistanceSquare = @intToFloat(f32, maxYRenderDistance)*@intToFloat(f32, maxYRenderDistance) - deltaY*deltaY;
|
||||||
|
if(maxZRenderDistanceSquare < 0) continue;
|
||||||
|
const maxZRenderDistance = @floatToInt(i32, @ceil(@sqrt(maxZRenderDistanceSquare)));
|
||||||
|
|
||||||
|
const minZ = pz-maxZRenderDistance-size & invMask;
|
||||||
|
const maxZ = pz+maxZRenderDistance & invMask;
|
||||||
|
var z = minZ;
|
||||||
|
while(z != maxZ): (z +%= size) {
|
||||||
|
const zIndex = @divExact(z -% startZ, size);
|
||||||
|
const index = (xIndex*maxSideLength + yIndex)*maxSideLength + zIndex;
|
||||||
|
const pos = chunk.ChunkPosition{.wx=x, .wy=y, .wz=z, .voxelSize=@as(chunk.UChunkCoordinate, 1)<<lod};
|
||||||
|
var node = _getNode(pos);
|
||||||
if(node) |_node| {
|
if(node) |_node| {
|
||||||
// Mark that this node should not be removed.
|
|
||||||
_node.shouldBeRemoved = false;
|
_node.shouldBeRemoved = false;
|
||||||
} else {
|
} else {
|
||||||
node = try Node.init(null, .{.wx=x, .wy=y, .wz=z, .voxelSize=@intCast(chunk.UChunkCoordinate, LODSize>>chunk.chunkShift)}, LODSize, &meshRequests);
|
node = try allocator.create(ChunkMeshNode);
|
||||||
// Mark this node to be potentially removed in the next update:
|
node.?.mesh = chunk.meshing.ChunkMesh.init(pos, null); // TODO: Replacement mesh?
|
||||||
node.?.shouldBeRemoved = true;
|
node.?.shouldBeRemoved = true; // Might be removed in the next iteration.
|
||||||
|
try meshRequests.append(pos);
|
||||||
}
|
}
|
||||||
try newMap.put(key, node.?);
|
if(frustum.testAAB(Vec3f{
|
||||||
try node.?.update(playerPos, renderDistance*chunk.chunkSize, maxRenderDistance, 0, 1024, nearRenderDistance, &meshRequests);
|
.x = @floatCast(f32, @intToFloat(f64, x) - playerPos.x),
|
||||||
|
.y = @floatCast(f32, @intToFloat(f64, y) - playerPos.y),
|
||||||
|
.z = @floatCast(f32, @intToFloat(f64, z) - playerPos.z),
|
||||||
|
}, Vec3f{
|
||||||
|
.x = @intToFloat(f32, size),
|
||||||
|
.y = @intToFloat(f32, size),
|
||||||
|
.z = @intToFloat(f32, size),
|
||||||
|
}) and node.?.drawableChildren < 8) { // TODO: Case where more than 0 and less than 8 exist.
|
||||||
|
try meshes.append(&node.?.mesh);
|
||||||
|
}
|
||||||
|
if(lod+1 != storageLists.len and node.?.mesh.generated) {
|
||||||
|
if(_getNode(.{.wx=x, .wy=y, .wz=z, .voxelSize=@as(chunk.UChunkCoordinate, 1)<<(lod+1)})) |parent| {
|
||||||
|
parent.drawableChildren += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.?.drawableChildren = 0;
|
||||||
|
newList[@intCast(usize, index)] = node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Clean memory for unused nodes:
|
|
||||||
mutex.lock();
|
var oldList = storageLists[lod];
|
||||||
defer mutex.unlock();
|
{
|
||||||
var iterator = roots.valueIterator();
|
lodMutex[lod].lock();
|
||||||
while(iterator.next()) |node| {
|
defer lodMutex[lod].unlock();
|
||||||
if(node.*.shouldBeRemoved) {
|
lastX[lod] = startX;
|
||||||
node.*.deinit();
|
lastY[lod] = startY;
|
||||||
|
lastZ[lod] = startZ;
|
||||||
|
lastSize[lod] = maxSideLength;
|
||||||
|
storageLists[lod] = newList;
|
||||||
|
}
|
||||||
|
for(oldList) |nullMesh| {
|
||||||
|
if(nullMesh) |mesh| {
|
||||||
|
if(mesh.shouldBeRemoved) {
|
||||||
|
mesh.mesh.deinit();
|
||||||
|
allocator.destroy(mesh);
|
||||||
} else {
|
} else {
|
||||||
node.*.shouldBeRemoved = true;
|
mesh.shouldBeRemoved = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
roots.deinit();
|
}
|
||||||
roots = newMap;
|
allocator.free(oldList);
|
||||||
|
}
|
||||||
|
|
||||||
lastRD = renderDistance;
|
lastRD = renderDistance;
|
||||||
lastFactor = LODFactor;
|
lastFactor = LODFactor;
|
||||||
// Make requests after updating the, to avoid concurrency issues and reduce the number of requests:
|
// Make requests after updating the, to avoid concurrency issues and reduce the number of requests:
|
||||||
try network.Protocols.chunkRequest.sendRequest(conn, meshRequests.items);
|
try network.Protocols.chunkRequest.sendRequest(conn, meshRequests.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn findNode(pos: chunk.ChunkPosition) ?*Node {
|
|
||||||
var LODShift = settings.highestLOD + chunk.chunkShift;
|
|
||||||
var LODSize = @as(chunk.UChunkCoordinate, 1) << LODShift;
|
|
||||||
var key = HashMapKey3D{
|
|
||||||
.x = (pos.wx - LODSize/2 + chunk.chunkSize) >> LODShift,
|
|
||||||
.y = (pos.wy - LODSize/2 + chunk.chunkSize) >> LODShift,
|
|
||||||
.z = (pos.wz - LODSize/2 + chunk.chunkSize) >> LODShift,
|
|
||||||
};
|
|
||||||
const rootNode: *Node = roots.get(key) orelse return null;
|
|
||||||
rootNode.mutex.lock();
|
|
||||||
defer rootNode.mutex.unlock();
|
|
||||||
|
|
||||||
var node = rootNode;
|
|
||||||
|
|
||||||
while(node.mesh.pos.voxelSize != pos.voxelSize) {
|
|
||||||
var children = node.children orelse return null;
|
|
||||||
var i: u3 = 0;
|
|
||||||
if(pos.wx >= node.mesh.pos.wx + @divFloor(node.size, 2)) i += 1;
|
|
||||||
if(pos.wy >= node.mesh.pos.wy + @divFloor(node.size, 2)) i += 2;
|
|
||||||
if(pos.wz >= node.mesh.pos.wz + @divFloor(node.size, 2)) i += 4;
|
|
||||||
node = children[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn updateMeshes(targetTime: i64) !void {
|
pub fn updateMeshes(targetTime: i64) !void {
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
defer mutex.unlock();
|
defer mutex.unlock();
|
||||||
while(updatableList.items.len != 0) {
|
while(updatableList.items.len != 0) {
|
||||||
const mesh = updatableList.pop();
|
const mesh = updatableList.pop();
|
||||||
const nullNode = findNode(mesh.pos);
|
const nullNode = _getNode(mesh.pos);
|
||||||
if(nullNode) |node| {
|
if(nullNode) |node| {
|
||||||
node.mutex.lock();
|
|
||||||
defer node.mutex.unlock();
|
|
||||||
try node.mesh.regenerateMesh(mesh);
|
try node.mesh.regenerateMesh(mesh);
|
||||||
}
|
}
|
||||||
mesh.visibles.deinit();
|
mesh.visibles.deinit();
|
||||||
@ -725,15 +626,6 @@ pub const RenderOctree = struct {
|
|||||||
defer mutex.unlock();
|
defer mutex.unlock();
|
||||||
try updatableList.append(mesh);
|
try updatableList.append(mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getRenderChunks(playerPos: Vec3d, frustum: Frustum, meshes: *std.ArrayList(*chunk.meshing.ChunkMesh)) !void {
|
|
||||||
mutex.lock();
|
|
||||||
defer mutex.unlock();
|
|
||||||
var iterator = roots.valueIterator();
|
|
||||||
while(iterator.next()) |node| {
|
|
||||||
try node.*.getChunks(playerPos, frustum, meshes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// public void updateChunkMesh(VisibleChunk mesh) {
|
// public void updateChunkMesh(VisibleChunk mesh) {
|
||||||
// OctTreeNode node = findNode(mesh);
|
// OctTreeNode node = findNode(mesh);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user