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
+40 -1
View File
@@ -3,6 +3,7 @@
import { CHUNK_AREA, CHUNK_HEIGHT, CHUNK_SIZE, SEA_LEVEL } from "$/common/constants.ts";
import { OctaveNoise2D } from "./noise.ts";
import { OverworldTerrain, TerrainColumn } from "./terrain.ts";
import { BIOME_TREES, grow_tree, tree_sites } from "./trees.ts";
// density is sampled every CELL_WIDTH blocks across and CELL_HEIGHT up, caves every CAVE_CELL_HEIGHT up since
// tunnels are thinner than terrain features
@@ -28,6 +29,8 @@ export interface OverworldBlocks {
sand: number;
snow: number;
water: number;
log: number;
leaves: number;
}
type Palette = "stone" | "dirt" | "grass" | "sand" | "snow";
@@ -183,7 +186,8 @@ function generators_for(seed: string): SeedGenerators {
return found;
}
// fills blocks (indexed y * CHUNK_AREA + z * CHUNK_SIZE + x) and each column's surface height and biome
// fills blocks (indexed y * CHUNK_AREA + z * CHUNK_SIZE + x) and each column's surface height and biome.
// blocks trees put in other chunks go to spill
export function generate_overworld(
blocks: Uint32Array,
heights: Int32Array,
@@ -192,6 +196,7 @@ export function generate_overworld(
chunk_z: number,
seed: string,
ids: OverworldBlocks,
spill: (x: number, y: number, z: number, block: number) => void,
) {
const { terrain, snow_line, strata } = generators_for(seed);
const x0 = chunk_x * CHUNK_SIZE;
@@ -236,6 +241,40 @@ export function generate_overworld(
fill_column(x, z);
}
}
grow_trees();
function grow_trees() {
const place = (x: number, y: number, z: number, block: "log" | "leaves") => {
if (y < 0 || y >= CHUNK_HEIGHT) return;
const lx = x - x0;
const lz = z - z0;
if (lx < 0 || lx >= CHUNK_SIZE || lz < 0 || lz >= CHUNK_SIZE) {
// the other chunk only takes it where it has air
spill(x, y, z, ids[block]);
return;
}
const i = y * CHUNK_AREA + lz * CHUNK_SIZE + lx;
const current = blocks[i];
if (current === 0 || (block === "log" && current === ids.leaves)) {
blocks[i] = ids[block];
}
};
for (const site of tree_sites(seed, chunk_x, chunk_z)) {
const column = (site.z - z0) * CHUNK_SIZE + (site.x - x0);
const trees = BIOME_TREES[biomes[column]];
if (!trees || site.rng.next() >= trees.chance) continue;
// on soil with air above it, so never under water or on bare rock
const ground_y = heights[column];
const ground = blocks[ground_y * CHUNK_AREA + column];
const above = blocks[(ground_y + 1) * CHUNK_AREA + column];
if ((ground !== ids.grass && ground !== ids.dirt && ground !== ids.snow) || above !== 0) continue;
const kind = trees.kinds[Math.floor(site.rng.next() * trees.kinds.length)];
grow_tree(kind, site.x, ground_y + 1, site.z, site.rng, place);
}
}
function fill_column(x: number, z: number) {
const cell_x = Math.min(Math.floor(x / CELL_WIDTH), CORNERS - 2);