@threenative/terrain 0.0.0-stage → 0.1.2

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.
Files changed (125) hide show
  1. package/AGENT_GUIDE.md +194 -0
  2. package/LICENSE +21 -0
  3. package/README.md +71 -2
  4. package/THIRD_PARTY_NOTICES.md +38 -0
  5. package/dist/editor/index.d.ts +231 -0
  6. package/dist/editor/index.js +5624 -0
  7. package/dist/editor/server.d.ts +3 -0
  8. package/dist/editor/server.js +3688 -0
  9. package/dist/editor/worker.d.ts +2 -0
  10. package/dist/editor/worker.js +2212 -0
  11. package/dist/export.d.ts +54 -0
  12. package/dist/export.js +297 -0
  13. package/dist/index.d.ts +335 -0
  14. package/dist/index.js +2927 -0
  15. package/dist/server-ByM2hmYJ.d.ts +630 -0
  16. package/dist/terrain-gPnkCU6S.d.ts +141 -0
  17. package/dist/three.d.ts +14 -0
  18. package/dist/three.js +40 -0
  19. package/dist/types-Ant8-Axc.d.ts +486 -0
  20. package/package.json +86 -3
  21. package/starter/alpine/README.md +29 -0
  22. package/starter/alpine/assets.json +12 -0
  23. package/starter/alpine/bake.mjs +118 -0
  24. package/starter/alpine/recipe.json +66167 -0
  25. package/starter/alpine/sky.ts +23 -0
  26. package/starter/alpine/surface.json +53 -0
  27. package/starter/alpine/world.ts +335 -0
  28. package/starter/coastal/README.md +37 -0
  29. package/starter/coastal/assets.json +10 -0
  30. package/starter/coastal/bake.mjs +118 -0
  31. package/starter/coastal/recipe.json +66161 -0
  32. package/starter/coastal/sea.ts +327 -0
  33. package/starter/coastal/sky.ts +24 -0
  34. package/starter/coastal/surface.json +41 -0
  35. package/starter/coastal/world.ts +386 -0
  36. package/starter/desert/README.md +35 -0
  37. package/starter/desert/assets.json +12 -0
  38. package/starter/desert/bake.mjs +118 -0
  39. package/starter/desert/recipe.json +66156 -0
  40. package/starter/desert/sky.ts +23 -0
  41. package/starter/desert/surface.json +54 -0
  42. package/starter/desert/world.ts +335 -0
  43. package/starter/forest/README.md +46 -0
  44. package/starter/forest/assets.json +32 -0
  45. package/starter/forest/bake.mjs +118 -0
  46. package/starter/forest/recipe.json +4586 -0
  47. package/starter/forest/sky.ts +27 -0
  48. package/starter/forest/surface.json +79 -0
  49. package/starter/forest/water.ts +266 -0
  50. package/starter/forest/world.ts +413 -0
  51. package/starter/tundra/README.md +29 -0
  52. package/starter/tundra/assets.json +12 -0
  53. package/starter/tundra/bake.mjs +118 -0
  54. package/starter/tundra/recipe.json +66325 -0
  55. package/starter/tundra/sky.ts +27 -0
  56. package/starter/tundra/surface.json +54 -0
  57. package/starter/tundra/water.ts +313 -0
  58. package/starter/tundra/world.ts +366 -0
  59. package/starter-assets/bark_brown_02/bark_brown_02_diff_1k.jpg +0 -0
  60. package/starter-assets/bark_brown_02/bark_brown_02_nor_gl_1k.jpg +0 -0
  61. package/starter-assets/bark_brown_02/bark_brown_02_rough_1k.jpg +0 -0
  62. package/starter-assets/boulder_01/boulder-mid.glb +0 -0
  63. package/starter-assets/boulder_01/boulder-near.glb +0 -0
  64. package/starter-assets/cliff_side/cliff_side_ao_1k.jpg +0 -0
  65. package/starter-assets/cliff_side/cliff_side_diff_1k.jpg +0 -0
  66. package/starter-assets/cliff_side/cliff_side_nor_gl_1k.jpg +0 -0
  67. package/starter-assets/cliff_side/cliff_side_rough_1k.jpg +0 -0
  68. package/starter-assets/credits.json +1022 -0
  69. package/starter-assets/fern_02/fern_02_alpha_512.png +0 -0
  70. package/starter-assets/fern_02/fern_02_diff_512.jpg +0 -0
  71. package/starter-assets/fern_02/fern_02_nor_gl_512.jpg +0 -0
  72. package/starter-assets/fir_tree_01/fir-b-lod1.glb +0 -0
  73. package/starter-assets/fir_tree_01/fir-b-lod2.glb +0 -0
  74. package/starter-assets/fir_tree_01/fir-c-lod1.glb +0 -0
  75. package/starter-assets/fir_tree_01/fir-c-lod2.glb +0 -0
  76. package/starter-assets/fir_tree_01/fir_tree_01_twig_arm_1k.jpg +0 -0
  77. package/starter-assets/fir_tree_01/fir_tree_01_twig_diff_1k.jpg +0 -0
  78. package/starter-assets/fir_tree_01/fir_tree_01_twig_nor_gl_1k.jpg +0 -0
  79. package/starter-assets/forest_ground_04/forest_ground_04_ao_1k.jpg +0 -0
  80. package/starter-assets/forest_ground_04/forest_ground_04_diff_1k.jpg +0 -0
  81. package/starter-assets/forest_ground_04/forest_ground_04_nor_gl_1k.jpg +0 -0
  82. package/starter-assets/forest_ground_04/forest_ground_04_rough_1k.jpg +0 -0
  83. package/starter-assets/forest_leaves_02/forest_leaves_02_arm_512.jpg +0 -0
  84. package/starter-assets/forest_leaves_02/forest_leaves_02_diff_512.jpg +0 -0
  85. package/starter-assets/forest_leaves_02/forest_leaves_02_nor_gl_512.jpg +0 -0
  86. package/starter-assets/grass_medium_02/grass_medium_02_alpha_512.png +0 -0
  87. package/starter-assets/grass_medium_02/grass_medium_02_diff_512.jpg +0 -0
  88. package/starter-assets/grass_medium_02/grass_medium_02_nor_gl_512.jpg +0 -0
  89. package/starter-assets/hdri/kloofendal_48d_partly_cloudy_1k.hdr +0 -0
  90. package/starter-assets/leafy_grass/leafy_grass_ao_1k.jpg +0 -0
  91. package/starter-assets/leafy_grass/leafy_grass_diff_1k.jpg +0 -0
  92. package/starter-assets/leafy_grass/leafy_grass_nor_gl_1k.jpg +0 -0
  93. package/starter-assets/leafy_grass/leafy_grass_rough_1k.jpg +0 -0
  94. package/starter-assets/lichen_rock/lichen_rock_arm_512.jpg +0 -0
  95. package/starter-assets/lichen_rock/lichen_rock_diff_512.jpg +0 -0
  96. package/starter-assets/lichen_rock/lichen_rock_nor_gl_512.jpg +0 -0
  97. package/starter-assets/mossy_rock/mossy_rock_ao_1k.jpg +0 -0
  98. package/starter-assets/mossy_rock/mossy_rock_diff_1k.jpg +0 -0
  99. package/starter-assets/mossy_rock/mossy_rock_nor_gl_1k.jpg +0 -0
  100. package/starter-assets/mossy_rock/mossy_rock_rough_1k.jpg +0 -0
  101. package/starter-assets/needle-atlas.png +0 -0
  102. package/starter-assets/needle-surface.png +0 -0
  103. package/starter-assets/river_small_rocks/river_small_rocks_arm_512.jpg +0 -0
  104. package/starter-assets/river_small_rocks/river_small_rocks_diff_512.jpg +0 -0
  105. package/starter-assets/river_small_rocks/river_small_rocks_nor_gl_512.jpg +0 -0
  106. package/starter-assets/rock_boulder_dry/rock_boulder_dry_arm_512.jpg +0 -0
  107. package/starter-assets/rock_boulder_dry/rock_boulder_dry_diff_512.jpg +0 -0
  108. package/starter-assets/rock_boulder_dry/rock_boulder_dry_nor_gl_512.jpg +0 -0
  109. package/starter-assets/rocks/boulder-mid.glb +0 -0
  110. package/starter-assets/rocks/boulder-near.glb +0 -0
  111. package/starter-assets/rocks/boulder-prep.json +37 -0
  112. package/starter-assets/rocks/rock01-mid.glb +0 -0
  113. package/starter-assets/rocks/rock01-near.glb +0 -0
  114. package/starter-assets/rocks/rock01-prep.json +37 -0
  115. package/starter-assets/rocks/rock04-mid.glb +0 -0
  116. package/starter-assets/rocks/rock04-near.glb +0 -0
  117. package/starter-assets/rocks/rock04-prep.json +37 -0
  118. package/starter-assets/sand_01/sand_01_ao_1k.jpg +0 -0
  119. package/starter-assets/sand_01/sand_01_diff_1k.jpg +0 -0
  120. package/starter-assets/sand_01/sand_01_nor_gl_1k.jpg +0 -0
  121. package/starter-assets/sand_01/sand_01_rough_1k.jpg +0 -0
  122. package/starter-assets/snow_02/snow_02_ao_1k.jpg +0 -0
  123. package/starter-assets/snow_02/snow_02_diff_1k.jpg +0 -0
  124. package/starter-assets/snow_02/snow_02_nor_gl_1k.jpg +0 -0
  125. package/starter-assets/snow_02/snow_02_rough_1k.jpg +0 -0
@@ -0,0 +1,141 @@
1
+ import { L as Layer, O as OperationType, a as IMask, b as LayerParams, c as ITerrainConfig, d as ITerrainDocument, e as IEvaluateOptions, I as ITerrainState, f as ICopyRegionOptions } from './types-Ant8-Axc.js';
2
+
3
+ type OperationOptions<T extends OperationType> = LayerParams[T] & {
4
+ id?: string;
5
+ label?: string;
6
+ enabled?: boolean;
7
+ opacity?: number;
8
+ mask?: IMask;
9
+ };
10
+ type LayerPatch = {
11
+ type?: OperationType;
12
+ name?: string;
13
+ enabled?: boolean;
14
+ opacity?: number;
15
+ mask?: IMask | null;
16
+ params?: Partial<LayerParams[OperationType]>;
17
+ };
18
+ type PatchCommand = {
19
+ op: "upsert";
20
+ layer: Layer;
21
+ } | {
22
+ op: "update";
23
+ id: string;
24
+ patch: LayerPatch;
25
+ } | {
26
+ op: "remove";
27
+ id: string;
28
+ } | {
29
+ op: "move";
30
+ id: string;
31
+ index: number;
32
+ } | {
33
+ op: "toggle";
34
+ id: string;
35
+ enabled?: boolean;
36
+ };
37
+ /**
38
+ * Recovered prefix-caching authoring evaluator. Returned buffers belong to the caller.
39
+ * @requires npm i @threenative/terrain
40
+ * @situation reuse terrain evaluation prefixes while authoring a finite heightfield
41
+ * @constraint evaluates authoring documents only; games consume pre-baked arrays
42
+ * @example const terrain = new Terrain({ resolution: 17 }); const evaluator = new TerrainEvaluator(); const state = evaluator.evaluate(terrain.toJSON());
43
+ * @override cacheMB bounds retained prefix data
44
+ */
45
+ declare class TerrainEvaluator {
46
+ private cache;
47
+ private bytes;
48
+ private limit;
49
+ constructor({ cacheMB }?: {
50
+ cacheMB?: number | undefined;
51
+ });
52
+ clear(): void;
53
+ evaluate(doc: ITerrainDocument, { resolution, signal, onProgress }?: IEvaluateOptions): ITerrainState;
54
+ }
55
+ /**
56
+ * Ordered, stable-ID terrain authoring with synchronous atomic transactions.
57
+ * @requires npm i @threenative/terrain
58
+ * @situation author seeded terrain with noise, sculpting, erosion, roads, rivers and scatter
59
+ * @situation generate a procedural heightmap landscape or island for a game
60
+ * @constraint authoring stays outside the game's steady-play graph; no renderer or physics is created
61
+ * @constraint units are metres with Y up; `size` is the world edge in metres (1 to 100000), centred on the origin
62
+ * @constraint `resolution` counts vertices per edge and must be one of 17, 33, 65, 129, 257, 513 or 1025
63
+ * @constraint `seed` is an integer from 0 to 4294967295; one document and seed evaluate to the same arrays
64
+ * @constraint `evaluate()` is synchronous on the calling thread: run it in a build script, never per frame
65
+ * @constraint no art is chosen or shipped: materials, models and texture paths belong to the game
66
+ * @example const terrain = new Terrain({ size: 512, resolution: 257, seed: 73 }).noise({ id: "hills", amplitude: 35 });
67
+ * @override size, resolution, seed and all layer parameters are caller choices
68
+ */
69
+ declare class Terrain {
70
+ private _doc;
71
+ private _undo;
72
+ private _redo;
73
+ private _listeners;
74
+ private _historyLimit;
75
+ private _transactionDepth;
76
+ private _evaluator;
77
+ constructor({ size, resolution, seed, historyLimit, }?: Partial<ITerrainConfig> & {
78
+ historyLimit?: number;
79
+ });
80
+ static fromJSON(json: string | ITerrainDocument): Terrain;
81
+ get layers(): Layer[];
82
+ get config(): ITerrainConfig;
83
+ get canUndo(): boolean;
84
+ get canRedo(): boolean;
85
+ toJSON(): ITerrainDocument;
86
+ subscribe(fn: (terrain: Terrain) => void): () => boolean;
87
+ private _emit;
88
+ private _change;
89
+ private _id;
90
+ add(layer: Layer): this;
91
+ private _operation;
92
+ layer(id: string): Layer;
93
+ update(id: string, patch: LayerPatch): this;
94
+ toggle(id: string, enabled?: boolean): this;
95
+ move(id: string, index: number): this;
96
+ remove(id: string): this;
97
+ setConfig(patch: Partial<ITerrainConfig>): this;
98
+ loadJSON(json: string | ITerrainDocument): this;
99
+ transaction(fn: (terrain: this) => unknown): this;
100
+ applyPatch(commands: readonly PatchCommand[]): this;
101
+ undo(): this;
102
+ redo(): this;
103
+ evaluate(options?: IEvaluateOptions): ITerrainState;
104
+ clearCache(): void;
105
+ copyRegion({ at, size, resolution }?: ICopyRegionOptions): {
106
+ width: number;
107
+ height: number;
108
+ values: number[];
109
+ };
110
+ inspect(options?: IEvaluateOptions): {
111
+ size: number;
112
+ resolution: number;
113
+ layers: number;
114
+ vertices: number;
115
+ triangles: number;
116
+ minHeight: number;
117
+ maxHeight: number;
118
+ instances: number;
119
+ diagnostics: string[];
120
+ };
121
+ noise(options?: OperationOptions<"noise">): this;
122
+ sculpt(options?: OperationOptions<"sculpt">): this;
123
+ smooth(options?: OperationOptions<"smooth">): this;
124
+ flatten(options?: OperationOptions<"flatten">): this;
125
+ ramp(options: OperationOptions<"ramp">): this;
126
+ stamp(options?: OperationOptions<"stamp">): this;
127
+ erode(options?: OperationOptions<"erode">): this;
128
+ terrace(options?: OperationOptions<"terrace">): this;
129
+ materials(options: OperationOptions<"materials">): this;
130
+ paint(options?: OperationOptions<"paint">): this;
131
+ biome(options?: OperationOptions<"biome">): this;
132
+ scatter(options?: OperationOptions<"scatter">): this;
133
+ clear(options: OperationOptions<"clear">): this;
134
+ road(options: OperationOptions<"road">): this;
135
+ river(options: OperationOptions<"river">): this;
136
+ water(options?: OperationOptions<"water">): this;
137
+ heightmap(options: OperationOptions<"heightmap">): this;
138
+ paste(options: OperationOptions<"paste">): this;
139
+ }
140
+
141
+ export { type LayerPatch as L, type OperationOptions as O, type PatchCommand as P, Terrain as T, TerrainEvaluator as a };
@@ -0,0 +1,14 @@
1
+ import { BufferGeometry } from 'three';
2
+ import { g as IBakedMesh } from './types-Ant8-Axc.js';
3
+
4
+ /**
5
+ * Converts baked arrays to ordinary geometry using the consumer's installed Three.js.
6
+ * @requires npm i @threenative/terrain
7
+ * @situation put an authored terrain mesh in a game-owned Three.js scene
8
+ * @constraint creates only geometry; the caller owns its material, scene and disposal
9
+ * @example const geometry = toGeometry(bakeMesh(new Terrain({ resolution: 17 }).evaluate()));
10
+ * @override every surface choice remains in game source
11
+ */
12
+ declare function toGeometry(mesh: IBakedMesh): BufferGeometry;
13
+
14
+ export { toGeometry };
package/dist/three.js ADDED
@@ -0,0 +1,40 @@
1
+ import { BufferGeometry, BufferAttribute } from 'three';
2
+
3
+ // src/three.ts
4
+
5
+ // src/core/bake.ts
6
+ function validateBakedMesh(mesh) {
7
+ if (![mesh.positions, mesh.normals, mesh.uvs, ...mesh.colors ? [mesh.colors] : []].every(
8
+ (array) => array instanceof Float32Array
9
+ ) || !(mesh.indices instanceof Uint32Array))
10
+ throw new TypeError("Baked mesh requires Float32 attributes and Uint32 indices");
11
+ const count = mesh.positions.length / 3;
12
+ if (!count || !Number.isInteger(count) || mesh.normals.length !== count * 3 || mesh.uvs.length !== count * 2 || mesh.colors && mesh.colors.length !== count * 3 || !mesh.indices.length || mesh.indices.length % 3)
13
+ throw new RangeError("Baked mesh attribute counts do not agree");
14
+ for (const array of [
15
+ mesh.positions,
16
+ mesh.normals,
17
+ mesh.uvs,
18
+ ...mesh.colors ? [mesh.colors] : []
19
+ ])
20
+ if (!array.every(Number.isFinite)) throw new RangeError("Baked mesh attributes must be finite");
21
+ if (mesh.indices.some((index) => index >= count))
22
+ throw new RangeError("Baked mesh index outside vertex range");
23
+ }
24
+
25
+ // src/three.ts
26
+ function toGeometry(mesh) {
27
+ validateBakedMesh(mesh);
28
+ const geometry = new BufferGeometry();
29
+ geometry.name = mesh.name;
30
+ geometry.setAttribute("position", new BufferAttribute(mesh.positions, 3));
31
+ geometry.setAttribute("normal", new BufferAttribute(mesh.normals, 3));
32
+ geometry.setAttribute("uv", new BufferAttribute(mesh.uvs, 2));
33
+ if (mesh.colors) geometry.setAttribute("color", new BufferAttribute(mesh.colors, 3));
34
+ geometry.setIndex(new BufferAttribute(mesh.indices, 1));
35
+ geometry.computeBoundingBox();
36
+ geometry.computeBoundingSphere();
37
+ return geometry;
38
+ }
39
+
40
+ export { toGeometry };
@@ -0,0 +1,486 @@
1
+ /**
2
+ * Shared shapes for the recovered Strata evaluator. Every type here is derived from the recovered
3
+ * `src/core/*` implementation and its `PARAMS` allow-lists, with explicit landform transform extensions. A recipe is plain
4
+ * JSON, so the evaluator validates it at runtime and these types only describe what the evaluator
5
+ * accepts.
6
+ */
7
+ /** The eight material channels a splat sample carries, in channel order. */
8
+ type MaterialId = "grass" | "dirt" | "rock" | "snow" | "sand" | "mud" | "road" | "moss";
9
+ /** Every operation type the evaluator accepts, in the recovered `OPERATION_TYPES` order. */
10
+ type OperationType = "noise" | "sculpt" | "smooth" | "flatten" | "ramp" | "stamp" | "erode" | "terrace" | "materials" | "paint" | "biome" | "scatter" | "clear" | "road" | "river" | "water" | "heightmap" | "paste";
11
+ /** The recoverable mask kinds; a mask is a serializable expression, never a callback. */
12
+ type MaskType = "all" | "none" | "circle" | "rectangle" | "height" | "slope" | "noise" | "biome" | "material" | "and" | "or" | "not";
13
+ /**
14
+ * A serializable mask expression. `type` selects the fields that mean anything; `validateMask`
15
+ * rejects every field the chosen type does not list, so this flat shape is the exact union the
16
+ * evaluator accepts rather than a loose bag.
17
+ */
18
+ interface IMask {
19
+ readonly type: MaskType;
20
+ /** `circle` and `rectangle` centre in world metres as `[x, z]`. */
21
+ readonly at?: readonly [number, number];
22
+ readonly radius?: number;
23
+ readonly size?: number | readonly [number, number];
24
+ readonly rotation?: number;
25
+ readonly falloff?: number;
26
+ /** `height` metres and `slope` degrees. */
27
+ readonly min?: number;
28
+ readonly max?: number;
29
+ readonly fade?: number;
30
+ readonly scale?: number;
31
+ readonly threshold?: number;
32
+ readonly seed?: number;
33
+ /** `biome` rule name or `material` channel. */
34
+ readonly name?: string;
35
+ readonly masks?: readonly IMask[];
36
+ readonly mask?: IMask;
37
+ }
38
+ /** A mask compiled against one evaluated state: sample index in, weight out. */
39
+ type MaskSampler = (index: number, x: number, z: number) => number;
40
+ /** A height block copied in or out of the document, in world metres, row-major. */
41
+ interface IHeightData {
42
+ readonly width: number;
43
+ readonly height: number;
44
+ /** Fluent calls accept a typed array and normalize it to a plain array before validation. */
45
+ readonly values: readonly number[] | Float32Array;
46
+ }
47
+ /** World-space grid extent; every coordinate runs from `-size / 2` to `+size / 2`, Y is up. */
48
+ interface IGrid {
49
+ readonly size: number;
50
+ readonly resolution: number;
51
+ }
52
+ /** The grid fields the samplers read. Heights are absolute world elevations in metres. */
53
+ interface ISampledGrid extends IGrid {
54
+ readonly height: Float32Array;
55
+ }
56
+ /** The subset of a world document the evaluator resolves before any operation runs. */
57
+ interface ITerrainConfig {
58
+ size: number;
59
+ resolution: number;
60
+ seed: number;
61
+ }
62
+ /** The serializable authoring document: one config plus an ordered, ID-addressable layer stack. */
63
+ interface ITerrainDocument {
64
+ version: 1;
65
+ config: ITerrainConfig;
66
+ layers: Layer[];
67
+ }
68
+ interface IOperationBase {
69
+ /** Overrides the document seed for this layer only. */
70
+ readonly seed?: number;
71
+ }
72
+ /** Stroke tools accept one `at` centre or a polyline `points`; `radius` is a radius, not a diameter. */
73
+ interface IBrushParams extends IOperationBase {
74
+ readonly at?: readonly [number, number];
75
+ readonly points?: readonly (readonly [number, number])[];
76
+ readonly radius?: number;
77
+ /** Signed metres for `sculpt`, `0..1` for smoothing, flattening and painting. */
78
+ readonly strength?: number;
79
+ readonly falloff?: number;
80
+ readonly shape?: "circle" | "square";
81
+ readonly rotation?: number;
82
+ readonly spacing?: number;
83
+ readonly jitter?: number;
84
+ }
85
+ interface INoiseParams extends IOperationBase {
86
+ readonly amplitude?: number;
87
+ readonly scale?: number;
88
+ readonly octaves?: number;
89
+ readonly persistence?: number;
90
+ readonly lacunarity?: number;
91
+ readonly mode?: "fbm" | "ridged" | "billow";
92
+ readonly warp?: number;
93
+ readonly base?: number;
94
+ readonly blend?: "add" | "replace";
95
+ readonly island?: boolean;
96
+ readonly coastDepth?: number;
97
+ }
98
+ interface ISculptParams extends IBrushParams {
99
+ }
100
+ interface ISmoothParams extends IBrushParams {
101
+ readonly iterations?: number;
102
+ }
103
+ interface IFlattenParams extends IBrushParams {
104
+ /** Absolute metres; omitted or null levels the pad to the ground already under it. */
105
+ readonly height?: number | null;
106
+ }
107
+ interface IPaintParams extends IBrushParams {
108
+ readonly material?: MaterialId;
109
+ }
110
+ interface IStampParams extends IOperationBase {
111
+ readonly at?: readonly [number, number];
112
+ /** One radius, or `[x, z]` half-extents. */
113
+ readonly radius?: number | readonly [number, number];
114
+ readonly size?: number | readonly [number, number];
115
+ readonly amplitude?: number;
116
+ readonly shape?: "mountain" | "crater" | "ridge" | "valley" | "mesa" | "dune";
117
+ readonly rotation?: number;
118
+ readonly mirrorX?: boolean;
119
+ readonly mirrorZ?: boolean;
120
+ readonly blend?: "add" | "replace" | "min" | "max";
121
+ readonly roughness?: number;
122
+ /** Positive vertical gain, applied before offset; defaults to 1. */
123
+ readonly scale?: number;
124
+ /** Metres added to the scaled landform, including additive blends. */
125
+ readonly offset?: number;
126
+ readonly data?: IHeightData;
127
+ readonly falloff?: number;
128
+ }
129
+ interface IPasteParams extends Omit<IStampParams, "shape" | "roughness" | "amplitude"> {
130
+ }
131
+ interface IErodeParams extends IBrushParams {
132
+ readonly method?: "thermal" | "hydraulic";
133
+ readonly iterations?: number;
134
+ /** Maximum stable slope in degrees. */
135
+ readonly talus?: number;
136
+ readonly rate?: number;
137
+ readonly droplets?: number;
138
+ readonly maxSteps?: number;
139
+ readonly inertia?: number;
140
+ readonly capacity?: number;
141
+ readonly erosion?: number;
142
+ readonly deposition?: number;
143
+ readonly evaporation?: number;
144
+ }
145
+ interface ITerraceParams extends IOperationBase {
146
+ readonly step?: number;
147
+ readonly softness?: number;
148
+ readonly strength?: number;
149
+ readonly offset?: number;
150
+ }
151
+ /** One ordered surface rule; a later rule can override an earlier one. */
152
+ interface IMaterialRule {
153
+ readonly material: MaterialId;
154
+ readonly mask?: IMask;
155
+ readonly strength?: number;
156
+ }
157
+ interface IMaterialsParams extends IOperationBase {
158
+ readonly base?: MaterialId;
159
+ readonly rules: IMaterialRule[];
160
+ readonly reset?: boolean;
161
+ }
162
+ interface IBiomeParams extends IBrushParams {
163
+ /** The biome key the mask's `biome` type reads, not a display name. */
164
+ readonly name?: string;
165
+ readonly value?: number;
166
+ }
167
+ interface IScatterParams extends IOperationBase {
168
+ readonly asset?: string;
169
+ readonly count?: number;
170
+ /** Guaranteed within one rule, never across rules. */
171
+ readonly minDistance?: number;
172
+ readonly scale?: number | readonly [number, number];
173
+ readonly minHeight?: number;
174
+ readonly maxHeight?: number;
175
+ readonly minSlope?: number;
176
+ readonly maxSlope?: number;
177
+ readonly avoidWater?: boolean;
178
+ readonly offsetY?: number;
179
+ readonly alignToNormal?: boolean;
180
+ }
181
+ interface IClearParams extends IOperationBase {
182
+ readonly target: "scatter" | "paint" | "biome" | "all";
183
+ readonly asset?: string;
184
+ readonly name?: string;
185
+ }
186
+ /** A spline control point; a null elevation means the spline reads the ground it crosses there. */
187
+ type SplinePoint = readonly [number, number | null, number];
188
+ /**
189
+ * Grading shared by roads, rivers and ramps. A following spline takes its elevation from the
190
+ * terrain it crosses — low-passed along arc length, then held inside the grade and cut/fill limits
191
+ * — so a corridor is a bench cut into a hillside rather than a causeway pinned above it.
192
+ */
193
+ interface IGradeParams {
194
+ /** Defaults to true when a control point omits its elevation; absolute points keep their meaning. */
195
+ readonly followTerrain?: boolean;
196
+ /** Maximum rise per metre travelled along the centreline; default 0.12. */
197
+ readonly maxGrade?: number;
198
+ /** Maximum metres the centreline may cut below the ground it crosses; default 2.5. */
199
+ readonly maxCut?: number;
200
+ /** Maximum metres it may fill above it; default 1.5. */
201
+ readonly maxFill?: number;
202
+ }
203
+ interface IRoadParams extends IOperationBase, IGradeParams {
204
+ readonly points: readonly SplinePoint[];
205
+ readonly width?: number;
206
+ readonly shoulder?: number;
207
+ readonly material?: MaterialId;
208
+ readonly smooth?: boolean;
209
+ }
210
+ interface IRiverParams extends IRoadParams {
211
+ readonly depth?: number;
212
+ readonly water?: boolean;
213
+ readonly waterWidth?: number;
214
+ readonly color?: string | number;
215
+ /** Validates non-increasing control-point Y instead of rewriting the profile. */
216
+ readonly enforceDownhill?: boolean;
217
+ }
218
+ interface IRampParams extends IOperationBase, IGradeParams {
219
+ readonly from: SplinePoint;
220
+ readonly to: SplinePoint;
221
+ readonly width?: number;
222
+ readonly shoulder?: number;
223
+ readonly material?: MaterialId;
224
+ }
225
+ interface IWaterParams extends IOperationBase {
226
+ readonly kind?: "lake" | "ocean";
227
+ readonly at?: readonly [number, number];
228
+ readonly radius?: number;
229
+ readonly level?: number;
230
+ readonly color?: string | number;
231
+ }
232
+ interface IHeightmapParams extends IOperationBase {
233
+ readonly data: IHeightData;
234
+ /** Optional footprint centre in metres; without footprint fields the map fills the world. */
235
+ readonly at?: readonly [number, number];
236
+ /** Full footprint width/depth in metres; defaults to the world size. */
237
+ readonly size?: number | readonly [number, number];
238
+ /** Degrees in the terrain X/Z plane, matching stamp/paste. */
239
+ readonly rotation?: number;
240
+ readonly falloff?: number;
241
+ /** Positive vertical gain, applied before offset. */
242
+ readonly scale?: number;
243
+ readonly offset?: number;
244
+ readonly blend?: "add" | "replace";
245
+ }
246
+ /** One recipe layer: a stable semantic `id`, its operation type, and that type's parameters. */
247
+ interface ILayer<T extends OperationType = OperationType> {
248
+ readonly id: string;
249
+ readonly type: T;
250
+ readonly params: LayerParams[T];
251
+ /** Human-readable name; the fluent calls spell this `label`. */
252
+ readonly name?: string;
253
+ readonly enabled?: boolean;
254
+ readonly opacity?: number;
255
+ readonly mask?: IMask;
256
+ }
257
+ /** Parameters accepted per operation type, exactly the recovered `PARAMS` allow-lists. */
258
+ interface LayerParams {
259
+ noise: INoiseParams;
260
+ sculpt: ISculptParams;
261
+ smooth: ISmoothParams;
262
+ flatten: IFlattenParams;
263
+ ramp: IRampParams;
264
+ stamp: IStampParams;
265
+ erode: IErodeParams;
266
+ terrace: ITerraceParams;
267
+ materials: IMaterialsParams;
268
+ paint: IPaintParams;
269
+ biome: IBiomeParams;
270
+ scatter: IScatterParams;
271
+ clear: IClearParams;
272
+ road: IRoadParams;
273
+ river: IRiverParams;
274
+ water: IWaterParams;
275
+ heightmap: IHeightmapParams;
276
+ paste: IPasteParams;
277
+ }
278
+ /** The layer union, discriminated by `type`, so `params` narrows with the operation. */
279
+ type Layer = ILayer<"noise"> | ILayer<"sculpt"> | ILayer<"smooth"> | ILayer<"flatten"> | ILayer<"ramp"> | ILayer<"stamp"> | ILayer<"erode"> | ILayer<"terrace"> | ILayer<"materials"> | ILayer<"paint"> | ILayer<"biome"> | ILayer<"scatter"> | ILayer<"clear"> | ILayer<"road"> | ILayer<"river"> | ILayer<"water"> | ILayer<"heightmap"> | ILayer<"paste">;
280
+ /** A scatter rule deferred until the final heights exist: masks re-evaluate against them. */
281
+ interface IScatterRule extends IScatterParams {
282
+ readonly id: string;
283
+ readonly mask?: IMask;
284
+ readonly opacity: number;
285
+ readonly clear: IScatterClear[];
286
+ }
287
+ interface IScatterClear {
288
+ readonly mask: IMask;
289
+ readonly opacity: number;
290
+ }
291
+ interface IWaterRule extends IWaterParams {
292
+ readonly id: string;
293
+ readonly mask?: IMask;
294
+ readonly opacity: number;
295
+ }
296
+ /** A flooded water body: the resolved rule, with its authoring mask replaced by the sample mask. */
297
+ interface IWaterBody extends Omit<IWaterRule, "mask"> {
298
+ readonly level: number;
299
+ readonly mask: Uint8Array;
300
+ }
301
+ /** A river ribbon for rendering; authoring extents, not a flow simulation. */
302
+ interface IRiver {
303
+ readonly id: string;
304
+ readonly points: readonly (readonly [number, number, number])[];
305
+ readonly width: number;
306
+ readonly color?: string | number;
307
+ readonly opacity: number;
308
+ }
309
+ /** One accepted scatter candidate. `rotation` is yaw in radians. */
310
+ interface IPlacement {
311
+ /** Layer/seed/candidate key, independent of acceptance order; changing the seed changes identity. */
312
+ readonly id: string;
313
+ readonly layer: string;
314
+ readonly asset: string;
315
+ readonly position: [number, number, number];
316
+ readonly rotation: number;
317
+ readonly scale: number;
318
+ readonly normal: [number, number, number];
319
+ readonly alignToNormal: boolean;
320
+ /** Authored transform; absent means the original scatter pose, with grounding enabled. */
321
+ readonly transform?: IPlacementOverride;
322
+ }
323
+ /** A manual edit of one stable scatter candidate; model bounds remain game-owned. */
324
+ interface IPlacementOverride {
325
+ readonly position: [number, number, number];
326
+ /** Unit quaternion in Three.js x/y/z/w order. */
327
+ readonly quaternion: [number, number, number, number];
328
+ readonly scale: [number, number, number];
329
+ /** False retains requested Y; clearance is still measured by the game. */
330
+ readonly grounding: boolean;
331
+ }
332
+ /** The disposable result of one evaluation: arrays the caller owns, plus its diagnostics. */
333
+ interface ITerrainState {
334
+ size: number;
335
+ resolution: number;
336
+ seed: number;
337
+ height: Float32Array;
338
+ /** Sample-major: eight channels per sample, weights summing to one. */
339
+ splat: Float32Array;
340
+ biomes: Record<string, Float32Array>;
341
+ scatterRules: IScatterRule[];
342
+ waterRules: IWaterRule[];
343
+ rivers: IRiver[];
344
+ instances: IPlacement[];
345
+ waters: IWaterBody[];
346
+ diagnostics: string[];
347
+ /** Measured transport summed over erosion passes, on the canonical vertex grid. */
348
+ erosion?: IErosionMaps;
349
+ }
350
+ interface IErosionMaps {
351
+ /** Droplet water visits, bilinearly distributed at each step. */
352
+ flow: Float32Array;
353
+ /** Carried sediment visits, in metres of equivalent terrain height. */
354
+ sediment: Float32Array;
355
+ /** Hydraulic material deposited, in metres. */
356
+ deposition: Float32Array;
357
+ /** Thermal material received from unstable neighbours, in metres. */
358
+ talus: Float32Array;
359
+ }
360
+ /** Options for one evaluation call. */
361
+ interface IEvaluateOptions {
362
+ /** Vertices per side; one of 17, 33, 65, 129, 257, 513, 1025. Defaults to the document's. */
363
+ readonly resolution?: number;
364
+ readonly signal?: AbortSignal;
365
+ readonly onProgress?: (progress: {
366
+ index: number;
367
+ total: number;
368
+ id: string;
369
+ }) => void;
370
+ }
371
+ /** What `inspect()` reports about an evaluation. */
372
+ interface ITerrainInspection {
373
+ size: number;
374
+ resolution: number;
375
+ layers: number;
376
+ vertices: number;
377
+ triangles: number;
378
+ minHeight: number;
379
+ maxHeight: number;
380
+ instances: number;
381
+ diagnostics: string[];
382
+ }
383
+ /** Options for `copyRegion()`. */
384
+ interface ICopyRegionOptions {
385
+ readonly at?: readonly [number, number];
386
+ readonly size?: number;
387
+ readonly resolution?: number;
388
+ }
389
+ /** One baked mesh: indexed arrays in world space, no Three.js types. */
390
+ interface IBakedMesh {
391
+ name: string;
392
+ positions: Float32Array;
393
+ normals: Float32Array;
394
+ colors?: Float32Array;
395
+ uvs: Float32Array;
396
+ indices: Uint32Array;
397
+ side: number;
398
+ topVertexCount: number;
399
+ bounds: {
400
+ x: number;
401
+ z: number;
402
+ cells: number;
403
+ step: number;
404
+ };
405
+ skirtDepth: number;
406
+ }
407
+ interface IBakeMeshOptions {
408
+ /** Optional sRGB colours per material channel, chosen by the game. */
409
+ readonly palette?: readonly (readonly [number, number, number])[];
410
+ /** Power-of-two sample step within the chunk. */
411
+ readonly step?: number;
412
+ readonly x?: number;
413
+ readonly z?: number;
414
+ readonly cells?: number;
415
+ readonly skirtDepth?: number;
416
+ }
417
+ interface IBakeTerrainOptions {
418
+ readonly palette?: readonly (readonly [number, number, number])[];
419
+ readonly chunkCells?: number;
420
+ readonly lodSteps?: readonly number[];
421
+ readonly skirtDepth?: number;
422
+ }
423
+ /** The baked runtime archive: LOD chunks, collision samples and resolved placements. */
424
+ interface IBakedTerrain {
425
+ version: 1;
426
+ size: number;
427
+ resolution: number;
428
+ lods: {
429
+ step: number;
430
+ chunks: IBakedMesh[];
431
+ }[];
432
+ collision: {
433
+ type: "heightfield";
434
+ size: number;
435
+ resolution: number;
436
+ cellSize: number;
437
+ /** Southwest corner in world metres. Not a `Heightfield` centre. */
438
+ origin: [number, number, number];
439
+ heights: Float32Array;
440
+ };
441
+ instances: IPlacement[];
442
+ materialChannels: MaterialId[];
443
+ }
444
+ /** One file inside an export archive. */
445
+ interface IExportFile {
446
+ name: string;
447
+ bytes: Uint8Array;
448
+ }
449
+ /** An export result: `name`, MIME `type`, and the bytes. */
450
+ interface IExportArchive {
451
+ name: string;
452
+ type: string;
453
+ bytes: Uint8Array;
454
+ }
455
+ /** The recoverable export kinds. */
456
+ type ExportKind = "project" | "png" | "raw" | "splat" | "glb" | "runtime";
457
+ interface IHeightRange {
458
+ min: number;
459
+ max: number;
460
+ }
461
+ interface IRaw16Options extends Partial<IHeightRange> {
462
+ readonly littleEndian?: boolean;
463
+ }
464
+ interface IDecodeRaw16Options extends IHeightRange {
465
+ readonly width: number;
466
+ readonly height: number;
467
+ readonly littleEndian?: boolean;
468
+ }
469
+ interface IDecodeHeightPngOptions extends Partial<IHeightRange> {
470
+ }
471
+ interface IDecodedHeightPng extends IHeightData {
472
+ readonly min: number;
473
+ readonly max: number;
474
+ readonly bitDepth: number;
475
+ readonly hasEmbeddedRange: boolean;
476
+ }
477
+ interface ISplatPng {
478
+ /** The four material channels this packed image carries. */
479
+ channels: readonly MaterialId[];
480
+ bytes: Uint8Array;
481
+ }
482
+ /** The recovered encoder's `StrataHeightRange` PNG metadata. */
483
+ interface IHeightPngMetadata extends IHeightRange {
484
+ }
485
+
486
+ export type { ITerraceParams as $, IGrid as A, IHeightData as B, IHeightPngMetadata as C, IHeightmapParams as D, ExportKind as E, ILayer as F, IMaterialRule as G, IMaterialsParams as H, ITerrainState as I, INoiseParams as J, IPaintParams as K, Layer as L, MaterialId as M, IPasteParams as N, OperationType as O, IPlacement as P, IRampParams as Q, IRiver as R, IRiverParams as S, IRoadParams as T, IScatterClear as U, IScatterParams as V, IScatterRule as W, ISculptParams as X, ISmoothParams as Y, ISplatPng as Z, IStampParams as _, IMask as a, ITerrainInspection as a0, IWaterBody as a1, IWaterParams as a2, IWaterRule as a3, MaskSampler as a4, MaskType as a5, SplinePoint as a6, LayerParams as b, ITerrainConfig as c, ITerrainDocument as d, IEvaluateOptions as e, ICopyRegionOptions as f, IBakedMesh as g, IPlacementOverride as h, ISampledGrid as i, IBakeMeshOptions as j, IBakeTerrainOptions as k, IBakedTerrain as l, IDecodeHeightPngOptions as m, IDecodeRaw16Options as n, IHeightRange as o, IRaw16Options as p, IExportFile as q, IExportArchive as r, IBiomeParams as s, IBrushParams as t, IClearParams as u, IDecodedHeightPng as v, IErodeParams as w, IErosionMaps as x, IFlattenParams as y, IGradeParams as z };