@threenative/terrain 0.0.0-stage → 0.1.1
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.
- package/AGENT_GUIDE.md +194 -0
- package/LICENSE +21 -0
- package/README.md +71 -2
- package/THIRD_PARTY_NOTICES.md +38 -0
- package/dist/editor/index.d.ts +231 -0
- package/dist/editor/index.js +5624 -0
- package/dist/editor/server.d.ts +3 -0
- package/dist/editor/server.js +3688 -0
- package/dist/editor/worker.d.ts +2 -0
- package/dist/editor/worker.js +2212 -0
- package/dist/export.d.ts +54 -0
- package/dist/export.js +297 -0
- package/dist/index.d.ts +335 -0
- package/dist/index.js +2927 -0
- package/dist/server-ByM2hmYJ.d.ts +630 -0
- package/dist/terrain-gPnkCU6S.d.ts +141 -0
- package/dist/three.d.ts +14 -0
- package/dist/three.js +40 -0
- package/dist/types-Ant8-Axc.d.ts +486 -0
- package/package.json +86 -3
- package/starter/alpine/README.md +29 -0
- package/starter/alpine/assets.json +12 -0
- package/starter/alpine/bake.mjs +118 -0
- package/starter/alpine/recipe.json +66167 -0
- package/starter/alpine/sky.ts +23 -0
- package/starter/alpine/surface.json +53 -0
- package/starter/alpine/world.ts +335 -0
- package/starter/coastal/README.md +37 -0
- package/starter/coastal/assets.json +10 -0
- package/starter/coastal/bake.mjs +118 -0
- package/starter/coastal/recipe.json +66161 -0
- package/starter/coastal/sea.ts +327 -0
- package/starter/coastal/sky.ts +24 -0
- package/starter/coastal/surface.json +41 -0
- package/starter/coastal/world.ts +386 -0
- package/starter/desert/README.md +35 -0
- package/starter/desert/assets.json +12 -0
- package/starter/desert/bake.mjs +118 -0
- package/starter/desert/recipe.json +66156 -0
- package/starter/desert/sky.ts +23 -0
- package/starter/desert/surface.json +54 -0
- package/starter/desert/world.ts +335 -0
- package/starter/forest/README.md +46 -0
- package/starter/forest/assets.json +32 -0
- package/starter/forest/bake.mjs +118 -0
- package/starter/forest/recipe.json +4586 -0
- package/starter/forest/sky.ts +27 -0
- package/starter/forest/surface.json +79 -0
- package/starter/forest/water.ts +266 -0
- package/starter/forest/world.ts +413 -0
- package/starter/tundra/README.md +29 -0
- package/starter/tundra/assets.json +12 -0
- package/starter/tundra/bake.mjs +118 -0
- package/starter/tundra/recipe.json +66325 -0
- package/starter/tundra/sky.ts +27 -0
- package/starter/tundra/surface.json +54 -0
- package/starter/tundra/water.ts +313 -0
- package/starter/tundra/world.ts +366 -0
- package/starter-assets/bark_brown_02/bark_brown_02_diff_1k.jpg +0 -0
- package/starter-assets/bark_brown_02/bark_brown_02_nor_gl_1k.jpg +0 -0
- package/starter-assets/bark_brown_02/bark_brown_02_rough_1k.jpg +0 -0
- package/starter-assets/boulder_01/boulder-mid.glb +0 -0
- package/starter-assets/boulder_01/boulder-near.glb +0 -0
- package/starter-assets/cliff_side/cliff_side_ao_1k.jpg +0 -0
- package/starter-assets/cliff_side/cliff_side_diff_1k.jpg +0 -0
- package/starter-assets/cliff_side/cliff_side_nor_gl_1k.jpg +0 -0
- package/starter-assets/cliff_side/cliff_side_rough_1k.jpg +0 -0
- package/starter-assets/credits.json +1022 -0
- package/starter-assets/fern_02/fern_02_alpha_512.png +0 -0
- package/starter-assets/fern_02/fern_02_diff_512.jpg +0 -0
- package/starter-assets/fern_02/fern_02_nor_gl_512.jpg +0 -0
- package/starter-assets/fir_tree_01/fir-b-lod1.glb +0 -0
- package/starter-assets/fir_tree_01/fir-b-lod2.glb +0 -0
- package/starter-assets/fir_tree_01/fir-c-lod1.glb +0 -0
- package/starter-assets/fir_tree_01/fir-c-lod2.glb +0 -0
- package/starter-assets/fir_tree_01/fir_tree_01_twig_arm_1k.jpg +0 -0
- package/starter-assets/fir_tree_01/fir_tree_01_twig_diff_1k.jpg +0 -0
- package/starter-assets/fir_tree_01/fir_tree_01_twig_nor_gl_1k.jpg +0 -0
- package/starter-assets/forest_ground_04/forest_ground_04_ao_1k.jpg +0 -0
- package/starter-assets/forest_ground_04/forest_ground_04_diff_1k.jpg +0 -0
- package/starter-assets/forest_ground_04/forest_ground_04_nor_gl_1k.jpg +0 -0
- package/starter-assets/forest_ground_04/forest_ground_04_rough_1k.jpg +0 -0
- package/starter-assets/forest_leaves_02/forest_leaves_02_arm_512.jpg +0 -0
- package/starter-assets/forest_leaves_02/forest_leaves_02_diff_512.jpg +0 -0
- package/starter-assets/forest_leaves_02/forest_leaves_02_nor_gl_512.jpg +0 -0
- package/starter-assets/grass_medium_02/grass_medium_02_alpha_512.png +0 -0
- package/starter-assets/grass_medium_02/grass_medium_02_diff_512.jpg +0 -0
- package/starter-assets/grass_medium_02/grass_medium_02_nor_gl_512.jpg +0 -0
- package/starter-assets/hdri/kloofendal_48d_partly_cloudy_1k.hdr +0 -0
- package/starter-assets/leafy_grass/leafy_grass_ao_1k.jpg +0 -0
- package/starter-assets/leafy_grass/leafy_grass_diff_1k.jpg +0 -0
- package/starter-assets/leafy_grass/leafy_grass_nor_gl_1k.jpg +0 -0
- package/starter-assets/leafy_grass/leafy_grass_rough_1k.jpg +0 -0
- package/starter-assets/lichen_rock/lichen_rock_arm_512.jpg +0 -0
- package/starter-assets/lichen_rock/lichen_rock_diff_512.jpg +0 -0
- package/starter-assets/lichen_rock/lichen_rock_nor_gl_512.jpg +0 -0
- package/starter-assets/mossy_rock/mossy_rock_ao_1k.jpg +0 -0
- package/starter-assets/mossy_rock/mossy_rock_diff_1k.jpg +0 -0
- package/starter-assets/mossy_rock/mossy_rock_nor_gl_1k.jpg +0 -0
- package/starter-assets/mossy_rock/mossy_rock_rough_1k.jpg +0 -0
- package/starter-assets/needle-atlas.png +0 -0
- package/starter-assets/needle-surface.png +0 -0
- package/starter-assets/river_small_rocks/river_small_rocks_arm_512.jpg +0 -0
- package/starter-assets/river_small_rocks/river_small_rocks_diff_512.jpg +0 -0
- package/starter-assets/river_small_rocks/river_small_rocks_nor_gl_512.jpg +0 -0
- package/starter-assets/rock_boulder_dry/rock_boulder_dry_arm_512.jpg +0 -0
- package/starter-assets/rock_boulder_dry/rock_boulder_dry_diff_512.jpg +0 -0
- package/starter-assets/rock_boulder_dry/rock_boulder_dry_nor_gl_512.jpg +0 -0
- package/starter-assets/rocks/boulder-mid.glb +0 -0
- package/starter-assets/rocks/boulder-near.glb +0 -0
- package/starter-assets/rocks/boulder-prep.json +37 -0
- package/starter-assets/rocks/rock01-mid.glb +0 -0
- package/starter-assets/rocks/rock01-near.glb +0 -0
- package/starter-assets/rocks/rock01-prep.json +37 -0
- package/starter-assets/rocks/rock04-mid.glb +0 -0
- package/starter-assets/rocks/rock04-near.glb +0 -0
- package/starter-assets/rocks/rock04-prep.json +37 -0
- package/starter-assets/sand_01/sand_01_ao_1k.jpg +0 -0
- package/starter-assets/sand_01/sand_01_diff_1k.jpg +0 -0
- package/starter-assets/sand_01/sand_01_nor_gl_1k.jpg +0 -0
- package/starter-assets/sand_01/sand_01_rough_1k.jpg +0 -0
- package/starter-assets/snow_02/snow_02_ao_1k.jpg +0 -0
- package/starter-assets/snow_02/snow_02_diff_1k.jpg +0 -0
- package/starter-assets/snow_02/snow_02_nor_gl_1k.jpg +0 -0
- package/starter-assets/snow_02/snow_02_rough_1k.jpg +0 -0
|
@@ -0,0 +1,630 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
import { I as ITerrainState, d as ITerrainDocument, h as IPlacementOverride } from './types-Ant8-Axc.js';
|
|
3
|
+
|
|
4
|
+
type IAssetKind = "model" | "image" | "environment";
|
|
5
|
+
interface IAssetBounds {
|
|
6
|
+
readonly min: readonly [number, number, number];
|
|
7
|
+
readonly max: readonly [number, number, number];
|
|
8
|
+
}
|
|
9
|
+
/** Source-unit scale and pivot applied when a model is placed; the file itself is never edited. */
|
|
10
|
+
interface IAssetAdjust {
|
|
11
|
+
/** Multiplies the model's own units to metres. 1 means the file is already in metres. */
|
|
12
|
+
readonly scale: number;
|
|
13
|
+
/** Which point of the bounds sits on the placement: the base centre, the centre or the origin. */
|
|
14
|
+
readonly pivot: "base" | "centre" | "origin";
|
|
15
|
+
}
|
|
16
|
+
/** One registered file, as saved in the authoring document. */
|
|
17
|
+
interface IProjectAsset {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly kind: IAssetKind;
|
|
20
|
+
readonly name: string;
|
|
21
|
+
/** Relative to the project's assets directory; the file's name carries its content hash. */
|
|
22
|
+
readonly path: string;
|
|
23
|
+
readonly sha256: string;
|
|
24
|
+
readonly bytes: number;
|
|
25
|
+
readonly status: "ready";
|
|
26
|
+
readonly license?: string;
|
|
27
|
+
readonly source?: string;
|
|
28
|
+
readonly bounds?: IAssetBounds;
|
|
29
|
+
readonly triangles?: number;
|
|
30
|
+
readonly adjust?: IAssetAdjust;
|
|
31
|
+
/** Authoring diagnostics, e.g. an imported light that stays inactive. */
|
|
32
|
+
readonly diagnostics?: readonly string[];
|
|
33
|
+
readonly width?: number;
|
|
34
|
+
readonly height?: number;
|
|
35
|
+
/** An image's declared format: `png`, `jpeg`, `webp`, `hdr` or `exr`. */
|
|
36
|
+
readonly format?: string;
|
|
37
|
+
}
|
|
38
|
+
interface IAssetLimits {
|
|
39
|
+
/** File size cap in bytes. */
|
|
40
|
+
readonly maxBytes: number;
|
|
41
|
+
/** Decoded image width/height cap in pixels. */
|
|
42
|
+
readonly maxDimension: number;
|
|
43
|
+
/** Triangle cap for one model. */
|
|
44
|
+
readonly maxTriangles: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Validate one saved asset registration exactly as a document commit stores it.
|
|
48
|
+
* @summary Validate a registered terrain-editor asset entry
|
|
49
|
+
* @requires npm i -D @threenative/terrain
|
|
50
|
+
* @situation check a model, image or environment asset entry before saving it in the authoring document
|
|
51
|
+
* @constraint authoring metadata only; ids, content-hashed paths, bounds and unit adjustments are checked, files are not read
|
|
52
|
+
* @example const asset = validateAsset({ id: "oak", kind: "model", name: "oak.glb", path: "models/0123456789ab-oak.glb", sha256: "0123456789ab".padEnd(64, "0"), bytes: 2180, status: "ready" });
|
|
53
|
+
* @override the project owns its asset directory and which entries it keeps
|
|
54
|
+
*/
|
|
55
|
+
declare function validateAsset(input: unknown): IProjectAsset;
|
|
56
|
+
/**
|
|
57
|
+
* Validate the whole asset list of an authoring document.
|
|
58
|
+
* @summary Validate the registered terrain-editor assets
|
|
59
|
+
* @requires npm i -D @threenative/terrain
|
|
60
|
+
* @situation check every registered model, image and environment entry together, with unique ids
|
|
61
|
+
* @constraint at most 64 entries; unique ids; each entry as validateAsset
|
|
62
|
+
* @example const assets = validateAssets(document.assets ?? []);
|
|
63
|
+
* @override the project owns its asset directory and which entries it keeps
|
|
64
|
+
*/
|
|
65
|
+
declare function validateAssets(input: unknown): IProjectAsset[];
|
|
66
|
+
type IAssetOperation = {
|
|
67
|
+
readonly op: "list";
|
|
68
|
+
}
|
|
69
|
+
/** The agent path: a local file, as an asset-MCP download result returns it. */
|
|
70
|
+
| {
|
|
71
|
+
readonly op: "register";
|
|
72
|
+
readonly id: string;
|
|
73
|
+
readonly kind?: IAssetKind;
|
|
74
|
+
readonly path: string;
|
|
75
|
+
readonly license?: string;
|
|
76
|
+
readonly source?: string;
|
|
77
|
+
readonly replace?: boolean;
|
|
78
|
+
}
|
|
79
|
+
/** The GUI path: bytes the browser read from a picked or dropped file. */
|
|
80
|
+
| {
|
|
81
|
+
readonly op: "upload";
|
|
82
|
+
readonly id: string;
|
|
83
|
+
readonly kind?: IAssetKind;
|
|
84
|
+
readonly name: string;
|
|
85
|
+
readonly data: string;
|
|
86
|
+
readonly license?: string;
|
|
87
|
+
readonly source?: string;
|
|
88
|
+
readonly replace?: boolean;
|
|
89
|
+
} | {
|
|
90
|
+
readonly op: "adjust";
|
|
91
|
+
readonly id: string;
|
|
92
|
+
readonly adjust: unknown;
|
|
93
|
+
}
|
|
94
|
+
/** Bind an image asset to one named surface input of the project's render source. */
|
|
95
|
+
| {
|
|
96
|
+
readonly op: "map";
|
|
97
|
+
readonly input: string;
|
|
98
|
+
readonly asset: string;
|
|
99
|
+
}
|
|
100
|
+
/** Return one surface input to the project's own art. */
|
|
101
|
+
| {
|
|
102
|
+
readonly op: "unmap";
|
|
103
|
+
readonly input: string;
|
|
104
|
+
}
|
|
105
|
+
/** Removes the palette entry only; the stored file is never erased. */
|
|
106
|
+
| {
|
|
107
|
+
readonly op: "remove";
|
|
108
|
+
readonly id: string;
|
|
109
|
+
};
|
|
110
|
+
/** One surface input and the registered image that replaces its starter art. */
|
|
111
|
+
interface ISurfaceMapping {
|
|
112
|
+
readonly asset: string;
|
|
113
|
+
}
|
|
114
|
+
type ISurfaceMappings = Record<string, ISurfaceMapping>;
|
|
115
|
+
interface IAssetResult {
|
|
116
|
+
readonly op: string;
|
|
117
|
+
readonly assets: readonly IProjectAsset[];
|
|
118
|
+
readonly surfaces: ISurfaceMappings;
|
|
119
|
+
readonly asset: IProjectAsset | null;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The kind and format a file's own bytes declare, whatever its name says.
|
|
123
|
+
* @summary Identify a GLB, PNG, JPEG, WebP, HDR or EXR file by its header
|
|
124
|
+
* @requires npm i -D @threenative/terrain
|
|
125
|
+
* @situation decide whether a dropped or downloaded file is a model, surface image or environment image
|
|
126
|
+
* @constraint reads only the first bytes; a file it cannot name returns undefined and must be refused
|
|
127
|
+
* @example const kind = sniff(new Uint8Array(await file.arrayBuffer()))?.kind;
|
|
128
|
+
* @override the caller decides what to do with an unknown file
|
|
129
|
+
*/
|
|
130
|
+
declare function sniff(bytes: Uint8Array): {
|
|
131
|
+
kind: IAssetKind;
|
|
132
|
+
ext: string;
|
|
133
|
+
format: string;
|
|
134
|
+
} | undefined;
|
|
135
|
+
/**
|
|
136
|
+
* The colour space an input's pixels are read in, from the channel its name ends with.
|
|
137
|
+
* @summary Resolve a surface input name to sRGB or linear
|
|
138
|
+
* @requires npm i -D @threenative/terrain
|
|
139
|
+
* @situation decide how an imported image bound to a named surface input must be sampled
|
|
140
|
+
* @constraint throws by name for an input that is not <surface>.<channel> with a known channel
|
|
141
|
+
* @example const space = surfaceSpace("bark.normal");
|
|
142
|
+
* @override the project owns which surface inputs exist
|
|
143
|
+
*/
|
|
144
|
+
declare function surfaceSpace(input: string): "srgb" | "linear";
|
|
145
|
+
/**
|
|
146
|
+
* Validate surface mappings against the registered images they name.
|
|
147
|
+
* @summary Validate terrain-editor surface image mappings
|
|
148
|
+
* @requires npm i -D @threenative/terrain
|
|
149
|
+
* @situation save which imported PBR image replaces which named surface input of the project's render source
|
|
150
|
+
* @constraint authoring metadata only; each input is <surface>.<channel>; every asset must be a registered image
|
|
151
|
+
* @example const surfaces = validateSurfaces({ "bark.normal": { asset: "my-normal" } }, document.assets ?? []);
|
|
152
|
+
* @override the project's render source defines which surface inputs exist and what they draw
|
|
153
|
+
*/
|
|
154
|
+
declare function validateSurfaces(input: unknown, assets: readonly IProjectAsset[]): ISurfaceMappings;
|
|
155
|
+
|
|
156
|
+
/** Position/target/planes of one named observation camera. */
|
|
157
|
+
interface ICameraPose {
|
|
158
|
+
/** Stable across renames and reloads; selection and saves address the camera by it. */
|
|
159
|
+
readonly id: string;
|
|
160
|
+
readonly name: string;
|
|
161
|
+
/** Local metres, Y up, centred on the terrain origin. */
|
|
162
|
+
readonly position: readonly [number, number, number];
|
|
163
|
+
readonly target: readonly [number, number, number];
|
|
164
|
+
readonly up: readonly [number, number, number];
|
|
165
|
+
readonly near: number;
|
|
166
|
+
readonly far: number;
|
|
167
|
+
}
|
|
168
|
+
/** A saved camera: perspective with a field of view, or orthographic with an extent and zoom. */
|
|
169
|
+
type ISavedCamera = ICameraPose & ({
|
|
170
|
+
readonly projection: "perspective";
|
|
171
|
+
readonly fov: number;
|
|
172
|
+
} | {
|
|
173
|
+
readonly projection: "orthographic";
|
|
174
|
+
readonly extent: number;
|
|
175
|
+
readonly zoom: number;
|
|
176
|
+
});
|
|
177
|
+
/** The camera list saved with the authoring document, plus which one is live. */
|
|
178
|
+
interface ICameraSet {
|
|
179
|
+
readonly cameras: readonly ISavedCamera[];
|
|
180
|
+
/** `null` is the ordinary editor camera, not a saved bookmark. */
|
|
181
|
+
readonly activeCamera: string | null;
|
|
182
|
+
}
|
|
183
|
+
type ICameraOperation = {
|
|
184
|
+
readonly op: "create";
|
|
185
|
+
readonly camera: unknown;
|
|
186
|
+
} | {
|
|
187
|
+
readonly op: "get";
|
|
188
|
+
readonly id: string;
|
|
189
|
+
} | {
|
|
190
|
+
readonly op: "list";
|
|
191
|
+
} | {
|
|
192
|
+
readonly op: "update";
|
|
193
|
+
readonly id: string;
|
|
194
|
+
readonly patch: unknown;
|
|
195
|
+
} | {
|
|
196
|
+
readonly op: "delete";
|
|
197
|
+
readonly id: string;
|
|
198
|
+
} | {
|
|
199
|
+
readonly op: "activate";
|
|
200
|
+
readonly id: string | null;
|
|
201
|
+
};
|
|
202
|
+
interface ICameraResult {
|
|
203
|
+
readonly op: string;
|
|
204
|
+
readonly cameras: readonly ISavedCamera[];
|
|
205
|
+
readonly activeCamera: string | null;
|
|
206
|
+
/** The camera the operation addresses: the created, read or changed one. */
|
|
207
|
+
readonly camera: ISavedCamera | null;
|
|
208
|
+
/** `editor-camera` when this operation left the ordinary editor camera live. */
|
|
209
|
+
readonly fallback: string | null;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Normalise and validate one saved camera, exactly as a document commit would store it.
|
|
213
|
+
* @summary Validate an editor observation camera bookmark
|
|
214
|
+
* @requires npm i -D @threenative/terrain
|
|
215
|
+
* @situation create or update a named terrain-editor camera in the shared authoring document
|
|
216
|
+
* @constraint authoring metadata only; finite noncoincident poses, usable up, valid projection, ordered planes
|
|
217
|
+
* @example const camera = validateCamera({ id: "survey", name: "Survey", position: [40, 60, 40], target: [0, 12, 0], up: [0, 1, 0], projection: "perspective", fov: 50, near: 0.5, far: 900 });
|
|
218
|
+
* @override the caller owns the document revision and the live viewport aspect
|
|
219
|
+
*/
|
|
220
|
+
declare function validateCamera(input: unknown, existing?: readonly ISavedCamera[]): ISavedCamera;
|
|
221
|
+
/**
|
|
222
|
+
* Validate a document's saved camera list, keeping ids unique across it.
|
|
223
|
+
* @summary Validate the saved editor camera list of an authoring document
|
|
224
|
+
* @requires npm i -D @threenative/terrain
|
|
225
|
+
* @situation reject a disk document whose saved cameras are malformed or duplicated
|
|
226
|
+
* @constraint authoring metadata only; the same validation the camera operations apply
|
|
227
|
+
* @example const cameras = validateCameras(JSON.parse(saved).cameras ?? []);
|
|
228
|
+
* @override the document owns how many cameras it keeps; the list bound is the editor's own
|
|
229
|
+
*/
|
|
230
|
+
declare function validateCameras(input: unknown): ISavedCamera[];
|
|
231
|
+
/**
|
|
232
|
+
* Run one create / get / list / update / delete / activate operation over saved cameras.
|
|
233
|
+
* @summary Apply an editor camera operation to a saved camera set
|
|
234
|
+
* @requires npm i -D @threenative/terrain
|
|
235
|
+
* @situation drive the terrain editor camera list from an agent or the GUI through one shared dispatch
|
|
236
|
+
* @constraint pure: the input set is never mutated, an unknown id throws, and deleting the live camera falls back
|
|
237
|
+
* @example const next = runCameraOperation(set, { op: "activate", id: "survey" });
|
|
238
|
+
* @override the caller persists the returned set against its own revision
|
|
239
|
+
*/
|
|
240
|
+
declare function runCameraOperation(set: ICameraSet, operation: unknown): ICameraResult;
|
|
241
|
+
/** World-space bounds of whatever a focus target resolved to. */
|
|
242
|
+
interface IFocusBounds {
|
|
243
|
+
readonly min: readonly [number, number, number];
|
|
244
|
+
readonly max: readonly [number, number, number];
|
|
245
|
+
}
|
|
246
|
+
type IFocusTarget = {
|
|
247
|
+
readonly kind: "point";
|
|
248
|
+
readonly at: readonly [number, number, number];
|
|
249
|
+
} | {
|
|
250
|
+
readonly kind: "prop" | "landmark" | "region";
|
|
251
|
+
readonly id: string;
|
|
252
|
+
};
|
|
253
|
+
/**
|
|
254
|
+
* Resolves a non-point focus target to real world bounds. The view owns this because only it holds
|
|
255
|
+
* the live instance matrices, the registered landmarks and the evaluated terrain extent.
|
|
256
|
+
*/
|
|
257
|
+
type IFocusResolver = (target: {
|
|
258
|
+
readonly kind: "prop" | "landmark" | "region";
|
|
259
|
+
readonly id: string;
|
|
260
|
+
}) => IFocusBounds | undefined;
|
|
261
|
+
interface IFocusRequest {
|
|
262
|
+
readonly target: IFocusTarget;
|
|
263
|
+
/** Measured from the live viewport; a saved browser aspect would frame the wrong picture. */
|
|
264
|
+
readonly aspect: number;
|
|
265
|
+
/** Extra room around the target as a multiplier of its own size. */
|
|
266
|
+
readonly margin?: number;
|
|
267
|
+
/** Heading kept from the current view, so focusing never spins the camera. */
|
|
268
|
+
readonly direction?: readonly [number, number, number];
|
|
269
|
+
}
|
|
270
|
+
interface IFocusFraming {
|
|
271
|
+
readonly centre: readonly [number, number, number];
|
|
272
|
+
readonly radiusMetres: number;
|
|
273
|
+
readonly distanceMetres: number;
|
|
274
|
+
readonly margin: number;
|
|
275
|
+
readonly aspect: number;
|
|
276
|
+
readonly near: number;
|
|
277
|
+
readonly far: number;
|
|
278
|
+
/** Orthographic only: the extent the viewport aspect needs to contain the target. */
|
|
279
|
+
readonly extent: number | null;
|
|
280
|
+
}
|
|
281
|
+
interface IFocusOutcome {
|
|
282
|
+
/** `null` keeps the last valid camera; the diagnostic then says why. */
|
|
283
|
+
readonly camera: ISavedCamera | null;
|
|
284
|
+
readonly framing: IFocusFraming | null;
|
|
285
|
+
readonly diagnostic: string | null;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Frame a point, prop, landmark or region for the live viewport and projection.
|
|
289
|
+
* @summary Frame a focus target with an editor observation camera
|
|
290
|
+
* @requires npm i -D @threenative/terrain
|
|
291
|
+
* @situation frame one selected prop, a registered landmark, a terrain region or a local point
|
|
292
|
+
* @constraint framing only; an unknown target returns a named diagnostic and no camera
|
|
293
|
+
* @example const outcome = focusCamera(camera, { target: { kind: "prop", id: "pine-3" }, aspect: 16 / 9 }, resolve);
|
|
294
|
+
* @override the view supplies target bounds, the live aspect and the camera to keep
|
|
295
|
+
*/
|
|
296
|
+
declare function focusCamera(camera: ISavedCamera, request: IFocusRequest, resolve: IFocusResolver): IFocusOutcome;
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Preview environment settings: validated values the editor binds to project-owned render source.
|
|
300
|
+
*
|
|
301
|
+
* This file decides nothing about how the world looks. Every field is optional and an absent field
|
|
302
|
+
* means "the project's own starter value", so the document carries only deliberate overrides and
|
|
303
|
+
* the view reports the effective values it started from.
|
|
304
|
+
*/
|
|
305
|
+
/** Hex colour as saved: `#rrggbb`. */
|
|
306
|
+
type IHexColour = string;
|
|
307
|
+
interface IEnvironment {
|
|
308
|
+
/** Direction towards the sun, as angles; intensity in the project's light units. */
|
|
309
|
+
readonly sun?: {
|
|
310
|
+
/** Degrees, `atan2(z, x)` of the horizontal part of the direction towards the sun. */
|
|
311
|
+
readonly azimuth?: number;
|
|
312
|
+
/** Degrees above the horizon, 0 to 90. */
|
|
313
|
+
readonly elevation?: number;
|
|
314
|
+
readonly intensity?: number;
|
|
315
|
+
readonly colour?: IHexColour;
|
|
316
|
+
};
|
|
317
|
+
/** Sky fill: the hemisphere light's intensity. */
|
|
318
|
+
readonly fill?: {
|
|
319
|
+
readonly intensity?: number;
|
|
320
|
+
};
|
|
321
|
+
/**
|
|
322
|
+
* What is drawn behind the world: a procedural colour, or a registered environment/image asset.
|
|
323
|
+
* An image replaces the colour; remove it (null) and the colour is back.
|
|
324
|
+
*/
|
|
325
|
+
readonly sky?: {
|
|
326
|
+
readonly colour?: IHexColour;
|
|
327
|
+
/** A registered HDR/EXR or ordinary equirectangular image id. */
|
|
328
|
+
readonly image?: string;
|
|
329
|
+
/** Degrees about the vertical axis. */
|
|
330
|
+
readonly rotation?: number;
|
|
331
|
+
/** Multiplies the image's radiance. */
|
|
332
|
+
readonly intensity?: number;
|
|
333
|
+
};
|
|
334
|
+
/**
|
|
335
|
+
* Illumination from an image, chosen independently of the background. When it is set and the
|
|
336
|
+
* sky fill is not, the hemisphere fill is 0 so the image is the one fill and light is not
|
|
337
|
+
* counted twice; the sun is its own, explicit contribution.
|
|
338
|
+
*/
|
|
339
|
+
readonly lighting?: {
|
|
340
|
+
readonly image?: string;
|
|
341
|
+
readonly rotation?: number;
|
|
342
|
+
readonly intensity?: number;
|
|
343
|
+
};
|
|
344
|
+
/** Distance haze. `mode` names a fog this source supports; anything else is refused by name. */
|
|
345
|
+
readonly fog?: {
|
|
346
|
+
readonly mode?: "exp2";
|
|
347
|
+
readonly colour?: IHexColour;
|
|
348
|
+
/** Per metre. */
|
|
349
|
+
readonly density?: number;
|
|
350
|
+
};
|
|
351
|
+
/** Linear exposure multiplier (2^EV). */
|
|
352
|
+
readonly exposure?: number;
|
|
353
|
+
/** The sea's shallow and deep colour; wave behaviour is project render source. */
|
|
354
|
+
readonly ocean?: {
|
|
355
|
+
readonly shallow?: IHexColour;
|
|
356
|
+
readonly deep?: IHexColour;
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
type IEnvironmentOperation = {
|
|
360
|
+
readonly op: "get";
|
|
361
|
+
}
|
|
362
|
+
/** Deep-merge validated values; a `null` field returns that field to the project's value. */
|
|
363
|
+
| {
|
|
364
|
+
readonly op: "patch";
|
|
365
|
+
readonly values: unknown;
|
|
366
|
+
} | {
|
|
367
|
+
readonly op: "reset";
|
|
368
|
+
};
|
|
369
|
+
interface IEnvironmentResult {
|
|
370
|
+
readonly op: string;
|
|
371
|
+
/** The saved overrides after this operation. */
|
|
372
|
+
readonly environment: IEnvironment;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Validate a complete overrides object exactly as a document commit stores it.
|
|
376
|
+
* @summary Validate preview environment overrides
|
|
377
|
+
* @requires npm i -D @threenative/terrain
|
|
378
|
+
* @situation save sun, sky, haze, exposure or ocean overrides in the shared authoring document
|
|
379
|
+
* @constraint authoring metadata only; unknown fields and unsupported fog modes are refused by name
|
|
380
|
+
* @example const environment = validateEnvironment({ sun: { elevation: 25, intensity: 3 } });
|
|
381
|
+
* @override the project's render source defines what each value does; absent fields keep its own
|
|
382
|
+
*/
|
|
383
|
+
declare function validateEnvironment(input: unknown): IEnvironment;
|
|
384
|
+
/**
|
|
385
|
+
* Apply one get / patch / reset against the saved environment overrides; the caller commits it.
|
|
386
|
+
* @summary Run one preview-environment operation
|
|
387
|
+
* @requires npm i -D @threenative/terrain
|
|
388
|
+
* @situation patch sun, haze, exposure or sea overrides from a controller or the editor GUI
|
|
389
|
+
* @constraint authoring metadata only; a null field returns to the project's own value
|
|
390
|
+
* @example const result = runEnvironmentOperation({}, { op: "patch", values: { sun: { elevation: 25 } } });
|
|
391
|
+
* @override the project's render source decides what each value does
|
|
392
|
+
*/
|
|
393
|
+
declare function runEnvironmentOperation(current: IEnvironment, operation: unknown): IEnvironmentResult;
|
|
394
|
+
/**
|
|
395
|
+
* Check that the images an environment names are registered environment or image assets.
|
|
396
|
+
* @summary Validate the assets a preview environment refers to
|
|
397
|
+
* @requires npm i -D @threenative/terrain
|
|
398
|
+
* @situation refuse a saved environment whose sky or lighting image is not a registered file
|
|
399
|
+
* @constraint throws by name; HDR/EXR environment files and ordinary images are accepted, models are not
|
|
400
|
+
* @example checkEnvironmentAssets({ sky: { image: "dusk" } }, document.assets ?? []);
|
|
401
|
+
* @override the project owns which images it registers
|
|
402
|
+
*/
|
|
403
|
+
declare function checkEnvironmentAssets(environment: IEnvironment, assets: readonly {
|
|
404
|
+
id: string;
|
|
405
|
+
kind: string;
|
|
406
|
+
}[]): void;
|
|
407
|
+
|
|
408
|
+
/** A labelled point shared between a reference image and local world metres. */
|
|
409
|
+
interface ISpatialControlPoint {
|
|
410
|
+
readonly id: string;
|
|
411
|
+
/** `[u, v]` image pixels. */
|
|
412
|
+
readonly image: readonly [number, number];
|
|
413
|
+
/** `[x, z]` local metres, Y up, centred on the terrain origin. */
|
|
414
|
+
readonly world: readonly [number, number];
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* A bounded local reference image saved with the document.
|
|
418
|
+
* `screenshot` is a perspective view reference; only a `top-down-map` is metric.
|
|
419
|
+
*/
|
|
420
|
+
interface ISpatialReference {
|
|
421
|
+
readonly id: string;
|
|
422
|
+
readonly kind: "top-down-map" | "screenshot";
|
|
423
|
+
/** Content hash of the imported image; different pixels are a different reference. */
|
|
424
|
+
readonly hash: string;
|
|
425
|
+
/** Where the image came from, in the author's words. */
|
|
426
|
+
readonly source: string;
|
|
427
|
+
/** Vertical datum. `null` means unknown, never zero. */
|
|
428
|
+
readonly datum?: string | null;
|
|
429
|
+
/** Horizontal residual budget for a checkpoint to count as aligned. Defaults to 1 m. */
|
|
430
|
+
readonly toleranceMetres?: number;
|
|
431
|
+
/** Two distinct controls fit scale, rotation and translation; more are reported, not fitted. */
|
|
432
|
+
readonly controls: readonly ISpatialControlPoint[];
|
|
433
|
+
/** An independent check point, never used to fit. Without one the fit is uncalibrated. */
|
|
434
|
+
readonly checkpoint?: ISpatialControlPoint | null;
|
|
435
|
+
}
|
|
436
|
+
/** A validated reference: every optional field resolved, unknown datum kept as `null`. */
|
|
437
|
+
type ISavedSpatialReference = ISpatialReference & {
|
|
438
|
+
readonly datum: string | null;
|
|
439
|
+
readonly toleranceMetres: number;
|
|
440
|
+
readonly checkpoint: ISpatialControlPoint | null;
|
|
441
|
+
};
|
|
442
|
+
type ISpatialQuery = {
|
|
443
|
+
readonly kind: "point";
|
|
444
|
+
readonly at: readonly number[];
|
|
445
|
+
} | {
|
|
446
|
+
readonly kind: "profile";
|
|
447
|
+
readonly from: readonly number[];
|
|
448
|
+
readonly to: readonly number[];
|
|
449
|
+
readonly samples?: number;
|
|
450
|
+
} | {
|
|
451
|
+
readonly kind: "reference";
|
|
452
|
+
readonly reference: ISpatialReference;
|
|
453
|
+
};
|
|
454
|
+
/** One measured surface value, labelled with the sampler that produced it. */
|
|
455
|
+
interface ISurfaceReading {
|
|
456
|
+
readonly source: "bilinear-heightfield" | "evaluated-triangle";
|
|
457
|
+
readonly height: number;
|
|
458
|
+
readonly slopeDeg: number;
|
|
459
|
+
/** Compass degrees clockwise from -Z. `null` on flat ground, where it is undefined. */
|
|
460
|
+
readonly aspectDeg: number | null;
|
|
461
|
+
readonly normal: readonly [number, number, number];
|
|
462
|
+
}
|
|
463
|
+
interface IPointObservation {
|
|
464
|
+
readonly kind: "point";
|
|
465
|
+
readonly revision: string;
|
|
466
|
+
readonly units: "metres";
|
|
467
|
+
readonly resolution: number;
|
|
468
|
+
readonly at: readonly [number, number];
|
|
469
|
+
readonly bilinear: ISurfaceReading;
|
|
470
|
+
readonly triangle: ISurfaceReading;
|
|
471
|
+
/** `triangle.height - bilinear.height`; non-zero inside a non-planar cell. */
|
|
472
|
+
readonly difference: number;
|
|
473
|
+
readonly datum: string | null;
|
|
474
|
+
readonly labels: readonly string[];
|
|
475
|
+
}
|
|
476
|
+
interface IProfileSample {
|
|
477
|
+
/** Horizontal distance from `from`, in metres. */
|
|
478
|
+
readonly distance: number;
|
|
479
|
+
/** Distance walked along the measured surface so far. */
|
|
480
|
+
readonly surfaceDistance: number;
|
|
481
|
+
readonly height: number;
|
|
482
|
+
readonly slopeDeg: number;
|
|
483
|
+
}
|
|
484
|
+
interface IProfileObservation {
|
|
485
|
+
readonly kind: "profile";
|
|
486
|
+
readonly revision: string;
|
|
487
|
+
readonly units: "metres";
|
|
488
|
+
readonly resolution: number;
|
|
489
|
+
readonly from: readonly [number, number];
|
|
490
|
+
readonly to: readonly [number, number];
|
|
491
|
+
readonly sampleCount: number;
|
|
492
|
+
readonly horizontalDistance: number;
|
|
493
|
+
readonly surfaceDistance: number;
|
|
494
|
+
readonly minElevation: number;
|
|
495
|
+
readonly maxElevation: number;
|
|
496
|
+
readonly maxGradeDeg: number;
|
|
497
|
+
readonly samples: readonly IProfileSample[];
|
|
498
|
+
readonly datum: string | null;
|
|
499
|
+
readonly labels: readonly string[];
|
|
500
|
+
}
|
|
501
|
+
interface IReferenceObservation {
|
|
502
|
+
readonly kind: "reference";
|
|
503
|
+
readonly revision: string;
|
|
504
|
+
readonly units: "metres";
|
|
505
|
+
readonly id: string;
|
|
506
|
+
readonly source: string;
|
|
507
|
+
readonly hash: string;
|
|
508
|
+
readonly metric: boolean;
|
|
509
|
+
/** Smallest 2D similarity from the two fit controls. `null` until two distinct ones exist. */
|
|
510
|
+
readonly fit: {
|
|
511
|
+
readonly scaleMetresPerPixel: number | null;
|
|
512
|
+
readonly rotationDegrees: number | null;
|
|
513
|
+
/** The fit never mirrors an image; a mirrored sheet is reported, not silently applied. */
|
|
514
|
+
readonly mirrored: false;
|
|
515
|
+
} | null;
|
|
516
|
+
readonly controlResidualMetres: number | null;
|
|
517
|
+
readonly landmarks: readonly {
|
|
518
|
+
readonly id: string;
|
|
519
|
+
readonly role: "control" | "checkpoint";
|
|
520
|
+
readonly image: readonly [number, number];
|
|
521
|
+
readonly world: readonly [number, number];
|
|
522
|
+
/** `null` until two distinct controls exist; never a fabricated zero. */
|
|
523
|
+
readonly fitted: readonly [number, number] | null;
|
|
524
|
+
readonly horizontalResidualMetres: number | null;
|
|
525
|
+
}[];
|
|
526
|
+
readonly checkpoint: {
|
|
527
|
+
readonly id: string;
|
|
528
|
+
readonly residualMetres: number;
|
|
529
|
+
readonly withinTolerance: boolean;
|
|
530
|
+
} | null;
|
|
531
|
+
readonly toleranceMetres: number;
|
|
532
|
+
/** Fitted controls alone are not calibration; an independent checkpoint within tolerance is. */
|
|
533
|
+
readonly calibrated: boolean;
|
|
534
|
+
readonly datum: string | null;
|
|
535
|
+
readonly labels: readonly string[];
|
|
536
|
+
}
|
|
537
|
+
type ISpatialObservation = IPointObservation | IProfileObservation | IReferenceObservation;
|
|
538
|
+
/**
|
|
539
|
+
* Probe the evaluated surface at one world point, separating bilinear from triangle values.
|
|
540
|
+
* @requires npm i -D @threenative/terrain
|
|
541
|
+
* @situation measure an authored terrain point with provenance and a revision identity
|
|
542
|
+
* @constraint authoring data only; non-finite, malformed and out-of-extent queries throw
|
|
543
|
+
* @example const probe = probeTerrain(state, revision, [12, -4]);
|
|
544
|
+
* @override the caller owns the revision identity and the evaluated state being inspected
|
|
545
|
+
*/
|
|
546
|
+
declare function probeTerrain(state: ITerrainState, revision: string, at: readonly number[]): IPointObservation;
|
|
547
|
+
/**
|
|
548
|
+
* Normalise and validate one saved reference, exactly as a document commit would store it.
|
|
549
|
+
* @requires npm i -D @threenative/terrain
|
|
550
|
+
* @situation accept a bounded local reference image registration into the shared document
|
|
551
|
+
* @constraint authoring metadata only; rejects unknown fields, malformed points and unknown scale
|
|
552
|
+
* @example const reference = validateSpatialReference(JSON.parse(saved));
|
|
553
|
+
* @override a project owns the image file; only its hash and provenance travel in the document
|
|
554
|
+
*/
|
|
555
|
+
declare function validateSpatialReference(input: unknown): ISavedSpatialReference;
|
|
556
|
+
/**
|
|
557
|
+
* Read-only spatial inspection of one evaluated terrain revision.
|
|
558
|
+
* @requires npm i -D @threenative/terrain
|
|
559
|
+
* @situation measure a point, profile a transect or score a saved reference against the world
|
|
560
|
+
* @constraint authoring data only; queries outside the extent or without observations throw
|
|
561
|
+
* @example const observation = inspectSpatial(state, revision, { kind: "point", at: [0, 0] });
|
|
562
|
+
* @override the caller owns the revision identity and the evaluated state being inspected
|
|
563
|
+
*/
|
|
564
|
+
declare function inspectSpatial(state: ITerrainState, revision: string, query: ISpatialQuery): ISpatialObservation;
|
|
565
|
+
|
|
566
|
+
interface IAuthoringDocument {
|
|
567
|
+
version: 1;
|
|
568
|
+
recipe: ITerrainDocument;
|
|
569
|
+
placementOverrides?: Record<string, IPlacementOverride>;
|
|
570
|
+
/** Saved reference registrations. Authoring metadata: never evaluated, never exported. */
|
|
571
|
+
references?: ReturnType<typeof validateSpatialReference>[];
|
|
572
|
+
/** Saved editor observation cameras. Authoring metadata: never evaluated, never exported. */
|
|
573
|
+
cameras?: ISavedCamera[];
|
|
574
|
+
/** Which saved camera is live; absent or `null` is the ordinary editor camera. */
|
|
575
|
+
activeCamera?: string | null;
|
|
576
|
+
/** Preview environment overrides; absent fields keep the project's own render-source values. */
|
|
577
|
+
environment?: IEnvironment;
|
|
578
|
+
/** Registered models, images and environment files: metadata only, the files live on disk. */
|
|
579
|
+
assets?: IProjectAsset[];
|
|
580
|
+
/** Imported images that replace named surface inputs of the project's render source. */
|
|
581
|
+
surfaces?: ISurfaceMappings;
|
|
582
|
+
}
|
|
583
|
+
interface IEditorSnapshot {
|
|
584
|
+
revision: string;
|
|
585
|
+
document: IAuthoringDocument;
|
|
586
|
+
diagnostic: string | null;
|
|
587
|
+
}
|
|
588
|
+
interface IEditorActivation {
|
|
589
|
+
editorUrl: string;
|
|
590
|
+
projectId: string;
|
|
591
|
+
sessionId: string;
|
|
592
|
+
revision: string;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Project-local terrain document authority; no renderer or game dependency.
|
|
596
|
+
* @requires npm i -D @threenative/terrain
|
|
597
|
+
* @situation validate atomic revision-based project-local terrain document edits
|
|
598
|
+
* @constraint Node authoring only; documentPath is a configured absolute regular file; input is bounded to 64 MiB
|
|
599
|
+
* @example const document = new TerrainEditorDocument("/project/terrain/world.json"); const snapshot = document.snapshot();
|
|
600
|
+
* @override the project chooses its document path; revisions come from validated content
|
|
601
|
+
*/
|
|
602
|
+
declare class TerrainEditorDocument {
|
|
603
|
+
#private;
|
|
604
|
+
readonly path: string;
|
|
605
|
+
constructor(path: string);
|
|
606
|
+
snapshot(): IEditorSnapshot;
|
|
607
|
+
subscribe(listener: (snapshot: IEditorSnapshot) => void): () => void;
|
|
608
|
+
refresh(): void;
|
|
609
|
+
commit(input: unknown): IEditorSnapshot;
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Mount the optional terrain document API on an existing loopback Vite server.
|
|
613
|
+
* @requires npm i -D @threenative/terrain vite
|
|
614
|
+
* @situation share terrain recipe edits between an agent and the live terrain editor
|
|
615
|
+
* @constraint dev tooling only; serve a project-owned /terrain-editor/index.html; no runtime import
|
|
616
|
+
* @example const editor = terrainEditor({ documentPath: resolve("terrain/world.json") });
|
|
617
|
+
* @override documentPath and optional configured viewerUrl belong to the project
|
|
618
|
+
*/
|
|
619
|
+
declare function terrainEditor(options: {
|
|
620
|
+
documentPath: string;
|
|
621
|
+
viewerUrl?: string;
|
|
622
|
+
/** Where registered files are stored; defaults to `assets/` beside the document. */
|
|
623
|
+
assetsDir?: string;
|
|
624
|
+
/** Explicit byte, decoded-dimension and triangle limits for imports. */
|
|
625
|
+
assetLimits?: Partial<IAssetLimits>;
|
|
626
|
+
}): Plugin & {
|
|
627
|
+
activate(): Promise<IEditorActivation>;
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
export { terrainEditor as $, type IProfileObservation as A, type IProfileSample as B, type IProjectAsset as C, type IReferenceObservation as D, type ISavedSpatialReference as E, type ISpatialControlPoint as F, type ISpatialReference as G, type ISurfaceMapping as H, type IAuthoringDocument as I, type ISurfaceMappings as J, type ISurfaceReading as K, checkEnvironmentAssets as L, focusCamera as M, inspectSpatial as N, probeTerrain as O, runCameraOperation as P, runEnvironmentOperation as Q, sniff as R, surfaceSpace as S, validateAsset as T, validateAssets as U, validateCamera as V, validateCameras as W, validateEnvironment as X, validateSpatialReference as Y, validateSurfaces as Z, TerrainEditorDocument as _, type ISavedCamera as a, type IFocusRequest as b, type IFocusOutcome as c, type IFocusBounds as d, type IEnvironment as e, type IEditorActivation as f, type IEditorSnapshot as g, type ISpatialQuery as h, type ISpatialObservation as i, type ICameraOperation as j, type ICameraResult as k, type IEnvironmentOperation as l, type IEnvironmentResult as m, type IAssetOperation as n, type IAssetResult as o, type IAssetAdjust as p, type IAssetBounds as q, type IAssetKind as r, type IAssetLimits as s, type ICameraPose as t, type ICameraSet as u, type IFocusFraming as v, type IFocusResolver as w, type IFocusTarget as x, type IHexColour as y, type IPointObservation as z };
|