2026-08-08 14:15:32 +02:00
|
|
|
import type { Vec3 } from "../../engine/math/Vec3"
|
|
|
|
|
import type { Mesh } from "../../engine/scene/Mesh"
|
2026-08-07 19:11:43 +02:00
|
|
|
import { oak } from "./trees/Oak"
|
|
|
|
|
import { spruce } from "./trees/Spruce"
|
|
|
|
|
import { birch } from "./trees/Birch"
|
2026-08-04 13:53:59 +02:00
|
|
|
|
2026-08-07 19:11:43 +02:00
|
|
|
export type TreeKind = "oak" | "spruce" | "birch"
|
2026-08-04 13:53:59 +02:00
|
|
|
|
2026-08-07 19:11:43 +02:00
|
|
|
/** 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. */
|
2026-08-04 13:53:59 +02:00
|
|
|
export type Tree = {
|
2026-08-07 19:11:43 +02:00
|
|
|
kind: TreeKind
|
2026-08-04 13:53:59 +02:00
|
|
|
/** Trunk base, sitting on the ground. */
|
|
|
|
|
position: Vec3
|
|
|
|
|
growth: number
|
|
|
|
|
seed: number
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-07 19:11:43 +02:00
|
|
|
/** 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/<Kind>.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
|
|
|
|
|
}
|
2026-08-04 13:53:59 +02:00
|
|
|
|
2026-08-07 19:11:43 +02:00
|
|
|
/** 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"]
|
2026-08-04 13:53:59 +02:00
|
|
|
|
2026-08-07 19:11:43 +02:00
|
|
|
/** 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<TreeKind, TreeSpecies> = { oak, spruce, birch }
|
2026-08-04 13:53:59 +02:00
|
|
|
|
2026-08-07 19:11:43 +02:00
|
|
|
export namespace Tree {
|
|
|
|
|
/** The species definition for a kind (its trunk/foliage materials + geometry). */
|
|
|
|
|
export function species(kind: TreeKind): TreeSpecies {
|
|
|
|
|
return SPECIES[kind]
|
2026-08-04 13:53:59 +02:00
|
|
|
}
|
|
|
|
|
|
2026-08-07 19:11:43 +02:00
|
|
|
/** 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)
|
2026-08-04 13:53:59 +02:00
|
|
|
}
|
|
|
|
|
}
|