@hello-terrain/three 0.0.0-alpha.4 → 0.0.0-alpha.6

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.cts CHANGED
@@ -1,11 +1,12 @@
1
1
  import { BufferGeometry } from 'three';
2
2
  import * as three_webgpu from 'three/webgpu';
3
- import { InstancedMesh, NodeMaterial, ConstNode, Node as Node$1, StorageBufferAttribute, StorageBufferNode, UniformNode, Vector3, Vector3Like } from 'three/webgpu';
3
+ import { InstancedMesh, NodeMaterial, Node, WebGPURenderer, StorageBufferAttribute, StorageBufferNode, UniformNode, Vector3, Vector3Like, ConstNode } from 'three/webgpu';
4
4
  import * as three_src_nodes_TSL_js from 'three/src/nodes/TSL.js';
5
5
  import { ShaderCallNodeInternal } from 'three/src/nodes/TSL.js';
6
- import Node from 'three/src/nodes/core/Node.js';
7
6
  import * as _hello_terrain_work from '@hello-terrain/work';
8
7
  import { TaskRef } from '@hello-terrain/work';
8
+ import Node$1 from 'three/src/nodes/core/Node.js';
9
+ import * as three_tsl from 'three/tsl';
9
10
 
10
11
  /**
11
12
  * Custom geometry for terrain tiles with properly handled skirts.
@@ -33,17 +34,19 @@ declare class TerrainGeometry extends BufferGeometry {
33
34
  * | / | \ | / | \ |
34
35
  * o---o---o---o---o
35
36
  *
36
- * INNER GRID (consistent diagonal, no rotational symmetry):
37
- * o---o---o
38
- * | \ | \ |
39
- * o---o---o
40
- * | \ | \ |
41
- * 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
42
45
  *
43
46
  * Where o = vertex
44
47
  * Each square cell is split into 2 triangles.
45
48
  * - Skirt cells (outer ring): diagonal flip based on quadrant for corner correctness
46
- * - Inner cells: consistent diagonal direction (all triangles "point" the same way)
49
+ * - Inner cells: alternating diagonal via (x+y)%2 to reduce interpolation artifacts
47
50
  *
48
51
  * Vertex layout (for innerSegments = 2):
49
52
  *
@@ -108,67 +111,47 @@ declare class TerrainMesh extends InstancedMesh {
108
111
  set maxNodes(maxNodes: number);
109
112
  }
110
113
 
111
- /**
112
- * Maps a value or node from texture space [0, 1] to vector space [-1, 1].
113
- *
114
- * @param value - The node or value in the range [0, 1].
115
- * @returns A node mapping the input value to the range [-1, 1].
116
- */
117
- declare const textureSpaceToVectorSpace: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node]>;
118
- /**
119
- * Maps a value or node from vector space [-1, 1] to texture space [0, 1].
120
- *
121
- * @param value - The node or value in the range [-1, 1].
122
- * @returns A node mapping the input value to the range [0, 1].
123
- */
124
- declare const vectorSpaceToTextureSpace: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node]>;
125
- /**
126
- * Blends two normal maps using the Reoriented Normal Mapping technique.
127
- * This is the same algorithm used by Unreal Engine's BlendAngleCorrectedNormals node.
128
- *
129
- * Both inputs should be in vector space [-1, 1].
130
- *
131
- * @see https://blog.selfshadow.com/publications/blending-in-detail/
132
- */
133
- declare const blendAngleCorrectedNormals: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node, number | Node]>;
134
- /**
135
- * Reconstructs the Z component of a normal from the X and Y components.
136
- *
137
- * @param normalXY - A vec2 containing the X and Y components of the normal
138
- * @returns A vec3 with the reconstructed normal (X, Y, derived Z)
139
- */
140
- declare const deriveNormalZ: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node]>;
114
+ type ComputeStageCallback = (nodeIndex: Node, globalVertexIndex: Node, uv: Node, localCoordinates: Node, texelSize: Node) => void;
115
+ type ComputePipeline = ComputeStageCallback[];
141
116
 
117
+ /** Default compile task — uses normalFieldStageTask as the leaf. */
118
+ declare const compileComputeTask: _hello_terrain_work.Task<{
119
+ execute: (renderer: WebGPURenderer, instanceCount: number) => void;
120
+ }, string, unknown>;
121
+ /** Default execute task — dispatches the compiled kernel. */
122
+ declare const executeComputeTask: _hello_terrain_work.Task<any, string, {
123
+ renderer: WebGPURenderer;
124
+ }>;
142
125
  /**
143
- * Input type for segment count: either a JS number or a TSL integer node.
144
- * When a number is provided, it's automatically converted to an int node.
145
- * When a Node is provided, it should resolve to an integer value.
146
- */
147
- type IntNodeInput = number | ConstNode<number> | Node$1;
148
- /**
149
- * Returns a node that is true for skirt vertices in the vertex stage.
126
+ * Factory for user-extensible pipelines.
150
127
  *
151
- * @remarks
152
- * Only valid in the vertex shader. A vertex belongs to the skirt if it is on
153
- * the outermost ring of the tile grid (first/last column or row). The grid
154
- * resolution is derived from `segments`.
128
+ * Users who add custom compute stages create their own stage tasks using
129
+ * the accumulation pattern (`get()` predecessor, spread, append), then pass
130
+ * their leaf stage to this helper to get compile + execute tasks.
155
131
  *
156
- * @param segments - The number of inner segments in the terrain grid.
157
- * @returns A node resolving to a boolean indicating a skirt vertex.
158
- */
159
- declare const isSkirtVertex: three_src_nodes_TSL_js.ShaderNodeFn<[segments: number | Node$1]>;
160
- /**
161
- * Returns a node that is true for skirt UVs.
162
- *
163
- * @remarks
164
- * Uses interpolated UVs and the grid size
165
- * from `segments` to mark fragments outside the inner range
166
- * `(step, 1 - step)` on either axis as skirt, where `step = 1 / (segments + 2)`.
132
+ * @example
133
+ * ```ts
134
+ * const erosionStageTask = task((get, work) => {
135
+ * const upstream = get(elevationFieldStageTask);
136
+ * return work((): ComputePipeline => [
137
+ * ...upstream,
138
+ * (nodeIndex, globalVertexIndex, uv) => {
139
+ * // custom erosion logic
140
+ * },
141
+ * ]);
142
+ * });
167
143
  *
168
- * @param segments - The number of inner segments in the terrain grid.
169
- * @returns A node resolving to a boolean indicating a skirt fragment.
144
+ * const { compile, execute } = createComputePipelineTasks(erosionStageTask);
145
+ * ```
170
146
  */
171
- declare const isSkirtUV: three_src_nodes_TSL_js.ShaderNodeFn<[segments: number | Node$1]>;
147
+ declare function createComputePipelineTasks(leafStageTask: TaskRef<ComputePipeline>): {
148
+ compile: _hello_terrain_work.Task<{
149
+ execute: (renderer: WebGPURenderer, instanceCount: number) => void;
150
+ }, string, unknown>;
151
+ execute: _hello_terrain_work.Task<any, string, {
152
+ renderer: WebGPURenderer;
153
+ }>;
154
+ };
172
155
 
173
156
  declare const Dir: {
174
157
  readonly LEFT: 0;
@@ -182,7 +165,9 @@ type TileId = {
182
165
  /** 0 for flat terrain; 0..5 for cube-sphere faces */
183
166
  space: number;
184
167
  level: number;
168
+ /** tile coordinate at this level (signed to support infinite surfaces) */
185
169
  x: number;
170
+ /** tile coordinate at this level (signed to support infinite surfaces) */
186
171
  y: number;
187
172
  };
188
173
  type TileBounds = {
@@ -195,6 +180,8 @@ type TileBounds = {
195
180
  };
196
181
  type Surface = {
197
182
  spaceCount: number;
183
+ /** maximum number of roots returned by `rootTiles` */
184
+ maxRootCount: number;
198
185
  /**
199
186
  * Compute the same-level neighbor TileId in the requested direction.
200
187
  * Returns false if the neighbor is outside the valid topology.
@@ -211,6 +198,15 @@ type Surface = {
211
198
  y: number;
212
199
  z: number;
213
200
  }, out: TileBounds): void;
201
+ /**
202
+ * Fill root tiles for the current frame and return the count.
203
+ * Implementations should write level-0 tiles into `out[0..count)`.
204
+ */
205
+ rootTiles(cameraOrigin: {
206
+ x: number;
207
+ y: number;
208
+ z: number;
209
+ }, out: TileId[]): number;
214
210
  };
215
211
  type LeafSet = {
216
212
  /** maximum number of leaves that fit in the buffers */
@@ -219,8 +215,8 @@ type LeafSet = {
219
215
  count: number;
220
216
  space: Uint8Array;
221
217
  level: Uint8Array;
222
- x: Uint32Array;
223
- y: Uint32Array;
218
+ x: Int32Array;
219
+ y: Int32Array;
224
220
  };
225
221
  declare function allocLeafSet(capacity: number): LeafSet;
226
222
  declare function resetLeafSet(leaves: LeafSet): void;
@@ -278,8 +274,8 @@ type NodeStore = {
278
274
  gen: Uint16Array;
279
275
  space: Uint8Array;
280
276
  level: Uint8Array;
281
- x: Uint32Array;
282
- y: Uint32Array;
277
+ x: Int32Array;
278
+ y: Int32Array;
283
279
  /** sentinel U32_EMPTY means no children; otherwise children are [firstChild..firstChild+3] */
284
280
  firstChild: Uint32Array;
285
281
  flags: Uint8Array;
@@ -319,6 +315,9 @@ type QuadtreeState = {
319
315
  leafIndex: SpatialIndex;
320
316
  /** traversal scratch */
321
317
  stack: Uint32Array;
318
+ /** root nodes for this frame */
319
+ rootNodeIds: Uint32Array;
320
+ rootCount: number;
322
321
  /** split scheduling scratch (dedupe without allocations) */
323
322
  splitQueue: Uint32Array;
324
323
  splitStamp: Uint16Array;
@@ -327,11 +326,12 @@ type QuadtreeState = {
327
326
  scratchTile: TileId;
328
327
  scratchNeighbor: TileId;
329
328
  scratchBounds: TileBounds;
329
+ scratchRootTiles: TileId[];
330
330
  /** surface space count is fixed for a given state */
331
331
  spaceCount: number;
332
332
  };
333
333
  declare function createState(cfg: QuadtreeConfig, surface: Surface): QuadtreeState;
334
- declare function beginUpdate(state: QuadtreeState, surface: Surface): void;
334
+ declare function beginUpdate(state: QuadtreeState, surface: Surface, params: UpdateParams): void;
335
335
 
336
336
  /**
337
337
  * Update the quadtree for the given surface + camera parameters.
@@ -364,6 +364,20 @@ type FlatSurfaceConfig = {
364
364
  };
365
365
  declare function createFlatSurface(cfg: FlatSurfaceConfig): Surface;
366
366
 
367
+ type InfiniteFlatSurfaceConfig = {
368
+ rootSize: number;
369
+ origin: {
370
+ x: number;
371
+ y: number;
372
+ z: number;
373
+ };
374
+ /** optional conservative vertical extent, included in bounds radius */
375
+ maxHeight?: number;
376
+ /** half-width of root grid in root tiles (1 => 3x3 roots) */
377
+ rootGridRadius?: number;
378
+ };
379
+ declare function createInfiniteFlatSurface(cfg: InfiniteFlatSurfaceConfig): Surface;
380
+
367
381
  type CubeSphereSurfaceConfig = {
368
382
  radius: number;
369
383
  maxHeight?: number;
@@ -376,41 +390,12 @@ type CubeSphereSurfaceConfig = {
376
390
  */
377
391
  declare function createCubeSphereSurface(_cfg: CubeSphereSurfaceConfig): Surface;
378
392
 
379
- interface LeafStorageState {
380
- data: Int32Array<ArrayBuffer>;
381
- attribute: StorageBufferAttribute;
382
- node: StorageBufferNode;
383
- }
384
- declare const quadtreeConfigTask: _hello_terrain_work.Task<{
385
- state: QuadtreeState;
386
- surface: Surface;
387
- }, string, unknown>;
388
- declare const quadtreeUpdateTask: _hello_terrain_work.Task<LeafSet, string, unknown>;
389
- /**
390
- * Creates the GPU storage buffer objects. Recreated when maxNodes changes.
391
- *
392
- * terrainVertextPositionNodeTask depends on this (not leafGpuBufferTask) so
393
- * the shader is only rebuilt when the buffer is resized, not on every
394
- * quadtree update.
395
- */
396
- declare const leafStorageTask: _hello_terrain_work.Task<{
397
- data: Int32Array<ArrayBuffer>;
398
- attribute: StorageBufferAttribute;
399
- node: StorageBufferNode;
400
- }, string, unknown>;
401
- declare const leafGpuBufferTask: _hello_terrain_work.Task<{
402
- count: number;
403
- data: Int32Array<ArrayBuffer>;
404
- attribute: StorageBufferAttribute;
405
- node: StorageBufferNode;
406
- }, string, unknown>;
407
-
408
393
  interface TerrainUniformsParams {
409
394
  rootSize: number;
410
395
  rootOrigin: Vector3Like;
411
396
  innerTileSegments: number;
412
397
  skirtScale: number;
413
- heightmapScale: number;
398
+ elevationScale: number;
414
399
  instanceId: string;
415
400
  }
416
401
  interface TerrainUniformsContext {
@@ -418,56 +403,109 @@ interface TerrainUniformsContext {
418
403
  uRootSize: UniformNode<number>;
419
404
  uInnerTileSegments: UniformNode<number>;
420
405
  uSkirtScale: UniformNode<number>;
421
- uHeightmapScale: UniformNode<number>;
406
+ uElevationScale: UniformNode<number>;
407
+ }
408
+ interface LeafStorageState {
409
+ data: Int32Array<ArrayBuffer>;
410
+ attribute: StorageBufferAttribute;
411
+ node: StorageBufferNode;
422
412
  }
423
- /**
424
- * Factory function for instance-specific uniforms for a TerrainMesh.
425
- * Each TerrainMesh gets its own set of uniforms to avoid global state conflicts.
426
- */
427
- declare function createTerrainUniforms(params: TerrainUniformsParams): TerrainUniformsContext;
428
-
429
- declare function createTileWorldPosition(leafStorage: LeafStorageState, terrainUniforms: TerrainUniformsContext): three_src_nodes_TSL_js.ShaderCallNodeInternal;
430
-
431
- /** Generates a unique instance ID per graph (cached once). */
432
- declare const instanceIdTask: _hello_terrain_work.Task<`${string}-${string}-${string}-${string}-${string}`, string, unknown>;
433
413
 
434
- declare function terrainGraph(): _hello_terrain_work.Graph<string, unknown>;
435
- /** All terrain task refs for direct access. */
436
- declare const terrainTasks: {
437
- readonly instanceId: _hello_terrain_work.Task<`${string}-${string}-${string}-${string}-${string}`, string, unknown>;
438
- readonly quadtreeConfig: _hello_terrain_work.Task<{
439
- state: QuadtreeState;
440
- surface: Surface;
441
- }, string, unknown>;
442
- readonly quadtreeUpdate: _hello_terrain_work.Task<LeafSet, string, unknown>;
443
- readonly leafStorage: _hello_terrain_work.Task<{
444
- data: Int32Array<ArrayBuffer>;
445
- attribute: three_webgpu.StorageBufferAttribute;
446
- node: three_webgpu.StorageBufferNode;
447
- }, string, unknown>;
448
- readonly leafGpuBuffer: _hello_terrain_work.Task<{
449
- count: number;
450
- data: Int32Array<ArrayBuffer>;
451
- attribute: three_webgpu.StorageBufferAttribute;
452
- node: three_webgpu.StorageBufferNode;
453
- }, string, unknown>;
454
- readonly createUniforms: _hello_terrain_work.Task<TerrainUniformsContext, string, unknown>;
455
- readonly updateUniforms: _hello_terrain_work.Task<TerrainUniformsContext, string, unknown>;
456
- readonly positionNode: _hello_terrain_work.Task<three_src_nodes_TSL_js.ShaderCallNodeInternal, string, unknown>;
414
+ declare function createTileCompute(leafStorage: LeafStorageState, uniforms: TerrainUniformsContext): {
415
+ tileLevel: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node]>;
416
+ tileOriginVec2: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node]>;
417
+ tileSize: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node]>;
418
+ rootUVCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node, number | Node, number | Node]>;
419
+ tileVertexWorldPositionCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node, number | Node, number | Node]>;
457
420
  };
458
421
 
422
+ interface QuadtreeConfigState {
423
+ state: QuadtreeState;
424
+ surface: Surface;
425
+ }
426
+ interface LeafGpuBufferState extends LeafStorageState {
427
+ count: number;
428
+ }
429
+ interface ElevationFieldContext {
430
+ data: Float32Array<ArrayBuffer>;
431
+ attribute: StorageBufferAttribute;
432
+ node: StorageBufferNode;
433
+ }
434
+ interface NormalFieldContext {
435
+ data: Uint32Array<ArrayBuffer>;
436
+ attribute: StorageBufferAttribute;
437
+ node: StorageBufferNode;
438
+ }
459
439
  /** Task refs for the standard terrain pipeline. */
460
440
  interface TerrainTasks {
461
441
  instanceId: TaskRef<string>;
462
- quadtreeConfig: TaskRef<any>;
463
- quadtreeUpdate: TaskRef<any>;
442
+ quadtreeConfig: TaskRef<QuadtreeConfigState>;
443
+ quadtreeUpdate: TaskRef<LeafSet>;
444
+ surface: TaskRef<Surface>;
464
445
  leafStorage: TaskRef<LeafStorageState>;
465
- leafGpuBuffer: TaskRef<any>;
446
+ leafGpuBuffer: TaskRef<LeafGpuBufferState>;
466
447
  createUniforms: TaskRef<TerrainUniformsContext>;
467
448
  updateUniforms: TaskRef<TerrainUniformsContext>;
468
449
  positionNode: TaskRef<ShaderCallNodeInternal>;
450
+ createElevationFieldContext: TaskRef<ElevationFieldContext>;
451
+ createTileNodes: TaskRef<ReturnType<typeof createTileCompute>>;
452
+ createNormalFieldContext: TaskRef<NormalFieldContext>;
453
+ elevationFieldStage: TaskRef<ComputePipeline>;
454
+ normalFieldStage: TaskRef<ComputePipeline>;
455
+ compileCompute: TaskRef<{
456
+ execute: (renderer: WebGPURenderer, instanceCount: number) => void;
457
+ }>;
458
+ executeCompute: TaskRef<void | (() => void)>;
469
459
  }
470
460
 
461
+ declare const createElevationFieldContextTask: _hello_terrain_work.Task<{
462
+ data: Float32Array<ArrayBuffer>;
463
+ attribute: StorageBufferAttribute;
464
+ node: three_webgpu.StorageBufferNode;
465
+ }, string, unknown>;
466
+ declare const tileNodesTask: _hello_terrain_work.Task<{
467
+ tileLevel: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
468
+ tileOriginVec2: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
469
+ tileSize: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
470
+ rootUVCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node, number | three_webgpu.Node, number | three_webgpu.Node]>;
471
+ tileVertexWorldPositionCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node, number | three_webgpu.Node, number | three_webgpu.Node]>;
472
+ }, string, unknown>;
473
+ /**
474
+ * Root compute stage — generates elevation data and writes to the
475
+ * elevation field storage buffer. Returns a single-element `ComputePipeline`.
476
+ */
477
+ declare const elevationFieldStageTask: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
478
+
479
+ /** Generates a unique instance ID per graph (cached once). */
480
+ declare const instanceIdTask: _hello_terrain_work.Task<`${string}-${string}-${string}-${string}-${string}`, string, unknown>;
481
+
482
+ declare const createNormalFieldContextTask: _hello_terrain_work.Task<{
483
+ data: Uint32Array<ArrayBuffer>;
484
+ attribute: StorageBufferAttribute;
485
+ node: three_webgpu.StorageBufferNode;
486
+ }, string, unknown>;
487
+ /**
488
+ * Normal field compute stage — reads height neighbors from the elevation field
489
+ * buffer, computes surface normals via central differences, packs XZ
490
+ * components into a u32 via `packHalf2x16`, and writes to the normal field
491
+ * storage buffer.
492
+ *
493
+ * Accumulates the upstream elevation pipeline via `get(elevationFieldStageTask)`.
494
+ */
495
+ declare const normalFieldStageTask: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
496
+
497
+ interface ElevationParams {
498
+ worldPosition: Node$1;
499
+ rootSize: Node$1;
500
+ rootUV: Node$1;
501
+ tileUV: Node$1;
502
+ tileLevel: Node$1;
503
+ tileSize: Node$1;
504
+ tileOriginVec2: Node$1;
505
+ nodeIndex: Node$1;
506
+ }
507
+ type ElevationCallback = (params: ElevationParams) => Node$1;
508
+
471
509
  /** Root tile size in world units. */
472
510
  declare const rootSize: _hello_terrain_work.ParamRef<number>;
473
511
  /** World-space origin of the terrain. */
@@ -476,29 +514,71 @@ declare const origin: _hello_terrain_work.ParamRef<{
476
514
  y: number;
477
515
  z: number;
478
516
  }>;
479
- /** Number of segments per inner tile edge. */
517
+ /**
518
+ * Number of segments per inner tile edge.
519
+ * 13 is the max tiles we can support for 256 workgroups (13 + 3 === 16.. 16x16)
520
+ */
480
521
  declare const innerTileSegments: _hello_terrain_work.ParamRef<number>;
481
522
  /** Skirt scale factor. */
482
523
  declare const skirtScale: _hello_terrain_work.ParamRef<number>;
483
- /** Heightmap vertical scale. */
484
- declare const heightmapScale: _hello_terrain_work.ParamRef<number>;
524
+ /** Elevation vertical scale. */
525
+ declare const elevationScale: _hello_terrain_work.ParamRef<number>;
485
526
  /** Maximum quadtree nodes. */
486
527
  declare const maxNodes: _hello_terrain_work.ParamRef<number>;
487
528
  /** Maximum quadtree subdivision level. */
488
529
  declare const maxLevel: _hello_terrain_work.ParamRef<number>;
489
530
  /** Quadtree update configuration (camera, mode, etc.). */
490
531
  declare const quadtreeUpdate: _hello_terrain_work.ParamRef<UpdateParams>;
532
+ /** Optional custom terrain surface; defaults to bounded flat surface when null. */
533
+ declare const surface: _hello_terrain_work.ParamRef<Surface | null>;
534
+ /** Terrain elevation control function (per vertex, in gpu compute) */
535
+ declare const elevationFn: _hello_terrain_work.ParamRef<ElevationCallback>;
491
536
 
492
537
  /**
493
538
  * Builds the TSL position node for the terrain shader.
494
539
  *
495
- * Depends on leafStorageTask (buffer objects) and createUniformsTask
496
- * (uniform nodes). Both only change when their GPU resources are recreated
540
+ * Depends on leafStorageTask (buffer objects), createUniformsTask
541
+ * (uniform nodes), createElevationFieldContextTask (elevation field storage),
542
+ * and createNormalFieldContextTask (normal field storage).
543
+ *
544
+ * The position node also reads normals from the normal field buffer
545
+ * per-vertex (using vertexIndex) and assigns them to the vNormal
546
+ * varying for use in the fragment shader.
547
+ *
548
+ * These only change when their GPU resources are recreated
497
549
  * (e.g. buffer resize), so this task stays cached during normal quadtree
498
550
  * updates — no unnecessary shader rebuilds.
499
551
  */
500
552
  declare const positionNodeTask: _hello_terrain_work.Task<three_src_nodes_TSL_js.ShaderCallNodeInternal, string, unknown>;
501
553
 
554
+ /**
555
+ * Derives the terrain surface from `rootSize` and `origin`.
556
+ * Automatically recomputes when either param changes, keeping the
557
+ * quadtree refinement in sync with the GPU-side tile positioning.
558
+ */
559
+ declare const surfaceTask: _hello_terrain_work.Task<Surface, string, unknown>;
560
+ declare const quadtreeConfigTask: _hello_terrain_work.Task<{
561
+ state: QuadtreeState;
562
+ surface: Surface;
563
+ }, string, unknown>;
564
+ declare const quadtreeUpdateTask: _hello_terrain_work.Task<LeafSet, string, unknown>;
565
+ /**
566
+ * Creates the GPU storage buffer objects. Recreated when maxNodes changes.
567
+ *
568
+ * positionNodeTask depends on this (not leafGpuBufferTask) so
569
+ * the shader is only rebuilt when the buffer is resized, not on every
570
+ * quadtree update.
571
+ */
572
+ declare const leafStorageTask: _hello_terrain_work.Task<LeafStorageState, string, unknown>;
573
+ declare const leafGpuBufferTask: _hello_terrain_work.Task<{
574
+ count: number;
575
+ data: Int32Array<ArrayBuffer>;
576
+ attribute: three_webgpu.StorageBufferAttribute;
577
+ node: three_webgpu.StorageBufferNode;
578
+ }, string, unknown>;
579
+
580
+ declare function createTerrainUniforms(params: TerrainUniformsParams): TerrainUniformsContext;
581
+
502
582
  /**
503
583
  * Creates the terrain uniform nodes once. Downstream tasks capture
504
584
  * references to these nodes in shader graphs, so the same instances
@@ -511,5 +591,126 @@ declare const createUniformsTask: _hello_terrain_work.Task<TerrainUniformsContex
511
591
  */
512
592
  declare const updateUniformsTask: _hello_terrain_work.Task<TerrainUniformsContext, string, unknown>;
513
593
 
514
- export { Dir, TerrainGeometry, TerrainMesh, U32_EMPTY, allocLeafSet, allocSeamTable, beginUpdate, blendAngleCorrectedNormals, buildLeafIndex, buildSeams2to1, createCubeSphereSurface, createFlatSurface, createSpatialIndex, createState, createTerrainUniforms, createTileWorldPosition, createUniformsTask, deriveNormalZ, heightmapScale, innerTileSegments, instanceIdTask, isSkirtUV, isSkirtVertex, leafGpuBufferTask, leafStorageTask, maxLevel, maxNodes, origin, positionNodeTask, quadtreeConfigTask, quadtreeUpdate, quadtreeUpdateTask, resetLeafSet, resetSeamTable, rootSize, skirtScale, terrainGraph, terrainTasks, textureSpaceToVectorSpace, update, updateUniformsTask, vectorSpaceToTextureSpace };
515
- export type { CubeSphereSurfaceConfig, FlatSurfaceConfig, IntNodeInput, LeafSet, LeafStorageState, LodMode, QuadtreeConfig, QuadtreeState, SeamTable, SpatialIndex, Surface, TerrainTasks, TerrainUniformsContext, TerrainUniformsParams, TileBounds, TileId, UpdateParams };
594
+ declare function terrainGraph(): _hello_terrain_work.Graph<string, {
595
+ renderer: WebGPURenderer;
596
+ }>;
597
+ /** All terrain task refs for direct access. */
598
+ declare const terrainTasks: {
599
+ readonly instanceId: _hello_terrain_work.Task<`${string}-${string}-${string}-${string}-${string}`, string, unknown>;
600
+ readonly quadtreeConfig: _hello_terrain_work.Task<{
601
+ state: QuadtreeState;
602
+ surface: Surface;
603
+ }, string, unknown>;
604
+ readonly quadtreeUpdate: _hello_terrain_work.Task<LeafSet, string, unknown>;
605
+ readonly leafStorage: _hello_terrain_work.Task<LeafStorageState, string, unknown>;
606
+ readonly surface: _hello_terrain_work.Task<Surface, string, unknown>;
607
+ readonly leafGpuBuffer: _hello_terrain_work.Task<{
608
+ count: number;
609
+ data: Int32Array<ArrayBuffer>;
610
+ attribute: three_webgpu.StorageBufferAttribute;
611
+ node: three_webgpu.StorageBufferNode;
612
+ }, string, unknown>;
613
+ readonly createUniforms: _hello_terrain_work.Task<TerrainUniformsContext, string, unknown>;
614
+ readonly updateUniforms: _hello_terrain_work.Task<TerrainUniformsContext, string, unknown>;
615
+ readonly positionNode: _hello_terrain_work.Task<three_src_nodes_TSL_js.ShaderCallNodeInternal, string, unknown>;
616
+ readonly createElevationFieldContext: _hello_terrain_work.Task<{
617
+ data: Float32Array<ArrayBuffer>;
618
+ attribute: three_webgpu.StorageBufferAttribute;
619
+ node: three_webgpu.StorageBufferNode;
620
+ }, string, unknown>;
621
+ readonly createTileNodes: _hello_terrain_work.Task<{
622
+ tileLevel: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
623
+ tileOriginVec2: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
624
+ tileSize: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
625
+ rootUVCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node, number | three_webgpu.Node, number | three_webgpu.Node]>;
626
+ tileVertexWorldPositionCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node, number | three_webgpu.Node, number | three_webgpu.Node]>;
627
+ }, string, unknown>;
628
+ readonly createNormalFieldContext: _hello_terrain_work.Task<{
629
+ data: Uint32Array<ArrayBuffer>;
630
+ attribute: three_webgpu.StorageBufferAttribute;
631
+ node: three_webgpu.StorageBufferNode;
632
+ }, string, unknown>;
633
+ readonly elevationFieldStage: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
634
+ readonly normalFieldStage: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
635
+ readonly compileCompute: _hello_terrain_work.Task<{
636
+ execute: (renderer: WebGPURenderer, instanceCount: number) => void;
637
+ }, string, unknown>;
638
+ readonly executeCompute: _hello_terrain_work.Task<any, string, {
639
+ renderer: WebGPURenderer;
640
+ }>;
641
+ };
642
+
643
+ /**
644
+ * Maps a value or node from texture space [0, 1] to vector space [-1, 1].
645
+ *
646
+ * @param value - The node or value in the range [0, 1].
647
+ * @returns A node mapping the input value to the range [-1, 1].
648
+ */
649
+ declare const textureSpaceToVectorSpace: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node$1]>;
650
+ /**
651
+ * Maps a value or node from vector space [-1, 1] to texture space [0, 1].
652
+ *
653
+ * @param value - The node or value in the range [-1, 1].
654
+ * @returns A node mapping the input value to the range [0, 1].
655
+ */
656
+ declare const vectorSpaceToTextureSpace: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node$1]>;
657
+ /**
658
+ * Blends two normal maps using the Reoriented Normal Mapping technique.
659
+ * This is the same algorithm used by Unreal Engine's BlendAngleCorrectedNormals node.
660
+ *
661
+ * Both inputs should be in vector space [-1, 1].
662
+ *
663
+ * @see https://blog.selfshadow.com/publications/blending-in-detail/
664
+ */
665
+ declare const blendAngleCorrectedNormals: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node$1, number | Node$1]>;
666
+ /**
667
+ * Reconstructs the Z component of a normal from the X and Y components.
668
+ *
669
+ * @param normalXY - A vec2 containing the X and Y components of the normal
670
+ * @returns A vec3 with the reconstructed normal (X, Y, derived Z)
671
+ */
672
+ declare const deriveNormalZ: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node$1]>;
673
+
674
+ /**
675
+ * Input type for segment count: either a JS number or a TSL integer node.
676
+ * When a number is provided, it's automatically converted to an int node.
677
+ * When a Node is provided, it should resolve to an integer value.
678
+ */
679
+ type IntNodeInput = number | ConstNode<number> | Node;
680
+ /**
681
+ * Returns a node that is true for skirt vertices in the vertex stage.
682
+ *
683
+ * @remarks
684
+ * Only valid in the vertex shader. A vertex belongs to the skirt if it is on
685
+ * the outermost ring of the tile grid (first/last column or row). The grid
686
+ * resolution is derived from `segments`.
687
+ *
688
+ * @param segments - The number of inner segments in the terrain grid.
689
+ * @returns A node resolving to a boolean indicating a skirt vertex.
690
+ */
691
+ declare const isSkirtVertex: three_src_nodes_TSL_js.ShaderNodeFn<[segments: number | Node]>;
692
+ /**
693
+ * Returns a node that is true for skirt UVs.
694
+ *
695
+ * @remarks
696
+ * Uses interpolated UVs and the grid size
697
+ * from `segments` to mark fragments outside the inner range
698
+ * `(step, 1 - step)` on either axis as skirt, where `step = 1 / (segments + 2)`.
699
+ *
700
+ * @param segments - The number of inner segments in the terrain grid.
701
+ * @returns A node resolving to a boolean indicating a skirt fragment.
702
+ */
703
+ declare const isSkirtUV: three_src_nodes_TSL_js.ShaderNodeFn<[segments: number | Node]>;
704
+
705
+ declare const vGlobalVertexIndex: three_webgpu.PropertyNode;
706
+ declare const vElevation: three_webgpu.PropertyNode;
707
+
708
+ declare const voronoiCells: three_src_nodes_TSL_js.ShaderNodeFn<[three_tsl.ProxiedObject<{
709
+ scale: number;
710
+ facet: number;
711
+ seed: number;
712
+ uv: Node;
713
+ }>]>;
714
+
715
+ export { Dir, TerrainGeometry, TerrainMesh, U32_EMPTY, allocLeafSet, allocSeamTable, beginUpdate, blendAngleCorrectedNormals, buildLeafIndex, buildSeams2to1, compileComputeTask, createComputePipelineTasks, createCubeSphereSurface, createElevationFieldContextTask, createFlatSurface, createInfiniteFlatSurface, createNormalFieldContextTask, createSpatialIndex, createState, createTerrainUniforms, createUniformsTask, deriveNormalZ, elevationFieldStageTask, elevationFn, elevationScale, executeComputeTask, innerTileSegments, instanceIdTask, isSkirtUV, isSkirtVertex, leafGpuBufferTask, leafStorageTask, maxLevel, maxNodes, normalFieldStageTask, origin, positionNodeTask, quadtreeConfigTask, quadtreeUpdate, quadtreeUpdateTask, resetLeafSet, resetSeamTable, rootSize, skirtScale, surface, surfaceTask, terrainGraph, terrainTasks, textureSpaceToVectorSpace, tileNodesTask, update, updateUniformsTask, vElevation, vGlobalVertexIndex, vectorSpaceToTextureSpace, voronoiCells };
716
+ export type { ComputePipeline, ComputeStageCallback, CubeSphereSurfaceConfig, ElevationCallback, ElevationFieldContext, ElevationParams, FlatSurfaceConfig, InfiniteFlatSurfaceConfig, IntNodeInput, LeafGpuBufferState, LeafSet, LeafStorageState, LodMode, NormalFieldContext, QuadtreeConfig, QuadtreeConfigState, QuadtreeState, SeamTable, SpatialIndex, Surface, TerrainTasks, TerrainUniformsContext, TerrainUniformsParams, TileBounds, TileId, UpdateParams };