import { Vec3 } from "engine/math/Vec3" import type { Mesh } from "engine/scene/Mesh" import type { Material } from "engine/render/Material" import type { Prefab } from "engine/scene/Prefab" import type { Tree } from "../Tree" import { blob, lerp, limb, TAU, rng } from "./treekit" // Silver birch: tall, slender, near-straight trunk under an airy, high, slightly // drooping canopy -- a lean silhouette between the broad oak and conical spruce. // Trunk = white birch bark, foliage = oak leaf (the white trunk carries the read). export type Birch = Prefab export namespace Birch { export function create(trunk: Material, foliage: Material): Birch { return { position: (tree) => tree.position, bakeNear: (tree, batch) => build(tree, batch.mesh(trunk), batch.mesh(foliage), "full"), bakeFar: (tree, batch) => build(tree, batch.mesh(trunk), batch.mesh(foliage), "impostor"), collider: treeCollider, } } function treeCollider(tree: Tree) { if (tree.growth <= 0.35) { return null } const radius = tree.growth * 0.2 + 0.15 return { shape: "box" as const, minX: tree.position.x - radius, maxX: tree.position.x + radius, minZ: tree.position.z - radius, maxZ: tree.position.z + radius, top: tree.position.y + 3, standable: false, } } } function build( tree: Tree, trunk: Mesh, leaves: Mesh, lod: "full" | "impostor", ): void { const base = tree.position const g = tree.growth const rand = rng(tree.seed) const h = lerp(1, 8.5, g) const rTrunk = lerp(0.03, 0.16, g) const canopyY = base.y + h * 0.75 const blobR = h * 0.22 if (lod === "impostor") { limb( trunk, base, { x: base.x, y: base.y + h * 0.9, z: base.z }, rTrunk, rTrunk * 0.5, 3, ) blob(leaves, { x: base.x, y: canopyY, z: base.z }, blobR * 1.1, rand, 4, 2) return } limb( trunk, base, { x: base.x, y: base.y + h * 0.88, z: base.z }, rTrunk, rTrunk * 0.35, 5, ) const spread = h * 0.22 // Sparse small blobs clustered high, biased downward so the crown droops. const blobs = 2 + Math.round(g * 2) for (let i = 0; i < blobs; i++) { const angle = rand() * TAU const rad = i === 0 ? 0 : spread * (0.5 + rand() * 0.5) const center = { x: base.x + Math.cos(angle) * rad, y: canopyY + (rand() - 0.6) * spread, z: base.z + Math.sin(angle) * rad, } blob(leaves, center, blobR * (0.7 + rand() * 0.4), rand) } // Grown birches trail a few thin, near-horizontal drooping twigs. if (g > 0.5) { const branches = 2 + Math.round(rand()) for (let i = 0; i < branches; i++) { const angle = rand() * TAU const dir = Vec3.normalize({ x: Math.cos(angle), y: 0.6, z: Math.sin(angle), }) const start = { x: base.x, y: base.y + h * 0.7, z: base.z } const end = Vec3.add(start, Vec3.scale(dir, h * 0.22)) limb(trunk, start, end, rTrunk * 0.4, rTrunk * 0.15, 4) blob(leaves, end, blobR * 0.55, rand) } } }