import type { Vec3 } from "../../engine/math/Vec3" import type { Mesh } from "../../engine/scene/Mesh" import { oak } from "./trees/Oak" import { spruce } from "./trees/Spruce" import { birch } from "./trees/Birch" export type TreeKind = "oak" | "spruce" | "birch" /** One procedural tree instance. `growth` 0..1 runs sapling -> full grown: it scales * height and girth and adds canopy blobs / tiers. `seed` drives the per-tree random * wobble so a forest doesn't look cloned. */ export type Tree = { kind: TreeKind /** Trunk base, sitting on the ground. */ position: Vec3 growth: number seed: number } /** Definition of a tree species: which chunk materials its trunk + foliage bake * into, plus how to append its geometry. Each lives in its own `trees/.ts` * module (silhouette carries the species read); this file just assembles them. * `trunk`/`foliage` are chunk-material keys (see `level.ts` `ChunkMaterials`): * oak/spruce use the brown `bark`, birch the white `birch`; foliage is the oak * `leaf` or spruce `needle`. */ export type TreeSpecies = { kind: TreeKind trunk: string foliage: string build: (tree: Tree, trunk: Mesh, foliage: Mesh, lod: "full" | "impostor") => void } /** All tree species (also the placement roll's palette). Trees are baked at load, * not shipped per frame, so this order isn't an id contract like `MOB_KINDS` -- but * keeping it lets placement + tests stay registry-driven. */ export const TREE_KINDS: TreeKind[] = ["oak", "spruce", "birch"] /** The per-species definitions, one module each. Imported (not cloned) wherever * used, so it works the same on the main thread and in workers. */ const SPECIES: Record = { oak, spruce, birch } export namespace Tree { /** The species definition for a kind (its trunk/foliage materials + geometry). */ export function species(kind: TreeKind): TreeSpecies { return SPECIES[kind] } /** Append one tree into the caller-provided `trunk` + `foliage` meshes (which the * caller selects from the species' `trunk`/`foliage` material keys). `lod` * "impostor" bakes a much cheaper stand-in for far chunks; "full" is up close. */ export function build(tree: Tree, trunk: Mesh, foliage: Mesh, lod: "full" | "impostor" = "full"): void { SPECIES[tree.kind].build(tree, trunk, foliage, lod) } }