@navaramap/three 0.0.0 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +70 -0
  2. package/dist/assets/fontWorker-8oWvyvwL.js +2 -0
  3. package/dist/assets/fontWorker-8oWvyvwL.js.map +1 -0
  4. package/dist/assets/index-D9G7kIEj.js +25 -0
  5. package/dist/assets/index-D9G7kIEj.js.map +1 -0
  6. package/dist/assets/{navara_wasm_font_worker_bg-665OLxjy.wasm → navara_wasm_font_worker_bg-CkZP9l2i.wasm} +0 -0
  7. package/dist/assets/{navara_wasm_worker_bg-BXhKC5bE.wasm → navara_wasm_worker_bg-Q8zrkLCR.wasm} +0 -0
  8. package/dist/draco_decoder.wasm +0 -0
  9. package/dist/draco_decoder2.wasm +0 -0
  10. package/dist/index.d.ts +587 -67
  11. package/dist/index.js +5640 -4944
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.umd.cjs +238 -211
  14. package/dist/index.umd.cjs.map +1 -1
  15. package/package.json +35 -34
  16. package/src/core/EffectDesc.ts +2 -4
  17. package/src/core/MeshDesc.ts +1 -2
  18. package/src/core/MeshDescWithSelectiveEffect.ts +1 -2
  19. package/src/declutter/DeclutterManager.test.ts +235 -0
  20. package/src/declutter/DeclutterManager.ts +256 -0
  21. package/src/declutter/index.ts +7 -0
  22. package/src/declutter/kernel.ts +46 -0
  23. package/src/declutter/types.ts +61 -0
  24. package/src/declutter/wasmKernel.ts +13 -0
  25. package/src/device.ts +18 -4
  26. package/src/evaluations/FeatureEvaluator.ts +21 -0
  27. package/src/event/context.ts +17 -0
  28. package/src/event/features/modelGpuBytes.ts +1 -3
  29. package/src/event/hillshade.test.ts +7 -13
  30. package/src/event/index.ts +2 -3
  31. package/src/event/tileHandler.ts +54 -0
  32. package/src/index.ts +143 -33
  33. package/src/material/enhancer/ShaderReplacer.ts +1 -2
  34. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/mutates.ts +6 -0
  35. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/types.ts +11 -0
  36. package/src/material/enhancer/tileComposite/tileCompositeBaseEnhancer/mutates.test.ts +83 -0
  37. package/src/material/enhancer/tileComposite/tileCompositeBaseEnhancer/mutates.ts +19 -4
  38. package/src/material/enhancer/tileComposite/tileElevationHeatmapEnhancer/shader.ts +9 -2
  39. package/src/mesh/batchTexture.ts +2 -4
  40. package/src/mesh/batchedSdfText.ts +61 -1
  41. package/src/mesh/sdfText.ts +170 -5
  42. package/src/mesh/sprite/instancedSprite.ts +245 -3
  43. package/src/mesh/tile/drapeResolver.ts +76 -0
  44. package/src/mesh/{tile.test.ts → tile/index.test.ts} +16 -6
  45. package/src/mesh/{tile.ts → tile/index.ts} +124 -392
  46. package/src/mesh/tile/rasterDrapeResolver.ts +423 -0
  47. package/src/mesh/tile/vectorDrapeResolver.ts +404 -0
  48. package/src/orchestrators/RenderPassOrchestrator.ts +25 -0
  49. package/src/passes/CustomRenderPass.ts +21 -5
  50. package/src/passes/RenderTargetCopyPass.ts +24 -2
  51. package/src/test-utils/engine.ts +53 -0
  52. package/src/tileTexture/TileTextureCache.test.ts +2 -0
  53. package/src/tileTexture/TileTextureCache.ts +1 -0
  54. package/src/tileTexture/TileTextureCompositor.test.ts +107 -0
  55. package/src/tileTexture/TileTextureCompositor.ts +248 -32
  56. package/src/tileTexture/__snapshots__/CompositeShader.snapshot.test.ts.snap +36 -16
  57. package/src/tileTexture/types.ts +7 -0
  58. package/src/utils/demNoDataColor.test.ts +47 -0
  59. package/src/utils/demNoDataColor.ts +48 -0
  60. package/src/utils/fixedGpuFootprint.test.ts +110 -0
  61. package/src/utils/fixedGpuFootprint.ts +162 -0
  62. package/src/worker/tasks/toCreasedNormals.ts +2 -7
  63. package/dist/assets/fontWorker-CEJEfRpb.js +0 -2
  64. package/dist/assets/fontWorker-CEJEfRpb.js.map +0 -1
  65. package/dist/assets/index-BUB-ijMO.js +0 -12
  66. package/dist/assets/index-BUB-ijMO.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -863,7 +863,7 @@ export declare class BatchedFeatureMesh<Buf extends BufferGeometry<BatchedFeatur
863
863
  clone(): this;
864
864
  }
865
865
 
866
- export declare class BatchedSdfTextMesh extends InstancedMesh<SDFTextMesh> implements PickableMesh {
866
+ export declare class BatchedSdfTextMesh extends InstancedMesh<SDFTextMesh> implements PickableMesh, DeclutterParticipant {
867
867
  readonly ctx: EventContext;
868
868
  /** The font identifier from material — may be a family name or a URL. */
869
869
  private _fontIdentifier;
@@ -882,6 +882,13 @@ export declare class BatchedSdfTextMesh extends InstancedMesh<SDFTextMesh> imple
882
882
  */
883
883
  private _loadedFaceUrls;
884
884
  constructor(ctx: EventContext, m: TextMesh, fontIdentifier: string, options: InstancedMeshOptions, loadedFaceUrls?: Set<string>);
885
+ collectDeclutterCandidates(out: DeclutterCandidate[]): void;
886
+ applyDeclutter(handle: number, hidden: boolean): void;
887
+ stepDeclutterFade(deltaMs: number): boolean;
888
+ /** Candidates changed (text, style, position, visibility): ask the shared
889
+ * declutter pass to re-place on its next update. */
890
+ private _markDeclutterDirty;
891
+ setActive(active: boolean): void;
885
892
  /**
886
893
  * After an atlas eviction, re-prepare and force-rebuild any visible mesh whose
887
894
  * shaped text is now stale (its pinned glyphs may have been evicted before the
@@ -902,6 +909,7 @@ export declare class BatchedSdfTextMesh extends InstancedMesh<SDFTextMesh> imple
902
909
  setFeatureShowByBatchIndex(batchIndex: number, rawVisible: boolean): void;
903
910
  setFeatureHeightByBatchIndex(batchIndex: number, height: number): void;
904
911
  setFeatureSizeByBatchIndex(batchIndex: number, size: number): void;
912
+ setFeatureDeclutterPriorityByBatchIndex(batchIndex: number, priority: number): void;
905
913
  setFeatureOpacityByBatchIndex(batchIndex: number, opacity: number): void;
906
914
  dispose(): void;
907
915
  }
@@ -951,6 +959,28 @@ declare class BillboardMaterial {
951
959
  set clampToGround(value: boolean | null | undefined);
952
960
  get color(): number | undefined;
953
961
  set color(value: number | null | undefined);
962
+ /**
963
+ * Placement priority for decluttering; higher wins. Only meaningful when
964
+ * `declutter` is enabled. Defaults to `0.0`.
965
+ */
966
+ get declutterPriority(): number | undefined;
967
+ /**
968
+ * Placement priority for decluttering; higher wins. Only meaningful when
969
+ * `declutter` is enabled. Defaults to `0.0`.
970
+ */
971
+ set declutterPriority(value: number | null | undefined);
972
+ /**
973
+ * Participate in screen-space decluttering: when labels/sprites overlap
974
+ * on screen, lower-priority ones are hidden. Defaults to `true`; set to
975
+ * `false` to draw every label unconditionally.
976
+ */
977
+ get declutter(): boolean | undefined;
978
+ /**
979
+ * Participate in screen-space decluttering: when labels/sprites overlap
980
+ * on screen, lower-priority ones are hidden. Defaults to `true`; set to
981
+ * `false` to draw every label unconditionally.
982
+ */
983
+ set declutter(value: boolean | null | undefined);
954
984
  get depthTest(): boolean | undefined;
955
985
  set depthTest(value: boolean | null | undefined);
956
986
  /**
@@ -1791,6 +1821,12 @@ declare class Core {
1791
1821
  getMartini(martini_id: ReconstructableEntity): TransferableMartini | undefined;
1792
1822
  getMemoryStats(): MemoryStats_2 | undefined;
1793
1823
  getParentTile(handle: bigint): TransferableTile | undefined;
1824
+ /**
1825
+ * The WebMercator raster tiles to bake into per-layer drape render targets for a
1826
+ * terrain tile (baked layers only; empty on WebMercator terrain, which drapes 1:1
1827
+ * through the per-slot material path).
1828
+ */
1829
+ getRasterTileStates(handle: bigint): RasterTileState[];
1794
1830
  getTile(handle: bigint): TransferableTile | undefined;
1795
1831
  getTileElevationDecoder(handle: bigint): ElevationDecoder | undefined;
1796
1832
  getTransferablePolygonBatchedFeature(batched_feature_id: bigint): ReturnedTransferablePolygonBatchedFeature | undefined;
@@ -1813,6 +1849,12 @@ declare class Core {
1813
1849
  newBufferU8(byte_length: number, f: Function): number | undefined;
1814
1850
  newBufferU8Cloned(data: Uint8Array): number | undefined;
1815
1851
  newExternalBuffer(byte_length: number): number | undefined;
1852
+ /**
1853
+ * Monotonic revision that changes only when the raster drape resolution could have
1854
+ * changed. The web renderer reads it once per frame and skips the per-terrain-tile
1855
+ * `getRasterTileStates` calls while it is unchanged.
1856
+ */
1857
+ rasterRevision(): number;
1816
1858
  readAllBatchedProperties(renderable_feature_bits: bigint, callback: Function): void;
1817
1859
  readEvents(): Events | undefined;
1818
1860
  readFilteredBatchedProperties(renderable_feature_bits: bigint, keys: any[], callback: Function): void;
@@ -1856,6 +1898,13 @@ declare class Core {
1856
1898
  */
1857
1899
  setDynamicSse(settings: DynamicSse): void;
1858
1900
  setExternalBuffer(handle: number, bits: bigint, byte_length: number): void;
1901
+ /**
1902
+ * Reports the estimated GPU bytes of fixed, screen-sized allocations
1903
+ * (the postprocessing render-target stack). Counted in the ledger's
1904
+ * usage so the tile budget binds against the remaining memory. The JS
1905
+ * side re-reports on init, resize, and pass-list changes.
1906
+ */
1907
+ setFixedGpuBytes(bytes: number): void;
1859
1908
  setFrustum(fov?: number | null, near?: number | null, far?: number | null): void;
1860
1909
  setGlobeColor(value: number): void;
1861
1910
  setGlobeElevationColormap(value: Float32Array): void;
@@ -2163,7 +2212,7 @@ export declare class CustomRenderPass extends RenderPass {
2163
2212
  constructor(scenes: Scenes, camera: PerspectiveCamera, inputBuffer: WebGLRenderTarget, globe: Globe, options?: CustomRenderPassOptions);
2164
2213
  protected _renderWithLight(renderer: WebGLRenderer, scene: Scene): void;
2165
2214
  render(renderer: WebGLRenderer, inputBuffer: WebGLRenderTarget | null, _outputBuffer: WebGLRenderTarget | null): void;
2166
- setDepthTexture(depthTexture: DepthTexture): void;
2215
+ setDepthTexture(): void;
2167
2216
  setSize(width: number, height: number): void;
2168
2217
  protected _renderDrapedMesh(renderer: WebGLRenderer): void;
2169
2218
  }
@@ -2233,6 +2282,179 @@ declare class DataRequestEvent {
2233
2282
  url: string;
2234
2283
  }
2235
2284
 
2285
+ /**
2286
+ * One label/sprite competing for screen space in a declutter pass.
2287
+ *
2288
+ * The local box is expressed around the projected anchor in the shader's
2289
+ * billboard-local units — CSS pixels when `sizeInMeters` is false, meters
2290
+ * otherwise — with +X right and +Y up (view-space convention). The manager
2291
+ * converts it to a screen-pixel AABB using the same `nvr_pxToWorld` math the
2292
+ * vertex shaders use, so the collision box matches what is actually drawn.
2293
+ */
2294
+ declare type DeclutterCandidate = {
2295
+ /** World-space anchor in ECEF meters (f64), before the height offset. */
2296
+ anchorX: number;
2297
+ anchorY: number;
2298
+ anchorZ: number;
2299
+ /** Extra offset in meters along the spherical surface normal — CPU mirror
2300
+ * of `mvr_getMvHeightOffset` (sprite_height_pars_vertex.glsl). */
2301
+ addHeight: number;
2302
+ /** Local box around the anchor (px or meters, see `sizeInMeters`). */
2303
+ minX: number;
2304
+ maxX: number;
2305
+ minY: number;
2306
+ maxY: number;
2307
+ /** Unit of the local box, mirroring the material's `sizeInMeters`. */
2308
+ sizeInMeters: boolean;
2309
+ /** Placement priority: higher wins an overlap. */
2310
+ priority: number;
2311
+ /** Whether the label is currently shown (its fade target is "visible").
2312
+ * Feeds placement hysteresis: shown labels win equal-priority ties and
2313
+ * tolerate marginal overlaps, so slow camera drift doesn't make near-tied
2314
+ * labels flicker in and out. */
2315
+ isShown: boolean;
2316
+ /** Participant that owns this candidate; receives the placement result. */
2317
+ owner: DeclutterParticipant;
2318
+ /** Participant-defined id (mesh index for text batches, instance index for
2319
+ * sprites) passed back to `applyDeclutter`. */
2320
+ handle: number;
2321
+ };
2322
+
2323
+ /**
2324
+ * The pure numeric placement kernel used by {@link DeclutterManager}.
2325
+ *
2326
+ * Given a flat list of candidates and the camera, it projects each anchor to a
2327
+ * screen-pixel box, sorts by priority (with placement hysteresis), and greedily
2328
+ * claims space in a uniform screen-space grid — returning one `hidden` flag
2329
+ * (`0` = shown, `1` = hidden) per candidate, in input order.
2330
+ *
2331
+ * The production implementation is the Rust `declutterPlace` in
2332
+ * `navara_wasm_api` (see {@link wasmDeclutterKernel}); the projection, collision
2333
+ * grid, and greedy placement all live there. Injecting it keeps the manager free
2334
+ * of an import-time WASM dependency, so its orchestration is unit-tested with a
2335
+ * stub. Placement correctness itself is covered by the crate's Rust tests.
2336
+ */
2337
+ declare type DeclutterKernel = {
2338
+ /**
2339
+ * @param candidates Packed `f64` data, {@link CANDIDATE_STRIDE} values per
2340
+ * candidate; pass a subarray of exactly `n * CANDIDATE_STRIDE`.
2341
+ * @param view Column-major view matrix (inverse of `camera.matrixWorld`), 16.
2342
+ * @param proj Column-major projection matrix, 16.
2343
+ * @returns One `hidden` flag (`0`/`1`) per candidate, in input order.
2344
+ */
2345
+ place(candidates: Float64Array, view: Float64Array, proj: Float64Array, cameraX: number, cameraY: number, cameraZ: number, near: number, widthPx: number, heightPx: number, fovRad: number, paddingPx: number, hysteresisPx: number): Uint8Array;
2346
+ };
2347
+
2348
+ /**
2349
+ * Shared screen-space label decluttering.
2350
+ *
2351
+ * Text batches and sprite meshes register as participants; each placement
2352
+ * pass packs every candidate label into a flat buffer and hands it to the
2353
+ * Rust {@link DeclutterKernel}, which projects the anchors, sorts by priority,
2354
+ * and greedily claims screen space — losers are hidden through the meshes'
2355
+ * declutter channels (`uDeclutterHide` uniform / `instanceDeclutterHide`
2356
+ * attribute), which are separate from user-driven `show` state and free to
2357
+ * toggle every pass.
2358
+ *
2359
+ * Placement is hysteretic: currently-shown labels win equal-priority ties
2360
+ * and tolerate {@link DeclutterManager.HYSTERESIS_PX} of marginal overlap
2361
+ * before eviction, so near-ties don't flicker while the camera drifts.
2362
+ *
2363
+ * The pass is view-dependent, so it runs from the render loop — but throttled:
2364
+ * placement only re-runs when the camera or the label set actually changed,
2365
+ * and at most once per {@link DeclutterManager.MIN_INTERVAL_MS}. Callers run
2366
+ * it *before* rendering so results land in the same frame.
2367
+ */
2368
+ declare class DeclutterManager {
2369
+ private readonly _kernel;
2370
+ /** Minimum time between placement passes (ms). */
2371
+ static readonly MIN_INTERVAL_MS = 150;
2372
+ /** Padding added around every collision box (px); two labels never sit
2373
+ * closer than twice this. */
2374
+ static readonly PADDING_PX = 2;
2375
+ /** Largest single fade step (ms), so a frame after an idle gap doesn't
2376
+ * swallow most of a fade in one jump. */
2377
+ static readonly MAX_FADE_STEP_MS = 50;
2378
+ /** Placement hysteresis (px): a currently-shown label's collision test box
2379
+ * shrinks by this per side, so it survives marginal overlaps that camera
2380
+ * drift toggles frame to frame instead of flickering. Its full box is
2381
+ * still claimed against competitors. */
2382
+ static readonly HYSTERESIS_PX = 6;
2383
+ private _participants;
2384
+ /** Set when the label set changed (register/update/text change) so the next
2385
+ * update re-places even with a static camera. */
2386
+ private _dirty;
2387
+ private _prevCamera;
2388
+ private _lastRunAt;
2389
+ private _lastStepAt;
2390
+ private _candidates;
2391
+ /** Packed candidate input for the kernel; grows as the label count does. */
2392
+ private _input;
2393
+ private _viewMatrix;
2394
+ private _view16;
2395
+ private _proj16;
2396
+ /** @param _kernel the placement kernel — the Rust-backed
2397
+ * `wasmDeclutterKernel` in production, a stub in unit tests. */
2398
+ constructor(_kernel: DeclutterKernel);
2399
+ /** Milliseconds until the throttle window since the last placement pass
2400
+ * elapses, clamped to `[0, MIN_INTERVAL_MS]`. Callers use this to schedule
2401
+ * a follow-up frame after a `"throttled"` result without overshooting the
2402
+ * window by waiting out the full constant from now. */
2403
+ remainingThrottleMs(nowMs: number): number;
2404
+ register(participant: DeclutterParticipant): void;
2405
+ unregister(participant: DeclutterParticipant): void;
2406
+ /** Signal that candidates changed (text, positions, visibility, style) so
2407
+ * the next update re-runs placement even if the camera is still. */
2408
+ markDirty(): void;
2409
+ /**
2410
+ * Run a placement pass if one is due, then advance any active show/hide
2411
+ * fades. `widthPx`/`heightPx` are the viewport in CSS pixels (matching the
2412
+ * `uScreenHeightPx` uniform); `nowMs` is the frame timestamp used for
2413
+ * throttling and fade stepping.
2414
+ */
2415
+ update(camera: PerspectiveCamera, widthPx: number, heightPx: number, nowMs: number): DeclutterUpdateResult;
2416
+ private _run;
2417
+ /** Drop all registered participants. Call when the owning view is disposed
2418
+ * so this manager doesn't keep strong references to removed meshes (and
2419
+ * their GPU/font resources) past the view's lifetime. */
2420
+ dispose(): void;
2421
+ private _snapshotChanged;
2422
+ private _takeSnapshot;
2423
+ }
2424
+
2425
+ /** A mesh that contributes label candidates to the shared declutter pass. */
2426
+ declare type DeclutterParticipant = {
2427
+ /**
2428
+ * Append the currently-visible, declutter-enabled candidates to `out`.
2429
+ * Called once per placement pass; must not retain `out`.
2430
+ */
2431
+ collectDeclutterCandidates: (out: DeclutterCandidate[]) => void;
2432
+ /**
2433
+ * Apply a placement result as the label's fade *target*. Must be cheap and
2434
+ * idempotent — the pass calls it for every candidate on every run, most
2435
+ * often with an unchanged value.
2436
+ */
2437
+ applyDeclutter: (handle: number, hidden: boolean) => void;
2438
+ /**
2439
+ * Advance this participant's hide factors toward their targets by
2440
+ * `deltaMs / DECLUTTER_FADE_MS`. Called by the manager every update while
2441
+ * anything is fading; returns true while any label is still mid-fade.
2442
+ */
2443
+ stepDeclutterFade: (deltaMs: number) => boolean;
2444
+ };
2445
+
2446
+ declare type DeclutterUpdateResult =
2447
+ /** Nothing to do: no participants, or camera and data unchanged. */
2448
+ "idle"
2449
+ /** A placement pass ran; results are applied to the participants. */
2450
+ | "ran"
2451
+ /** Labels are mid-fade — the caller should schedule a prompt follow-up
2452
+ * frame so the animation keeps stepping. */
2453
+ | "animating"
2454
+ /** A pass is due but the throttle window hasn't elapsed — the caller should
2455
+ * schedule another frame so placement settles after the camera stops. */
2456
+ | "throttled";
2457
+
2236
2458
  export declare const DEFAULT_EFFECT_OPTIONS: Required<EffectOptions>;
2237
2459
 
2238
2460
  export declare type DefaultBatchAttributeValues = {
@@ -2376,7 +2598,7 @@ export declare type Descriptions = {
2376
2598
  * Why a tile's composite atlas needs to be re-rendered.
2377
2599
  * Aggregated per-handle so a single composite pass services multiple updates.
2378
2600
  */
2379
- declare type DirtyReason = "material" | "texture-binding" | "vector-revision" | "hillshade";
2601
+ declare type DirtyReason = "material" | "texture-binding" | "vector-revision" | "raster-revision" | "hillshade";
2380
2602
 
2381
2603
  export declare class DrapedMesh<TGeometry extends BufferGeometry = BufferGeometry<NormalBufferAttributes>, TMaterial extends Material | Material[] = Material | Material[], TEventMap extends Object3DEventMap = Object3DEventMap> extends Mesh_2<TGeometry, TMaterial, TEventMap> {
2382
2604
  drapedEnable: boolean;
@@ -2705,6 +2927,12 @@ declare class ElevationDecoder {
2705
2927
  declare type ElevationHeatmapCompositeLayer = CompositeLayerBase & {
2706
2928
  kind: "elevationHeatmap";
2707
2929
  opacity: number;
2930
+ /**
2931
+ * Same Mercator latitude reprojection band as {@link RasterCompositeLayer}:
2932
+ * the heatmap decode samples the reprojected per-slot UV, so a DEM draped on
2933
+ * Geographic terrain (baked or direct) remaps identically to color imagery.
2934
+ */
2935
+ reproject?: [number, number];
2708
2936
  };
2709
2937
 
2710
2938
  /**
@@ -2836,6 +3064,9 @@ export declare type EvaluatableMaterialProperty = {
2836
3064
  size: AvailableMaterialProperty["size"];
2837
3065
  /** Opacity expression from layer configuration (for polygons/points/text where supported). */
2838
3066
  opacity: AvailableMaterialProperty["opacity"];
3067
+ /** Per-feature declutter placement priority (for points/billboards/text);
3068
+ * higher wins an overlap. Overrides the layer's `declutterPriority`. */
3069
+ declutterPriority: AvailableMaterialProperty["declutterPriority"];
2839
3070
  /** Image URL for billboards; packed into a shared per-mesh texture atlas. */
2840
3071
  image: string;
2841
3072
  };
@@ -2851,6 +3082,7 @@ declare type EvaluatedMaterialProperty = {
2851
3082
  width: number;
2852
3083
  size: number;
2853
3084
  opacity: number;
3085
+ declutterPriority: number;
2854
3086
  image: string | null;
2855
3087
  };
2856
3088
 
@@ -2894,6 +3126,9 @@ declare class EventContext {
2894
3126
  readonly textureFragmentIndex?: Map<string, Set<TextureSlot>>;
2895
3127
  readonly tileMeshToFragmentIds?: Map<TileMesh, Set<string>>;
2896
3128
  readonly hillshadeContext?: HillshadeContext;
3129
+ /** Shared screen-space label declutterer; text/sprite meshes register as
3130
+ * participants on creation. Optional so headless contexts can skip it. */
3131
+ readonly declutter?: DeclutterManager;
2897
3132
  updatedAt: number;
2898
3133
  constructor(args: EventContextArgs);
2899
3134
  }
@@ -2926,6 +3161,7 @@ declare type EventContextArgs = {
2926
3161
  textureFragmentIndex?: Map<string, Set<TextureSlot>>;
2927
3162
  tileMeshToFragmentIds?: Map<TileMesh, Set<string>>;
2928
3163
  hillshadeContext?: HillshadeContext;
3164
+ declutter?: DeclutterManager;
2929
3165
  };
2930
3166
 
2931
3167
  export declare class EventHandler<T extends BaseEventMap = BaseEventMap, K extends keyof T = keyof T> {
@@ -3128,6 +3364,8 @@ export declare class FeatureEvaluator {
3128
3364
  * - `width` - Line width in pixels (for polylines)
3129
3365
  * - `size` - Feature size in pixels (for points/text)
3130
3366
  * - `opacity` - Feature opacity 0-1
3367
+ * - `declutterPriority` - Placement priority for decluttering; higher wins
3368
+ * an overlap (for points/billboards/text with `declutter` enabled)
3131
3369
  * - `image` - Image URL (for billboards); loaded once per distinct URL and
3132
3370
  * packed into the layer's texture atlas. Return `null` to clear a
3133
3371
  * previous per-feature image and revert to the material's default `url`;
@@ -3467,7 +3705,7 @@ export declare function getWGS84SemiMinorAxis(): number;
3467
3705
  * Provides an interface for accessing and modifying globe properties
3468
3706
  * that are shared across different material types (VectorTile, RasterTile, RasterTerrain).
3469
3707
  */
3470
- export declare class Globe implements Omit<RemoveFreeRecursively<Globe_2>, "elevationColormap" | "color"> {
3708
+ export declare class Globe implements Omit<RemoveFreeRecursively<Globe_2>, "elevationColormap" | "color" | "isGeographicTiling"> {
3471
3709
  private handler;
3472
3710
  private _elevationColormap?;
3473
3711
  constructor(handler: GlobeHandler, options?: GlobeOptions);
@@ -3515,6 +3753,14 @@ declare class Globe_2 {
3515
3753
  * Whether to hide underground geometry. Disabling this value might cause unexpected behavior when using effect.
3516
3754
  */
3517
3755
  hideUnderground: boolean;
3756
+ /**
3757
+ * Whether the globe tiles with the Geographic scheme (quantized-mesh
3758
+ * terrain) rather than the default WebMercator. Engine-set output — the
3759
+ * web renderer selects its raster drape pipeline (baked vs direct) from
3760
+ * it at tile-mesh creation; ignored on input (the scheme is dictated by
3761
+ * the terrain source, not configurable).
3762
+ */
3763
+ isGeographicTiling: boolean;
3518
3764
  /**
3519
3765
  * Screen-space error threshold for level of detail (LOD) calculations. Initialization only.
3520
3766
  */
@@ -3567,7 +3813,7 @@ declare type GlobeHandler = {
3567
3813
  setElevationColormap: (value: ColorMap) => void;
3568
3814
  };
3569
3815
 
3570
- declare type GlobeOptions = Partial<Omit<Globe_2, "constructor" | "free" | "elevationColormap" | "color">> & {
3816
+ declare type GlobeOptions = Partial<Omit<Globe_2, "constructor" | "free" | "elevationColormap" | "color" | "isGeographicTiling">> & {
3571
3817
  elevationColormap?: ColorMap;
3572
3818
  color?: Color_2;
3573
3819
  };
@@ -4018,7 +4264,7 @@ export declare type InstancedSpriteMaterialProps = {
4018
4264
  base?: InstancedSpriteBaseProps;
4019
4265
  };
4020
4266
 
4021
- export declare class InstancedSpriteMesh extends Mesh_2 implements PickableMesh {
4267
+ export declare class InstancedSpriteMesh extends Mesh_2<BufferGeometry, Material | Material[], CustomObject3DEventMap> implements PickableMesh, DeclutterParticipant {
4022
4268
  private _batchIdToInstance;
4023
4269
  private _initialColor;
4024
4270
  private _initialHeight;
@@ -4042,8 +4288,31 @@ export declare class InstancedSpriteMesh extends Mesh_2 implements PickableMesh
4042
4288
  readonly ctx: EventContext;
4043
4289
  /** Material enhancer for encapsulated state management */
4044
4290
  private _enhancedMaterial?;
4291
+ /** Per-instance world anchors in ECEF meters (f64, 3 per instance), kept in
4292
+ * sync with the position attributes for the declutter pass. */
4293
+ private _anchors;
4294
+ /** Whether this mesh's instances participate in screen-space decluttering. */
4295
+ private _declutter;
4296
+ /** Layer-level placement priority from the material. */
4297
+ private _declutterPriority;
4298
+ /** Per-instance priorities set through the evaluator (NaN = no override,
4299
+ * fall back to the layer value). Lazily allocated on first use. */
4300
+ private _declutterPriorityOverrides;
4301
+ /** Per-instance fade targets for `instanceDeclutterHide` (0 = shown,
4302
+ * 1 = hidden); the attribute animates toward these in stepDeclutterFade. */
4303
+ private _declutterTargets;
4304
+ /** True while any instance's hide factor may differ from its target. */
4305
+ private _declutterAnimating;
4045
4306
  constructor(options: InstancedSpriteOptions);
4046
4307
  setActive(active: boolean): void;
4308
+ collectDeclutterCandidates(out: DeclutterCandidate[]): void;
4309
+ applyDeclutter(handle: number, hidden: boolean): void;
4310
+ stepDeclutterFade(deltaMs: number): boolean;
4311
+ private _cacheDeclutterState;
4312
+ private _clearDeclutterHidden;
4313
+ /** Reconstruct absolute ECEF anchors from the same arrays the position
4314
+ * attributes receive (RTE high+low split, or RTC-relative + center). */
4315
+ private _cacheAnchors;
4047
4316
  _init(m: PointMesh | BillboardMesh): Promise<void>;
4048
4317
  _update(m: PointMesh | BillboardMesh): Promise<void>;
4049
4318
  private _initGeometry;
@@ -4083,6 +4352,19 @@ export declare class InstancedSpriteMesh extends Mesh_2 implements PickableMesh
4083
4352
  setFeatureShowByBatchId(batchId: number, rawVisible: boolean): void;
4084
4353
  setFeatureOpacityByBatchId(batchId: number, opacity: number): void;
4085
4354
  setFeatureHeightByBatchId(batchId: number, height: number): void;
4355
+ /**
4356
+ * Set one instance's declutter fade target; the attribute animates toward
4357
+ * it in {@link stepDeclutterFade}. Deliberately separate from the `show`
4358
+ * component of `instanceParams` so user-driven visibility and declutter
4359
+ * results compose instead of clobbering each other.
4360
+ */
4361
+ setDeclutterHiddenByInstance(instanceIndex: number, hidden: boolean): void;
4362
+ /**
4363
+ * Set a per-feature placement priority (higher wins), overriding the
4364
+ * layer-level `declutterPriority` for this instance. Driven by the feature
4365
+ * evaluator.
4366
+ */
4367
+ setFeatureDeclutterPriorityByBatchId(batchId: number, priority: number): void;
4086
4368
  setFeatureSizeByBatchId(batchId: number, size: number): void;
4087
4369
  /**
4088
4370
  * Give one feature its own image, packed into this mesh's texture atlas.
@@ -4215,7 +4497,7 @@ declare type LayerId = string;
4215
4497
  declare class LayersManager {
4216
4498
  private layers;
4217
4499
  add(l: Layer | BaseHandle): void;
4218
- get(id: string): Layer | BaseHandle<BaseDesc<BaseDescConfig, BaseDescConfigUpdate, BaseInstance, BaseEventMap>> | undefined;
4500
+ get(id: string): Layer | BaseHandle< BaseDesc<BaseDescConfig, BaseDescConfigUpdate, BaseInstance, BaseEventMap>> | undefined;
4219
4501
  emitById<K extends keyof LayerEvent>(k: K, id: string, ...args: Parameters<LayerEvent[K]>): void;
4220
4502
  emitAll<K extends keyof LayerEvent>(k: K, ...args: Parameters<LayerEvent[K]>): void;
4221
4503
  getResourceLayers(): Generator<Layer, void, unknown>;
@@ -4677,6 +4959,10 @@ export declare type MemoryStats = {
4677
4959
  gpuBytesEst: number;
4678
4960
  externalCpuBytes: number;
4679
4961
  reservedBytes: number;
4962
+ /** Estimated GPU bytes of fixed screen-sized allocations (the
4963
+ * postprocessing render-target stack), reported via
4964
+ * `Core.setFixedGpuBytes` on init, resize, and pass-list changes. */
4965
+ fixedGpuBytes: number;
4680
4966
  budgetBytes: number | undefined;
4681
4967
  evictedCount: number;
4682
4968
  sseMultiplier: number;
@@ -4700,6 +4986,7 @@ declare class MemoryStats_2 {
4700
4986
  readonly evictedCount: number;
4701
4987
  readonly externalBufferBytes: number;
4702
4988
  readonly externalCpuBytes: number;
4989
+ readonly fixedGpuBytes: number;
4703
4990
  readonly gpuBytesEst: number;
4704
4991
  readonly reservedBytes: number;
4705
4992
  readonly retainedRaster: number;
@@ -5009,7 +5296,7 @@ export declare abstract class MeshDesc<Config extends MeshConfig = MeshConfig, U
5009
5296
  * `this.ctx.registerPickableMesh(this.id, wrapper)` before returning.
5010
5297
  */
5011
5298
  abstract createMesh(): Instance;
5012
- get raw(): (Instance extends Object3D<Object3DEventMap> ? Instance : never) | (Instance extends {
5299
+ get raw(): (Instance extends Object3D< Object3DEventMap> ? Instance : never) | (Instance extends {
5013
5300
  raw: infer Raw extends Object3D;
5014
5301
  } ? Raw : never) | undefined;
5015
5302
  onCreate(): void;
@@ -5662,7 +5949,10 @@ export declare type Options = {
5662
5949
  container?: HTMLElement;
5663
5950
  /** Canvas element for rendering. If not provided, a new canvas is created. */
5664
5951
  canvas?: HTMLCanvasElement | OffscreenCanvas;
5665
- /** Device pixel ratio override. Uses device default if not specified. */
5952
+ /** Device pixel ratio override. When not specified, the device value is
5953
+ * used, capped at 2 on mobile (1 with `mobileOptimization`) — screen-sized
5954
+ * render targets grow quadratically with pixel ratio and dominate mobile
5955
+ * GPU memory. An explicit value bypasses the caps. */
5666
5956
  pixelRatio?: number;
5667
5957
  /** Disables automatic resize handling on window resize events. */
5668
5958
  disableAutoResize?: boolean;
@@ -6064,6 +6354,28 @@ declare class PointMaterial {
6064
6354
  set clampToGround(value: boolean | null | undefined);
6065
6355
  get color(): number | undefined;
6066
6356
  set color(value: number | null | undefined);
6357
+ /**
6358
+ * Placement priority for decluttering; higher wins. Only meaningful when
6359
+ * `declutter` is enabled. Defaults to `0.0`.
6360
+ */
6361
+ get declutterPriority(): number | undefined;
6362
+ /**
6363
+ * Placement priority for decluttering; higher wins. Only meaningful when
6364
+ * `declutter` is enabled. Defaults to `0.0`.
6365
+ */
6366
+ set declutterPriority(value: number | null | undefined);
6367
+ /**
6368
+ * Participate in screen-space decluttering: when labels/sprites overlap
6369
+ * on screen, lower-priority ones are hidden. Defaults to `true`; set to
6370
+ * `false` to draw every label unconditionally.
6371
+ */
6372
+ get declutter(): boolean | undefined;
6373
+ /**
6374
+ * Participate in screen-space decluttering: when labels/sprites overlap
6375
+ * on screen, lower-priority ones are hidden. Defaults to `true`; set to
6376
+ * `false` to draw every label unconditionally.
6377
+ */
6378
+ set declutter(value: boolean | null | undefined);
6067
6379
  get depthTest(): boolean | undefined;
6068
6380
  set depthTest(value: boolean | null | undefined);
6069
6381
  /**
@@ -6949,6 +7261,30 @@ export declare function radianToDegree(radian: number): number;
6949
7261
 
6950
7262
  export declare const RAIN_ASSETS_URL: string;
6951
7263
 
7264
+ /** One baked raster layer: its overlapping WM tile textures, mosaicked into one
7265
+ * render target. Heatmap layers carry encoded DEM, copied raw (nearest, no
7266
+ * color conversion) so the composite decode stays valid. */
7267
+ declare type RasterBakeSlot = {
7268
+ isElevationHeatmap: boolean;
7269
+ /**
7270
+ * The decoder's no-data (boundary) color for heatmap slots: painted across
7271
+ * the render target before the sources so uncovered regions decode as
7272
+ * "no elevation" — the no-data story lives entirely in the encoded-elevation
7273
+ * domain, keeping the alpha channel free for future RGBA encodings. Absent
7274
+ * for color slots and for decoders with no byte-representable boundary.
7275
+ */
7276
+ noDataColor?: [number, number, number];
7277
+ sources: RasterBakeSource[];
7278
+ };
7279
+
7280
+ /** One WebMercator raster tile texture to bake into a layer's render target,
7281
+ * framed by the Rust-resolved mercator affine (terrain `[0,1]` UV → source UV). */
7282
+ declare type RasterBakeSource = {
7283
+ texture: Texture;
7284
+ uvOffset: [number, number];
7285
+ uvScale: [number, number];
7286
+ };
7287
+
6952
7288
  /** A sampled color layer — a raster tile or a texturized vector scene. */
6953
7289
  declare type RasterCompositeLayer = CompositeLayerBase & {
6954
7290
  kind: "raster";
@@ -7123,6 +7459,27 @@ declare class RasterTileSourceDescription {
7123
7459
  set url(value: string | null | undefined);
7124
7460
  }
7125
7461
 
7462
+ /**
7463
+ * One WebMercator raster tile to bake into a layer's drape render target (one per
7464
+ * overlapping WM tile per baked layer). `layer_ordinal` pairs the entry with the k-th
7465
+ * baked composite slot; `fragment_ind`/`fragment_gen` identify the loaded texture
7466
+ * fragment (the web derives its `loadedTexs` key, `${ind}_${gen}`, from them);
7467
+ * `uv_offset`/`uv_scale` place this tile into the terrain tile's `[0, 1]` UV for the
7468
+ * bake camera; `reproject_terrain_lat` is `[south, north]` (radians) on Geographic
7469
+ * terrain (Mercator reprojection) or empty on WebMercator terrain.
7470
+ */
7471
+ declare class RasterTileState {
7472
+ private constructor();
7473
+ free(): void;
7474
+ [Symbol.dispose](): void;
7475
+ fragment_gen: number;
7476
+ fragment_ind: number;
7477
+ layer_ordinal: number;
7478
+ reproject_terrain_lat: Float32Array;
7479
+ uv_offset: Float32Array;
7480
+ uv_scale: Float32Array;
7481
+ }
7482
+
7126
7483
  export declare type Ray = Required<NormalizeWASMClass<Ray_2>>;
7127
7484
 
7128
7485
  declare class Ray_2 {
@@ -7256,16 +7613,21 @@ export declare class RenderPass extends RenderPass_2 {
7256
7613
  * Orchestrate rendering passes with ordered management and flexible insertion.
7257
7614
  */
7258
7615
  declare class RenderPassOrchestrator {
7259
- lights: Group<Object3DEventMap>;
7616
+ lights: Group< Object3DEventMap>;
7260
7617
  scenes: {
7261
- mrt: Scene<Object3DEventMap>;
7262
- globe: Scene<Object3DEventMap>;
7263
- draped: Scene<Object3DEventMap>;
7264
- opaque: Scene<Object3DEventMap>;
7265
- transparent: Scene<Object3DEventMap>;
7266
- skyEnvMap: Scene<Object3DEventMap>;
7618
+ mrt: Scene< Object3DEventMap>;
7619
+ globe: Scene< Object3DEventMap>;
7620
+ draped: Scene< Object3DEventMap>;
7621
+ opaque: Scene< Object3DEventMap>;
7622
+ transparent: Scene< Object3DEventMap>;
7623
+ skyEnvMap: Scene< Object3DEventMap>;
7267
7624
  };
7268
7625
  effectComposer: EffectComposer;
7626
+ /**
7627
+ * Invoked whenever the pass list changes (add/insert/remove/clear), so the
7628
+ * owner can re-report the fixed GPU footprint to the memory ledger.
7629
+ */
7630
+ onPassesChanged?: () => void;
7269
7631
  private passMap;
7270
7632
  constructor(renderer: WebGLRenderer, options: RenderPassOrchestratorOptions);
7271
7633
  setSize(width: number, height: number): void;
@@ -7298,6 +7660,12 @@ declare class RenderPassOrchestrator {
7298
7660
  * Clear all passes.
7299
7661
  */
7300
7662
  clearPasses(): void;
7663
+ /**
7664
+ * Estimates the resident GPU bytes of the fixed, screen-sized render-target
7665
+ * stack (composer buffers + every target reachable from the passes), for
7666
+ * reporting into the memory ledger's `fixed_gpu_bytes` term.
7667
+ */
7668
+ estimateFixedGpuBytes(): number;
7301
7669
  /**
7302
7670
  * Dispose all GPU resources held by the effect composer.
7303
7671
  */
@@ -7325,7 +7693,12 @@ export declare class RenderTargetCopyPass extends Pass_2 {
7325
7693
  get autoResize(): boolean;
7326
7694
  set autoResize(value: boolean);
7327
7695
  setTexture(texture: Texture): void;
7328
- setDepthTexture(texture: Texture): void;
7696
+ /**
7697
+ * When a depth texture is set, the pass copies its depth into the target's
7698
+ * depth buffer via gl_FragDepth. Depth test must be enabled (with AlwaysDepth)
7699
+ * because GL discards depth writes while the depth test is disabled.
7700
+ */
7701
+ setDepthTexture(texture: Texture | null): void;
7329
7702
  render(renderer: WebGLRenderer, inputBuffer: WebGLRenderTarget | null, _outputBuffer: WebGLRenderTarget | null): void;
7330
7703
  dispose(): void;
7331
7704
  }
@@ -7454,6 +7827,13 @@ export declare type SdfTextBaseMutates = Mutates<SdfTextBaseState, SdfTextBaseUn
7454
7827
  * Set the batch ID uniform (immutable after mount, but needed for batch creation).
7455
7828
  */
7456
7829
  setBatchId: (batchId: number) => void;
7830
+ /**
7831
+ * Set the declutter hide factor (0 = shown … 1 = hidden); the fragment
7832
+ * shader scales opacity by `1 - value` and the vertex shader hard-culls
7833
+ * at 1. Kept separate from `show`/`opacity` so user-driven visibility and
7834
+ * declutter results compose instead of clobbering each other.
7835
+ */
7836
+ setDeclutterHide: (value: number) => void;
7457
7837
  }>;
7458
7838
 
7459
7839
  /**
@@ -7512,6 +7892,10 @@ declare type SdfTextBaseRefs = {
7512
7892
  uTextWidth: UniformValue<number>;
7513
7893
  uTextHeight: UniformValue<number>;
7514
7894
  uBgYBounds: UniformValue<Vector2>;
7895
+ /** Screen-space declutter hide factor (0 = shown … 1 = hidden), animated
7896
+ * by the declutter fade. Driven by the declutter pass, not by material
7897
+ * style updates. */
7898
+ uDeclutterHide: UniformValue<number>;
7515
7899
  uRTCCenter: UniformValue<Vector3>;
7516
7900
  uRTCCenterView: UniformValue<Vector3>;
7517
7901
  uEyeRTELow: UniformValue<Vector3>;
@@ -7599,6 +7983,26 @@ export declare class SDFTextMesh extends Mesh_2<InstancedBufferGeometry, ShaderM
7599
7983
  private _reprepare?;
7600
7984
  /** Ask the owner to schedule a render after an async re-prepare. */
7601
7985
  private _requestRender?;
7986
+ /** World-space anchor in ECEF meters (f64), kept in sync with the position
7987
+ * uniforms so the declutter pass can project it without touching WASM. */
7988
+ private _worldAnchor;
7989
+ /** Whether this label participates in screen-space decluttering. */
7990
+ private _declutter;
7991
+ /** Layer-level placement priority from the material. */
7992
+ private _declutterPriority;
7993
+ /** Per-feature priority set through the evaluator; overrides the layer
7994
+ * value when defined. */
7995
+ private _declutterPriorityOverride;
7996
+ /** Current animated hide factor (mirrors the uDeclutterHide uniform) and
7997
+ * the placement target it is fading toward. */
7998
+ private _declutterHide;
7999
+ private _declutterTarget;
8000
+ /** Text block dimensions in em units, mirroring the uTextWidth/uTextHeight/
8001
+ * uBgYBounds uniforms; 0 width means "no collision box" (empty text). */
8002
+ private _labelWidthEm;
8003
+ private _labelHeightEm;
8004
+ private _labelMinYEm;
8005
+ private _labelMaxYEm;
7602
8006
  private _enhancer;
7603
8007
  constructor(position: Float32Array | {
7604
8008
  high: Float32Array;
@@ -7645,12 +8049,42 @@ export declare class SDFTextMesh extends Mesh_2<InstancedBufferGeometry, ShaderM
7645
8049
  setHeight(height: number): void;
7646
8050
  setSize(size: number): void;
7647
8051
  setOpacity(opacity: number): void;
8052
+ /**
8053
+ * Set this label's declutter fade target; the actual hide factor animates
8054
+ * toward it in {@link stepDeclutterFade}. Deliberately separate from
8055
+ * `visible`/`show`: it neither churns scene-graph membership nor releases
8056
+ * glyph atlas retains, so the declutter pass can retarget it every
8057
+ * placement run at no cost.
8058
+ */
8059
+ setDeclutterHidden(hidden: boolean): void;
8060
+ /**
8061
+ * Advance the hide factor toward its target by `deltaMs / DECLUTTER_FADE_MS`
8062
+ * and push it to the uniform. Returns true while still mid-fade.
8063
+ */
8064
+ stepDeclutterFade(deltaMs: number): boolean;
7648
8065
  _setFeatureWidth(_width: number): void;
7649
8066
  _setFeatureOpacity(opacity: number): void;
7650
8067
  setPosition(position: Float32Array | {
7651
8068
  high: Float32Array;
7652
8069
  low: Float32Array;
7653
8070
  }, RTE: boolean, transform: Transform_2): void;
8071
+ /** Reconstruct the absolute ECEF anchor from the same inputs the position
8072
+ * uniforms receive (RTE high+low split, or RTC-relative + center). */
8073
+ private _cacheWorldAnchor;
8074
+ /**
8075
+ * Build this label's collision candidate for the shared declutter pass, or
8076
+ * null when it doesn't participate (declutter disabled, hidden, or empty).
8077
+ * The local box mirrors the vertex shader's layout: glyphs span
8078
+ * [0, textWidth] × [bgMinY, bgMaxY] in em units, shifted by the `center`
8079
+ * anchor and scaled by the font size (sdfText.vert.glsl:111-115).
8080
+ */
8081
+ getDeclutterCandidate(owner: DeclutterParticipant, handle: number): DeclutterCandidate | null;
8082
+ /**
8083
+ * Set a per-feature placement priority (higher wins), overriding the
8084
+ * layer-level `declutterPriority` for this label. Pass `undefined` to fall
8085
+ * back to the layer value. Driven by the feature evaluator.
8086
+ */
8087
+ setDeclutterPriority(priority: number | undefined): void;
7654
8088
  /**
7655
8089
  * Apply material properties from WASM TextMaterial.
7656
8090
  * Maps relevant properties to enhancer updates, with change tracking.
@@ -8251,6 +8685,28 @@ declare class TextMaterial {
8251
8685
  set clampToGround(value: boolean | null | undefined);
8252
8686
  get color(): number | undefined;
8253
8687
  set color(value: number | null | undefined);
8688
+ /**
8689
+ * Placement priority for decluttering; higher wins. Only meaningful when
8690
+ * `declutter` is enabled. Defaults to `0.0`.
8691
+ */
8692
+ get declutterPriority(): number | undefined;
8693
+ /**
8694
+ * Placement priority for decluttering; higher wins. Only meaningful when
8695
+ * `declutter` is enabled. Defaults to `0.0`.
8696
+ */
8697
+ set declutterPriority(value: number | null | undefined);
8698
+ /**
8699
+ * Participate in screen-space decluttering: when labels/sprites overlap
8700
+ * on screen, lower-priority ones are hidden. Defaults to `true`; set to
8701
+ * `false` to draw every label unconditionally.
8702
+ */
8703
+ get declutter(): boolean | undefined;
8704
+ /**
8705
+ * Participate in screen-space decluttering: when labels/sprites overlap
8706
+ * on screen, lower-priority ones are hidden. Defaults to `true`; set to
8707
+ * `false` to draw every label unconditionally.
8708
+ */
8709
+ set declutter(value: boolean | null | undefined);
8254
8710
  get depthTest(): boolean | undefined;
8255
8711
  set depthTest(value: boolean | null | undefined);
8256
8712
  /**
@@ -8520,6 +8976,14 @@ declare class ThreeView<D extends Descriptions = EmptyDescriptions> extends Even
8520
8976
  private _eventDisposer;
8521
8977
  private _disposed;
8522
8978
  private _renderFlag;
8979
+ /** Shared screen-space label declutterer for text/sprite features. */
8980
+ private _declutter;
8981
+ /** Reusable Vector2 for the declutter pass's per-frame viewport query. */
8982
+ private _declutterSize;
8983
+ /** Pending follow-up frame for a throttled declutter pass or an active
8984
+ * fade, with the delay it was scheduled at (so a shorter need preempts). */
8985
+ private _declutterRetry;
8986
+ private _declutterRetryDelay;
8523
8987
  private _isIdle;
8524
8988
  private _uniforms;
8525
8989
  private _meshes;
@@ -8544,6 +9008,7 @@ declare class ThreeView<D extends Descriptions = EmptyDescriptions> extends Even
8544
9008
  private _buf;
8545
9009
  private _texFragment;
8546
9010
  private _vectorRevision;
9011
+ private _rasterRevision;
8547
9012
  private _tileHandler;
8548
9013
  private _globeHandler;
8549
9014
  private _workerTaskHandler;
@@ -8564,6 +9029,10 @@ declare class ThreeView<D extends Descriptions = EmptyDescriptions> extends Even
8564
9029
  /** Built-in attribution UI; unset when disabled or in a worker (no DOM). */
8565
9030
  private _attribution?;
8566
9031
  private pixelRatioMatchedMedia?;
9032
+ private fixedGpuBytesReportScheduled;
9033
+ private fixedGpuBytesResyncPending;
9034
+ private framesSinceFixedGpuBytesReport;
9035
+ private lastReportedFixedGpuBytes;
8567
9036
  /**
8568
9037
  * The built-in attribution (credit) UI. `undefined` when disabled via
8569
9038
  * `defaultAttribution: false`, or in a worker / no-DOM environment. Feed it
@@ -8673,6 +9142,19 @@ declare class ThreeView<D extends Descriptions = EmptyDescriptions> extends Even
8673
9142
  * @param pixelRatio - Device pixel ratio
8674
9143
  */
8675
9144
  resize: (width?: number, height?: number, pixelRatio?: number) => void;
9145
+ /**
9146
+ * Walks the fixed render-target stack and pushes the estimate into the
9147
+ * memory ledger; the WASM call is skipped while the value is unchanged.
9148
+ */
9149
+ private _reportFixedGpuBytes;
9150
+ /**
9151
+ * Re-reports the fixed GPU footprint for event-triggered changes (init,
9152
+ * resize, pass add/remove). Scheduled as a microtask so bursts — e.g.
9153
+ * effect setup during init — coalesce into a single walk, and marks a
9154
+ * one-shot post-render resync because these events run before the GPU has
9155
+ * lazily allocated the affected targets.
9156
+ */
9157
+ private _scheduleFixedGpuBytesReport;
8676
9158
  private _updateUniforms;
8677
9159
  /** Returns true if the scene was updated and needs to be rendered. */
8678
9160
  private _update;
@@ -8681,6 +9163,14 @@ declare class ThreeView<D extends Descriptions = EmptyDescriptions> extends Even
8681
9163
  * This is called after the main update loop to batch feature updates
8682
9164
  */
8683
9165
  private _forceFeatureUpdates;
9166
+ /**
9167
+ * Ask for a future frame on behalf of the declutter pass. Runs through a
9168
+ * timer because the main loop clears `forceUpdate` right after `_render` —
9169
+ * a flag set synchronously here would be wiped before the next tick reads
9170
+ * it. A pending longer timer is preempted when a shorter delay is needed
9171
+ * (a fade must not wait out a throttle window).
9172
+ */
9173
+ private _scheduleDeclutterFrame;
8684
9174
  private _render;
8685
9175
  /**
8686
9176
  * Since passing Color class to WASM is tricky, converts Navara Color
@@ -9055,11 +9545,21 @@ declare type TileHandler = {
9055
9545
  getTileElevationDecoder: (handle: bigint) => ElevationDecoder | undefined;
9056
9546
  getVectorTileStates: (handle: bigint) => VectorTileState[] | undefined;
9057
9547
  vectorRevision: () => number;
9548
+ /** The WM raster tiles to bake into per-layer drape render targets for a
9549
+ * terrain tile (Geographic terrain only; empty on WebMercator terrain). */
9550
+ getRasterTileStates: (handle: bigint) => RasterTileState[] | undefined;
9551
+ rasterRevision: () => number;
9058
9552
  /** Reports a terrain tile's drape render-target GPU footprint (bytes) so the
9059
9553
  * memory ledger tracks clamp-to-ground vector bakes that scale with terrain
9060
9554
  * subdivision past the vector `maxZoom`. Pass 0 to release on dispose. */
9061
9555
  reportDrapeGpuBytes: (handle: bigint, bytes: number) => void;
9062
9556
  calcMetersPerTexel: (tileHandle: bigint, textureZoom: number, textureWidth: number) => number;
9557
+ /** Rust's clamped WebMercator northing (`navara_geometry::mercator_y`).
9558
+ * The composite paste derives its per-slot reprojection constants through
9559
+ * this so paste and bake (`uv_rect_from_extents_mercator`) share one
9560
+ * implementation — a drifting reimplementation misaligns the pasted
9561
+ * latitude band (and NaNs on the unclamped polar band). */
9562
+ mercatorY: (lat: number) => number;
9063
9563
  };
9064
9564
 
9065
9565
  declare class TileLayerDescription {
@@ -9087,62 +9587,26 @@ export declare class TileMesh extends Mesh_2<BufferGeometry, TileMaterial, Custo
9087
9587
  maxTextures: number;
9088
9588
  texturizedSceneIndexFrom: number;
9089
9589
  numTexturizedVector: number;
9090
- private vectorSlots;
9091
- private prevVectorSignature;
9092
- private _lastVectorRevision;
9590
+ private readonly vectorDrape;
9591
+ private readonly rasterDrape;
9093
9592
  private shadowMesh?;
9094
- private texturizedSceneByTileCoordinates;
9095
9593
  private compositor;
9096
- texturizedSceneRenderTargets: WebGLRenderTarget[];
9097
9594
  private readonly drapeRtSize;
9098
9595
  private readonly atlasBytes;
9099
9596
  private reportedDrapeGpuBytes;
9100
- private warnedExceededTextures;
9101
9597
  readonly ctx: EventContext;
9102
9598
  constructor(ctx: EventContext, mesh: MeshAdded);
9103
9599
  /**
9104
- * Pull the Rust-resolved texturized-vector slots for this terrain tile into
9105
- * {@link vectorSlots}: one bake slot per clamp-to-ground layer, keyed by the
9106
- * rendered WM vector tile that backs it (ancestor fallback already resolved in
9107
- * Rust). Only layers with an actual draped scene take a slot — Rust also returns
9108
- * non-draped layers (rendered in the MRT scene), which must not consume the budget.
9109
- * Capped to the GPU slot budget; warns once when more draped layers are requested.
9110
- */
9111
- private refreshVectorSlots;
9112
- /**
9113
- * Size the drape render-target pool to the live slot count (already capped to
9114
- * `numTexturizedVector` by {@link refreshVectorSlots}): allocate the ones a
9115
- * newly-draped layer needs, dispose the ones a vanished layer freed. Then
9116
- * report the pool's GPU footprint to the memory ledger so it tracks drape
9117
- * memory that scales with terrain subdivision past the vector maxZoom — a cost
9118
- * per-vector-tile accounting cannot see. Reports only when the count changes.
9119
- */
9120
- private syncDrapeRenderTargets;
9121
- /**
9122
- * Identity of the current slot set + the backing scenes' content. A change means
9123
- * the offscreen scenes must be re-baked and the per-slot material state re-bound.
9600
+ * Report the tile's drape GPU footprint (composite atlas + vector and baked-raster
9601
+ * drape render targets) to the memory ledger when it changed. The atlas is acquired
9602
+ * in the constructor and held for the tile's lifetime regardless of draping, so it
9603
+ * is always part of the report — otherwise a tile whose last draped layer vanished
9604
+ * would report 0 and wipe the atlas cost Rust seeds at mesh-attach time. The render
9605
+ * targets on top of it scale with terrain subdivision past the layers' maxZoom — a
9606
+ * cost per-source-tile accounting cannot see.
9124
9607
  */
9125
- private vectorSignature;
9608
+ private reportDrapeGpuBytesIfChanged;
9126
9609
  private _onBeforeRender;
9127
- /**
9128
- * Drive each vector slot's main-shader state per frame: texture, per-slot UV
9129
- * transform, visibility and the draped mesh's material attributes. One slot per
9130
- * layer, sourced from the WM vector tile the Rust resolve picked. The resolve
9131
- * walks up to the nearest rendered tile (readiness derived from ECS activation
9132
- * / the Rust resolve, not a JS-side scene_ready flag), so the slot's scene is
9133
- * always bakeable (its own render target was framed by the bake) and the UV is
9134
- * identity — the ancestor LOD fallback lives entirely in Rust. Replaces the old
9135
- * scene-observer `updateTexturizedSceneTextureVisibility`.
9136
- */
9137
- private bindVectorSlots;
9138
- /**
9139
- * First available draped mesh backing any of a layer's sources. A clamp-to-ground
9140
- * layer's material attributes (water/specular/emissive/…) are uniform across its
9141
- * tiles, so any source's mesh is a faithful representative for the slot.
9142
- */
9143
- private representativeVectorMesh;
9144
- /** Copy a draped mesh's material attributes into the main shader's slot `lastIdx`. */
9145
- private copyVectorMeshAttrs;
9146
9610
  /**
9147
9611
  * Feature flags for the **main** TileMesh shader (atlas-sampling side): drives
9148
9612
  * its program cache key and which normal/specular branches compile. Sourced
@@ -9160,7 +9624,7 @@ export declare class TileMesh extends Mesh_2<BufferGeometry, TileMaterial, Custo
9160
9624
  private buildCompositeLayers;
9161
9625
  _init(mesh: MeshAdded): Promise<void>;
9162
9626
  private createMesh;
9163
- createSkirtMesh(mesh: Mesh, terrainGeometry: BufferGeometry, position: Float32Array, uv: Float32Array | null, indices: Uint32Array, normals: Float32Array | null): BufferGeometry<NormalBufferAttributes, BufferGeometryEventMap>;
9627
+ createSkirtMesh(mesh: Mesh, terrainGeometry: BufferGeometry, position: Float32Array, uv: Float32Array | null, indices: Uint32Array, normals: Float32Array | null): BufferGeometry< NormalBufferAttributes, BufferGeometryEventMap>;
9164
9628
  private shouldUseNormal;
9165
9629
  private initMaterial;
9166
9630
  _update(mesh: MeshChanged): void;
@@ -9352,12 +9816,17 @@ declare class TileTextureCompositor {
9352
9816
  * per-tile GPU byte cost it reports to the memory ledger. */
9353
9817
  readonly size: number;
9354
9818
  private readonly texturizedScenes;
9355
- private readonly vectorCamera;
9819
+ private readonly mercatorY;
9820
+ private readonly bakeCamera;
9356
9821
  private readonly quadScene;
9357
9822
  private readonly quadCamera;
9358
9823
  private readonly quadMesh;
9359
9824
  private readonly materialCache;
9360
- private readonly touchedVectorTargets;
9825
+ private readonly touchedTargets;
9826
+ private readonly rasterBakeScene;
9827
+ private readonly rasterBakeMaterial;
9828
+ private demNoDataTexture;
9829
+ private demNoDataTextureKey;
9361
9830
  constructor(opts: TileTextureCompositorOptions);
9362
9831
  acquire(handle: TileHandle): CompositeOutputs;
9363
9832
  release(handle: TileHandle): void;
@@ -9390,6 +9859,53 @@ declare class TileTextureCompositor {
9390
9859
  uvScale: [number, number];
9391
9860
  }[];
9392
9861
  }[], renderTargets: WebGLRenderTarget[]): void;
9862
+ /**
9863
+ * Bake each raster layer's overlapping WM tile textures into ONE render
9864
+ * target (slot `i` → `renderTargets[i]`), mirroring {@link renderVectorScenes}:
9865
+ * every source is a textured quad framed by the Rust-supplied mercator affine
9866
+ * `uvOffset`/`uvScale`, so the render target ends up spanning the terrain
9867
+ * tile's (Mercator-projected) extent and the composite treats the whole layer
9868
+ * as one slot with the terrain-band reprojection — instead of one composite
9869
+ * slot per overlapping tile, which overflowed the GPU slot budget on
9870
+ * Geographic terrain once 3+ layers were draped.
9871
+ *
9872
+ * Sources are drawn coarse-first (ascending `uvScale`): an ancestor fallback
9873
+ * covers the whole terrain rect, so painting it first lets the finer tiles
9874
+ * overwrite it instead of being buried under coarse texels. The bake is a
9875
+ * value-preserving copy (`toneMapped: false`; per-kind color spaces below), so
9876
+ * heatmap DEM texels survive it bit-faithfully:
9877
+ * - color imagery: source + target declared sRGB — decode at fetch, encode at
9878
+ * write, decode again at the composite fetch: the same linear values the
9879
+ * direct (non-baked) path produced.
9880
+ * - elevation heatmap: everything `NoColorSpace` + nearest — raw texels, no
9881
+ * interpolation, so the composite's decode-then-interpolate keeps working.
9882
+ */
9883
+ renderRasterTiles(slots: (RasterBakeSlot | undefined)[], renderTargets: (WebGLRenderTarget | undefined)[]): void;
9884
+ /**
9885
+ * Shared bake loop over per-layer drape render targets (slot `i` →
9886
+ * `renderTargets[i]`): saves/restores the renderer state, clears each live
9887
+ * target once and lets `drawSlot` accumulate that slot's sources into it —
9888
+ * autoClear stays off so the sources mosaic instead of overwriting one
9889
+ * another.
9890
+ */
9891
+ private bakeSlotTargets;
9892
+ /**
9893
+ * Frame the shared bake camera on the terrain tile's sub-rect of a source
9894
+ * from the mercator affine: meshUv ∈ [0,1] → sourceUv = uvOffset +
9895
+ * meshUv·uvScale, source NDC = 2·sourceUv − 1. A source finer than the
9896
+ * terrain tile yields a camera wider than [-1, 1], so it draws into only
9897
+ * its sub-rect and leaves the rest of the render target transparent.
9898
+ */
9899
+ private frameBakeCamera;
9900
+ /** Draw one textured quad into the current render target through the bake
9901
+ * camera window — the value-preserving copy described on
9902
+ * {@link renderRasterTiles}. */
9903
+ private drawRasterBakeQuad;
9904
+ /** The cached 1×1 no-data texture, (re)created when the color changes. */
9905
+ private demNoDataUnderlay;
9906
+ /** Value-preserving sampler settings for a bake source texture (see
9907
+ * {@link renderRasterTiles}); idempotent so re-bakes don't re-upload. */
9908
+ private configureRasterBakeSource;
9393
9909
  /**
9394
9910
  * Run the composite MRT pass for a handle if (and only if) its atlas is
9395
9911
  * dirty. Consumes the dirty flags on success so the next frame skips work
@@ -9411,6 +9927,10 @@ declare class TileTextureCompositor {
9411
9927
  declare type TileTextureCompositorOptions = {
9412
9928
  renderer: WebGLRenderer;
9413
9929
  texturizedSceneByTileCoordinates: TexturizedSceneByTileCoordinates;
9930
+ /** Rust's clamped WebMercator northing (`TileHandler.mercatorY`), handed to
9931
+ * the core uniform mutates so the paste-side reprojection constants come
9932
+ * from the same implementation as the Rust bake affine. */
9933
+ mercatorY: (lat: number) => number;
9414
9934
  /** Atlas RT side length (defaults to 512). */
9415
9935
  size?: number;
9416
9936
  /** Test seam: replace MRT RT creation. */
@@ -9636,7 +10156,7 @@ export declare type TransparentPassConfig = Description_4 & EffectConfig;
9636
10156
  export declare class TransparentPassEffectDesc extends EffectDesc<TransparentPassConfig, TransparentPassUpdate, RenderPass> {
9637
10157
  static key: string;
9638
10158
  static insertAfter: string[];
9639
- light: Group<Object3DEventMap>;
10159
+ light: Group< Object3DEventMap>;
9640
10160
  lightsSyncMap: Map<any, any>;
9641
10161
  createPass(): RenderPass;
9642
10162
  update(_time: number): void;