import { b as Palette, c as Prop, R as Rng } from './types-C_yucwmh.js'; import { Vector2, Material, Object3D, Group, Vector3, Quaternion } from 'three'; interface WindFieldOptions { /** Wind bearing: degrees (0 = +X, 90 = +Z) or a world [x, z] vector. Default 35°. */ direction?: number | [number, number]; /** Steady lean at full sway, in world units. Default 0.3. */ strength?: number; /** Gustiness 0–1: 0 = constant lean, 1 = deep travelling gusts. Default 0.5. */ gust?: number; /** Distance between gust crests, in metres (smaller = tighter ripples). Default 6. */ waveLength?: number; /** Speed the gust crests travel downwind. Default 2.2. */ waveSpeed?: number; } interface SwayOptions { /** Local height (metres) at which sway reaches full. Default 1. */ height?: number; /** Stiffness curve exponent — higher keeps the base stiffer. Default 1.6. */ stiffness?: number; /** Local height below which nothing moves (keeps trunks planted). Default 0. */ anchor?: number; } interface WindField { /** The shared shader uniforms (one set, referenced by every bound material). */ readonly uniforms: Record; /** The (normalized) wind direction in world XZ. Mutate via `setDirection`. */ readonly direction: Vector2; /** Current steady strength. */ strength: number; /** Every material patched so far. */ materials: Material[]; /** Point the wind along a new bearing (degrees) or [x, z] vector. */ setDirection(direction: number | [number, number]): WindField; /** Change the steady strength. */ setStrength(strength: number): WindField; /** The wind vector at a world point (CPU side) — for pushing agents, particles, boats. */ sample(x: number, z: number, time?: number): Vector2; /** Patch a material to sway. Composes with surface materials; idempotent per material. */ bind(material: Material, options?: SwayOptions): WindField; /** Self-animate: drive the shared clock from a rendered object's `onBeforeRender`. */ attach(object: Object3D): WindField; /** Convenience: `bind` every material under `target`, then `attach` the driver. */ sway(target: Object3D, options?: SwayOptions): WindField; /** Advance the clock manually instead of self-driving (for deterministic loops). */ update(dt: number): void; } /** * A shared wind field for vegetation and cloth — the environmental handshake * that makes a world breathe. One field drives many props: trees and grass * bend, a wheat field ripples, banners fly, all from the *same* gust, so a * breeze crosses the whole scene in step. The bend is a vertex-shader effect * (full PBR/shadows/fog survive), the gust travels downwind so nothing sways * in lockstep, and the field self-animates from the render loop — no update * wiring needed. `sample(x, z)` exposes the same wind on the CPU, so gameplay * (drifting agents, bobbing boats, blown particles) can read it too. * * ```ts * const wind = createWindField({ direction: 40, strength: 0.35 }); * wind.sway(forest.group, { height: 4, anchor: 1.0 }); // canopies bend * wind.sway(wheat.group, { height: 0.9, stiffness: 1.2 }); // blades ripple * ``` */ declare function createWindField(options?: WindFieldOptions): WindField; interface WindOptions extends WindFieldOptions, SwayOptions { /** @deprecated use `anchor`. Local height where sway begins. */ anchorHeight?: number; /** Bind against an existing field instead of making a new one. */ field?: WindField; } type Wind = WindField; /** * Make everything under `target` sway in the wind — the one-call path. Builds * (or reuses) a {@link WindField}, binds every material and self-animates. * Returns the field, so you can `sample()` it, re-aim it, or share it with more * props. Kept `update()`-compatible for deterministic loops. * * ```ts * const wind = applyWind(forest.group, { strength: 0.3, height: 4, anchor: 1 }); * // optional: game.onUpdate((t) => wind.update(t.delta)); * ``` */ declare function applyWind(target: Object3D, options?: WindOptions): Wind; type TreeSpecies = 'pine' | 'oak' | 'cypress' | 'birch' | 'cedar' | 'maple' | 'sakura' | 'palm' | 'willow' | 'sequoia' | 'banyan' | 'baobab' | 'acacia'; /** The season a tree wears. Currently shapes `sakura` (bloom / green / warm / bare). */ type TreeSeason = 'spring' | 'summer' | 'autumn' | 'winter'; /** Every species `createTree` can build. */ declare const TREE_SPECIES: readonly TreeSpecies[]; interface TreeOptions { seed?: number; /** Overall height in world units. Default is species-specific. */ height?: number; /** Which species. Default: a seeded pick of pine or oak (new species are opt-in). */ species?: TreeSpecies; /** @deprecated Use `species`. Kept as an alias so old calls keep working. */ style?: TreeSpecies; /** Season — currently drives `sakura` (blossom in spring, green in summer, warm in autumn, bare in winter). */ season?: TreeSeason; /** A WindField to sway the canopy in (the trunk stays planted). */ wind?: WindField; palette?: Palette; } /** * A seeded low-poly tree. Nine species — `pine` and `oak` (the originals), plus * `cypress` (a tall narrow flame), `birch` (slender, pale, banded), `cedar` * (broad flat tiers), `maple` (a full rounded dome), `sakura` (a blossom * umbrella), `palm` (a curved stem with drooping fronds) and `willow` (a veil of * swaying strands) — each with its own silhouette, colour, wind response and * steering footprint. Same seed → identical tree, forever. * * New species are opt-in via `species`; with none given, a forest stays the * familiar pine/oak mix, so existing scenes are untouched. `season` dresses a * `sakura` — pink in spring, green in summer, warm in autumn, bare in winter. * * ```ts * const cypress = createTree({ species: 'cypress', seed: 7 }); * const bloom = createTree({ species: 'sakura', season: 'spring' }); * const palm = createTree({ species: 'palm', seed: 3 }); * ``` */ declare function createTree(options?: TreeOptions): Prop; type TreeBiome = 'temperate' | 'boreal' | 'mediterranean' | 'tropical' | 'savanna' | 'redwood' | 'grove' | 'wetland'; /** The species mix (and relative frequency) each biome scatters. */ declare const TREE_BIOMES: Record>; interface TreeBiomeOptions { palette?: Palette; season?: TreeSeason; /** Visual variants generated per species. Default 4. */ variants?: number; } /** * The species mix for a biome, ready to drop into `scatter({ items })` — so a * whole wood takes on a character in one word. A `redwood` stand towers with * sequoias over pines; a `tropical` shore is palms and the odd banyan; a * `savanna` is acacias and a baobab. Each species keeps its own silhouette, * wind response and (height-scaled) steering footprint. * * ```ts * scatter({ items: treeBiome('tropical', { palette }), area, density: 0.02 }); * ``` */ declare function treeBiome(biome: TreeBiome, options?: TreeBiomeOptions): Array<{ create: (rng: Rng) => Prop; weight: number; variants: number; }>; /** * Railway track: two rails, sleepers, ballast, and — the part that matters — * a way to ask where you are at a given distance along it. * * ```ts * const line = createTrack([a, b, c], { surface: terrain.heightAt }); * scene.add(line.object); * * const where = line.at(120); // 120 m along * carriage.position.copy(where.position); * carriage.quaternion.copy(where.rotation); * ``` * * ## `at(distance)` is the whole point * * Everything else in the trilogy steers: an agent picks a direction and the * simulation integrates it. A train does not. Its entire position is one * number — how far along — and the track turns that number into a place and a * facing. That single function is what a controller drives, what a carriage * is placed by, and what a station stop is expressed in. * * It is deliberately the ONLY thing a driver needs, so GAMA's rail controller * can take `{ length, at }` structurally and never import SCENA. Same * handshake as everywhere else in the trilogy: a shape, not a package. * * ## Arc length, not curve parameter * * `CatmullRomCurve3.getPoint(t)` walks the curve's PARAMETER, which is not * distance: on a curve with a tight bend and a long straight, equal steps in * `t` cover wildly unequal ground. A train driven on `t` would speed up and * slow down for no reason as it went round a bend, which is exactly the class * of defect `measureFootSkate` exists to catch in a walk cycle. * * So the curve is resampled into a table of equally-spaced-in-DISTANCE points * once, at build time, and `at()` interpolates that. `distanceError` reports * how far off the table is — see the note on it. */ interface TrackOptions { /** Distance between rail centres. Default 1.435 — standard gauge, in metres. */ gauge?: number; /** Ground height lookup; a number means flat ground. Default 0. */ surface?: number | ((x: number, z: number) => number); /** Metres between sleepers. Default 0.65. */ sleeperSpacing?: number; /** Close the track into a loop. Default false. */ loop?: boolean; /** Extra clearance added to scatter keep-out circles. Default 2.4. */ keepOutMargin?: number; /** * Samples per metre in the arc-length table. Default 2. * * This is a resolution/memory trade, not a quality dial for the mesh: the * rails are built from the same table, so raising it smooths tight curves * and costs vertices. `distanceError` says whether it is enough. */ samplesPerMetre?: number; /** Build the ballast shoulder. Default true. */ ballast?: boolean; palette?: Palette; } /** Where the track is, and which way it faces, at some distance along it. */ interface TrackPoint { position: Vector3; /** Unit vector along the track, pointing in the direction of travel. */ tangent: Vector3; /** A rotation that faces −Z down the track and keeps +Y up. */ rotation: Quaternion; } interface RailTrack { object: Group; /** Total length in metres. `at(length)` is the far end. */ length: number; gauge: number; loop: boolean; /** * Position and facing at `distance` metres along the track. * * Past the ends it CLAMPS rather than extrapolating (or wraps, on a loop) — * a train that overruns should stop at the buffers, not fly off down the * tangent into the scenery. * * Pass `out` to avoid allocating; the same object is returned. */ at(distance: number, out?: TrackPoint): TrackPoint; /** The centreline, for scatter keep-out or a camera dolly. */ route: Vector3[]; keepOut: Array<{ center: { x: number; z: number; }; radius: number; }>; /** * Worst gap between the arc-length table's spacing and its nominal step, * as a fraction. Near zero means `at()` is honest about distance. * * Reported rather than asserted, because the honest value depends on how * sharply the caller's own control points turn. A track laid with a 5 m * radius curve cannot be resampled evenly at 0.5 m steps, and the number * says so instead of the library pretending otherwise. */ distanceError: number; dispose(): void; } /** * Lay track along a polyline. * * Four draw calls whatever the length: two rails, one instanced sleeper mesh, * one ballast ribbon. A kilometre of track at 0.65 m spacing is 1,538 * sleepers, and one mesh each would be 1,538 draw calls — which is the whole * reason `npm run geometry` counts them. */ declare function createTrack(points: Array, options?: TrackOptions): RailTrack; export { type RailTrack as R, type SwayOptions as S, TREE_BIOMES as T, type Wind as W, TREE_SPECIES as a, type TrackOptions as b, type TrackPoint as c, type TreeBiome as d, type TreeBiomeOptions as e, type TreeOptions as f, type TreeSeason as g, type TreeSpecies as h, type WindField as i, type WindFieldOptions as j, type WindOptions as k, applyWind as l, createTrack as m, createTree as n, createWindField as o, treeBiome as t };