@navaramap/three 0.0.6 → 0.0.7

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 (35) hide show
  1. package/dist/assets/fontWorker-DaqfXmZf.js +2 -0
  2. package/dist/assets/fontWorker-DaqfXmZf.js.map +1 -0
  3. package/dist/assets/index-Dydat1h1.js +25 -0
  4. package/dist/assets/index-Dydat1h1.js.map +1 -0
  5. package/dist/assets/{navara_wasm_font_worker_bg-DympKYVX.wasm → navara_wasm_font_worker_bg-XvE_oYPr.wasm} +0 -0
  6. package/dist/assets/{navara_wasm_worker_bg-Bx_l45cW.wasm → navara_wasm_worker_bg-Dg9fnGLP.wasm} +0 -0
  7. package/dist/index.d.ts +140 -4
  8. package/dist/index.js +5385 -5022
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.umd.cjs +164 -86
  11. package/dist/index.umd.cjs.map +1 -1
  12. package/package.json +12 -12
  13. package/src/declutter/DeclutterManager.test.ts +65 -0
  14. package/src/declutter/DeclutterManager.ts +60 -1
  15. package/src/declutter/types.ts +17 -0
  16. package/src/event/feature.ts +45 -2
  17. package/src/event/worker.ts +18 -0
  18. package/src/index.ts +51 -2
  19. package/src/layers/effect/TransparentPassEffectDesc.ts +1 -1
  20. package/src/mesh/nvLineGeometry.test.ts +88 -0
  21. package/src/mesh/nvLineGeometry.ts +148 -0
  22. package/src/mesh/polygonOutline.ts +126 -42
  23. package/src/mesh/sdfText/batchedSdfText.test.ts +377 -0
  24. package/src/mesh/sdfText/batchedSdfText.ts +192 -21
  25. package/src/mesh/sdfText/labelVisibility.test.ts +106 -0
  26. package/src/mesh/sdfText/labelVisibility.ts +131 -0
  27. package/src/mesh/sdfText/pendingSettlement.test.ts +151 -0
  28. package/src/mesh/sdfText/pendingSettlement.ts +68 -0
  29. package/src/pick/pickTerrain.ts +22 -18
  30. package/src/terrain/sampleTerrainMostDetailed.test.ts +236 -0
  31. package/src/terrain/sampleTerrainMostDetailed.ts +447 -0
  32. package/dist/assets/fontWorker-JfTCpd38.js +0 -2
  33. package/dist/assets/fontWorker-JfTCpd38.js.map +0 -1
  34. package/dist/assets/index-B3U_DvcP.js +0 -25
  35. package/dist/assets/index-B3U_DvcP.js.map +0 -1
@@ -0,0 +1,148 @@
1
+ import {
2
+ Box3,
3
+ InstancedInterleavedBuffer,
4
+ InterleavedBufferAttribute,
5
+ Sphere,
6
+ Vector3,
7
+ } from "three";
8
+ import { LineGeometry, LineSegmentsGeometry } from "three-stdlib";
9
+
10
+ export class NvLineGeometry extends LineGeometry {
11
+ setPositions(
12
+ array: Float32Array,
13
+ skipIdx: Uint32Array = new Uint32Array(),
14
+ ): this {
15
+ const positions: number[] = [];
16
+ const skipSet = new Set(skipIdx);
17
+
18
+ for (let i = 0; i < array.length - 3; i += 3) {
19
+ const currentIndex = i / 3;
20
+ if (skipSet.has(currentIndex)) {
21
+ continue;
22
+ }
23
+
24
+ // segment start
25
+ positions.push(array[i], array[i + 1], array[i + 2]);
26
+ // segment end
27
+ positions.push(array[i + 3], array[i + 4], array[i + 5]);
28
+ }
29
+
30
+ const points = new Float32Array(positions);
31
+
32
+ // This function is used to override LineGeometry's setPositions,
33
+ // so we don't call super.setPositions.
34
+ LineSegmentsGeometry.prototype.setPositions.call(this, points);
35
+ return this;
36
+ }
37
+
38
+ /**
39
+ * Attach RTE high/low positions as per-segment instanced attributes.
40
+ *
41
+ * Mirrors the segment expansion (and skip handling) of `setPositions` and
42
+ * the interleaved layout of `LineSegmentsGeometry.setPositions`
43
+ * (stride 6: start xyz at offset 0, end xyz at offset 3). The RTE shader
44
+ * reads only these attributes, so callers skip `setPositions` entirely —
45
+ * no unused `instanceStart`/`instanceEnd` buffer is uploaded to the GPU.
46
+ * Because `LineSegmentsGeometry.computeBoundingBox/Sphere` read
47
+ * `instanceStart`/`instanceEnd`, the bounds are computed here instead,
48
+ * from the reconstructed absolute (high + low) positions.
49
+ */
50
+ setPositionsHighLow(
51
+ high: Float32Array,
52
+ low: Float32Array,
53
+ skipIdx: Uint32Array = new Uint32Array(),
54
+ ): this {
55
+ const highSegments: number[] = [];
56
+ const lowSegments: number[] = [];
57
+ const skipSet = new Set(skipIdx);
58
+
59
+ const box = new Box3();
60
+ const point = new Vector3();
61
+
62
+ for (let i = 0; i < high.length - 3; i += 3) {
63
+ const currentIndex = i / 3;
64
+ if (skipSet.has(currentIndex)) {
65
+ continue;
66
+ }
67
+
68
+ // segment start + end
69
+ highSegments.push(
70
+ high[i],
71
+ high[i + 1],
72
+ high[i + 2],
73
+ high[i + 3],
74
+ high[i + 4],
75
+ high[i + 5],
76
+ );
77
+ lowSegments.push(
78
+ low[i],
79
+ low[i + 1],
80
+ low[i + 2],
81
+ low[i + 3],
82
+ low[i + 4],
83
+ low[i + 5],
84
+ );
85
+
86
+ box.expandByPoint(
87
+ point.set(
88
+ high[i] + low[i],
89
+ high[i + 1] + low[i + 1],
90
+ high[i + 2] + low[i + 2],
91
+ ),
92
+ );
93
+ box.expandByPoint(
94
+ point.set(
95
+ high[i + 3] + low[i + 3],
96
+ high[i + 4] + low[i + 4],
97
+ high[i + 5] + low[i + 5],
98
+ ),
99
+ );
100
+ }
101
+
102
+ const highBuffer = new InstancedInterleavedBuffer(
103
+ new Float32Array(highSegments),
104
+ 6,
105
+ 1,
106
+ );
107
+ const lowBuffer = new InstancedInterleavedBuffer(
108
+ new Float32Array(lowSegments),
109
+ 6,
110
+ 1,
111
+ );
112
+ this.setAttribute(
113
+ "instanceStartHigh",
114
+ new InterleavedBufferAttribute(highBuffer, 3, 0),
115
+ );
116
+ this.setAttribute(
117
+ "instanceEndHigh",
118
+ new InterleavedBufferAttribute(highBuffer, 3, 3),
119
+ );
120
+ this.setAttribute(
121
+ "instanceStartLow",
122
+ new InterleavedBufferAttribute(lowBuffer, 3, 0),
123
+ );
124
+ this.setAttribute(
125
+ "instanceEndLow",
126
+ new InterleavedBufferAttribute(lowBuffer, 3, 3),
127
+ );
128
+
129
+ // Exact-radius sphere (same behavior as LineSegmentsGeometry's own
130
+ // computeBoundingSphere, which cannot run without instanceStart/End).
131
+ this.boundingBox = box;
132
+ const sphere = this.boundingSphere ?? new Sphere();
133
+ const center = box.getCenter(sphere.center);
134
+ let maxRadiusSq = 0;
135
+ for (let i = 0; i < highSegments.length; i += 3) {
136
+ point.set(
137
+ highSegments[i] + lowSegments[i],
138
+ highSegments[i + 1] + lowSegments[i + 1],
139
+ highSegments[i + 2] + lowSegments[i + 2],
140
+ );
141
+ maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(point));
142
+ }
143
+ sphere.radius = Math.sqrt(maxRadiusSq);
144
+ this.boundingSphere = sphere;
145
+
146
+ return this;
147
+ }
148
+ }
@@ -2,19 +2,25 @@ import {
2
2
  PolygonMesh as NavaraPolygonMesh,
3
3
  PolygonMaterial,
4
4
  } from "@navaramap/engine";
5
+ import { RTE_ONE_UNIFORM } from "@navaramap/three-api";
5
6
  import BatchTextureParsVertex from "@shaders/glsl/chunks/batch_texture_pars_vertex.glsl";
6
7
  import BranchFreeTernary from "@shaders/glsl/chunks/branchFreeTernary.glsl";
7
8
  import ExtrudedHeightParsVertex from "@shaders/glsl/chunks/extruded_height_pars_vertex.glsl";
8
9
  import ExtrudedHeightVertex from "@shaders/glsl/chunks/extruded_height_vertex.glsl";
9
10
  import HeightParsVertex from "@shaders/glsl/chunks/height_pars_vertex.glsl";
10
11
  import HeightVertex from "@shaders/glsl/chunks/height_vertex.glsl";
11
- import { Color, type DataTexture, InstancedBufferAttribute } from "three";
12
12
  import {
13
- Line2,
14
- LineGeometry,
15
- LineMaterial,
16
- LineSegmentsGeometry,
17
- } from "three-stdlib";
13
+ Color,
14
+ type DataTexture,
15
+ InstancedBufferAttribute,
16
+ type Intersection,
17
+ Matrix4,
18
+ type Object3D,
19
+ type Raycaster,
20
+ Vector3,
21
+ Vector4,
22
+ } from "three";
23
+ import { Line2, LineGeometry, LineMaterial } from "three-stdlib";
18
24
 
19
25
  import type { EventContext } from "../event/context";
20
26
  import { setupMaterialForMRT } from "../material";
@@ -23,39 +29,14 @@ import { createReplacer } from "../utils/replacer";
23
29
  import { POLYGON_BATCH_TEXTURE_ROWS } from "./batchedFeature";
24
30
  import { initBatchedMaterial } from "./batchTexture";
25
31
  import type { FeatureMesh } from "./featureMesh";
26
-
27
- class NvLineGeometry extends LineGeometry {
28
- setPositions(
29
- array: Float32Array,
30
- skipIdx: Uint32Array = new Uint32Array(),
31
- ): this {
32
- const positions: number[] = [];
33
- const skipSet = new Set(skipIdx);
34
-
35
- for (let i = 0; i < array.length - 3; i += 3) {
36
- const currentIndex = i / 3;
37
- if (skipSet.has(currentIndex)) {
38
- continue;
39
- }
40
-
41
- // segment start
42
- positions.push(array[i], array[i + 1], array[i + 2]);
43
- // segment end
44
- positions.push(array[i + 3], array[i + 4], array[i + 5]);
45
- }
46
-
47
- const points = new Float32Array(positions);
48
-
49
- // This function is used to override LineGeometry's setPositions,
50
- // so we don't call super.setPositions.
51
- LineSegmentsGeometry.prototype.setPositions.call(this, points);
52
- return this;
53
- }
54
- }
32
+ import { NvLineGeometry } from "./nvLineGeometry";
33
+ import { setupRTECallback } from "./rtcRteHelper";
55
34
 
56
35
  export class PolygonOutlineMesh extends Line2 implements FeatureMesh {
57
36
  readonly ctx: EventContext;
58
37
  private resizeEventUnsubscribe?: () => void;
38
+ /** True when the geometry carries RTE high/low positions (GeoJSON path). */
39
+ private useRTE = false;
59
40
 
60
41
  constructor(ctx: EventContext, mesh: NavaraPolygonMesh) {
61
42
  super(new NvLineGeometry(), new LineMaterial());
@@ -64,6 +45,13 @@ export class PolygonOutlineMesh extends Line2 implements FeatureMesh {
64
45
  this.initMaterial(mesh);
65
46
  }
66
47
 
48
+ raycast(raycaster: Raycaster, intersects: Intersection[]): void {
49
+ // RTE geometry carries no instanceStart/End attributes, which
50
+ // LineSegments2.raycast requires — picking is GPU-based, so skip.
51
+ if (this.useRTE) return;
52
+ super.raycast(raycaster, intersects);
53
+ }
54
+
67
55
  private initGeometry(mesh: NavaraPolygonMesh) {
68
56
  const g = mesh.outline_geometry;
69
57
  if (!g || !g.position) {
@@ -74,6 +62,15 @@ export class PolygonOutlineMesh extends Line2 implements FeatureMesh {
74
62
  return;
75
63
  }
76
64
 
65
+ const position3dHigh =
66
+ g.position_3d_high && g.position_3d_high.size > 0
67
+ ? this.ctx.buf.removeF32(g.position_3d_high.data)
68
+ : undefined;
69
+ const position3dLow =
70
+ g.position_3d_low && g.position_3d_low.size > 0
71
+ ? this.ctx.buf.removeF32(g.position_3d_low.data)
72
+ : undefined;
73
+
77
74
  const scale_normal_and_cap = g.scale_normal_and_cap
78
75
  ? this.ctx.buf.removeF32(g.scale_normal_and_cap.data)
79
76
  : undefined;
@@ -88,7 +85,17 @@ export class PolygonOutlineMesh extends Line2 implements FeatureMesh {
88
85
 
89
86
  // Convert position buffer to Line2 format
90
87
  const lineGeometry = this.geometry as NvLineGeometry;
91
- lineGeometry.setPositions(position, skipIdx);
88
+
89
+ if (position3dHigh && position3dLow) {
90
+ // RTE mode: upload only the high/low pairs — the RTE shader never
91
+ // reads instanceStart/End, so calling setPositions would leave an
92
+ // unused buffer resident on the GPU. setPositionsHighLow also sets
93
+ // the bounds that setPositions would have computed.
94
+ lineGeometry.setPositionsHighLow(position3dHigh, position3dLow, skipIdx);
95
+ this.useRTE = true;
96
+ } else {
97
+ lineGeometry.setPositions(position, skipIdx);
98
+ }
92
99
 
93
100
  // Add scale_normal_and_cap attributes if available
94
101
  if (g.scale_normal_and_cap && scale_normal_and_cap) {
@@ -192,6 +199,47 @@ export class PolygonOutlineMesh extends Line2 implements FeatureMesh {
192
199
  batchLength: 0,
193
200
  });
194
201
 
202
+ if (this.useRTE) {
203
+ material.userData.defines ??= {};
204
+ material.userData.defines.USE_RTE = true;
205
+ material.userData.modelViewMatrixRTE = { value: new Matrix4() };
206
+ material.userData.uCameraPositionHigh = { value: new Vector3() };
207
+ material.userData.uCameraPositionLow = { value: new Vector3() };
208
+
209
+ // The outline mesh sits directly in the scene with an identity
210
+ // matrixWorld (positions are absolute ECEF), so pass identity matrices
211
+ // like PolylineMesh does.
212
+ const rteCallback = setupRTECallback(
213
+ this,
214
+ (modelViewMatrixRTE, cameraPositionHigh, cameraPositionLow) => {
215
+ material.userData.modelViewMatrixRTE.value.copy(modelViewMatrixRTE);
216
+ material.userData.uCameraPositionHigh.value.copy(cameraPositionHigh);
217
+ material.userData.uCameraPositionLow.value.copy(cameraPositionLow);
218
+ },
219
+ new Matrix4(),
220
+ new Matrix4(),
221
+ );
222
+ // LineSegments2.prototype.onBeforeRender refreshes the `resolution`
223
+ // uniform from the viewport every frame; assigning our own callback
224
+ // shadows it, so replicate that update here — otherwise resolution
225
+ // stays at the (1,1) default and line widths blow up to viewport size.
226
+ const updateRTE: Object3D["onBeforeRender"] = rteCallback;
227
+ const viewport = new Vector4();
228
+ this.onBeforeRender = (renderer, scene, camera, geometry, mat, group) => {
229
+ renderer.getViewport(viewport);
230
+ material.resolution.set(viewport.z, viewport.w);
231
+ updateRTE(renderer, scene, camera, geometry, mat, group);
232
+ };
233
+ this.onBeforeShadow = rteCallback;
234
+ }
235
+
236
+ // The defines are stamped in onBeforeCompile (not via material.defines),
237
+ // so they must be part of the program cache key — otherwise RTE and
238
+ // non-RTE outlines (or different batch-define sets) would share one
239
+ // compiled program.
240
+ material.customProgramCacheKey = () =>
241
+ `nvr-polygon-outline:${JSON.stringify(material.userData.defines ?? {})}`;
242
+
195
243
  material.onBeforeCompile = (shader) => {
196
244
  // Merge user-defined defines (batch texture, color/show, height, etc.)
197
245
  shader.defines ??= {};
@@ -206,6 +254,17 @@ export class PolygonOutlineMesh extends Line2 implements FeatureMesh {
206
254
  shader.uniforms.batchDataTexture = material.userData.batchDataTexture;
207
255
  }
208
256
 
257
+ // RTE uniforms (updated per-frame via setupRTECallback)
258
+ if (material.userData.defines?.USE_RTE) {
259
+ shader.uniforms.modelViewMatrixRTE =
260
+ material.userData.modelViewMatrixRTE;
261
+ shader.uniforms.u_cameraPositionHigh =
262
+ material.userData.uCameraPositionHigh;
263
+ shader.uniforms.u_cameraPositionLow =
264
+ material.userData.uCameraPositionLow;
265
+ shader.uniforms.u_rteOne = RTE_ONE_UNIFORM;
266
+ }
267
+
209
268
  shader.vertexShader = createReplacer(shader.vertexShader)
210
269
  .replace(
211
270
  "attribute vec3 instanceEnd;",
@@ -215,6 +274,16 @@ export class PolygonOutlineMesh extends Line2 implements FeatureMesh {
215
274
  attribute vec4 scaleNormalAndCapEnd;
216
275
  varying float nvr_vShow;
217
276
  uniform vec2 uMinMaxHeight;
277
+ #ifdef USE_RTE
278
+ attribute vec3 instanceStartHigh;
279
+ attribute vec3 instanceStartLow;
280
+ attribute vec3 instanceEndHigh;
281
+ attribute vec3 instanceEndLow;
282
+ uniform vec3 u_cameraPositionHigh;
283
+ uniform vec3 u_cameraPositionLow;
284
+ uniform float u_rteOne;
285
+ uniform mat4 modelViewMatrixRTE;
286
+ #endif
218
287
  ${ExtrudedHeightParsVertex}
219
288
  ${HeightParsVertex}
220
289
  ${BranchFreeTernary}
@@ -243,25 +312,40 @@ export class PolygonOutlineMesh extends Line2 implements FeatureMesh {
243
312
  #endif
244
313
  #endif
245
314
 
246
- vec3 adjustedInstanceStart = instanceStart;
247
- adjustedInstanceStart.xyz += scaleNormalAndCapStart.xyz * nvr_branchFreeTernary(
315
+ vec3 nvr_heightOffsetStart = scaleNormalAndCapStart.xyz * nvr_branchFreeTernary(
248
316
  scaleNormalAndCapStart.w == 0.0,
249
317
  uMinMaxHeight.x + addHeight,
250
318
  uMinMaxHeight.y + addExtrudedHeight
251
319
  );
252
- vec4 start = modelViewMatrix * vec4( adjustedInstanceStart, 1.0 );
320
+ #ifdef USE_RTE
321
+ // Reconstruct the camera-relative position from high/low pairs to
322
+ // avoid f32 jitter at globe scale. u_rteOne (always 1.0) blocks
323
+ // fast-math reassociation of the high-difference term.
324
+ vec3 nvr_startHighDiff = (instanceStartHigh - u_cameraPositionHigh) * u_rteOne;
325
+ vec3 nvr_startLowDiff = instanceStartLow - u_cameraPositionLow;
326
+ vec4 start = modelViewMatrixRTE * vec4( nvr_startHighDiff + nvr_startLowDiff + nvr_heightOffsetStart, 1.0 );
327
+ #else
328
+ vec3 adjustedInstanceStart = instanceStart + nvr_heightOffsetStart;
329
+ vec4 start = modelViewMatrix * vec4( adjustedInstanceStart, 1.0 );
330
+ #endif
253
331
  `,
254
332
  )
255
333
  .replace(
256
334
  "vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );",
257
335
  `
258
- vec3 adjustedInstanceEnd = instanceEnd;
259
- adjustedInstanceEnd.xyz += scaleNormalAndCapEnd.xyz * nvr_branchFreeTernary(
336
+ vec3 nvr_heightOffsetEnd = scaleNormalAndCapEnd.xyz * nvr_branchFreeTernary(
260
337
  scaleNormalAndCapEnd.w == 0.0,
261
338
  uMinMaxHeight.x + addHeight,
262
339
  uMinMaxHeight.y + addExtrudedHeight
263
340
  );
264
- vec4 end = modelViewMatrix * vec4( adjustedInstanceEnd, 1.0 );
341
+ #ifdef USE_RTE
342
+ vec3 nvr_endHighDiff = (instanceEndHigh - u_cameraPositionHigh) * u_rteOne;
343
+ vec3 nvr_endLowDiff = instanceEndLow - u_cameraPositionLow;
344
+ vec4 end = modelViewMatrixRTE * vec4( nvr_endHighDiff + nvr_endLowDiff + nvr_heightOffsetEnd, 1.0 );
345
+ #else
346
+ vec3 adjustedInstanceEnd = instanceEnd + nvr_heightOffsetEnd;
347
+ vec4 end = modelViewMatrix * vec4( adjustedInstanceEnd, 1.0 );
348
+ #endif
265
349
  `,
266
350
  ).source;
267
351