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

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
@@ -13,7 +13,14 @@ import * as three_tsl from 'three/tsl';
13
13
  * This geometry ensures that corner triangles are subdivided correctly.
14
14
  */
15
15
  declare class TerrainGeometry extends BufferGeometry {
16
- constructor(innerSegments?: number, extendUV?: boolean);
16
+ /**
17
+ * @param flipWinding Reverse triangle winding so front faces point the
18
+ * opposite way. The default winding makes flat tiles front-face `+Y`; the
19
+ * cube-sphere maps `(u→right, v→up)`, which would otherwise leave the
20
+ * planet's outer shell back-facing, so it passes `flipWinding` to render
21
+ * the outer surface with `FrontSide`.
22
+ */
23
+ constructor(innerSegments?: number, extendUV?: boolean, flipWinding?: boolean);
17
24
  /**
18
25
  * Generate indices for terrain geometry with proper skirt corner handling.
19
26
  * The key improvement is in how corner triangles are subdivided.
@@ -96,218 +103,6 @@ declare class TerrainGeometry extends BufferGeometry {
96
103
  private generateNormals;
97
104
  }
98
105
 
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
106
  declare const Dir: {
312
107
  readonly LEFT: 0;
313
108
  readonly RIGHT: 1;
@@ -333,10 +128,27 @@ type TileBounds = {
333
128
  /** conservative radius */
334
129
  r: number;
335
130
  };
336
- type Surface = {
131
+ type TopologyProjection = "flat" | "cubeSphere";
132
+ type Topology = {
337
133
  spaceCount: number;
338
134
  /** maximum number of roots returned by `rootTiles` */
339
135
  maxRootCount: number;
136
+ /**
137
+ * GPU position/normal assembly projection. Defaults to `flat` when absent.
138
+ * `cubeSphere` selects radial sphere mapping from cube faces.
139
+ */
140
+ projection?: TopologyProjection;
141
+ /** Sphere radius in world units (cube-sphere projection only). */
142
+ radius?: number;
143
+ /**
144
+ * Planet center in world space (cube-sphere projection only). Used to apply
145
+ * the camera elevation offset along the radial up-direction during LOD.
146
+ */
147
+ center?: {
148
+ x: number;
149
+ y: number;
150
+ z: number;
151
+ };
340
152
  /**
341
153
  * Compute the same-level neighbor TileId in the requested direction.
342
154
  * Returns false if the neighbor is outside the valid topology.
@@ -400,9 +212,11 @@ type UpdateParams = {
400
212
  z: number;
401
213
  };
402
214
  /**
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.
215
+ * Terrain elevation beneath the camera (from the previous frame). During
216
+ * refinement it offsets the camera toward the terrain surface so LOD distance
217
+ * is measured relative to the surface rather than the datum:
218
+ * - flat: subtracted from `cameraOrigin.y`.
219
+ * - cube-sphere: subtracted along the radial up-direction from the planet center.
406
220
  */
407
221
  elevationAtCameraXZ?: number;
408
222
  /**
@@ -488,18 +302,18 @@ type QuadtreeState = {
488
302
  scratchNeighbor: TileId;
489
303
  scratchBounds: TileBounds;
490
304
  scratchRootTiles: TileId[];
491
- /** surface space count is fixed for a given state */
305
+ /** topology space count is fixed for a given state */
492
306
  spaceCount: number;
493
307
  };
494
- declare function createState(cfg: QuadtreeConfig, surface: Surface): QuadtreeState;
495
- declare function beginUpdate(state: QuadtreeState, surface: Surface, params: UpdateParams): void;
308
+ declare function createState(cfg: QuadtreeConfig, topology: Topology): QuadtreeState;
309
+ declare function beginUpdate(state: QuadtreeState, topology: Topology, params: UpdateParams): void;
496
310
 
497
311
  /**
498
- * Update the quadtree for the given surface + camera parameters.
312
+ * Update the quadtree for the given topology + camera parameters.
499
313
  *
500
314
  * Produces a LeafSet of TileIds (SoA typed arrays).
501
315
  */
502
- declare function update(state: QuadtreeState, surface: Surface, params: UpdateParams, outLeaves?: LeafSet): LeafSet;
316
+ declare function update(state: QuadtreeState, topology: Topology, params: UpdateParams, outLeaves?: LeafSet): LeafSet;
503
317
 
504
318
  /**
505
319
  * Build a fixed-width seam/neighbor table for balanced leaves (2:1).
@@ -507,9 +321,9 @@ declare function update(state: QuadtreeState, surface: Surface, params: UpdatePa
507
321
  * Output neighbors are leaf-list indices, with U32_EMPTY for missing entries.
508
322
  * Layout: neighbors[leafIndex * 8 + edge*2 + slot].
509
323
  */
510
- declare function buildSeams2to1(surface: Surface, leaves: LeafSet, outSeams: SeamTable, outIndex?: SpatialIndex): SeamTable;
324
+ declare function buildSeams2to1(topology: Topology, leaves: LeafSet, outSeams: SeamTable, outIndex?: SpatialIndex): SeamTable;
511
325
 
512
- type FlatSurfaceConfig = {
326
+ type FlatTopologyConfig = {
513
327
  /**
514
328
  * World-space size of the root tile edge.
515
329
  * The root tile covers [-rootSize/2, +rootSize/2] around origin in X/Z.
@@ -523,9 +337,9 @@ type FlatSurfaceConfig = {
523
337
  /** optional conservative vertical extent, included in bounds radius */
524
338
  maxHeight?: number;
525
339
  };
526
- declare function createFlatSurface(cfg: FlatSurfaceConfig): Surface;
340
+ declare function createFlatTopology(cfg: FlatTopologyConfig): Topology;
527
341
 
528
- type InfiniteFlatSurfaceConfig = {
342
+ type InfiniteFlatTopologyConfig = {
529
343
  rootSize: number;
530
344
  origin: {
531
345
  x: number;
@@ -537,24 +351,368 @@ type InfiniteFlatSurfaceConfig = {
537
351
  /** half-width of root grid in root tiles (1 => 3x3 roots) */
538
352
  rootGridRadius?: number;
539
353
  };
540
- declare function createInfiniteFlatSurface(cfg: InfiniteFlatSurfaceConfig): Surface;
354
+ declare function createInfiniteFlatTopology(cfg: InfiniteFlatTopologyConfig): Topology;
541
355
 
542
- type CubeSphereSurfaceConfig = {
356
+ type CubeSphereTopologyConfig = {
357
+ /** Sphere radius in world units. */
543
358
  radius: number;
359
+ /** Planet center in world space (defaults to origin). */
360
+ center?: {
361
+ x: number;
362
+ y: number;
363
+ z: number;
364
+ };
365
+ /** Optional conservative vertical extent, included in bounds radius. */
544
366
  maxHeight?: number;
545
367
  };
546
368
  /**
547
- * Placeholder cube-sphere surface.
369
+ * Cube-sphere topology: six quadtree faces wrapped onto a sphere.
370
+ *
371
+ * Topology (`neighborSameLevel`) is derived numerically from the shared
372
+ * `CUBE_FACES` basis so cross-face edges (including rotated pole edges)
373
+ * resolve to the correct neighbor tile without hand-coded transforms.
374
+ */
375
+ declare function createCubeSphereTopology(cfg: CubeSphereTopologyConfig): Topology;
376
+
377
+ /**
378
+ * Canonical cube-sphere face basis.
379
+ *
380
+ * Shared single source of truth between the CPU surface topology
381
+ * (`cubeSphere.ts`) and the GPU position/normal assembly (`tsl/cubeSphere.ts`)
382
+ * so both agree on geometry and faces seam correctly.
383
+ *
384
+ * Each face maps a face-local coordinate (u, v) in [0, 1] to a point on the
385
+ * cube `[-1, 1]^3` via:
386
+ *
387
+ * s = 2u - 1, t = 2v - 1
388
+ * cube = forward + s * right + t * up
389
+ *
390
+ * Normalizing `cube` yields the unit-sphere direction for that vertex.
391
+ *
392
+ * Bases are right-handed (`forward = right x up`) and outward-facing.
393
+ * Space indices: 0:+X 1:-X 2:+Y 3:-Y 4:+Z 5:-Z.
394
+ */
395
+ type Vec3 = readonly [number, number, number];
396
+ type CubeFace = {
397
+ forward: Vec3;
398
+ right: Vec3;
399
+ up: Vec3;
400
+ };
401
+ declare const CUBE_FACE_COUNT = 6;
402
+ declare const CUBE_FACES: readonly CubeFace[];
403
+
404
+ type Vec3Mutable = [number, number, number];
405
+ /**
406
+ * Cube-space point for a face-local coordinate (u, v) in [0, 1]:
407
+ * cube = forward + (2u-1) * right + (2v-1) * up
408
+ * The result is unnormalized; normalize it to obtain the sphere direction.
409
+ */
410
+ declare function faceUVToCube(face: number, u: number, v: number, out: Vec3Mutable): void;
411
+ /** Pick the cube face whose normal axis dominates the direction. */
412
+ declare function directionToFace(d: Vec3): number;
413
+ /** Face-local (u, v) in [0, 1] for a direction known to fall on `face`. */
414
+ declare function directionToFaceUV(face: number, d: Vec3, out: [number, number]): void;
415
+ /**
416
+ * Convert latitude/longitude (degrees) to a unit sphere direction.
417
+ *
418
+ * Convention matches `CUBE_FACES` (+Y is the north pole):
419
+ * - latitude is the angle above the equator, in `[-90, 90]`
420
+ * - longitude is the angle around the +Y axis, in `[-180, 180]`,
421
+ * measured from +Z toward +X (lon = 0 points along +Z).
422
+ */
423
+ declare function latLongToDirection(latDeg: number, lonDeg: number, out: Vec3Mutable): void;
424
+ /** Inverse of {@link latLongToDirection}; returns degrees. */
425
+ declare function directionToLatLong(d: Vec3): {
426
+ latitude: number;
427
+ longitude: number;
428
+ };
429
+
430
+ type TerrainFieldStorageBackendType = "array-texture" | "atlas" | "texture-3d";
431
+ type TerrainFieldStorageFormat = "rgba16float" | "rgba32float";
432
+ type TerrainFieldStorageOptions = {
433
+ backend?: TerrainFieldStorageBackendType;
434
+ filter?: "nearest" | "linear";
435
+ format?: TerrainFieldStorageFormat;
436
+ };
437
+ interface TerrainFieldStorage {
438
+ readonly backendType: TerrainFieldStorageBackendType;
439
+ readonly edgeVertexCount: number;
440
+ readonly tileCount: number;
441
+ readonly texture: StorageArrayTexture | StorageTexture;
442
+ uv(ix: Node, iy: Node, tileIndex: Node): Node;
443
+ texel(ix: Node, iy: Node, tileIndex: Node): Node;
444
+ /** UV-based filtered sample. `u, v` in [0, 1] tile-local space. */
445
+ sample(u: Node, v: Node, tileIndex: Node): Node;
446
+ resize(width: number, height: number, tileCount: number): void;
447
+ }
448
+ declare function ArrayTextureBackend(edgeVertexCount: number, tileCount: number, options: Required<Pick<TerrainFieldStorageOptions, "format" | "filter">>): TerrainFieldStorage;
449
+ declare function AtlasBackend(edgeVertexCount: number, tileCount: number, options: Required<Pick<TerrainFieldStorageOptions, "format" | "filter">>): TerrainFieldStorage;
450
+ declare function createTerrainFieldStorage(edgeVertexCount: number, tileCount: number, renderer?: WebGPURenderer, options?: TerrainFieldStorageOptions): TerrainFieldStorage;
451
+ declare function storeTerrainField(storage: TerrainFieldStorage, ix: Node, iy: Node, tileIndex: Node, value: Node): Node;
452
+ declare function loadTerrainField(storage: TerrainFieldStorage, ix: Node, iy: Node, tileIndex: Node): Node;
453
+ declare function loadTerrainFieldElevation(storage: TerrainFieldStorage, ix: Node, iy: Node, tileIndex: Node): Node;
454
+ declare function loadTerrainFieldNormal(storage: TerrainFieldStorage, ix: Node, iy: Node, tileIndex: Node): Node;
455
+ /**
456
+ * UV-based filtered sample. `u, v` are in [0, 1] tile-local space.
457
+ * Respects the filter mode (nearest / linear) set on the storage.
458
+ */
459
+ declare function sampleTerrainField(storage: TerrainFieldStorage, u: Node, v: Node, tileIndex: Node): Node;
460
+ declare function sampleTerrainFieldElevation(storage: TerrainFieldStorage, u: Node, v: Node, tileIndex: Node): Node;
461
+ declare function packTerrainFieldSample(height: Node, normalXZ: Node, extra?: Node): Node;
462
+
463
+ interface ElevationParams {
464
+ worldPosition: Node$1;
465
+ rootSize: Node$1;
466
+ rootUV: Node$1;
467
+ tileUV: Node$1;
468
+ tileLevel: Node$1;
469
+ tileSize: Node$1;
470
+ tileOriginVec2: Node$1;
471
+ nodeIndex: Node$1;
472
+ }
473
+ type ElevationCallback = (params: ElevationParams) => Node$1;
474
+
475
+ interface TerrainUniformsParams {
476
+ rootSize: number;
477
+ rootOrigin: Vector3Like;
478
+ innerTileSegments: number;
479
+ skirtScale: number;
480
+ elevationScale: number;
481
+ radius: number;
482
+ instanceId: string;
483
+ }
484
+ interface TerrainUniformsContext {
485
+ uRootOrigin: UniformNode<Vector3>;
486
+ uRootSize: UniformNode<number>;
487
+ uInnerTileSegments: UniformNode<number>;
488
+ uSkirtScale: UniformNode<number>;
489
+ uElevationScale: UniformNode<number>;
490
+ uRadius: UniformNode<number>;
491
+ }
492
+ interface LeafStorageState {
493
+ data: Int32Array<ArrayBuffer>;
494
+ attribute: StorageBufferAttribute;
495
+ node: StorageBufferNode;
496
+ }
497
+
498
+ interface GpuSpatialIndexContext {
499
+ data: Uint32Array<ArrayBuffer>;
500
+ size: number;
501
+ mask: number;
502
+ stampGen: UniformNode<number>;
503
+ attribute: StorageBufferAttribute;
504
+ node: StorageBufferNode;
505
+ }
506
+ interface TerrainSampler {
507
+ sampleElevation: (worldX: Node, worldZ: Node) => Node;
508
+ sampleNormal: (worldX: Node, worldZ: Node) => Node;
509
+ sampleTerrain: (worldX: Node, worldZ: Node) => Node;
510
+ sampleValidity: (worldX: Node, worldZ: Node) => Node;
511
+ evaluateElevation: (worldX: Node, worldZ: Node) => Node;
512
+ evaluateNormal: (worldX: Node, worldZ: Node, epsilon?: Node) => Node;
513
+ /** Packed `vec4(elevation, nx, ny, nz)` where the normal is tangent-space. */
514
+ sampleTerrainByDirection?: (direction: Node) => Node;
515
+ sampleElevationByDirection?: (direction: Node) => Node;
516
+ /** World-space surface normal reconstructed in the sphere tangent frame. */
517
+ sampleNormalByDirection?: (direction: Node) => Node;
518
+ sampleValidityByDirection?: (direction: Node) => Node;
519
+ }
520
+ interface CreateTerrainSamplerParams {
521
+ terrainFieldStorage: TerrainFieldStorage;
522
+ spatialIndex: GpuSpatialIndexContext;
523
+ uniforms: TerrainUniformsContext;
524
+ elevationCallback: ElevationCallback;
525
+ /** Maximum quadtree level to probe during tile lookup. */
526
+ maxLevel: number;
527
+ projection?: TopologyProjection;
528
+ }
529
+ interface TerrainSample {
530
+ elevation: number;
531
+ normal: Vector3$1;
532
+ valid: boolean;
533
+ }
534
+ interface TerrainSampleBatch {
535
+ elevations: Float32Array;
536
+ normals: Float32Array;
537
+ valid: Uint8Array;
538
+ generation: number;
539
+ }
540
+ /**
541
+ * Result of sampling a cube-sphere surface from a direction/position/lat-long.
542
+ *
543
+ * `elevation` is the radial displacement above the base radius (already scaled
544
+ * by `elevationScale`); `position` is the full world-space surface point
545
+ * `center + direction * (radius + elevation)`.
546
+ */
547
+ interface TerrainSurfaceSample {
548
+ position: Vector3$1;
549
+ normal: Vector3$1;
550
+ direction: Vector3$1;
551
+ elevation: number;
552
+ valid: boolean;
553
+ }
554
+ interface TerrainSurfaceSampleBatch {
555
+ positions: Float32Array;
556
+ normals: Float32Array;
557
+ elevations: Float32Array;
558
+ valid: Uint8Array;
559
+ generation: number;
560
+ }
561
+ interface TerrainTile {
562
+ /** Surface space index: 0 for flat terrain, 0..5 for cube-sphere faces. */
563
+ space: number;
564
+ level: number;
565
+ x: number;
566
+ y: number;
567
+ index: number;
568
+ }
569
+ interface TerrainTileBounds extends TerrainTile {
570
+ minElevation: number;
571
+ maxElevation: number;
572
+ }
573
+ interface ElevationRange {
574
+ min: number;
575
+ max: number;
576
+ }
577
+ /**
578
+ * Flat (heightfield) terrain query, keyed on world XZ. For cube-sphere
579
+ * surfaces use {@link TerrainSphereQuery} instead.
580
+ */
581
+ interface TerrainQuery {
582
+ getElevation(worldX: number, worldZ: number): number | null;
583
+ getNormal(worldX: number, worldZ: number): Vector3$1 | null;
584
+ getTile(worldX: number, worldZ: number): TerrainTile | null;
585
+ getTileBounds(worldX: number, worldZ: number): TerrainTileBounds | null;
586
+ getGlobalElevationRange(): ElevationRange | null;
587
+ sampleTerrain(worldX: number, worldZ: number): TerrainSample;
588
+ sampleTerrainBatch(positions: Float32Array): TerrainSampleBatch;
589
+ readonly generation: number;
590
+ }
591
+ /**
592
+ * Cube-sphere terrain query. A surface location is identified by a direction
593
+ * from the planet center (the canonical form); `ByPosition` projects any world
594
+ * point onto its direction, and `ByLatLong` takes degrees (latitude
595
+ * `[-90, 90]`, longitude `[-180, 180]`). Elevation is the radial displacement
596
+ * above the base radius.
597
+ *
598
+ * Exposed only when the active surface uses the `cubeSphere` projection
599
+ * (otherwise `null` on the query context / runtime).
600
+ */
601
+ interface TerrainSphereQuery {
602
+ readonly generation: number;
603
+ getElevationByDirection(direction: Vector3$1): number | null;
604
+ getElevationByPosition(position: Vector3$1): number | null;
605
+ getElevationByLatLong(latitudeDeg: number, longitudeDeg: number): number | null;
606
+ getNormalByDirection(direction: Vector3$1): Vector3$1 | null;
607
+ getNormalByPosition(position: Vector3$1): Vector3$1 | null;
608
+ getNormalByLatLong(latitudeDeg: number, longitudeDeg: number): Vector3$1 | null;
609
+ sampleTerrainByDirection(direction: Vector3$1): TerrainSurfaceSample;
610
+ sampleTerrainByPosition(position: Vector3$1): TerrainSurfaceSample;
611
+ sampleTerrainByLatLong(latitudeDeg: number, longitudeDeg: number): TerrainSurfaceSample;
612
+ getTileByDirection(direction: Vector3$1): TerrainTile | null;
613
+ getTileByPosition(position: Vector3$1): TerrainTile | null;
614
+ getTileByLatLong(latitudeDeg: number, longitudeDeg: number): TerrainTile | null;
615
+ getTileBoundsByDirection(direction: Vector3$1): TerrainTileBounds | null;
616
+ getTileBoundsByPosition(position: Vector3$1): TerrainTileBounds | null;
617
+ getTileBoundsByLatLong(latitudeDeg: number, longitudeDeg: number): TerrainTileBounds | null;
618
+ /** Batch sample; `directions` is a Float32Array of xyz triples. */
619
+ sampleTerrainBatchByDirection(directions: Float32Array): TerrainSurfaceSampleBatch;
620
+ }
621
+ interface RaycastOptions {
622
+ maxSteps?: number;
623
+ refinementSteps?: number;
624
+ maxDistance?: number;
625
+ }
626
+ interface TerrainRaycastResult {
627
+ position: Vector3$1;
628
+ normal: Vector3$1;
629
+ distance: number;
630
+ }
631
+ interface TerrainRaycast {
632
+ pick(ray: Ray, options?: RaycastOptions): TerrainRaycastResult | null;
633
+ }
634
+
635
+ type TerrainMeshParams = {
636
+ innerTileSegments: number;
637
+ maxNodes: number;
638
+ material: NodeMaterial;
639
+ /**
640
+ * Reverse tile triangle winding. Cube-sphere surfaces set this so the
641
+ * planet's outer shell is front-facing and renders with `FrontSide`.
642
+ */
643
+ flipWinding: boolean;
644
+ };
645
+ declare class TerrainMesh extends InstancedMesh {
646
+ private _innerTileSegments;
647
+ private _maxNodes;
648
+ private _flipWinding;
649
+ terrainRaycast: TerrainRaycast | null;
650
+ constructor(params?: Partial<TerrainMeshParams>);
651
+ get innerTileSegments(): number;
652
+ set innerTileSegments(tileSegments: number);
653
+ get flipWinding(): boolean;
654
+ set flipWinding(flip: boolean);
655
+ get maxNodes(): number;
656
+ set maxNodes(maxNodes: number);
657
+ raycast(raycaster: Raycaster, intersects: Intersection[]): void;
658
+ }
659
+
660
+ interface TileBoundsContext {
661
+ data: Float32Array<ArrayBuffer>;
662
+ attribute: StorageBufferAttribute;
663
+ node: StorageBufferNode;
664
+ }
665
+
666
+ type ComputeStageCallback = (nodeIndex: Node, globalVertexIndex: Node, uv: Node, localCoordinates: Node, texelSize: Node) => void;
667
+ type ComputePipeline = ComputeStageCallback[];
668
+
669
+ /**
670
+ * Default compile + execute tasks — uses terrainFieldStageTask as the leaf.
671
+ * Derived from the same factory as user pipelines to avoid duplicated logic.
672
+ */
673
+ declare const compileComputeTask: _hello_terrain_work.Task<{
674
+ execute: (renderer: WebGPURenderer, instanceCount: number) => void;
675
+ }, string, unknown>;
676
+ declare const executeComputeTask: _hello_terrain_work.Task<any, string, {
677
+ renderer: WebGPURenderer;
678
+ }>;
679
+ /**
680
+ * Factory for user-extensible pipelines.
681
+ *
682
+ * Users who add custom compute stages create their own stage tasks using
683
+ * the accumulation pattern (`get()` predecessor, spread, append), then pass
684
+ * their leaf stage to this helper to get compile + execute tasks.
685
+ *
686
+ * @example
687
+ * ```ts
688
+ * const erosionStageTask = task((get, work) => {
689
+ * const upstream = get(elevationFieldStageTask);
690
+ * return work((): ComputePipeline => [
691
+ * ...upstream,
692
+ * (nodeIndex, globalVertexIndex, uv) => {
693
+ * // custom erosion logic
694
+ * },
695
+ * ]);
696
+ * });
548
697
  *
549
- * This exists to localize future planet work behind the `Surface` interface.
550
- * Topology remapping across face edges is intentionally TODO.
698
+ * const { compile, execute } = createComputePipelineTasks(erosionStageTask);
699
+ * ```
551
700
  */
552
- declare function createCubeSphereSurface(_cfg: CubeSphereSurfaceConfig): Surface;
701
+ declare function createComputePipelineTasks(leafStageTask: TaskRef<ComputePipeline>): {
702
+ compile: _hello_terrain_work.Task<{
703
+ execute: (renderer: WebGPURenderer, instanceCount: number) => void;
704
+ }, string, unknown>;
705
+ execute: _hello_terrain_work.Task<any, string, {
706
+ renderer: WebGPURenderer;
707
+ }>;
708
+ };
553
709
 
554
- declare function createTileCompute(leafStorage: LeafStorageState, uniforms: TerrainUniformsContext): {
710
+ declare function createTileCompute(leafStorage: LeafStorageState, uniforms: TerrainUniformsContext, projection?: TopologyProjection): {
555
711
  tileLevel: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node]>;
712
+ tileFace: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node]>;
556
713
  tileOriginVec2: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node]>;
557
714
  tileSize: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node]>;
715
+ tileFaceUV: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node, number | Node, number | Node]>;
558
716
  rootUVCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node, number | Node, number | Node]>;
559
717
  tileVertexWorldPositionCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node, number | Node, number | Node]>;
560
718
  };
@@ -567,6 +725,10 @@ type TerrainQueryConfig = {
567
725
  innerTileSegments: number;
568
726
  elevationScale: number;
569
727
  maxLevel: number;
728
+ /** Topology projection; `cubeSphere` enables the direction/lat-long queries. */
729
+ projection: TopologyProjection;
730
+ /** Sphere radius in world units (cube-sphere projection only). */
731
+ radius: number;
570
732
  };
571
733
  interface CpuTerrainCache {
572
734
  readonly generation: number;
@@ -580,11 +742,27 @@ interface CpuTerrainCache {
580
742
  getGlobalElevationRange(): ElevationRange | null;
581
743
  sampleTerrainBatch(positions: Float32Array): TerrainSampleBatch;
582
744
  sampleTerrain(worldX: number, worldZ: number): TerrainSample;
745
+ getElevationByDirection(direction: Vector3$1): number | null;
746
+ getElevationByPosition(position: Vector3$1): number | null;
747
+ getElevationByLatLong(latitudeDeg: number, longitudeDeg: number): number | null;
748
+ getNormalByDirection(direction: Vector3$1): Vector3$1 | null;
749
+ getNormalByPosition(position: Vector3$1): Vector3$1 | null;
750
+ getNormalByLatLong(latitudeDeg: number, longitudeDeg: number): Vector3$1 | null;
751
+ sampleTerrainByDirection(direction: Vector3$1): TerrainSurfaceSample;
752
+ sampleTerrainByPosition(position: Vector3$1): TerrainSurfaceSample;
753
+ sampleTerrainByLatLong(latitudeDeg: number, longitudeDeg: number): TerrainSurfaceSample;
754
+ getTileByDirection(direction: Vector3$1): TerrainTile | null;
755
+ getTileByPosition(position: Vector3$1): TerrainTile | null;
756
+ getTileByLatLong(latitudeDeg: number, longitudeDeg: number): TerrainTile | null;
757
+ getTileBoundsByDirection(direction: Vector3$1): TerrainTileBounds | null;
758
+ getTileBoundsByPosition(position: Vector3$1): TerrainTileBounds | null;
759
+ getTileBoundsByLatLong(latitudeDeg: number, longitudeDeg: number): TerrainTileBounds | null;
760
+ sampleTerrainBatchByDirection(directions: Float32Array): TerrainSurfaceSampleBatch;
583
761
  }
584
762
 
585
763
  interface QuadtreeConfigState {
586
764
  state: QuadtreeState;
587
- surface: Surface;
765
+ topology: Topology;
588
766
  }
589
767
  interface LeafGpuBufferState extends LeafStorageState {
590
768
  count: number;
@@ -597,6 +775,8 @@ interface ElevationFieldContext {
597
775
  interface TerrainQueryContext {
598
776
  cache: CpuTerrainCache;
599
777
  query: TerrainQuery;
778
+ /** Cube-sphere query; `null` unless the topology uses the cubeSphere projection. */
779
+ sphereQuery: TerrainSphereQuery | null;
600
780
  shapeKey: string;
601
781
  }
602
782
  /** Task refs for the standard terrain pipeline. */
@@ -604,7 +784,7 @@ interface TerrainTasks {
604
784
  instanceId: TaskRef<string>;
605
785
  quadtreeConfig: TaskRef<QuadtreeConfigState>;
606
786
  quadtreeUpdate: TaskRef<LeafSet>;
607
- surface: TaskRef<Surface>;
787
+ topology: TaskRef<Topology>;
608
788
  leafStorage: TaskRef<LeafStorageState>;
609
789
  leafGpuBuffer: TaskRef<LeafGpuBufferState>;
610
790
  gpuSpatialIndexStorage: TaskRef<GpuSpatialIndexContext>;
@@ -641,8 +821,10 @@ declare const createElevationFieldContextTask: _hello_terrain_work.Task<{
641
821
  }, string, unknown>;
642
822
  declare const tileNodesTask: _hello_terrain_work.Task<{
643
823
  tileLevel: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
824
+ tileFace: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
644
825
  tileOriginVec2: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
645
826
  tileSize: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
827
+ tileFaceUV: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node, number | three_webgpu.Node, number | three_webgpu.Node]>;
646
828
  rootUVCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node, number | three_webgpu.Node, number | three_webgpu.Node]>;
647
829
  tileVertexWorldPositionCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node, number | three_webgpu.Node, number | three_webgpu.Node]>;
648
830
  }, string, unknown>;
@@ -689,22 +871,24 @@ declare const origin: _hello_terrain_work.ParamRef<{
689
871
  z: number;
690
872
  }>;
691
873
  /**
692
- * Number of segments per inner tile edge.
693
- * Effective edge vertex count is `innerTileSegments + 3`.
874
+ * Default number of segments per inner tile edge. The effective edge vertex
875
+ * count is `innerTileSegments + 3`
694
876
  */
695
877
  declare const innerTileSegments: _hello_terrain_work.ParamRef<number>;
696
878
  /** Skirt scale factor. */
697
879
  declare const skirtScale: _hello_terrain_work.ParamRef<number>;
698
880
  /** Elevation vertical scale. */
699
881
  declare const elevationScale: _hello_terrain_work.ParamRef<number>;
882
+ /** Sphere radius in world units (cube-sphere projection only). */
883
+ declare const radius: _hello_terrain_work.ParamRef<number>;
700
884
  /** Maximum quadtree nodes. */
701
885
  declare const maxNodes: _hello_terrain_work.ParamRef<number>;
702
886
  /** Maximum quadtree subdivision level. */
703
887
  declare const maxLevel: _hello_terrain_work.ParamRef<number>;
704
888
  /** Quadtree update configuration (camera, mode, etc.). */
705
889
  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>;
890
+ /** Optional custom terrain topology; defaults to bounded flat topology when null. */
891
+ declare const topology: _hello_terrain_work.ParamRef<Topology | null>;
708
892
  /** Terrain field texture filter mode. */
709
893
  declare const terrainFieldFilter: _hello_terrain_work.ParamRef<"nearest" | "linear">;
710
894
  /** Terrain elevation control function (per vertex, in gpu compute) */
@@ -727,11 +911,11 @@ declare const elevationFn: _hello_terrain_work.ParamRef<ElevationCallback>;
727
911
  declare const positionNodeTask: _hello_terrain_work.Task<three_src_nodes_TSL_js.ShaderCallNodeInternal, string, unknown>;
728
912
 
729
913
  /**
730
- * Derives the terrain surface from `rootSize` and `origin`.
914
+ * Derives the terrain topology from `rootSize` and `origin`.
731
915
  * Automatically recomputes when either param changes, keeping the
732
916
  * quadtree refinement in sync with the GPU-side tile positioning.
733
917
  */
734
- declare const surfaceTask: _hello_terrain_work.Task<Surface, string, unknown>;
918
+ declare const topologyTask: _hello_terrain_work.Task<Topology, string, unknown>;
735
919
  declare const quadtreeConfigTask: _hello_terrain_work.Task<QuadtreeConfigState, string, unknown>;
736
920
  declare const quadtreeUpdateTask: _hello_terrain_work.Task<LeafSet, string, unknown>;
737
921
  /**
@@ -763,14 +947,13 @@ declare const createUniformsTask: _hello_terrain_work.Task<TerrainUniformsContex
763
947
  */
764
948
  declare const updateUniformsTask: _hello_terrain_work.Task<TerrainUniformsContext, string, unknown>;
765
949
 
766
- declare function terrainGraph(): TerrainGraph;
767
950
  /** All terrain task refs for direct access. */
768
951
  declare const terrainTasks: {
769
952
  readonly instanceId: _hello_terrain_work.Task<`${string}-${string}-${string}-${string}-${string}`, string, unknown>;
770
953
  readonly quadtreeConfig: _hello_terrain_work.Task<QuadtreeConfigState, string, unknown>;
771
954
  readonly quadtreeUpdate: _hello_terrain_work.Task<LeafSet, string, unknown>;
772
955
  readonly leafStorage: _hello_terrain_work.Task<LeafStorageState, string, unknown>;
773
- readonly surface: _hello_terrain_work.Task<Surface, string, unknown>;
956
+ readonly topology: _hello_terrain_work.Task<Topology, string, unknown>;
774
957
  readonly leafGpuBuffer: _hello_terrain_work.Task<{
775
958
  count: number;
776
959
  data: Int32Array<ArrayBuffer>;
@@ -789,8 +972,10 @@ declare const terrainTasks: {
789
972
  }, string, unknown>;
790
973
  readonly createTileNodes: _hello_terrain_work.Task<{
791
974
  tileLevel: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
975
+ tileFace: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
792
976
  tileOriginVec2: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
793
977
  tileSize: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node]>;
978
+ tileFaceUV: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node, number | three_webgpu.Node, number | three_webgpu.Node]>;
794
979
  rootUVCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node, number | three_webgpu.Node, number | three_webgpu.Node]>;
795
980
  tileVertexWorldPositionCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | three_webgpu.Node, number | three_webgpu.Node, number | three_webgpu.Node]>;
796
981
  }, string, unknown>;
@@ -818,6 +1003,7 @@ declare const terrainTasks: {
818
1003
  }>;
819
1004
  readonly terrainRaycast: _hello_terrain_work.Task<TerrainRaycast, string, unknown>;
820
1005
  };
1006
+ declare function terrainGraph(): TerrainGraph;
821
1007
 
822
1008
  type ComputeDeviceLimits = {
823
1009
  maxWorkgroupSizeX: number;
@@ -828,7 +1014,10 @@ declare function getDeviceComputeLimits(renderer: WebGPURenderer): ComputeDevice
828
1014
 
829
1015
  declare function createTerrainSampler(params: CreateTerrainSamplerParams): TerrainSampler;
830
1016
 
1017
+ /** Flat (heightfield) query, keyed on world XZ. */
831
1018
  declare function createTerrainQuery(cache: CpuTerrainCache): TerrainQuery;
1019
+ /** Cube-sphere query, keyed on a direction / position / lat-long. */
1020
+ declare function createTerrainSphereQuery(cache: CpuTerrainCache): TerrainSphereQuery;
832
1021
 
833
1022
  type CpuRaycastConfig = {
834
1023
  rootSize: number;
@@ -836,14 +1025,68 @@ type CpuRaycastConfig = {
836
1025
  originZ: number;
837
1026
  minY: number;
838
1027
  maxY: number;
1028
+ /** Topology projection; `cubeSphere` selects the radial sphere raycast. */
1029
+ projection?: TopologyProjection;
1030
+ /** Planet center (cube-sphere only). */
1031
+ centerX?: number;
1032
+ centerY?: number;
1033
+ centerZ?: number;
1034
+ /** Base sphere radius (cube-sphere only). */
1035
+ radius?: number;
1036
+ /** Inner/outer radial bounds of the terrain shell (cube-sphere only). */
1037
+ minRadius?: number;
1038
+ maxRadius?: number;
839
1039
  };
840
1040
 
841
1041
  type TerrainRaycastConfig = CpuRaycastConfig;
842
1042
  declare function createTerrainRaycast(params: {
843
1043
  getTerrainQuery: () => TerrainQuery | null;
1044
+ getSphereQuery: () => TerrainSphereQuery | null;
844
1045
  getConfig: () => TerrainRaycastConfig;
845
1046
  }): TerrainRaycast;
846
1047
 
1048
+ type CubeFaceBasis = {
1049
+ forward: Node;
1050
+ right: Node;
1051
+ up: Node;
1052
+ };
1053
+ /** Per-face basis vectors selected by the dynamic face index. */
1054
+ declare function cubeFaceBasis(face: Node): CubeFaceBasis;
1055
+ /**
1056
+ * Cube-space point for face-local (u, v) in [0, 1]:
1057
+ * cube = forward + (2u-1) * right + (2v-1) * up
1058
+ */
1059
+ declare function cubeFacePoint(basis: CubeFaceBasis, u: Node, v: Node): Node;
1060
+ /** Unit-sphere direction for face-local (u, v). */
1061
+ declare function cubeFaceDirection(basis: CubeFaceBasis, u: Node, v: Node): Node;
1062
+ /**
1063
+ * Project a basis axis onto the tangent plane at `dir` and normalize.
1064
+ * Used to build the sphere tangent frame for normal reconstruction.
1065
+ */
1066
+ declare function tangentFromAxis(dir: Node, axis: Node): Node;
1067
+ /**
1068
+ * Reconstruct a unit tangent-space normal from its packed horizontal
1069
+ * components: `ny = sqrt(max(0, 1 - nx² - nz²))`.
1070
+ */
1071
+ declare function unpackTangentNormal(nx: Node, nz: Node): Node;
1072
+ /**
1073
+ * Rotate a tangent-space normal into the sphere tangent frame
1074
+ * `(tu, dir, tv)` at `dir` and normalize.
1075
+ *
1076
+ * Mirrors: the CPU `computeSphereNormal` in `query/cpu-terrain-cache.ts`.
1077
+ */
1078
+ declare function sphereTangentFrameNormal(dir: Node, basis: CubeFaceBasis, tangentNormal: Node): Node;
1079
+ /**
1080
+ * Pick the cube face index (0..5) whose normal axis dominates `dir`.
1081
+ * GPU mirror of `directionToFace` in `quadtree/topology/cubeSphereInverse.ts`.
1082
+ */
1083
+ declare function cubeFaceFromDirection(dir: Node): Node;
1084
+ /**
1085
+ * Face-local (u, v) in [0, 1] for a direction known to fall on `face`.
1086
+ * GPU mirror of `directionToFaceUV` in `quadtree/topology/cubeSphereInverse.ts`.
1087
+ */
1088
+ declare function cubeFaceUVFromDirection(basis: CubeFaceBasis, dir: Node): Node;
1089
+
847
1090
  /**
848
1091
  * Maps a value or node from texture space [0, 1] to vector space [-1, 1].
849
1092
  *
@@ -916,5 +1159,5 @@ declare const voronoiCells: three_src_nodes_TSL_js.ShaderNodeFn<[three_tsl.Proxi
916
1159
  uv: Node;
917
1160
  }>]>;
918
1161
 
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 };
1162
+ export { ArrayTextureBackend, AtlasBackend, CUBE_FACES, CUBE_FACE_COUNT, Dir, TerrainGeometry, TerrainMesh, U32_EMPTY, allocLeafSet, allocSeamTable, beginUpdate, blendAngleCorrectedNormals, buildLeafIndex, buildSeams2to1, compileComputeTask, createComputePipelineTasks, createCubeSphereTopology, createElevationFieldContextTask, createFlatTopology, createInfiniteFlatTopology, createSpatialIndex, createState, createTerrainFieldStorage, createTerrainFieldTextureTask, createTerrainQuery, createTerrainRaycast, createTerrainSampler, createTerrainSamplerTask, createTerrainSphereQuery, createTerrainUniforms, createUniformsTask, cubeFaceBasis, cubeFaceDirection, cubeFaceFromDirection, cubeFacePoint, cubeFaceUVFromDirection, deriveNormalZ, directionToFace, directionToFaceUV, directionToLatLong, elevationFieldStageTask, elevationFn, elevationScale, executeComputeTask, faceUVToCube, getDeviceComputeLimits, gpuSpatialIndexStorageTask, gpuSpatialIndexUploadTask, innerTileSegments, instanceIdTask, isSkirtUV, isSkirtVertex, latLongToDirection, leafGpuBufferTask, leafStorageTask, loadTerrainField, loadTerrainFieldElevation, loadTerrainFieldNormal, maxLevel, maxNodes, origin, packTerrainFieldSample, positionNodeTask, quadtreeConfigTask, quadtreeUpdate, quadtreeUpdateTask, radius, resetLeafSet, resetSeamTable, rootSize, sampleTerrainField, sampleTerrainFieldElevation, skirtScale, sphereTangentFrameNormal, storeTerrainField, tangentFromAxis, terrainFieldFilter, terrainFieldStageTask, terrainGraph, terrainQueryTask, terrainRaycastTask, terrainReadbackTask, terrainTasks, textureSpaceToVectorSpace, tileNodesTask, topology, topologyTask, unpackTangentNormal, update, updateUniformsTask, vElevation, vGlobalVertexIndex, vectorSpaceToTextureSpace, voronoiCells };
1163
+ export type { ComputePipeline, ComputeStageCallback, CreateTerrainSamplerParams, CubeFace, CubeFaceBasis, CubeSphereTopologyConfig, ElevationCallback, ElevationFieldContext, ElevationParams, ElevationRange, FlatTopologyConfig, GpuSpatialIndexContext, InfiniteFlatTopologyConfig, IntNodeInput, LeafGpuBufferState, LeafSet, LeafStorageState, LodMode, QuadtreeConfig, QuadtreeConfigState, QuadtreeState, RaycastOptions, SeamTable, SpatialIndex, TerrainFieldStorage, TerrainFieldStorageBackendType, TerrainFieldStorageFormat, TerrainFieldStorageOptions, TerrainGraph, TerrainQuery, TerrainQueryContext, TerrainRaycast, TerrainRaycastConfig, TerrainRaycastResult, TerrainSample, TerrainSampleBatch, TerrainSampler, TerrainSphereQuery, TerrainSurfaceSample, TerrainSurfaceSampleBatch, TerrainTasks, TerrainTile, TerrainTileBounds, TerrainUniformsContext, TerrainUniformsParams, TileBounds, TileId, Topology, TopologyProjection, UpdateParams, Vec3, Vec3Mutable };