@navaramap/three 0.0.0 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/assets/fontWorker-CCWL5pbx.js +2 -0
  2. package/dist/assets/fontWorker-CCWL5pbx.js.map +1 -0
  3. package/dist/assets/index-BdtHOOT9.js +12 -0
  4. package/dist/assets/index-BdtHOOT9.js.map +1 -0
  5. package/dist/assets/{navara_wasm_font_worker_bg-665OLxjy.wasm → navara_wasm_font_worker_bg-Bnck8OGf.wasm} +0 -0
  6. package/dist/assets/{navara_wasm_worker_bg-BXhKC5bE.wasm → navara_wasm_worker_bg-DG9ORAmj.wasm} +0 -0
  7. package/dist/index.d.ts +142 -51
  8. package/dist/index.js +4772 -4510
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.umd.cjs +226 -218
  11. package/dist/index.umd.cjs.map +1 -1
  12. package/package.json +13 -13
  13. package/src/event/context.ts +5 -0
  14. package/src/index.ts +10 -2
  15. package/src/material/enhancer/tileComposite/tileCompositeBaseEnhancer/mutates.ts +6 -1
  16. package/src/material/enhancer/tileComposite/tileElevationHeatmapEnhancer/shader.ts +9 -2
  17. package/src/mesh/tile/drapeResolver.ts +76 -0
  18. package/src/mesh/{tile.test.ts → tile/index.test.ts} +16 -6
  19. package/src/mesh/{tile.ts → tile/index.ts} +124 -392
  20. package/src/mesh/tile/rasterDrapeResolver.ts +423 -0
  21. package/src/mesh/tile/vectorDrapeResolver.ts +404 -0
  22. package/src/tileTexture/TileTextureCache.test.ts +2 -0
  23. package/src/tileTexture/TileTextureCache.ts +1 -0
  24. package/src/tileTexture/TileTextureCompositor.test.ts +105 -0
  25. package/src/tileTexture/TileTextureCompositor.ts +241 -31
  26. package/src/tileTexture/__snapshots__/CompositeShader.snapshot.test.ts.snap +36 -16
  27. package/src/tileTexture/types.ts +7 -0
  28. package/src/utils/demNoDataColor.test.ts +47 -0
  29. package/src/utils/demNoDataColor.ts +48 -0
  30. package/dist/assets/fontWorker-CEJEfRpb.js +0 -2
  31. package/dist/assets/fontWorker-CEJEfRpb.js.map +0 -1
  32. package/dist/assets/index-BUB-ijMO.js +0 -12
  33. package/dist/assets/index-BUB-ijMO.js.map +0 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@navaramap/three",
3
3
  "type": "module",
4
- "version": "0.0.0",
4
+ "version": "0.0.1",
5
5
  "main": "dist/index.umd.cjs",
6
6
  "module": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -12,7 +12,7 @@
12
12
  "license": "MIT OR Apache-2.0",
13
13
  "repository": {
14
14
  "type": "git",
15
- "url": "git+https://github.com/eukarya-inc/navara.git",
15
+ "url": "git+https://github.com/reearth/navara.git",
16
16
  "directory": "web/navara_three"
17
17
  },
18
18
  "publishConfig": {
@@ -30,13 +30,13 @@
30
30
  "astronomy-engine": "2.1.19",
31
31
  "stats.js": "0.17.0",
32
32
  "three-stdlib": "2.36.1",
33
- "@navaramap/core": "0.0.0",
34
- "@navaramap/worker": "0.0.0",
35
- "@navaramap/engine": "0.0.0",
36
- "@navaramap/font": "0.0.0",
37
- "@navaramap/engine-api": "0.0.0",
38
- "@navaramap/three_csm": "0.0.0",
39
- "@navaramap/three_api": "0.0.0"
33
+ "@navaramap/core": "0.0.1",
34
+ "@navaramap/engine-api": "0.0.1",
35
+ "@navaramap/engine": "0.0.1",
36
+ "@navaramap/three_api": "0.0.1",
37
+ "@navaramap/three_csm": "0.0.1",
38
+ "@navaramap/font": "0.0.1",
39
+ "@navaramap/worker": "0.0.1"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@playwright/test": "1.59.1",
@@ -70,10 +70,10 @@
70
70
  "tweakpane": "4.0.5",
71
71
  "vite-plugin-glsl": "1.6.0",
72
72
  "vite-plugin-static-copy": "4.1.0",
73
- "@navaramap/three_default_descs": "0.0.0",
74
- "@navaramap/three_default_plugin": "0.0.0",
75
- "@navaramap/three_plugins": "0.0.0",
76
- "@navaramap/three_react": "0.0.0"
73
+ "@navaramap/three_default_descs": "0.0.1",
74
+ "@navaramap/three_default_plugin": "0.0.1",
75
+ "@navaramap/three_plugins": "0.0.1",
76
+ "@navaramap/three_react": "0.0.1"
77
77
  },
78
78
  "scripts": {
79
79
  "start": "vite",
@@ -7,6 +7,7 @@ import type {
7
7
  import type {
8
8
  DelegatedWorkerTasksResult,
9
9
  ElevationDecoder,
10
+ RasterTileState,
10
11
  ReconstructableEntity,
11
12
  ReturnedTransferablePolygonBatchedFeature,
12
13
  ReturnedTransferablePolylineBatchedFeature,
@@ -106,6 +107,10 @@ export type TileHandler = {
106
107
  getTileElevationDecoder: (handle: bigint) => ElevationDecoder | undefined;
107
108
  getVectorTileStates: (handle: bigint) => VectorTileState[] | undefined;
108
109
  vectorRevision: () => number;
110
+ /** The WM raster tiles to bake into per-layer drape render targets for a
111
+ * terrain tile (Geographic terrain only; empty on WebMercator terrain). */
112
+ getRasterTileStates: (handle: bigint) => RasterTileState[] | undefined;
113
+ rasterRevision: () => number;
109
114
  /** Reports a terrain tile's drape render-target GPU footprint (bytes) so the
110
115
  * memory ledger tracks clamp-to-ground vector bakes that scale with terrain
111
116
  * subdivision past the vector `maxZoom`. Pass 0 to release on dispose. */
package/src/index.ts CHANGED
@@ -632,6 +632,8 @@ export default class ThreeView<
632
632
  };
633
633
  // Per-frame snapshot of the WASM vector resolution revision, refreshed in `_render`.
634
634
  private _vectorRevision = 0;
635
+ // Per-frame snapshot of the WASM raster drape resolution revision, refreshed in `_render`.
636
+ private _rasterRevision = 0;
635
637
  private _tileHandler: TileHandler = {
636
638
  getMartini: (id) => {
637
639
  return this._core?.getMartini(id);
@@ -651,6 +653,10 @@ export default class ThreeView<
651
653
  // Returns the per-frame cached revision (read once in `_render`), so per-tile
652
654
  // `onBeforeRender` gating costs no WASM-boundary call.
653
655
  vectorRevision: () => this._vectorRevision,
656
+ getRasterTileStates: (handle) => {
657
+ return this._core?.getRasterTileStates(handle);
658
+ },
659
+ rasterRevision: () => this._rasterRevision,
654
660
  reportDrapeGpuBytes: (handle, bytes) => {
655
661
  this._core?.reportTerrainDrapeGpuBytes(handle, bytes);
656
662
  },
@@ -1739,9 +1745,11 @@ export default class ThreeView<
1739
1745
  }
1740
1746
 
1741
1747
  private _render(updatedAt: number) {
1742
- // Read the vector resolution revision once per frame so each terrain tile's
1743
- // `onBeforeRender` can gate its slot re-fetch against it without its own FFI call.
1748
+ // Read the vector/raster resolution revisions once per frame so each terrain
1749
+ // tile's `onBeforeRender` can gate its slot re-fetch against them without its
1750
+ // own FFI calls.
1744
1751
  this._vectorRevision = this._core?.vectorRevision() ?? this._vectorRevision;
1752
+ this._rasterRevision = this._core?.rasterRevision() ?? this._rasterRevision;
1745
1753
 
1746
1754
  this._uniforms.time.value = updatedAt;
1747
1755
 
@@ -104,8 +104,13 @@ export function createCoreUniformMutates(): CoreUniformMutates {
104
104
  refs.uLayerUvScale.value[k].set(1, 1);
105
105
  }
106
106
 
107
+ // Reproject applies to color rasters and elevation heatmaps alike — the
108
+ // shader warps the per-slot UV before either sampler runs; hillshade is
109
+ // terrain-side and never reprojects.
107
110
  const reproject =
108
- layer && layer.kind === "raster" ? layer.reproject : undefined;
111
+ layer && (layer.kind === "raster" || layer.kind === "elevationHeatmap")
112
+ ? layer.reproject
113
+ : undefined;
109
114
  if (reproject && layer) {
110
115
  const [Ts, Tn] = reproject;
111
116
  // Recover the source tile's latitude band [Rs, Rn] from the affine y
@@ -10,6 +10,12 @@ export const elevationIncludes = (): string => ElevationParsFragment;
10
10
  /**
11
11
  * Replaces the per-slot sampler with a DEM-decode + colormap lookup; non-heatmap
12
12
  * slots fall back to the straight raster fetch in the else branch.
13
+ *
14
+ * The heatmap alpha derives from the decode's validity: a no-data (boundary)
15
+ * texel — including a baked drape render target's uncovered regions, which the
16
+ * bake paints with the boundary color — renders transparent instead of being
17
+ * colormapped as height 0. The DEM texel's alpha channel is never read: it is
18
+ * not part of any elevation encoding and stays reserved for future RGBA ones.
13
19
  */
14
20
  export const elevationSampleProducer = ({
15
21
  k,
@@ -17,8 +23,9 @@ export const elevationSampleProducer = ({
17
23
  vec4 texColor${k};
18
24
  if (uIsElevationHeatmaps[${k}]) {
19
25
  ivec2 demTexSize = textureSize(uTextures[${k}], 0);
20
- float normalized_h = sampleElevationBilinear(uTextures[${k}], texUv${k}, demTexSize);
21
- texColor${k} = vec4(texture2D(uColorMapTexture, vec2(normalized_h, 0.5)).rgb, 1.0);
26
+ bool demValid${k};
27
+ float normalized_h = sampleElevationBilinear(uTextures[${k}], texUv${k}, demTexSize, demValid${k});
28
+ texColor${k} = vec4(texture2D(uColorMapTexture, vec2(normalized_h, 0.5)).rgb, demValid${k} ? 1.0 : 0.0);
22
29
  } else {
23
30
  texColor${k} = texture2D(uTextures[${k}], texUv${k}) * vec4(uColors[${k}], 1.0);
24
31
  }`;
@@ -0,0 +1,76 @@
1
+ import type {
2
+ MagnificationTextureFilter,
3
+ MinificationTextureFilter,
4
+ Texture,
5
+ } from "three";
6
+
7
+ import type { TextureOptions } from "../../textures";
8
+
9
+ /**
10
+ * A drape resolver owns one family of composite material slots for a terrain
11
+ * tile: how its layers' pixels reach those slots (direct texture binding or an
12
+ * offscreen bake into per-layer render targets), gated so the work only runs
13
+ * when the Rust-side resolve or the backing content actually changed.
14
+ *
15
+ * Three implementations, side by side, spell out the rendering differences:
16
+ * - {@link VectorDrapeResolver} (`vectorDrapeResolver.ts`) — clamp-to-ground
17
+ * vector layers. Always baked on every tiling scheme (vector content is an
18
+ * offscreen scene, so rasterizing is intrinsic); WebMercator terrain is the
19
+ * degenerate single-source identity case, Geographic terrain the N:M mosaic.
20
+ * - `DirectRasterDrapeResolver` (`rasterDrapeResolver.ts`) — raster layers on
21
+ * WebMercator terrain. Same-scheme 1:1: the fetched tile texture is bound
22
+ * straight to its slot by `setupTextures`; every hook is a no-op.
23
+ * - `BakedRasterDrapeResolver` (`rasterDrapeResolver.ts`) — raster layers on
24
+ * Geographic (quantized-mesh) terrain. Cross-scheme N:M mosaic bake with
25
+ * per-slot Mercator latitude reprojection.
26
+ */
27
+ export type DrapeResolver = {
28
+ /**
29
+ * Per-frame hook (`_onBeforeRender`): refresh the Rust-resolved slots when
30
+ * the resolve revision moved, and re-bake when the slot/content signature
31
+ * changed (which also catches async content landing after the resolve).
32
+ */
33
+ update(): void;
34
+
35
+ /**
36
+ * `setupTextures` hook, after the material's per-slot arrays were rebuilt:
37
+ * re-derive the resolver's slot pairing and rebind (or invalidate) its
38
+ * textures. `textureFragments` holds the raster region's per-slot fragment
39
+ * ids; the vector resolver ignores it (its region is positional).
40
+ */
41
+ syncMaterialSlots(
42
+ textureFragments: readonly (string | null | undefined)[] | undefined,
43
+ ): void;
44
+
45
+ /**
46
+ * Mercator latitude reprojection band (`[south, north]`, radians) for one of
47
+ * this resolver's composite slots, or undefined when the slot samples
48
+ * without reprojection.
49
+ */
50
+ slotReproject(absSlot: number): [number, number] | undefined;
51
+
52
+ /**
53
+ * Force a re-bake + re-bind on the next `update()` even though the resolved
54
+ * slots are unchanged (e.g. picking passes repaint the offscreen content).
55
+ */
56
+ invalidate(): void;
57
+
58
+ /** Live drape render targets, counted into the tile's GPU byte report. */
59
+ liveRenderTargetCount(): number;
60
+
61
+ dispose(): void;
62
+ };
63
+
64
+ /** Apply the shared texturized-slot sampler settings to a drape render target's
65
+ * texture. Returns the texture for call-site chaining. */
66
+ export function configureTexturizedTexture(
67
+ tex: Texture,
68
+ textureOptions: TextureOptions,
69
+ ): Texture {
70
+ tex.minFilter = textureOptions.minFilter as MinificationTextureFilter;
71
+ tex.magFilter = textureOptions.magFilter as MagnificationTextureFilter;
72
+ tex.anisotropy = textureOptions.maxAnisotropy;
73
+ tex.generateMipmaps = textureOptions.useMipmaps;
74
+ tex.needsUpdate = true;
75
+ return tex;
76
+ }
@@ -1,9 +1,9 @@
1
1
  import { Mesh, BoxGeometry, MeshBasicMaterial } from "three";
2
2
  import { describe, expect, it, vi } from "vitest";
3
3
 
4
- import { TexturizedSceneByTileCoordinates } from "../scene";
4
+ import { TexturizedSceneByTileCoordinates } from "../../scene";
5
5
 
6
- import { TileMesh } from "./tile";
6
+ import { TileMesh } from ".";
7
7
 
8
8
  // The package barrel pulled in by tile.ts initializes a worker pool at module
9
9
  // load (os.cpus()), which is unavailable in the test environment. Stub it.
@@ -36,6 +36,7 @@ function state(layerId: string, tileHandle: bigint) {
36
36
  uv_offset: new Float32Array([0, 0]),
37
37
  uv_scale: new Float32Array([1, 1]),
38
38
  reproject_terrain_lat: new Float32Array([]),
39
+ free: vi.fn(),
39
40
  };
40
41
  }
41
42
 
@@ -86,6 +87,9 @@ function makeTile(atlasSize = 512) {
86
87
 
87
88
  const meshAdded = {
88
89
  tile_handle: 1n,
90
+ // WebMercator tiling → the direct (no-op) raster drape resolver, keeping
91
+ // the baked-raster machinery out of these vector-drape tests.
92
+ globe: { isGeographicTiling: false, free: vi.fn() },
89
93
  } as unknown as ConstructorParameters<typeof TileMesh>[1];
90
94
 
91
95
  const tile = new TileMesh(ctx, meshAdded);
@@ -109,8 +113,11 @@ const RT_BYTES_SMALL = 256 * 256 * 4;
109
113
  const ATLAS_BYTES_SMALL = 256 * 256 * 4 * 3;
110
114
 
111
115
  function renderTargetCount(tile: TileMesh): number {
112
- return (tile as unknown as { texturizedSceneRenderTargets: unknown[] })
113
- .texturizedSceneRenderTargets.length;
116
+ return (
117
+ tile as unknown as {
118
+ vectorDrape: { liveRenderTargetCount(): number };
119
+ }
120
+ ).vectorDrape.liveRenderTargetCount();
114
121
  }
115
122
 
116
123
  /** Invoke the private onBeforeRender hook (args are ignored by the impl). */
@@ -207,8 +214,11 @@ describe("TileMesh vector bake dirty-gating", () => {
207
214
 
208
215
  frame(tile);
209
216
 
210
- const slots = (tile as unknown as { vectorSlots: { layerId: string }[] })
211
- .vectorSlots;
217
+ const slots = (
218
+ tile as unknown as {
219
+ vectorDrape: { vectorSlots: { layerId: string }[] };
220
+ }
221
+ ).vectorDrape.vectorSlots;
212
222
  expect(slots.map((s) => s.layerId).sort()).toEqual(["a", "c"]);
213
223
  expect(renderVectorScenes).toHaveBeenCalledTimes(1);
214
224
  });