Tree generation

This commit is contained in:
2026-09-25 17:41:36 -03:00
parent ea9d821047
commit fdd236071e
7 changed files with 305 additions and 7 deletions
+27 -2
View File
@@ -38,6 +38,9 @@ export interface Chunk {
meshes: Partial<Record<RenderLayer, ChunkMesh>>;
// only for translucent meshes that have to be sorted again as the camera moves
translucent_sort?: TranslucentSort;
// blocks its generation put in neighboring chunks (leaves), as x, y, z, numeric id. kept so a neighbor that
// generates later, or unloads and comes back, still gets them
spills?: Int32Array;
}
export interface ChunkMesh {
@@ -491,7 +494,7 @@ export class ClientLevel {
let chunk = this.chunks.get(key);
if (chunk) {
// a placeholder made by a neighbor's feature, keep its blocks where generation left air
// blocks placed here before it generated, keep them where generation left air
const existing = chunk.blocks;
for (let i = 0; i < blocks.length; i++) {
if (blocks[i] !== AIR) {
@@ -503,7 +506,16 @@ export class ClientLevel {
}
chunk.generated = true;
chunk.dirty = true;
chunk.spills = spills;
// the same rules as the server (server/game/world.ts): a chunk's own blocks, then what its neighbors'
// features put in it, only where it has air. both ways, since the neighbors may have generated first
for (const [dx, dz] of ALL_NEIGHBOR_OFFSETS) {
const neighbor_spills = this.get_chunk(cx + dx, cz + dz)?.spills;
if (neighbor_spills) {
this.#apply_spills(neighbor_spills, chunk);
}
}
for (let i = 0; i < spills.length; i += 4) {
this.#set_block_raw(spills[i], spills[i + 1], spills[i + 2], spills[i + 3]);
}
@@ -583,7 +595,11 @@ export class ClientLevel {
}
const chunk_x = Math.floor(x / CHUNK_SIZE);
const chunk_z = Math.floor(z / CHUNK_SIZE);
const chunk = this.get_chunk(chunk_x, chunk_z) ?? this.add_chunk(chunk_x, chunk_z);
const chunk = this.get_chunk(chunk_x, chunk_z);
// a chunk that isn't generated yet takes it from our spills when it is
if (!chunk?.generated) {
return;
}
const lx = x - chunk_x * CHUNK_SIZE;
const lz = z - chunk_z * CHUNK_SIZE;
const index = y * CHUNK_AREA + lz * CHUNK_SIZE + lx;
@@ -594,6 +610,15 @@ export class ClientLevel {
}
}
// the spills that land in chunk
#apply_spills(spills: Int32Array, chunk: Chunk) {
for (let i = 0; i < spills.length; i += 4) {
if (Math.floor(spills[i] / CHUNK_SIZE) === chunk.x && Math.floor(spills[i + 2] / CHUNK_SIZE) === chunk.z) {
this.#set_block_raw(spills[i], spills[i + 1], spills[i + 2], spills[i + 3]);
}
}
}
delete_chunk_mesh(chunk: Chunk) {
for (const mesh of Object.values(chunk.meshes)) {
destroy_buffer(mesh.vertex_buffer);