@hello-terrain/three 0.0.0-alpha.1 → 0.0.0-alpha.10

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/dist/index.d.ts CHANGED
@@ -1,6 +1,12 @@
1
- import { BufferGeometry } from 'three';
1
+ import { BufferGeometry, Ray, Vector3 as Vector3$1, Raycaster, Intersection } from 'three';
2
+ import * as three_webgpu from 'three/webgpu';
3
+ import { StorageArrayTexture, StorageTexture, Node, WebGPURenderer, StorageBufferAttribute, StorageBufferNode, UniformNode, Vector3, Vector3Like, InstancedMesh, NodeMaterial, ConstNode } from 'three/webgpu';
4
+ import Node$1 from 'three/src/nodes/core/Node.js';
2
5
  import * as three_src_nodes_TSL_js from 'three/src/nodes/TSL.js';
3
- import { Node } from 'three/webgpu';
6
+ import { ShaderCallNodeInternal } from 'three/src/nodes/TSL.js';
7
+ import * as _hello_terrain_work from '@hello-terrain/work';
8
+ import { TaskRef, Graph } from '@hello-terrain/work';
9
+ import * as three_tsl from 'three/tsl';
4
10
 
5
11
  /**
6
12
  * Custom geometry for terrain tiles with properly handled skirts.
@@ -28,17 +34,19 @@ declare class TerrainGeometry extends BufferGeometry {
28
34
  * | / | \ | / | \ |
29
35
  * o---o---o---o---o
30
36
  *
31
- * INNER GRID (consistent diagonal, no rotational symmetry):
32
- * o---o---o
33
- * | \ | \ |
34
- * o---o---o
35
- * | \ | \ |
36
- * o---o---o
37
+ * INNER GRID (alternating diagonals checkerboard pattern):
38
+ * o---o---o---o---o
39
+ * | \ | / | \ | / |
40
+ * o---o---o---o---o
41
+ * | / | \ | / | \ |
42
+ * o---o---o---o---o
43
+ * | \ | / | \ | / |
44
+ * o---o---o---o---o
37
45
  *
38
46
  * Where o = vertex
39
47
  * Each square cell is split into 2 triangles.
40
48
  * - Skirt cells (outer ring): diagonal flip based on quadrant for corner correctness
41
- * - Inner cells: consistent diagonal direction (all triangles "point" the same way)
49
+ * - Inner cells: alternating diagonal via (x+y)%2 to reduce interpolation artifacts
42
50
  *
43
51
  * Vertex layout (for innerSegments = 2):
44
52
  *
@@ -88,6 +96,791 @@ declare class TerrainGeometry extends BufferGeometry {
88
96
  private generateNormals;
89
97
  }
90
98
 
99
+ type TerrainFieldStorageBackendType = "array-texture" | "atlas" | "texture-3d";
100
+ type TerrainFieldStorageFormat = "rgba16float" | "rgba32float";
101
+ type TerrainFieldStorageOptions = {
102
+ backend?: TerrainFieldStorageBackendType;
103
+ filter?: "nearest" | "linear";
104
+ format?: TerrainFieldStorageFormat;
105
+ };
106
+ interface TerrainFieldStorage {
107
+ readonly backendType: TerrainFieldStorageBackendType;
108
+ readonly edgeVertexCount: number;
109
+ readonly tileCount: number;
110
+ readonly texture: StorageArrayTexture | StorageTexture;
111
+ uv(ix: Node, iy: Node, tileIndex: Node): Node;
112
+ texel(ix: Node, iy: Node, tileIndex: Node): Node;
113
+ /** UV-based filtered sample. `u, v` in [0, 1] tile-local space. */
114
+ sample(u: Node, v: Node, tileIndex: Node): Node;
115
+ resize(width: number, height: number, tileCount: number): void;
116
+ }
117
+ declare function ArrayTextureBackend(edgeVertexCount: number, tileCount: number, options: Required<Pick<TerrainFieldStorageOptions, "format" | "filter">>): TerrainFieldStorage;
118
+ declare function AtlasBackend(edgeVertexCount: number, tileCount: number, options: Required<Pick<TerrainFieldStorageOptions, "format" | "filter">>): TerrainFieldStorage;
119
+ /**
120
+ * Placeholder backend for future true 3D storage-texture support in Three.js.
121
+ * We keep it present to preserve the backend API shape.
122
+ */
123
+ declare function Texture3DBackend(edgeVertexCount: number, tileCount: number, options: Required<Pick<TerrainFieldStorageOptions, "format" | "filter">>): TerrainFieldStorage;
124
+ declare function createTerrainFieldStorage(edgeVertexCount: number, tileCount: number, renderer?: WebGPURenderer, options?: TerrainFieldStorageOptions): TerrainFieldStorage;
125
+ declare function storeTerrainField(storage: TerrainFieldStorage, ix: Node, iy: Node, tileIndex: Node, value: Node): Node;
126
+ declare function loadTerrainField(storage: TerrainFieldStorage, ix: Node, iy: Node, tileIndex: Node): Node;
127
+ declare function loadTerrainFieldElevation(storage: TerrainFieldStorage, ix: Node, iy: Node, tileIndex: Node): Node;
128
+ declare function loadTerrainFieldNormal(storage: TerrainFieldStorage, ix: Node, iy: Node, tileIndex: Node): Node;
129
+ /**
130
+ * UV-based filtered sample. `u, v` are in [0, 1] tile-local space.
131
+ * Respects the filter mode (nearest / linear) set on the storage.
132
+ */
133
+ declare function sampleTerrainField(storage: TerrainFieldStorage, u: Node, v: Node, tileIndex: Node): Node;
134
+ declare function sampleTerrainFieldElevation(storage: TerrainFieldStorage, u: Node, v: Node, tileIndex: Node): Node;
135
+ declare function sampleTerrainFieldNormal(storage: TerrainFieldStorage, u: Node, v: Node, tileIndex: Node): Node;
136
+ declare function packTerrainFieldSample(height: Node, normalXZ: Node, extra?: Node): Node;
137
+
138
+ interface ElevationParams {
139
+ worldPosition: Node$1;
140
+ rootSize: Node$1;
141
+ rootUV: Node$1;
142
+ tileUV: Node$1;
143
+ tileLevel: Node$1;
144
+ tileSize: Node$1;
145
+ tileOriginVec2: Node$1;
146
+ nodeIndex: Node$1;
147
+ }
148
+ type ElevationCallback = (params: ElevationParams) => Node$1;
149
+
150
+ interface TerrainUniformsParams {
151
+ rootSize: number;
152
+ rootOrigin: Vector3Like;
153
+ innerTileSegments: number;
154
+ skirtScale: number;
155
+ elevationScale: number;
156
+ instanceId: string;
157
+ }
158
+ interface TerrainUniformsContext {
159
+ uRootOrigin: UniformNode<Vector3>;
160
+ uRootSize: UniformNode<number>;
161
+ uInnerTileSegments: UniformNode<number>;
162
+ uSkirtScale: UniformNode<number>;
163
+ uElevationScale: UniformNode<number>;
164
+ }
165
+ interface LeafStorageState {
166
+ data: Int32Array<ArrayBuffer>;
167
+ attribute: StorageBufferAttribute;
168
+ node: StorageBufferNode;
169
+ }
170
+
171
+ interface GpuSpatialIndexContext {
172
+ data: Uint32Array<ArrayBuffer>;
173
+ size: number;
174
+ mask: number;
175
+ stampGen: UniformNode<number>;
176
+ attribute: StorageBufferAttribute;
177
+ node: StorageBufferNode;
178
+ }
179
+ interface TerrainSampler {
180
+ sampleElevation: (worldX: Node, worldZ: Node) => Node;
181
+ sampleNormal: (worldX: Node, worldZ: Node) => Node;
182
+ sampleTerrain: (worldX: Node, worldZ: Node) => Node;
183
+ sampleValidity: (worldX: Node, worldZ: Node) => Node;
184
+ evaluateElevation: (worldX: Node, worldZ: Node) => Node;
185
+ evaluateNormal: (worldX: Node, worldZ: Node, epsilon?: Node) => Node;
186
+ }
187
+ interface CreateTerrainSamplerParams {
188
+ terrainFieldStorage: TerrainFieldStorage;
189
+ spatialIndex: GpuSpatialIndexContext;
190
+ uniforms: TerrainUniformsContext;
191
+ elevationCallback: ElevationCallback;
192
+ }
193
+ interface TerrainSample {
194
+ elevation: number;
195
+ normal: Vector3$1;
196
+ valid: boolean;
197
+ }
198
+ interface TerrainElevationSample {
199
+ elevation: number;
200
+ valid: boolean;
201
+ }
202
+ interface TerrainSampleBatch {
203
+ elevations: Float32Array;
204
+ normals: Float32Array;
205
+ valid: Uint8Array;
206
+ generation: number;
207
+ }
208
+ interface TerrainTile {
209
+ level: number;
210
+ x: number;
211
+ y: number;
212
+ index: number;
213
+ }
214
+ interface TerrainTileBounds extends TerrainTile {
215
+ minElevation: number;
216
+ maxElevation: number;
217
+ }
218
+ interface ElevationRange {
219
+ min: number;
220
+ max: number;
221
+ }
222
+ interface TerrainQuery {
223
+ getElevation(worldX: number, worldZ: number): number | null;
224
+ getNormal(worldX: number, worldZ: number): Vector3$1 | null;
225
+ getTile(worldX: number, worldZ: number): TerrainTile | null;
226
+ getTileBounds(worldX: number, worldZ: number): TerrainTileBounds | null;
227
+ getGlobalElevationRange(): ElevationRange | null;
228
+ sampleTerrain(worldX: number, worldZ: number): TerrainSample;
229
+ sampleTerrainBatch(positions: Float32Array): TerrainSampleBatch;
230
+ readonly generation: number;
231
+ }
232
+ interface RaycastOptions {
233
+ maxSteps?: number;
234
+ refinementSteps?: number;
235
+ maxDistance?: number;
236
+ }
237
+ interface TerrainRaycastResult {
238
+ position: Vector3$1;
239
+ normal: Vector3$1;
240
+ distance: number;
241
+ }
242
+ interface TerrainRaycast {
243
+ pick(ray: Ray, options?: RaycastOptions): TerrainRaycastResult | null;
244
+ }
245
+
246
+ type TerrainMeshParams = {
247
+ innerTileSegments: number;
248
+ maxNodes: number;
249
+ material: NodeMaterial;
250
+ };
251
+ declare class TerrainMesh extends InstancedMesh {
252
+ private _innerTileSegments;
253
+ private _maxNodes;
254
+ terrainRaycast: TerrainRaycast | null;
255
+ constructor(params?: Partial<TerrainMeshParams>);
256
+ get innerTileSegments(): number;
257
+ set innerTileSegments(tileSegments: number);
258
+ get maxNodes(): number;
259
+ set maxNodes(maxNodes: number);
260
+ raycast(raycaster: Raycaster, intersects: Intersection[]): void;
261
+ }
262
+
263
+ interface TileBoundsContext {
264
+ data: Float32Array<ArrayBuffer>;
265
+ attribute: StorageBufferAttribute;
266
+ node: StorageBufferNode;
267
+ }
268
+
269
+ type ComputeStageCallback = (nodeIndex: Node, globalVertexIndex: Node, uv: Node, localCoordinates: Node, texelSize: Node) => void;
270
+ type ComputePipeline = ComputeStageCallback[];
271
+
272
+ /** Default compile task — uses terrainFieldStageTask as the leaf. */
273
+ declare const compileComputeTask: _hello_terrain_work.Task<{
274
+ execute: (renderer: WebGPURenderer, instanceCount: number) => void;
275
+ }, string, unknown>;
276
+ /** Default execute task — dispatches the compiled kernel. */
277
+ declare const executeComputeTask: _hello_terrain_work.Task<any, string, {
278
+ renderer: WebGPURenderer;
279
+ }>;
280
+ /**
281
+ * Factory for user-extensible pipelines.
282
+ *
283
+ * Users who add custom compute stages create their own stage tasks using
284
+ * the accumulation pattern (`get()` predecessor, spread, append), then pass
285
+ * their leaf stage to this helper to get compile + execute tasks.
286
+ *
287
+ * @example
288
+ * ```ts
289
+ * const erosionStageTask = task((get, work) => {
290
+ * const upstream = get(elevationFieldStageTask);
291
+ * return work((): ComputePipeline => [
292
+ * ...upstream,
293
+ * (nodeIndex, globalVertexIndex, uv) => {
294
+ * // custom erosion logic
295
+ * },
296
+ * ]);
297
+ * });
298
+ *
299
+ * const { compile, execute } = createComputePipelineTasks(erosionStageTask);
300
+ * ```
301
+ */
302
+ declare function createComputePipelineTasks(leafStageTask: TaskRef<ComputePipeline>): {
303
+ compile: _hello_terrain_work.Task<{
304
+ execute: (renderer: WebGPURenderer, instanceCount: number) => void;
305
+ }, string, unknown>;
306
+ execute: _hello_terrain_work.Task<any, string, {
307
+ renderer: WebGPURenderer;
308
+ }>;
309
+ };
310
+
311
+ declare const Dir: {
312
+ readonly LEFT: 0;
313
+ readonly RIGHT: 1;
314
+ readonly TOP: 2;
315
+ readonly BOTTOM: 3;
316
+ };
317
+ type Dir = (typeof Dir)[keyof typeof Dir];
318
+ declare const U32_EMPTY = 4294967295;
319
+ type TileId = {
320
+ /** 0 for flat terrain; 0..5 for cube-sphere faces */
321
+ space: number;
322
+ level: number;
323
+ /** tile coordinate at this level (signed to support infinite surfaces) */
324
+ x: number;
325
+ /** tile coordinate at this level (signed to support infinite surfaces) */
326
+ y: number;
327
+ };
328
+ type TileBounds = {
329
+ /** camera-relative center */
330
+ cx: number;
331
+ cy: number;
332
+ cz: number;
333
+ /** conservative radius */
334
+ r: number;
335
+ };
336
+ type Surface = {
337
+ spaceCount: number;
338
+ /** maximum number of roots returned by `rootTiles` */
339
+ maxRootCount: number;
340
+ /**
341
+ * Compute the same-level neighbor TileId in the requested direction.
342
+ * Returns false if the neighbor is outside the valid topology.
343
+ *
344
+ * IMPORTANT: This must handle cross-space edges in the future (cube-sphere).
345
+ */
346
+ neighborSameLevel(tile: TileId, dir: Dir, out: TileId): boolean;
347
+ /**
348
+ * Conservative camera-relative bounds for LOD decisions.
349
+ * Avoids absolute world coordinates so Earth-scale worlds remain stable.
350
+ */
351
+ tileBounds(tile: TileId, cameraOrigin: {
352
+ x: number;
353
+ y: number;
354
+ z: number;
355
+ }, out: TileBounds): void;
356
+ /**
357
+ * Fill root tiles for the current frame and return the count.
358
+ * Implementations should write level-0 tiles into `out[0..count)`.
359
+ */
360
+ rootTiles(cameraOrigin: {
361
+ x: number;
362
+ y: number;
363
+ z: number;
364
+ }, out: TileId[]): number;
365
+ };
366
+ type LeafSet = {
367
+ /** maximum number of leaves that fit in the buffers */
368
+ capacity: number;
369
+ /** number of valid leaf entries in this frame */
370
+ count: number;
371
+ space: Uint8Array;
372
+ level: Uint8Array;
373
+ x: Int32Array;
374
+ y: Int32Array;
375
+ };
376
+ declare function allocLeafSet(capacity: number): LeafSet;
377
+ declare function resetLeafSet(leaves: LeafSet): void;
378
+ type SeamTable = {
379
+ /** maximum number of leaves the table can describe */
380
+ capacity: number;
381
+ /** number of leaves described (typically equals leaves.count) */
382
+ count: number;
383
+ /** fixed stride per leaf, in u32 entries */
384
+ stride: 8;
385
+ /**
386
+ * neighbors in leaf-list index space
387
+ * layout: neighbors[leafIndex * 8 + edge*2 + slot]
388
+ * edge order: LEFT, RIGHT, TOP, BOTTOM
389
+ * slot: 0..1 (at most 2 neighbors per edge under 2:1 balance)
390
+ */
391
+ neighbors: Uint32Array;
392
+ };
393
+ declare function allocSeamTable(capacity: number): SeamTable;
394
+ declare function resetSeamTable(seams: SeamTable): void;
395
+ type LodMode = "distance" | "screen";
396
+ type UpdateParams = {
397
+ cameraOrigin: {
398
+ x: number;
399
+ y: number;
400
+ z: number;
401
+ };
402
+ /**
403
+ * Terrain elevation at the camera's XZ position (from previous frame).
404
+ * Subtracted from `cameraOrigin.y` during refinement so that LOD
405
+ * distance is measured relative to the terrain surface, not the datum.
406
+ */
407
+ elevationAtCameraXZ?: number;
408
+ /**
409
+ * Controls how subdivision decisions are made.
410
+ * `distance` is the initial focus; `screen` is supported for future parity.
411
+ */
412
+ mode?: LodMode;
413
+ /**
414
+ * Distance-based refinement threshold.
415
+ * Interpretation is criteria-dependent; keep it stable across surfaces by using bounds.
416
+ */
417
+ distanceFactor?: number;
418
+ /** Screen-space projection factor = screenHeight / (2*tan(fovY/2)) */
419
+ projectionFactor?: number;
420
+ /** Target pixel radius/size threshold for screen-space refinement */
421
+ targetPixels?: number;
422
+ /** Prevent flicker by separating split/merge thresholds (0..1 typical) */
423
+ hysteresis?: number;
424
+ };
425
+ type QuadtreeConfig = {
426
+ maxNodes: number;
427
+ maxLevel: number;
428
+ };
429
+
430
+ type NodeStore = {
431
+ maxNodes: number;
432
+ nodesUsed: number;
433
+ /** generation stamping to avoid clearing buffers */
434
+ currentGen: number;
435
+ gen: Uint16Array;
436
+ space: Uint8Array;
437
+ level: Uint8Array;
438
+ x: Int32Array;
439
+ y: Int32Array;
440
+ /** sentinel U32_EMPTY means no children; otherwise children are [firstChild..firstChild+3] */
441
+ firstChild: Uint32Array;
442
+ flags: Uint8Array;
443
+ /** root node id per space */
444
+ roots: Uint32Array;
445
+ };
446
+
447
+ type SpatialIndex = {
448
+ size: number;
449
+ mask: number;
450
+ stampGen: number;
451
+ stamp: Uint16Array;
452
+ keysSpace: Uint8Array;
453
+ keysLevel: Uint8Array;
454
+ keysX: Uint32Array;
455
+ keysY: Uint32Array;
456
+ values: Uint32Array;
457
+ };
458
+ declare function createSpatialIndex(maxEntries: number): SpatialIndex;
459
+
460
+ /**
461
+ * Build a spatial index for the current LeafSet.
462
+ * Maps (space, level, x, y) -> leafIndex.
463
+ *
464
+ * Allocation-free if `out` is provided.
465
+ */
466
+ declare function buildLeafIndex(leaves: LeafSet, out?: SpatialIndex): SpatialIndex;
467
+
468
+ type QuadtreeState = {
469
+ cfg: QuadtreeConfig;
470
+ store: NodeStore;
471
+ /** default reusable leaf buffers (capacity = cfg.maxNodes) */
472
+ leaves: LeafSet;
473
+ /** internal: node id per leaf entry (parallel to leaves.* arrays) */
474
+ leafNodeIds: Uint32Array;
475
+ /** reusable leaf spatial index (capacity = cfg.maxNodes) */
476
+ leafIndex: SpatialIndex;
477
+ /** traversal scratch */
478
+ stack: Uint32Array;
479
+ /** root nodes for this frame */
480
+ rootNodeIds: Uint32Array;
481
+ rootCount: number;
482
+ /** split scheduling scratch (dedupe without allocations) */
483
+ splitQueue: Uint32Array;
484
+ splitStamp: Uint16Array;
485
+ splitGen: number;
486
+ /** scratch objects to avoid allocations */
487
+ scratchTile: TileId;
488
+ scratchNeighbor: TileId;
489
+ scratchBounds: TileBounds;
490
+ scratchRootTiles: TileId[];
491
+ /** surface space count is fixed for a given state */
492
+ spaceCount: number;
493
+ };
494
+ declare function createState(cfg: QuadtreeConfig, surface: Surface): QuadtreeState;
495
+ declare function beginUpdate(state: QuadtreeState, surface: Surface, params: UpdateParams): void;
496
+
497
+ /**
498
+ * Update the quadtree for the given surface + camera parameters.
499
+ *
500
+ * Produces a LeafSet of TileIds (SoA typed arrays).
501
+ */
502
+ declare function update(state: QuadtreeState, surface: Surface, params: UpdateParams, outLeaves?: LeafSet): LeafSet;
503
+
504
+ /**
505
+ * Build a fixed-width seam/neighbor table for balanced leaves (2:1).
506
+ *
507
+ * Output neighbors are leaf-list indices, with U32_EMPTY for missing entries.
508
+ * Layout: neighbors[leafIndex * 8 + edge*2 + slot].
509
+ */
510
+ declare function buildSeams2to1(surface: Surface, leaves: LeafSet, outSeams: SeamTable, outIndex?: SpatialIndex): SeamTable;
511
+
512
+ type FlatSurfaceConfig = {
513
+ /**
514
+ * World-space size of the root tile edge.
515
+ * The root tile covers [-rootSize/2, +rootSize/2] around origin in X/Z.
516
+ */
517
+ rootSize: number;
518
+ origin: {
519
+ x: number;
520
+ y: number;
521
+ z: number;
522
+ };
523
+ /** optional conservative vertical extent, included in bounds radius */
524
+ maxHeight?: number;
525
+ };
526
+ declare function createFlatSurface(cfg: FlatSurfaceConfig): Surface;
527
+
528
+ type InfiniteFlatSurfaceConfig = {
529
+ rootSize: number;
530
+ origin: {
531
+ x: number;
532
+ y: number;
533
+ z: number;
534
+ };
535
+ /** optional conservative vertical extent, included in bounds radius */
536
+ maxHeight?: number;
537
+ /** half-width of root grid in root tiles (1 => 3x3 roots) */
538
+ rootGridRadius?: number;
539
+ };
540
+ declare function createInfiniteFlatSurface(cfg: InfiniteFlatSurfaceConfig): Surface;
541
+
542
+ type CubeSphereSurfaceConfig = {
543
+ radius: number;
544
+ maxHeight?: number;
545
+ };
546
+ /**
547
+ * Placeholder cube-sphere surface.
548
+ *
549
+ * This exists to localize future planet work behind the `Surface` interface.
550
+ * Topology remapping across face edges is intentionally TODO.
551
+ */
552
+ declare function createCubeSphereSurface(_cfg: CubeSphereSurfaceConfig): Surface;
553
+
554
+ declare function createTileCompute(leafStorage: LeafStorageState, uniforms: TerrainUniformsContext): {
555
+ tileLevel: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node]>;
556
+ tileOriginVec2: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node]>;
557
+ tileSize: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node]>;
558
+ rootUVCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node, number | Node, number | Node]>;
559
+ tileVertexWorldPositionCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node, number | Node, number | Node]>;
560
+ };
561
+
562
+ type TerrainQueryConfig = {
563
+ rootSize: number;
564
+ originX: number;
565
+ originY: number;
566
+ originZ: number;
567
+ innerTileSegments: number;
568
+ elevationScale: number;
569
+ maxLevel: number;
570
+ };
571
+ interface CpuTerrainCache {
572
+ readonly generation: number;
573
+ readonly ready: boolean;
574
+ updateConfig(config: TerrainQueryConfig): void;
575
+ triggerReadback(renderer: WebGPURenderer, attribute: StorageBufferAttribute, spatialIndex: SpatialIndex, boundsAttribute?: StorageBufferAttribute, activeLeafCount?: number): void;
576
+ getElevation(worldX: number, worldZ: number): number | null;
577
+ getNormal(worldX: number, worldZ: number): Vector3$1 | null;
578
+ getTile(worldX: number, worldZ: number): TerrainTile | null;
579
+ getTileBounds(worldX: number, worldZ: number): TerrainTileBounds | null;
580
+ getGlobalElevationRange(): ElevationRange | null;
581
+ sampleTerrainBatch(positions: Float32Array): TerrainSampleBatch;
582
+ sampleTerrain(worldX: number, worldZ: number): TerrainSample;
583
+ }
584
+
585
+ interface QuadtreeConfigState {
586
+ state: QuadtreeState;
587
+ surface: Surface;
588
+ }
589
+ interface LeafGpuBufferState extends LeafStorageState {
590
+ count: number;
591
+ }
592
+ interface ElevationFieldContext {
593
+ data: Float32Array<ArrayBuffer>;
594
+ attribute: StorageBufferAttribute;
595
+ node: StorageBufferNode;
596
+ }
597
+ interface TerrainQueryContext {
598
+ cache: CpuTerrainCache;
599
+ query: TerrainQuery;
600
+ shapeKey: string;
601
+ }
602
+ /** Task refs for the standard terrain pipeline. */
603
+ interface TerrainTasks {
604
+ instanceId: TaskRef<string>;
605
+ quadtreeConfig: TaskRef<QuadtreeConfigState>;
606
+ quadtreeUpdate: TaskRef<LeafSet>;
607
+ surface: TaskRef<Surface>;
608
+ leafStorage: TaskRef<LeafStorageState>;
609
+ leafGpuBuffer: TaskRef<LeafGpuBufferState>;
610
+ gpuSpatialIndexStorage: TaskRef<GpuSpatialIndexContext>;
611
+ gpuSpatialIndexUpload: TaskRef<GpuSpatialIndexContext>;
612
+ createUniforms: TaskRef<TerrainUniformsContext>;
613
+ updateUniforms: TaskRef<TerrainUniformsContext>;
614
+ positionNode: TaskRef<ShaderCallNodeInternal>;
615
+ createElevationFieldContext: TaskRef<ElevationFieldContext>;
616
+ createTileNodes: TaskRef<ReturnType<typeof createTileCompute>>;
617
+ createTerrainFieldTexture: TaskRef<TerrainFieldStorage>;
618
+ createTerrainSampler: TaskRef<TerrainSampler>;
619
+ elevationFieldStage: TaskRef<ComputePipeline>;
620
+ terrainFieldStage: TaskRef<ComputePipeline>;
621
+ compileCompute: TaskRef<{
622
+ execute: (renderer: WebGPURenderer, instanceCount: number) => void;
623
+ }>;
624
+ executeCompute: TaskRef<void | (() => void)>;
625
+ tileBoundsContext: TaskRef<TileBoundsContext & {
626
+ kernel: unknown;
627
+ }>;
628
+ tileBoundsReduction: TaskRef<TileBoundsContext>;
629
+ terrainQuery: TaskRef<TerrainQueryContext>;
630
+ terrainReadback: TaskRef<void>;
631
+ terrainRaycast: TaskRef<TerrainRaycast>;
632
+ }
633
+ type TerrainGraph = Graph<string, {
634
+ renderer: WebGPURenderer;
635
+ }>;
636
+
637
+ declare const createElevationFieldContextTask: _hello_terrain_work.Task<{
638
+ data: Float32Array<ArrayBuffer>;
639
+ attribute: StorageBufferAttribute;
640
+ node: three_webgpu.StorageBufferNode;
641
+ }, string, unknown>;
642
+ declare const tileNodesTask: _hello_terrain_work.Task<{
643
+ tileLevel: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
644
+ tileOriginVec2: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
645
+ tileSize: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
646
+ rootUVCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node, number | three_webgpu.Node, number | three_webgpu.Node]>;
647
+ tileVertexWorldPositionCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node, number | three_webgpu.Node, number | three_webgpu.Node]>;
648
+ }, string, unknown>;
649
+ /**
650
+ * Root compute stage — generates elevation data and writes to the
651
+ * elevation field storage buffer. Returns a single-element `ComputePipeline`.
652
+ */
653
+ declare const elevationFieldStageTask: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
654
+
655
+ declare const gpuSpatialIndexStorageTask: _hello_terrain_work.Task<GpuSpatialIndexContext, string, unknown>;
656
+ declare const gpuSpatialIndexUploadTask: _hello_terrain_work.Task<GpuSpatialIndexContext, string, unknown>;
657
+
658
+ /** Generates a unique instance ID per graph (cached once). */
659
+ declare const instanceIdTask: _hello_terrain_work.Task<`${string}-${string}-${string}-${string}-${string}`, string, unknown>;
660
+
661
+ declare const createTerrainFieldTextureTask: _hello_terrain_work.Task<any, string, {
662
+ renderer: WebGPURenderer;
663
+ }>;
664
+ /**
665
+ * Normal field compute stage — reads height neighbors from the elevation field
666
+ * buffer, computes surface normals via central differences, packs XZ
667
+ * components into a u32 via `packHalf2x16`, and writes to the normal field
668
+ * storage buffer.
669
+ *
670
+ * Accumulates the upstream elevation pipeline via `get(elevationFieldStageTask)`.
671
+ */
672
+ declare const terrainFieldStageTask: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
673
+
674
+ declare const createTerrainSamplerTask: _hello_terrain_work.Task<TerrainSampler, string, unknown>;
675
+
676
+ declare const terrainQueryTask: _hello_terrain_work.Task<TerrainQueryContext, string, unknown>;
677
+ declare const terrainReadbackTask: _hello_terrain_work.Task<any, string, {
678
+ renderer: WebGPURenderer;
679
+ }>;
680
+
681
+ declare const terrainRaycastTask: _hello_terrain_work.Task<TerrainRaycast, string, unknown>;
682
+
683
+ /** Root tile size in world units. */
684
+ declare const rootSize: _hello_terrain_work.ParamRef<number>;
685
+ /** World-space origin of the terrain. */
686
+ declare const origin: _hello_terrain_work.ParamRef<{
687
+ x: number;
688
+ y: number;
689
+ z: number;
690
+ }>;
691
+ /**
692
+ * Number of segments per inner tile edge.
693
+ * Effective edge vertex count is `innerTileSegments + 3`.
694
+ */
695
+ declare const innerTileSegments: _hello_terrain_work.ParamRef<number>;
696
+ /** Skirt scale factor. */
697
+ declare const skirtScale: _hello_terrain_work.ParamRef<number>;
698
+ /** Elevation vertical scale. */
699
+ declare const elevationScale: _hello_terrain_work.ParamRef<number>;
700
+ /** Maximum quadtree nodes. */
701
+ declare const maxNodes: _hello_terrain_work.ParamRef<number>;
702
+ /** Maximum quadtree subdivision level. */
703
+ declare const maxLevel: _hello_terrain_work.ParamRef<number>;
704
+ /** Quadtree update configuration (camera, mode, etc.). */
705
+ declare const quadtreeUpdate: _hello_terrain_work.ParamRef<UpdateParams>;
706
+ /** Optional custom terrain surface; defaults to bounded flat surface when null. */
707
+ declare const surface: _hello_terrain_work.ParamRef<Surface | null>;
708
+ /** Terrain field texture filter mode. */
709
+ declare const terrainFieldFilter: _hello_terrain_work.ParamRef<"nearest" | "linear">;
710
+ /** Terrain elevation control function (per vertex, in gpu compute) */
711
+ declare const elevationFn: _hello_terrain_work.ParamRef<ElevationCallback>;
712
+
713
+ /**
714
+ * Builds the TSL position node for the terrain shader.
715
+ *
716
+ * Depends on leafStorageTask (buffer objects), updateUniformsTask
717
+ * (uniform nodes), and createTerrainFieldTextureTask (combined terrain field storage).
718
+ *
719
+ * The position node reads packed terrain samples (height + normal.xz)
720
+ * per-vertex and assigns them to the vNormal
721
+ * varying for use in the fragment shader.
722
+ *
723
+ * These only change when their GPU resources are recreated
724
+ * (e.g. buffer resize), so this task stays cached during normal quadtree
725
+ * updates — no unnecessary shader rebuilds.
726
+ */
727
+ declare const positionNodeTask: _hello_terrain_work.Task<three_src_nodes_TSL_js.ShaderCallNodeInternal, string, unknown>;
728
+
729
+ /**
730
+ * Derives the terrain surface from `rootSize` and `origin`.
731
+ * Automatically recomputes when either param changes, keeping the
732
+ * quadtree refinement in sync with the GPU-side tile positioning.
733
+ */
734
+ declare const surfaceTask: _hello_terrain_work.Task<Surface, string, unknown>;
735
+ declare const quadtreeConfigTask: _hello_terrain_work.Task<QuadtreeConfigState, string, unknown>;
736
+ declare const quadtreeUpdateTask: _hello_terrain_work.Task<LeafSet, string, unknown>;
737
+ /**
738
+ * Creates the GPU storage buffer objects. Recreated when maxNodes changes.
739
+ *
740
+ * positionNodeTask depends on this (not leafGpuBufferTask) so
741
+ * the shader is only rebuilt when the buffer is resized, not on every
742
+ * quadtree update.
743
+ */
744
+ declare const leafStorageTask: _hello_terrain_work.Task<LeafStorageState, string, unknown>;
745
+ declare const leafGpuBufferTask: _hello_terrain_work.Task<{
746
+ count: number;
747
+ data: Int32Array<ArrayBuffer>;
748
+ attribute: three_webgpu.StorageBufferAttribute;
749
+ node: three_webgpu.StorageBufferNode;
750
+ }, string, unknown>;
751
+
752
+ declare function createTerrainUniforms(params: TerrainUniformsParams): TerrainUniformsContext;
753
+
754
+ /**
755
+ * Creates the terrain uniform nodes once. Downstream tasks capture
756
+ * references to these nodes in shader graphs, so the same instances
757
+ * must persist across runs.
758
+ */
759
+ declare const createUniformsTask: _hello_terrain_work.Task<TerrainUniformsContext, string, unknown>;
760
+ /**
761
+ * Updates the terrain uniform values each run. Reads the persisted uniform
762
+ * nodes from createUniformsTask and writes the latest param values.
763
+ */
764
+ declare const updateUniformsTask: _hello_terrain_work.Task<TerrainUniformsContext, string, unknown>;
765
+
766
+ declare function terrainGraph(): TerrainGraph;
767
+ /** All terrain task refs for direct access. */
768
+ declare const terrainTasks: {
769
+ readonly instanceId: _hello_terrain_work.Task<`${string}-${string}-${string}-${string}-${string}`, string, unknown>;
770
+ readonly quadtreeConfig: _hello_terrain_work.Task<QuadtreeConfigState, string, unknown>;
771
+ readonly quadtreeUpdate: _hello_terrain_work.Task<LeafSet, string, unknown>;
772
+ readonly leafStorage: _hello_terrain_work.Task<LeafStorageState, string, unknown>;
773
+ readonly surface: _hello_terrain_work.Task<Surface, string, unknown>;
774
+ readonly leafGpuBuffer: _hello_terrain_work.Task<{
775
+ count: number;
776
+ data: Int32Array<ArrayBuffer>;
777
+ attribute: three_webgpu.StorageBufferAttribute;
778
+ node: three_webgpu.StorageBufferNode;
779
+ }, string, unknown>;
780
+ readonly gpuSpatialIndexStorage: _hello_terrain_work.Task<GpuSpatialIndexContext, string, unknown>;
781
+ readonly gpuSpatialIndexUpload: _hello_terrain_work.Task<GpuSpatialIndexContext, string, unknown>;
782
+ readonly createUniforms: _hello_terrain_work.Task<TerrainUniformsContext, string, unknown>;
783
+ readonly updateUniforms: _hello_terrain_work.Task<TerrainUniformsContext, string, unknown>;
784
+ readonly positionNode: _hello_terrain_work.Task<three_src_nodes_TSL_js.ShaderCallNodeInternal, string, unknown>;
785
+ readonly createElevationFieldContext: _hello_terrain_work.Task<{
786
+ data: Float32Array<ArrayBuffer>;
787
+ attribute: three_webgpu.StorageBufferAttribute;
788
+ node: three_webgpu.StorageBufferNode;
789
+ }, string, unknown>;
790
+ readonly createTileNodes: _hello_terrain_work.Task<{
791
+ tileLevel: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
792
+ tileOriginVec2: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
793
+ tileSize: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
794
+ rootUVCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node, number | three_webgpu.Node, number | three_webgpu.Node]>;
795
+ tileVertexWorldPositionCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node, number | three_webgpu.Node, number | three_webgpu.Node]>;
796
+ }, string, unknown>;
797
+ readonly createTerrainFieldTexture: _hello_terrain_work.Task<any, string, {
798
+ renderer: WebGPURenderer;
799
+ }>;
800
+ readonly createTerrainSampler: _hello_terrain_work.Task<TerrainSampler, string, unknown>;
801
+ readonly elevationFieldStage: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
802
+ readonly terrainFieldStage: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
803
+ readonly compileCompute: _hello_terrain_work.Task<{
804
+ execute: (renderer: WebGPURenderer, instanceCount: number) => void;
805
+ }, string, unknown>;
806
+ readonly executeCompute: _hello_terrain_work.Task<any, string, {
807
+ renderer: WebGPURenderer;
808
+ }>;
809
+ readonly tileBoundsContext: _hello_terrain_work.Task<TileBoundsContext & {
810
+ kernel: ReturnType<(elevationFieldNode: three_webgpu.StorageBufferNode, boundsNode: three_webgpu.StorageBufferNode, verticesPerNode: number) => three_webgpu.ComputeNode>;
811
+ }, string, unknown>;
812
+ readonly tileBoundsReduction: _hello_terrain_work.Task<any, string, {
813
+ renderer: WebGPURenderer;
814
+ }>;
815
+ readonly terrainQuery: _hello_terrain_work.Task<TerrainQueryContext, string, unknown>;
816
+ readonly terrainReadback: _hello_terrain_work.Task<any, string, {
817
+ renderer: WebGPURenderer;
818
+ }>;
819
+ readonly terrainRaycast: _hello_terrain_work.Task<TerrainRaycast, string, unknown>;
820
+ };
821
+
822
+ type ComputeDeviceLimits = {
823
+ maxWorkgroupSizeX: number;
824
+ maxWorkgroupSizeY: number;
825
+ maxWorkgroupInvocations: number;
826
+ };
827
+ declare function getDeviceComputeLimits(renderer: WebGPURenderer): ComputeDeviceLimits;
828
+
829
+ declare function createTerrainSampler(params: CreateTerrainSamplerParams): TerrainSampler;
830
+
831
+ declare function createTerrainQuery(cache: CpuTerrainCache): TerrainQuery;
832
+
833
+ type CpuRaycastConfig = {
834
+ rootSize: number;
835
+ originX: number;
836
+ originZ: number;
837
+ minY: number;
838
+ maxY: number;
839
+ };
840
+
841
+ type TerrainRaycastConfig = CpuRaycastConfig;
842
+ declare function createTerrainRaycast(params: {
843
+ getTerrainQuery: () => TerrainQuery | null;
844
+ getConfig: () => TerrainRaycastConfig;
845
+ }): TerrainRaycast;
846
+
847
+ /**
848
+ * Maps a value or node from texture space [0, 1] to vector space [-1, 1].
849
+ *
850
+ * @param value - The node or value in the range [0, 1].
851
+ * @returns A node mapping the input value to the range [-1, 1].
852
+ */
853
+ declare const textureSpaceToVectorSpace: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node$1]>;
854
+ /**
855
+ * Maps a value or node from vector space [-1, 1] to texture space [0, 1].
856
+ *
857
+ * @param value - The node or value in the range [-1, 1].
858
+ * @returns A node mapping the input value to the range [0, 1].
859
+ */
860
+ declare const vectorSpaceToTextureSpace: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node$1]>;
861
+ /**
862
+ * Blends two normal maps using the Reoriented Normal Mapping technique.
863
+ * This is the same algorithm used by Unreal Engine's BlendAngleCorrectedNormals node.
864
+ *
865
+ * Both inputs should be in vector space [-1, 1].
866
+ *
867
+ * @see https://blog.selfshadow.com/publications/blending-in-detail/
868
+ */
869
+ declare const blendAngleCorrectedNormals: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node$1, number | Node$1]>;
870
+ /**
871
+ * Reconstructs the Z component of a normal from the X and Y components.
872
+ *
873
+ * @param normalXY - A vec2 containing the X and Y components of the normal
874
+ * @returns A vec3 with the reconstructed normal (X, Y, derived Z)
875
+ */
876
+ declare const deriveNormalZ: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node$1]>;
877
+
878
+ /**
879
+ * Input type for segment count: either a JS number or a TSL integer node.
880
+ * When a number is provided, it's automatically converted to an int node.
881
+ * When a Node is provided, it should resolve to an integer value.
882
+ */
883
+ type IntNodeInput = number | ConstNode<number> | Node;
91
884
  /**
92
885
  * Returns a node that is true for skirt vertices in the vertex stage.
93
886
  *
@@ -113,4 +906,15 @@ declare const isSkirtVertex: three_src_nodes_TSL_js.ShaderNodeFn<[segments: numb
113
906
  */
114
907
  declare const isSkirtUV: three_src_nodes_TSL_js.ShaderNodeFn<[segments: number | Node]>;
115
908
 
116
- export { TerrainGeometry, isSkirtUV, isSkirtVertex };
909
+ declare const vGlobalVertexIndex: three_webgpu.PropertyNode;
910
+ declare const vElevation: three_webgpu.PropertyNode;
911
+
912
+ declare const voronoiCells: three_src_nodes_TSL_js.ShaderNodeFn<[three_tsl.ProxiedObject<{
913
+ scale: number;
914
+ facet: number;
915
+ seed: number;
916
+ uv: Node;
917
+ }>]>;
918
+
919
+ export { ArrayTextureBackend, AtlasBackend, Dir, TerrainGeometry, TerrainMesh, Texture3DBackend, U32_EMPTY, allocLeafSet, allocSeamTable, beginUpdate, blendAngleCorrectedNormals, buildLeafIndex, buildSeams2to1, compileComputeTask, createComputePipelineTasks, createCubeSphereSurface, createElevationFieldContextTask, createFlatSurface, createInfiniteFlatSurface, createSpatialIndex, createState, createTerrainFieldStorage, createTerrainFieldTextureTask, createTerrainQuery, createTerrainRaycast, createTerrainSampler, createTerrainSamplerTask, createTerrainUniforms, createUniformsTask, deriveNormalZ, elevationFieldStageTask, elevationFn, elevationScale, executeComputeTask, getDeviceComputeLimits, gpuSpatialIndexStorageTask, gpuSpatialIndexUploadTask, innerTileSegments, instanceIdTask, isSkirtUV, isSkirtVertex, leafGpuBufferTask, leafStorageTask, loadTerrainField, loadTerrainFieldElevation, loadTerrainFieldNormal, maxLevel, maxNodes, origin, packTerrainFieldSample, positionNodeTask, quadtreeConfigTask, quadtreeUpdate, quadtreeUpdateTask, resetLeafSet, resetSeamTable, rootSize, sampleTerrainField, sampleTerrainFieldElevation, sampleTerrainFieldNormal, skirtScale, storeTerrainField, surface, surfaceTask, terrainFieldFilter, terrainFieldStageTask, terrainGraph, terrainQueryTask, terrainRaycastTask, terrainReadbackTask, terrainTasks, textureSpaceToVectorSpace, tileNodesTask, update, updateUniformsTask, vElevation, vGlobalVertexIndex, vectorSpaceToTextureSpace, voronoiCells };
920
+ export type { ComputePipeline, ComputeStageCallback, CreateTerrainSamplerParams, CubeSphereSurfaceConfig, ElevationCallback, ElevationFieldContext, ElevationParams, ElevationRange, FlatSurfaceConfig, GpuSpatialIndexContext, InfiniteFlatSurfaceConfig, IntNodeInput, LeafGpuBufferState, LeafSet, LeafStorageState, LodMode, QuadtreeConfig, QuadtreeConfigState, QuadtreeState, RaycastOptions, SeamTable, SpatialIndex, Surface, TerrainElevationSample, TerrainFieldStorage, TerrainFieldStorageBackendType, TerrainFieldStorageFormat, TerrainFieldStorageOptions, TerrainGraph, TerrainQuery, TerrainQueryContext, TerrainRaycast, TerrainRaycastConfig, TerrainRaycastResult, TerrainSample, TerrainSampleBatch, TerrainSampler, TerrainTasks, TerrainTile, TerrainTileBounds, TerrainUniformsContext, TerrainUniformsParams, TileBounds, TileId, UpdateParams };