scena3d 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,288 @@
1
+ import { Vector3, Group, Mesh, DirectionalLight, AmbientLight, HemisphereLight, Scene } from 'three';
2
+
3
+ /**
4
+ * Deterministic seeded randomness — the backbone of SCENA. Same seed,
5
+ * same tree; forests are reproducible, diffable and network-syncable.
6
+ */
7
+ declare class Rng {
8
+ private state;
9
+ constructor(seed?: number);
10
+ /** Next float in [0, 1) (mulberry32). */
11
+ next(): number;
12
+ /** Float in [min, max). */
13
+ range(min: number, max: number): number;
14
+ /** Integer in [min, max] inclusive. */
15
+ int(min: number, max: number): number;
16
+ /** Random element of a non-empty array. */
17
+ pick<T>(items: readonly T[]): T;
18
+ /** value ± spread (uniform). */
19
+ jitter(value: number, spread: number): number;
20
+ /** A new independent Rng derived from this one. */
21
+ fork(): Rng;
22
+ }
23
+ /** Integer-lattice hash to [0, 1) — the base of the value noise. */
24
+ declare function hash2(ix: number, iz: number, seed: number): number;
25
+ /** 2D value noise in [0, 1). Continuous; used by terrain and scatter density. */
26
+ declare function valueNoise2(x: number, z: number, seed: number): number;
27
+ /** Fractal (octaved) value noise in [0, 1). */
28
+ declare function fractalNoise2(x: number, z: number, seed: number, octaves?: number, lacunarity?: number, gain?: number): number;
29
+
30
+ /**
31
+ * Theme palettes: one coherent set of colors shared by every generator,
32
+ * so procedural props look like a matched set rather than a junk drawer.
33
+ * Pass `palette` to any generator to restyle it; whole scenes retheme by
34
+ * building with a different palette.
35
+ */
36
+ interface Palette {
37
+ foliage: number[];
38
+ trunk: number;
39
+ rock: number[];
40
+ wood: number;
41
+ woodDark: number;
42
+ metal: number;
43
+ lampGlow: number;
44
+ grassLow: number;
45
+ grassHigh: number;
46
+ cliff: number;
47
+ peak: number;
48
+ skyTop: number;
49
+ skyBottom: number;
50
+ fog: number;
51
+ }
52
+ declare const PALETTES: Record<'meadow' | 'autumn' | 'dusk', Palette>;
53
+ declare const DEFAULT_PALETTE: Palette;
54
+
55
+ /**
56
+ * A steering obstacle in world space — structurally identical to GAMA's
57
+ * `Obstacle`, so SCENA props plug straight into `ObstacleAvoidance`
58
+ * without either library importing the other.
59
+ */
60
+ interface Obstacle {
61
+ center: Vector3;
62
+ radius: number;
63
+ }
64
+ /** What a prop generator returns: the visual plus gameplay metadata. */
65
+ interface Prop {
66
+ object: Group;
67
+ /**
68
+ * Footprint radius for steering/placement, in the prop's local space
69
+ * (centered at its origin). 0 means walk-through (e.g. grass).
70
+ */
71
+ obstacleRadius: number;
72
+ }
73
+ /** Collect world-space obstacles from placed props (call after positioning). */
74
+ declare function collectObstacles(props: Iterable<Prop>): Obstacle[];
75
+
76
+ interface TreeOptions {
77
+ seed?: number;
78
+ /** Overall height in world units. Default ~3.5–5 by seed. */
79
+ height?: number;
80
+ style?: 'pine' | 'oak';
81
+ palette?: Palette;
82
+ }
83
+ /**
84
+ * A seeded low-poly tree. `pine` stacks cones; `oak` clusters foliage
85
+ * blobs on a forked trunk. Same seed → identical tree, forever.
86
+ */
87
+ declare function createTree(options?: TreeOptions): Prop;
88
+
89
+ interface RockOptions {
90
+ seed?: number;
91
+ /** Approximate radius. Default ~0.4–1.1 by seed. */
92
+ size?: number;
93
+ palette?: Palette;
94
+ }
95
+ /**
96
+ * A seeded low-poly boulder: an icosahedron with jittered vertices and a
97
+ * flattened underside so it sits on the ground.
98
+ */
99
+ declare function createRock(options?: RockOptions): Prop;
100
+
101
+ interface CrateOptions {
102
+ seed?: number;
103
+ size?: number;
104
+ /** 0–1 color wear. Default 0.3. */
105
+ weathering?: number;
106
+ palette?: Palette;
107
+ }
108
+ /** A wooden crate: panel box with darker edge framing, seeded wear tint. */
109
+ declare function createCrate(options?: CrateOptions): Prop;
110
+
111
+ interface FenceOptions {
112
+ seed?: number;
113
+ /** Run length along local +x. Default 6. */
114
+ length?: number;
115
+ postSpacing?: number;
116
+ height?: number;
117
+ palette?: Palette;
118
+ }
119
+ /**
120
+ * A rustic fence run along local +x, centered at the origin: posts with
121
+ * two slightly-crooked rails. Chain several and rotate to enclose areas.
122
+ * The obstacle radius covers the whole run (rough but steering-safe);
123
+ * for tight navigation, bake a navmesh — the geometry is authoritative.
124
+ */
125
+ declare function createFence(options?: FenceOptions): Prop;
126
+
127
+ interface LampOptions {
128
+ seed?: number;
129
+ height?: number;
130
+ /** Add a real PointLight. Off by default — lights are a budget. */
131
+ light?: boolean;
132
+ lightIntensity?: number;
133
+ palette?: Palette;
134
+ }
135
+ /** A street lamp: post, head, glowing bulb, optional real PointLight. */
136
+ declare function createLamp(options?: LampOptions): Prop;
137
+
138
+ interface TerrainOptions {
139
+ seed?: number;
140
+ /** Square side length. Default 80. */
141
+ size?: number;
142
+ /** Vertices per side. Default 96. */
143
+ resolution?: number;
144
+ /** Peak height. Default 6. */
145
+ amplitude?: number;
146
+ /** Noise feature size in world units. Default 28. */
147
+ noiseScale?: number;
148
+ octaves?: number;
149
+ /** Flatten low areas into meadows (0–1, higher = flatter valleys). Default 0.55. */
150
+ valleyFlatness?: number;
151
+ palette?: Palette;
152
+ }
153
+ interface Terrain {
154
+ mesh: Mesh;
155
+ /** Exact analytic height at any (x, z) — same function that built the
156
+ * mesh, so agents/navmesh queries never disagree with the visuals. */
157
+ heightAt(x: number, z: number): number;
158
+ size: number;
159
+ seed: number;
160
+ }
161
+ /**
162
+ * A seeded low-poly terrain: fractal value noise displacing a plane, with
163
+ * height/slope-banded vertex colors (grass → high grass → cliff → peak).
164
+ * The height function is exported, not just baked into vertices — that's
165
+ * what lets gameplay (spawning, scattering, navmesh baking, agent ground
166
+ * clamping) agree exactly with what's rendered.
167
+ */
168
+ declare function createTerrain(options?: TerrainOptions): Terrain;
169
+
170
+ interface SkyOptions {
171
+ topColor?: number;
172
+ bottomColor?: number;
173
+ radius?: number;
174
+ palette?: Palette;
175
+ }
176
+ interface Sky {
177
+ mesh: Mesh;
178
+ setColors(top: number, bottom: number): void;
179
+ }
180
+ /**
181
+ * A gradient sky dome (vertical color blend on an inverted sphere).
182
+ * Colors default to the palette, so themed scenes get matching skies.
183
+ */
184
+ declare function createSky(options?: SkyOptions): Sky;
185
+
186
+ type LightingPreset = 'day' | 'golden-hour' | 'overcast' | 'night';
187
+ interface LightingRig {
188
+ group: Group;
189
+ sun: DirectionalLight;
190
+ ambient: AmbientLight;
191
+ hemisphere: HemisphereLight;
192
+ }
193
+ /**
194
+ * The three lights every scene rebuilds, as a preset: warm directional
195
+ * "sun" (position doubles as light direction), ambient fill, and a
196
+ * hemisphere tint. Retune any of them via the returned rig.
197
+ */
198
+ declare function createLightingRig(preset?: LightingPreset): LightingRig;
199
+ type FogPreset = 'clear' | 'haze' | 'thick' | 'eerie';
200
+ /** Distance fog matched to the palette's fog color. 'clear' removes it. */
201
+ declare function applyFog(scene: Scene, preset: FogPreset, palette?: Palette): void;
202
+
203
+ interface ScatterItem {
204
+ /** Prop factory, called once per visual variant with a seeded Rng. */
205
+ create(rng: Rng): Prop;
206
+ /** Relative frequency among items. Default 1. */
207
+ weight?: number;
208
+ /** Distinct variants generated per item (visual variety). Default 4. */
209
+ variants?: number;
210
+ /** Per-instance uniform scale range. Default [0.8, 1.25]. */
211
+ scale?: [number, number];
212
+ }
213
+ interface ScatterOptions {
214
+ seed?: number;
215
+ items: ScatterItem[];
216
+ /** Placement region in the XZ plane. */
217
+ area: {
218
+ min: {
219
+ x: number;
220
+ z: number;
221
+ };
222
+ max: {
223
+ x: number;
224
+ z: number;
225
+ };
226
+ };
227
+ /** Ground height lookup; a number means flat ground. Default 0. */
228
+ surface?: number | ((x: number, z: number) => number);
229
+ /** Expected instances per square unit (thinned by density noise). */
230
+ density?: number;
231
+ /** Or an exact target count (before masks/spacing rejections). */
232
+ count?: number;
233
+ /** Veto function: return false to reject a candidate point. */
234
+ mask?: (x: number, z: number, y: number) => boolean;
235
+ /** Minimum distance between any two placements. Default 1.2. */
236
+ minSpacing?: number;
237
+ /** World-space circles to keep clear (paths, spawns, buildings). */
238
+ keepOut?: Obstacle[] | {
239
+ center: {
240
+ x: number;
241
+ z: number;
242
+ };
243
+ radius: number;
244
+ }[];
245
+ /** Density-noise feature size in world units. Default 18. */
246
+ clumpScale?: number;
247
+ }
248
+ interface Placement {
249
+ position: Vector3;
250
+ rotationY: number;
251
+ scale: number;
252
+ itemIndex: number;
253
+ }
254
+ interface ScatterResult {
255
+ /** One InstancedMesh per template part — a handful of draw calls total. */
256
+ group: Group;
257
+ placements: Placement[];
258
+ /** World-space steering obstacles for everything with a footprint. */
259
+ obstacles: Obstacle[];
260
+ count: number;
261
+ }
262
+ /**
263
+ * Populate an area with seeded, instanced props: "empty plane → forest"
264
+ * in one call. Placement uses density noise for natural clumping, a
265
+ * spatial hash for minimum spacing, and masks/keep-out circles for
266
+ * exclusion. Rendering merges every placement into InstancedMeshes — a
267
+ * few draw calls for thousands of props.
268
+ *
269
+ * ```ts
270
+ * const forest = scatter({
271
+ * seed: 7,
272
+ * area: { min: { x: -40, z: -40 }, max: { x: 40, z: 40 } },
273
+ * surface: terrain.heightAt,
274
+ * density: 0.04,
275
+ * items: [
276
+ * { create: (rng) => createTree({ seed: rng.int(1, 1e9) }), weight: 3 },
277
+ * { create: (rng) => createRock({ seed: rng.int(1, 1e9) }) },
278
+ * ],
279
+ * mask: (x, z, y) => y > 0.5 && y < 5, // between shore and peaks
280
+ * keepOut: [{ center: village, radius: 12 }],
281
+ * });
282
+ * scene.add(forest.group);
283
+ * agent.addBehavior(new ObstacleAvoidance(() => forest.obstacles));
284
+ * ```
285
+ */
286
+ declare function scatter(options: ScatterOptions): ScatterResult;
287
+
288
+ export { type CrateOptions, DEFAULT_PALETTE, type FenceOptions, type FogPreset, type LampOptions, type LightingPreset, type LightingRig, type Obstacle, PALETTES, type Palette, type Placement, type Prop, Rng, type RockOptions, type ScatterItem, type ScatterOptions, type ScatterResult, type Sky, type SkyOptions, type Terrain, type TerrainOptions, type TreeOptions, applyFog, collectObstacles, createCrate, createFence, createLamp, createLightingRig, createRock, createSky, createTerrain, createTree, fractalNoise2, hash2, scatter, valueNoise2 };